diff --git a/.clang-format b/.clang-format
index e5114ffd062d399b881d114f50e94b45988832dc..4ffb182c0fb8f021a6b1f31aee4b7f1b34608f63 100644
--- a/.clang-format
+++ b/.clang-format
@@ -62,16 +62,18 @@ IncludeCategories:
   - Regex:           '^"core/'
     Priority:        4
   - Regex:           '^"domain_decomposition/'
-    Priority:        6
+    Priority:        5
   - Regex:           '^"executiontree/'
-    Priority:        7
+    Priority:        6
   - Regex:           '^"fft/'
-    Priority:        8
+    Priority:        7
   - Regex:           '^"field/'
-    Priority:        9
+    Priority:        8
   - Regex:           '^"gather/'
-    Priority:        10
+    Priority:        9
   - Regex:           '^"geometry/'
+    Priority:        10
+  - Regex:           '^"gpu/'
     Priority:        11
   - Regex:           '^"gpu/'
     Priority:        12
@@ -97,16 +99,18 @@ IncludeCategories:
     Priority:        21
   - Regex:           '^"simd/'
     Priority:        22
-  - Regex:           '^"stencil/'
+  - Regex:           '^"sqlite/'
     Priority:        23
-  - Regex:           '^"timeloop/'
+  - Regex:           '^"stencil/'
     Priority:        24
-  - Regex:           '^"vtk/'
+  - Regex:           '^"timeloop/'
     Priority:        25
-  - Regex:           '^<boost/'
+  - Regex:           '^"vtk/'
     Priority:        26
-  - Regex:           '^<'
+  - Regex:           '^<boost/'
     Priority:        27
+  - Regex:           '^<'
+    Priority:        28
 IndentCaseLabels: false
 IndentPPDirectives: AfterHash
 IndentWidth: 3
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 47ac8e0266e3ae7ddf305a22c70fb22406304238..1ee46ddc0af7fd8dfd8e859d18ce0bce05e4184d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -57,6 +57,7 @@ stages:
         -DWALBERLA_BUILD_WITH_CODEGEN=$WALBERLA_BUILD_WITH_CODEGEN
         -DWALBERLA_STL_BOUNDS_CHECKS=$WALBERLA_STL_BOUNDS_CHECKS
         -DWALBERLA_LOGLEVEL=$WALBERLA_LOGLEVEL
+        -DCMAKE_CUDA_ARCHITECTURES=60
       - cmake . -LA
       - make -j $NUM_BUILD_CORES -l $NUM_CORES
       - ctest -LE $CTEST_EXCLUDE_LABELS -C $CMAKE_BUILD_TYPE --output-on-failure -j $NUM_CORES -T Test
diff --git a/apps/benchmarks/CMakeLists.txt b/apps/benchmarks/CMakeLists.txt
index f418d434ca38902dc79be8f9b9a4207f9fb90de6..cffa8f427d9fc06e9bdc96090553374eb3e73ba3 100644
--- a/apps/benchmarks/CMakeLists.txt
+++ b/apps/benchmarks/CMakeLists.txt
@@ -5,9 +5,11 @@ add_subdirectory( DEM )
 add_subdirectory( MeshDistance )
 add_subdirectory( CouetteFlow )
 add_subdirectory( FreeSurfaceAdvection )
+add_subdirectory( FluidizedBed )
 add_subdirectory( FluidParticleCoupling )
 add_subdirectory( FluidParticleCouplingWithLoadBalancing )
 add_subdirectory( ForcesOnSphereNearPlaneInShearFlow )
+add_subdirectory(Percolation)
 add_subdirectory( GranularGas )
 add_subdirectory( IntegratorAccuracy )
 add_subdirectory( LennardJones )
diff --git a/apps/benchmarks/FluidParticleCoupling/CMakeLists.txt b/apps/benchmarks/FluidParticleCoupling/CMakeLists.txt
index 898352998666621d6bdfec7f0f07f5c6b4de3724..34ffaca075f6ac90be4f9f077f779ec21f7e6603 100644
--- a/apps/benchmarks/FluidParticleCoupling/CMakeLists.txt
+++ b/apps/benchmarks/FluidParticleCoupling/CMakeLists.txt
@@ -67,7 +67,5 @@ waLBerla_add_executable ( NAME ObliqueWetCollision FILES ObliqueWetCollision.cpp
 
 endif()
 
-
-
 waLBerla_add_executable ( NAME ObliqueDryCollision FILES ObliqueDryCollision.cpp
-      DEPENDS blockforest core mesa_pd postprocessing )
\ No newline at end of file
+      DEPENDS blockforest core mesa_pd postprocessing )
diff --git a/apps/benchmarks/FluidizedBed/CMakeLists.txt b/apps/benchmarks/FluidizedBed/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9c33fb523801a7f55935e99661496e38142b412a
--- /dev/null
+++ b/apps/benchmarks/FluidizedBed/CMakeLists.txt
@@ -0,0 +1,6 @@
+waLBerla_link_files_to_builddir("*.prm")
+
+if (WALBERLA_BUILD_WITH_GPU_SUPPORT AND WALBERLA_BUILD_WITH_CODEGEN AND (CMAKE_CUDA_ARCHITECTURES GREATER_EQUAL 60 OR WALBERLA_BUILD_WITH_HIP))
+    waLBerla_add_executable(NAME FluidizedBed_PSM_GPU FILES FluidizedBedGPU.cpp
+            DEPENDS blockforest boundary core gpu domain_decomposition field lbm lbm_mesapd_coupling mesa_pd timeloop vtk PSMCodegenPython_srt_sc1)
+endif ()
diff --git a/apps/benchmarks/FluidizedBed/FluidizedBedGPU.cpp b/apps/benchmarks/FluidizedBed/FluidizedBedGPU.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cf454243e5c71ce9b8cde90dd73fa92b47d44bff
--- /dev/null
+++ b/apps/benchmarks/FluidizedBed/FluidizedBedGPU.cpp
@@ -0,0 +1,841 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file FluidizedBedGPU.cpp
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//! \brief Modification of showcases/FluidizedBed/FluidizedBedPSM.cpp
+//
+//======================================================================================================================
+
+#include "blockforest/Initialization.h"
+
+#include "core/DataTypes.h"
+#include "core/Environment.h"
+#include "core/debug/Debug.h"
+#include "core/grid_generator/SCIterator.h"
+#include "core/logging/all.h"
+#include "core/math/all.h"
+#include "core/mpi/Broadcast.h"
+#include "core/timing/RemainingTimeLogger.h"
+
+#include "field/AddToStorage.h"
+#include "field/vtk/all.h"
+
+#include "geometry/InitBoundaryHandling.h"
+
+#include "gpu/AddGPUFieldToStorage.h"
+#include "gpu/DeviceSelectMPI.h"
+#include "gpu/communication/UniformGPUScheme.h"
+
+#include "lbm/PerformanceLogger.h"
+#include "lbm/vtk/all.h"
+
+#include "lbm_mesapd_coupling/DataTypesCodegen.h"
+#include "lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMSweepCollection.h"
+#include "lbm_mesapd_coupling/utility/AddForceOnParticlesKernel.h"
+#include "lbm_mesapd_coupling/utility/AddHydrodynamicInteractionKernel.h"
+#include "lbm_mesapd_coupling/utility/AverageHydrodynamicForceTorqueKernel.h"
+#include "lbm_mesapd_coupling/utility/InitializeHydrodynamicForceTorqueForAveragingKernel.h"
+#include "lbm_mesapd_coupling/utility/LubricationCorrectionKernel.h"
+#include "lbm_mesapd_coupling/utility/ParticleSelector.h"
+#include "lbm_mesapd_coupling/utility/ResetHydrodynamicForceTorqueKernel.h"
+
+#include "mesa_pd/collision_detection/AnalyticContactDetection.h"
+#include "mesa_pd/data/DataTypes.h"
+#include "mesa_pd/data/LinkedCells.h"
+#include "mesa_pd/data/ParticleAccessorWithShape.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/ShapeStorage.h"
+#include "mesa_pd/data/shape/HalfSpace.h"
+#include "mesa_pd/data/shape/Sphere.h"
+#include "mesa_pd/domain/BlockForestDataHandling.h"
+#include "mesa_pd/domain/BlockForestDomain.h"
+#include "mesa_pd/kernel/AssocToBlock.h"
+#include "mesa_pd/kernel/DoubleCast.h"
+#include "mesa_pd/kernel/InsertParticleIntoLinkedCells.h"
+#include "mesa_pd/kernel/LinearSpringDashpot.h"
+#include "mesa_pd/kernel/ParticleSelector.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
+#include "mesa_pd/mpi/ContactFilter.h"
+#include "mesa_pd/mpi/ReduceContactHistory.h"
+#include "mesa_pd/mpi/ReduceProperty.h"
+#include "mesa_pd/mpi/SyncNextNeighbors.h"
+#include "mesa_pd/mpi/notifications/ForceTorqueNotification.h"
+#include "mesa_pd/mpi/notifications/HydrodynamicForceTorqueNotification.h"
+#include "mesa_pd/vtk/ParticleVtkOutput.h"
+
+#include "vtk/all.h"
+
+#include "InitializeDomainForPSM.h"
+#include "PSMPackInfo.h"
+#include "PSMSweepSplit.h"
+#include "PSM_Density.h"
+#include "PSM_InfoHeader.h"
+#include "PSM_MacroGetter.h"
+#include "PSM_NoSlip.h"
+#include "PSM_UBB.h"
+
+namespace fluidized_bed
+{
+
+///////////
+// USING //
+///////////
+
+using namespace walberla;
+using walberla::uint_t;
+
+using flag_t      = walberla::uint8_t;
+using FlagField_T = FlagField< flag_t >;
+
+using namespace lbm_mesapd_coupling::psm::gpu;
+typedef pystencils::PSMPackInfo PackInfo_T;
+
+///////////
+// FLAGS //
+///////////
+
+const FlagUID Fluid_Flag("Fluid");
+const FlagUID NoSlip_Flag("NoSlip");
+const FlagUID Inflow_Flag("Inflow");
+const FlagUID Outflow_Flag("Outflow");
+
+void createPlane(const shared_ptr< mesa_pd::data::ParticleStorage >& ps,
+                 const shared_ptr< mesa_pd::data::ShapeStorage >& ss, Vector3< real_t > position,
+                 Vector3< real_t > normal)
+{
+   mesa_pd::data::Particle&& p0 = *ps->create(true);
+   p0.setPosition(position);
+   p0.setInteractionRadius(std::numeric_limits< real_t >::infinity());
+   p0.setShapeID(ss->create< mesa_pd::data::HalfSpace >(normal));
+   p0.setOwner(mpi::MPIManager::instance()->rank());
+   p0.setType(0);
+   mesa_pd::data::particle_flags::set(p0.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
+   mesa_pd::data::particle_flags::set(p0.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
+}
+
+void createPlaneSetup(const shared_ptr< mesa_pd::data::ParticleStorage >& ps,
+                      const shared_ptr< mesa_pd::data::ShapeStorage >& ss, const math::AABB& simulationDomain,
+                      bool periodicInX, bool periodicInY, real_t offsetAtInflow, real_t offsetAtOutflow)
+{
+   createPlane(ps, ss, simulationDomain.minCorner() + Vector3< real_t >(0, 0, offsetAtInflow),
+               Vector3< real_t >(0, 0, 1));
+   createPlane(ps, ss, simulationDomain.maxCorner() + Vector3< real_t >(0, 0, offsetAtOutflow),
+               Vector3< real_t >(0, 0, -1));
+
+   if (!periodicInX)
+   {
+      createPlane(ps, ss, simulationDomain.minCorner(), Vector3< real_t >(1, 0, 0));
+      createPlane(ps, ss, simulationDomain.maxCorner(), Vector3< real_t >(-1, 0, 0));
+   }
+
+   if (!periodicInY)
+   {
+      createPlane(ps, ss, simulationDomain.minCorner(), Vector3< real_t >(0, 1, 0));
+      createPlane(ps, ss, simulationDomain.maxCorner(), Vector3< real_t >(0, -1, 0));
+   }
+}
+
+struct ParticleInfo
+{
+   real_t averageVelocity = 0_r;
+   real_t maximumVelocity = 0_r;
+   uint_t numParticles    = 0;
+   real_t maximumHeight   = 0_r;
+   real_t particleVolume  = 0_r;
+   real_t heightOfMass    = 0_r;
+
+   void allReduce()
+   {
+      walberla::mpi::allReduceInplace(numParticles, walberla::mpi::SUM);
+      walberla::mpi::allReduceInplace(averageVelocity, walberla::mpi::SUM);
+      walberla::mpi::allReduceInplace(maximumVelocity, walberla::mpi::MAX);
+      walberla::mpi::allReduceInplace(maximumHeight, walberla::mpi::MAX);
+      walberla::mpi::allReduceInplace(particleVolume, walberla::mpi::SUM);
+      walberla::mpi::allReduceInplace(heightOfMass, walberla::mpi::SUM);
+
+      averageVelocity /= real_c(numParticles);
+      heightOfMass /= particleVolume;
+   }
+};
+
+std::ostream& operator<<(std::ostream& os, ParticleInfo const& m)
+{
+   return os << "Particle Info: uAvg = " << m.averageVelocity << ", uMax = " << m.maximumVelocity
+             << ", numParticles = " << m.numParticles << ", zMax = " << m.maximumHeight << ", Vp = " << m.particleVolume
+             << ", zMass = " << m.heightOfMass;
+}
+
+template< typename Accessor_T >
+ParticleInfo evaluateParticleInfo(const Accessor_T& ac)
+{
+   static_assert(std::is_base_of< mesa_pd::data::IAccessor, Accessor_T >::value, "Provide a valid accessor");
+
+   ParticleInfo info;
+   for (uint_t i = 0; i < ac.size(); ++i)
+   {
+      if (isSet(ac.getFlags(i), mesa_pd::data::particle_flags::GHOST)) continue;
+      if (isSet(ac.getFlags(i), mesa_pd::data::particle_flags::GLOBAL)) continue;
+
+      ++info.numParticles;
+      real_t velMagnitude   = ac.getLinearVelocity(i).length();
+      real_t particleVolume = ac.getShape(i)->getVolume();
+      real_t height         = ac.getPosition(i)[2];
+      info.averageVelocity += velMagnitude;
+      info.maximumVelocity = std::max(info.maximumVelocity, velMagnitude);
+      info.maximumHeight   = std::max(info.maximumHeight, height);
+      info.particleVolume += particleVolume;
+      info.heightOfMass += particleVolume * height;
+   }
+
+   info.allReduce();
+
+   return info;
+}
+
+struct FluidInfo
+{
+   uint_t numFluidCells   = 0;
+   real_t averageVelocity = 0_r;
+   real_t maximumVelocity = 0_r;
+   real_t averageDensity  = 0_r;
+   real_t maximumDensity  = 0_r;
+
+   void allReduce()
+   {
+      walberla::mpi::allReduceInplace(numFluidCells, walberla::mpi::SUM);
+      walberla::mpi::allReduceInplace(averageVelocity, walberla::mpi::SUM);
+      walberla::mpi::allReduceInplace(maximumVelocity, walberla::mpi::MAX);
+      ;
+      walberla::mpi::allReduceInplace(averageDensity, walberla::mpi::SUM);
+      walberla::mpi::allReduceInplace(maximumDensity, walberla::mpi::MAX);
+
+      averageVelocity /= real_c(numFluidCells);
+      averageDensity /= real_c(numFluidCells);
+   }
+};
+
+std::ostream& operator<<(std::ostream& os, FluidInfo const& m)
+{
+   return os << "Fluid Info: numFluidCells = " << m.numFluidCells << ", uAvg = " << m.averageVelocity
+             << ", uMax = " << m.maximumVelocity << ", densityAvg = " << m.averageDensity
+             << ", densityMax = " << m.maximumDensity;
+}
+
+FluidInfo evaluateFluidInfo(const shared_ptr< StructuredBlockStorage >& blocks, const BlockDataID& densityFieldID,
+                            const BlockDataID& velocityFieldID)
+{
+   FluidInfo info;
+
+   for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+   {
+      auto densityField  = blockIt->getData< DensityField_T >(densityFieldID);
+      auto velocityField = blockIt->getData< VelocityField_T >(velocityFieldID);
+
+      WALBERLA_FOR_ALL_CELLS_XYZ(
+         densityField, ++info.numFluidCells; Vector3< real_t > velocity(
+            velocityField->get(x, y, z, 0), velocityField->get(x, y, z, 1), velocityField->get(x, y, z, 2));
+         real_t density = densityField->get(x, y, z); real_t velMagnitude = velocity.length();
+         info.averageVelocity += velMagnitude; info.maximumVelocity = std::max(info.maximumVelocity, velMagnitude);
+         info.averageDensity += density; info.maximumDensity        = std::max(info.maximumDensity, density);)
+   }
+   info.allReduce();
+   return info;
+}
+
+//////////
+// MAIN //
+//////////
+
+//*******************************************************************************************************************
+/*!\brief Basic simulation of a fluidization setup
+ *
+ * Initially, the mono-sized sphere are created on a structured grid inside the domain.
+ * The domain is either periodic or bounded by walls in the horizontal directions (x and y).
+ * In z-direction, a constant inflow from below is provided
+ * and a pressure boundary condition is set at the top, resembling an outflow boundary.
+ *
+ * The simulation is run for the given number of seconds (runtime).
+ *
+ * All parameters should be set via the input file.
+ *
+ * For the overall algorithm and the different model parameters, see
+ * Rettinger, Rüde - An efficient four-way coupled lattice Boltzmann - discrete element method for
+ * fully resolved simulations of particle-laden flows (2020, preprint: https://arxiv.org/abs/2003.01490)
+ *
+ */
+//*******************************************************************************************************************
+int main(int argc, char** argv)
+{
+   Environment env(argc, argv);
+   gpu::selectDeviceBasedOnMpiRank();
+
+   auto cfgFile = env.config();
+   if (!cfgFile) { WALBERLA_ABORT("Usage: " << argv[0] << " path-to-configuration-file \n"); }
+
+   WALBERLA_LOG_INFO_ON_ROOT("waLBerla revision: " << std::string(WALBERLA_GIT_SHA1).substr(0, 8));
+   WALBERLA_LOG_INFO_ON_ROOT("compiler flags: " << std::string(WALBERLA_COMPILER_FLAGS));
+   WALBERLA_LOG_INFO_ON_ROOT("build machine: " << std::string(WALBERLA_BUILD_MACHINE));
+   WALBERLA_LOG_INFO_ON_ROOT(*cfgFile);
+
+   // read all parameters from the config file
+
+   Config::BlockHandle physicalSetup         = cfgFile->getBlock("PhysicalSetup");
+   const real_t xSize_SI                     = physicalSetup.getParameter< real_t >("xSize");
+   const real_t ySize_SI                     = physicalSetup.getParameter< real_t >("ySize");
+   const real_t zSize_SI                     = physicalSetup.getParameter< real_t >("zSize");
+   const bool periodicInX                    = physicalSetup.getParameter< bool >("periodicInX");
+   const bool periodicInY                    = physicalSetup.getParameter< bool >("periodicInY");
+   const real_t runtime_SI                   = physicalSetup.getParameter< real_t >("runtime");
+   const real_t uInflow_SI                   = physicalSetup.getParameter< real_t >("uInflow");
+   const real_t gravitationalAcceleration_SI = physicalSetup.getParameter< real_t >("gravitationalAcceleration");
+   const real_t kinematicViscosityFluid_SI   = physicalSetup.getParameter< real_t >("kinematicViscosityFluid");
+   const real_t densityFluid_SI              = physicalSetup.getParameter< real_t >("densityFluid");
+   const real_t particleDiameter_SI          = physicalSetup.getParameter< real_t >("particleDiameter");
+   const real_t densityParticle_SI           = physicalSetup.getParameter< real_t >("densityParticle");
+   const real_t dynamicFrictionCoefficient   = physicalSetup.getParameter< real_t >("dynamicFrictionCoefficient");
+   const real_t coefficientOfRestitution     = physicalSetup.getParameter< real_t >("coefficientOfRestitution");
+   const real_t collisionTimeFactor          = physicalSetup.getParameter< real_t >("collisionTimeFactor");
+   const real_t particleGenerationSpacing_SI = physicalSetup.getParameter< real_t >("particleGenerationSpacing");
+
+   Config::BlockHandle numericalSetup = cfgFile->getBlock("NumericalSetup");
+   const real_t dx_SI                 = numericalSetup.getParameter< real_t >("dx");
+   const real_t uInflow               = numericalSetup.getParameter< real_t >("uInflow");
+   const uint_t numXBlocks            = numericalSetup.getParameter< uint_t >("numXBlocks");
+   const uint_t numYBlocks            = numericalSetup.getParameter< uint_t >("numYBlocks");
+   const uint_t numZBlocks            = numericalSetup.getParameter< uint_t >("numZBlocks");
+   WALBERLA_CHECK_EQUAL(numXBlocks * numYBlocks * numZBlocks, uint_t(MPIManager::instance()->numProcesses()),
+                        "When using GPUs, the number of blocks ("
+                           << numXBlocks * numYBlocks * numZBlocks << ") has to match the number of MPI processes ("
+                           << uint_t(MPIManager::instance()->numProcesses()) << ")");
+   if ((periodicInX && numXBlocks == 1) || (periodicInY && numYBlocks == 1))
+   {
+      WALBERLA_ABORT("The number of blocks must be greater than 1 in periodic dimensions.")
+   }
+   const bool useLubricationForces        = numericalSetup.getParameter< bool >("useLubricationForces");
+   const uint_t numberOfParticleSubCycles = numericalSetup.getParameter< uint_t >("numberOfParticleSubCycles");
+   const Vector3< uint_t > particleSubBlockSize =
+      numericalSetup.getParameter< Vector3< uint_t > >("particleSubBlockSize");
+   const real_t linkedCellWidthRation = numericalSetup.getParameter< real_t >("linkedCellWidthRation");
+   const bool particleBarriers        = numericalSetup.getParameter< bool >("particleBarriers");
+
+   Config::BlockHandle outputSetup      = cfgFile->getBlock("Output");
+   const real_t infoSpacing_SI          = outputSetup.getParameter< real_t >("infoSpacing");
+   const real_t vtkSpacingParticles_SI  = outputSetup.getParameter< real_t >("vtkSpacingParticles");
+   const real_t vtkSpacingFluid_SI      = outputSetup.getParameter< real_t >("vtkSpacingFluid");
+   const std::string vtkFolder          = outputSetup.getParameter< std::string >("vtkFolder");
+   const uint_t performanceLogFrequency = outputSetup.getParameter< uint_t >("performanceLogFrequency");
+
+   // convert SI units to simulation (LBM) units and check setup
+
+   Vector3< uint_t > domainSize(uint_c(std::ceil(xSize_SI / dx_SI)), uint_c(std::ceil(ySize_SI / dx_SI)),
+                                uint_c(std::ceil(zSize_SI / dx_SI)));
+   WALBERLA_CHECK_FLOAT_EQUAL(real_t(domainSize[0]) * dx_SI, xSize_SI, "domain size in x is not divisible by given dx");
+   WALBERLA_CHECK_FLOAT_EQUAL(real_t(domainSize[1]) * dx_SI, ySize_SI, "domain size in y is not divisible by given dx");
+   WALBERLA_CHECK_FLOAT_EQUAL(real_t(domainSize[2]) * dx_SI, zSize_SI, "domain size in z is not divisible by given dx");
+
+   Vector3< uint_t > cellsPerBlockPerDirection(domainSize[0] / numXBlocks, domainSize[1] / numYBlocks,
+                                               domainSize[2] / numZBlocks);
+
+   WALBERLA_CHECK_EQUAL(domainSize[0], cellsPerBlockPerDirection[0] * numXBlocks,
+                        "number of cells in x of " << domainSize[0]
+                                                   << " is not divisible by given number of blocks in x direction");
+   WALBERLA_CHECK_EQUAL(domainSize[1], cellsPerBlockPerDirection[1] * numYBlocks,
+                        "number of cells in y of " << domainSize[1]
+                                                   << " is not divisible by given number of blocks in y direction");
+   WALBERLA_CHECK_EQUAL(domainSize[2], cellsPerBlockPerDirection[2] * numZBlocks,
+                        "number of cells in z of " << domainSize[2]
+                                                   << " is not divisible by given number of blocks in z direction");
+
+   WALBERLA_CHECK_GREATER(
+      particleDiameter_SI / dx_SI, 5_r,
+      "Your numerical resolution is below 5 cells per diameter and thus too small for such simulations!");
+
+   const real_t densityRatio           = densityParticle_SI / densityFluid_SI;
+   const real_t ReynoldsNumberParticle = uInflow_SI * particleDiameter_SI / kinematicViscosityFluid_SI;
+   const real_t GalileiNumber = std::sqrt((densityRatio - 1_r) * particleDiameter_SI * gravitationalAcceleration_SI) *
+                                particleDiameter_SI / kinematicViscosityFluid_SI;
+
+   // in simulation units: dt = 1, dx = 1, densityFluid = 1
+
+   const real_t dt_SI                     = uInflow / uInflow_SI * dx_SI;
+   const real_t diameter                  = particleDiameter_SI / dx_SI;
+   const real_t particleGenerationSpacing = particleGenerationSpacing_SI / dx_SI;
+   const real_t viscosity                 = kinematicViscosityFluid_SI * dt_SI / (dx_SI * dx_SI);
+   const real_t omega                     = lbm::collision_model::omegaFromViscosity(viscosity);
+   const real_t gravitationalAcceleration = gravitationalAcceleration_SI * dt_SI * dt_SI / dx_SI;
+   const real_t particleVolume            = math::pi / 6_r * diameter * diameter * diameter;
+
+   const real_t densityFluid    = real_t(1);
+   const real_t densityParticle = densityRatio;
+   const real_t dx              = real_t(1);
+
+   const uint_t numTimeSteps        = uint_c(std::ceil(runtime_SI / dt_SI));
+   const uint_t infoSpacing         = uint_c(std::ceil(infoSpacing_SI / dt_SI));
+   const uint_t vtkSpacingParticles = uint_c(std::ceil(vtkSpacingParticles_SI / dt_SI));
+   const uint_t vtkSpacingFluid     = uint_c(std::ceil(vtkSpacingFluid_SI / dt_SI));
+
+   const Vector3< real_t > inflowVec(0_r, 0_r, uInflow);
+
+   const real_t poissonsRatio         = real_t(0.22);
+   const real_t kappa                 = real_t(2) * (real_t(1) - poissonsRatio) / (real_t(2) - poissonsRatio);
+   const real_t particleCollisionTime = collisionTimeFactor * diameter;
+
+   WALBERLA_LOG_INFO_ON_ROOT("Simulation setup:");
+   WALBERLA_LOG_INFO_ON_ROOT(" - particles: diameter = " << diameter << ", densityRatio = " << densityRatio);
+   WALBERLA_LOG_INFO_ON_ROOT(" - fluid: kin. visc = " << viscosity << ", relaxation rate = " << omega);
+   WALBERLA_LOG_INFO_ON_ROOT(" - grav. acceleration = " << gravitationalAcceleration);
+   WALBERLA_LOG_INFO_ON_ROOT(" - Galileo number = " << GalileiNumber);
+   WALBERLA_LOG_INFO_ON_ROOT(" - particle Reynolds number = " << ReynoldsNumberParticle);
+   WALBERLA_LOG_INFO_ON_ROOT(" - domain size = " << domainSize);
+   WALBERLA_LOG_INFO_ON_ROOT(" - cells per blocks per direction = " << cellsPerBlockPerDirection);
+   WALBERLA_LOG_INFO_ON_ROOT(" - dx = " << dx_SI << " m");
+   WALBERLA_LOG_INFO_ON_ROOT(" - dt = " << dt_SI << " s");
+   WALBERLA_LOG_INFO_ON_ROOT(" - total time steps = " << numTimeSteps);
+   WALBERLA_LOG_INFO_ON_ROOT(" - particle generation spacing = " << particleGenerationSpacing);
+   WALBERLA_LOG_INFO_ON_ROOT(" - info spacing = " << infoSpacing);
+   WALBERLA_LOG_INFO_ON_ROOT(" - vtk spacing particles = " << vtkSpacingParticles
+                                                           << ", fluid slice = " << vtkSpacingFluid);
+
+   ///////////////////////////
+   // BLOCK STRUCTURE SETUP //
+   ///////////////////////////
+
+   const bool periodicInZ                     = false;
+   shared_ptr< StructuredBlockForest > blocks = blockforest::createUniformBlockGrid(
+      numXBlocks, numYBlocks, numZBlocks, cellsPerBlockPerDirection[0], cellsPerBlockPerDirection[1],
+      cellsPerBlockPerDirection[2], dx, 0, false, false, periodicInX, periodicInY, periodicInZ, // periodicity
+      false);
+
+   auto simulationDomain = blocks->getDomain();
+
+   //////////////////
+   // RPD COUPLING //
+   //////////////////
+
+   auto rpdDomain = std::make_shared< mesa_pd::domain::BlockForestDomain >(blocks->getBlockForestPointer());
+
+   // init data structures
+   auto ps                  = walberla::make_shared< mesa_pd::data::ParticleStorage >(1);
+   auto ss                  = walberla::make_shared< mesa_pd::data::ShapeStorage >();
+   using ParticleAccessor_T = mesa_pd::data::ParticleAccessorWithShape;
+   auto accessor            = walberla::make_shared< ParticleAccessor_T >(ps, ss);
+
+   // prevent particles from interfering with inflow and outflow by putting the bounding planes slightly in front
+   const real_t planeOffsetFromInflow  = dx;
+   const real_t planeOffsetFromOutflow = dx;
+   createPlaneSetup(ps, ss, simulationDomain, periodicInX, periodicInY, planeOffsetFromInflow, planeOffsetFromOutflow);
+
+   auto sphereShape = ss->create< mesa_pd::data::Sphere >(diameter * real_t(0.5));
+   ss->shapes[sphereShape]->updateMassAndInertia(densityParticle);
+
+   // create spheres
+   auto generationDomain = simulationDomain.getExtended(-particleGenerationSpacing * 0.5_r);
+   for (auto pt : grid_generator::SCGrid(generationDomain, generationDomain.center(), particleGenerationSpacing))
+   {
+      if (rpdDomain->isContainedInProcessSubdomain(uint_c(mpi::MPIManager::instance()->rank()), pt))
+      {
+         mesa_pd::data::Particle&& p = *ps->create();
+         p.setPosition(pt);
+         p.setInteractionRadius(diameter * real_t(0.5));
+         p.setOwner(mpi::MPIManager::instance()->rank());
+         p.setShapeID(sphereShape);
+         p.setType(1);
+         p.setLinearVelocity(0.1_r * Vector3< real_t >(math::realRandom(
+                                        -uInflow, uInflow))); // set small initial velocity to break symmetries
+      }
+   }
+
+   ///////////////////////
+   // ADD DATA TO BLOCKS //
+   ////////////////////////
+
+   // add PDF field
+   BlockDataID pdfFieldID =
+      field::addToStorage< PdfField_T >(blocks, "pdf field (fzyx)", real_c(std::nan("")), field::fzyx);
+   BlockDataID pdfFieldGPUID = gpu::addGPUFieldToStorage< PdfField_T >(blocks, pdfFieldID, "pdf field GPU");
+
+   BlockDataID densityFieldID = field::addToStorage< DensityField_T >(blocks, "Density", real_t(0), field::fzyx);
+   BlockDataID velFieldID     = field::addToStorage< VelocityField_T >(blocks, "Velocity", real_t(0), field::fzyx);
+
+   BlockDataID BFieldID =
+      field::addToStorage< lbm_mesapd_coupling::psm::gpu::BField_T >(blocks, "B field", 0, field::fzyx, 1);
+
+   // add flag field
+   BlockDataID flagFieldID = field::addFlagFieldToStorage< FlagField_T >(blocks, "flag field");
+
+   // set up RPD functionality
+   std::function< void(void) > syncCall = [&ps, &rpdDomain]() {
+      // keep overlap for lubrication
+      const real_t overlap = real_t(1.5);
+      mesa_pd::mpi::SyncNextNeighbors syncNextNeighborFunc;
+      syncNextNeighborFunc(*ps, *rpdDomain, overlap);
+   };
+
+   syncCall();
+
+   real_t timeStepSizeRPD = real_t(1) / real_t(numberOfParticleSubCycles);
+   mesa_pd::kernel::VelocityVerletPreForceUpdate vvIntegratorPreForce(timeStepSizeRPD);
+   mesa_pd::kernel::VelocityVerletPostForceUpdate vvIntegratorPostForce(timeStepSizeRPD);
+   mesa_pd::kernel::LinearSpringDashpot collisionResponse(2);
+   collisionResponse.setFrictionCoefficientDynamic(0, 1, dynamicFrictionCoefficient);
+   collisionResponse.setFrictionCoefficientDynamic(1, 1, dynamicFrictionCoefficient);
+   real_t massSphere       = densityParticle * particleVolume;
+   real_t meffSpherePlane  = massSphere;
+   real_t meffSphereSphere = massSphere * massSphere / (real_t(2) * massSphere);
+   collisionResponse.setStiffnessAndDamping(0, 1, coefficientOfRestitution, particleCollisionTime, kappa,
+                                            meffSpherePlane);
+   collisionResponse.setStiffnessAndDamping(1, 1, coefficientOfRestitution, particleCollisionTime, kappa,
+                                            meffSphereSphere);
+   mesa_pd::kernel::AssocToBlock assoc(blocks->getBlockForestPointer());
+   mesa_pd::mpi::ReduceProperty reduceProperty;
+   mesa_pd::mpi::ReduceContactHistory reduceAndSwapContactHistory;
+
+   // set up coupling functionality
+   Vector3< real_t > gravitationalForce(real_t(0), real_t(0),
+                                        -(densityParticle - densityFluid) * gravitationalAcceleration * particleVolume);
+   lbm_mesapd_coupling::AddForceOnParticlesKernel addGravitationalForce(gravitationalForce);
+   lbm_mesapd_coupling::ResetHydrodynamicForceTorqueKernel resetHydrodynamicForceTorque;
+   lbm_mesapd_coupling::AverageHydrodynamicForceTorqueKernel averageHydrodynamicForceTorque;
+   lbm_mesapd_coupling::LubricationCorrectionKernel lubricationCorrectionKernel(
+      viscosity, [](real_t r) { return (real_t(0.001 + real_t(0.00007) * r)) * r; });
+
+   // assemble boundary block string
+   std::string boundariesBlockString = " Boundaries"
+                                       "{"
+                                       "Border { direction T;    walldistance -1;  flag Outflow; }"
+                                       "Border { direction B;    walldistance -1;  flag Inflow; }";
+
+   if (!periodicInX)
+   {
+      boundariesBlockString += "Border { direction W;    walldistance -1;  flag NoSlip; }"
+                               "Border { direction E;    walldistance -1;  flag NoSlip; }";
+   }
+
+   if (!periodicInY)
+   {
+      boundariesBlockString += "Border { direction S;    walldistance -1;  flag NoSlip; }"
+                               "Border { direction N;    walldistance -1;  flag NoSlip; }";
+   }
+
+   boundariesBlockString += "}";
+   WALBERLA_ROOT_SECTION()
+   {
+      std::ofstream boundariesFile("boundaries.prm");
+      boundariesFile << boundariesBlockString;
+      boundariesFile.close();
+   }
+   WALBERLA_MPI_BARRIER()
+
+   auto boundariesCfgFile = Config();
+   boundariesCfgFile.readParameterFile("boundaries.prm");
+   auto boundariesConfig = boundariesCfgFile.getBlock("Boundaries");
+
+   // map boundaries into the LBM simulation
+   geometry::initBoundaryHandling< FlagField_T >(*blocks, flagFieldID, boundariesConfig);
+   geometry::setNonBoundaryCellsToDomain< FlagField_T >(*blocks, flagFieldID, Fluid_Flag);
+   lbm::PSM_NoSlip noSlip(blocks, pdfFieldGPUID);
+   noSlip.fillFromFlagField< FlagField_T >(blocks, flagFieldID, NoSlip_Flag, Fluid_Flag);
+   lbm::PSM_UBB ubb(blocks, pdfFieldGPUID, inflowVec[0], inflowVec[1], inflowVec[2]);
+   ubb.fillFromFlagField< FlagField_T >(blocks, flagFieldID, Inflow_Flag, Fluid_Flag);
+   lbm::PSM_Density density_bc(blocks, pdfFieldGPUID, real_t(1));
+   density_bc.fillFromFlagField< FlagField_T >(blocks, flagFieldID, Outflow_Flag, Fluid_Flag);
+
+   ///////////////
+   // TIME LOOP //
+   ///////////////
+
+   // map particles into the LBM simulation
+   // note: planes are not mapped and are thus only visible to the particles, not to the fluid
+   // instead, the respective boundary conditions for the fluid are explicitly set, see the boundary handling
+   ParticleAndVolumeFractionSoA_T< 1 > particleAndVolumeFractionSoA(blocks, omega);
+   PSMSweepCollection psmSweepCollection(blocks, accessor, lbm_mesapd_coupling::RegularParticlesSelector(),
+                                            particleAndVolumeFractionSoA, particleSubBlockSize);
+   for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+   {
+      psmSweepCollection.particleMappingSweep(&(*blockIt));
+   }
+
+   pystencils::InitializeDomainForPSM pdfSetter(
+      particleAndVolumeFractionSoA.BsFieldID, particleAndVolumeFractionSoA.BFieldID,
+      particleAndVolumeFractionSoA.particleVelocitiesFieldID, pdfFieldGPUID, real_t(0), real_t(0), real_t(0),
+      real_t(1.0), real_t(0), real_t(0), real_t(0));
+
+   for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+   {
+      // pdfSetter requires particle velocities at cell centers
+      psmSweepCollection.setParticleVelocitiesSweep(&(*blockIt));
+      pdfSetter(&(*blockIt));
+   }
+
+   // setup of the LBM communication for synchronizing the pdf field between neighboring blocks
+   gpu::communication::UniformGPUScheme< Stencil_T > com(blocks, true, false);
+   com.addPackInfo(make_shared< PackInfo_T >(pdfFieldGPUID));
+   auto communication = std::function< void() >([&]() { com.communicate(); });
+
+   // create the timeloop
+   SweepTimeloop commTimeloop(blocks->getBlockStorage(), numTimeSteps);
+   SweepTimeloop timeloop(blocks->getBlockStorage(), numTimeSteps);
+
+   timeloop.addFuncBeforeTimeStep(RemainingTimeLogger(timeloop.getNrOfTimeSteps()), "Remaining Time Logger");
+
+   pystencils::PSM_MacroGetter getterSweep(BFieldID, densityFieldID, pdfFieldID, velFieldID, real_t(0.0), real_t(0.0),
+                                           real_t(0.0));
+   // vtk output
+   if (vtkSpacingParticles != uint_t(0))
+   {
+      // sphere
+      auto particleVtkOutput = make_shared< mesa_pd::vtk::ParticleVtkOutput >(ps);
+      particleVtkOutput->addOutput< mesa_pd::data::SelectParticleUid >("uid");
+      particleVtkOutput->addOutput< mesa_pd::data::SelectParticleLinearVelocity >("velocity");
+      particleVtkOutput->addOutput< mesa_pd::data::SelectParticleInteractionRadius >("radius");
+      // limit output to process-local spheres
+      particleVtkOutput->setParticleSelector([sphereShape](const mesa_pd::data::ParticleStorage::iterator& pIt) {
+         return pIt->getShapeID() == sphereShape &&
+                !(mesa_pd::data::particle_flags::isSet(pIt->getFlags(), mesa_pd::data::particle_flags::GHOST));
+      });
+      auto particleVtkWriter =
+         vtk::createVTKOutput_PointData(particleVtkOutput, "particles", vtkSpacingParticles, vtkFolder);
+      timeloop.addFuncBeforeTimeStep(vtk::writeFiles(particleVtkWriter), "VTK (sphere data)");
+   }
+
+   if (vtkSpacingFluid != uint_t(0))
+   {
+      // velocity field, only a slice
+      auto pdfFieldVTK = vtk::createVTKOutput_BlockData(blocks, "fluid", vtkSpacingFluid, 0, false, vtkFolder);
+
+      pdfFieldVTK->addBeforeFunction(communication);
+
+      pdfFieldVTK->addBeforeFunction([&]() {
+         gpu::fieldCpy< PdfField_T, gpu::GPUField< real_t > >(blocks, pdfFieldID, pdfFieldGPUID);
+         gpu::fieldCpy< GhostLayerField< real_t, 1 >, BFieldGPU_T >(blocks, BFieldID,
+                                                                    particleAndVolumeFractionSoA.BFieldID);
+         for (auto& block : *blocks)
+            getterSweep(&block);
+      });
+
+      AABB sliceAABB(real_t(0), real_c(domainSize[1]) * real_t(0.5) - real_t(1), real_t(0), real_c(domainSize[0]),
+                     real_c(domainSize[1]) * real_t(0.5) + real_t(1), real_c(domainSize[2]));
+      vtk::AABBCellFilter aabbSliceFilter(sliceAABB);
+
+      field::FlagFieldCellFilter< FlagField_T > fluidFilter(flagFieldID);
+      fluidFilter.addFlag(Fluid_Flag);
+
+      vtk::ChainedFilter combinedSliceFilter;
+      combinedSliceFilter.addFilter(fluidFilter);
+      combinedSliceFilter.addFilter(aabbSliceFilter);
+
+      pdfFieldVTK->addCellInclusionFilter(combinedSliceFilter);
+
+      pdfFieldVTK->addCellDataWriter(make_shared< field::VTKWriter< VelocityField_T > >(velFieldID, "Velocity"));
+      pdfFieldVTK->addCellDataWriter(make_shared< field::VTKWriter< DensityField_T > >(densityFieldID, "Density"));
+      pdfFieldVTK->addCellDataWriter(make_shared< field::VTKWriter< BField_T > >(BFieldID, "Fraction mapping field B"));
+
+      timeloop.addFuncBeforeTimeStep(vtk::writeFiles(pdfFieldVTK), "VTK (fluid field data)");
+   }
+
+   if (vtkSpacingFluid != uint_t(0) || vtkSpacingParticles != uint_t(0))
+   {
+      vtk::writeDomainDecomposition(blocks, "domain_decomposition", vtkFolder);
+   }
+
+   // add performance logging
+   const lbm::PerformanceLogger< FlagField_T > performanceLogger(blocks, flagFieldID, Fluid_Flag,
+                                                                 performanceLogFrequency);
+   timeloop.addFuncAfterTimeStep(performanceLogger, "Evaluate performance logging");
+
+   // add LBM communication function and boundary handling sweep
+   timeloop.add() << Sweep(deviceSyncWrapper(ubb.getSweep()), "Boundary Handling (UBB)");
+   timeloop.add() << Sweep(deviceSyncWrapper(density_bc.getSweep()), "Boundary Handling (Density)");
+   timeloop.add() << Sweep(deviceSyncWrapper(noSlip.getSweep()), "Boundary Handling (NoSlip)");
+
+   // stream + collide LBM step
+   pystencils::PSMSweepSplit PSMSweep(particleAndVolumeFractionSoA.BsFieldID, particleAndVolumeFractionSoA.BFieldID,
+                                      particleAndVolumeFractionSoA.particleForcesFieldID,
+                                      particleAndVolumeFractionSoA.particleVelocitiesFieldID, pdfFieldGPUID,
+                                      real_t(0.0), real_t(0.0), real_t(0.0), omega);
+   addPSMSweepsToTimeloops(commTimeloop, timeloop, com, psmSweepCollection, PSMSweep);
+
+   ////////////////////////
+   // EXECUTE SIMULATION //
+   ////////////////////////
+
+   WcTimingPool timeloopTiming;
+   const bool useOpenMP = true;
+
+   real_t linkedCellWidth = linkedCellWidthRation * diameter;
+   mesa_pd::data::LinkedCells linkedCells(rpdDomain->getUnionOfLocalAABBs().getExtended(linkedCellWidth),
+                                          linkedCellWidth);
+   mesa_pd::kernel::InsertParticleIntoLinkedCells ipilc;
+
+   // time loop
+   for (uint_t timeStep = 0; timeStep < numTimeSteps; ++timeStep)
+   {
+      // perform a single simulation step -> this contains LBM and setting of the hydrodynamic interactions
+      commTimeloop.singleStep(timeloopTiming);
+      timeloop.singleStep(timeloopTiming);
+
+      if (particleBarriers) WALBERLA_MPI_BARRIER();
+      timeloopTiming["RPD forEachParticle assoc"].start();
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, assoc, *accessor);
+      if (particleBarriers) WALBERLA_MPI_BARRIER();
+      timeloopTiming["RPD forEachParticle assoc"].end();
+      timeloopTiming["RPD reduceProperty HydrodynamicForceTorqueNotification"].start();
+      reduceProperty.operator()< mesa_pd::HydrodynamicForceTorqueNotification >(*ps);
+      if (particleBarriers) WALBERLA_MPI_BARRIER();
+      timeloopTiming["RPD reduceProperty HydrodynamicForceTorqueNotification"].end();
+
+      if (timeStep == 0)
+      {
+         lbm_mesapd_coupling::InitializeHydrodynamicForceTorqueForAveragingKernel
+            initializeHydrodynamicForceTorqueForAveragingKernel;
+         timeloopTiming["RPD forEachParticle initializeHydrodynamicForceTorqueForAveragingKernel"].start();
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor,
+                             initializeHydrodynamicForceTorqueForAveragingKernel, *accessor);
+         if (particleBarriers) WALBERLA_MPI_BARRIER();
+         timeloopTiming["RPD forEachParticle initializeHydrodynamicForceTorqueForAveragingKernel"].end();
+      }
+      timeloopTiming["RPD forEachParticle averageHydrodynamicForceTorque"].start();
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, averageHydrodynamicForceTorque,
+                          *accessor);
+      if (particleBarriers) WALBERLA_MPI_BARRIER();
+      timeloopTiming["RPD forEachParticle averageHydrodynamicForceTorque"].end();
+
+      for (auto subCycle = uint_t(0); subCycle < numberOfParticleSubCycles; ++subCycle)
+      {
+         timeloopTiming["RPD forEachParticle vvIntegratorPreForce"].start();
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPreForce, *accessor);
+         if (particleBarriers) WALBERLA_MPI_BARRIER();
+         timeloopTiming["RPD forEachParticle vvIntegratorPreForce"].end();
+         timeloopTiming["RPD syncCall"].start();
+         syncCall();
+         if (particleBarriers) WALBERLA_MPI_BARRIER();
+         timeloopTiming["RPD syncCall"].end();
+
+         timeloopTiming["RPD linkedCells.clear"].start();
+         linkedCells.clear();
+         if (particleBarriers) WALBERLA_MPI_BARRIER();
+         timeloopTiming["RPD linkedCells.clear"].end();
+         timeloopTiming["RPD forEachParticle ipilc"].start();
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectAll(), *accessor, ipilc, *accessor, linkedCells);
+         if (particleBarriers) WALBERLA_MPI_BARRIER();
+         timeloopTiming["RPD forEachParticle ipilc"].end();
+
+         if (useLubricationForces)
+         {
+            // lubrication correction
+            timeloopTiming["RPD forEachParticlePairHalf lubricationCorrectionKernel"].start();
+            linkedCells.forEachParticlePairHalf(
+               useOpenMP, mesa_pd::kernel::ExcludeInfiniteInfinite(), *accessor,
+               [&lubricationCorrectionKernel, &rpdDomain](const size_t idx1, const size_t idx2, auto& ac) {
+                  mesa_pd::collision_detection::AnalyticContactDetection acd;
+                  acd.getContactThreshold() = lubricationCorrectionKernel.getNormalCutOffDistance();
+                  mesa_pd::kernel::DoubleCast double_cast;
+                  mesa_pd::mpi::ContactFilter contact_filter;
+                  if (double_cast(idx1, idx2, ac, acd, ac))
+                  {
+                     if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), *rpdDomain))
+                     {
+                        double_cast(acd.getIdx1(), acd.getIdx2(), ac, lubricationCorrectionKernel, ac,
+                                    acd.getContactNormal(), acd.getPenetrationDepth());
+                     }
+                  }
+               },
+               *accessor);
+            if (particleBarriers) WALBERLA_MPI_BARRIER();
+            timeloopTiming["RPD forEachParticlePairHalf lubricationCorrectionKernel"].end();
+         }
+
+         // collision response
+         timeloopTiming["RPD forEachParticlePairHalf collisionResponse"].start();
+         linkedCells.forEachParticlePairHalf(
+            useOpenMP, mesa_pd::kernel::ExcludeInfiniteInfinite(), *accessor,
+            [&collisionResponse, &rpdDomain, timeStepSizeRPD](const size_t idx1, const size_t idx2, auto& ac) {
+               mesa_pd::collision_detection::AnalyticContactDetection acd;
+               mesa_pd::kernel::DoubleCast double_cast;
+               mesa_pd::mpi::ContactFilter contact_filter;
+               if (double_cast(idx1, idx2, ac, acd, ac))
+               {
+                  if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), *rpdDomain))
+                  {
+                     collisionResponse(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), acd.getContactNormal(),
+                                       acd.getPenetrationDepth(), timeStepSizeRPD);
+                  }
+               }
+            },
+            *accessor);
+         if (particleBarriers) WALBERLA_MPI_BARRIER();
+         timeloopTiming["RPD forEachParticlePairHalf collisionResponse"].end();
+
+         timeloopTiming["RPD reduceProperty reduceAndSwapContactHistory"].start();
+         reduceAndSwapContactHistory(*ps);
+         if (particleBarriers) WALBERLA_MPI_BARRIER();
+         timeloopTiming["RPD reduceProperty reduceAndSwapContactHistory"].end();
+
+         // add hydrodynamic force
+         lbm_mesapd_coupling::AddHydrodynamicInteractionKernel addHydrodynamicInteraction;
+         timeloopTiming["RPD forEachParticle addHydrodynamicInteraction + addGravitationalForce"].start();
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addHydrodynamicInteraction,
+                             *accessor);
+
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addGravitationalForce, *accessor);
+         if (particleBarriers) WALBERLA_MPI_BARRIER();
+         timeloopTiming["RPD forEachParticle addHydrodynamicInteraction + addGravitationalForce"].end();
+
+         timeloopTiming["RPD reduceProperty ForceTorqueNotification"].start();
+         reduceProperty.operator()< mesa_pd::ForceTorqueNotification >(*ps);
+         if (particleBarriers) WALBERLA_MPI_BARRIER();
+         timeloopTiming["RPD reduceProperty ForceTorqueNotification"].end();
+
+         timeloopTiming["RPD forEachParticle vvIntegratorPostForce"].start();
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPostForce, *accessor);
+         if (particleBarriers) WALBERLA_MPI_BARRIER();
+         timeloopTiming["RPD forEachParticle vvIntegratorPostForce"].end();
+      }
+
+      timeloopTiming["RPD syncCall"].start();
+      syncCall();
+      if (particleBarriers) WALBERLA_MPI_BARRIER();
+      timeloopTiming["RPD syncCall"].end();
+
+      timeloopTiming["RPD forEachParticle resetHydrodynamicForceTorque"].start();
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectAll(), *accessor, resetHydrodynamicForceTorque, *accessor);
+      if (particleBarriers) WALBERLA_MPI_BARRIER();
+      timeloopTiming["RPD forEachParticle resetHydrodynamicForceTorque"].end();
+
+      if (infoSpacing != 0 && timeStep % infoSpacing == 0)
+      {
+         timeloopTiming["Evaluate infos"].start();
+
+         auto particleInfo = evaluateParticleInfo(*accessor);
+         WALBERLA_LOG_INFO_ON_ROOT(particleInfo);
+
+         auto fluidInfo = evaluateFluidInfo(blocks, densityFieldID, velFieldID);
+         WALBERLA_LOG_INFO_ON_ROOT(fluidInfo);
+         if (particleBarriers) WALBERLA_MPI_BARRIER();
+         timeloopTiming["Evaluate infos"].end();
+      }
+   }
+
+   timeloopTiming.logResultOnRoot();
+
+   return EXIT_SUCCESS;
+}
+
+} // namespace fluidized_bed
+
+int main(int argc, char** argv) { fluidized_bed::main(argc, argv); }
diff --git a/apps/benchmarks/FluidizedBed/input.prm b/apps/benchmarks/FluidizedBed/input.prm
new file mode 100644
index 0000000000000000000000000000000000000000..aa293e8609c90fa61cf982b5d69d998d5a6e66ec
--- /dev/null
+++ b/apps/benchmarks/FluidizedBed/input.prm
@@ -0,0 +1,54 @@
+PhysicalSetup // all to be specified in SI units!
+{
+    xSize 0.05; // = width
+    ySize 0.02; // = depth
+    zSize 0.08; // = height
+
+    periodicInX false;
+    periodicInY false;
+
+    runtime 0.1;
+
+    uInflow 0.005;
+    gravitationalAcceleration 9.81;
+
+    kinematicViscosityFluid 1e-5;
+    densityFluid 1000.;
+
+    particleDiameter 0.002;
+    densityParticle 1100.;
+    dynamicFrictionCoefficient 0.15;
+    coefficientOfRestitution 0.6;
+    collisionTimeFactor 1.0;
+
+    particleGenerationSpacing 0.00401; // 0.00401 or 0.00201
+}
+
+NumericalSetup
+{
+    dx 0.0001; // in m
+    uInflow 0.01; // in LBM units, should be smaller than 0.1, this then determines dt
+
+    // product of number of blocks should be equal to number of used processes
+    numXBlocks 1;
+    numYBlocks 1;
+    numZBlocks 1;
+
+    useLubricationForces true;
+    numberOfParticleSubCycles 10;
+
+    particleSubBlockSize <10, 10, 10>;
+    linkedCellWidthRation 1.01;
+    particleBarriers true;
+}
+
+Output
+{
+    infoSpacing 0.0; // in s
+
+    vtkSpacingParticles 0.0; // in s
+    vtkSpacingFluid 0.0; // in s
+    vtkFolder vtk_out;
+
+    performanceLogFrequency 500;
+}
diff --git a/apps/benchmarks/Percolation/CMakeLists.txt b/apps/benchmarks/Percolation/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..bb8c4f3dc9aa15c71786f4a1bc0a9cc0d28c9664
--- /dev/null
+++ b/apps/benchmarks/Percolation/CMakeLists.txt
@@ -0,0 +1,9 @@
+waLBerla_link_files_to_builddir("*.prm")
+
+if (WALBERLA_BUILD_WITH_CODEGEN)
+    if (NOT WALBERLA_BUILD_WITH_GPU_SUPPORT OR (WALBERLA_BUILD_WITH_GPU_SUPPORT AND (CMAKE_CUDA_ARCHITECTURES GREATER_EQUAL 60 OR WALBERLA_BUILD_WITH_HIP)))
+        waLBerla_add_executable(NAME Percolation FILES Percolation.cpp
+                DEPENDS blockforest core field geometry gpu lbm lbm_mesapd_coupling mesa_pd sqlite vtk PSMCodegenPython_trt-smagorinsky_sc1)
+        target_compile_definitions(Percolation PRIVATE Weighting=2)
+    endif ()
+endif ()
diff --git a/apps/benchmarks/Percolation/Percolation.cpp b/apps/benchmarks/Percolation/Percolation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f778f7bc951582c2aa5ef04281c66801bd8c0728
--- /dev/null
+++ b/apps/benchmarks/Percolation/Percolation.cpp
@@ -0,0 +1,507 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file Percolation.cpp
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#include "blockforest/Initialization.h"
+#include "blockforest/communication/UniformBufferedScheme.h"
+
+#include "core/DataTypes.h"
+#include "core/Environment.h"
+#include "core/grid_generator/SCIterator.h"
+#include "core/logging/all.h"
+#include "core/timing/RemainingTimeLogger.h"
+
+#include "field/AddToStorage.h"
+#include "field/vtk/all.h"
+
+#include "geometry/InitBoundaryHandling.h"
+
+#include "gpu/AddGPUFieldToStorage.h"
+#include "gpu/DeviceSelectMPI.h"
+#include "gpu/communication/UniformGPUScheme.h"
+
+#include "lbm/PerformanceLogger.h"
+#include "lbm/vtk/all.h"
+
+#include "lbm_mesapd_coupling/DataTypesCodegen.h"
+#include "lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMSweepCollection.h"
+#include "lbm_mesapd_coupling/utility/ParticleSelector.h"
+
+#include "mesa_pd/data/DataTypes.h"
+#include "mesa_pd/data/ParticleAccessorWithShape.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/ShapeStorage.h"
+#include "mesa_pd/data/shape/Sphere.h"
+#include "mesa_pd/domain/BlockForestDomain.h"
+#include "mesa_pd/mpi/SyncNextNeighbors.h"
+#include "mesa_pd/vtk/ParticleVtkOutput.h"
+
+#include "sqlite/SQLite.h"
+
+#include "vtk/all.h"
+
+#include "LBMSweep.h"
+#include "PSMPackInfo.h"
+#include "PSMSweep.h"
+#include "PSM_Density.h"
+#include "PSM_InfoHeader.h"
+#include "PSM_MacroGetter.h"
+
+namespace percolation
+{
+
+///////////
+// USING //
+///////////
+
+using namespace walberla;
+using namespace lbm_mesapd_coupling::psm::gpu;
+typedef pystencils::PSMPackInfo PackInfo_T;
+
+using flag_t      = walberla::uint8_t;
+using FlagField_T = FlagField< flag_t >;
+
+///////////
+// FLAGS //
+///////////
+
+const FlagUID Fluid_Flag("Fluid");
+const FlagUID Density_Flag("Density");
+const FlagUID NoSlip_Flag("NoSlip");
+const FlagUID Inflow_Flag("Inflow");
+
+//////////
+// MAIN //
+//////////
+
+//*******************************************************************************************************************
+/*!\brief Benchmark of a percolation setup
+ *
+ * This code can be used as a percolation (useParticles=true) or as a channel flow (useParticles=false) benchmark.
+ * A constant inflow from west is applied and a pressure boundary condition is set at the east.
+ * For the percolation, mono-sized fixed spherical particles are generated on a structured grid with an offset for
+ * every second particle layer in flow direction to avoid channels in flow direction. The flow is described by Darcy's
+ * law. For the channel flow, the flow is described by the Hagen–Poiseuille equation.
+ *
+ * The domain is either periodic or bounded by (no slip) walls in the vertical directions (y and z).
+ *
+ * For the percolation, the PSM is used in combination with a two-way coupling, but no particle dynamics.
+ * For the channel flow, only the LBM is used.
+ *
+ * The parameters can be changed via the input file.
+ *
+ */
+//*******************************************************************************************************************
+int main(int argc, char** argv)
+{
+   Environment env(argc, argv);
+   auto cfgFile = env.config();
+   if (!cfgFile) { WALBERLA_ABORT("Usage: " << argv[0] << " path-to-configuration-file \n"); }
+
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   gpu::selectDeviceBasedOnMpiRank();
+#endif
+
+   WALBERLA_LOG_INFO_ON_ROOT("waLBerla revision: " << std::string(WALBERLA_GIT_SHA1).substr(0, 8));
+   WALBERLA_LOG_INFO_ON_ROOT("compiler flags: " << std::string(WALBERLA_COMPILER_FLAGS));
+   WALBERLA_LOG_INFO_ON_ROOT("build machine: " << std::string(WALBERLA_BUILD_MACHINE));
+   WALBERLA_LOG_INFO_ON_ROOT(*cfgFile);
+
+   // Read config file
+   Config::BlockHandle numericalSetup = cfgFile->getBlock("NumericalSetup");
+   const uint_t numXBlocks            = numericalSetup.getParameter< uint_t >("numXBlocks");
+   const uint_t numYBlocks            = numericalSetup.getParameter< uint_t >("numYBlocks");
+   const uint_t numZBlocks            = numericalSetup.getParameter< uint_t >("numZBlocks");
+   WALBERLA_CHECK_EQUAL(numXBlocks * numYBlocks * numZBlocks, uint_t(MPIManager::instance()->numProcesses()),
+                        "When using GPUs, the number of blocks ("
+                           << numXBlocks * numYBlocks * numZBlocks << ") has to match the number of MPI processes ("
+                           << uint_t(MPIManager::instance()->numProcesses()) << ")");
+   const bool periodicInY                 = numericalSetup.getParameter< bool >("periodicInY");
+   const bool periodicInZ                 = numericalSetup.getParameter< bool >("periodicInZ");
+   const uint_t numXCellsPerBlock         = numericalSetup.getParameter< uint_t >("numXCellsPerBlock");
+   const uint_t numYCellsPerBlock         = numericalSetup.getParameter< uint_t >("numYCellsPerBlock");
+   const uint_t numZCellsPerBlock         = numericalSetup.getParameter< uint_t >("numZCellsPerBlock");
+   const bool sendDirectlyFromGPU         = numericalSetup.getParameter< bool >("sendDirectlyFromGPU");
+   const bool useCommunicationHiding      = numericalSetup.getParameter< bool >("useCommunicationHiding");
+   const Vector3< uint_t > frameWidth     = numericalSetup.getParameter< Vector3< uint_t > >("frameWidth");
+   const uint_t timeSteps                 = numericalSetup.getParameter< uint_t >("timeSteps");
+   const bool useParticles                = numericalSetup.getParameter< bool >("useParticles");
+   const real_t particleDiameter          = numericalSetup.getParameter< real_t >("particleDiameter");
+   const real_t particleGenerationSpacing = numericalSetup.getParameter< real_t >("particleGenerationSpacing");
+   const Vector3< real_t > generationDomainFraction =
+      numericalSetup.getParameter< Vector3< real_t > >("generationDomainFraction");
+   const Vector3< uint_t > generationPointOfReferenceOffset =
+      numericalSetup.getParameter< Vector3< uint_t > >("generationPointOfReferenceOffset");
+   const bool useParticleOffset = numericalSetup.getParameter< bool >("useParticleOffset");
+   const Vector3< uint_t > particleSubBlockSize =
+      numericalSetup.getParameter< Vector3< uint_t > >("particleSubBlockSize");
+   const real_t uInflow        = numericalSetup.getParameter< real_t >("uInflow");
+   const real_t relaxationRate = numericalSetup.getParameter< real_t >("relaxationRate");
+   if ((periodicInY && numYBlocks == 1) || (periodicInZ && numZBlocks == 1))
+   {
+      WALBERLA_LOG_WARNING_ON_ROOT("Using only 1 block in periodic dimensions can lead to unexpected behavior.")
+   }
+   const real_t viscosity = lbm::collision_model::viscosityFromOmega(relaxationRate);
+   WALBERLA_LOG_DEVEL_VAR_ON_ROOT(viscosity)
+
+   Config::BlockHandle outputSetup      = cfgFile->getBlock("Output");
+   const uint_t vtkSpacing              = outputSetup.getParameter< uint_t >("vtkSpacing");
+   const std::string vtkFolder          = outputSetup.getParameter< std::string >("vtkFolder");
+   const uint_t performanceLogFrequency = outputSetup.getParameter< uint_t >("performanceLogFrequency");
+
+   ///////////////////////////
+   // BLOCK STRUCTURE SETUP //
+   ///////////////////////////
+
+   const bool periodicInX                     = false;
+   shared_ptr< StructuredBlockForest > blocks = blockforest::createUniformBlockGrid(
+      numXBlocks, numYBlocks, numZBlocks, numXCellsPerBlock, numYCellsPerBlock, numZCellsPerBlock, real_t(1), uint_t(0),
+      false, false, periodicInX, periodicInY, periodicInZ, // periodicity
+      false);
+
+   auto simulationDomain = blocks->getDomain();
+
+   ////////////
+   // MesaPD //
+   ////////////
+
+   auto rpdDomain = std::make_shared< mesa_pd::domain::BlockForestDomain >(blocks->getBlockForestPointer());
+
+   // Init data structures
+   auto ps                  = walberla::make_shared< mesa_pd::data::ParticleStorage >(1);
+   auto ss                  = walberla::make_shared< mesa_pd::data::ShapeStorage >();
+   using ParticleAccessor_T = mesa_pd::data::ParticleAccessorWithShape;
+   auto accessor            = walberla::make_shared< ParticleAccessor_T >(ps, ss);
+   auto sphereShape         = ss->create< mesa_pd::data::Sphere >(particleDiameter * real_t(0.5));
+
+   // Create spheres
+   if (useParticles)
+   {
+      // Ensure that generation domain is computed correctly
+      WALBERLA_CHECK_FLOAT_EQUAL(simulationDomain.xMin(), real_t(0));
+      WALBERLA_CHECK_FLOAT_EQUAL(simulationDomain.yMin(), real_t(0));
+      WALBERLA_CHECK_FLOAT_EQUAL(simulationDomain.zMin(), real_t(0));
+
+      auto generationDomain = math::AABB::createFromMinMaxCorner(
+         math::Vector3< real_t >(simulationDomain.xMax() * (real_t(1) - generationDomainFraction[0]) / real_t(2),
+                                 simulationDomain.yMax() * (real_t(1) - generationDomainFraction[1]) / real_t(2),
+                                 simulationDomain.zMax() * (real_t(1) - generationDomainFraction[2]) / real_t(2)),
+         math::Vector3< real_t >(simulationDomain.xMax() * (real_t(1) + generationDomainFraction[0]) / real_t(2),
+                                 simulationDomain.yMax() * (real_t(1) + generationDomainFraction[1]) / real_t(2),
+                                 simulationDomain.zMax() * (real_t(1) + generationDomainFraction[2]) / real_t(2)));
+      real_t particleOffset = particleGenerationSpacing / real_t(2);
+      for (auto pt :
+           grid_generator::SCGrid(generationDomain, generationDomain.center() + generationPointOfReferenceOffset,
+                                  particleGenerationSpacing))
+      {
+         // Offset every second particle layer in flow direction to avoid channels in flow direction
+         if (useParticleOffset &&
+             uint_t(round(math::abs(generationDomain.center()[0] - pt[0]) / (particleGenerationSpacing))) % uint_t(2) !=
+                uint_t(0))
+         {
+            pt = pt + Vector3(real_t(0), particleOffset, particleOffset);
+         }
+         if (rpdDomain->isContainedInProcessSubdomain(uint_c(mpi::MPIManager::instance()->rank()), pt))
+         {
+            mesa_pd::data::Particle&& p = *ps->create();
+            p.setPosition(pt);
+            p.setInteractionRadius(particleDiameter * real_t(0.5));
+            p.setOwner(mpi::MPIManager::instance()->rank());
+            p.setShapeID(sphereShape);
+            p.setType(0);
+         }
+      }
+   }
+
+   ///////////////////////
+   // ADD DATA TO BLOCKS //
+   ////////////////////////
+
+   // Setting initial PDFs to nan helps to detect bugs in the initialization/BC handling
+   // Depending on WALBERLA_BUILD_WITH_GPU_SUPPORT, pdfFieldCPUGPUID is either a CPU or a CPU field
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   BlockDataID pdfFieldID =
+      field::addToStorage< PdfField_T >(blocks, "pdf field (fzyx)", real_c(std::nan("")), field::fzyx);
+   BlockDataID BFieldID         = field::addToStorage< BField_T >(blocks, "B field", 0, field::fzyx, 1);
+   BlockDataID pdfFieldCPUGPUID = gpu::addGPUFieldToStorage< PdfField_T >(blocks, pdfFieldID, "pdf field GPU");
+#else
+   BlockDataID pdfFieldCPUGPUID =
+      field::addToStorage< PdfField_T >(blocks, "pdf field CPU", real_c(std::nan("")), field::fzyx);
+#endif
+   BlockDataID densityFieldID = field::addToStorage< DensityField_T >(blocks, "density field", real_t(0), field::fzyx);
+   BlockDataID velFieldID  = field::addToStorage< VelocityField_T >(blocks, "velocity field", real_t(0), field::fzyx);
+   BlockDataID flagFieldID = field::addFlagFieldToStorage< FlagField_T >(blocks, "flag field");
+
+   // Synchronize particles between the blocks for the correct mapping of ghost particles
+   mesa_pd::mpi::SyncNextNeighbors syncNextNeighborFunc;
+   syncNextNeighborFunc(*ps, *rpdDomain);
+
+   // Assemble boundary block string
+   std::string boundariesBlockString = " Boundaries"
+                                       "{"
+                                       "Border { direction W;    walldistance -1;  flag Inflow; }"
+                                       "Border { direction E;    walldistance -1;  flag Density; }";
+
+   if (!periodicInY)
+   {
+      boundariesBlockString += "Border { direction S;    walldistance -1;  flag NoSlip; }"
+                               "Border { direction N;    walldistance -1;  flag NoSlip; }";
+   }
+
+   if (!periodicInZ)
+   {
+      boundariesBlockString += "Border { direction T;    walldistance -1;  flag NoSlip; }"
+                               "Border { direction B;    walldistance -1;  flag NoSlip; }";
+   }
+
+   boundariesBlockString += "}";
+   WALBERLA_ROOT_SECTION()
+   {
+      std::ofstream boundariesFile("boundaries.prm");
+      boundariesFile << boundariesBlockString;
+      boundariesFile.close();
+   }
+   WALBERLA_MPI_BARRIER()
+
+   auto boundariesCfgFile = Config();
+   boundariesCfgFile.readParameterFile("boundaries.prm");
+   auto boundariesConfig = boundariesCfgFile.getBlock("Boundaries");
+
+   // map boundaries into the LBM simulation
+   geometry::initBoundaryHandling< FlagField_T >(*blocks, flagFieldID, boundariesConfig);
+   geometry::setNonBoundaryCellsToDomain< FlagField_T >(*blocks, flagFieldID, Fluid_Flag);
+   lbm::PSM_Density density_bc(blocks, pdfFieldCPUGPUID, real_t(1.0));
+   density_bc.fillFromFlagField< FlagField_T >(blocks, flagFieldID, Density_Flag, Fluid_Flag);
+   lbm::PSM_NoSlip noSlip(blocks, pdfFieldCPUGPUID);
+   noSlip.fillFromFlagField< FlagField_T >(blocks, flagFieldID, NoSlip_Flag, Fluid_Flag);
+   lbm::PSM_UBB ubb(blocks, pdfFieldCPUGPUID, uInflow, real_t(0), real_t(0));
+   ubb.fillFromFlagField< FlagField_T >(blocks, flagFieldID, Inflow_Flag, Fluid_Flag);
+
+   ///////////////
+   // TIME LOOP //
+   ///////////////
+
+   // Map particles into the fluid domain
+   ParticleAndVolumeFractionSoA_T< Weighting > particleAndVolumeFractionSoA(blocks, relaxationRate);
+   PSMSweepCollection psmSweepCollection(blocks, accessor, lbm_mesapd_coupling::RegularParticlesSelector(),
+                                         particleAndVolumeFractionSoA, particleSubBlockSize);
+   if (useParticles)
+   {
+      for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+      {
+         psmSweepCollection.particleMappingSweep(&(*blockIt));
+      }
+   }
+
+   // Initialize PDFs
+   pystencils::InitializeDomainForPSM pdfSetter(
+      particleAndVolumeFractionSoA.BsFieldID, particleAndVolumeFractionSoA.BFieldID,
+      particleAndVolumeFractionSoA.particleVelocitiesFieldID, pdfFieldCPUGPUID, real_t(0), real_t(0), real_t(0),
+      real_t(1.0), real_t(0), real_t(0), real_t(0));
+
+   for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+   {
+      // pdfSetter requires particle velocities at cell centers
+      if (useParticles) { psmSweepCollection.setParticleVelocitiesSweep(&(*blockIt)); }
+      pdfSetter(&(*blockIt));
+   }
+
+   // Setup of the LBM communication for synchronizing the pdf field between neighboring blocks
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   gpu::communication::UniformGPUScheme< Stencil_T > com(blocks, sendDirectlyFromGPU, false);
+#else
+   walberla::blockforest::communication::UniformBufferedScheme< Stencil_T > com(blocks);
+#endif
+   com.addPackInfo(make_shared< PackInfo_T >(pdfFieldCPUGPUID));
+   auto communication = std::function< void() >([&]() { com.communicate(); });
+
+   SweepTimeloop commTimeloop(blocks->getBlockStorage(), timeSteps);
+   SweepTimeloop timeloop(blocks->getBlockStorage(), timeSteps);
+
+   timeloop.addFuncBeforeTimeStep(RemainingTimeLogger(timeloop.getNrOfTimeSteps()), "Remaining Time Logger");
+
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   pystencils::PSM_MacroGetter getterSweep(BFieldID, densityFieldID, pdfFieldID, velFieldID, real_t(0.0), real_t(0.0),
+                                           real_t(0.0));
+#else
+   pystencils::PSM_MacroGetter getterSweep(particleAndVolumeFractionSoA.BFieldID, densityFieldID, pdfFieldCPUGPUID,
+                                           velFieldID, real_t(0.0), real_t(0.0), real_t(0.0));
+#endif
+   // VTK output
+   if (vtkSpacing != uint_t(0))
+   {
+      // Spheres
+      auto particleVtkOutput = make_shared< mesa_pd::vtk::ParticleVtkOutput >(ps);
+      particleVtkOutput->addOutput< mesa_pd::data::SelectParticleUid >("uid");
+      particleVtkOutput->addOutput< mesa_pd::data::SelectParticleLinearVelocity >("velocity");
+      particleVtkOutput->addOutput< mesa_pd::data::SelectParticleInteractionRadius >("radius");
+      // Limit output to process-local spheres
+      particleVtkOutput->setParticleSelector([sphereShape](const mesa_pd::data::ParticleStorage::iterator& pIt) {
+         return pIt->getShapeID() == sphereShape &&
+                !(mesa_pd::data::particle_flags::isSet(pIt->getFlags(), mesa_pd::data::particle_flags::GHOST));
+      });
+      auto particleVtkWriter = vtk::createVTKOutput_PointData(particleVtkOutput, "particles", vtkSpacing, vtkFolder);
+      timeloop.addFuncBeforeTimeStep(vtk::writeFiles(particleVtkWriter), "VTK (sphere data)");
+
+      // Fields
+      auto pdfFieldVTK = vtk::createVTKOutput_BlockData(blocks, "fluid", vtkSpacing, 0, false, vtkFolder);
+
+      pdfFieldVTK->addBeforeFunction(communication);
+
+      pdfFieldVTK->addBeforeFunction([&]() {
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+         gpu::fieldCpy< PdfField_T, gpu::GPUField< real_t > >(blocks, pdfFieldID, pdfFieldCPUGPUID);
+         gpu::fieldCpy< GhostLayerField< real_t, 1 >, BFieldGPU_T >(blocks, BFieldID,
+                                                                    particleAndVolumeFractionSoA.BFieldID);
+#endif
+         for (auto& block : *blocks)
+            getterSweep(&block);
+      });
+
+      pdfFieldVTK->addCellDataWriter(make_shared< field::VTKWriter< VelocityField_T > >(velFieldID, "Velocity"));
+      pdfFieldVTK->addCellDataWriter(make_shared< field::VTKWriter< DensityField_T > >(densityFieldID, "Density"));
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+      pdfFieldVTK->addCellDataWriter(make_shared< field::VTKWriter< BField_T > >(BFieldID, "OverlapFraction"));
+#else
+      pdfFieldVTK->addCellDataWriter(
+         make_shared< field::VTKWriter< BField_T > >(particleAndVolumeFractionSoA.BFieldID, "OverlapFraction"));
+#endif
+      pdfFieldVTK->addCellDataWriter(make_shared< field::VTKWriter< FlagField_T > >(flagFieldID, "FlagField"));
+
+      timeloop.addFuncBeforeTimeStep(vtk::writeFiles(pdfFieldVTK), "VTK (fluid field data)");
+   }
+
+   if (vtkSpacing != uint_t(0)) { vtk::writeDomainDecomposition(blocks, "domain_decomposition", vtkFolder); }
+
+   // Add performance logging
+   lbm::PerformanceLogger< FlagField_T > performanceLogger(blocks, flagFieldID, Fluid_Flag, performanceLogFrequency);
+   if (performanceLogFrequency > 0)
+   {
+      timeloop.addFuncAfterTimeStep(performanceLogger, "Evaluate performance logging");
+   }
+
+   // Add LBM communication function and boundary handling sweep
+   if (useCommunicationHiding)
+   {
+      timeloop.add() << Sweep(deviceSyncWrapper(density_bc.getSweep()), "Boundary Handling (Density)");
+   }
+   else
+   {
+      timeloop.add() << BeforeFunction(communication, "LBM Communication")
+                     << Sweep(deviceSyncWrapper(density_bc.getSweep()), "Boundary Handling (Density)");
+   }
+   timeloop.add() << Sweep(deviceSyncWrapper(ubb.getSweep()), "Boundary Handling (UBB)");
+   if (!periodicInY || !periodicInZ)
+   {
+      timeloop.add() << Sweep(deviceSyncWrapper(noSlip.getSweep()), "Boundary Handling (NoSlip)");
+   }
+
+   // PSM kernel
+   pystencils::PSMSweep PSMSweep(particleAndVolumeFractionSoA.BsFieldID, particleAndVolumeFractionSoA.BFieldID,
+                                 particleAndVolumeFractionSoA.particleForcesFieldID,
+                                 particleAndVolumeFractionSoA.particleVelocitiesFieldID, pdfFieldCPUGPUID, real_t(0.0),
+                                 real_t(0.0), real_t(0.0), relaxationRate);
+   pystencils::PSMSweepSplit PSMSplitSweep(
+      particleAndVolumeFractionSoA.BsFieldID, particleAndVolumeFractionSoA.BFieldID,
+      particleAndVolumeFractionSoA.particleForcesFieldID, particleAndVolumeFractionSoA.particleVelocitiesFieldID,
+      pdfFieldCPUGPUID, real_t(0.0), real_t(0.0), real_t(0.0), relaxationRate, frameWidth);
+   pystencils::LBMSweep LBMSweep(pdfFieldCPUGPUID, real_t(0.0), real_t(0.0), real_t(0.0), relaxationRate);
+   pystencils::LBMSplitSweep LBMSplitSweep(pdfFieldCPUGPUID, real_t(0.0), real_t(0.0), real_t(0.0), relaxationRate,
+                                           frameWidth);
+
+   if (useParticles)
+   {
+      if (useCommunicationHiding)
+      {
+         addPSMSweepsToTimeloops(commTimeloop, timeloop, com, psmSweepCollection, PSMSplitSweep);
+      }
+      else { addPSMSweepsToTimeloop(timeloop, psmSweepCollection, PSMSweep); }
+   }
+   else
+   {
+      if (useCommunicationHiding)
+      {
+         commTimeloop.add() << BeforeFunction([&]() { com.startCommunication(); }, "LBM Communication (start)")
+                            << Sweep(deviceSyncWrapper(LBMSplitSweep.getInnerSweep()), "LBM inner sweep")
+                            << AfterFunction([&]() { com.wait(); }, "LBM Communication (wait)");
+         timeloop.add() << Sweep(deviceSyncWrapper(LBMSplitSweep.getOuterSweep()), "LBM outer sweep");
+      }
+      else { timeloop.add() << Sweep(deviceSyncWrapper(LBMSweep), "LBM sweep"); }
+   }
+
+   WcTimingPool timeloopTiming;
+   // TODO: maybe add warmup phase
+   for (uint_t timeStep = 0; timeStep < timeSteps; ++timeStep)
+   {
+      if (useCommunicationHiding) { commTimeloop.singleStep(timeloopTiming); }
+      timeloop.singleStep(timeloopTiming);
+   }
+   timeloopTiming.logResultOnRoot();
+   auto timeloopTimingReduced = timeloopTiming.getReduced();
+
+   // Write parameters and performance results in sqlite database
+   WALBERLA_ROOT_SECTION()
+   {
+      // Use DB_FILE environment variable if set
+      std::string dbFile;
+      if (std::getenv("DB_FILE") != nullptr) { dbFile = std::getenv("DB_FILE"); }
+      else
+      {
+         if (useParticles) { dbFile = "percolation_benchmark.sqlite3"; }
+         else { dbFile = "channel_flow_benchmark.sqlite3"; }
+      }
+
+      std::map< std::string, int > integerProperties;
+      std::map< std::string, double > realProperties;
+      std::map< std::string, std::string > stringProperties;
+
+      integerProperties["numXBlocks"]                = int(numXBlocks);
+      integerProperties["numYBlocks"]                = int(numYBlocks);
+      integerProperties["numZBlocks"]                = int(numZBlocks);
+      integerProperties["numXCellsPerBlock"]         = int(numXCellsPerBlock);
+      integerProperties["numYCellsPerBlock"]         = int(numYCellsPerBlock);
+      integerProperties["numZCellsPerBlock"]         = int(numZCellsPerBlock);
+      integerProperties["timeSteps"]                 = int(timeSteps);
+      integerProperties["sendDirectlyFromGPU"]       = int(sendDirectlyFromGPU);
+      integerProperties["useCommunicationHiding"]    = int(useCommunicationHiding);
+      integerProperties["communicationHidingXWidth"] = int(frameWidth[0]);
+      integerProperties["communicationHidingYWidth"] = int(frameWidth[1]);
+      integerProperties["communicationHidingZWidth"] = int(frameWidth[2]);
+      integerProperties["useParticles"]              = int(useParticles);
+      integerProperties["numParticles"]              = int(ps->size());
+      integerProperties["particleSubBlockXSize"]     = int(particleSubBlockSize[0]);
+      integerProperties["particleSubBlockYSize"]     = int(particleSubBlockSize[1]);
+      integerProperties["particleSubBlockZSize"]     = int(particleSubBlockSize[2]);
+
+      realProperties["particleDiameter"]          = double(particleDiameter);
+      realProperties["particleGenerationSpacing"] = double(particleGenerationSpacing);
+
+      performanceLogger.getBestResultsForSQLOnRoot(integerProperties, realProperties, stringProperties);
+
+      auto runId = sqlite::storeRunInSqliteDB(dbFile, integerProperties, stringProperties, realProperties);
+      sqlite::storeTimingPoolInSqliteDB(dbFile, runId, *timeloopTimingReduced, "Timeloop");
+   }
+
+   return EXIT_SUCCESS;
+}
+
+} // namespace percolation
+
+int main(int argc, char** argv) { percolation::main(argc, argv); }
diff --git a/apps/benchmarks/Percolation/benchmark.prm b/apps/benchmarks/Percolation/benchmark.prm
new file mode 100644
index 0000000000000000000000000000000000000000..f401949b546987d81fd55c052f1aa91973c834ad
--- /dev/null
+++ b/apps/benchmarks/Percolation/benchmark.prm
@@ -0,0 +1,42 @@
+
+NumericalSetup
+{
+    // product of number of blocks should be equal to number of used processes
+    numXBlocks 1;
+    numYBlocks 1;
+    numZBlocks 1;
+
+    periodicInY false;
+    periodicInZ false;
+
+    numXCellsPerBlock 256;
+    numYCellsPerBlock 128;
+    numZCellsPerBlock 128;
+
+    timeSteps 100;
+
+    sendDirectlyFromGPU false; // use GPU-GPU communication
+    useCommunicationHiding false;
+    frameWidth <1, 1, 1>; // width of the outer region if splitting the LBM/PSM into inner and outer (only used if useCommunicationHiding is true)
+
+    // particle distribution in LBM units
+    useParticles true; // if true, PSM/particle mapping/velocity computation/hydrodynamic force reduction is used, else LBM is used
+    particleDiameter 20.0;
+    particleGenerationSpacing 21.0;
+    generationDomainFraction <0.8, 1.0, 1.0>; // fraction of the domain where particles are generated
+    generationPointOfReferenceOffset <0, 0, 0>; // offset of point of reference from domain center, see SCIterator.h
+    useParticleOffset true; // offset every second particle layer in flow direction
+    particleSubBlockSize <8, 8, 8>;
+
+    // fluid quantities in LBM units
+    uInflow 0.00008;
+    relaxationRate 0.9;
+}
+
+Output
+{
+    vtkSpacing 0;
+    vtkFolder vtk_out;
+
+    performanceLogFrequency 100;
+}
diff --git a/apps/showcases/CMakeLists.txt b/apps/showcases/CMakeLists.txt
index f601fdd24ebb1454ae518db8405be992f9f0be57..d845f5271c267d684793a6c795c49a558b323cd7 100644
--- a/apps/showcases/CMakeLists.txt
+++ b/apps/showcases/CMakeLists.txt
@@ -7,6 +7,7 @@ add_subdirectory( LightRisingParticleInFluidAMR )
 add_subdirectory( Mixer )
 add_subdirectory( ParticlePacking )
 add_subdirectory( PegIntoSphereBed )
+add_subdirectory( Piping )
 if ( WALBERLA_BUILD_WITH_CODEGEN)
 
    add_subdirectory( Antidunes )
diff --git a/apps/showcases/Piping/CMakeLists.txt b/apps/showcases/Piping/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c86a1ee331e80325e60c529c37cc59c9f8871078
--- /dev/null
+++ b/apps/showcases/Piping/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_subdirectory(setups)
+
+waLBerla_add_executable(NAME SettlingSpheres
+        FILES SettlingSpheres.cpp
+        DEPENDS blockforest core field lbm_mesapd_coupling mesa_pd vtk)
diff --git a/apps/showcases/Piping/SettlingSpheres.cpp b/apps/showcases/Piping/SettlingSpheres.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d2477f0fb0716f2521f271c6eb4c309bed166baa
--- /dev/null
+++ b/apps/showcases/Piping/SettlingSpheres.cpp
@@ -0,0 +1,300 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file   SettlingSpheres.cpp
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//! \brief Based on showcases/Antidunes/BedGeneration.cpp
+//
+//======================================================================================================================
+
+#include "blockforest/Initialization.h"
+
+#include "core/Environment.h"
+#include "core/grid_generator/SCIterator.h"
+#include "core/math/Random.h"
+#include "core/mpi/Reduce.h"
+
+#include "mesa_pd/collision_detection/AnalyticContactDetection.h"
+#include "mesa_pd/data/DataTypes.h"
+#include "mesa_pd/data/LinkedCells.h"
+#include "mesa_pd/data/ParticleAccessorWithBaseShape.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/shape/Sphere.h"
+#include "mesa_pd/domain/BlockForestDomain.h"
+#include "mesa_pd/kernel/AssocToBlock.h"
+#include "mesa_pd/kernel/DoubleCast.h"
+#include "mesa_pd/kernel/InsertParticleIntoLinkedCells.h"
+#include "mesa_pd/kernel/LinearSpringDashpot.h"
+#include "mesa_pd/kernel/ParticleSelector.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
+#include "mesa_pd/mpi/ContactFilter.h"
+#include "mesa_pd/mpi/ReduceContactHistory.h"
+#include "mesa_pd/mpi/ReduceProperty.h"
+#include "mesa_pd/mpi/SyncNextNeighbors.h"
+#include "mesa_pd/mpi/notifications/ForceTorqueNotification.h"
+#include "mesa_pd/vtk/ParticleVtkOutput.h"
+
+#include "vtk/VTKOutput.h"
+
+#include "utility/ParticleUtility.h"
+
+namespace walberla
+{
+namespace piping
+{
+
+using namespace mesa_pd;
+
+int main(int argc, char** argv)
+{
+   Environment env(argc, argv);
+   walberla::mpi::MPIManager::instance()->useWorldComm();
+
+   // Config
+   auto cfg = env.config();
+   if (cfg == nullptr) WALBERLA_ABORT("No config specified!");
+   WALBERLA_LOG_INFO_ON_ROOT(*cfg);
+   const Config::BlockHandle bedGenerationConf = cfg->getBlock("BedGeneration");
+
+   const Vec3 domainSize_SI    = bedGenerationConf.getParameter< Vec3 >("domainSize_SI");
+   const Vector3< int > blocks = bedGenerationConf.getParameter< Vector3< int > >("blocks");
+   WALBERLA_CHECK_EQUAL(blocks[0] * blocks[1] * blocks[2], uint_t(MPIManager::instance()->numProcesses()),
+                        "The number of blocks (" << blocks[0] * blocks[1] * blocks[2]
+                                                 << ") has to match the number of MPI processes ("
+                                                 << uint_t(MPIManager::instance()->numProcesses()) << ")");
+   const bool periodicInX = bedGenerationConf.getParameter< bool >("periodicInX");
+   const bool periodicInY = bedGenerationConf.getParameter< bool >("periodicInY");
+   if ((periodicInX && blocks[0] == 1) || (periodicInY && blocks[1] == 1))
+   {
+      WALBERLA_ABORT("The number of blocks in periodic dimensions must be greater than 1.")
+   }
+   const real_t minDiameter_SI         = bedGenerationConf.getParameter< real_t >("minDiameter_SI");
+   const real_t maxDiameter_SI         = bedGenerationConf.getParameter< real_t >("maxDiameter_SI");
+   const real_t gravity_SI             = bedGenerationConf.getParameter< real_t >("gravity_SI");
+   const real_t densityFluid_SI        = bedGenerationConf.getParameter< real_t >("densityFluid_SI");
+   const real_t densityParticle_SI     = bedGenerationConf.getParameter< real_t >("densityParticle_SI");
+   const real_t generationSpacing_SI   = bedGenerationConf.getParameter< real_t >("generationSpacing_SI");
+   const real_t initialVelocity_SI     = bedGenerationConf.getParameter< real_t >("initialVelocity_SI");
+   const real_t dt_SI                  = bedGenerationConf.getParameter< real_t >("dt_SI");
+   const real_t frictionCoefficient    = bedGenerationConf.getParameter< real_t >("frictionCoefficient");
+   const real_t restitutionCoefficient = bedGenerationConf.getParameter< real_t >("restitutionCoefficient");
+   const real_t collisionTime_SI       = bedGenerationConf.getParameter< real_t >("collisionTime_SI");
+   const real_t poissonsRatio          = bedGenerationConf.getParameter< real_t >("poissonsRatio");
+   const uint_t timeSteps              = bedGenerationConf.getParameter< uint_t >("timeSteps");
+   const uint_t visSpacing             = bedGenerationConf.getParameter< uint_t >("visSpacing");
+   const std::string outFileName       = bedGenerationConf.getParameter< std::string >("outFileName");
+
+   bool useOpenMP = false;
+
+   // BlockForest
+   const math::AABB simulationDomain_SI(real_t(0.0), real_t(0.0), real_t(0.0), domainSize_SI[0], domainSize_SI[1],
+                                        domainSize_SI[2]);
+   const Vector3< bool > isPeriodic{ periodicInX, periodicInY, false };
+
+   shared_ptr< BlockForest > forest = blockforest::createBlockForest(simulationDomain_SI, blocks, isPeriodic);
+   auto domain                      = std::make_shared< mesa_pd::domain::BlockForestDomain >(forest);
+
+   // MesaPD data structures
+   auto ps = std::make_shared< data::ParticleStorage >(1);
+   data::ParticleAccessorWithBaseShape accessor(ps);
+
+   // Init spheres
+   // Use offset to domain boundary to prevent the spheres from touching in the beginning
+   const real_t domainOffset = maxDiameter_SI / real_t(2);
+   const math::AABB generationDomain_SI(
+      simulationDomain_SI.xMin() + domainOffset, simulationDomain_SI.yMin() + domainOffset,
+      simulationDomain_SI.zMin() + domainOffset, simulationDomain_SI.xMax() - domainOffset,
+      simulationDomain_SI.yMax() - domainOffset, simulationDomain_SI.zMax() - domainOffset);
+   math::seedRandomGenerator(42);
+
+   for (auto pt :
+        grid_generator::SCGrid(generationDomain_SI, Vec3(generationSpacing_SI) * real_c(0.5), generationSpacing_SI))
+   {
+      auto diameter = math::realRandom< real_t >(minDiameter_SI, maxDiameter_SI);
+
+      if (!domain->isContainedInLocalSubdomain(pt, real_t(0))) continue;
+      auto p                       = ps->create();
+      p->getPositionRef()          = pt;
+      p->getInteractionRadiusRef() = diameter * real_t(0.5);
+      p->getBaseShapeRef()         = std::make_shared< data::Sphere >(p->getInteractionRadius());
+      p->getBaseShapeRef()->updateMassAndInertia(densityParticle_SI);
+
+      p->setLinearVelocity(Vec3(real_t(0.1) * math::realRandom(-initialVelocity_SI, initialVelocity_SI),
+                                real_t(0.1) * math::realRandom(-initialVelocity_SI, initialVelocity_SI),
+                                -initialVelocity_SI));
+      p->getOwnerRef() = walberla::mpi::MPIManager::instance()->rank();
+      p->getTypeRef()  = 0;
+   }
+
+   uint_t numParticles = ps->size();
+   walberla::mpi::reduceInplace(numParticles, walberla::mpi::SUM);
+
+   createPlane(*ps, simulationDomain_SI.minCorner(), Vec3(real_t(0), real_t(0), real_t(1)));
+   createPlane(*ps, simulationDomain_SI.maxCorner(), Vec3(real_t(0), real_t(0), real_t(-1)));
+
+   if (!isPeriodic[0])
+   {
+      createPlane(*ps, simulationDomain_SI.minCorner(), Vector3< real_t >(1, 0, 0));
+      createPlane(*ps, simulationDomain_SI.maxCorner(), Vector3< real_t >(-1, 0, 0));
+   }
+   if (!isPeriodic[1])
+   {
+      createPlane(*ps, simulationDomain_SI.minCorner(), Vector3< real_t >(0, 1, 0));
+      createPlane(*ps, simulationDomain_SI.maxCorner(), Vector3< real_t >(0, -1, 0));
+   }
+
+   // VTK
+   auto vtkDomainOutput = walberla::vtk::createVTKOutput_DomainDecomposition(forest, "domain_decomposition", 1,
+                                                                             "vtk_settling_spheres", "simulation_step");
+   if (visSpacing > 0) { vtkDomainOutput->write(); }
+
+   auto particleVtkOutput = make_shared< mesa_pd::vtk::ParticleVtkOutput >(ps);
+   particleVtkOutput->addOutput< mesa_pd::data::SelectParticleLinearVelocity >("velocity");
+   particleVtkOutput->addOutput< mesa_pd::data::SelectParticleInteractionRadius >("radius");
+   particleVtkOutput->setParticleSelector([](const data::ParticleStorage::iterator& pIt) {
+      using namespace walberla::mesa_pd::data::particle_flags;
+      return (pIt->getBaseShape()->getShapeType() == data::Sphere::SHAPE_TYPE) && !isSet(pIt->getFlags(), GHOST);
+   });
+   auto vtkWriter = walberla::vtk::createVTKOutput_PointData(particleVtkOutput, "Particles", 1, "vtk_settling_spheres",
+                                                             "simulation_step", false, false);
+
+   // Init kernels
+   mesa_pd::kernel::VelocityVerletPreForceUpdate vvIntegratorPreForce(dt_SI);
+   mesa_pd::kernel::VelocityVerletPostForceUpdate vvIntegratorPostForce(dt_SI);
+   kernel::LinearSpringDashpot dem(2);
+   dem.setFrictionCoefficientDynamic(0, 0, frictionCoefficient);
+   // Use friction between spheres and planes to speed up the settling
+   dem.setFrictionCoefficientDynamic(0, 1, frictionCoefficient);
+   real_t kappa = real_t(2) * (real_t(1) - poissonsRatio) / (real_t(2) - poissonsRatio); // from Thornton et al
+
+   kernel::AssocToBlock assoc(forest);
+   mesa_pd::mpi::ReduceProperty RP;
+   mesa_pd::mpi::ReduceContactHistory reduceAndSwapContactHistory;
+   mesa_pd::mpi::SyncNextNeighbors SNN;
+
+   ps->forEachParticle(useOpenMP, kernel::SelectLocal(), accessor, assoc, accessor);
+
+   // initial sync
+   SNN(*ps, *domain);
+
+   real_t linkedCellWidth = 1.01_r * maxDiameter_SI;
+   data::LinkedCells linkedCells(domain->getUnionOfLocalAABBs().getExtended(linkedCellWidth), linkedCellWidth);
+   kernel::InsertParticleIntoLinkedCells ipilc;
+
+   WcTimer timer;
+   WcTimingPool timeloopTiming;
+   timer.start();
+   for (uint_t i = 0; i < timeSteps; ++i)
+   {
+      if (visSpacing > 0 && i % visSpacing == 0) { vtkWriter->write(); }
+
+      timeloopTiming["RPD forEachParticle assoc"].start();
+      ps->forEachParticle(useOpenMP, kernel::SelectLocal(), accessor, assoc, accessor);
+      timeloopTiming["RPD forEachParticle assoc"].end();
+
+      timeloopTiming["RPD forEachParticle vvIntegratorPreForce"].start();
+      ps->forEachParticle(useOpenMP, kernel::SelectLocal(), accessor, vvIntegratorPreForce, accessor);
+      timeloopTiming["RPD forEachParticle vvIntegratorPreForce"].end();
+
+      timeloopTiming["SNN"].start();
+      SNN(*ps, *domain);
+      timeloopTiming["SNN"].end();
+
+      // gravity - buoyancy
+      timeloopTiming["RPD forEachParticle addGravitationalForce"].start();
+      ps->forEachParticle(
+         useOpenMP, kernel::SelectLocal(), accessor,
+         [densityParticle_SI, densityFluid_SI, gravity_SI](const size_t idx, auto& ac) {
+            mesa_pd::addForceAtomic(
+               idx, ac, Vec3(0, 0, -(densityParticle_SI - densityFluid_SI) * ac.getVolume(idx) * gravity_SI));
+         },
+         accessor);
+      timeloopTiming["RPD forEachParticle addGravitationalForce"].end();
+
+      timeloopTiming["RPD linkedCells.clear"].start();
+      linkedCells.clear();
+      timeloopTiming["RPD linkedCells.clear"].end();
+      timeloopTiming["RPD forEachParticle ipilc"].start();
+      ps->forEachParticle(useOpenMP, kernel::SelectAll(), accessor, ipilc, accessor, linkedCells);
+      timeloopTiming["RPD forEachParticle ipilc"].end();
+      timeloopTiming["RPD forEachParticlePairHalf dem"].start();
+      linkedCells.forEachParticlePairHalf(
+         useOpenMP, kernel::ExcludeInfiniteInfinite(), accessor,
+         [restitutionCoefficient, collisionTime_SI, kappa, domain, &dem, dt_SI](const size_t idx1, const size_t idx2,
+                                                                                auto& ac) {
+            kernel::DoubleCast double_cast;
+            mesa_pd::mpi::ContactFilter contact_filter;
+            collision_detection::AnalyticContactDetection acd;
+
+            if (double_cast(idx1, idx2, ac, acd, ac))
+            {
+               if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), *domain))
+               {
+                  auto meff = real_t(1) / (ac.getInvMass(idx1) + ac.getInvMass(idx2));
+                  dem.setStiffnessAndDamping(ac.getType(idx1), ac.getType(idx2), restitutionCoefficient,
+                                             collisionTime_SI, kappa, meff);
+                  dem(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), acd.getContactNormal(),
+                      acd.getPenetrationDepth(), dt_SI);
+               }
+            }
+         },
+         accessor);
+      timeloopTiming["RPD forEachParticlePairHalf dem"].end();
+
+      timeloopTiming["RPD reduceProperty reduceAndSwapContactHistory"].start();
+      reduceAndSwapContactHistory(*ps);
+      timeloopTiming["RPD reduceProperty reduceAndSwapContactHistory"].end();
+
+      timeloopTiming["RPD reduceProperty ForceTorqueNotification"].start();
+      RP.operator()< ForceTorqueNotification >(*ps);
+      timeloopTiming["RPD reduceProperty ForceTorqueNotification"].end();
+
+      timeloopTiming["RPD forEachParticle vvIntegratorPostForce"].start();
+      ps->forEachParticle(useOpenMP, kernel::SelectLocal(), accessor, vvIntegratorPostForce, accessor);
+      timeloopTiming["RPD forEachParticle vvIntegratorPostForce"].end();
+
+      // Log particle velocities every 10% of progress. Turn logging off for benchmark run (i.e., no vtk output).
+      if (i % (timeSteps / uint_t(10)) == 0 && visSpacing != 0)
+      {
+         real_t maxVelocity;
+         real_t averageVelocity;
+         uint_t numAveragedParticles;
+
+         getParticleVelocities(accessor, numAveragedParticles, maxVelocity, averageVelocity);
+         WALBERLA_LOG_INFO_ON_ROOT("Timestep " << i << " / " << timeSteps << ", average velocity = " << averageVelocity
+                                               << ", max velocity = " << maxVelocity
+                                               << ", #particles = " << numAveragedParticles);
+      }
+   }
+   timer.end();
+
+   auto timer_reduced = walberla::timing::getReduced(timer, timing::REDUCE_TOTAL, 0);
+   WALBERLA_ROOT_SECTION()
+   {
+      WALBERLA_LOG_INFO_ON_ROOT(*timer_reduced);
+      real_t PUpS = real_t(numParticles) * real_t(timeSteps) / real_t(timer_reduced->max());
+      WALBERLA_LOG_INFO_ON_ROOT("PUpS: " << PUpS);
+   }
+
+   timeloopTiming.logResultOnRoot();
+
+   writeSphereInformationToFile(outFileName, *ps, domainSize_SI);
+
+   return EXIT_SUCCESS;
+}
+} // namespace piping
+} // namespace walberla
+
+int main(int argc, char** argv) { return walberla::piping::main(argc, argv); }
diff --git a/apps/showcases/Piping/setups/CMakeLists.txt b/apps/showcases/Piping/setups/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..77d65d222e15d50cbd546143cb68672ae8483946
--- /dev/null
+++ b/apps/showcases/Piping/setups/CMakeLists.txt
@@ -0,0 +1 @@
+waLBerla_link_files_to_builddir(*.prm)
diff --git a/apps/showcases/Piping/setups/SettlingSpheres_Fukumoto.prm b/apps/showcases/Piping/setups/SettlingSpheres_Fukumoto.prm
new file mode 100644
index 0000000000000000000000000000000000000000..92a9ac319fd303fa95d4293411a6050a4c6d80ae
--- /dev/null
+++ b/apps/showcases/Piping/setups/SettlingSpheres_Fukumoto.prm
@@ -0,0 +1,21 @@
+BedGeneration{
+    domainSize_SI < 0.015, 0.003, 0.015 >; // for Fukumoto: < 0.2, 0.003, 0.133 > (results in bed height of ~0.05)
+    blocks < 3, 3, 1 >;
+    periodicInX false;
+    periodicInY false;
+    minDiameter_SI 0.0005;
+    maxDiameter_SI 0.0006;
+    gravity_SI 9.81;
+    densityFluid_SI 1000;
+    densityParticle_SI 2500;
+    generationSpacing_SI 0.0007;
+    initialVelocity_SI 0.05;
+    dt_SI 5e-5; // decrease dt_SI to get lower particle velocity in the end
+    frictionCoefficient 0.6;
+    restitutionCoefficient 0.1; // artificial low value to decrease the rebound (increase settling speed)
+    collisionTime_SI 5e-4; // time to resolve a collision
+    poissonsRatio 0.22;
+    timeSteps 10000;
+    visSpacing 100;
+    outFileName spheres_out.dat;
+}
diff --git a/apps/showcases/Piping/utility/ParticleUtility.h b/apps/showcases/Piping/utility/ParticleUtility.h
new file mode 100644
index 0000000000000000000000000000000000000000..e9b223efcf8328fa8cef1cb47f24ec647914fbbf
--- /dev/null
+++ b/apps/showcases/Piping/utility/ParticleUtility.h
@@ -0,0 +1,306 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file   ParticleUtility.h
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/mpi/Broadcast.h"
+#include "core/mpi/MPITextFile.h"
+#include "core/mpi/Reduce.h"
+
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/shape/Sphere.h"
+
+namespace walberla
+{
+namespace piping
+{
+
+// Some functions in this file (as the one below) are based on showcases/Antidunes/Utility.cpp
+
+void writeSphereInformationToFile(const std::string& filename, walberla::mesa_pd::data::ParticleStorage& ps,
+                                  const Vector3< real_t >& domainSize, const int precision = 12)
+{
+   std::ostringstream ossData;
+   ossData << std::setprecision(precision);
+
+   WALBERLA_ROOT_SECTION() { ossData << domainSize[0] << " " << domainSize[1] << " " << domainSize[2] << "\n"; }
+
+   for (auto pIt : ps)
+   {
+      using namespace walberla::mesa_pd::data;
+      if (pIt->getBaseShape()->getShapeType() != Sphere::SHAPE_TYPE) continue;
+      using namespace walberla::mesa_pd::data::particle_flags;
+      if (isSet(pIt->getFlags(), GHOST)) continue;
+      auto sp = static_cast< Sphere* >(pIt->getBaseShape().get());
+
+      auto position = pIt->getPosition();
+
+      ossData << position[0] << " " << position[1] << " " << position[2] << " " << sp->getRadius() << '\n';
+   }
+
+   walberla::mpi::writeMPITextFile(filename, ossData.str());
+}
+
+bool sphereBoxOverlap(const mesa_pd::Vec3& spherePosition, const real_t sphereRadius, const mesa_pd::Vec3& boxPosition,
+                      const mesa_pd::Vec3& boxEdgeLength)
+{
+   if ((spherePosition[0] + sphereRadius < boxPosition[0] - boxEdgeLength[0] / real_t(2)) ||
+       (spherePosition[1] + sphereRadius < boxPosition[1] - boxEdgeLength[1] / real_t(2)) ||
+       (spherePosition[2] + sphereRadius < boxPosition[2] - boxEdgeLength[2] / real_t(2)) ||
+       (spherePosition[0] - sphereRadius > boxPosition[0] + boxEdgeLength[0] / real_t(2)) ||
+       (spherePosition[1] - sphereRadius > boxPosition[1] + boxEdgeLength[1] / real_t(2)) ||
+       (spherePosition[2] - sphereRadius > boxPosition[2] + boxEdgeLength[2] / real_t(2)))
+   {
+      return false;
+   }
+   return true;
+}
+
+void initSpheresFromFile(const std::string& fileName, walberla::mesa_pd::data::ParticleStorage& ps,
+                         const walberla::mesa_pd::domain::IDomain& domain, walberla::real_t particleDensity,
+                         math::AABB& simulationDomain, const Vector3< uint_t >& domainSize,
+                         const mesa_pd::Vec3& boxPosition, const mesa_pd::Vec3& boxEdgeLength, real_t& maxDiameter)
+{
+   using namespace walberla::mesa_pd::data;
+
+   auto rank = walberla::mpi::MPIManager::instance()->rank();
+
+   std::string textFile;
+
+   WALBERLA_ROOT_SECTION()
+   {
+      std::ifstream t(fileName.c_str());
+      if (!t) { WALBERLA_ABORT("Invalid input file " << fileName << "\n"); }
+      std::stringstream buffer;
+      buffer << t.rdbuf();
+      textFile = buffer.str();
+   }
+
+   walberla::mpi::broadcastObject(textFile);
+
+   std::istringstream fileIss(textFile);
+   std::string line;
+
+   // first line contains generation domain sizes
+   std::getline(fileIss, line);
+   Vector3< real_t > generationDomainSize_SI(0_r);
+   std::istringstream firstLine(line);
+   firstLine >> generationDomainSize_SI[0] >> generationDomainSize_SI[1] >> generationDomainSize_SI[2];
+   real_t scalingFactor = real_t(domainSize[0]) / generationDomainSize_SI[0];
+   WALBERLA_LOG_DEVEL_VAR_ON_ROOT(generationDomainSize_SI)
+   WALBERLA_LOG_DEVEL_VAR_ON_ROOT(scalingFactor)
+
+   real_t minParticleDiameter = std::numeric_limits< real_t >::max();
+   real_t maxParticleDiameter = real_t(0);
+
+   while (std::getline(fileIss, line))
+   {
+      std::istringstream iss(line);
+
+      ParticleStorage::position_type position;
+      real_t radius;
+      iss >> position[0] >> position[1] >> position[2] >> radius;
+      position *= scalingFactor;
+      radius *= scalingFactor;
+
+      WALBERLA_CHECK(simulationDomain.contains(position),
+                     "Particle read from file is not contained in simulation domain");
+
+      if (!domain.isContainedInProcessSubdomain(uint_c(rank), position)) continue;
+      if (sphereBoxOverlap(position, radius, boxPosition, boxEdgeLength)) continue;
+
+      auto pIt = ps.create();
+      pIt->setPosition(position);
+      pIt->getBaseShapeRef() = std::make_shared< data::Sphere >(radius);
+      pIt->getBaseShapeRef()->updateMassAndInertia(particleDensity);
+      pIt->setInteractionRadius(radius);
+      pIt->setOwner(rank);
+      pIt->setType(0);
+
+      minParticleDiameter = std::min(real_t(2) * radius, minParticleDiameter);
+      maxParticleDiameter = std::max(real_t(2) * radius, maxParticleDiameter);
+
+      WALBERLA_CHECK_EQUAL(iss.tellg(), -1);
+   }
+
+   WALBERLA_MPI_SECTION() { walberla::mpi::allReduceInplace(minParticleDiameter, walberla::mpi::MIN); }
+   WALBERLA_MPI_SECTION() { walberla::mpi::allReduceInplace(maxParticleDiameter, walberla::mpi::MAX); }
+   WALBERLA_LOG_DEVEL_VAR_ON_ROOT(minParticleDiameter)
+   WALBERLA_LOG_DEVEL_VAR_ON_ROOT(maxParticleDiameter)
+   // Maximum particle diameter is used for the size of the linked cells
+   maxDiameter = maxParticleDiameter;
+}
+
+template< typename ParticleAccessor_T >
+void getParticleVelocities(const ParticleAccessor_T& ac, uint_t& numParticles, real_t& maxVelocity,
+                           real_t& averageVelocity)
+{
+   maxVelocity     = real_t(0);
+   averageVelocity = real_t(0);
+   numParticles    = uint_t(0);
+
+   for (uint_t i = 0; i < ac.size(); ++i)
+   {
+      if (isSet(ac.getFlags(i), walberla::mesa_pd::data::particle_flags::GHOST)) continue;
+      if (isSet(ac.getFlags(i), walberla::mesa_pd::data::particle_flags::GLOBAL)) continue;
+
+      ++numParticles;
+      real_t velMagnitude = ac.getLinearVelocity(i).length();
+      maxVelocity         = std::max(maxVelocity, velMagnitude);
+      averageVelocity += velMagnitude;
+   }
+
+   WALBERLA_MPI_SECTION()
+   {
+      walberla::mpi::allReduceInplace(maxVelocity, walberla::mpi::MAX);
+      walberla::mpi::allReduceInplace(averageVelocity, walberla::mpi::SUM);
+      walberla::mpi::allReduceInplace(numParticles, walberla::mpi::SUM);
+   }
+
+   averageVelocity /= real_t(numParticles);
+}
+
+auto createPlane(mesa_pd::data::ParticleStorage& ps, const mesa_pd::Vec3& pos, const mesa_pd::Vec3& normal)
+{
+   auto p0 = ps.create(true);
+   p0->setPosition(pos);
+   p0->setBaseShape(std::make_shared< mesa_pd::data::HalfSpace >(normal));
+   // Mass is set to infinity internally for HalfSpace (independent of the density that is set here)
+   p0->getBaseShapeRef()->updateMassAndInertia(real_t(1));
+   p0->setOwner(walberla::mpi::MPIManager::instance()->rank());
+   p0->setType(1);
+   p0->setInteractionRadius(std::numeric_limits< real_t >::infinity());
+   mesa_pd::data::particle_flags::set(p0->getFlagsRef(), mesa_pd::data::particle_flags::GLOBAL);
+   mesa_pd::data::particle_flags::set(p0->getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
+   mesa_pd::data::particle_flags::set(p0->getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
+   mesa_pd::data::particle_flags::set(p0->getFlagsRef(), mesa_pd::data::particle_flags::NON_COMMUNICATING);
+   return p0;
+}
+
+auto createBox(mesa_pd::data::ParticleStorage& ps, const mesa_pd::Vec3& pos, const mesa_pd::Vec3& edgeLength,
+               const bool movingBucket)
+{
+   auto p0 = ps.create(true);
+   p0->setPosition(pos);
+   p0->setBaseShape(std::make_shared< mesa_pd::data::Box >(edgeLength));
+   if (movingBucket)
+   {
+      // TODO: replace the density of 2.0
+      p0->getBaseShapeRef()->updateMassAndInertia(real_t(2.0));
+   }
+   else
+   {
+      // If the bucket is fixed, its collision behaviour should be the same as for the bounding planes
+      p0->getBaseShapeRef()->updateMassAndInertia(std::numeric_limits< real_t >::infinity());
+   }
+   p0->setOwner(walberla::mpi::MPIManager::instance()->rank());
+   p0->setType(1);
+   p0->setInteractionRadius(std::numeric_limits< real_t >::infinity());
+   mesa_pd::data::particle_flags::set(p0->getFlagsRef(), mesa_pd::data::particle_flags::GLOBAL);
+   mesa_pd::data::particle_flags::set(p0->getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
+   return p0->getUid();
+}
+
+template< typename ParticleAccessor_T, typename Sync_T, typename CollisionResponse_T >
+void settleParticles(const uint_t numTimeSteps, const shared_ptr< ParticleAccessor_T >& accessor,
+                     const shared_ptr< mesa_pd::data::ParticleStorage >& ps,
+                     const walberla::mesa_pd::domain::IDomain& domain, mesa_pd::data::LinkedCells& linkedCells,
+                     Sync_T& syncNextNeighborFunc, CollisionResponse_T& collisionResponse,
+                     const real_t& particleDensityRatio, const real_t& gravitationalAcceleration, const bool& useOpenMP)
+{
+   // Increase the settling speed
+   const real_t timeStepSizeParticles = real_t(10);
+   mesa_pd::kernel::VelocityVerletPreForceUpdate vvIntegratorPreForce(timeStepSizeParticles);
+   mesa_pd::kernel::VelocityVerletPostForceUpdate vvIntegratorPostForce(timeStepSizeParticles);
+   mesa_pd::mpi::ReduceProperty reduceProperty;
+   mesa_pd::mpi::ReduceContactHistory reduceAndSwapContactHistory;
+   mesa_pd::kernel::InsertParticleIntoLinkedCells ipilc;
+
+   WALBERLA_LOG_INFO_ON_ROOT("Starting initial particle settling...")
+
+   for (uint_t t = uint_t(0); t < numTimeSteps; ++t)
+   {
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPreForce, *accessor);
+      syncNextNeighborFunc(*ps, domain);
+
+      linkedCells.clear();
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectAll(), *accessor, ipilc, *accessor, linkedCells);
+
+      // collision
+      linkedCells.forEachParticlePairHalf(
+         useOpenMP, mesa_pd::kernel::ExcludeInfiniteInfinite(), *accessor,
+         [&collisionResponse, &domain, &timeStepSizeParticles](const size_t idx1, const size_t idx2, auto& ac) {
+            mesa_pd::collision_detection::AnalyticContactDetection acd;
+            mesa_pd::kernel::DoubleCast double_cast;
+            mesa_pd::mpi::ContactFilter contact_filter;
+            if (double_cast(idx1, idx2, ac, acd, ac))
+            {
+               if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), domain))
+               {
+                  collisionResponse(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), acd.getContactNormal(),
+                                    acd.getPenetrationDepth(), timeStepSizeParticles);
+               }
+            }
+         },
+         *accessor);
+      reduceAndSwapContactHistory(*ps);
+
+      // gravity - buoyancy
+      ps->forEachParticle(
+         useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor,
+         [particleDensityRatio, gravitationalAcceleration](const size_t idx, auto& ac) {
+            mesa_pd::addForceAtomic(
+               idx, ac,
+               Vector3< real_t >(real_t(0), real_t(0),
+                                 -(particleDensityRatio - real_c(1)) * ac.getVolume(idx) * gravitationalAcceleration));
+         },
+         *accessor);
+
+      reduceProperty.operator()< mesa_pd::ForceTorqueNotification >(*ps);
+
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPostForce, *accessor);
+      syncNextNeighborFunc(*ps, domain);
+
+      if (t % (numTimeSteps / uint_t(10)) == 0)
+      {
+         real_t maxVelocity;
+         real_t averageVelocity;
+         uint_t numAveragedParticles;
+
+         getParticleVelocities(*accessor, numAveragedParticles, maxVelocity, averageVelocity);
+         WALBERLA_LOG_INFO_ON_ROOT("Timestep "
+                                   << t << " / " << numTimeSteps << ", average velocity = " << averageVelocity
+                                   << ", max velocity = " << maxVelocity << ", #particles = " << numAveragedParticles);
+      }
+   }
+
+   // Velocities should be 0 after settling such that the simulation starts from rest
+   ps->forEachParticle(
+      useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor,
+      [](const size_t idx, auto& ac) {
+         ac.setLinearVelocity(idx, Vector3(real_t(0)));
+         ac.setAngularVelocity(idx, Vector3(real_t(0)));
+      },
+      *accessor);
+   syncNextNeighborFunc(*ps, domain);
+}
+
+} // namespace piping
+} // namespace walberla
diff --git a/python/mesa_pd.py b/python/mesa_pd.py
index adbd847e84d7880afa67fd3006a0405c6c8171f0..18c01edbcefb75dc6994fb0a6f6a67fe74dc8465 100755
--- a/python/mesa_pd.py
+++ b/python/mesa_pd.py
@@ -23,6 +23,7 @@ if __name__ == '__main__':
     ps.add_property("invMass", "walberla::real_t", defValue="real_t(1)", syncMode="ON_GHOST_CREATION")
     ps.add_property("force", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="NEVER")
     ps.add_property("oldForce", "walberla::mesa_pd::Vec3", defValue="real_t(0)", syncMode="ON_OWNERSHIP_CHANGE")
+    ps.add_property("charge", "walberla::real_t", defValue="real_t(0)", syncMode="ALWAYS")
 
     # shape definition for cases with small number of different shapes
     ps.add_property("shapeID", "size_t", defValue="", syncMode="ON_GHOST_CREATION")
@@ -70,6 +71,12 @@ if __name__ == '__main__':
                     syncMode="ON_OWNERSHIP_CHANGE")
     ps.add_property("oldHydrodynamicTorque", "walberla::mesa_pd::Vec3", defValue="real_t(0)",
                     syncMode="ON_OWNERSHIP_CHANGE")
+    ps.add_property("electrostaticForce", "walberla::mesa_pd::Vec3", defValue="real_t(0)",
+                    syncMode="ON_OWNERSHIP_CHANGE")
+
+    # Properties for evaluation purposes
+    ps.add_property("totalDisplacement", "walberla::real_t", defValue="real_t(0)", syncMode="ON_OWNERSHIP_CHANGE")
+    ps.add_property("collisionForceNorm", "walberla::real_t", defValue="real_t(0)", syncMode="ON_OWNERSHIP_CHANGE")
 
     # Properties for virtual mass:
     ps.add_property("virtualMass",                  "walberla::real_t",        defValue="real_t(0)",
@@ -150,6 +157,8 @@ if __name__ == '__main__':
     hftn = mpd.add(mpi.PropertyNotification('HydrodynamicForceTorqueNotification'))
     hftn.add_property('hydrodynamicForce', 'mesa_pd::Vec3', 'Vec3(real_t(0))')
     hftn.add_property('hydrodynamicTorque', 'mesa_pd::Vec3', 'Vec3(real_t(0))')
+    eftn = mpd.add(mpi.PropertyNotification('ElectrostaticForceNotification'))
+    eftn.add_property('electrostaticForce', 'mesa_pd::Vec3', 'Vec3(real_t(0))')
     hfn = mpd.add(mpi.PropertyNotification('HeatFluxNotification'))
     hfn.add_property('heatFlux', 'real_t', 'real_t(0)')
     ncn = mpd.add(mpi.PropertyNotification('NumContactNotification'))
diff --git a/python/mesa_pd/templates/common/ParticleFunctions.templ.h b/python/mesa_pd/templates/common/ParticleFunctions.templ.h
index 61629f05909a60069ccb2b91d69bf79c8a034f2f..d90adcf692baa4bf01dc31fee3a9d9d52fcb2b6d 100644
--- a/python/mesa_pd/templates/common/ParticleFunctions.templ.h
+++ b/python/mesa_pd/templates/common/ParticleFunctions.templ.h
@@ -93,19 +93,19 @@ inline void addForceAtomic(const size_t p_idx, Accessor& ac, const Vec3& f)
    #ifdef _OPENMP
    #pragma omp atomic
    #endif
-   {%- endif %};
+   {%- endif %}
    ac.getForceRef(p_idx)[0]  += f[0];
    {%- if module.enableOpenMP %}
    #ifdef _OPENMP
    #pragma omp atomic
    #endif
-   {%- endif %};
+   {%- endif %}
    ac.getForceRef(p_idx)[1]  += f[1];
    {%- if module.enableOpenMP %}
    #ifdef _OPENMP
    #pragma omp atomic
    #endif
-   {%- endif %};
+   {%- endif %}
    ac.getForceRef(p_idx)[2]  += f[2];
 }
 
@@ -117,19 +117,19 @@ inline void addForceAtWFPosAtomic(const size_t p_idx, Accessor& ac, const Vec3&
    #ifdef _OPENMP
    #pragma omp atomic
    #endif
-   {%- endif %};
+   {%- endif %}
    ac.getForceRef(p_idx)[0]  += f[0];
    {%- if module.enableOpenMP %}
    #ifdef _OPENMP
    #pragma omp atomic
    #endif
-   {%- endif %};
+   {%- endif %}
    ac.getForceRef(p_idx)[1]  += f[1];
    {%- if module.enableOpenMP %}
    #ifdef _OPENMP
    #pragma omp atomic
    #endif
-   {%- endif %};
+   {%- endif %}
    ac.getForceRef(p_idx)[2]  += f[2];
 
    const auto t = cross(( wf_pt - ac.getPosition(p_idx) ), f);
@@ -138,19 +138,19 @@ inline void addForceAtWFPosAtomic(const size_t p_idx, Accessor& ac, const Vec3&
    #ifdef _OPENMP
    #pragma omp atomic
    #endif
-   {%- endif %};
+   {%- endif %}
    ac.getTorqueRef(p_idx)[0] += t[0];
    {%- if module.enableOpenMP %}
    #ifdef _OPENMP
    #pragma omp atomic
    #endif
-   {%- endif %};
+   {%- endif %}
    ac.getTorqueRef(p_idx)[1] += t[1];
    {%- if module.enableOpenMP %}
    #ifdef _OPENMP
    #pragma omp atomic
    #endif
-   {%- endif %};
+   {%- endif %}
    ac.getTorqueRef(p_idx)[2] += t[2];
 }
 
@@ -165,19 +165,19 @@ inline void addTorqueAtomic(const size_t p_idx, Accessor& ac, const Vec3& t)
    #ifdef _OPENMP
    #pragma omp atomic
    #endif
-   {%- endif %};
+   {%- endif %}
    ac.getTorqueRef(p_idx)[0]  += t[0];
    {%- if module.enableOpenMP %}
    #ifdef _OPENMP
    #pragma omp atomic
    #endif
-   {%- endif %};
+   {%- endif %}
    ac.getTorqueRef(p_idx)[1]  += t[1];
    {%- if module.enableOpenMP %}
    #ifdef _OPENMP
    #pragma omp atomic
    #endif
-   {%- endif %};
+   {%- endif %}
    ac.getTorqueRef(p_idx)[2]  += t[2];
 }
 
diff --git a/python/mesa_pd/templates/kernel/LinearSpringDashpot.templ.h b/python/mesa_pd/templates/kernel/LinearSpringDashpot.templ.h
index efa4138873bc7c70f41b0ee02a4250fa418e6753..bec0b5bb815243f0e61a6d467d15da904b758488 100644
--- a/python/mesa_pd/templates/kernel/LinearSpringDashpot.templ.h
+++ b/python/mesa_pd/templates/kernel/LinearSpringDashpot.templ.h
@@ -240,7 +240,14 @@ inline void LinearSpringDashpot::operator()(const size_t p_idx1,
       const real_t fTabs( std::min( fTLS.length(), fFrictionAbs) );
       const Vec3   fT   ( fTabs * t );
 
-      //TODO check if tangential spring displacement is same for symmetric case
+      // TODO check if tangential spring displacement is same for symmetric case
+      // TODO: check why exactly this critical section is needed
+      {%- if module.enableOpenMP %}
+      #ifdef _OPENMP
+      #pragma omp critical
+      {
+      #endif
+      {%- endif %}
       auto& ch1 = ac.getNewContactHistoryRef(p_idx1)[ac.getUid(p_idx2)];
       ch1.setTangentialSpringDisplacement(newTangentialSpringDisplacement);
       ch1.setIsSticking(isSticking);
@@ -250,6 +257,11 @@ inline void LinearSpringDashpot::operator()(const size_t p_idx1,
       ch2.setTangentialSpringDisplacement(newTangentialSpringDisplacement);
       ch2.setIsSticking(isSticking);
       ch2.setImpactVelocityMagnitude(impactVelocityMagnitude);
+      {%- if module.enableOpenMP %}
+      #ifdef _OPENMP
+      }
+      #endif
+      {%- endif %}
 
       // Add normal force at contact point
       addForceAtWFPosAtomic( p_idx1, ac,  fN, contactPoint );
diff --git a/src/core/math/extern/exprtk.h b/src/core/math/extern/exprtk.h
index aab5062bacc2510e37cf55418eaaa5b46d0005a3..4f14480b9ff5ea09ba74801c7bba4f301c236d8e 100644
--- a/src/core/math/extern/exprtk.h
+++ b/src/core/math/extern/exprtk.h
@@ -2,14 +2,15 @@
  ******************************************************************
  *           C++ Mathematical Expression Toolkit Library          *
  *                                                                *
- * Author: Arash Partow (1999-2021)                               *
- * URL: http://www.partow.net/programming/exprtk/index.html       *
+ * Author: Arash Partow (1999-2024)                               *
+ * URL: https://www.partow.net/programming/exprtk/index.html      *
  *                                                                *
  * Copyright notice:                                              *
  * Free use of the C++ Mathematical Expression Toolkit Library is *
  * permitted under the guidelines and in accordance with the most *
  * current version of the MIT License.                            *
- * http://www.opensource.org/licenses/MIT                         *
+ * https://www.opensource.org/licenses/MIT                        *
+ * SPDX-License-Identifier: MIT                                   *
  *                                                                *
  * Example expressions:                                           *
  * (00) (y + x / y) * (x - y / x)                                 *
@@ -17,12 +18,12 @@
  * (02) sqrt(1 - (x^2))                                           *
  * (03) 1 - sin(2 * x) + cos(pi / y)                              *
  * (04) a * exp(2 * t) + c                                        *
- * (05) if(((x + 2) == 3) and ((y + 5) <= 9),1 + w, 2 / z)        *
+ * (05) if(((x + 2) == 3) and ((y + 5) <= 9), 1 + w, 2 / z)       *
  * (06) (avg(x,y) <= x + y ? x - y : x * y) + 2 * pi / x          *
  * (07) z := x + sin(2 * pi / y)                                  *
  * (08) u := 2 * (pi * z) / (w := x + cos(y / pi))                *
- * (09) clamp(-1,sin(2 * pi * x) + cos(y / 2 * pi),+1)            *
- * (10) inrange(-2,m,+2) == if(({-2 <= m} and [m <= +2]),1,0)     *
+ * (09) clamp(-1, sin(2 * pi * x) + cos(y / 2 * pi), +1)          *
+ * (10) inrange(-2, m, +2) == if(({-2 <= m} and [m <= +2]), 1, 0) *
  * (11) (2sin(x)cos(2y)7 + 1) == (2 * sin(x) * cos(2*y) * 7 + 1)  *
  * (12) (x ilike 's*ri?g') and [y < (3 z^7 + w)]                  *
  *                                                                *
@@ -38,12 +39,10 @@
 #include <cassert>
 #include <cctype>
 #include <cmath>
-#include <complex>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <deque>
-#include <exception>
 #include <functional>
 #include <iterator>
 #include <limits>
@@ -56,7 +55,7 @@
 #include <utility>
 #include <vector>
 
-//NOLINTBEGIN(modernize-use-nullptr)
+//NOLINTBEGIN(modernize-*,readability-*,performance-no-int-to-ptr,bugprone-empty-catch,bugprone-inc-dec-in-conditions)
 
 namespace exprtk
 {
@@ -69,30 +68,40 @@ namespace exprtk
    #define exprtk_error_location             \
    "exprtk.hpp:" + details::to_str(__LINE__) \
 
-   #if defined(__GNUC__) && (__GNUC__  >= 7)
-
-      #define exprtk_disable_fallthrough_begin                      \
-      _Pragma ("GCC diagnostic push")                               \
-      _Pragma ("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") \
-
-      #define exprtk_disable_fallthrough_end                        \
-      _Pragma ("GCC diagnostic pop")                                \
+   #if __cplusplus >= 201103L
+      #define exprtk_override override
+      #define exprtk_final    final
+      #define exprtk_delete   = delete
+      #define exprtk_default  = default
+   #else
+      #define exprtk_override
+      #define exprtk_final
+      #define exprtk_delete
+      #define exprtk_default  {}
+   #endif
 
+   #if __cplusplus >= 201603L
+      #define exprtk_fallthrough [[fallthrough]];
+   #elif __cplusplus >= 201103L
+      #define exprtk_fallthrough [[gnu::fallthrough]];
    #else
-      #define exprtk_disable_fallthrough_begin (void)0;
-      #define exprtk_disable_fallthrough_end   (void)0;
+      #ifndef _MSC_VER
+      #define exprtk_fallthrough __attribute__ ((fallthrough));
+      #else
+      #define exprtk_fallthrough
+      #endif
    #endif
 
    namespace details
    {
-      typedef unsigned char            uchar_t;
-      typedef char                      char_t;
-      typedef uchar_t*               uchar_ptr;
-      typedef char_t*                 char_ptr;
-      typedef uchar_t const*        uchar_cptr;
+      typedef char                   char_t;
+      typedef char_t*                char_ptr;
       typedef char_t const*          char_cptr;
+      typedef unsigned char          uchar_t;
+      typedef uchar_t*               uchar_ptr;
+      typedef uchar_t const*         uchar_cptr;
       typedef unsigned long long int _uint64_t;
-      typedef long long int           _int64_t;
+      typedef long long int          _int64_t;
 
       inline bool is_whitespace(const char_t c)
       {
@@ -168,7 +177,7 @@ namespace exprtk
 
       inline bool is_valid_string_char(const char_t c)
       {
-         return std::isprint(static_cast<unsigned char>(c)) ||
+         return std::isprint(static_cast<uchar_t>(c)) ||
                 is_whitespace(c);
       }
 
@@ -210,15 +219,15 @@ namespace exprtk
          {
             const std::size_t length = std::min(s1.size(),s2.size());
 
-            for (std::size_t i = 0; i < length;  ++i)
+            for (std::size_t i = 0; i < length; ++i)
             {
-               const char_t c1 = static_cast<char>(std::tolower(s1[i]));
-               const char_t c2 = static_cast<char>(std::tolower(s2[i]));
+               const char_t c1 = static_cast<char_t>(std::tolower(s1[i]));
+               const char_t c2 = static_cast<char_t>(std::tolower(s2[i]));
 
-               if (c1 > c2)
-                  return false;
-               else if (c1 < c2)
+               if (c1 < c2)
                   return true;
+               else if (c2 < c1)
+                  return false;
             }
 
             return s1.size() < s2.size();
@@ -275,21 +284,16 @@ namespace exprtk
 
          std::string result;
 
-         if (i < 0)
-         {
-            for ( ; i; i /= 10)
-            {
-               result += '0' + char(-(i % 10));
-            }
+         const int sign = (i < 0) ? -1 : 1;
 
-            result += '-';
+         for ( ; i; i /= 10)
+         {
+            result += '0' + static_cast<char_t>(sign * (i % 10));
          }
-         else
+
+         if (sign < 0)
          {
-            for ( ; i; i /= 10)
-            {
-               result += '0' + char(i % 10);
-            }
+            result += '-';
          }
 
          std::reverse(result.begin(), result.end());
@@ -302,7 +306,7 @@ namespace exprtk
          return to_str(static_cast<int>(i));
       }
 
-      inline bool is_hex_digit(const std::string::value_type digit)
+      inline bool is_hex_digit(const uchar_t digit)
       {
          return (('0' <= digit) && (digit <= '9')) ||
                 (('A' <= digit) && (digit <= 'F')) ||
@@ -314,12 +318,12 @@ namespace exprtk
          if (('0' <= h) && (h <= '9'))
             return (h - '0');
          else
-            return static_cast<unsigned char>(std::toupper(h) - 'A');
+            return static_cast<uchar_t>(std::toupper(h) - 'A');
       }
 
       template <typename Iterator>
       inline bool parse_hex(Iterator& itr, Iterator end,
-                            std::string::value_type& result)
+                            char_t& result)
       {
          if (
               (end ==  (itr    ))               ||
@@ -361,9 +365,9 @@ namespace exprtk
                }
                else if (parse_hex(itr1, end, *itr2))
                {
-                  itr1+= 4;
-                  itr2+= 1;
-                  removal_count +=4;
+                  itr1 += 4;
+                  itr2 += 1;
+                  removal_count += 4;
                }
                else if ('a' == (*itr1)) { (*itr2++) = '\a'; ++itr1; ++removal_count; }
                else if ('b' == (*itr1)) { (*itr2++) = '\b'; ++itr1; ++removal_count; }
@@ -378,6 +382,7 @@ namespace exprtk
                   (*itr2++) = (*itr1++);
                   ++removal_count;
                }
+
                continue;
             }
             else
@@ -396,7 +401,7 @@ namespace exprtk
       {
       public:
 
-         build_string(const std::size_t& initial_size = 64)
+         explicit build_string(const std::size_t& initial_size = 64)
          {
             data_.reserve(initial_size);
          }
@@ -429,85 +434,85 @@ namespace exprtk
       };
 
       static const std::string reserved_words[] =
-                                  {
-                                    "break",  "case",  "continue",  "default",  "false",  "for",
-                                    "if", "else", "ilike",  "in", "like", "and",  "nand", "nor",
-                                    "not",  "null",  "or",   "repeat", "return",  "shl",  "shr",
-                                    "swap", "switch", "true",  "until", "var",  "while", "xnor",
-                                    "xor", "&", "|"
-                                  };
+      {
+         "assert",  "break", "case", "continue", "const",  "default",
+         "false", "for", "if", "else", "ilike", "in", "like",  "and",
+         "nand",  "nor",  "not",  "null",  "or",  "repeat", "return",
+         "shl",  "shr",  "swap",  "switch",  "true",  "until", "var",
+         "while", "xnor", "xor", "&", "|"
+      };
 
       static const std::size_t reserved_words_size = sizeof(reserved_words) / sizeof(std::string);
 
       static const std::string reserved_symbols[] =
-                                  {
-                                    "abs",  "acos",  "acosh",  "and",  "asin",  "asinh", "atan",
-                                    "atanh", "atan2", "avg",  "break", "case", "ceil",  "clamp",
-                                    "continue",   "cos",   "cosh",   "cot",   "csc",  "default",
-                                    "deg2grad",  "deg2rad",   "equal",  "erf",   "erfc",  "exp",
-                                    "expm1",  "false",   "floor",  "for",   "frac",  "grad2deg",
-                                    "hypot", "iclamp", "if",  "else", "ilike", "in",  "inrange",
-                                    "like",  "log",  "log10", "log2",  "logn",  "log1p", "mand",
-                                    "max", "min",  "mod", "mor",  "mul", "ncdf",  "nand", "nor",
-                                    "not",   "not_equal",   "null",   "or",   "pow",  "rad2deg",
-                                    "repeat", "return", "root", "round", "roundn", "sec", "sgn",
-                                    "shl", "shr", "sin", "sinc", "sinh", "sqrt",  "sum", "swap",
-                                    "switch", "tan",  "tanh", "true",  "trunc", "until",  "var",
-                                    "while", "xnor", "xor", "&", "|"
-                                  };
+      {
+         "abs", "acos",  "acosh", "and",  "asin", "asinh",  "assert",
+         "atan", "atanh",  "atan2", "avg",  "break", "case",  "ceil",
+         "clamp", "continue", "const",  "cos", "cosh", "cot",  "csc",
+         "default",  "deg2grad", "deg2rad",  "equal", "erf",  "erfc",
+         "exp", "expm1", "false", "floor", "for", "frac", "grad2deg",
+         "hypot", "iclamp", "if",  "else", "ilike", "in",  "inrange",
+         "like",  "log",  "log10", "log2",  "logn",  "log1p", "mand",
+         "max", "min",  "mod", "mor",  "mul", "ncdf",  "nand", "nor",
+         "not",   "not_equal",   "null",   "or",   "pow",  "rad2deg",
+         "repeat", "return", "root", "round", "roundn", "sec", "sgn",
+         "shl", "shr", "sin", "sinc", "sinh", "sqrt", "sum",  "swap",
+         "switch", "tan",  "tanh", "true",  "trunc", "until",  "var",
+         "while", "xnor", "xor", "&", "|"
+      };
 
       static const std::size_t reserved_symbols_size = sizeof(reserved_symbols) / sizeof(std::string);
 
       static const std::string base_function_list[] =
-                                  {
-                                    "abs", "acos",  "acosh", "asin",  "asinh", "atan",  "atanh",
-                                    "atan2",  "avg",  "ceil",  "clamp",  "cos",  "cosh",  "cot",
-                                    "csc",  "equal",  "erf",  "erfc",  "exp",  "expm1", "floor",
-                                    "frac", "hypot", "iclamp",  "like", "log", "log10",  "log2",
-                                    "logn", "log1p", "mand", "max", "min", "mod", "mor",  "mul",
-                                    "ncdf",  "pow",  "root",  "round",  "roundn",  "sec", "sgn",
-                                    "sin", "sinc", "sinh", "sqrt", "sum", "swap", "tan", "tanh",
-                                    "trunc",  "not_equal",  "inrange",  "deg2grad",   "deg2rad",
-                                    "rad2deg", "grad2deg"
-                                  };
+      {
+         "abs", "acos",  "acosh", "asin",  "asinh", "atan",  "atanh",
+         "atan2",  "avg",  "ceil",  "clamp",  "cos",  "cosh",  "cot",
+         "csc",  "equal",  "erf",  "erfc",  "exp",  "expm1", "floor",
+         "frac", "hypot", "iclamp",  "like", "log", "log10",  "log2",
+         "logn", "log1p", "mand", "max", "min", "mod", "mor",  "mul",
+         "ncdf",  "pow",  "root",  "round",  "roundn",  "sec", "sgn",
+         "sin", "sinc", "sinh", "sqrt", "sum", "swap", "tan", "tanh",
+         "trunc",  "not_equal",  "inrange",  "deg2grad",   "deg2rad",
+         "rad2deg", "grad2deg"
+      };
 
       static const std::size_t base_function_list_size = sizeof(base_function_list) / sizeof(std::string);
 
       static const std::string logic_ops_list[] =
-                                  {
-                                    "and", "nand", "nor", "not", "or",  "xnor", "xor", "&", "|"
-                                  };
+      {
+         "and", "nand", "nor", "not", "or",  "xnor", "xor", "&", "|"
+      };
 
       static const std::size_t logic_ops_list_size = sizeof(logic_ops_list) / sizeof(std::string);
 
       static const std::string cntrl_struct_list[] =
-                                  {
-                                     "if", "switch", "for", "while", "repeat", "return"
-                                  };
+      {
+         "if", "switch", "for", "while", "repeat", "return"
+      };
 
       static const std::size_t cntrl_struct_list_size = sizeof(cntrl_struct_list) / sizeof(std::string);
 
       static const std::string arithmetic_ops_list[] =
-                                  {
-                                    "+", "-", "*", "/", "%", "^"
-                                  };
+      {
+         "+", "-", "*", "/", "%", "^"
+      };
 
       static const std::size_t arithmetic_ops_list_size = sizeof(arithmetic_ops_list) / sizeof(std::string);
 
       static const std::string assignment_ops_list[] =
-                                  {
-                                    ":=", "+=", "-=",
-                                    "*=", "/=", "%="
-                                  };
+      {
+         ":=", "+=", "-=",
+         "*=", "/=", "%="
+      };
 
       static const std::size_t assignment_ops_list_size = sizeof(assignment_ops_list) / sizeof(std::string);
 
       static const std::string inequality_ops_list[] =
-                                  {
-                                     "<",  "<=", "==",
-                                     "=",  "!=", "<>",
-                                    ">=",  ">"
-                                  };
+      {
+         "<",  "<=", "==",
+         "=",  "!=", "<>",
+         ">=",  ">"
+      };
 
       static const std::size_t inequality_ops_list_size = sizeof(inequality_ops_list) / sizeof(std::string);
 
@@ -598,78 +603,87 @@ namespace exprtk
                              const Iterator data_begin   ,
                              const Iterator data_end     ,
                              const typename std::iterator_traits<Iterator>::value_type& zero_or_more,
-                             const typename std::iterator_traits<Iterator>::value_type& zero_or_one )
+                             const typename std::iterator_traits<Iterator>::value_type& exactly_one )
       {
+         typedef typename std::iterator_traits<Iterator>::value_type type;
+
          const Iterator null_itr(0);
 
-         Iterator d_itr    = data_begin;
-         Iterator p_itr    = pattern_begin;
-         Iterator tb_p_itr = null_itr;
-         Iterator tb_d_itr = null_itr;
+         Iterator p_itr  = pattern_begin;
+         Iterator d_itr  = data_begin;
+         Iterator np_itr = null_itr;
+         Iterator nd_itr = null_itr;
 
-         while (d_itr != data_end)
+         for ( ; ; )
          {
-            if (zero_or_more == *p_itr)
+            if (p_itr != pattern_end)
             {
-               while ((pattern_end != p_itr) && ((zero_or_more == *p_itr) || (zero_or_one == *p_itr)))
-               {
-                  ++p_itr;
-               }
-
-               if (pattern_end == p_itr)
-                  return true;
-
-               const typename std::iterator_traits<Iterator>::value_type c = *(p_itr);
+               const type c = *(p_itr);
 
-               while ((data_end != d_itr) && !Compare::cmp(c,*d_itr))
+               if ((data_end != d_itr) && (Compare::cmp(c,*(d_itr)) || (exactly_one == c)))
                {
                   ++d_itr;
+                  ++p_itr;
+                  continue;
                }
+               else if (zero_or_more == c)
+               {
+                  while ((pattern_end != p_itr) && (zero_or_more == *(p_itr)))
+                  {
+                     ++p_itr;
+                  }
 
-               tb_p_itr = p_itr;
-               tb_d_itr = d_itr;
+                  const type d = *(p_itr);
 
-               continue;
-            }
-            else if (!Compare::cmp(*p_itr, *d_itr) && (zero_or_one != *p_itr))
-            {
-               if (null_itr == tb_d_itr)
-                  return false;
+                  while ((data_end != d_itr) && !(Compare::cmp(d,*(d_itr)) || (exactly_one == d)))
+                  {
+                     ++d_itr;
+                  }
 
-               d_itr = tb_d_itr++;
-               p_itr = tb_p_itr;
+                  // set backtrack iterators
+                  np_itr = p_itr - 1;
+                  nd_itr = d_itr + 1;
 
-               continue;
+                  continue;
+               }
             }
+            else if (data_end == d_itr)
+               break;
 
-            ++p_itr;
-            ++d_itr;
-         }
+            if ((data_end == d_itr) || (null_itr == nd_itr))
+                return false;
 
-         while ((pattern_end != p_itr) && ((zero_or_more == *p_itr) || (zero_or_one == *p_itr)))
-         {
-            ++p_itr;
+            p_itr = np_itr;
+            d_itr = nd_itr;
          }
 
-         return (pattern_end == p_itr);
+         return true;
       }
 
       inline bool wc_match(const std::string& wild_card,
                            const std::string& str)
       {
-         return match_impl<char_cptr,cs_match>(
-                   wild_card.data(), wild_card.data() + wild_card.size(),
-                   str.data(), str.data() + str.size(),
-                   '*', '?');
+         return match_impl<char_cptr,cs_match>
+                (
+                   wild_card.data(),
+                   wild_card.data() + wild_card.size(),
+                   str.data(),
+                   str.data() + str.size(),
+                   '*', '?'
+                );
       }
 
       inline bool wc_imatch(const std::string& wild_card,
                             const std::string& str)
       {
-         return match_impl<char_cptr,cis_match>(
-                   wild_card.data(), wild_card.data() + wild_card.size(),
-                   str.data(), str.data() + str.size(),
-                   '*', '?');
+         return match_impl<char_cptr,cis_match>
+                (
+                   wild_card.data(),
+                   wild_card.data() + wild_card.size(),
+                   str.data(),
+                   str.data() + str.size(),
+                   '*', '?'
+                );
       }
 
       inline bool sequence_match(const std::string& pattern,
@@ -689,19 +703,19 @@ namespace exprtk
          itr_t p_itr = pattern.begin();
          itr_t s_itr = str    .begin();
 
-         itr_t p_end = pattern.end();
-         itr_t s_end = str    .end();
+         const itr_t p_end = pattern.end();
+         const itr_t s_end = str    .end();
 
          while ((s_end != s_itr) && (p_end != p_itr))
          {
             if ('*' == (*p_itr))
             {
-               const char_t target = static_cast<char>(std::toupper(*(p_itr - 1)));
+               const char_t target = static_cast<char_t>(std::toupper(*(p_itr - 1)));
 
                if ('*' == target)
                {
                   diff_index = static_cast<std::size_t>(std::distance(str.begin(),s_itr));
-                  diff_value = static_cast<char>(std::toupper(*p_itr));
+                  diff_value = static_cast<char_t>(std::toupper(*p_itr));
 
                   return false;
                }
@@ -724,7 +738,7 @@ namespace exprtk
                     )
             {
                diff_index = static_cast<std::size_t>(std::distance(str.begin(),s_itr));
-               diff_value = static_cast<char>(std::toupper(*p_itr));
+               diff_value = static_cast<char_t>(std::toupper(*p_itr));
 
                return false;
             }
@@ -742,13 +756,55 @@ namespace exprtk
                 );
       }
 
-      static const double pow10[] = {
-                                      1.0,
-                                      1.0E+001, 1.0E+002, 1.0E+003, 1.0E+004,
-                                      1.0E+005, 1.0E+006, 1.0E+007, 1.0E+008,
-                                      1.0E+009, 1.0E+010, 1.0E+011, 1.0E+012,
-                                      1.0E+013, 1.0E+014, 1.0E+015, 1.0E+016
-                                    };
+      template<typename T>
+      struct set_zero_value_impl
+      {
+         static inline void process(T* base_ptr, const std::size_t size)
+         {
+            const T zero = T(0);
+            for (std::size_t i = 0; i < size; ++i)
+            {
+               base_ptr[i] = zero;
+            }
+         }
+      };
+
+      #define pod_set_zero_value(T)                                      \
+      template <>                                                        \
+      struct set_zero_value_impl<T>                                      \
+      {                                                                  \
+         static inline void process(T* base_ptr, const std::size_t size) \
+         { std::memset(base_ptr, 0x00, size * sizeof(T)); }              \
+      };                                                                 \
+
+      pod_set_zero_value(float      )
+      pod_set_zero_value(double     )
+      pod_set_zero_value(long double)
+
+      #ifdef pod_set_zero_value
+      #undef pod_set_zero_value
+      #endif
+
+      template<typename T>
+      inline void set_zero_value(T* data, const std::size_t size)
+      {
+         set_zero_value_impl<T>::process(data,size);
+      }
+
+      template<typename T>
+      inline void set_zero_value(std::vector<T>& v)
+      {
+         set_zero_value(v.data(),v.size());
+      }
+
+      static const double pow10[] =
+      {
+         1.0,
+         1.0E+001, 1.0E+002, 1.0E+003, 1.0E+004,
+         1.0E+005, 1.0E+006, 1.0E+007, 1.0E+008,
+         1.0E+009, 1.0E+010, 1.0E+011, 1.0E+012,
+         1.0E+013, 1.0E+014, 1.0E+015, 1.0E+016
+      };
 
       static const std::size_t pow10_size = sizeof(pow10) / sizeof(double);
 
@@ -770,37 +826,28 @@ namespace exprtk
 
          namespace details
          {
-            struct unknown_type_tag { unknown_type_tag() = default; };
-            struct real_type_tag    { real_type_tag   () = default; };
-            struct complex_type_tag { complex_type_tag() = default; };
-            struct int_type_tag     { int_type_tag    () = default; };
+            struct unknown_type_tag { unknown_type_tag() exprtk_default; };
+            struct real_type_tag    { real_type_tag   () exprtk_default; };
+            struct int_type_tag     { int_type_tag    () exprtk_default; };
 
             template <typename T>
             struct number_type
             {
                typedef unknown_type_tag type;
-               number_type() = default;
+               number_type() exprtk_default;
             };
 
-            #define exprtk_register_real_type_tag(T)             \
-            template <> struct number_type<T>                    \
-            { typedef real_type_tag type; number_type() {} };    \
-
-            #define exprtk_register_complex_type_tag(T)          \
-            template <> struct number_type<std::complex<T> >     \
-            { typedef complex_type_tag type; number_type() {} }; \
+            #define exprtk_register_real_type_tag(T)          \
+            template <> struct number_type<T>                 \
+            { typedef real_type_tag type; number_type() {} }; \
 
-            #define exprtk_register_int_type_tag(T)              \
-            template <> struct number_type<T>                    \
-            { typedef int_type_tag type; number_type() {} };     \
+            #define exprtk_register_int_type_tag(T)           \
+            template <> struct number_type<T>                 \
+            { typedef int_type_tag type; number_type() {} };  \
 
+            exprtk_register_real_type_tag(float      )
             exprtk_register_real_type_tag(double     )
             exprtk_register_real_type_tag(long double)
-            exprtk_register_real_type_tag(float      )
-
-            exprtk_register_complex_type_tag(double     )
-            exprtk_register_complex_type_tag(long double)
-            exprtk_register_complex_type_tag(float      )
 
             exprtk_register_int_type_tag(short         )
             exprtk_register_int_type_tag(int           )
@@ -813,34 +860,23 @@ namespace exprtk
             #undef exprtk_register_int_type_tag
 
             template <typename T>
-            struct epsilon_type
-            {
-               static inline T value()
-               {
-                  const T epsilon = T(0.0000000001);
-                  return epsilon;
-               }
-            };
+            struct epsilon_type {};
 
-            template <>
-            struct epsilon_type <float>
-            {
-               static inline float value()
-               {
-                  const float epsilon = float(0.000001f);
-                  return epsilon;
-               }
-            };
+            #define exprtk_define_epsilon_type(Type, Epsilon)      \
+            template <> struct epsilon_type<Type>                  \
+            {                                                      \
+               static inline Type value()                          \
+               {                                                   \
+                  const Type epsilon = static_cast<Type>(Epsilon); \
+                  return epsilon;                                  \
+               }                                                   \
+            };                                                     \
 
-            template <>
-            struct epsilon_type <long double>
-            {
-               static inline long double value()
-               {
-                  const long double epsilon = static_cast<long double>(0.000000000001);
-                  return epsilon;
-               }
-            };
+            exprtk_define_epsilon_type(float      , 0.00000100000f)
+            exprtk_define_epsilon_type(double     , 0.000000000100)
+            exprtk_define_epsilon_type(long double, 0.000000000001)
+
+            #undef exprtk_define_epsilon_type
 
             template <typename T>
             inline bool is_nan_impl(const T v, real_type_tag)
@@ -860,6 +896,12 @@ namespace exprtk
                return static_cast<_int64_t>(v);
             }
 
+            template <typename T>
+            inline _uint64_t to_uint64_impl(const T v, real_type_tag)
+            {
+               return static_cast<_uint64_t>(v);
+            }
+
             template <typename T>
             inline bool is_true_impl(const T v)
             {
@@ -994,8 +1036,8 @@ namespace exprtk
                   else
                      return (T(-0.5) * v + T(1)) * v;
                }
-               else
-                  return std::numeric_limits<T>::quiet_NaN();
+
+               return std::numeric_limits<T>::quiet_NaN();
             }
 
             template <typename T>
@@ -1005,8 +1047,8 @@ namespace exprtk
                {
                   return std::log(T(1) + v);
                }
-               else
-                  return std::numeric_limits<T>::quiet_NaN();
+
+               return std::numeric_limits<T>::quiet_NaN();
             }
 
             template <typename T>
@@ -1104,7 +1146,7 @@ namespace exprtk
             template <typename T>
             inline T sgn_impl(const T v, real_type_tag)
             {
-                    if (v > T(0)) return T(+1);
+               if      (v > T(0)) return T(+1);
                else if (v < T(0)) return T(-1);
                else               return T( 0);
             }
@@ -1112,7 +1154,7 @@ namespace exprtk
             template <typename T>
             inline T sgn_impl(const T v, int_type_tag)
             {
-                    if (v > T(0)) return T(+1);
+               if      (v > T(0)) return T(+1);
                else if (v < T(0)) return T(-1);
                else               return T( 0);
             }
@@ -1202,36 +1244,37 @@ namespace exprtk
             }
 
             #if (defined(_MSC_VER) && (_MSC_VER >= 1900)) || !defined(_MSC_VER)
-            #define exprtk_define_erf(TT,impl)           \
-            inline TT erf_impl(TT v) { return impl(v); } \
+            #define exprtk_define_erf(TT, impl)                \
+            inline TT erf_impl(const TT v) { return impl(v); } \
 
-            exprtk_define_erf(      float,::erff)
-            exprtk_define_erf(     double,::erf )
-            exprtk_define_erf(long double,::erfl)
+            exprtk_define_erf(float      , ::erff)
+            exprtk_define_erf(double     , ::erf )
+            exprtk_define_erf(long double, ::erfl)
             #undef exprtk_define_erf
             #endif
 
             template <typename T>
-            inline T erf_impl(T v, real_type_tag)
+            inline T erf_impl(const T v, real_type_tag)
             {
                #if defined(_MSC_VER) && (_MSC_VER < 1900)
                // Credits: Abramowitz & Stegun Equations 7.1.25-28
-               static const T c[] = {
-                                      T( 1.26551223), T(1.00002368),
-                                      T( 0.37409196), T(0.09678418),
-                                      T(-0.18628806), T(0.27886807),
-                                      T(-1.13520398), T(1.48851587),
-                                      T(-0.82215223), T(0.17087277)
-                                    };
+               static const T c[] =
+               {
+                  T( 1.26551223), T(1.00002368),
+                  T( 0.37409196), T(0.09678418),
+                  T(-0.18628806), T(0.27886807),
+                  T(-1.13520398), T(1.48851587),
+                  T(-0.82215223), T(0.17087277)
+               };
 
                const T t = T(1) / (T(1) + T(0.5) * abs_impl(v,real_type_tag()));
 
-               T result = T(1) - t * std::exp((-v * v) -
-                                      c[0] + t * (c[1] + t *
-                                     (c[2] + t * (c[3] + t *
-                                     (c[4] + t * (c[5] + t *
-                                     (c[6] + t * (c[7] + t *
-                                     (c[8] + t * (c[9]))))))))));
+               const T result = T(1) - t * std::exp((-v * v) -
+                                            c[0] + t * (c[1] + t *
+                                           (c[2] + t * (c[3] + t *
+                                           (c[4] + t * (c[5] + t *
+                                           (c[6] + t * (c[7] + t *
+                                           (c[8] + t * (c[9]))))))))));
 
                return (v >= T(0)) ? result : -result;
                #else
@@ -1240,23 +1283,23 @@ namespace exprtk
             }
 
             template <typename T>
-            inline T erf_impl(T v, int_type_tag)
+            inline T erf_impl(const T v, int_type_tag)
             {
                return erf_impl(static_cast<double>(v),real_type_tag());
             }
 
             #if (defined(_MSC_VER) && (_MSC_VER >= 1900)) || !defined(_MSC_VER)
-            #define exprtk_define_erfc(TT,impl)           \
-            inline TT erfc_impl(TT v) { return impl(v); } \
+            #define exprtk_define_erfc(TT, impl)                \
+            inline TT erfc_impl(const TT v) { return impl(v); } \
 
-            exprtk_define_erfc(      float,::erfcf)
-            exprtk_define_erfc(     double,::erfc )
+            exprtk_define_erfc(float      ,::erfcf)
+            exprtk_define_erfc(double     ,::erfc )
             exprtk_define_erfc(long double,::erfcl)
             #undef exprtk_define_erfc
             #endif
 
             template <typename T>
-            inline T erfc_impl(T v, real_type_tag)
+            inline T erfc_impl(const T v, real_type_tag)
             {
                #if defined(_MSC_VER) && (_MSC_VER < 1900)
                return T(1) - erf_impl(v,real_type_tag());
@@ -1266,28 +1309,25 @@ namespace exprtk
             }
 
             template <typename T>
-            inline T erfc_impl(T v, int_type_tag)
+            inline T erfc_impl(const T v, int_type_tag)
             {
                return erfc_impl(static_cast<double>(v),real_type_tag());
             }
 
             template <typename T>
-            inline T ncdf_impl(T v, real_type_tag)
+            inline T ncdf_impl(const T v, real_type_tag)
             {
-               T cnd = T(0.5) * (T(1) + erf_impl(
-                                           abs_impl(v,real_type_tag()) /
-                                           T(numeric::constant::sqrt2),real_type_tag()));
-               return  (v < T(0)) ? (T(1) - cnd) : cnd;
+               return T(0.5) * erfc_impl(-(v / T(numeric::constant::sqrt2)),real_type_tag());
             }
 
             template <typename T>
-            inline T ncdf_impl(T v, int_type_tag)
+            inline T ncdf_impl(const T v, int_type_tag)
             {
                return ncdf_impl(static_cast<double>(v),real_type_tag());
             }
 
             template <typename T>
-            inline T sinc_impl(T v, real_type_tag)
+            inline T sinc_impl(const T v, real_type_tag)
             {
                if (std::abs(v) >= std::numeric_limits<T>::epsilon())
                    return(std::sin(v) / v);
@@ -1296,17 +1336,52 @@ namespace exprtk
             }
 
             template <typename T>
-            inline T sinc_impl(T v, int_type_tag)
+            inline T sinc_impl(const T v, int_type_tag)
             {
                return sinc_impl(static_cast<double>(v),real_type_tag());
             }
 
+            #if __cplusplus >= 201103L
+            template <typename T>
+            inline T acosh_impl(const T v, real_type_tag)
+            {
+               return std::acosh(v);
+            }
+
+            template <typename T>
+            inline T asinh_impl(const T v, real_type_tag)
+            {
+               return std::asinh(v);
+            }
+
+            template <typename T>
+            inline T atanh_impl(const T v, real_type_tag)
+            {
+               return std::atanh(v);
+            }
+            #else
+            template <typename T>
+            inline T acosh_impl(const T v, real_type_tag)
+            {
+               return std::log(v + std::sqrt((v * v) - T(1)));
+            }
+
+            template <typename T>
+            inline T asinh_impl(const T v, real_type_tag)
+            {
+               return std::log(v + std::sqrt((v * v) + T(1)));
+            }
+
+            template <typename T>
+            inline T atanh_impl(const T v, real_type_tag)
+            {
+               return (std::log(T(1) + v) - std::log(T(1) - v)) / T(2);
+            }
+            #endif
+
             template <typename T> inline T  acos_impl(const T v, real_type_tag) { return std::acos (v); }
-            template <typename T> inline T acosh_impl(const T v, real_type_tag) { return std::log(v + std::sqrt((v * v) - T(1))); }
             template <typename T> inline T  asin_impl(const T v, real_type_tag) { return std::asin (v); }
-            template <typename T> inline T asinh_impl(const T v, real_type_tag) { return std::log(v + std::sqrt((v * v) + T(1))); }
             template <typename T> inline T  atan_impl(const T v, real_type_tag) { return std::atan (v); }
-            template <typename T> inline T atanh_impl(const T v, real_type_tag) { return (std::log(T(1) + v) - std::log(T(1) - v)) / T(2); }
             template <typename T> inline T  ceil_impl(const T v, real_type_tag) { return std::ceil (v); }
             template <typename T> inline T   cos_impl(const T v, real_type_tag) { return std::cos  (v); }
             template <typename T> inline T  cosh_impl(const T v, real_type_tag) { return std::cosh (v); }
@@ -1327,14 +1402,15 @@ namespace exprtk
             template <typename T> inline T   csc_impl(const T v, real_type_tag) { return T(1) / std::sin(v); }
             template <typename T> inline T   r2d_impl(const T v, real_type_tag) { return (v * T(numeric::constant::_180_pi)); }
             template <typename T> inline T   d2r_impl(const T v, real_type_tag) { return (v * T(numeric::constant::pi_180));  }
-            template <typename T> inline T   d2g_impl(const T v, real_type_tag) { return (v * T(20.0/9.0)); }
-            template <typename T> inline T   g2d_impl(const T v, real_type_tag) { return (v * T(9.0/20.0)); }
+            template <typename T> inline T   d2g_impl(const T v, real_type_tag) { return (v * T(10.0/9.0)); }
+            template <typename T> inline T   g2d_impl(const T v, real_type_tag) { return (v * T(9.0/10.0)); }
             template <typename T> inline T  notl_impl(const T v, real_type_tag) { return (std::not_equal_to<T>()(T(0),v) ? T(0) : T(1)); }
             template <typename T> inline T  frac_impl(const T v, real_type_tag) { return (v - static_cast<long long>(v)); }
             template <typename T> inline T trunc_impl(const T v, real_type_tag) { return T(static_cast<long long>(v));    }
 
-            template <typename T> inline T const_pi_impl(real_type_tag) { return T(numeric::constant::pi); }
-            template <typename T> inline T const_e_impl (real_type_tag) { return T(numeric::constant::e);  }
+            template <typename T> inline T   const_pi_impl(real_type_tag) { return T(numeric::constant::pi);            }
+            template <typename T> inline T    const_e_impl(real_type_tag) { return T(numeric::constant::e);             }
+            template <typename T> inline T const_qnan_impl(real_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
 
             template <typename T> inline T   abs_impl(const T v, int_type_tag) { return ((v >= T(0)) ? v : -v); }
             template <typename T> inline T   exp_impl(const T v, int_type_tag) { return std::exp  (v); }
@@ -1382,10 +1458,10 @@ namespace exprtk
          template <typename Type>
          struct numeric_info { enum { length = 0, size = 32, bound_length = 0, min_exp = 0, max_exp = 0 }; };
 
-         template <> struct numeric_info<int>         { enum { length = 10, size = 16, bound_length = 9}; };
-         template <> struct numeric_info<float>       { enum { min_exp =  -38, max_exp =  +38}; };
-         template <> struct numeric_info<double>      { enum { min_exp = -308, max_exp = +308}; };
-         template <> struct numeric_info<long double> { enum { min_exp = -308, max_exp = +308}; };
+         template <> struct numeric_info<int        > { enum { length = 10, size = 16, bound_length = 9 }; };
+         template <> struct numeric_info<float      > { enum { min_exp =  -38, max_exp =  +38 }; };
+         template <> struct numeric_info<double     > { enum { min_exp = -308, max_exp = +308 }; };
+         template <> struct numeric_info<long double> { enum { min_exp = -308, max_exp = +308 }; };
 
          template <typename T>
          inline int to_int32(const T v)
@@ -1401,6 +1477,13 @@ namespace exprtk
             return to_int64_impl(v, num_type);
          }
 
+         template <typename T>
+         inline _uint64_t to_uint64(const T v)
+         {
+            const typename details::number_type<T>::type num_type;
+            return to_uint64_impl(v, num_type);
+         }
+
          template <typename T>
          inline bool is_nan(const T v)
          {
@@ -1558,31 +1641,31 @@ namespace exprtk
 
                while (k)
                {
-                  if (k & 1)
+                  if (1 == (k % 2))
                   {
                      l *= v;
                      --k;
                   }
 
                   v *= v;
-                  k >>= 1;
+                  k /= 2;
                }
 
                return l;
             }
          };
 
-         template <typename T> struct fast_exp<T,10> { static inline T result(T v) { T v_5 = fast_exp<T,5>::result(v); return v_5 * v_5; } };
-         template <typename T> struct fast_exp<T, 9> { static inline T result(T v) { return fast_exp<T,8>::result(v) * v; } };
-         template <typename T> struct fast_exp<T, 8> { static inline T result(T v) { T v_4 = fast_exp<T,4>::result(v); return v_4 * v_4; } };
-         template <typename T> struct fast_exp<T, 7> { static inline T result(T v) { return fast_exp<T,6>::result(v) * v; } };
-         template <typename T> struct fast_exp<T, 6> { static inline T result(T v) { T v_3 = fast_exp<T,3>::result(v); return v_3 * v_3; } };
-         template <typename T> struct fast_exp<T, 5> { static inline T result(T v) { return fast_exp<T,4>::result(v) * v; } };
-         template <typename T> struct fast_exp<T, 4> { static inline T result(T v) { T v_2 = v * v; return v_2 * v_2; } };
-         template <typename T> struct fast_exp<T, 3> { static inline T result(T v) { return v * v * v; } };
-         template <typename T> struct fast_exp<T, 2> { static inline T result(T v) { return v * v;     } };
-         template <typename T> struct fast_exp<T, 1> { static inline T result(T v) { return v;         } };
-         template <typename T> struct fast_exp<T, 0> { static inline T result(T  ) { return T(1);      } };
+         template <typename T> struct fast_exp<T,10> { static inline T result(const T v) { T v_5 = fast_exp<T,5>::result(v); return v_5 * v_5; } };
+         template <typename T> struct fast_exp<T, 9> { static inline T result(const T v) { return fast_exp<T,8>::result(v) * v; } };
+         template <typename T> struct fast_exp<T, 8> { static inline T result(const T v) { T v_4 = fast_exp<T,4>::result(v); return v_4 * v_4; } };
+         template <typename T> struct fast_exp<T, 7> { static inline T result(const T v) { return fast_exp<T,6>::result(v) * v; } };
+         template <typename T> struct fast_exp<T, 6> { static inline T result(const T v) { T v_3 = fast_exp<T,3>::result(v); return v_3 * v_3; } };
+         template <typename T> struct fast_exp<T, 5> { static inline T result(const T v) { return fast_exp<T,4>::result(v) * v; } };
+         template <typename T> struct fast_exp<T, 4> { static inline T result(const T v) { T v_2 = v * v; return v_2 * v_2; } };
+         template <typename T> struct fast_exp<T, 3> { static inline T result(const T v) { return v * v * v; } };
+         template <typename T> struct fast_exp<T, 2> { static inline T result(const T v) { return v * v;     } };
+         template <typename T> struct fast_exp<T, 1> { static inline T result(const T v) { return v;         } };
+         template <typename T> struct fast_exp<T, 0> { static inline T result(const T  ) { return T(1);      } };
 
          #define exprtk_define_unary_function(FunctionName)        \
          template <typename T>                                     \
@@ -1640,38 +1723,38 @@ namespace exprtk
       {
          static const double fract10[] =
          {
-           0.0,
-           1.0E+001, 1.0E+002, 1.0E+003, 1.0E+004, 1.0E+005, 1.0E+006, 1.0E+007, 1.0E+008, 1.0E+009, 1.0E+010,
-           1.0E+011, 1.0E+012, 1.0E+013, 1.0E+014, 1.0E+015, 1.0E+016, 1.0E+017, 1.0E+018, 1.0E+019, 1.0E+020,
-           1.0E+021, 1.0E+022, 1.0E+023, 1.0E+024, 1.0E+025, 1.0E+026, 1.0E+027, 1.0E+028, 1.0E+029, 1.0E+030,
-           1.0E+031, 1.0E+032, 1.0E+033, 1.0E+034, 1.0E+035, 1.0E+036, 1.0E+037, 1.0E+038, 1.0E+039, 1.0E+040,
-           1.0E+041, 1.0E+042, 1.0E+043, 1.0E+044, 1.0E+045, 1.0E+046, 1.0E+047, 1.0E+048, 1.0E+049, 1.0E+050,
-           1.0E+051, 1.0E+052, 1.0E+053, 1.0E+054, 1.0E+055, 1.0E+056, 1.0E+057, 1.0E+058, 1.0E+059, 1.0E+060,
-           1.0E+061, 1.0E+062, 1.0E+063, 1.0E+064, 1.0E+065, 1.0E+066, 1.0E+067, 1.0E+068, 1.0E+069, 1.0E+070,
-           1.0E+071, 1.0E+072, 1.0E+073, 1.0E+074, 1.0E+075, 1.0E+076, 1.0E+077, 1.0E+078, 1.0E+079, 1.0E+080,
-           1.0E+081, 1.0E+082, 1.0E+083, 1.0E+084, 1.0E+085, 1.0E+086, 1.0E+087, 1.0E+088, 1.0E+089, 1.0E+090,
-           1.0E+091, 1.0E+092, 1.0E+093, 1.0E+094, 1.0E+095, 1.0E+096, 1.0E+097, 1.0E+098, 1.0E+099, 1.0E+100,
-           1.0E+101, 1.0E+102, 1.0E+103, 1.0E+104, 1.0E+105, 1.0E+106, 1.0E+107, 1.0E+108, 1.0E+109, 1.0E+110,
-           1.0E+111, 1.0E+112, 1.0E+113, 1.0E+114, 1.0E+115, 1.0E+116, 1.0E+117, 1.0E+118, 1.0E+119, 1.0E+120,
-           1.0E+121, 1.0E+122, 1.0E+123, 1.0E+124, 1.0E+125, 1.0E+126, 1.0E+127, 1.0E+128, 1.0E+129, 1.0E+130,
-           1.0E+131, 1.0E+132, 1.0E+133, 1.0E+134, 1.0E+135, 1.0E+136, 1.0E+137, 1.0E+138, 1.0E+139, 1.0E+140,
-           1.0E+141, 1.0E+142, 1.0E+143, 1.0E+144, 1.0E+145, 1.0E+146, 1.0E+147, 1.0E+148, 1.0E+149, 1.0E+150,
-           1.0E+151, 1.0E+152, 1.0E+153, 1.0E+154, 1.0E+155, 1.0E+156, 1.0E+157, 1.0E+158, 1.0E+159, 1.0E+160,
-           1.0E+161, 1.0E+162, 1.0E+163, 1.0E+164, 1.0E+165, 1.0E+166, 1.0E+167, 1.0E+168, 1.0E+169, 1.0E+170,
-           1.0E+171, 1.0E+172, 1.0E+173, 1.0E+174, 1.0E+175, 1.0E+176, 1.0E+177, 1.0E+178, 1.0E+179, 1.0E+180,
-           1.0E+181, 1.0E+182, 1.0E+183, 1.0E+184, 1.0E+185, 1.0E+186, 1.0E+187, 1.0E+188, 1.0E+189, 1.0E+190,
-           1.0E+191, 1.0E+192, 1.0E+193, 1.0E+194, 1.0E+195, 1.0E+196, 1.0E+197, 1.0E+198, 1.0E+199, 1.0E+200,
-           1.0E+201, 1.0E+202, 1.0E+203, 1.0E+204, 1.0E+205, 1.0E+206, 1.0E+207, 1.0E+208, 1.0E+209, 1.0E+210,
-           1.0E+211, 1.0E+212, 1.0E+213, 1.0E+214, 1.0E+215, 1.0E+216, 1.0E+217, 1.0E+218, 1.0E+219, 1.0E+220,
-           1.0E+221, 1.0E+222, 1.0E+223, 1.0E+224, 1.0E+225, 1.0E+226, 1.0E+227, 1.0E+228, 1.0E+229, 1.0E+230,
-           1.0E+231, 1.0E+232, 1.0E+233, 1.0E+234, 1.0E+235, 1.0E+236, 1.0E+237, 1.0E+238, 1.0E+239, 1.0E+240,
-           1.0E+241, 1.0E+242, 1.0E+243, 1.0E+244, 1.0E+245, 1.0E+246, 1.0E+247, 1.0E+248, 1.0E+249, 1.0E+250,
-           1.0E+251, 1.0E+252, 1.0E+253, 1.0E+254, 1.0E+255, 1.0E+256, 1.0E+257, 1.0E+258, 1.0E+259, 1.0E+260,
-           1.0E+261, 1.0E+262, 1.0E+263, 1.0E+264, 1.0E+265, 1.0E+266, 1.0E+267, 1.0E+268, 1.0E+269, 1.0E+270,
-           1.0E+271, 1.0E+272, 1.0E+273, 1.0E+274, 1.0E+275, 1.0E+276, 1.0E+277, 1.0E+278, 1.0E+279, 1.0E+280,
-           1.0E+281, 1.0E+282, 1.0E+283, 1.0E+284, 1.0E+285, 1.0E+286, 1.0E+287, 1.0E+288, 1.0E+289, 1.0E+290,
-           1.0E+291, 1.0E+292, 1.0E+293, 1.0E+294, 1.0E+295, 1.0E+296, 1.0E+297, 1.0E+298, 1.0E+299, 1.0E+300,
-           1.0E+301, 1.0E+302, 1.0E+303, 1.0E+304, 1.0E+305, 1.0E+306, 1.0E+307, 1.0E+308
+            0.0,
+            1.0E+001, 1.0E+002, 1.0E+003, 1.0E+004, 1.0E+005, 1.0E+006, 1.0E+007, 1.0E+008, 1.0E+009, 1.0E+010,
+            1.0E+011, 1.0E+012, 1.0E+013, 1.0E+014, 1.0E+015, 1.0E+016, 1.0E+017, 1.0E+018, 1.0E+019, 1.0E+020,
+            1.0E+021, 1.0E+022, 1.0E+023, 1.0E+024, 1.0E+025, 1.0E+026, 1.0E+027, 1.0E+028, 1.0E+029, 1.0E+030,
+            1.0E+031, 1.0E+032, 1.0E+033, 1.0E+034, 1.0E+035, 1.0E+036, 1.0E+037, 1.0E+038, 1.0E+039, 1.0E+040,
+            1.0E+041, 1.0E+042, 1.0E+043, 1.0E+044, 1.0E+045, 1.0E+046, 1.0E+047, 1.0E+048, 1.0E+049, 1.0E+050,
+            1.0E+051, 1.0E+052, 1.0E+053, 1.0E+054, 1.0E+055, 1.0E+056, 1.0E+057, 1.0E+058, 1.0E+059, 1.0E+060,
+            1.0E+061, 1.0E+062, 1.0E+063, 1.0E+064, 1.0E+065, 1.0E+066, 1.0E+067, 1.0E+068, 1.0E+069, 1.0E+070,
+            1.0E+071, 1.0E+072, 1.0E+073, 1.0E+074, 1.0E+075, 1.0E+076, 1.0E+077, 1.0E+078, 1.0E+079, 1.0E+080,
+            1.0E+081, 1.0E+082, 1.0E+083, 1.0E+084, 1.0E+085, 1.0E+086, 1.0E+087, 1.0E+088, 1.0E+089, 1.0E+090,
+            1.0E+091, 1.0E+092, 1.0E+093, 1.0E+094, 1.0E+095, 1.0E+096, 1.0E+097, 1.0E+098, 1.0E+099, 1.0E+100,
+            1.0E+101, 1.0E+102, 1.0E+103, 1.0E+104, 1.0E+105, 1.0E+106, 1.0E+107, 1.0E+108, 1.0E+109, 1.0E+110,
+            1.0E+111, 1.0E+112, 1.0E+113, 1.0E+114, 1.0E+115, 1.0E+116, 1.0E+117, 1.0E+118, 1.0E+119, 1.0E+120,
+            1.0E+121, 1.0E+122, 1.0E+123, 1.0E+124, 1.0E+125, 1.0E+126, 1.0E+127, 1.0E+128, 1.0E+129, 1.0E+130,
+            1.0E+131, 1.0E+132, 1.0E+133, 1.0E+134, 1.0E+135, 1.0E+136, 1.0E+137, 1.0E+138, 1.0E+139, 1.0E+140,
+            1.0E+141, 1.0E+142, 1.0E+143, 1.0E+144, 1.0E+145, 1.0E+146, 1.0E+147, 1.0E+148, 1.0E+149, 1.0E+150,
+            1.0E+151, 1.0E+152, 1.0E+153, 1.0E+154, 1.0E+155, 1.0E+156, 1.0E+157, 1.0E+158, 1.0E+159, 1.0E+160,
+            1.0E+161, 1.0E+162, 1.0E+163, 1.0E+164, 1.0E+165, 1.0E+166, 1.0E+167, 1.0E+168, 1.0E+169, 1.0E+170,
+            1.0E+171, 1.0E+172, 1.0E+173, 1.0E+174, 1.0E+175, 1.0E+176, 1.0E+177, 1.0E+178, 1.0E+179, 1.0E+180,
+            1.0E+181, 1.0E+182, 1.0E+183, 1.0E+184, 1.0E+185, 1.0E+186, 1.0E+187, 1.0E+188, 1.0E+189, 1.0E+190,
+            1.0E+191, 1.0E+192, 1.0E+193, 1.0E+194, 1.0E+195, 1.0E+196, 1.0E+197, 1.0E+198, 1.0E+199, 1.0E+200,
+            1.0E+201, 1.0E+202, 1.0E+203, 1.0E+204, 1.0E+205, 1.0E+206, 1.0E+207, 1.0E+208, 1.0E+209, 1.0E+210,
+            1.0E+211, 1.0E+212, 1.0E+213, 1.0E+214, 1.0E+215, 1.0E+216, 1.0E+217, 1.0E+218, 1.0E+219, 1.0E+220,
+            1.0E+221, 1.0E+222, 1.0E+223, 1.0E+224, 1.0E+225, 1.0E+226, 1.0E+227, 1.0E+228, 1.0E+229, 1.0E+230,
+            1.0E+231, 1.0E+232, 1.0E+233, 1.0E+234, 1.0E+235, 1.0E+236, 1.0E+237, 1.0E+238, 1.0E+239, 1.0E+240,
+            1.0E+241, 1.0E+242, 1.0E+243, 1.0E+244, 1.0E+245, 1.0E+246, 1.0E+247, 1.0E+248, 1.0E+249, 1.0E+250,
+            1.0E+251, 1.0E+252, 1.0E+253, 1.0E+254, 1.0E+255, 1.0E+256, 1.0E+257, 1.0E+258, 1.0E+259, 1.0E+260,
+            1.0E+261, 1.0E+262, 1.0E+263, 1.0E+264, 1.0E+265, 1.0E+266, 1.0E+267, 1.0E+268, 1.0E+269, 1.0E+270,
+            1.0E+271, 1.0E+272, 1.0E+273, 1.0E+274, 1.0E+275, 1.0E+276, 1.0E+277, 1.0E+278, 1.0E+279, 1.0E+280,
+            1.0E+281, 1.0E+282, 1.0E+283, 1.0E+284, 1.0E+285, 1.0E+286, 1.0E+287, 1.0E+288, 1.0E+289, 1.0E+290,
+            1.0E+291, 1.0E+292, 1.0E+293, 1.0E+294, 1.0E+295, 1.0E+296, 1.0E+297, 1.0E+298, 1.0E+299, 1.0E+300,
+            1.0E+301, 1.0E+302, 1.0E+303, 1.0E+304, 1.0E+305, 1.0E+306, 1.0E+307, 1.0E+308
          };
 
          static const int fract10_size = static_cast<int>(sizeof(fract10) / sizeof(double));
@@ -1717,11 +1800,10 @@ namespace exprtk
 
          bool return_result = true;
          unsigned int digit = 0;
-         const std::size_t length  = static_cast<std::size_t>(std::distance(itr,end));
+         const std::size_t length = static_cast<std::size_t>(std::distance(itr,end));
 
          if (length <= 4)
          {
-            exprtk_disable_fallthrough_begin
             switch (length)
             {
                #ifdef exprtk_use_lut
@@ -1734,28 +1816,33 @@ namespace exprtk
                   return_result = false;                             \
                   break;                                             \
                }                                                     \
+               exprtk_fallthrough                                    \
 
                #else
 
-               #define exprtk_process_digit         \
-               if ((digit = (*itr++ - zero)) < 10)  \
-                  result = result * T(10) + digit;  \
-               else                                 \
-               {                                    \
-                  return_result = false;            \
-                  break;                            \
-               }                                    \
+               #define exprtk_process_digit        \
+               if ((digit = (*itr++ - zero)) < 10) \
+                  result = result * T(10) + digit; \
+               else                                \
+               {                                   \
+                  return_result = false;           \
+                  break;                           \
+               }                                   \
+               exprtk_fallthrough                  \
 
                #endif
 
-               case  4 : exprtk_process_digit
-               case  3 : exprtk_process_digit
-               case  2 : exprtk_process_digit
-               case  1 : if ((digit = (*itr - zero))>= 10) { digit = 0; return_result = false; }
+               case 4 : exprtk_process_digit
+               case 3 : exprtk_process_digit
+               case 2 : exprtk_process_digit
+               case 1 : if ((digit = (*itr - zero))>= 10)
+                        {
+                           digit = 0;
+                           return_result = false;
+                        }
 
                #undef exprtk_process_digit
             }
-            exprtk_disable_fallthrough_end
          }
          else
             return_result = false;
@@ -1804,7 +1891,7 @@ namespace exprtk
       }
 
       template <typename Iterator, typename T>
-      static inline bool parse_inf(Iterator& itr, const Iterator end, T& t, bool negative)
+      static inline bool parse_inf(Iterator& itr, const Iterator end, T& t, const bool negative)
       {
          static const char_t inf_uc[] = "INFINITY";
          static const char_t inf_lc[] = "infinity";
@@ -1819,7 +1906,7 @@ namespace exprtk
 
          while (end != itr)
          {
-            if (*inf_itr == static_cast<char>(*itr))
+            if (*inf_itr == static_cast<char_t>(*itr))
             {
                ++itr;
                ++inf_itr;
@@ -1840,8 +1927,8 @@ namespace exprtk
       template <typename T>
       inline bool valid_exponent(const int exponent, numeric::details::real_type_tag)
       {
-         return (std::numeric_limits<T>::min_exponent10 <= exponent) &&
-                (exponent <= std::numeric_limits<T>::max_exponent10) ;
+         using namespace details::numeric;
+         return (numeric_info<T>::min_exp <= exponent) && (exponent <= numeric_info<T>::max_exp);
       }
 
       template <typename Iterator, typename T>
@@ -1875,7 +1962,8 @@ namespace exprtk
          #define parse_digit_2(d)          \
          if ((digit = (*itr - zero)) < 10) \
             { d = d * T(10) + digit; }     \
-         else { break; }                   \
+         else                              \
+            { break; }                     \
             ++itr;                         \
 
          if ('.' != (*itr))
@@ -1886,14 +1974,7 @@ namespace exprtk
 
             while (end != itr)
             {
-               // Note: For 'physical' superscalar architectures it
-               // is advised that the following loop be: 4xPD1 and 1xPD2
                unsigned int digit;
-
-               #ifdef exprtk_enable_superscalar
-               parse_digit_1(d)
-               parse_digit_1(d)
-               #endif
                parse_digit_1(d)
                parse_digit_1(d)
                parse_digit_2(d)
@@ -1914,12 +1995,6 @@ namespace exprtk
                while (end != itr)
                {
                   unsigned int digit;
-
-                  #ifdef exprtk_enable_superscalar
-                  parse_digit_1(tmp_d)
-                  parse_digit_1(tmp_d)
-                  parse_digit_1(tmp_d)
-                  #endif
                   parse_digit_1(tmp_d)
                   parse_digit_1(tmp_d)
                   parse_digit_2(tmp_d)
@@ -1929,12 +2004,12 @@ namespace exprtk
                {
                   instate = true;
 
-                  const int exponent = static_cast<int>(-std::distance(curr, itr));
+                  const int frac_exponent = static_cast<int>(-std::distance(curr, itr));
 
-                  if (!valid_exponent<T>(exponent, numeric::details::real_type_tag()))
+                  if (!valid_exponent<T>(frac_exponent, numeric::details::real_type_tag()))
                      return false;
 
-                  d += compute_pow10(tmp_d, exponent);
+                  d += compute_pow10(tmp_d, frac_exponent);
                }
 
                #undef parse_digit_1
@@ -2066,8 +2141,8 @@ namespace exprtk
       loop_types loop_set;
 
       loop_runtime_check()
-      : loop_set(e_invalid),
-        max_loop_iterations(0)
+      : loop_set(e_invalid)
+      , max_loop_iterations(0)
       {}
 
       details::_uint64_t max_loop_iterations;
@@ -2079,16 +2154,77 @@ namespace exprtk
          details::_uint64_t iteration_count;
       };
 
+      virtual bool check()
+      {
+         return true;
+      }
+
       virtual void handle_runtime_violation(const violation_context&)
       {
-         throw std::runtime_error("ExprTk Loop run-time violation.");
+         throw std::runtime_error("ExprTk Loop runtime violation.");
       }
 
-      virtual ~loop_runtime_check() = default;
+      virtual ~loop_runtime_check() exprtk_default;
    };
 
    typedef loop_runtime_check* loop_runtime_check_ptr;
 
+   struct vector_access_runtime_check
+   {
+      struct violation_context
+      {
+         void* base_ptr;
+         void* end_ptr;
+         void* access_ptr;
+         std::size_t type_size;
+      };
+
+      virtual ~vector_access_runtime_check() exprtk_default;
+
+      virtual bool handle_runtime_violation(violation_context& /*context*/)
+      {
+         throw std::runtime_error("ExprTk runtime vector access violation.");
+         #if !defined(_MSC_VER) && !defined(__NVCOMPILER)
+         return false;
+         #endif
+      }
+   };
+
+   typedef vector_access_runtime_check* vector_access_runtime_check_ptr;
+
+   struct assert_check
+   {
+      struct assert_context
+      {
+         std::string condition;
+         std::string message;
+         std::string id;
+         std::size_t offet;
+      };
+
+      virtual ~assert_check() exprtk_default;
+
+      virtual void handle_assert(const assert_context& /*context*/)
+      {
+      }
+   };
+
+   typedef assert_check* assert_check_ptr;
+
+   struct compilation_check
+   {
+      struct compilation_context
+      {
+         std::string error_message;
+      };
+
+      virtual bool continue_compilation(compilation_context& /*context*/) = 0;
+
+      virtual ~compilation_check() exprtk_default;
+   };
+
+   typedef compilation_check* compilation_check_ptr;
+
    namespace lexer
    {
       struct token
@@ -2112,9 +2248,9 @@ namespace exprtk
          };
 
          token()
-         : type(e_none),
-           value(""),
-           position(std::numeric_limits<std::size_t>::max())
+         : type(e_none)
+         , value("")
+         , position(std::numeric_limits<std::size_t>::max())
          {}
 
          void clear()
@@ -2247,6 +2383,19 @@ namespace exprtk
             }
          }
 
+         static inline std::string seperator_to_str(const token_type t)
+         {
+            switch (t)
+            {
+               case e_comma : return ",";
+               case e_colon : return ":";
+               case e_eof   : return ";";
+               default      : return "UNKNOWN";
+            }
+
+            return "UNKNOWN";
+         }
+
          inline bool is_error() const
          {
             return (
@@ -2273,9 +2422,9 @@ namespace exprtk
          typedef details::char_t char_t;
 
          generator()
-         : base_itr_(0),
-           s_itr_   (0),
-           s_end_   (0)
+         : base_itr_(0)
+         , s_itr_   (0)
+         , s_end_   (0)
          {
             clear();
          }
@@ -2296,7 +2445,7 @@ namespace exprtk
             s_itr_    = str.data();
             s_end_    = str.data() + str.size();
 
-            eof_token_.set_operator(token_t::e_eof,s_end_,s_end_,base_itr_);
+            eof_token_.set_operator(token_t::e_eof, s_end_, s_end_, base_itr_);
             token_list_.clear();
 
             while (!is_end(s_itr_))
@@ -2359,7 +2508,9 @@ namespace exprtk
          inline token_t& operator[](const std::size_t& index)
          {
             if (index < token_list_.size())
+            {
                return token_list_[index];
+            }
             else
                return eof_token_;
          }
@@ -2367,7 +2518,9 @@ namespace exprtk
          inline token_t operator[](const std::size_t& index) const
          {
             if (index < token_list_.size())
+            {
                return token_list_[index];
+            }
             else
                return eof_token_;
          }
@@ -2391,10 +2544,10 @@ namespace exprtk
             }
          }
 
-         inline std::string substr(const std::size_t& begin, const std::size_t& end)
+         inline std::string substr(const std::size_t& begin, const std::size_t& end) const
          {
             const details::char_cptr begin_itr = ((base_itr_ + begin) < s_end_) ? (base_itr_ + begin) : s_end_;
-            const details::char_cptr end_itr   = ((base_itr_ +   end) < s_end_) ? (base_itr_ +   end) : s_end_;
+            const details::char_cptr end_itr   = ((base_itr_ + end  ) < s_end_) ? (base_itr_ + end  ) : s_end_;
 
             return std::string(begin_itr,end_itr);
          }
@@ -2411,13 +2564,13 @@ namespace exprtk
 
       private:
 
-         inline bool is_end(details::char_cptr itr)
+         inline bool is_end(details::char_cptr itr) const
          {
             return (s_end_ == itr);
          }
 
          #ifndef exprtk_disable_comments
-         inline bool is_comment_start(details::char_cptr itr)
+         inline bool is_comment_start(details::char_cptr itr) const
          {
             const char_t c0 = *(itr + 0);
             const char_t c1 = *(itr + 1);
@@ -2432,7 +2585,7 @@ namespace exprtk
             return false;
          }
          #else
-         inline bool is_comment_start(details::char_cptr)
+         inline bool is_comment_start(details::char_cptr) const
          {
             return false;
          }
@@ -2458,10 +2611,10 @@ namespace exprtk
                static inline bool comment_start(const char_t c0, const char_t c1, int& mode, int& incr)
                {
                   mode = 0;
-                       if ('#' == c0)    { mode = 1; incr = 1; }
+                  if      ('#' == c0)    { mode = 1; incr = 1; }
                   else if ('/' == c0)
                   {
-                          if ('/' == c1) { mode = 1; incr = 2; }
+                     if      ('/' == c1) { mode = 1; incr = 2; }
                      else if ('*' == c1) { mode = 2; incr = 2; }
                   }
                   return (0 != mode);
@@ -2511,7 +2664,7 @@ namespace exprtk
                   }
                }
 
-                ++s_itr_;
+               ++s_itr_;
             }
 
             if (2 == mode)
@@ -2523,9 +2676,17 @@ namespace exprtk
             #endif
          }
 
+         inline bool next_is_digit(const details::char_cptr itr) const
+         {
+            return ((itr + 1) != s_end_) &&
+                   details::is_digit(*(itr + 1));
+         }
+
          inline void scan_token()
          {
-            if (details::is_whitespace(*s_itr_))
+            const char_t c = *s_itr_;
+
+            if (details::is_whitespace(c))
             {
                skip_whitespace();
                return;
@@ -2535,34 +2696,39 @@ namespace exprtk
                skip_comments();
                return;
             }
-            else if (details::is_operator_char(*s_itr_))
+            else if (details::is_operator_char(c))
             {
                scan_operator();
                return;
             }
-            else if (details::is_letter(*s_itr_))
+            else if (details::is_letter(c))
             {
                scan_symbol();
                return;
             }
-            else if (details::is_digit((*s_itr_)) || ('.' == (*s_itr_)))
+            else if (('.' == c) && !next_is_digit(s_itr_))
+            {
+               scan_operator();
+               return;
+            }
+            else if (details::is_digit(c) || ('.' == c))
             {
                scan_number();
                return;
             }
-            else if ('$' == (*s_itr_))
+            else if ('$' == c)
             {
                scan_special_function();
                return;
             }
             #ifndef exprtk_disable_string_capabilities
-            else if ('\'' == (*s_itr_))
+            else if ('\'' == c)
             {
                scan_string();
                return;
             }
             #endif
-            else if ('~' == (*s_itr_))
+            else if ('~' == c)
             {
                token_t t;
                t.set_symbol(s_itr_, s_itr_ + 1, base_itr_);
@@ -2604,7 +2770,7 @@ namespace exprtk
 
                token_t::token_type ttype = token_t::e_none;
 
-                    if ((c0 == '<') && (c1 == '=')) ttype = token_t::e_lte;
+               if      ((c0 == '<') && (c1 == '=')) ttype = token_t::e_lte;
                else if ((c0 == '>') && (c1 == '=')) ttype = token_t::e_gte;
                else if ((c0 == '<') && (c1 == '>')) ttype = token_t::e_ne;
                else if ((c0 == '!') && (c1 == '=')) ttype = token_t::e_ne;
@@ -2672,7 +2838,7 @@ namespace exprtk
             }
 
             token_t t;
-            t.set_symbol(initial_itr,s_itr_,base_itr_);
+            t.set_symbol(initial_itr, s_itr_, base_itr_);
             token_list_.push_back(t);
          }
 
@@ -2872,12 +3038,12 @@ namespace exprtk
                        ((s_itr_ + 4) <= s_end_)
                      )
                   {
-                     const bool x_seperator = ('X' == std::toupper(*(s_itr_ + 1)));
+                     const bool x_separator = ('X' == std::toupper(*(s_itr_ + 1)));
 
                      const bool both_digits = details::is_hex_digit(*(s_itr_ + 2)) &&
                                               details::is_hex_digit(*(s_itr_ + 3)) ;
 
-                     if (!(x_seperator && both_digits))
+                     if (!(x_separator && both_digits))
                      {
                         t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_);
                         token_list_.push_back(t);
@@ -2917,8 +3083,8 @@ namespace exprtk
                }
 
                t.set_string(
-                    parsed_string,
-                    static_cast<std::size_t>(std::distance(base_itr_,initial_itr)));
+                  parsed_string,
+                  static_cast<std::size_t>(std::distance(base_itr_,initial_itr)));
             }
 
             token_list_.push_back(t);
@@ -2942,7 +3108,7 @@ namespace exprtk
          friend class token_modifier;
          friend class token_inserter;
          friend class token_joiner;
-      };
+      }; // class generator
 
       class helper_interface
       {
@@ -2952,14 +3118,14 @@ namespace exprtk
          virtual void reset()                    {              }
          virtual bool result()                   { return true; }
          virtual std::size_t process(generator&) { return 0;    }
-         virtual ~helper_interface() = default;
+         virtual ~helper_interface() exprtk_default;
       };
 
       class token_scanner : public helper_interface
       {
       public:
 
-         ~token_scanner() override = default;
+         ~token_scanner() exprtk_override exprtk_default;
 
          explicit token_scanner(const std::size_t& stride)
          : stride_(stride)
@@ -2970,7 +3136,7 @@ namespace exprtk
             }
          }
 
-         inline std::size_t process(generator& g) override
+         inline std::size_t process(generator& g) exprtk_override
          {
             if (g.token_list_.size() >= stride_)
             {
@@ -2986,7 +3152,7 @@ namespace exprtk
 
                                  if (!operator()(t0))
                                  {
-                                    return i;
+                                    return 0;
                                  }
                               }
                               break;
@@ -2998,7 +3164,7 @@ namespace exprtk
 
                                  if (!operator()(t0, t1))
                                  {
-                                    return i;
+                                    return 0;
                                  }
                               }
                               break;
@@ -3011,7 +3177,7 @@ namespace exprtk
 
                                  if (!operator()(t0, t1, t2))
                                  {
-                                    return i;
+                                    return 0;
                                  }
                               }
                               break;
@@ -3025,7 +3191,7 @@ namespace exprtk
 
                                  if (!operator()(t0, t1, t2, t3))
                                  {
-                                    return i;
+                                    return 0;
                                  }
                               }
                               break;
@@ -3033,7 +3199,7 @@ namespace exprtk
                }
             }
 
-            return (g.token_list_.size() - stride_ + 1);
+            return 0;
          }
 
          virtual bool operator() (const token&)
@@ -3059,13 +3225,13 @@ namespace exprtk
       private:
 
          const std::size_t stride_;
-      };
+      }; // class token_scanner
 
       class token_modifier : public helper_interface
       {
       public:
 
-         inline std::size_t process(generator& g) override
+         inline std::size_t process(generator& g) exprtk_override
          {
             std::size_t changes = 0;
 
@@ -3093,7 +3259,7 @@ namespace exprtk
             }
          }
 
-         inline std::size_t process(generator& g) override
+         inline std::size_t process(generator& g) exprtk_override
          {
             if (g.token_list_.empty())
                return 0;
@@ -3197,7 +3363,7 @@ namespace exprtk
          : stride_(stride)
          {}
 
-         inline std::size_t process(generator& g) override
+         inline std::size_t process(generator& g) exprtk_override
          {
             if (g.token_list_.empty())
                return 0;
@@ -3225,7 +3391,7 @@ namespace exprtk
             generator::token_list_t token_list;
             token_list.reserve(10000);
 
-            for (int i = 0;  i < static_cast<int>(g.token_list_.size() - 1); ++i)
+            for (int i = 0; i < static_cast<int>(g.token_list_.size() - 1); ++i)
             {
                token t;
 
@@ -3241,15 +3407,17 @@ namespace exprtk
 
                   ++changes;
 
-                  i+=2;
+                  i += 2;
 
-                  if (static_cast<std::size_t>(i) >= g.token_list_.size())
+                  if (static_cast<std::size_t>(i) >= (g.token_list_.size() - 1))
                      break;
                }
             }
 
             token_list.push_back(g.token_list_.back());
 
+            assert(token_list.size() <= g.token_list_.size());
+
             std::swap(token_list, g.token_list_);
 
             return changes;
@@ -3265,7 +3433,7 @@ namespace exprtk
             generator::token_list_t token_list;
             token_list.reserve(10000);
 
-            for (int i = 0;  i < static_cast<int>(g.token_list_.size() - 2); ++i)
+            for (int i = 0; i < static_cast<int>(g.token_list_.size() - 2); ++i)
             {
                token t;
 
@@ -3281,9 +3449,9 @@ namespace exprtk
 
                   ++changes;
 
-                  i+=3;
+                  i += 3;
 
-                  if (static_cast<std::size_t>(i) >= g.token_list_.size())
+                  if (static_cast<std::size_t>(i) >= (g.token_list_.size() - 2))
                      break;
                }
             }
@@ -3291,6 +3459,8 @@ namespace exprtk
             token_list.push_back(*(g.token_list_.begin() + g.token_list_.size() - 2));
             token_list.push_back(*(g.token_list_.begin() + g.token_list_.size() - 1));
 
+            assert(token_list.size() <= g.token_list_.size());
+
             std::swap(token_list, g.token_list_);
 
             return changes;
@@ -3330,7 +3500,7 @@ namespace exprtk
                ignore_set_.insert(symbol);
             }
 
-            inline int insert(const lexer::token& t0, const lexer::token& t1, lexer::token& new_token) override
+            inline int insert(const lexer::token& t0, const lexer::token& t1, lexer::token& new_token) exprtk_override
             {
                bool match         = false;
                new_token.type     = lexer::token::e_mul;
@@ -3356,7 +3526,7 @@ namespace exprtk
                      return -1;
                   }
                }
-                    if ((t0.type == lexer::token::e_number     ) && (t1.type == lexer::token::e_symbol     )) match = true;
+               if      ((t0.type == lexer::token::e_number     ) && (t1.type == lexer::token::e_symbol     )) match = true;
                else if ((t0.type == lexer::token::e_number     ) && (t1.type == lexer::token::e_lbracket   )) match = true;
                else if ((t0.type == lexer::token::e_number     ) && (t1.type == lexer::token::e_lcrlbracket)) match = true;
                else if ((t0.type == lexer::token::e_number     ) && (t1.type == lexer::token::e_lsqrbracket)) match = true;
@@ -3377,7 +3547,7 @@ namespace exprtk
             std::set<std::string,details::ilesscompare> ignore_set_;
          };
 
-         class operator_joiner : public token_joiner
+         class operator_joiner exprtk_final : public token_joiner
          {
          public:
 
@@ -3385,7 +3555,7 @@ namespace exprtk
             : token_joiner(stride)
             {}
 
-            inline bool join(const lexer::token& t0, const lexer::token& t1, lexer::token& t) override
+            inline bool join(const lexer::token& t0, const lexer::token& t1, lexer::token& t) exprtk_override
             {
                // ': =' --> ':='
                if ((t0.type == lexer::token::e_colon) && (t1.type == lexer::token::e_eq))
@@ -3469,7 +3639,7 @@ namespace exprtk
                   return true;
                }
                // '! =' --> '!='
-               else if ((static_cast<char>(t0.type) == '!') && (t1.type == lexer::token::e_eq))
+               else if ((static_cast<details::char_t>(t0.type) == '!') && (t1.type == lexer::token::e_eq))
                {
                   t.type     = lexer::token::e_ne;
                   t.value    = "!=";
@@ -3530,7 +3700,10 @@ namespace exprtk
                   return false;
             }
 
-            inline bool join(const lexer::token& t0, const lexer::token& t1, const lexer::token& t2, lexer::token& t) override
+            inline bool join(const lexer::token& t0,
+                             const lexer::token& t1,
+                             const lexer::token& t2,
+                             lexer::token& t) exprtk_override
             {
                // '[ * ]' --> '[*]'
                if (
@@ -3550,18 +3723,18 @@ namespace exprtk
             }
          };
 
-         class bracket_checker : public lexer::token_scanner
+         class bracket_checker exprtk_final : public lexer::token_scanner
          {
          public:
 
             using lexer::token_scanner::operator();
 
             bracket_checker()
-            : token_scanner(1),
-              state_(true)
+            : token_scanner(1)
+            , state_(true)
             {}
 
-            bool result() override
+            bool result() exprtk_override
             {
                if (!stack_.empty())
                {
@@ -3582,7 +3755,7 @@ namespace exprtk
                return error_token_;
             }
 
-            void reset() override
+            void reset() exprtk_override
             {
                // Why? because msvc doesn't support swap properly.
                stack_ = std::stack<std::pair<char,std::size_t> >();
@@ -3590,7 +3763,7 @@ namespace exprtk
                error_token_.clear();
             }
 
-            bool operator() (const lexer::token& t) override
+            bool operator() (const lexer::token& t) exprtk_override
             {
                if (
                     !t.value.empty()                       &&
@@ -3601,7 +3774,7 @@ namespace exprtk
                {
                   details::char_t c = t.value[0];
 
-                       if (t.type == lexer::token::e_lbracket   ) stack_.push(std::make_pair(')',t.position));
+                  if      (t.type == lexer::token::e_lbracket   ) stack_.push(std::make_pair(')',t.position));
                   else if (t.type == lexer::token::e_lcrlbracket) stack_.push(std::make_pair('}',t.position));
                   else if (t.type == lexer::token::e_lsqrbracket) stack_.push(std::make_pair(']',t.position));
                   else if (exprtk::details::is_right_bracket(c))
@@ -3635,33 +3808,34 @@ namespace exprtk
             lexer::token error_token_;
          };
 
-         class numeric_checker : public lexer::token_scanner
+         template <typename T>
+         class numeric_checker exprtk_final : public lexer::token_scanner
          {
          public:
 
             using lexer::token_scanner::operator();
 
             numeric_checker()
-            : token_scanner (1),
-              current_index_(0)
+            : token_scanner (1)
+            , current_index_(0)
             {}
 
-            bool result() override
+            bool result() exprtk_override
             {
                return error_list_.empty();
             }
 
-            void reset() override
+            void reset() exprtk_override
             {
                error_list_.clear();
                current_index_ = 0;
             }
 
-            bool operator() (const lexer::token& t) override
+            bool operator() (const lexer::token& t) exprtk_override
             {
                if (token::e_number == t.type)
                {
-                  double v;
+                  T v;
 
                   if (!exprtk::details::string_to_real(t.value,v))
                   {
@@ -3698,7 +3872,7 @@ namespace exprtk
             std::vector<std::size_t> error_list_;
          };
 
-         class symbol_replacer : public lexer::token_modifier
+         class symbol_replacer exprtk_final : public lexer::token_modifier
          {
          private:
 
@@ -3741,7 +3915,7 @@ namespace exprtk
 
          private:
 
-            bool modify(lexer::token& t) override
+            bool modify(lexer::token& t) exprtk_override
             {
                if (lexer::token::e_symbol == t.type)
                {
@@ -3765,7 +3939,7 @@ namespace exprtk
             replace_map_t replace_map_;
          };
 
-         class sequence_validator : public lexer::token_scanner
+         class sequence_validator exprtk_final : public lexer::token_scanner
          {
          private:
 
@@ -3804,12 +3978,12 @@ namespace exprtk
                add_invalid_set1(lexer::token::e_ternary);
             }
 
-            bool result() override
+            bool result() exprtk_override
             {
                return error_list_.empty();
             }
 
-            bool operator() (const lexer::token& t0, const lexer::token& t1) override
+            bool operator() (const lexer::token& t0, const lexer::token& t1) exprtk_override
             {
                const set_t::value_type p = std::make_pair(t0.type,t1.type);
 
@@ -3850,12 +4024,12 @@ namespace exprtk
 
          private:
 
-            void add_invalid(lexer::token::token_type base, lexer::token::token_type t)
+            void add_invalid(const lexer::token::token_type base, const lexer::token::token_type t)
             {
                invalid_comb_.insert(std::make_pair(base,t));
             }
 
-            void add_invalid_set1(lexer::token::token_type t)
+            void add_invalid_set1(const lexer::token::token_type t)
             {
                add_invalid(t, lexer::token::e_assign);
                add_invalid(t, lexer::token::e_shr   );
@@ -3874,9 +4048,9 @@ namespace exprtk
                add_invalid(t, lexer::token::e_colon );
             }
 
-            bool invalid_bracket_check(lexer::token::token_type base, lexer::token::token_type t)
+            bool invalid_bracket_check(const lexer::token::token_type base, const lexer::token::token_type t)
             {
-               if (details::is_right_bracket(static_cast<char>(base)))
+               if (details::is_right_bracket(static_cast<details::char_t>(base)))
                {
                   switch (t)
                   {
@@ -3885,11 +4059,11 @@ namespace exprtk
                      default                     : return false;
                   }
                }
-               else if (details::is_left_bracket(static_cast<char>(base)))
+               else if (details::is_left_bracket(static_cast<details::char_t>(base)))
                {
-                  if (details::is_right_bracket(static_cast<char>(t)))
+                  if (details::is_right_bracket(static_cast<details::char_t>(t)))
                      return false;
-                  else if (details::is_left_bracket(static_cast<char>(t)))
+                  else if (details::is_left_bracket(static_cast<details::char_t>(t)))
                      return false;
                   else
                   {
@@ -3906,7 +4080,7 @@ namespace exprtk
                      }
                   }
                }
-               else if (details::is_right_bracket(static_cast<char>(t)))
+               else if (details::is_right_bracket(static_cast<details::char_t>(t)))
                {
                   switch (base)
                   {
@@ -3919,7 +4093,7 @@ namespace exprtk
                      default                      : return true ;
                   }
                }
-               else if (details::is_left_bracket(static_cast<char>(t)))
+               else if (details::is_left_bracket(static_cast<details::char_t>(t)))
                {
                   switch (base)
                   {
@@ -3937,7 +4111,7 @@ namespace exprtk
             std::vector<std::pair<lexer::token,lexer::token> > error_list_;
          };
 
-         class sequence_validator_3tokens : public lexer::token_scanner
+         class sequence_validator_3tokens exprtk_final : public lexer::token_scanner
          {
          private:
 
@@ -3952,31 +4126,31 @@ namespace exprtk
             sequence_validator_3tokens()
             : lexer::token_scanner(3)
             {
-               add_invalid(lexer::token::e_number, lexer::token::e_number, lexer::token::e_number);
-               add_invalid(lexer::token::e_string, lexer::token::e_string, lexer::token::e_string);
-               add_invalid(lexer::token::e_comma , lexer::token::e_comma , lexer::token::e_comma );
+               add_invalid(lexer::token::e_number , lexer::token::e_number , lexer::token::e_number);
+               add_invalid(lexer::token::e_string , lexer::token::e_string , lexer::token::e_string);
+               add_invalid(lexer::token::e_comma  , lexer::token::e_comma  , lexer::token::e_comma );
 
-               add_invalid(lexer::token::e_add   , lexer::token::e_add   , lexer::token::e_add   );
-               add_invalid(lexer::token::e_sub   , lexer::token::e_sub   , lexer::token::e_sub   );
-               add_invalid(lexer::token::e_div   , lexer::token::e_div   , lexer::token::e_div   );
-               add_invalid(lexer::token::e_mul   , lexer::token::e_mul   , lexer::token::e_mul   );
-               add_invalid(lexer::token::e_mod   , lexer::token::e_mod   , lexer::token::e_mod   );
-               add_invalid(lexer::token::e_pow   , lexer::token::e_pow   , lexer::token::e_pow   );
+               add_invalid(lexer::token::e_add    , lexer::token::e_add    , lexer::token::e_add   );
+               add_invalid(lexer::token::e_sub    , lexer::token::e_sub    , lexer::token::e_sub   );
+               add_invalid(lexer::token::e_div    , lexer::token::e_div    , lexer::token::e_div   );
+               add_invalid(lexer::token::e_mul    , lexer::token::e_mul    , lexer::token::e_mul   );
+               add_invalid(lexer::token::e_mod    , lexer::token::e_mod    , lexer::token::e_mod   );
+               add_invalid(lexer::token::e_pow    , lexer::token::e_pow    , lexer::token::e_pow   );
 
-               add_invalid(lexer::token::e_add   , lexer::token::e_sub   , lexer::token::e_add   );
-               add_invalid(lexer::token::e_sub   , lexer::token::e_add   , lexer::token::e_sub   );
-               add_invalid(lexer::token::e_div   , lexer::token::e_mul   , lexer::token::e_div   );
-               add_invalid(lexer::token::e_mul   , lexer::token::e_div   , lexer::token::e_mul   );
-               add_invalid(lexer::token::e_mod   , lexer::token::e_pow   , lexer::token::e_mod   );
-               add_invalid(lexer::token::e_pow   , lexer::token::e_mod   , lexer::token::e_pow   );
+               add_invalid(lexer::token::e_add    , lexer::token::e_sub    , lexer::token::e_add   );
+               add_invalid(lexer::token::e_sub    , lexer::token::e_add    , lexer::token::e_sub   );
+               add_invalid(lexer::token::e_div    , lexer::token::e_mul    , lexer::token::e_div   );
+               add_invalid(lexer::token::e_mul    , lexer::token::e_div    , lexer::token::e_mul   );
+               add_invalid(lexer::token::e_mod    , lexer::token::e_pow    , lexer::token::e_mod   );
+               add_invalid(lexer::token::e_pow    , lexer::token::e_mod    , lexer::token::e_pow   );
             }
 
-            bool result() override
+            bool result() exprtk_override
             {
                return error_list_.empty();
             }
 
-            bool operator() (const lexer::token& t0, const lexer::token& t1, const lexer::token& t2) override
+            bool operator() (const lexer::token& t0, const lexer::token& t1, const lexer::token& t2) exprtk_override
             {
                const set_t::value_type p = std::make_pair(t0.type,std::make_pair(t1.type,t2.type));
 
@@ -4013,7 +4187,7 @@ namespace exprtk
 
          private:
 
-            void add_invalid(token_t t0, token_t t1, token_t t2)
+            void add_invalid(const token_t t0, const token_t t1, const token_t t2)
             {
                invalid_comb_.insert(std::make_pair(t0,std::make_pair(t1,t2)));
             }
@@ -4184,7 +4358,7 @@ namespace exprtk
       {
       public:
 
-         typedef token         token_t;
+         typedef token     token_t;
          typedef generator generator_t;
 
          inline bool init(const std::string& str)
@@ -4233,6 +4407,11 @@ namespace exprtk
             return current_token_;
          }
 
+         inline const token_t& peek_next_token()
+         {
+            return lexer_.peek_next_token();
+         }
+
          enum token_advance_mode
          {
             e_hold    = 0,
@@ -4276,6 +4455,110 @@ namespace exprtk
             return true;
          }
 
+         inline bool token_is(const std::string& value,
+                              const token_advance_mode mode = e_advance)
+         {
+            if (!exprtk::details::imatch(value,current_token().value))
+            {
+               return false;
+            }
+
+            advance_token(mode);
+
+            return true;
+         }
+
+         inline bool token_is_arithmetic_opr(const token_advance_mode mode = e_advance)
+         {
+            switch (current_token().type)
+            {
+               case token_t::e_add :
+               case token_t::e_sub :
+               case token_t::e_div :
+               case token_t::e_mul :
+               case token_t::e_mod :
+               case token_t::e_pow : break;
+               default             : return false;
+            }
+
+            advance_token(mode);
+
+            return true;
+         }
+
+         inline bool token_is_ineq_opr(const token_advance_mode mode = e_advance)
+         {
+            switch (current_token().type)
+            {
+               case token_t::e_eq  :
+               case token_t::e_lte :
+               case token_t::e_ne  :
+               case token_t::e_gte :
+               case token_t::e_lt  :
+               case token_t::e_gt  : break;
+               default             : return false;
+            }
+
+            advance_token(mode);
+
+            return true;
+         }
+
+         inline bool token_is_left_bracket(const token_advance_mode mode = e_advance)
+         {
+            switch (current_token().type)
+            {
+               case token_t::e_lbracket    :
+               case token_t::e_lcrlbracket :
+               case token_t::e_lsqrbracket : break;
+               default                     : return false;
+            }
+
+            advance_token(mode);
+
+            return true;
+         }
+
+         inline bool token_is_right_bracket(const token_advance_mode mode = e_advance)
+         {
+            switch (current_token().type)
+            {
+               case token_t::e_rbracket    :
+               case token_t::e_rcrlbracket :
+               case token_t::e_rsqrbracket : break;
+               default                     : return false;
+            }
+
+            advance_token(mode);
+
+            return true;
+         }
+
+         inline bool token_is_bracket(const token_advance_mode mode = e_advance)
+         {
+            switch (current_token().type)
+            {
+               case token_t::e_rbracket    :
+               case token_t::e_rcrlbracket :
+               case token_t::e_rsqrbracket :
+               case token_t::e_lbracket    :
+               case token_t::e_lcrlbracket :
+               case token_t::e_lsqrbracket : break;
+               default                     : return false;
+            }
+
+            advance_token(mode);
+
+            return true;
+         }
+
+         inline bool token_is_loop(const token_advance_mode mode = e_advance)
+         {
+            return token_is("for"   , mode) ||
+                   token_is("while" , mode) ||
+                   token_is("repeat", mode) ;
+         }
+
          inline bool peek_token_is(const token_t::token_type& ttype)
          {
             return (lexer_.peek_next_token().type == ttype);
@@ -4302,16 +4585,22 @@ namespace exprtk
       typedef T* data_ptr_t;
 
       vector_view(data_ptr_t data, const std::size_t& size)
-      : size_(size),
-        data_(data),
-        data_ref_(0)
-      {}
+      : base_size_(size)
+      , size_(size)
+      , data_(data)
+      , data_ref_(0)
+      {
+         assert(size_ > 0);
+      }
 
       vector_view(const vector_view<T>& vv)
-      : size_(vv.size_),
-        data_(vv.data_),
-        data_ref_(0)
-      {}
+      : base_size_(vv.base_size_)
+      , size_(vv.size_)
+      , data_(vv.data_)
+      , data_ref_(0)
+      {
+         assert(size_ > 0);
+      }
 
       inline void rebase(data_ptr_t data)
       {
@@ -4331,6 +4620,11 @@ namespace exprtk
          return data_;
       }
 
+      inline std::size_t base_size() const
+      {
+         return base_size_;
+      }
+
       inline std::size_t size() const
       {
          return size_;
@@ -4338,22 +4632,55 @@ namespace exprtk
 
       inline const T& operator[](const std::size_t index) const
       {
+         assert(index < size_);
          return data_[index];
       }
 
       inline T& operator[](const std::size_t index)
       {
+         assert(index < size_);
          return data_[index];
       }
 
       void set_ref(data_ptr_t* data_ref)
       {
          data_ref_.push_back(data_ref);
+         exprtk_debug(("vector_view::set_ref() - data_ref: %p data_ref_.size(): %d\n",
+                       reinterpret_cast<void*>(data_ref),
+                       static_cast<int>(data_ref_.size())));
+      }
+
+      void remove_ref(data_ptr_t* data_ref)
+      {
+         data_ref_.erase(
+            std::remove(data_ref_.begin(), data_ref_.end(), data_ref),
+            data_ref_.end());
+         exprtk_debug(("vector_view::remove_ref() - data_ref: %p data_ref_.size(): %d\n",
+                       reinterpret_cast<void*>(data_ref),
+                       static_cast<int>(data_ref_.size())));
+      }
+
+      bool set_size(const std::size_t new_size)
+      {
+         if ((new_size > 0) && (new_size <= base_size_))
+         {
+            size_ = new_size;
+            exprtk_debug(("vector_view::set_size() - data_: %p size: %lu\n",
+                          reinterpret_cast<void*>(data_),
+                          size_));
+            return true;
+         }
+
+         exprtk_debug(("vector_view::set_size() - error invalid new_size: %lu  base_size: %lu\n",
+                       new_size,
+                       base_size_));
+         return false;
       }
 
    private:
 
-      const std::size_t size_;
+      const std::size_t base_size_;
+      std::size_t size_;
       data_ptr_t  data_;
       std::vector<data_ptr_t*> data_ref_;
    };
@@ -4386,15 +4713,15 @@ namespace exprtk
       };
 
       type_store()
-      : data(0),
-        size(0),
-        type(e_unknown)
+      : data(0)
+      , size(0)
+      , type(e_unknown)
       {}
 
       union
       {
-          void*  data;
-          T*     vec_data;
+         void* data;
+         T*    vec_data;
       };
 
       std::size_t size;
@@ -4462,13 +4789,13 @@ namespace exprtk
          typedef ViewType      value_t;
 
          explicit type_view(type_store_t& ts)
-         : ts_(ts),
-           data_(reinterpret_cast<value_t*>(ts_.data))
+         : ts_(ts)
+         , data_(reinterpret_cast<value_t*>(ts_.data))
          {}
 
          explicit type_view(const type_store_t& ts)
-         : ts_(const_cast<type_store_t&>(ts)),
-           data_(reinterpret_cast<value_t*>(ts_.data))
+         : ts_(const_cast<type_store_t&>(ts))
+         , data_(reinterpret_cast<value_t*>(ts_.data))
          {}
 
          inline std::size_t size() const
@@ -4529,6 +4856,16 @@ namespace exprtk
             return v_;
          }
 
+         inline operator value_t() const
+         {
+            return v_;
+         }
+
+         inline operator value_t()
+         {
+            return v_;
+         }
+
          template <typename IntType>
          inline bool to_int(IntType& i) const
          {
@@ -4577,6 +4914,9 @@ namespace exprtk
    public:
 
       typedef type_store<T> type_store_t;
+      typedef typename type_store_t::scalar_view scalar_t;
+      typedef typename type_store_t::vector_view vector_t;
+      typedef typename type_store_t::string_view string_t;
 
       results_context()
       : results_available_(false)
@@ -4600,6 +4940,61 @@ namespace exprtk
          return parameter_list_[index];
       }
 
+      inline bool get_scalar(const std::size_t& index, T& out) const
+      {
+         if (
+              (index < parameter_list_.size()) &&
+              (parameter_list_[index].type == type_store_t::e_scalar)
+            )
+         {
+            const scalar_t scalar(parameter_list_[index]);
+            out = scalar();
+            return true;
+         }
+
+         return false;
+      }
+
+      template <typename OutputIterator>
+      inline bool get_vector(const std::size_t& index, OutputIterator out_itr) const
+      {
+         if (
+              (index < parameter_list_.size()) &&
+              (parameter_list_[index].type == type_store_t::e_vector)
+            )
+         {
+            const vector_t vector(parameter_list_[index]);
+            for (std::size_t i = 0; i < vector.size(); ++i)
+            {
+               *(out_itr++) = vector[i];
+            }
+
+            return true;
+         }
+
+         return false;
+      }
+
+      inline bool get_vector(const std::size_t& index, std::vector<T>& out) const
+      {
+         return get_vector(index,std::back_inserter(out));
+      }
+
+      inline bool get_string(const std::size_t& index, std::string& out) const
+      {
+         if (
+              (index < parameter_list_.size()) &&
+              (parameter_list_[index].type == type_store_t::e_string)
+            )
+         {
+            const string_t str(parameter_list_[index]);
+            out.assign(str.begin(),str.size());
+            return true;
+         }
+
+         return false;
+      }
+
    private:
 
       inline void clear()
@@ -4736,8 +5131,8 @@ namespace exprtk
       struct base_operation_t
       {
          base_operation_t(const operator_type t, const unsigned int& np)
-         : type(t),
-           num_params(np)
+         : type(t)
+         , num_params(np)
          {}
 
          operator_type type;
@@ -4746,23 +5141,24 @@ namespace exprtk
 
       namespace loop_unroll
       {
+         const unsigned int global_loop_batch_size =
          #ifndef exprtk_disable_superscalar_unroll
-         const unsigned int global_loop_batch_size = 16;
+         16;
          #else
-         const unsigned int global_loop_batch_size = 4;
+          4;
          #endif
 
          struct details
          {
             explicit details(const std::size_t& vsize,
                              const unsigned int loop_batch_size = global_loop_batch_size)
-            : batch_size(loop_batch_size   ),
-              remainder (vsize % batch_size),
-              upper_bound(static_cast<int>(vsize - (remainder ? loop_batch_size : 0)))
+            : batch_size(loop_batch_size   )
+            , remainder (vsize % batch_size)
+            , upper_bound(static_cast<int>(vsize - (remainder ? loop_batch_size : 0)))
             {}
 
             unsigned int batch_size;
-            int   remainder;
+            int remainder;
             int upper_bound;
          };
       }
@@ -4771,16 +5167,33 @@ namespace exprtk
       inline void dump_ptr(const std::string& s, const void* ptr, const std::size_t size = 0)
       {
          if (size)
-            exprtk_debug(("%s - addr: %p\n",s.c_str(),ptr));
-         else
             exprtk_debug(("%s - addr: %p size: %d\n",
                           s.c_str(),
                           ptr,
                           static_cast<unsigned int>(size)));
+         else
+            exprtk_debug(("%s - addr: %p\n", s.c_str(), ptr));
+      }
+
+      template <typename T>
+      inline void dump_vector(const std::string& vec_name, const T* data, const std::size_t size)
+      {
+         printf("----- %s (%p) -----\n",
+                vec_name.c_str(),
+                static_cast<const void*>(data));
+         printf("[ ");
+         for (std::size_t i = 0; i <  size; ++i)
+         {
+            printf("%8.3f\t", data[i]);
+         }
+         printf(" ]\n");
+         printf("---------------------\n");
       }
       #else
       inline void dump_ptr(const std::string&, const void*) {}
       inline void dump_ptr(const std::string&, const void*, const std::size_t) {}
+      template <typename T>
+      inline void dump_vector(const std::string&, const T*, const std::size_t) {}
       #endif
 
       template <typename T>
@@ -4796,31 +5209,31 @@ namespace exprtk
          struct control_block
          {
             control_block()
-            : ref_count(1),
-              size     (0),
-              data     (0),
-              destruct (true)
+            : ref_count(1)
+            , size     (0)
+            , data     (0)
+            , destruct (true)
             {}
 
             explicit control_block(const std::size_t& dsize)
-            : ref_count(1    ),
-              size     (dsize),
-              data     (0    ),
-              destruct (true )
+            : ref_count(1    )
+            , size     (dsize)
+            , data     (0    )
+            , destruct (true )
             { create_data(); }
 
             control_block(const std::size_t& dsize, data_t dptr, bool dstrct = false)
-            : ref_count(1     ),
-              size     (dsize ),
-              data     (dptr  ),
-              destruct (dstrct)
+            : ref_count(1     )
+            , size     (dsize )
+            , data     (dptr  )
+            , destruct (dstrct)
             {}
 
            ~control_block()
             {
                if (data && destruct && (0 == ref_count))
                {
-                  dump_ptr("~control_block() data",data);
+                  dump_ptr("~vec_data_store::control_block() data",data);
                   delete[] data;
                   data = reinterpret_cast<data_t>(0);
                }
@@ -4862,15 +5275,15 @@ namespace exprtk
 
          private:
 
-            control_block(const control_block&);
-            control_block& operator=(const control_block&);
+            control_block(const control_block&) exprtk_delete;
+            control_block& operator=(const control_block&) exprtk_delete;
 
             inline void create_data()
             {
                destruct = true;
                data     = new T[size];
                std::fill_n(data, size, T(0));
-               dump_ptr("control_block::create_data() - data",data,size);
+               dump_ptr("control_block::create_data() - data", data, size);
             }
          };
 
@@ -4903,7 +5316,7 @@ namespace exprtk
          {
             if (this != &vds)
             {
-               std::size_t final_size = min_size(control_block_, vds.control_block_);
+               const std::size_t final_size = min_size(control_block_, vds.control_block_);
 
                vds.control_block_->size = final_size;
                    control_block_->size = final_size;
@@ -4930,11 +5343,6 @@ namespace exprtk
             return control_block_->data;
          }
 
-         inline std::size_t size()
-         {
-            return control_block_->size;
-         }
-
          inline std::size_t size() const
          {
             return control_block_->size;
@@ -4958,7 +5366,7 @@ namespace exprtk
                if (5 == i)
                   exprtk_debug(("\n"));
 
-               exprtk_debug(("%15.10f ",data()[i]));
+               exprtk_debug(("%15.10f ", data()[i]));
             }
             exprtk_debug(("\n"));
             #endif
@@ -4973,7 +5381,7 @@ namespace exprtk
 
       private:
 
-         static inline std::size_t min_size(control_block* cb0, control_block* cb1)
+         static inline std::size_t min_size(const control_block* cb0, const control_block* cb1)
          {
             const std::size_t size0 = cb0->size;
             const std::size_t size1 = cb1->size;
@@ -5071,8 +5479,8 @@ namespace exprtk
                   case e_xnor   : return xnor_opr<T>(arg0,arg1);
                   case e_root   : return root    <T>(arg0,arg1);
                   case e_roundn : return roundn  <T>(arg0,arg1);
-                  case e_equal  : return equal      (arg0,arg1);
-                  case e_nequal : return nequal     (arg0,arg1);
+                  case e_equal  : return equal   <T>(arg0,arg1);
+                  case e_nequal : return nequal  <T>(arg0,arg1);
                   case e_hypot  : return hypot   <T>(arg0,arg1);
                   case e_shr    : return shr     <T>(arg0,arg1);
                   case e_shl    : return shl     <T>(arg0,arg1);
@@ -5141,17 +5549,18 @@ namespace exprtk
          typedef Node** node_pp_t;
          typedef std::vector<node_pp_t> noderef_list_t;
 
-         virtual ~node_collector_interface() = default;
+         virtual ~node_collector_interface() exprtk_default;
 
-         virtual void collect_nodes(noderef_list_t&) {}
+         virtual void collect_nodes(noderef_list_t&)
+         {}
       };
 
       template <typename Node>
       struct node_depth_base;
 
       template <typename T>
-      class expression_node : public node_collector_interface<expression_node<T> >,
-                              public node_depth_base<expression_node<T> >
+      class expression_node : public node_collector_interface<expression_node<T> >
+                            , public node_depth_base<expression_node<T> >
       {
       public:
 
@@ -5188,12 +5597,14 @@ namespace exprtk
             e_vovovoc       , e_vovocov       , e_vocovov     , e_covovov      ,
             e_covocov       , e_vocovoc       , e_covovoc     , e_vococov      ,
             e_sf3ext        , e_sf4ext        , e_nulleq      , e_strass       ,
-            e_vector        , e_vecelem       , e_rbvecelem   , e_rbveccelem   ,
-            e_vecdefass     , e_vecvalass     , e_vecvecass   , e_vecopvalass  ,
-            e_vecopvecass   , e_vecfunc       , e_vecvecswap  , e_vecvecineq   ,
-            e_vecvalineq    , e_valvecineq    , e_vecvecarith , e_vecvalarith  ,
-            e_valvecarith   , e_vecunaryop    , e_break       , e_continue     ,
-            e_swap
+            e_vector        , e_vecsize       , e_vecelem     , e_veccelem     ,
+            e_vecelemrtc    , e_veccelemrtc   , e_rbvecelem   , e_rbvecelemrtc ,
+            e_rbveccelem    , e_rbveccelemrtc , e_vecinit     , e_vecvalass    ,
+            e_vecvecass     , e_vecopvalass   , e_vecopvecass , e_vecfunc      ,
+            e_vecvecswap    , e_vecvecineq    , e_vecvalineq  , e_valvecineq   ,
+            e_vecvecarith   , e_vecvalarith   , e_valvecarith , e_vecunaryop   ,
+            e_vecondition   , e_break         , e_continue    , e_swap         ,
+            e_assert
          };
 
          typedef T value_type;
@@ -5202,7 +5613,7 @@ namespace exprtk
          typedef typename nci_t::noderef_list_t noderef_list_t;
          typedef node_depth_base<expression_node<T> > ndb_t;
 
-         ~expression_node() override = default;
+         ~expression_node() exprtk_override exprtk_default;
 
          inline virtual T value() const
          {
@@ -5218,7 +5629,12 @@ namespace exprtk
          {
             return e_none;
          }
-      };
+
+         inline virtual bool valid() const
+         {
+            return true;
+         }
+      }; // class expression_node
 
       template <typename T>
       inline bool is_generally_string_node(const expression_node<T>* node);
@@ -5238,12 +5654,6 @@ namespace exprtk
          return std::not_equal_to<float>()(0.0f,v);
       }
 
-      template <typename T>
-      inline bool is_true(const std::complex<T>& v)
-      {
-         return std::not_equal_to<std::complex<T> >()(std::complex<T>(0),v);
-      }
-
       template <typename T>
       inline bool is_true(const expression_node<T>* node)
       {
@@ -5268,6 +5678,12 @@ namespace exprtk
          return std::equal_to<T>()(T(0),node.first->value());
       }
 
+      template <typename T>
+      inline bool is_literal_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_constant == node->type());
+      }
+
       template <typename T>
       inline bool is_unary_node(const expression_node<T>* node)
       {
@@ -5297,10 +5713,15 @@ namespace exprtk
       {
          return node &&
                 (
-                  details::expression_node<T>::e_variable   == node->type() ||
-                  details::expression_node<T>::e_vecelem    == node->type() ||
-                  details::expression_node<T>::e_rbvecelem  == node->type() ||
-                  details::expression_node<T>::e_rbveccelem == node->type()
+                  details::expression_node<T>::e_variable      == node->type() ||
+                  details::expression_node<T>::e_vecelem       == node->type() ||
+                  details::expression_node<T>::e_veccelem      == node->type() ||
+                  details::expression_node<T>::e_vecelemrtc    == node->type() ||
+                  details::expression_node<T>::e_veccelemrtc   == node->type() ||
+                  details::expression_node<T>::e_rbvecelem     == node->type() ||
+                  details::expression_node<T>::e_rbveccelem    == node->type() ||
+                  details::expression_node<T>::e_rbvecelemrtc  == node->type() ||
+                  details::expression_node<T>::e_rbveccelemrtc == node->type()
                 );
       }
 
@@ -5310,12 +5731,42 @@ namespace exprtk
          return node && (details::expression_node<T>::e_vecelem == node->type());
       }
 
+      template <typename T>
+      inline bool is_vector_celem_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_veccelem == node->type());
+      }
+
+      template <typename T>
+      inline bool is_vector_elem_rtc_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_vecelemrtc == node->type());
+      }
+
+      template <typename T>
+      inline bool is_vector_celem_rtc_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_veccelemrtc == node->type());
+      }
+
       template <typename T>
       inline bool is_rebasevector_elem_node(const expression_node<T>* node)
       {
          return node && (details::expression_node<T>::e_rbvecelem == node->type());
       }
 
+      template <typename T>
+      inline bool is_rebasevector_elem_rtc_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_rbvecelemrtc == node->type());
+      }
+
+      template <typename T>
+      inline bool is_rebasevector_celem_rtc_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_rbveccelemrtc == node->type());
+      }
+
       template <typename T>
       inline bool is_rebasevector_celem_node(const expression_node<T>* node)
       {
@@ -5344,7 +5795,8 @@ namespace exprtk
                case details::expression_node<T>::e_vecvecarith :
                case details::expression_node<T>::e_vecvalarith :
                case details::expression_node<T>::e_valvecarith :
-               case details::expression_node<T>::e_vecunaryop  : return true;
+               case details::expression_node<T>::e_vecunaryop  :
+               case details::expression_node<T>::e_vecondition : return true;
                default                                         : return false;
             }
          }
@@ -5355,7 +5807,11 @@ namespace exprtk
       template <typename T>
       inline bool is_constant_node(const expression_node<T>* node)
       {
-         return node && (details::expression_node<T>::e_constant == node->type());
+         return node &&
+         (
+           details::expression_node<T>::e_constant    == node->type() ||
+           details::expression_node<T>::e_stringconst == node->type()
+         );
       }
 
       template <typename T>
@@ -5388,6 +5844,12 @@ namespace exprtk
          return node && (details::expression_node<T>::e_function == node->type());
       }
 
+      template <typename T>
+      inline bool is_vararg_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_vararg == node->type());
+      }
+
       template <typename T>
       inline bool is_return_node(const expression_node<T>* node)
       {
@@ -5408,7 +5870,13 @@ namespace exprtk
       }
 
       template <typename T>
-      inline bool branch_deletable(expression_node<T>* node)
+      inline bool is_assert_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_assert == node->type());
+      }
+
+      template <typename T>
+      inline bool branch_deletable(const expression_node<T>* node)
       {
          return (0 != node)             &&
                 !is_variable_node(node) &&
@@ -5416,7 +5884,7 @@ namespace exprtk
       }
 
       template <std::size_t N, typename T>
-      inline bool all_nodes_valid(expression_node<T>* (&b)[N])
+      inline bool all_nodes_valid(expression_node<T>* const (&b)[N])
       {
          for (std::size_t i = 0; i < N; ++i)
          {
@@ -5440,7 +5908,7 @@ namespace exprtk
       }
 
       template <std::size_t N, typename T>
-      inline bool all_nodes_variables(expression_node<T>* (&b)[N])
+      inline bool all_nodes_variables(expression_node<T>* const (&b)[N])
       {
          for (std::size_t i = 0; i < N; ++i)
          {
@@ -5456,7 +5924,7 @@ namespace exprtk
       template <typename T,
                 typename Allocator,
                 template <typename, typename> class Sequence>
-      inline bool all_nodes_variables(Sequence<expression_node<T>*,Allocator>& b)
+      inline bool all_nodes_variables(const Sequence<expression_node<T>*,Allocator>& b)
       {
          for (std::size_t i = 0; i < b.size(); ++i)
          {
@@ -5490,7 +5958,7 @@ namespace exprtk
             for (std::size_t i = 0; i < node_delete_list.size(); ++i)
             {
                node_ptr_t& node = *node_delete_list[i];
-               exprtk_debug(("ncd::delete_nodes() - deleting: %p\n", static_cast<void*>(node)));
+               exprtk_debug(("ncd::delete_nodes() - deleting: %p\n", reinterpret_cast<void*>(node)));
                delete node;
                node = reinterpret_cast<node_ptr_t>(0);
             }
@@ -5587,12 +6055,15 @@ namespace exprtk
       template <typename Node>
       struct node_depth_base
       {
+         typedef Node* node_ptr_t;
+         typedef std::pair<node_ptr_t,bool> nb_pair_t;
+
          node_depth_base()
-         : depth_set(false),
-           depth(0)
+         : depth_set(false)
+         , depth(0)
          {}
 
-         virtual ~node_depth_base() = default;
+         virtual ~node_depth_base() exprtk_default;
 
          virtual std::size_t node_depth() const { return 1; }
 
@@ -5607,7 +6078,7 @@ namespace exprtk
             return depth;
          }
 
-         std::size_t compute_node_depth(const std::pair<Node*,bool>& branch) const
+         std::size_t compute_node_depth(const nb_pair_t& branch) const
          {
             if (!depth_set)
             {
@@ -5619,11 +6090,12 @@ namespace exprtk
          }
 
          template <std::size_t N>
-         std::size_t compute_node_depth(const std::pair<Node*,bool> (&branch)[N]) const
+         std::size_t compute_node_depth(const nb_pair_t (&branch)[N]) const
          {
             if (!depth_set)
             {
                depth = 0;
+
                for (std::size_t i = 0; i < N; ++i)
                {
                   if (branch[i].first)
@@ -5631,6 +6103,7 @@ namespace exprtk
                      depth = std::max(depth,branch[i].first->node_depth());
                   }
                }
+
                depth += 1;
                depth_set = true;
             }
@@ -5638,12 +6111,34 @@ namespace exprtk
             return depth;
          }
 
+         template <typename BranchType>
+         std::size_t max_node_depth(const BranchType& n0, const BranchType& n1) const
+         {
+            return std::max(compute_node_depth(n0), compute_node_depth(n1));
+         }
+
+         template <typename BranchType>
+         std::size_t max_node_depth(const BranchType& n0, const BranchType& n1, const BranchType& n2) const
+         {
+            return std::max(compute_node_depth(n0),
+                   std::max(compute_node_depth(n1), compute_node_depth(n2)));
+         }
+
+         template <typename BranchType>
+         std::size_t max_node_depth(const BranchType& n0, const BranchType& n1,
+                                    const BranchType& n2, const BranchType& n3) const
+         {
+            return std::max(
+                     std::max(compute_node_depth(n0), compute_node_depth(n1)),
+                     std::max(compute_node_depth(n2), compute_node_depth(n3)));
+         }
+
          template <typename BranchType>
          std::size_t compute_node_depth(const BranchType& n0, const BranchType& n1) const
          {
             if (!depth_set)
             {
-               depth = 1 + std::max(compute_node_depth(n0), compute_node_depth(n1));
+               depth = 1 + max_node_depth(n0, n1);
                depth_set = true;
             }
 
@@ -5656,9 +6151,7 @@ namespace exprtk
          {
             if (!depth_set)
             {
-               depth = 1 + std::max(
-                              std::max(compute_node_depth(n0), compute_node_depth(n1)),
-                              compute_node_depth(n2));
+               depth = 1 + max_node_depth(n0, n1, n2);
                depth_set = true;
             }
 
@@ -5671,9 +6164,7 @@ namespace exprtk
          {
             if (!depth_set)
             {
-               depth = 1 + std::max(
-                           std::max(compute_node_depth(n0), compute_node_depth(n1)),
-                           std::max(compute_node_depth(n2), compute_node_depth(n3)));
+               depth = 1 + max_node_depth(n0, n1, n2, n3);
                depth_set = true;
             }
 
@@ -5682,7 +6173,7 @@ namespace exprtk
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
-         std::size_t compute_node_depth(const Sequence<Node*, Allocator>& branch_list) const
+         std::size_t compute_node_depth(const Sequence<node_ptr_t, Allocator>& branch_list) const
          {
             if (!depth_set)
             {
@@ -5693,6 +6184,7 @@ namespace exprtk
                      depth = std::max(depth, compute_node_depth(branch_list[i]));
                   }
                }
+
                depth_set = true;
             }
 
@@ -5701,7 +6193,7 @@ namespace exprtk
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
-         std::size_t compute_node_depth(const Sequence<std::pair<Node*,bool>,Allocator>& branch_list) const
+         std::size_t compute_node_depth(const Sequence<nb_pair_t,Allocator>& branch_list) const
          {
             if (!depth_set)
             {
@@ -5712,6 +6204,7 @@ namespace exprtk
                      depth = std::max(depth, compute_node_depth(branch_list[i].first));
                   }
                }
+
                depth_set = true;
             }
 
@@ -5722,18 +6215,18 @@ namespace exprtk
          mutable std::size_t depth;
 
          template <typename NodeSequence>
-         void collect(Node*const& node,
+         void collect(node_ptr_t const& node,
                       const bool deletable,
                       NodeSequence& delete_node_list) const
          {
             if ((0 != node) && deletable)
             {
-               delete_node_list.push_back(const_cast<Node**>(&node));
+               delete_node_list.push_back(const_cast<node_ptr_t*>(&node));
             }
          }
 
          template <typename NodeSequence>
-         void collect(const std::pair<Node*, bool>& branch,
+         void collect(const nb_pair_t& branch,
                       NodeSequence& delete_node_list) const
          {
             collect(branch.first, branch.second, delete_node_list);
@@ -5747,7 +6240,7 @@ namespace exprtk
          }
 
          template <std::size_t N, typename NodeSequence>
-         void collect(const std::pair<Node*, bool>(&branch)[N],
+         void collect(const nb_pair_t(&branch)[N],
                       NodeSequence& delete_node_list) const
          {
             for (std::size_t i = 0; i < N; ++i)
@@ -5759,7 +6252,7 @@ namespace exprtk
          template <typename Allocator,
                    template <typename, typename> class Sequence,
                    typename NodeSequence>
-         void collect(const Sequence<std::pair<Node*, bool>, Allocator>& branch,
+         void collect(const Sequence<nb_pair_t, Allocator>& branch,
                       NodeSequence& delete_node_list) const
          {
             for (std::size_t i = 0; i < branch.size(); ++i)
@@ -5771,7 +6264,7 @@ namespace exprtk
          template <typename Allocator,
                    template <typename, typename> class Sequence,
                    typename NodeSequence>
-         void collect(const Sequence<Node*, Allocator>& branch_list,
+         void collect(const Sequence<node_ptr_t, Allocator>& branch_list,
                       NodeSequence& delete_node_list) const
          {
             for (std::size_t i = 0; i < branch_list.size(); ++i)
@@ -5785,7 +6278,7 @@ namespace exprtk
                    typename AllocatorB,
                    template <typename, typename> class Sequence,
                    typename NodeSequence>
-         void collect(const Sequence<Node*, AllocatorT>& branch_list,
+         void collect(const Sequence<node_ptr_t, AllocatorT>& branch_list,
                       const Sequence<Boolean, AllocatorB>& branch_deletable_list,
                       NodeSequence& delete_node_list) const
          {
@@ -5804,12 +6297,13 @@ namespace exprtk
          typedef Type value_type;
          typedef value_type* value_ptr;
          typedef const value_ptr const_value_ptr;
+         typedef vector_holder<Type> vector_holder_t;
 
          class vector_holder_base
          {
          public:
 
-            virtual ~vector_holder_base() = default;
+            virtual ~vector_holder_base() exprtk_default;
 
             inline value_ptr operator[](const std::size_t& index) const
             {
@@ -5821,6 +6315,11 @@ namespace exprtk
                return vector_size();
             }
 
+            inline std::size_t base_size() const
+            {
+               return vector_base_size();
+            }
+
             inline value_ptr data() const
             {
                return value_at(0);
@@ -5831,41 +6330,57 @@ namespace exprtk
                return false;
             }
 
-            virtual void set_ref(value_ptr*) {}
+            virtual void set_ref(value_ptr*)
+            {}
+
+            virtual void remove_ref(value_ptr*)
+            {}
+
+            virtual vector_view<Type>* rebaseable_instance()
+            {
+               return reinterpret_cast<vector_view<Type>*>(0);
+            }
 
          protected:
 
             virtual value_ptr value_at(const std::size_t&) const = 0;
             virtual std::size_t vector_size()              const = 0;
+            virtual std::size_t vector_base_size()         const = 0;
          };
 
-         class array_vector_impl : public vector_holder_base
+         class array_vector_impl exprtk_final : public vector_holder_base
          {
          public:
 
             array_vector_impl(const Type* vec, const std::size_t& vec_size)
-            : vec_(vec),
-              size_(vec_size)
+            : vec_(vec)
+            , size_(vec_size)
             {}
 
+            ~array_vector_impl() exprtk_override exprtk_default;
+
          protected:
 
-            value_ptr value_at(const std::size_t& index) const override
+            value_ptr value_at(const std::size_t& index) const exprtk_override
             {
-               if (index < size_)
-                  return const_cast<const_value_ptr>(vec_ + index);
-               else
-                  return const_value_ptr(0);
+               assert(index < size_);
+               return const_cast<const_value_ptr>(vec_ + index);
             }
 
-            std::size_t vector_size() const override
+            std::size_t vector_size() const exprtk_override
             {
                return size_;
             }
 
+            std::size_t vector_base_size() const exprtk_override
+            {
+               return vector_size();
+            }
+
          private:
 
-            array_vector_impl operator=(const array_vector_impl&);
+            array_vector_impl(const array_vector_impl&) exprtk_delete;
+            array_vector_impl& operator=(const array_vector_impl&) exprtk_delete;
 
             const Type* vec_;
             const std::size_t size_;
@@ -5873,36 +6388,45 @@ namespace exprtk
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
-         class sequence_vector_impl : public vector_holder_base
+         class sequence_vector_impl exprtk_final : public vector_holder_base
          {
          public:
 
             typedef Sequence<Type,Allocator> sequence_t;
 
-            sequence_vector_impl(sequence_t& seq)
+            explicit sequence_vector_impl(sequence_t& seq)
             : sequence_(seq)
             {}
 
+            ~sequence_vector_impl() exprtk_override exprtk_default;
+
          protected:
 
-            value_ptr value_at(const std::size_t& index) const override
+            value_ptr value_at(const std::size_t& index) const exprtk_override
             {
-               return (index < sequence_.size()) ? (&sequence_[index]) : const_value_ptr(0);
+               assert(index < sequence_.size());
+               return (&sequence_[index]);
             }
 
-            std::size_t vector_size() const override
+            std::size_t vector_size() const exprtk_override
             {
                return sequence_.size();
             }
 
+            std::size_t vector_base_size() const exprtk_override
+            {
+               return vector_size();
+            }
+
          private:
 
-            sequence_vector_impl operator=(const sequence_vector_impl&);
+            sequence_vector_impl(const sequence_vector_impl&) exprtk_delete;
+            sequence_vector_impl& operator=(const sequence_vector_impl&) exprtk_delete;
 
             sequence_t& sequence_;
          };
 
-         class vector_view_impl : public vector_holder_base
+         class vector_view_impl exprtk_final : public vector_holder_base
          {
          public:
 
@@ -5910,37 +6434,113 @@ namespace exprtk
 
             vector_view_impl(vector_view_t& vec_view)
             : vec_view_(vec_view)
-            {}
+            {
+               assert(vec_view_.size() > 0);
+            }
 
-            void set_ref(value_ptr* ref) override
+            void set_ref(value_ptr* ref) exprtk_override
             {
                vec_view_.set_ref(ref);
             }
 
-            inline bool rebaseable() const override
+            void remove_ref(value_ptr* ref) exprtk_override
+            {
+               vec_view_.remove_ref(ref);
+            }
+
+            bool rebaseable() const exprtk_override
             {
                return true;
             }
 
+            vector_view<Type>* rebaseable_instance() exprtk_override
+            {
+               return &vec_view_;
+            }
+
          protected:
 
-            value_ptr value_at(const std::size_t& index) const override
+            value_ptr value_at(const std::size_t& index) const exprtk_override
             {
-               return (index < vec_view_.size()) ? (&vec_view_[index]) : const_value_ptr(0);
+               assert(index < vec_view_.size());
+               return (&vec_view_[index]);
             }
 
-            std::size_t vector_size() const override
+            std::size_t vector_size() const exprtk_override
             {
                return vec_view_.size();
             }
 
+            std::size_t vector_base_size() const exprtk_override
+            {
+               return vec_view_.base_size();
+            }
+
+            ~vector_view_impl() exprtk_override exprtk_default;
+
          private:
 
-            vector_view_impl operator=(const vector_view_impl&);
+            vector_view_impl(const vector_view_impl&) exprtk_delete;
+            vector_view_impl& operator=(const vector_view_impl&) exprtk_delete;
 
             vector_view_t& vec_view_;
          };
 
+         class resizable_vector_impl exprtk_final : public vector_holder_base
+         {
+         public:
+
+            resizable_vector_impl(vector_holder& vec_view_holder,
+                                  const Type* vec,
+                                  const std::size_t& vec_size)
+            : vec_(vec)
+            , size_(vec_size)
+            , vec_view_holder_(*vec_view_holder.rebaseable_instance())
+            {
+               assert(vec_view_holder.rebaseable_instance());
+               assert(size_ <= vector_base_size());
+            }
+
+            ~resizable_vector_impl() exprtk_override exprtk_default;
+
+         protected:
+
+            value_ptr value_at(const std::size_t& index) const exprtk_override
+            {
+               assert(index < vector_size());
+               return const_cast<const_value_ptr>(vec_ + index);
+            }
+
+            std::size_t vector_size() const exprtk_override
+            {
+               return vec_view_holder_.size();
+            }
+
+            std::size_t vector_base_size() const exprtk_override
+            {
+               return vec_view_holder_.base_size();
+            }
+
+            bool rebaseable() const exprtk_override
+            {
+               return true;
+            }
+
+            vector_view<Type>* rebaseable_instance() exprtk_override
+            {
+               return &vec_view_holder_;
+            }
+
+         private:
+
+            resizable_vector_impl(const resizable_vector_impl&) exprtk_delete;
+            resizable_vector_impl& operator=(const resizable_vector_impl&) exprtk_delete;
+
+            const Type* vec_;
+            const std::size_t size_;
+            vector_view<Type>& vec_view_holder_;
+         };
+
       public:
 
          typedef typename details::vec_data_store<Type> vds_t;
@@ -5949,19 +6549,23 @@ namespace exprtk
          : vector_holder_base_(new(buffer)array_vector_impl(vec,vec_size))
          {}
 
-         vector_holder(const vds_t& vds)
+         explicit vector_holder(const vds_t& vds)
          : vector_holder_base_(new(buffer)array_vector_impl(vds.data(),vds.size()))
          {}
 
          template <typename Allocator>
-         vector_holder(std::vector<Type,Allocator>& vec)
+         explicit vector_holder(std::vector<Type,Allocator>& vec)
          : vector_holder_base_(new(buffer)sequence_vector_impl<Allocator,std::vector>(vec))
          {}
 
-         vector_holder(exprtk::vector_view<Type>& vec)
+         explicit vector_holder(exprtk::vector_view<Type>& vec)
          : vector_holder_base_(new(buffer)vector_view_impl(vec))
          {}
 
+         explicit vector_holder(vector_holder_t& vec_holder, const vds_t& vds)
+         : vector_holder_base_(new(buffer)resizable_vector_impl(vec_holder, vds.data(), vds.size()))
+         {}
+
          inline value_ptr operator[](const std::size_t& index) const
          {
             return (*vector_holder_base_)[index];
@@ -5972,6 +6576,11 @@ namespace exprtk
             return vector_holder_base_->size();
          }
 
+         inline std::size_t base_size() const
+         {
+            return vector_holder_base_->base_size();
+         }
+
          inline value_ptr data() const
          {
             return vector_holder_base_->data();
@@ -5979,7 +6588,18 @@ namespace exprtk
 
          void set_ref(value_ptr* ref)
          {
-            vector_holder_base_->set_ref(ref);
+            if (rebaseable())
+            {
+               vector_holder_base_->set_ref(ref);
+            }
+         }
+
+         void remove_ref(value_ptr* ref)
+         {
+            if (rebaseable())
+            {
+               vector_holder_base_->remove_ref(ref);
+            }
          }
 
          bool rebaseable() const
@@ -5987,23 +6607,31 @@ namespace exprtk
             return vector_holder_base_->rebaseable();
          }
 
+         vector_view<Type>* rebaseable_instance()
+         {
+            return vector_holder_base_->rebaseable_instance();
+         }
+
       private:
 
+         vector_holder(const vector_holder<Type>&) exprtk_delete;
+         vector_holder<Type>& operator=(const vector_holder<Type>&) exprtk_delete;
+
          mutable vector_holder_base* vector_holder_base_;
          uchar_t buffer[64];
       };
 
       template <typename T>
-      class null_node : public expression_node<T>
+      class null_node exprtk_final : public expression_node<T>
       {
       public:
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_null;
          }
@@ -6055,7 +6683,7 @@ namespace exprtk
       }
 
       template <typename T>
-      class null_eq_node : public expression_node<T>
+      class null_eq_node exprtk_final : public expression_node<T>
       {
       public:
 
@@ -6066,42 +6694,41 @@ namespace exprtk
          : equality_(equality)
          {
             construct_branch_pair(branch_, branch);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(branch_.first);
-
             const T v = branch_.first->value();
             const bool result = details::numeric::is_nan(v);
 
             if (result)
-               return (equality_) ? T(1) : T(0);
+               return equality_ ? T(1) : T(0);
             else
-               return (equality_) ? T(0) : T(1);
+               return equality_ ? T(0) : T(1);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_nulleq;
          }
 
-         inline operator_type operation() const
+         inline expression_node<T>* branch(const std::size_t&) const exprtk_override
          {
-            return details::e_eq;
+            return branch_.first;
          }
 
-         inline expression_node<T>* branch(const std::size_t&) const override
+         inline bool valid() const exprtk_override
          {
             return branch_.first;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            expression_node<T>::ndb_t::collect(branch_,node_delete_list);
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
@@ -6113,7 +6740,7 @@ namespace exprtk
       };
 
       template <typename T>
-      class literal_node : public expression_node<T>
+      class literal_node exprtk_final : public expression_node<T>
       {
       public:
 
@@ -6121,25 +6748,25 @@ namespace exprtk
          : value_(v)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return value_;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_constant;
          }
 
-         inline expression_node<T>* branch(const std::size_t&) const override
+         inline expression_node<T>* branch(const std::size_t&) const exprtk_override
          {
             return reinterpret_cast<expression_node<T>*>(0);
          }
 
       private:
 
-         literal_node(literal_node<T>&) {}
-         literal_node<T>& operator=(literal_node<T>&) { return (*this); }
+         literal_node(const literal_node<T>&) exprtk_delete;
+         literal_node<T>& operator=(const literal_node<T>&) exprtk_delete;
 
          const T value_;
       };
@@ -6157,7 +6784,7 @@ namespace exprtk
 
          typedef range_pack<T> range_t;
 
-         virtual ~range_interface() = default;
+         virtual ~range_interface() exprtk_default;
 
          virtual range_t& range_ref() = 0;
 
@@ -6172,7 +6799,7 @@ namespace exprtk
 
          typedef range_data_type<T> range_data_type_t;
 
-         virtual ~string_base_node() = default;
+         virtual ~string_base_node() exprtk_default;
 
          virtual std::string str () const = 0;
 
@@ -6182,9 +6809,10 @@ namespace exprtk
       };
 
       template <typename T>
-      class string_literal_node : public expression_node <T>,
-                                  public string_base_node<T>,
-                                  public range_interface <T>
+      class string_literal_node exprtk_final
+                                : public expression_node <T>
+                                , public string_base_node<T>
+                                , public range_interface <T>
       {
       public:
 
@@ -6193,56 +6821,56 @@ namespace exprtk
          explicit string_literal_node(const std::string& v)
          : value_(v)
          {
-            rp_.n0_c = std::make_pair<bool,std::size_t>(true,0);
-            rp_.n1_c = std::make_pair<bool,std::size_t>(true,v.size() - 1);
+            rp_.n0_c = std::make_pair<bool,std::size_t>(true, 0);
+            rp_.n1_c = std::make_pair<bool,std::size_t>(true, v.size());
             rp_.cache.first  = rp_.n0_c.second;
             rp_.cache.second = rp_.n1_c.second;
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_stringconst;
          }
 
-         inline expression_node<T>* branch(const std::size_t&) const override
+         inline expression_node<T>* branch(const std::size_t&) const exprtk_override
          {
             return reinterpret_cast<expression_node<T>*>(0);
          }
 
-         std::string str() const override
+         std::string str() const exprtk_override
          {
             return value_;
          }
 
-         char_cptr base() const override
+         char_cptr base() const exprtk_override
          {
             return value_.data();
          }
 
-         std::size_t size() const override
+         std::size_t size() const exprtk_override
          {
             return value_.size();
          }
 
-         range_t& range_ref() override
+         range_t& range_ref() exprtk_override
          {
             return rp_;
          }
 
-         const range_t& range_ref() const override
+         const range_t& range_ref() const exprtk_override
          {
             return rp_;
          }
 
       private:
 
-         string_literal_node(const string_literal_node<T>&);
-         string_literal_node<T>& operator=(const string_literal_node<T>&);
+         string_literal_node(const string_literal_node<T>&) exprtk_delete;
+         string_literal_node<T>& operator=(const string_literal_node<T>&) exprtk_delete;
 
          const std::string value_;
          range_t rp_;
@@ -6261,48 +6889,51 @@ namespace exprtk
          : operation_(opr)
          {
             construct_branch_pair(branch_,branch);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(branch_.first);
-
-            const T arg = branch_.first->value();
-
-            return numeric::process<T>(operation_,arg);
+            return numeric::process<T>
+                     (operation_,branch_.first->value());
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_unary;
          }
 
-         inline operator_type operation() const
+         inline operator_type operation()
          {
             return operation_;
          }
 
-         inline expression_node<T>* branch(const std::size_t&) const override
+         inline expression_node<T>* branch(const std::size_t&) const exprtk_override
          {
             return branch_.first;
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return branch_.first && branch_.first->valid();
+         }
+
          inline void release()
          {
             branch_.second = false;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
             expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_final
          {
             return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
-      protected:
+      private:
 
          operator_type operation_;
          branch_t branch_;
@@ -6322,20 +6953,20 @@ namespace exprtk
          : operation_(opr)
          {
             init_branches<2>(branch_, branch0, branch1);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(branch_[0].first);
-            assert(branch_[1].first);
-
-            const T arg0 = branch_[0].first->value();
-            const T arg1 = branch_[1].first->value();
-
-            return numeric::process<T>(operation_,arg0,arg1);
+            return numeric::process<T>
+                   (
+                      operation_,
+                      branch_[0].first->value(),
+                      branch_[1].first->value()
+                   );
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_binary;
          }
@@ -6345,34 +6976,37 @@ namespace exprtk
             return operation_;
          }
 
-         inline expression_node<T>* branch(const std::size_t& index = 0) const override
+         inline expression_node<T>* branch(const std::size_t& index = 0) const exprtk_override
          {
-            if (0 == index)
-               return branch_[0].first;
-            else if (1 == index)
-               return branch_[1].first;
-            else
-               return reinterpret_cast<expression_ptr>(0);
+            assert(index < 2);
+            return branch_[index].first;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return
+               branch_[0].first && branch_[0].first->valid() &&
+               branch_[1].first && branch_[1].first->valid() ;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            expression_node<T>::ndb_t::template collect<>(branch_, node_delete_list);
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_final
          {
             return expression_node<T>::ndb_t::template compute_node_depth<2>(branch_);
          }
 
-      protected:
+      private:
 
          operator_type operation_;
          branch_t branch_[2];
       };
 
       template <typename T, typename Operation>
-      class binary_ext_node : public expression_node<T>
+      class binary_ext_node exprtk_final : public expression_node<T>
       {
       public:
 
@@ -6382,20 +7016,17 @@ namespace exprtk
          binary_ext_node(expression_ptr branch0, expression_ptr branch1)
          {
             init_branches<2>(branch_, branch0, branch1);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(branch_[0].first);
-            assert(branch_[1].first);
-
             const T arg0 = branch_[0].first->value();
             const T arg1 = branch_[1].first->value();
-
             return Operation::process(arg0,arg1);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_binary_ext;
          }
@@ -6405,22 +7036,25 @@ namespace exprtk
             return Operation::operation();
          }
 
-         inline expression_node<T>* branch(const std::size_t& index = 0) const override
+         inline expression_node<T>* branch(const std::size_t& index = 0) const exprtk_override
          {
-            if (0 == index)
-               return branch_[0].first;
-            else if (1 == index)
-               return branch_[1].first;
-            else
-               return reinterpret_cast<expression_ptr>(0);
+            assert(index < 2);
+            return branch_[index].first;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return
+               branch_[0].first && branch_[0].first->valid() &&
+               branch_[1].first && branch_[1].first->valid() ;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            expression_node<T>::ndb_t::template collect<>(branch_, node_delete_list);
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::template compute_node_depth<2>(branch_);
          }
@@ -6445,14 +7079,11 @@ namespace exprtk
          : operation_(opr)
          {
             init_branches<3>(branch_, branch0, branch1, branch2);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(branch_[0].first);
-            assert(branch_[1].first);
-            assert(branch_[2].first);
-
             const T arg0 = branch_[0].first->value();
             const T arg1 = branch_[1].first->value();
             const T arg2 = branch_[2].first->value();
@@ -6473,17 +7104,25 @@ namespace exprtk
             }
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_trinary;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
          {
-            expression_node<T>::ndb_t::template collect<>(branch_, node_delete_list);
+            return
+               branch_[0].first && branch_[0].first->valid() &&
+               branch_[1].first && branch_[1].first->valid() &&
+               branch_[2].first && branch_[2].first->valid() ;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_final
          {
             return expression_node<T>::ndb_t::template compute_node_depth<3>(branch_);
          }
@@ -6512,26 +7151,35 @@ namespace exprtk
             init_branches<4>(branch_, branch0, branch1, branch2, branch3);
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_quaternary;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            expression_node<T>::ndb_t::template collect<>(branch_, node_delete_list);
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_final
          {
             return expression_node<T>::ndb_t::template compute_node_depth<4>(branch_);
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return
+               branch_[0].first && branch_[0].first->valid() &&
+               branch_[1].first && branch_[1].first->valid() &&
+               branch_[2].first && branch_[2].first->valid() &&
+               branch_[3].first && branch_[3].first->valid() ;
+         }
+
       protected:
 
          operator_type operation_;
@@ -6539,7 +7187,7 @@ namespace exprtk
       };
 
       template <typename T>
-      class conditional_node : public expression_node<T>
+      class conditional_node exprtk_final : public expression_node<T>
       {
       public:
 
@@ -6553,33 +7201,38 @@ namespace exprtk
             construct_branch_pair(condition_  , condition  );
             construct_branch_pair(consequent_ , consequent );
             construct_branch_pair(alternative_, alternative);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(condition_  .first);
-            assert(consequent_ .first);
-            assert(alternative_.first);
-
             if (is_true(condition_))
                return consequent_.first->value();
             else
                return alternative_.first->value();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_conditional;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
          {
-            expression_node<T>::ndb_t::collect(condition_  , node_delete_list);
-            expression_node<T>::ndb_t::collect(consequent_ , node_delete_list);
-            expression_node<T>::ndb_t::collect(alternative_, node_delete_list);
+            return
+               condition_  .first && condition_  .first->valid() &&
+               consequent_ .first && consequent_ .first->valid() &&
+               alternative_.first && alternative_.first->valid() ;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(condition_   , node_delete_list);
+            expression_node<T>::ndb_t::collect(consequent_  , node_delete_list);
+            expression_node<T>::ndb_t::collect(alternative_ , node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth
                (condition_, consequent_, alternative_);
@@ -6593,7 +7246,7 @@ namespace exprtk
       };
 
       template <typename T>
-      class cons_conditional_node : public expression_node<T>
+      class cons_conditional_node exprtk_final : public expression_node<T>
       {
       public:
 
@@ -6606,31 +7259,36 @@ namespace exprtk
          {
             construct_branch_pair(condition_ , condition );
             construct_branch_pair(consequent_, consequent);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(condition_ .first);
-            assert(consequent_.first);
-
             if (is_true(condition_))
                return consequent_.first->value();
             else
                return std::numeric_limits<T>::quiet_NaN();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_conditional;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
          {
-            expression_node<T>::ndb_t::collect(condition_ , node_delete_list);
-            expression_node<T>::ndb_t::collect(consequent_, node_delete_list);
+            return
+               condition_ .first && condition_ .first->valid() &&
+               consequent_.first && consequent_.first->valid() ;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(condition_  , node_delete_list);
+            expression_node<T>::ndb_t::collect(consequent_ , node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::
                compute_node_depth(condition_, consequent_);
@@ -6655,41 +7313,45 @@ namespace exprtk
          T value;
       };
 
-      class continue_exception
-      {};
+      class continue_exception {};
 
       template <typename T>
-      class break_node : public expression_node<T>
+      class break_node exprtk_final : public expression_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
          typedef std::pair<expression_ptr,bool> branch_t;
 
-         break_node(expression_ptr ret = expression_ptr(0))
+         explicit break_node(expression_ptr ret = expression_ptr(0))
          {
             construct_branch_pair(return_, ret);
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            throw break_exception<T>(return_.first ? return_.first->value() : std::numeric_limits<T>::quiet_NaN());
-            #ifndef _MSC_VER
+            const T result = return_.first ?
+                             return_.first->value() :
+                             std::numeric_limits<T>::quiet_NaN();
+
+            throw break_exception<T>(result);
+
+            #if !defined(_MSC_VER) && !defined(__NVCOMPILER)
             return std::numeric_limits<T>::quiet_NaN();
             #endif
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_break;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
             expression_node<T>::ndb_t::collect(return_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(return_);
          }
@@ -6700,34 +7362,36 @@ namespace exprtk
       };
 
       template <typename T>
-      class continue_node : public expression_node<T>
+      class continue_node exprtk_final : public expression_node<T>
       {
       public:
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             throw continue_exception();
-            #ifndef _MSC_VER
+            #if !defined(_MSC_VER) && !defined(__NVCOMPILER)
             return std::numeric_limits<T>::quiet_NaN();
             #endif
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_break;
          }
       };
       #endif
 
-      #ifdef exprtk_enable_runtime_checks
       struct loop_runtime_checker
       {
-         loop_runtime_checker(loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0),
+         loop_runtime_checker(loop_runtime_check_ptr loop_runtime_check,
                               loop_runtime_check::loop_types lp_typ = loop_runtime_check::e_invalid)
-         : iteration_count_(0),
-           loop_runtime_check_(loop_rt_chk),
-           loop_type(lp_typ)
-         {}
+         : iteration_count_(0)
+         , loop_runtime_check_(loop_runtime_check)
+         , max_loop_iterations_(loop_runtime_check_->max_loop_iterations)
+         , loop_type_(lp_typ)
+         {
+            assert(loop_runtime_check_);
+         }
 
          inline void reset(const _uint64_t initial_value = 0) const
          {
@@ -6736,16 +7400,18 @@ namespace exprtk
 
          inline bool check() const
          {
+            assert(loop_runtime_check_);
+
             if (
-                 (0 == loop_runtime_check_) ||
-                 (++iteration_count_ <= loop_runtime_check_->max_loop_iterations)
+                 (++iteration_count_ <= max_loop_iterations_) &&
+                 loop_runtime_check_->check()
                )
             {
                return true;
             }
 
             loop_runtime_check::violation_context ctxt;
-            ctxt.loop      = loop_type;
+            ctxt.loop      = loop_type_;
             ctxt.violation = loop_runtime_check::e_iteration_count;
 
             loop_runtime_check_->handle_runtime_violation(ctxt);
@@ -6753,29 +7419,19 @@ namespace exprtk
             return false;
          }
 
-         mutable _uint64_t iteration_count_;
-         mutable loop_runtime_check_ptr loop_runtime_check_;
-         loop_runtime_check::loop_types loop_type;
-      };
-      #else
-      struct loop_runtime_checker
-      {
-         loop_runtime_checker(loop_runtime_check_ptr, loop_runtime_check::loop_types)
-         {}
-
-         inline void reset(const _uint64_t = 0) const
-         {}
-
-         inline bool check() const
+         bool valid() const
          {
-            return true;
+            return 0 != loop_runtime_check_;
          }
+
+         mutable _uint64_t iteration_count_;
+         mutable loop_runtime_check_ptr loop_runtime_check_;
+         const details::_uint64_t& max_loop_iterations_;
+         loop_runtime_check::loop_types loop_type_;
       };
-      #endif
 
       template <typename T>
-      class while_loop_node : public expression_node<T>,
-                              public loop_runtime_checker
+      class while_loop_node : public expression_node<T>
       {
       public:
 
@@ -6783,24 +7439,18 @@ namespace exprtk
          typedef std::pair<expression_ptr,bool> branch_t;
 
          while_loop_node(expression_ptr condition,
-                         expression_ptr loop_body,
-                         loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
-         : loop_runtime_checker(loop_rt_chk,loop_runtime_check::e_while_loop)
+                         expression_ptr loop_body)
          {
             construct_branch_pair(condition_, condition);
             construct_branch_pair(loop_body_, loop_body);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(condition_.first);
-            assert(loop_body_.first);
-
             T result = T(0);
 
-            loop_runtime_checker::reset();
-
-            while (is_true(condition_) && loop_runtime_checker::check())
+            while (is_true(condition_))
             {
                result = loop_body_.first->value();
             }
@@ -6808,31 +7458,80 @@ namespace exprtk
             return result;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_while;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
+         {
+            return
+               condition_.first && condition_.first->valid() &&
+               loop_body_.first && loop_body_.first->valid() ;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            expression_node<T>::ndb_t::collect(condition_, node_delete_list);
-            expression_node<T>::ndb_t::collect(loop_body_, node_delete_list);
+            expression_node<T>::ndb_t::collect(condition_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_ , node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(condition_, loop_body_);
          }
 
-      private:
+      protected:
 
          branch_t condition_;
          branch_t loop_body_;
       };
 
       template <typename T>
-      class repeat_until_loop_node : public expression_node<T>,
-                                     public loop_runtime_checker
+      class while_loop_rtc_node exprtk_final
+                                : public while_loop_node<T>
+                                , public loop_runtime_checker
+      {
+      public:
+
+         typedef while_loop_node<T>  parent_t;
+         typedef expression_node<T>* expression_ptr;
+
+         while_loop_rtc_node(expression_ptr condition,
+                             expression_ptr loop_body,
+                             loop_runtime_check_ptr loop_rt_chk)
+         : parent_t(condition, loop_body)
+         , loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_while_loop)
+         {
+            assert(valid());
+         }
+
+         inline T value() const exprtk_override
+         {
+
+            T result = T(0);
+
+            loop_runtime_checker::reset();
+
+            while (is_true(parent_t::condition_) && loop_runtime_checker::check())
+            {
+               result = parent_t::loop_body_.first->value();
+            }
+
+            return result;
+         }
+
+         using parent_t::valid;
+
+         bool valid() const exprtk_final
+         {
+            return parent_t::valid() &&
+                   loop_runtime_checker::valid();
+         }
+      };
+
+      template <typename T>
+      class repeat_until_loop_node : public expression_node<T>
       {
       public:
 
@@ -6840,57 +7539,100 @@ namespace exprtk
          typedef std::pair<expression_ptr,bool> branch_t;
 
          repeat_until_loop_node(expression_ptr condition,
-                                expression_ptr loop_body,
-                                loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
-         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_repeat_until_loop)
+                                expression_ptr loop_body)
          {
             construct_branch_pair(condition_, condition);
             construct_branch_pair(loop_body_, loop_body);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(condition_.first);
-            assert(loop_body_.first);
-
             T result = T(0);
 
-            loop_runtime_checker::reset(1);
-
             do
             {
                result = loop_body_.first->value();
             }
-            while (is_false(condition_.first) && loop_runtime_checker::check());
+            while (is_false(condition_.first));
 
             return result;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_repeat;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
          {
-            expression_node<T>::ndb_t::collect(condition_, node_delete_list);
-            expression_node<T>::ndb_t::collect(loop_body_, node_delete_list);
+            return
+               condition_.first && condition_.first->valid() &&
+               loop_body_.first && loop_body_.first->valid() ;
          }
 
-         std::size_t node_depth() const override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(condition_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_ , node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(condition_, loop_body_);
          }
 
-      private:
+      protected:
 
          branch_t condition_;
          branch_t loop_body_;
       };
 
       template <typename T>
-      class for_loop_node : public expression_node<T>,
-                            public loop_runtime_checker
+      class repeat_until_loop_rtc_node exprtk_final
+                                       : public repeat_until_loop_node<T>
+                                       , public loop_runtime_checker
+      {
+      public:
+
+         typedef repeat_until_loop_node<T> parent_t;
+         typedef expression_node<T>*       expression_ptr;
+
+         repeat_until_loop_rtc_node(expression_ptr condition,
+                                    expression_ptr loop_body,
+                                    loop_runtime_check_ptr loop_rt_chk)
+         : parent_t(condition, loop_body)
+         , loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_repeat_until_loop)
+         {
+            assert(valid());
+         }
+
+         inline T value() const exprtk_override
+         {
+            T result = T(0);
+
+            loop_runtime_checker::reset(1);
+
+            do
+            {
+               result = parent_t::loop_body_.first->value();
+            }
+            while (is_false(parent_t::condition_.first) && loop_runtime_checker::check());
+
+            return result;
+         }
+
+         using parent_t::valid;
+
+         inline bool valid() const exprtk_final
+         {
+            return parent_t::valid() &&
+                   loop_runtime_checker::valid();
+         }
+      };
+
+      template <typename T>
+      class for_loop_node : public expression_node<T>
       {
       public:
 
@@ -6900,31 +7642,25 @@ namespace exprtk
          for_loop_node(expression_ptr initialiser,
                        expression_ptr condition,
                        expression_ptr incrementor,
-                       expression_ptr loop_body,
-                       loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
-         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_for_loop)
+                       expression_ptr loop_body)
          {
             construct_branch_pair(initialiser_, initialiser);
             construct_branch_pair(condition_  , condition  );
             construct_branch_pair(incrementor_, incrementor);
             construct_branch_pair(loop_body_  , loop_body  );
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(condition_.first);
-            assert(loop_body_.first);
-
             T result = T(0);
 
-            loop_runtime_checker::reset();
-
             if (initialiser_.first)
                initialiser_.first->value();
 
             if (incrementor_.first)
             {
-               while (is_true(condition_) && loop_runtime_checker::check())
+               while (is_true(condition_))
                {
                   result = loop_body_.first->value();
                   incrementor_.first->value();
@@ -6932,7 +7668,7 @@ namespace exprtk
             }
             else
             {
-               while (is_true(condition_) && loop_runtime_checker::check())
+               while (is_true(condition_))
                {
                   result = loop_body_.first->value();
                }
@@ -6941,26 +7677,31 @@ namespace exprtk
             return result;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_for;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
          {
-            expression_node<T>::ndb_t::collect(initialiser_, node_delete_list);
-            expression_node<T>::ndb_t::collect(condition_  , node_delete_list);
-            expression_node<T>::ndb_t::collect(incrementor_, node_delete_list);
-            expression_node<T>::ndb_t::collect(loop_body_  , node_delete_list);
+            return condition_.first && loop_body_.first;
          }
 
-         std::size_t node_depth() const override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(initialiser_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(condition_   , node_delete_list);
+            expression_node<T>::ndb_t::collect(incrementor_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_   , node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth
                (initialiser_, condition_, incrementor_, loop_body_);
          }
 
-      private:
+      protected:
 
          branch_t initialiser_;
          branch_t condition_  ;
@@ -6968,39 +7709,89 @@ namespace exprtk
          branch_t loop_body_  ;
       };
 
-      #ifndef exprtk_disable_break_continue
       template <typename T>
-      class while_loop_bc_node : public expression_node<T>,
-                                 public loop_runtime_checker
+      class for_loop_rtc_node exprtk_final
+                              : public for_loop_node<T>
+                              , public loop_runtime_checker
       {
       public:
 
+         typedef for_loop_node<T>    parent_t;
          typedef expression_node<T>* expression_ptr;
-         typedef std::pair<expression_ptr,bool> branch_t;
 
-         while_loop_bc_node(expression_ptr condition,
-                            expression_ptr loop_body,
-                            loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
-         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_while_loop)
+         for_loop_rtc_node(expression_ptr initialiser,
+                           expression_ptr condition,
+                           expression_ptr incrementor,
+                           expression_ptr loop_body,
+                           loop_runtime_check_ptr loop_rt_chk)
+         : parent_t(initialiser, condition, incrementor, loop_body)
+         , loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_for_loop)
          {
-            construct_branch_pair(condition_, condition);
-            construct_branch_pair(loop_body_, loop_body);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(condition_.first);
-            assert(loop_body_.first);
-
             T result = T(0);
 
             loop_runtime_checker::reset();
 
-            while (is_true(condition_) && loop_runtime_checker::check())
+            if (parent_t::initialiser_.first)
+               parent_t::initialiser_.first->value();
+
+            if (parent_t::incrementor_.first)
+            {
+               while (is_true(parent_t::condition_) && loop_runtime_checker::check())
+               {
+                  result = parent_t::loop_body_.first->value();
+                  parent_t::incrementor_.first->value();
+               }
+            }
+            else
+            {
+               while (is_true(parent_t::condition_) && loop_runtime_checker::check())
+               {
+                  result = parent_t::loop_body_.first->value();
+               }
+            }
+
+            return result;
+         }
+
+         using parent_t::valid;
+
+         inline bool valid() const exprtk_final
+         {
+            return parent_t::valid() &&
+                   loop_runtime_checker::valid();
+         }
+      };
+
+      #ifndef exprtk_disable_break_continue
+      template <typename T>
+      class while_loop_bc_node : public while_loop_node<T>
+      {
+      public:
+
+         typedef while_loop_node<T>  parent_t;
+         typedef expression_node<T>* expression_ptr;
+
+         while_loop_bc_node(expression_ptr condition,
+                            expression_ptr loop_body)
+         : parent_t(condition, loop_body)
+         {
+            assert(parent_t::valid());
+         }
+
+         inline T value() const exprtk_override
+         {
+            T result = T(0);
+
+            while (is_true(parent_t::condition_))
             {
                try
                {
-                  result = loop_body_.first->value();
+                  result = parent_t::loop_body_.first->value();
                }
                catch(const break_exception<T>& e)
                {
@@ -7012,61 +7803,83 @@ namespace exprtk
 
             return result;
          }
+      };
 
-         inline typename expression_node<T>::node_type type() const override
-         {
-            return expression_node<T>::e_while;
-         }
+      template <typename T>
+      class while_loop_bc_rtc_node exprtk_final
+                                   : public while_loop_bc_node<T>
+                                   , public loop_runtime_checker
+      {
+      public:
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         typedef while_loop_bc_node<T> parent_t;
+         typedef expression_node<T>*   expression_ptr;
+
+         while_loop_bc_rtc_node(expression_ptr condition,
+                                expression_ptr loop_body,
+                                loop_runtime_check_ptr loop_rt_chk)
+         : parent_t(condition, loop_body)
+         , loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_while_loop)
          {
-            expression_node<T>::ndb_t::collect(condition_, node_delete_list);
-            expression_node<T>::ndb_t::collect(loop_body_, node_delete_list);
+            assert(valid());
          }
 
-         std::size_t node_depth() const override
+         inline T value() const exprtk_override
          {
-            return expression_node<T>::ndb_t::compute_node_depth(condition_, loop_body_);
+            T result = T(0);
+
+            loop_runtime_checker::reset();
+
+            while (is_true(parent_t::condition_) && loop_runtime_checker::check())
+            {
+               try
+               {
+                  result = parent_t::loop_body_.first->value();
+               }
+               catch(const break_exception<T>& e)
+               {
+                  return e.value;
+               }
+               catch(const continue_exception&)
+               {}
+            }
+
+            return result;
          }
 
-      private:
+         using parent_t::valid;
 
-         branch_t condition_;
-         branch_t loop_body_;
+         inline bool valid() const exprtk_final
+         {
+            return parent_t::valid() &&
+                   loop_runtime_checker::valid();
+         }
       };
 
       template <typename T>
-      class repeat_until_loop_bc_node : public expression_node<T>,
-                                        public loop_runtime_checker
+      class repeat_until_loop_bc_node : public repeat_until_loop_node<T>
       {
       public:
 
-         typedef expression_node<T>* expression_ptr;
-         typedef std::pair<expression_ptr,bool> branch_t;
+         typedef repeat_until_loop_node<T> parent_t;
+         typedef expression_node<T>*       expression_ptr;
 
          repeat_until_loop_bc_node(expression_ptr condition,
-                                   expression_ptr loop_body,
-                                   loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
-         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_repeat_until_loop)
+                                   expression_ptr loop_body)
+         : parent_t(condition, loop_body)
          {
-            construct_branch_pair(condition_, condition);
-            construct_branch_pair(loop_body_, loop_body);
+            assert(parent_t::valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(condition_.first);
-            assert(loop_body_.first);
-
             T result = T(0);
 
-            loop_runtime_checker::reset();
-
             do
             {
                try
                {
-                  result = loop_body_.first->value();
+                  result = parent_t::loop_body_.first->value();
                }
                catch(const break_exception<T>& e)
                {
@@ -7075,74 +7888,95 @@ namespace exprtk
                catch(const continue_exception&)
                {}
             }
-            while (is_false(condition_.first) && loop_runtime_checker::check());
+            while (is_false(parent_t::condition_.first));
 
             return result;
          }
+      };
 
-         inline typename expression_node<T>::node_type type() const override
-         {
-            return expression_node<T>::e_repeat;
-         }
+      template <typename T>
+      class repeat_until_loop_bc_rtc_node exprtk_final
+                                          : public repeat_until_loop_bc_node<T>
+                                          , public loop_runtime_checker
+      {
+      public:
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         typedef repeat_until_loop_bc_node<T> parent_t;
+         typedef expression_node<T>*          expression_ptr;
+
+         repeat_until_loop_bc_rtc_node(expression_ptr condition,
+                                       expression_ptr loop_body,
+                                       loop_runtime_check_ptr loop_rt_chk)
+         : parent_t(condition, loop_body)
+         , loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_repeat_until_loop)
          {
-            expression_node<T>::ndb_t::collect(condition_, node_delete_list);
-            expression_node<T>::ndb_t::collect(loop_body_, node_delete_list);
+            assert(valid());
          }
 
-         std::size_t node_depth() const override
+         inline T value() const exprtk_override
          {
-            return expression_node<T>::ndb_t::compute_node_depth(condition_, loop_body_);
+            T result = T(0);
+
+            loop_runtime_checker::reset();
+
+            do
+            {
+               try
+               {
+                  result = parent_t::loop_body_.first->value();
+               }
+               catch(const break_exception<T>& e)
+               {
+                  return e.value;
+               }
+               catch(const continue_exception&)
+               {}
+            }
+            while (is_false(parent_t::condition_.first) && loop_runtime_checker::check());
+
+            return result;
          }
 
-      private:
+         using parent_t::valid;
 
-         branch_t condition_;
-         branch_t loop_body_;
+         inline bool valid() const exprtk_final
+         {
+            return parent_t::valid() &&
+                   loop_runtime_checker::valid();
+         }
       };
 
       template <typename T>
-      class for_loop_bc_node : public expression_node<T>,
-                               public loop_runtime_checker
+      class for_loop_bc_node : public for_loop_node<T>
       {
       public:
 
+         typedef for_loop_node<T>    parent_t;
          typedef expression_node<T>* expression_ptr;
-         typedef std::pair<expression_ptr,bool> branch_t;
 
          for_loop_bc_node(expression_ptr initialiser,
                           expression_ptr condition,
                           expression_ptr incrementor,
-                          expression_ptr loop_body,
-                          loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
-         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_for_loop)
+                          expression_ptr loop_body)
+         : parent_t(initialiser, condition, incrementor, loop_body)
          {
-            construct_branch_pair(initialiser_, initialiser);
-            construct_branch_pair(condition_  , condition  );
-            construct_branch_pair(incrementor_, incrementor);
-            construct_branch_pair(loop_body_  , loop_body  );
+            assert(parent_t::valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(condition_.first);
-            assert(loop_body_.first);
-
             T result = T(0);
 
-            loop_runtime_checker::reset();
+            if (parent_t::initialiser_.first)
+               parent_t::initialiser_.first->value();
 
-            if (initialiser_.first)
-               initialiser_.first->value();
-
-            if (incrementor_.first)
+            if (parent_t::incrementor_.first)
             {
-               while (is_true(condition_) && loop_runtime_checker::check())
+               while (is_true(parent_t::condition_))
                {
                   try
                   {
-                     result = loop_body_.first->value();
+                     result = parent_t::loop_body_.first->value();
                   }
                   catch(const break_exception<T>& e)
                   {
@@ -7151,16 +7985,16 @@ namespace exprtk
                   catch(const continue_exception&)
                   {}
 
-                  incrementor_.first->value();
+                  parent_t::incrementor_.first->value();
                }
             }
             else
             {
-               while (is_true(condition_) && loop_runtime_checker::check())
+               while (is_true(parent_t::condition_))
                {
                   try
                   {
-                     result = loop_body_.first->value();
+                     result = parent_t::loop_body_.first->value();
                   }
                   catch(const break_exception<T>& e)
                   {
@@ -7173,32 +8007,83 @@ namespace exprtk
 
             return result;
          }
+      };
 
-         inline typename expression_node<T>::node_type type() const override
-         {
-            return expression_node<T>::e_for;
-         }
+      template <typename T>
+      class for_loop_bc_rtc_node exprtk_final
+                                 : public for_loop_bc_node<T>
+                                 , public loop_runtime_checker
+      {
+      public:
+
+         typedef for_loop_bc_node<T> parent_t;
+         typedef expression_node<T>* expression_ptr;
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         for_loop_bc_rtc_node(expression_ptr initialiser,
+                              expression_ptr condition,
+                              expression_ptr incrementor,
+                              expression_ptr loop_body,
+                              loop_runtime_check_ptr loop_rt_chk)
+         : parent_t(initialiser, condition, incrementor, loop_body)
+         , loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_for_loop)
          {
-            expression_node<T>::ndb_t::collect(initialiser_, node_delete_list);
-            expression_node<T>::ndb_t::collect(condition_  , node_delete_list);
-            expression_node<T>::ndb_t::collect(incrementor_, node_delete_list);
-            expression_node<T>::ndb_t::collect(loop_body_  , node_delete_list);
+            assert(valid());
          }
 
-         std::size_t node_depth() const override
+         inline T value() const exprtk_override
          {
-            return expression_node<T>::ndb_t::compute_node_depth
-               (initialiser_, condition_, incrementor_, loop_body_);
+            T result = T(0);
+
+            loop_runtime_checker::reset();
+
+            if (parent_t::initialiser_.first)
+               parent_t::initialiser_.first->value();
+
+            if (parent_t::incrementor_.first)
+            {
+               while (is_true(parent_t::condition_) && loop_runtime_checker::check())
+               {
+                  try
+                  {
+                     result = parent_t::loop_body_.first->value();
+                  }
+                  catch(const break_exception<T>& e)
+                  {
+                     return e.value;
+                  }
+                  catch(const continue_exception&)
+                  {}
+
+                  parent_t::incrementor_.first->value();
+               }
+            }
+            else
+            {
+               while (is_true(parent_t::condition_) && loop_runtime_checker::check())
+               {
+                  try
+                  {
+                     result = parent_t::loop_body_.first->value();
+                  }
+                  catch(const break_exception<T>& e)
+                  {
+                     return e.value;
+                  }
+                  catch(const continue_exception&)
+                  {}
+               }
+            }
+
+            return result;
          }
 
-      private:
+         using parent_t::valid;
 
-         branch_t initialiser_;
-         branch_t condition_  ;
-         branch_t incrementor_;
-         branch_t loop_body_  ;
+         inline bool valid() const exprtk_final
+         {
+            return parent_t::valid() &&
+                   loop_runtime_checker::valid();
+         }
       };
       #endif
 
@@ -7221,7 +8106,7 @@ namespace exprtk
 
             for (std::size_t i = 0; i < arg_list.size(); ++i)
             {
-               if (arg_list[i])
+               if (arg_list[i] && arg_list[i]->valid())
                {
                   construct_branch_pair(arg_list_[i], arg_list[i]);
                }
@@ -7231,42 +8116,44 @@ namespace exprtk
                   return;
                }
             }
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (!arg_list_.empty())
+            const std::size_t upper_bound = (arg_list_.size() - 1);
+
+            for (std::size_t i = 0; i < upper_bound; i += 2)
             {
-               const std::size_t upper_bound = (arg_list_.size() - 1);
+               expression_ptr condition  = arg_list_[i    ].first;
+               expression_ptr consequent = arg_list_[i + 1].first;
 
-               for (std::size_t i = 0; i < upper_bound; i += 2)
+               if (is_true(condition))
                {
-                  expression_ptr condition  = arg_list_[i    ].first;
-                  expression_ptr consequent = arg_list_[i + 1].first;
-
-                  if (is_true(condition))
-                  {
-                     return consequent->value();
-                  }
+                  return consequent->value();
                }
-
-               return arg_list_[upper_bound].first->value();
             }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+
+            return arg_list_[upper_bound].first->value();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_final
          {
             return expression_node<T>::e_switch;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
+         {
+            return !arg_list_.empty();
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
             expression_node<T>::ndb_t::collect(arg_list_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_final
          {
             return expression_node<T>::ndb_t::compute_node_depth(arg_list_);
          }
@@ -7277,7 +8164,7 @@ namespace exprtk
       };
 
       template <typename T, typename Switch_N>
-      class switch_n_node : public switch_node<T>
+      class switch_n_node exprtk_final : public switch_node<T>
       {
       public:
 
@@ -7289,14 +8176,14 @@ namespace exprtk
          : switch_node<T>(arg_list)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return Switch_N::process(switch_node<T>::arg_list_);
          }
       };
 
       template <typename T>
-      class multi_switch_node : public expression_node<T>
+      class multi_switch_node exprtk_final : public expression_node<T>
       {
       public:
 
@@ -7314,7 +8201,7 @@ namespace exprtk
 
             for (std::size_t i = 0; i < arg_list.size(); ++i)
             {
-               if (arg_list[i])
+               if (arg_list[i] && arg_list[i]->valid())
                {
                   construct_branch_pair(arg_list_[i], arg_list[i]);
                }
@@ -7324,19 +8211,16 @@ namespace exprtk
                   return;
                }
             }
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            T result = T(0);
-
-            if (arg_list_.empty())
-            {
-               return std::numeric_limits<T>::quiet_NaN();
-            }
-
             const std::size_t upper_bound = (arg_list_.size() - 1);
 
+            T result = T(0);
+
             for (std::size_t i = 0; i < upper_bound; i += 2)
             {
                expression_ptr condition  = arg_list_[i    ].first;
@@ -7351,17 +8235,22 @@ namespace exprtk
             return result;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_mswitch;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
+         {
+            return !arg_list_.empty() && (0 == (arg_list_.size() % 2));
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
             expression_node<T>::ndb_t::collect(arg_list_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_final
          {
             return expression_node<T>::ndb_t::compute_node_depth(arg_list_);
          }
@@ -7376,15 +8265,16 @@ namespace exprtk
       {
       public:
 
-         virtual ~ivariable() = default;
+         virtual ~ivariable() exprtk_default;
 
          virtual T& ref() = 0;
          virtual const T& ref() const = 0;
       };
 
       template <typename T>
-      class variable_node : public expression_node<T>,
-                            public ivariable      <T>
+      class variable_node exprtk_final
+                          : public expression_node<T>
+                          , public ivariable      <T>
       {
       public:
 
@@ -7403,22 +8293,22 @@ namespace exprtk
             return this < (&v);
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return (*value_);
          }
 
-         inline T& ref() override
+         inline T& ref() exprtk_override
          {
             return (*value_);
          }
 
-         inline const T& ref() const override
+         inline const T& ref() const exprtk_override
          {
             return (*value_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_variable;
          }
@@ -7438,11 +8328,11 @@ namespace exprtk
          typedef std::pair<std::size_t,std::size_t> cached_range_t;
 
          range_pack()
-         : n0_e (std::make_pair(false,expression_node_ptr(0))),
-           n1_e (std::make_pair(false,expression_node_ptr(0))),
-           n0_c (std::make_pair(false,0)),
-           n1_c (std::make_pair(false,0)),
-           cache(std::make_pair(0,0))
+         : n0_e (std::make_pair(false,expression_node_ptr(0)))
+         , n1_e (std::make_pair(false,expression_node_ptr(0)))
+         , n0_c (std::make_pair(false,0))
+         , n1_c (std::make_pair(false,0))
+         , cache(std::make_pair(0,0))
          {}
 
          void clear()
@@ -7521,13 +8411,13 @@ namespace exprtk
                  (std::numeric_limits<std::size_t>::max() == r1  )
                )
             {
-               r1 = size - 1;
+               r1 = size;
             }
 
             cache.first  = r0;
             cache.second = r1;
 
-            #ifndef exprtk_enable_runtime_checks
+            #ifndef exprtk_enable_range_runtime_checks
             return (r0 <= r1);
             #else
             return range_runtime_check(r0, r1, size);
@@ -7536,12 +8426,12 @@ namespace exprtk
 
          inline std::size_t const_size() const
          {
-            return (n1_c.second - n0_c.second + 1);
+            return (n1_c.second - n0_c.second);
          }
 
          inline std::size_t cache_size() const
          {
-            return (cache.second - cache.first + 1);
+            return (cache.second - cache.first);
          }
 
          std::pair<bool,expression_node_ptr> n0_e;
@@ -7550,21 +8440,25 @@ namespace exprtk
          std::pair<bool,std::size_t        > n1_c;
          mutable cached_range_t             cache;
 
-         #ifdef exprtk_enable_runtime_checks
+         #ifdef exprtk_enable_range_runtime_checks
          bool range_runtime_check(const std::size_t r0,
                                   const std::size_t r1,
                                   const std::size_t size) const
          {
-            if ((r0 < 0) || (r0 >= size))
+            if (r0 > size)
             {
-               throw std::runtime_error("range error: (r0 < 0) || (r0 >= size)");
+               throw std::runtime_error("range error: (r0 < 0) || (r0 > size)");
+               #if !defined(_MSC_VER) && !defined(__NVCOMPILER)
                return false;
+               #endif
             }
 
-            if ((r1 < 0) || (r1 >= size))
+            if (r1 > size)
             {
-               throw std::runtime_error("range error: (r1 < 0) || (r1 >= size)");
+               throw std::runtime_error("range error: (r1 < 0) || (r1 > size)");
+               #if !defined(_MSC_VER) && !defined(__NVCOMPILER)
                return false;
+               #endif
             }
 
             return (r0 <= r1);
@@ -7582,11 +8476,11 @@ namespace exprtk
          typedef string_base_node<T>* strbase_ptr_t;
 
          range_data_type()
-         : range(0),
-           data (0),
-           size (0),
-           type_size(0),
-           str_node (0)
+         : range(0)
+         , data (0)
+         , size (0)
+         , type_size(0)
+         , str_node (0)
          {}
 
          range_t*      range;
@@ -7604,77 +8498,96 @@ namespace exprtk
       public:
 
          typedef vector_node<T>*   vector_node_ptr;
-         typedef vec_data_store<T>           vds_t;
+         typedef vec_data_store<T> vds_t;
 
-         virtual ~vector_interface() = default;
+         virtual ~vector_interface() exprtk_default;
 
-         virtual std::size_t size   () const = 0;
+         virtual std::size_t size     () const = 0;
 
-         virtual vector_node_ptr vec() const = 0;
+         virtual std::size_t base_size() const = 0;
 
-         virtual vector_node_ptr vec()       = 0;
+         virtual vector_node_ptr vec  () const = 0;
 
-         virtual       vds_t& vds   ()       = 0;
+         virtual vector_node_ptr vec  ()       = 0;
 
-         virtual const vds_t& vds   () const = 0;
+         virtual       vds_t& vds     ()       = 0;
 
-         virtual bool side_effect   () const { return false; }
+         virtual const vds_t& vds     () const = 0;
+
+         virtual bool side_effect     () const { return false; }
       };
 
       template <typename T>
-      class vector_node : public expression_node <T>,
-                          public vector_interface<T>
+      class vector_node exprtk_final
+                        : public expression_node <T>
+                        , public vector_interface<T>
       {
       public:
 
-         typedef expression_node<T>*  expression_ptr;
+         typedef expression_node<T>* expression_ptr;
          typedef vector_holder<T>    vector_holder_t;
          typedef vector_node<T>*     vector_node_ptr;
-         typedef vec_data_store<T>             vds_t;
+         typedef vec_data_store<T>   vds_t;
 
          explicit vector_node(vector_holder_t* vh)
-         : vector_holder_(vh),
-           vds_((*vector_holder_).size(),(*vector_holder_)[0])
+         : vector_holder_(vh)
+         , vds_((*vector_holder_).size(),(*vector_holder_)[0])
          {
             vector_holder_->set_ref(&vds_.ref());
          }
 
          vector_node(const vds_t& vds, vector_holder_t* vh)
-         : vector_holder_(vh),
-           vds_(vds)
+         : vector_holder_(vh)
+         , vds_(vds)
          {}
 
-         inline T value() const override
+        ~vector_node() exprtk_override
+         {
+            assert(valid());
+            vector_holder_->remove_ref(&vds_.ref());
+         }
+
+         inline T value() const exprtk_override
          {
             return vds().data()[0];
          }
 
-         vector_node_ptr vec() const override
+         vector_node_ptr vec() const exprtk_override
          {
             return const_cast<vector_node_ptr>(this);
          }
 
-         vector_node_ptr vec() override
+         vector_node_ptr vec() exprtk_override
          {
             return this;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_vector;
          }
 
-         std::size_t size() const override
+         inline bool valid() const exprtk_override
+         {
+            return vector_holder_;
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return vec_holder().size();
+         }
+
+         std::size_t base_size() const exprtk_override
          {
-            return vds().size();
+            return vec_holder().base_size();
          }
 
-         vds_t& vds() override
+         vds_t& vds() exprtk_override
          {
             return vds_;
          }
 
-         const vds_t& vds() const override
+         const vds_t& vds() const exprtk_override
          {
             return vds_;
          }
@@ -7684,6 +8597,11 @@ namespace exprtk
             return (*vector_holder_);
          }
 
+         inline vector_holder_t& vec_holder() const
+         {
+            return (*vector_holder_);
+         }
+
       private:
 
          vector_holder_t* vector_holder_;
@@ -7691,103 +8609,100 @@ namespace exprtk
       };
 
       template <typename T>
-      class vector_elem_node : public expression_node<T>,
-                               public ivariable      <T>
+      class vector_size_node exprtk_final
+                        : public expression_node <T>
       {
       public:
 
-         typedef expression_node<T>*            expression_ptr;
-         typedef vector_holder<T>               vector_holder_t;
-         typedef vector_holder_t*               vector_holder_ptr;
-         typedef std::pair<expression_ptr,bool> branch_t;
-
-         vector_elem_node(expression_ptr index, vector_holder_ptr vec_holder)
-         : vec_holder_(vec_holder),
-           vector_base_((*vec_holder)[0])
-         {
-            construct_branch_pair(index_, index);
-         }
-
-         inline T value() const override
-         {
-            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
-         }
+         typedef expression_node<T>* expression_ptr;
+         typedef vector_holder<T>    vector_holder_t;
 
-         inline T& ref() override
-         {
-            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
-         }
+         explicit vector_size_node(vector_holder_t* vh)
+         : vector_holder_(vh)
+         {}
 
-         inline const T& ref() const override
+        ~vector_size_node() exprtk_override
          {
-            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
+            assert(valid());
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline T value() const exprtk_override
          {
-            return expression_node<T>::e_vecelem;
+            assert(vector_holder_);
+            return static_cast<T>(vector_holder_->size());
          }
 
-         inline vector_holder_t& vec_holder()
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
-            return (*vec_holder_);
+            return expression_node<T>::e_vecsize;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
          {
-            expression_node<T>::ndb_t::collect(index_, node_delete_list);
+            return vector_holder_ && vector_holder_->size();
          }
 
-         std::size_t node_depth() const override
+         inline vector_holder_t* vec_holder()
          {
-            return expression_node<T>::ndb_t::compute_node_depth(index_);
+            return vector_holder_;
          }
 
       private:
 
-         vector_holder_ptr vec_holder_;
-         T* vector_base_;
-         branch_t index_;
+         vector_holder_t* vector_holder_;
       };
 
       template <typename T>
-      class rebasevector_elem_node : public expression_node<T>,
-                                     public ivariable      <T>
+      class vector_elem_node exprtk_final
+                             : public expression_node<T>
+                             , public ivariable      <T>
       {
       public:
 
          typedef expression_node<T>*            expression_ptr;
          typedef vector_holder<T>               vector_holder_t;
          typedef vector_holder_t*               vector_holder_ptr;
-         typedef vec_data_store<T>              vds_t;
          typedef std::pair<expression_ptr,bool> branch_t;
 
-         rebasevector_elem_node(expression_ptr index, vector_holder_ptr vec_holder)
-         : vector_holder_(vec_holder),
-           vds_((*vector_holder_).size(),(*vector_holder_)[0])
+         vector_elem_node(expression_ptr vec_node,
+                          expression_ptr index,
+                          vector_holder_ptr vec_holder)
+         : vector_holder_(vec_holder)
+         , vector_base_((*vec_holder)[0])
          {
-            vector_holder_->set_ref(&vds_.ref());
-            construct_branch_pair(index_, index);
+            construct_branch_pair(vector_node_, vec_node);
+            construct_branch_pair(index_      , index   );
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
+            return *access_vector();
          }
 
-         inline T& ref() override
+         inline T& ref() exprtk_override
          {
-            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
+            return *access_vector();
          }
 
-         inline const T& ref() const override
+         inline const T& ref() const exprtk_override
          {
-            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
+            return *access_vector();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
-            return expression_node<T>::e_rbvecelem;
+            return expression_node<T>::e_vecelem;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return
+               vector_holder_        &&
+               index_.first          &&
+               vector_node_.first    &&
+               index_.first->valid() &&
+               vector_node_.first->valid();
          }
 
          inline vector_holder_t& vec_holder()
@@ -7795,60 +8710,81 @@ namespace exprtk
             return (*vector_holder_);
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            expression_node<T>::ndb_t::template collect<>(index_, node_delete_list);
+            expression_node<T>::ndb_t::collect(vector_node_, node_delete_list);
+            expression_node<T>::ndb_t::collect(index_      , node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
-            return expression_node<T>::ndb_t::compute_node_depth(index_);
+            return expression_node<T>::ndb_t::compute_node_depth
+               (vector_node_, index_);
          }
 
       private:
 
+         inline T* access_vector() const
+         {
+            vector_node_.first->value();
+            return (vector_base_ + details::numeric::to_uint64(index_.first->value()));
+         }
+
          vector_holder_ptr vector_holder_;
-         vds_t             vds_;
-         branch_t          index_;
+         T* vector_base_;
+         branch_t vector_node_;
+         branch_t index_;
       };
 
       template <typename T>
-      class rebasevector_celem_node : public expression_node<T>,
-                                      public ivariable      <T>
+      class vector_celem_node exprtk_final
+                              : public expression_node<T>
+                              , public ivariable      <T>
       {
       public:
 
-         typedef expression_node<T>* expression_ptr;
-         typedef vector_holder<T>    vector_holder_t;
-         typedef vector_holder_t*    vector_holder_ptr;
-         typedef vec_data_store<T>   vds_t;
+         typedef expression_node<T>*            expression_ptr;
+         typedef vector_holder<T>               vector_holder_t;
+         typedef vector_holder_t*               vector_holder_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-         rebasevector_celem_node(const std::size_t index, vector_holder_ptr vec_holder)
-         : index_(index),
-           vector_holder_(vec_holder),
-           vds_((*vector_holder_).size(),(*vector_holder_)[0])
+         vector_celem_node(expression_ptr vec_node,
+                           const std::size_t index,
+                           vector_holder_ptr vec_holder)
+         : index_(index)
+         , vector_holder_(vec_holder)
+         , vector_base_((*vec_holder)[0])
          {
-            vector_holder_->set_ref(&vds_.ref());
+            construct_branch_pair(vector_node_, vec_node);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            return *(vds_.data() + index_);
+            return *access_vector();
          }
 
-         inline T& ref() override
+         inline T& ref() exprtk_override
          {
-            return *(vds_.data() + index_);
+            return *access_vector();
          }
 
-         inline const T& ref() const override
+         inline const T& ref() const exprtk_override
          {
-            return *(vds_.data() + index_);
+            return *access_vector();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
-            return expression_node<T>::e_rbveccelem;
+            return expression_node<T>::e_veccelem;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return
+               vector_holder_     &&
+               vector_node_.first &&
+               vector_node_.first->valid();
          }
 
          inline vector_holder_t& vec_holder()
@@ -7856,684 +8792,1773 @@ namespace exprtk
             return (*vector_holder_);
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(vector_node_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(vector_node_);
+         }
+
       private:
 
+         inline T* access_vector() const
+         {
+            vector_node_.first->value();
+            return (vector_base_ + index_);
+         }
+
          const std::size_t index_;
          vector_holder_ptr vector_holder_;
-         vds_t vds_;
+         T* vector_base_;
+         branch_t vector_node_;
       };
 
       template <typename T>
-      class vector_assignment_node : public expression_node<T>
+      class vector_elem_rtc_node exprtk_final
+                                 : public expression_node<T>
+                                 , public ivariable      <T>
       {
       public:
 
-         typedef expression_node<T>* expression_ptr;
+         typedef expression_node<T>*            expression_ptr;
+         typedef vector_holder<T>               vector_holder_t;
+         typedef vector_holder_t*               vector_holder_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-         vector_assignment_node(T* vector_base,
-                                const std::size_t& size,
-                                const std::vector<expression_ptr>& initialiser_list,
-                                const bool single_value_initialse)
-         : vector_base_(vector_base),
-           initialiser_list_(initialiser_list),
-           size_(size),
-           single_value_initialse_(single_value_initialse)
-         {}
+         vector_elem_rtc_node(expression_ptr vec_node,
+                              expression_ptr index,
+                              vector_holder_ptr vec_holder,
+                              vector_access_runtime_check_ptr vec_rt_chk)
+         : vector_holder_(vec_holder)
+         , vector_base_((*vec_holder)[0])
+         , vec_rt_chk_(vec_rt_chk)
+         , max_vector_index_(vector_holder_->size() - 1)
+         {
+            construct_branch_pair(vector_node_, vec_node);
+            construct_branch_pair(index_      , index   );
+            assert(valid());
+         }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (single_value_initialse_)
-            {
-               for (std::size_t i = 0; i < size_; ++i)
-               {
-                  *(vector_base_ + i) = initialiser_list_[0]->value();
-               }
-            }
-            else
-            {
-               std::size_t il_size = initialiser_list_.size();
+            return *access_vector();
+         }
 
-               for (std::size_t i = 0; i < il_size; ++i)
-               {
-                  *(vector_base_ + i) = initialiser_list_[i]->value();
-               }
+         inline T& ref() exprtk_override
+         {
+            return *access_vector();
+         }
 
-               if (il_size < size_)
-               {
-                  for (std::size_t i = il_size; i < size_; ++i)
-                  {
-                     *(vector_base_ + i) = T(0);
-                  }
-               }
-            }
+         inline const T& ref() const exprtk_override
+         {
+            return *access_vector();
+         }
 
-            return *(vector_base_);
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_vecelemrtc;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline bool valid() const exprtk_override
          {
-            return expression_node<T>::e_vecdefass;
+            return
+               vector_holder_        &&
+               index_.first          &&
+               vector_node_.first    &&
+               index_.first->valid() &&
+               vector_node_.first->valid();
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline vector_holder_t& vec_holder()
          {
-            expression_node<T>::ndb_t::collect(initialiser_list_, node_delete_list);
+            return (*vector_holder_);
          }
 
-         std::size_t node_depth() const override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            return expression_node<T>::ndb_t::compute_node_depth(initialiser_list_);
+            expression_node<T>::ndb_t::collect(vector_node_, node_delete_list);
+            expression_node<T>::ndb_t::collect(index_,       node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::compute_node_depth
+               (vector_node_, index_);
          }
 
       private:
 
-         vector_assignment_node<T>& operator=(const vector_assignment_node<T>&);
+         inline T* access_vector() const
+         {
+            const _uint64_t index = details::numeric::to_uint64(index_.first->value());
+            vector_node_.first->value();
 
-         mutable T* vector_base_;
-         std::vector<expression_ptr> initialiser_list_;
-         const std::size_t size_;
-         const bool single_value_initialse_;
+            if (index <= max_vector_index_)
+            {
+               return (vector_holder_->data() + index);
+            }
+
+            assert(vec_rt_chk_);
+
+            vector_access_runtime_check::violation_context context;
+            context.base_ptr   = reinterpret_cast<void*>(vector_base_);
+            context.end_ptr    = reinterpret_cast<void*>(vector_base_ + vector_holder_->size());
+            context.access_ptr = reinterpret_cast<void*>(vector_base_ + index);
+            context.type_size  = sizeof(T);
+
+            return vec_rt_chk_->handle_runtime_violation(context) ?
+               reinterpret_cast<T*>(context.access_ptr) :
+               vector_base_ ;
+         }
+
+         vector_holder_ptr vector_holder_;
+         T*                vector_base_;
+         branch_t          vector_node_;
+         branch_t          index_;
+         vector_access_runtime_check_ptr vec_rt_chk_;
+         const std::size_t max_vector_index_;
       };
 
       template <typename T>
-      class swap_node : public expression_node<T>
+      class vector_celem_rtc_node exprtk_final
+                                 : public expression_node<T>
+                                 , public ivariable      <T>
       {
       public:
 
-         typedef expression_node<T>* expression_ptr;
-         typedef variable_node<T>*   variable_node_ptr;
-
-         swap_node(variable_node_ptr var0, variable_node_ptr var1)
-         : var0_(var0),
-           var1_(var1)
-         {}
+         typedef expression_node<T>*            expression_ptr;
+         typedef vector_holder<T>               vector_holder_t;
+         typedef vector_holder_t*               vector_holder_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-         inline T value() const override
+         vector_celem_rtc_node(expression_ptr vec_node,
+                               const std::size_t index,
+                               vector_holder_ptr vec_holder,
+                               vector_access_runtime_check_ptr vec_rt_chk)
+         : index_(index)
+         , max_vector_index_(vec_holder->size() - 1)
+         , vector_holder_(vec_holder)
+         , vector_base_((*vec_holder)[0])
+         , vec_rt_chk_(vec_rt_chk)
          {
-            std::swap(var0_->ref(),var1_->ref());
-            return var1_->ref();
+            construct_branch_pair(vector_node_, vec_node);
+            assert(valid());
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline T value() const exprtk_override
          {
-            return expression_node<T>::e_swap;
+            return *access_vector();
          }
 
-      private:
+         inline T& ref() exprtk_override
+         {
+            return *access_vector();
+         }
 
-         variable_node_ptr var0_;
-         variable_node_ptr var1_;
-      };
+         inline const T& ref() const exprtk_override
+         {
+            return *access_vector();
+         }
 
-      template <typename T>
-      class swap_generic_node : public binary_node<T>
-      {
-      public:
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_veccelemrtc;
+         }
 
-         typedef expression_node<T>* expression_ptr;
-         typedef ivariable<T>* ivariable_ptr;
+         inline bool valid() const exprtk_override
+         {
+            return
+               vector_holder_     &&
+               vector_node_.first &&
+               vector_node_.first->valid();
+         }
 
-         swap_generic_node(expression_ptr var0, expression_ptr var1)
-         : binary_node<T>(details::e_swap, var0, var1),
-           var0_(dynamic_cast<ivariable_ptr>(var0)),
-           var1_(dynamic_cast<ivariable_ptr>(var1))
-         {}
+         inline vector_holder_t& vec_holder()
+         {
+            return (*vector_holder_);
+         }
 
-         inline T value() const override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            std::swap(var0_->ref(),var1_->ref());
-            return var1_->ref();
+            expression_node<T>::ndb_t::collect(vector_node_, node_delete_list);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         std::size_t node_depth() const exprtk_override
          {
-            return expression_node<T>::e_swap;
+            return expression_node<T>::ndb_t::compute_node_depth(vector_node_);
          }
 
       private:
 
-         ivariable_ptr var0_;
-         ivariable_ptr var1_;
-      };
-
-      template <typename T>
-      class swap_vecvec_node : public binary_node     <T>,
-                               public vector_interface<T>
-      {
-      public:
-
-         typedef expression_node<T>*  expression_ptr;
-         typedef vector_node<T>*     vector_node_ptr;
-         typedef vec_data_store<T>             vds_t;
-
-         swap_vecvec_node(expression_ptr branch0,
-                          expression_ptr branch1)
-         : binary_node<T>(details::e_swap, branch0, branch1),
-           vec0_node_ptr_(0),
-           vec1_node_ptr_(0),
-           vec_size_     (0),
-           initialised_  (false)
+         inline T* access_vector() const
          {
-            if (is_ivector_node(binary_node<T>::branch_[0].first))
-            {
-               vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
-
-               if (0 != (vi = dynamic_cast<vector_interface<T>*>(binary_node<T>::branch_[0].first)))
-               {
-                  vec0_node_ptr_ = vi->vec();
-                  vds()          = vi->vds();
-               }
-            }
+            vector_node_.first->value();
 
-            if (is_ivector_node(binary_node<T>::branch_[1].first))
+            if (index_ <= max_vector_index_)
             {
-               vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
-
-               if (0 != (vi = dynamic_cast<vector_interface<T>*>(binary_node<T>::branch_[1].first)))
-               {
-                  vec1_node_ptr_ = vi->vec();
-               }
+               return (vector_holder_->data() + index_);
             }
 
-            if (vec0_node_ptr_ && vec1_node_ptr_)
-            {
-               vec_size_ = std::min(vec0_node_ptr_->vds().size(),
-                                    vec1_node_ptr_->vds().size());
+            assert(vec_rt_chk_);
 
-               initialised_ = true;
-            }
+            vector_access_runtime_check::violation_context context;
+            context.base_ptr   = reinterpret_cast<void*>(vector_base_);
+            context.end_ptr    = reinterpret_cast<void*>(vector_base_ + vector_holder_->size());
+            context.access_ptr = reinterpret_cast<void*>(vector_base_ + index_);
+            context.type_size  = sizeof(T);
+
+            return vec_rt_chk_->handle_runtime_violation(context) ?
+               reinterpret_cast<T*>(context.access_ptr) :
+               vector_base_ ;
          }
 
-         inline T value() const override
-         {
-            assert(binary_node<T>::branch_[0].first);
-            assert(binary_node<T>::branch_[1].first);
+         const std::size_t index_;
+         const std::size_t max_vector_index_;
+         vector_holder_ptr vector_holder_;
+         T*                vector_base_;
+         branch_t          vector_node_;
+         vector_access_runtime_check_ptr vec_rt_chk_;
+      };
 
-            if (initialised_)
-            {
-               binary_node<T>::branch_[0].first->value();
-               binary_node<T>::branch_[1].first->value();
+      template <typename T>
+      class rebasevector_elem_node exprtk_final
+                                   : public expression_node<T>
+                                   , public ivariable      <T>
+      {
+      public:
 
-               T* vec0 = vec0_node_ptr_->vds().data();
-               T* vec1 = vec1_node_ptr_->vds().data();
+         typedef expression_node<T>*            expression_ptr;
+         typedef vector_holder<T>               vector_holder_t;
+         typedef vector_holder_t*               vector_holder_ptr;
+         typedef vec_data_store<T>              vds_t;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-               for (std::size_t i = 0; i < vec_size_; ++i)
-               {
-                  std::swap(vec0[i],vec1[i]);
-               }
+         rebasevector_elem_node(expression_ptr vec_node,
+                                expression_ptr index,
+                                vector_holder_ptr vec_holder)
+         : vector_holder_(vec_holder)
+         {
+            construct_branch_pair(vector_node_, vec_node);
+            construct_branch_pair(index_      , index   );
+            assert(valid());
+         }
 
-               return vec1_node_ptr_->value();
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+         inline T value() const exprtk_override
+         {
+            return *access_vector();
          }
 
-         vector_node_ptr vec() const override
+         inline T& ref() exprtk_override
          {
-            return vec0_node_ptr_;
+            return *access_vector();
          }
 
-         vector_node_ptr vec() override
+         inline const T& ref() const exprtk_override
          {
-            return vec0_node_ptr_;
+            return *access_vector();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
-            return expression_node<T>::e_vecvecswap;
+            return expression_node<T>::e_rbvecelem;
          }
 
-         std::size_t size() const override
+         inline bool valid() const exprtk_override
          {
-            return vec_size_;
+            return
+               vector_holder_        &&
+               index_.first          &&
+               vector_node_.first    &&
+               index_.first->valid() &&
+               vector_node_.first->valid();
          }
 
-         vds_t& vds() override
+         inline vector_holder_t& vec_holder()
          {
-            return vds_;
+            return (*vector_holder_);
          }
 
-         const vds_t& vds() const override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            return vds_;
+            expression_node<T>::ndb_t::collect(vector_node_, node_delete_list);
+            expression_node<T>::ndb_t::collect(index_,       node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::compute_node_depth
+               (vector_node_, index_);
          }
 
       private:
 
-         vector_node<T>* vec0_node_ptr_;
-         vector_node<T>* vec1_node_ptr_;
-         std::size_t     vec_size_;
-         bool            initialised_;
-         vds_t           vds_;
+         inline T* access_vector() const
+         {
+            vector_node_.first->value();
+            return (vector_holder_->data() + details::numeric::to_uint64(index_.first->value()));
+         }
+
+         vector_holder_ptr vector_holder_;
+         branch_t          vector_node_;
+         branch_t          index_;
       };
 
-      #ifndef exprtk_disable_string_capabilities
       template <typename T>
-      class stringvar_node : public expression_node <T>,
-                             public string_base_node<T>,
-                             public range_interface <T>
+      class rebasevector_celem_node exprtk_final
+                                    : public expression_node<T>
+                                    , public ivariable      <T>
       {
       public:
 
-         typedef range_pack<T> range_t;
-
-         static std::string null_value;
-
-         explicit stringvar_node()
-         : value_(&null_value)
-         {}
-
-         explicit stringvar_node(std::string& v)
-         : value_(&v)
-         {
-            rp_.n0_c = std::make_pair<bool,std::size_t>(true,0);
-            rp_.n1_c = std::make_pair<bool,std::size_t>(true,v.size() - 1);
-            rp_.cache.first  = rp_.n0_c.second;
-            rp_.cache.second = rp_.n1_c.second;
-         }
-
-         inline bool operator <(const stringvar_node<T>& v) const
-         {
-            return this < (&v);
-         }
+         typedef expression_node<T>* expression_ptr;
+         typedef vector_holder<T>    vector_holder_t;
+         typedef vector_holder_t*    vector_holder_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-         inline T value() const override
+         rebasevector_celem_node(expression_ptr vec_node,
+                                 const std::size_t index,
+                                 vector_holder_ptr vec_holder)
+         : index_(index)
+         , vector_holder_(vec_holder)
          {
-            rp_.n1_c.second  = (*value_).size() - 1;
-            rp_.cache.second = rp_.n1_c.second;
-
-            return std::numeric_limits<T>::quiet_NaN();
+            construct_branch_pair(vector_node_, vec_node);
+            assert(valid());
          }
 
-         std::string str() const override
+         inline T value() const exprtk_override
          {
-            return ref();
+            vector_node_.first->value();
+            return ref();;
          }
 
-         char_cptr base() const override
+         inline T& ref() exprtk_override
          {
-            return &(*value_)[0];
+            return *(vector_holder_->data() + index_);
          }
 
-         std::size_t size() const override
+         inline const T& ref() const exprtk_override
          {
-            return ref().size();
+            return *(vector_holder_->data() + index_);
          }
 
-         std::string& ref()
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
-            return (*value_);
+            return expression_node<T>::e_rbveccelem;
          }
 
-         const std::string& ref() const
+         inline bool valid() const exprtk_override
          {
-            return (*value_);
+            return
+               vector_holder_     &&
+               vector_node_.first &&
+               vector_node_.first->valid();
          }
 
-         range_t& range_ref() override
+         inline vector_holder_t& vec_holder()
          {
-            return rp_;
+            return (*vector_holder_);
          }
 
-         const range_t& range_ref() const override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            return rp_;
+            expression_node<T>::ndb_t::collect(vector_node_, node_delete_list);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         std::size_t node_depth() const exprtk_override
          {
-            return expression_node<T>::e_stringvar;
+            return expression_node<T>::ndb_t::compute_node_depth(vector_node_);
          }
 
       private:
 
-         std::string* value_;
-         mutable range_t rp_;
+         const std::size_t index_;
+         vector_holder_ptr vector_holder_;
+         branch_t          vector_node_;
       };
 
       template <typename T>
-      std::string stringvar_node<T>::null_value = std::string("");
-
-      template <typename T>
-      class string_range_node : public expression_node <T>,
-                                public string_base_node<T>,
-                                public range_interface <T>
+      class rebasevector_elem_rtc_node exprtk_final
+                                       : public expression_node<T>
+                                       , public ivariable      <T>
       {
       public:
 
-         typedef range_pack<T> range_t;
-
-         static std::string null_value;
-
-         explicit string_range_node(std::string& v, const range_t& rp)
-         : value_(&v),
-           rp_(rp)
-         {}
+         typedef expression_node<T>*            expression_ptr;
+         typedef vector_holder<T>               vector_holder_t;
+         typedef vector_holder_t*               vector_holder_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-         ~string_range_node() override
+         rebasevector_elem_rtc_node(expression_ptr vec_node,
+                                    expression_ptr index,
+                                    vector_holder_ptr vec_holder,
+                                    vector_access_runtime_check_ptr vec_rt_chk)
+         : vector_holder_(vec_holder)
+         , vec_rt_chk_(vec_rt_chk)
          {
-            rp_.free();
+            construct_branch_pair(vector_node_, vec_node);
+            construct_branch_pair(index_      , index   );
+            assert(valid());
          }
 
-         inline bool operator <(const string_range_node<T>& v) const
+         inline T value() const exprtk_override
          {
-            return this < (&v);
+            return *access_vector();
          }
 
-         inline T value() const override
+         inline T& ref() exprtk_override
          {
-            return std::numeric_limits<T>::quiet_NaN();
+            return *access_vector();
          }
 
-         inline std::string str() const override
+         inline const T& ref() const exprtk_override
          {
-            return (*value_);
+            return *access_vector();
          }
 
-         char_cptr base() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
-            return &(*value_)[0];
+            return expression_node<T>::e_rbvecelemrtc;
          }
 
-         std::size_t size() const override
+         inline bool valid() const exprtk_override
          {
-            return ref().size();
+            return
+               vector_holder_        &&
+               index_.first          &&
+               vector_node_.first    &&
+               index_.first->valid() &&
+               vector_node_.first->valid();
          }
 
-         inline range_t range() const
+         inline vector_holder_t& vec_holder()
          {
-            return rp_;
+            return (*vector_holder_);
          }
 
-         inline virtual std::string& ref()
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            return (*value_);
+            expression_node<T>::ndb_t::collect(vector_node_, node_delete_list);
+            expression_node<T>::ndb_t::collect(index_      , node_delete_list);
          }
 
-         inline virtual const std::string& ref() const
+         std::size_t node_depth() const exprtk_override
          {
-            return (*value_);
+            return expression_node<T>::ndb_t::compute_node_depth
+               (vector_node_, index_);
          }
 
-         inline range_t& range_ref() override
-         {
-            return rp_;
-         }
+      private:
 
-         inline const range_t& range_ref() const override
+         inline T* access_vector() const
          {
-            return rp_;
-         }
+            vector_node_.first->value();
+            const _uint64_t index = details::numeric::to_uint64(index_.first->value());
 
-         inline typename expression_node<T>::node_type type() const override
-         {
-            return expression_node<T>::e_stringvarrng;
-         }
+            if (index <= (vector_holder_->size() - 1))
+            {
+               return (vector_holder_->data() + index);
+            }
 
-      private:
+            assert(vec_rt_chk_);
 
-         std::string* value_;
-         range_t      rp_;
-      };
+            vector_access_runtime_check::violation_context context;
+            context.base_ptr   = reinterpret_cast<void*>(vector_holder_->data());
+            context.end_ptr    = reinterpret_cast<void*>(vector_holder_->data() + vector_holder_->size());
+            context.access_ptr = reinterpret_cast<void*>(vector_holder_->data() + index);
+            context.type_size  = sizeof(T);
 
-      template <typename T>
-      std::string string_range_node<T>::null_value = std::string("");
+            return vec_rt_chk_->handle_runtime_violation(context) ?
+                   reinterpret_cast<T*>(context.access_ptr) :
+                   vector_holder_->data() ;
+         }
+
+         vector_holder_ptr vector_holder_;
+         branch_t          vector_node_;
+         branch_t          index_;
+         vector_access_runtime_check_ptr vec_rt_chk_;
+      };
 
       template <typename T>
-      class const_string_range_node : public expression_node <T>,
-                                      public string_base_node<T>,
-                                      public range_interface <T>
+      class rebasevector_celem_rtc_node exprtk_final
+                                    : public expression_node<T>
+                                    , public ivariable      <T>
       {
       public:
 
-         typedef range_pack<T> range_t;
-
-         explicit const_string_range_node(const std::string& v, const range_t& rp)
-         : value_(v),
-           rp_(rp)
-         {}
+         typedef expression_node<T>*            expression_ptr;
+         typedef vector_holder<T>               vector_holder_t;
+         typedef vector_holder_t*               vector_holder_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-        ~const_string_range_node() override
+         rebasevector_celem_rtc_node(expression_ptr vec_node,
+                                     const std::size_t index,
+                                     vector_holder_ptr vec_holder,
+                                     vector_access_runtime_check_ptr vec_rt_chk)
+         : index_(index)
+         , vector_holder_(vec_holder)
+         , vector_base_((*vec_holder)[0])
+         , vec_rt_chk_(vec_rt_chk)
          {
-            rp_.free();
+            construct_branch_pair(vector_node_, vec_node);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            return std::numeric_limits<T>::quiet_NaN();
+            return *access_vector();
          }
 
-         std::string str() const override
+         inline T& ref() exprtk_override
          {
-            return value_;
+            return *access_vector();
          }
 
-         char_cptr base() const override
+         inline const T& ref() const exprtk_override
          {
-            return value_.data();
+            return *access_vector();
          }
 
-         std::size_t size() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
-            return value_.size();
+            return expression_node<T>::e_rbveccelemrtc;
          }
 
-         range_t range() const
+         inline bool valid() const exprtk_override
          {
-            return rp_;
+            return
+               vector_holder_     &&
+               vector_node_.first &&
+               vector_node_.first->valid();
          }
 
-         range_t& range_ref() override
+         inline vector_holder_t& vec_holder()
          {
-            return rp_;
+            return (*vector_holder_);
          }
 
-         const range_t& range_ref() const override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            return rp_;
+            expression_node<T>::ndb_t::collect(vector_node_, node_delete_list);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         std::size_t node_depth() const exprtk_override
          {
-            return expression_node<T>::e_cstringvarrng;
+            return expression_node<T>::ndb_t::compute_node_depth(vector_node_);
          }
 
       private:
 
-         const_string_range_node<T>& operator=(const const_string_range_node<T>&);
+         inline T* access_vector() const
+         {
+            vector_node_.first->value();
 
-         const std::string value_;
-         range_t rp_;
+            if (index_ <= vector_holder_->size() - 1)
+            {
+               return (vector_holder_->data() + index_);
+            }
+
+            assert(vec_rt_chk_);
+
+            vector_access_runtime_check::violation_context context;
+            context.base_ptr   = reinterpret_cast<void*>(vector_base_);
+            context.end_ptr    = reinterpret_cast<void*>(vector_base_ + vector_holder_->size());
+            context.access_ptr = reinterpret_cast<void*>(vector_base_ + index_);
+            context.type_size  = sizeof(T);
+
+            return vec_rt_chk_->handle_runtime_violation(context) ?
+               reinterpret_cast<T*>(context.access_ptr) :
+               vector_base_ ;
+         }
+
+         const std::size_t index_;
+         vector_holder_ptr vector_holder_;
+         T*                vector_base_;
+         branch_t          vector_node_;
+         vector_access_runtime_check_ptr vec_rt_chk_;
       };
 
       template <typename T>
-      class generic_string_range_node : public expression_node <T>,
-                                        public string_base_node<T>,
-                                        public range_interface <T>
+      class vector_initialisation_node exprtk_final : public expression_node<T>
       {
       public:
 
-         typedef expression_node <T>*      expression_ptr;
-         typedef stringvar_node  <T>*     strvar_node_ptr;
-         typedef string_base_node<T>*        str_base_ptr;
-         typedef range_pack      <T>              range_t;
-         typedef range_t*                       range_ptr;
-         typedef range_interface<T>              irange_t;
-         typedef irange_t*                     irange_ptr;
-         typedef std::pair<expression_ptr,bool>  branch_t;
-
+         typedef expression_node<T>* expression_ptr;
 
-         generic_string_range_node(expression_ptr str_branch, const range_t& brange)
-         : initialised_(false),
-           str_base_ptr_ (0),
-           str_range_ptr_(0),
-           base_range_(brange)
+         vector_initialisation_node(T* vector_base,
+                                    const std::size_t& size,
+                                    const std::vector<expression_ptr>& initialiser_list,
+                                    const bool single_value_initialse)
+         : vector_base_(vector_base)
+         , initialiser_list_(initialiser_list)
+         , size_(size)
+         , single_value_initialse_(single_value_initialse)
+         , zero_value_initialse_(false)
+         , const_nonzero_literal_value_initialse_(false)
+         , single_initialiser_value_(T(0))
          {
-            range_.n0_c = std::make_pair<bool,std::size_t>(true,0);
-            range_.n1_c = std::make_pair<bool,std::size_t>(true,0);
-            range_.cache.first  = range_.n0_c.second;
-            range_.cache.second = range_.n1_c.second;
+            if (single_value_initialse_)
+            {
+               if (initialiser_list_.empty())
+                  zero_value_initialse_ = true;
+               else if (
+                         (initialiser_list_.size() == 1) &&
+                         details::is_constant_node(initialiser_list_[0]) &&
+                         (T(0) == initialiser_list_[0]->value())
+                       )
+               {
+                  zero_value_initialse_ = true;
+               }
+               else
+               {
+                  assert(initialiser_list_.size() == 1);
 
-            construct_branch_pair(branch_, str_branch);
+                  if (details::is_constant_node(initialiser_list_[0]))
+                  {
+                     const_nonzero_literal_value_initialse_ = true;
+                     single_initialiser_value_ = initialiser_list_[0]->value();
+                     assert(T(0) != single_initialiser_value_);
+                  }
+               }
+            }
+         }
 
-            if (is_generally_string_node(branch_.first))
+         inline T value() const exprtk_override
+         {
+            if (single_value_initialse_)
             {
-               str_base_ptr_ = dynamic_cast<str_base_ptr>(branch_.first);
-
-               if (0 == str_base_ptr_)
-                  return;
+               if (zero_value_initialse_)
+               {
+                  details::set_zero_value(vector_base_, size_);
+               }
+               else if (const_nonzero_literal_value_initialse_)
+               {
+                  for (std::size_t i = 0; i < size_; ++i)
+                  {
+                     *(vector_base_ + i) = single_initialiser_value_;
+                  }
+               }
+               else
+               {
+                  for (std::size_t i = 0; i < size_; ++i)
+                  {
+                     *(vector_base_ + i) = initialiser_list_[0]->value();
+                  }
+               }
+            }
+            else
+            {
+               const std::size_t initialiser_list_size = initialiser_list_.size();
 
-               str_range_ptr_ = dynamic_cast<irange_ptr>(branch_.first);
+               for (std::size_t i = 0; i < initialiser_list_size; ++i)
+               {
+                  *(vector_base_ + i) = initialiser_list_[i]->value();
+               }
 
-               if (0 == str_range_ptr_)
-                  return;
+               if (initialiser_list_size < size_)
+               {
+                  details::set_zero_value(
+                     vector_base_ + initialiser_list_size,
+                     (size_ - initialiser_list_size));
+               }
             }
 
-            initialised_ = (str_base_ptr_ && str_range_ptr_);
+            return *(vector_base_);
          }
 
-        ~generic_string_range_node() override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
-            base_range_.free();
+            return expression_node<T>::e_vecinit;
          }
 
-         inline T value() const override
+         inline bool valid() const exprtk_override
          {
-            if (initialised_)
-            {
-               assert(branch_.first);
+            return vector_base_;
+         }
 
-               branch_.first->value();
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(initialiser_list_, node_delete_list);
+         }
 
-               std::size_t str_r0 = 0;
-               std::size_t str_r1 = 0;
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(initialiser_list_);
+         }
 
-               std::size_t r0 = 0;
-               std::size_t r1 = 0;
+      private:
 
-               const range_t& range = str_range_ptr_->range_ref();
+         vector_initialisation_node(const vector_initialisation_node<T>&) exprtk_delete;
+         vector_initialisation_node<T>& operator=(const vector_initialisation_node<T>&) exprtk_delete;
 
-               const std::size_t base_str_size = str_base_ptr_->size();
+         mutable T* vector_base_;
+         std::vector<expression_ptr> initialiser_list_;
+         const std::size_t size_;
+         const bool single_value_initialse_;
+         bool zero_value_initialse_;
+         bool const_nonzero_literal_value_initialse_;
+         T single_initialiser_value_;
+      };
 
-               if (
-                    range      (str_r0, str_r1, base_str_size) &&
-                    base_range_(    r0,     r1, base_str_size)
-                  )
-               {
-                  const std::size_t size = (r1 - r0) + 1;
+      template <typename T>
+      class vector_init_zero_value_node exprtk_final : public expression_node<T>
+      {
+      public:
 
-                  range_.n1_c.second  = size - 1;
-                  range_.cache.second = range_.n1_c.second;
+         typedef expression_node<T>* expression_ptr;
 
-                  value_.assign(str_base_ptr_->base() + str_r0 + r0, size);
-               }
-            }
+         vector_init_zero_value_node(T* vector_base,
+                                     const std::size_t& size,
+                                     const std::vector<expression_ptr>& initialiser_list)
+         : vector_base_(vector_base)
+         , size_(size)
+         , initialiser_list_(initialiser_list)
+         {}
 
-            return std::numeric_limits<T>::quiet_NaN();
+         inline T value() const exprtk_override
+         {
+            details::set_zero_value(vector_base_, size_);
+            return *(vector_base_);
          }
 
-         std::string str() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
-            return value_;
+            return expression_node<T>::e_vecinit;
          }
 
-         char_cptr base() const override
+         inline bool valid() const exprtk_override
          {
-            return &value_[0];
+            return vector_base_;
          }
 
-         std::size_t size() const override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            return value_.size();
+            expression_node<T>::ndb_t::collect(initialiser_list_, node_delete_list);
          }
 
-         range_t& range_ref() override
+         std::size_t node_depth() const exprtk_override
          {
-            return range_;
+            return expression_node<T>::ndb_t::compute_node_depth(initialiser_list_);
+         }
+
+      private:
+
+         vector_init_zero_value_node(const vector_init_zero_value_node<T>&) exprtk_delete;
+         vector_init_zero_value_node<T>& operator=(const vector_init_zero_value_node<T>&) exprtk_delete;
+
+         mutable T* vector_base_;
+         const std::size_t size_;
+         std::vector<expression_ptr> initialiser_list_;
+      };
+
+      template <typename T>
+      class vector_init_single_constvalue_node exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         vector_init_single_constvalue_node(T* vector_base,
+                                            const std::size_t& size,
+                                            const std::vector<expression_ptr>& initialiser_list)
+         : vector_base_(vector_base)
+         , size_(size)
+         , initialiser_list_(initialiser_list)
+         {
+            single_initialiser_value_ = initialiser_list_[0]->value();
+            assert(valid());
          }
 
-         const range_t& range_ref() const override
+         inline T value() const exprtk_override
          {
-            return range_;
+            for (std::size_t i = 0; i < size_; ++i)
+            {
+               *(vector_base_ + i) = single_initialiser_value_;
+            }
+
+            return *(vector_base_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
-            return expression_node<T>::e_strgenrange;
+            return expression_node<T>::e_vecinit;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
          {
-            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+            return vector_base_ &&
+                   (initialiser_list_.size() == 1) &&
+                   (details::is_constant_node(initialiser_list_[0])) &&
+                   (single_initialiser_value_ != T(0));
          }
 
-         std::size_t node_depth() const override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+            expression_node<T>::ndb_t::collect(initialiser_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(initialiser_list_);
          }
 
       private:
 
-         bool                initialised_;
-         branch_t                 branch_;
-         str_base_ptr       str_base_ptr_;
-         irange_ptr        str_range_ptr_;
-         mutable range_t      base_range_;
-         mutable range_t           range_;
-         mutable std::string       value_;
+         vector_init_single_constvalue_node(const vector_init_single_constvalue_node<T>&) exprtk_delete;
+         vector_init_single_constvalue_node<T>& operator=(const vector_init_single_constvalue_node<T>&) exprtk_delete;
+
+         mutable T* vector_base_;
+         const std::size_t size_;
+         std::vector<expression_ptr> initialiser_list_;
+         T single_initialiser_value_;
       };
 
       template <typename T>
-      class string_concat_node : public binary_node     <T>,
-                                 public string_base_node<T>,
-                                 public range_interface <T>
+      class vector_init_single_value_node exprtk_final : public expression_node<T>
       {
       public:
 
-         typedef expression_node <T>*  expression_ptr;
-         typedef string_base_node<T>*    str_base_ptr;
-         typedef range_pack      <T>          range_t;
-         typedef range_t*                   range_ptr;
-         typedef range_interface<T>          irange_t;
-         typedef irange_t*                 irange_ptr;
+         typedef expression_node<T>* expression_ptr;
 
-         string_concat_node(const operator_type& opr,
-                            expression_ptr branch0,
-                            expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           initialised_(false),
-           str0_base_ptr_ (0),
-           str1_base_ptr_ (0),
-           str0_range_ptr_(0),
-           str1_range_ptr_(0)
+         vector_init_single_value_node(T* vector_base,
+                                       const std::size_t& size,
+                                       const std::vector<expression_ptr>& initialiser_list)
+         : vector_base_(vector_base)
+         , size_(size)
+         , initialiser_list_(initialiser_list)
          {
-            range_.n0_c = std::make_pair<bool,std::size_t>(true,0);
-            range_.n1_c = std::make_pair<bool,std::size_t>(true,0);
+            assert(valid());
+         }
 
-            range_.cache.first  = range_.n0_c.second;
-            range_.cache.second = range_.n1_c.second;
+         inline T value() const exprtk_override
+         {
+            expression_node<T>& node = *initialiser_list_[0];
 
-            if (is_generally_string_node(binary_node<T>::branch_[0].first))
+            for (std::size_t i = 0; i < size_; ++i)
             {
-               str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
+               *(vector_base_ + i) = node.value();
+            }
+
+            return *(vector_base_);
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_vecinit;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return vector_base_ &&
+                   (initialiser_list_.size() == 1) &&
+                   !details::is_constant_node(initialiser_list_[0]);
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(initialiser_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(initialiser_list_);
+         }
+
+      private:
+
+         vector_init_single_value_node(const vector_init_single_value_node<T>&) exprtk_delete;
+         vector_init_single_value_node<T>& operator=(const vector_init_single_value_node<T>&) exprtk_delete;
+
+         mutable T* vector_base_;
+         const std::size_t size_;
+         std::vector<expression_ptr> initialiser_list_;
+      };
+
+      template <typename T>
+      class vector_init_iota_constconst_node exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         vector_init_iota_constconst_node(T* vector_base,
+                                          const std::size_t& size,
+                                          const std::vector<expression_ptr>& initialiser_list)
+         : vector_base_(vector_base)
+         , size_(size)
+         , initialiser_list_(initialiser_list)
+         {
+            base_value_      = initialiser_list_[0]->value();
+            increment_value_ = initialiser_list_[1]->value();
+
+            assert(valid());
+         }
+
+         inline T value() const exprtk_override
+         {
+            T value = base_value_;
+
+            for (std::size_t i = 0; i < size_; ++i, value += increment_value_)
+            {
+               *(vector_base_ + i) = value;
+            }
+
+            return *(vector_base_);
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_vecinit;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return vector_base_ &&
+                   (initialiser_list_.size() == 2) &&
+                   (details::is_constant_node(initialiser_list_[0])) &&
+                   (details::is_constant_node(initialiser_list_[1])) ;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(initialiser_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(initialiser_list_);
+         }
+
+      private:
+
+         vector_init_iota_constconst_node(const vector_init_iota_constconst_node<T>&) exprtk_delete;
+         vector_init_iota_constconst_node<T>& operator=(const vector_init_iota_constconst_node<T>&) exprtk_delete;
+
+         mutable T* vector_base_;
+         const std::size_t size_;
+         std::vector<expression_ptr> initialiser_list_;
+         T base_value_;
+         T increment_value_;
+      };
+
+      template <typename T>
+      class vector_init_iota_constnconst_node exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         vector_init_iota_constnconst_node(T* vector_base,
+                                           const std::size_t& size,
+                                           const std::vector<expression_ptr>& initialiser_list)
+         : vector_base_(vector_base)
+         , size_(size)
+         , initialiser_list_(initialiser_list)
+         {
+            assert(valid());
+            base_value_ = initialiser_list_[0]->value();
+         }
+
+         inline T value() const exprtk_override
+         {
+            T value = base_value_;
+            expression_node<T>& increment = *initialiser_list_[1];
+
+            for (std::size_t i = 0; i < size_; ++i, value += increment.value())
+            {
+               *(vector_base_ + i) = value;
+            }
+
+            return *(vector_base_);
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_vecinit;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return vector_base_ &&
+                  (initialiser_list_.size() == 2) &&
+                  ( details::is_constant_node(initialiser_list_[0])) &&
+                  (!details::is_constant_node(initialiser_list_[1]));
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(initialiser_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(initialiser_list_);
+         }
+
+      private:
+
+         vector_init_iota_constnconst_node(const vector_init_iota_constnconst_node<T>&) exprtk_delete;
+         vector_init_iota_constnconst_node<T>& operator=(const vector_init_iota_constnconst_node<T>&) exprtk_delete;
+
+         mutable T* vector_base_;
+         const std::size_t size_;
+         std::vector<expression_ptr> initialiser_list_;
+         T base_value_;
+      };
+
+      template <typename T>
+      class vector_init_iota_nconstconst_node exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         vector_init_iota_nconstconst_node(T* vector_base,
+                                           const std::size_t& size,
+                                           const std::vector<expression_ptr>& initialiser_list)
+         : vector_base_(vector_base)
+         , size_(size)
+         , initialiser_list_(initialiser_list)
+         {
+            assert(valid());
+         }
+
+         inline T value() const exprtk_override
+         {
+            T value = initialiser_list_[0]->value();
+            const T increment = initialiser_list_[1]->value();
+
+            for (std::size_t i = 0; i < size_; ++i, value += increment)
+            {
+               *(vector_base_ + i) = value;
+            }
+
+            return *(vector_base_);
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_vecinit;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return vector_base_ &&
+                   (initialiser_list_.size() == 2) &&
+                   (!details::is_constant_node(initialiser_list_[0])) &&
+                   (details::is_constant_node(initialiser_list_[1]));
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(initialiser_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(initialiser_list_);
+         }
+
+      private:
+
+         vector_init_iota_nconstconst_node(const vector_init_iota_nconstconst_node<T>&) exprtk_delete;
+         vector_init_iota_nconstconst_node<T>& operator=(const vector_init_iota_nconstconst_node<T>&) exprtk_delete;
+
+         mutable T* vector_base_;
+         const std::size_t size_;
+         std::vector<expression_ptr> initialiser_list_;
+      };
+
+      template <typename T>
+      class vector_init_iota_nconstnconst_node exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         vector_init_iota_nconstnconst_node(T* vector_base,
+                                            const std::size_t& size,
+                                            const std::vector<expression_ptr>& initialiser_list)
+         : vector_base_(vector_base)
+         , size_(size)
+         , initialiser_list_(initialiser_list)
+         {
+            assert(valid());
+         }
+
+         inline T value() const exprtk_override
+         {
+            T value = initialiser_list_[0]->value();
+            expression_node<T>& increment = *initialiser_list_[1];
+
+            for (std::size_t i = 0; i < size_; ++i, value += increment.value())
+            {
+               *(vector_base_ + i) = value;
+            }
+
+            return *(vector_base_);
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_vecinit;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return vector_base_ &&
+                   (initialiser_list_.size() == 2) &&
+                   (!details::is_constant_node(initialiser_list_[0])) &&
+                   (!details::is_constant_node(initialiser_list_[1]));
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(initialiser_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(initialiser_list_);
+         }
+
+      private:
+
+         vector_init_iota_nconstnconst_node(const vector_init_iota_nconstnconst_node<T>&) exprtk_delete;
+         vector_init_iota_nconstnconst_node<T>& operator=(const vector_init_iota_nconstnconst_node<T>&) exprtk_delete;
+
+         mutable T* vector_base_;
+         const std::size_t size_;
+         std::vector<expression_ptr> initialiser_list_;
+      };
+
+      template <typename T>
+      class swap_node exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef variable_node<T>*   variable_node_ptr;
+
+         swap_node(variable_node_ptr var0, variable_node_ptr var1)
+         : var0_(var0)
+         , var1_(var1)
+         {}
+
+         inline T value() const exprtk_override
+         {
+            std::swap(var0_->ref(),var1_->ref());
+            return var1_->ref();
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_swap;
+         }
+
+      private:
+
+         variable_node_ptr var0_;
+         variable_node_ptr var1_;
+      };
+
+      template <typename T>
+      class swap_generic_node exprtk_final : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef ivariable<T>*       ivariable_ptr;
+
+         swap_generic_node(expression_ptr var0, expression_ptr var1)
+         : binary_node<T>(details::e_swap, var0, var1)
+         , var0_(dynamic_cast<ivariable_ptr>(var0))
+         , var1_(dynamic_cast<ivariable_ptr>(var1))
+         {}
+
+         inline T value() const exprtk_override
+         {
+            std::swap(var0_->ref(),var1_->ref());
+            return var1_->ref();
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_swap;
+         }
+
+      private:
+
+         ivariable_ptr var0_;
+         ivariable_ptr var1_;
+      };
+
+      template <typename T>
+      class swap_vecvec_node exprtk_final
+                             : public binary_node     <T>
+                             , public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef vector_node    <T>* vector_node_ptr;
+         typedef vec_data_store <T>  vds_t;
+
+         using binary_node<T>::branch;
+
+         swap_vecvec_node(expression_ptr branch0,
+                          expression_ptr branch1)
+         : binary_node<T>(details::e_swap, branch0, branch1)
+         , vec0_node_ptr_(0)
+         , vec1_node_ptr_(0)
+         , initialised_  (false)
+         {
+            if (is_ivector_node(branch(0)))
+            {
+               vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
+
+               if (0 != (vi = dynamic_cast<vector_interface<T>*>(branch(0))))
+               {
+                  vec0_node_ptr_ = vi->vec();
+                  vds()          = vi->vds();
+               }
+            }
+
+            if (is_ivector_node(branch(1)))
+            {
+               vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
+
+               if (0 != (vi = dynamic_cast<vector_interface<T>*>(branch(1))))
+               {
+                  vec1_node_ptr_ = vi->vec();
+               }
+            }
+
+            if (vec0_node_ptr_ && vec1_node_ptr_)
+            {
+               initialised_ = size() <= base_size();
+            }
+
+            assert(valid());
+         }
+
+         inline T value() const exprtk_override
+         {
+            binary_node<T>::branch(0)->value();
+            binary_node<T>::branch(1)->value();
+
+            T* vec0 = vec0_node_ptr_->vds().data();
+            T* vec1 = vec1_node_ptr_->vds().data();
+
+            assert(size() <= base_size());
+            const std::size_t n = size();
+
+            for (std::size_t i = 0; i < n; ++i)
+            {
+               std::swap(vec0[i],vec1[i]);
+            }
+
+            return vec1_node_ptr_->value();
+         }
+
+         vector_node_ptr vec() const exprtk_override
+         {
+            return vec0_node_ptr_;
+         }
+
+         vector_node_ptr vec() exprtk_override
+         {
+            return vec0_node_ptr_;
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_vecvecswap;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return initialised_ && binary_node<T>::valid();
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return std::min(
+               vec0_node_ptr_->vec_holder().size(),
+               vec1_node_ptr_->vec_holder().size());
+         }
+
+         std::size_t base_size() const exprtk_override
+         {
+            return std::min(
+               vec0_node_ptr_->vec_holder().base_size(),
+               vec1_node_ptr_->vec_holder().base_size());
+         }
+
+         vds_t& vds() exprtk_override
+         {
+            return vds_;
+         }
+
+         const vds_t& vds() const exprtk_override
+         {
+            return vds_;
+         }
+
+      private:
+
+         vector_node<T>* vec0_node_ptr_;
+         vector_node<T>* vec1_node_ptr_;
+         bool            initialised_;
+         vds_t           vds_;
+      };
+
+      #ifndef exprtk_disable_string_capabilities
+      template <typename T>
+      class stringvar_node exprtk_final
+                           : public expression_node <T>
+                           , public string_base_node<T>
+                           , public range_interface <T>
+      {
+      public:
+
+         typedef typename range_interface<T>::range_t range_t;
+
+         static std::string null_value;
+
+         explicit stringvar_node()
+         : value_(&null_value)
+         {}
+
+         explicit stringvar_node(std::string& v)
+         : value_(&v)
+         {
+            rp_.n0_c = std::make_pair<bool,std::size_t>(true,0);
+            rp_.n1_c = std::make_pair<bool,std::size_t>(true,v.size());
+            rp_.cache.first  = rp_.n0_c.second;
+            rp_.cache.second = rp_.n1_c.second;
+         }
+
+         inline bool operator <(const stringvar_node<T>& v) const
+         {
+            return this < (&v);
+         }
+
+         inline T value() const exprtk_override
+         {
+            rp_.n1_c.second  = (*value_).size();
+            rp_.cache.second = rp_.n1_c.second;
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         std::string str() const exprtk_override
+         {
+            return ref();
+         }
+
+         char_cptr base() const exprtk_override
+         {
+            return &(*value_)[0];
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return ref().size();
+         }
+
+         std::string& ref()
+         {
+            return (*value_);
+         }
+
+         const std::string& ref() const
+         {
+            return (*value_);
+         }
+
+         range_t& range_ref() exprtk_override
+         {
+            return rp_;
+         }
+
+         const range_t& range_ref() const exprtk_override
+         {
+            return rp_;
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_stringvar;
+         }
+
+         void rebase(std::string& s)
+         {
+            value_ = &s;
+            rp_.n0_c = std::make_pair<bool,std::size_t>(true,0);
+            rp_.n1_c = std::make_pair<bool,std::size_t>(true,value_->size() - 1);
+            rp_.cache.first  = rp_.n0_c.second;
+            rp_.cache.second = rp_.n1_c.second;
+         }
+
+      private:
+
+         std::string* value_;
+         mutable range_t rp_;
+      };
+
+      template <typename T>
+      std::string stringvar_node<T>::null_value = std::string("");
+
+      template <typename T>
+      class string_range_node exprtk_final
+                              : public expression_node <T>
+                              , public string_base_node<T>
+                              , public range_interface <T>
+      {
+      public:
+
+         typedef typename range_interface<T>::range_t range_t;
+
+         static std::string null_value;
+
+         explicit string_range_node(std::string& v, const range_t& rp)
+         : value_(&v)
+         , rp_(rp)
+         {}
+
+         ~string_range_node() exprtk_override
+         {
+            rp_.free();
+         }
+
+         inline bool operator <(const string_range_node<T>& v) const
+         {
+            return this < (&v);
+         }
+
+         inline T value() const exprtk_override
+         {
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline std::string str() const exprtk_override
+         {
+            return (*value_);
+         }
+
+         char_cptr base() const exprtk_override
+         {
+            return &(*value_)[0];
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return ref().size();
+         }
+
+         inline range_t range() const
+         {
+            return rp_;
+         }
+
+         inline virtual std::string& ref()
+         {
+            return (*value_);
+         }
+
+         inline virtual const std::string& ref() const
+         {
+            return (*value_);
+         }
+
+         inline range_t& range_ref() exprtk_override
+         {
+            return rp_;
+         }
+
+         inline const range_t& range_ref() const exprtk_override
+         {
+            return rp_;
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_stringvarrng;
+         }
+
+      private:
+
+         std::string* value_;
+         range_t      rp_;
+      };
+
+      template <typename T>
+      std::string string_range_node<T>::null_value = std::string("");
+
+      template <typename T>
+      class const_string_range_node exprtk_final
+                                    : public expression_node <T>
+                                    , public string_base_node<T>
+                                    , public range_interface <T>
+      {
+      public:
+
+         typedef typename range_interface<T>::range_t range_t;
+
+         explicit const_string_range_node(const std::string& v, const range_t& rp)
+         : value_(v)
+         , rp_(rp)
+         {}
+
+        ~const_string_range_node() exprtk_override
+         {
+            rp_.free();
+         }
+
+         inline T value() const exprtk_override
+         {
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         std::string str() const exprtk_override
+         {
+            return value_;
+         }
+
+         char_cptr base() const exprtk_override
+         {
+            return value_.data();
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return value_.size();
+         }
+
+         range_t range() const
+         {
+            return rp_;
+         }
+
+         range_t& range_ref() exprtk_override
+         {
+            return rp_;
+         }
+
+         const range_t& range_ref() const exprtk_override
+         {
+            return rp_;
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_cstringvarrng;
+         }
+
+      private:
+
+         const_string_range_node(const const_string_range_node<T>&) exprtk_delete;
+         const_string_range_node<T>& operator=(const const_string_range_node<T>&) exprtk_delete;
+
+         const std::string value_;
+         range_t rp_;
+      };
+
+      template <typename T>
+      class generic_string_range_node exprtk_final
+                                      : public expression_node <T>
+                                      , public string_base_node<T>
+                                      , public range_interface <T>
+      {
+      public:
+
+         typedef expression_node <T>* expression_ptr;
+         typedef stringvar_node  <T>* strvar_node_ptr;
+         typedef string_base_node<T>* str_base_ptr;
+         typedef typename range_interface<T>::range_t range_t;
+         typedef range_t*             range_ptr;
+         typedef range_interface<T>   irange_t;
+         typedef irange_t*            irange_ptr;
+         typedef std::pair<expression_ptr,bool>  branch_t;
+
+         generic_string_range_node(expression_ptr str_branch, const range_t& brange)
+         : initialised_(false)
+         , str_base_ptr_ (0)
+         , str_range_ptr_(0)
+         , base_range_(brange)
+         {
+            range_.n0_c = std::make_pair<bool,std::size_t>(true,0);
+            range_.n1_c = std::make_pair<bool,std::size_t>(true,0);
+            range_.cache.first  = range_.n0_c.second;
+            range_.cache.second = range_.n1_c.second;
+
+            construct_branch_pair(branch_, str_branch);
+
+            if (is_generally_string_node(branch_.first))
+            {
+               str_base_ptr_ = dynamic_cast<str_base_ptr>(branch_.first);
+
+               if (0 == str_base_ptr_)
+                  return;
+
+               str_range_ptr_ = dynamic_cast<irange_ptr>(branch_.first);
+
+               if (0 == str_range_ptr_)
+                  return;
+            }
+
+            initialised_ = (str_base_ptr_ && str_range_ptr_);
+            assert(valid());
+         }
+
+        ~generic_string_range_node() exprtk_override
+         {
+            base_range_.free();
+         }
+
+         inline T value() const exprtk_override
+         {
+            branch_.first->value();
+
+            std::size_t str_r0 = 0;
+            std::size_t str_r1 = 0;
+
+            std::size_t r0 = 0;
+            std::size_t r1 = 0;
+
+            const range_t& range = str_range_ptr_->range_ref();
+
+            const std::size_t base_str_size = str_base_ptr_->size();
+
+            if (
+                  range      (str_r0, str_r1, base_str_size         ) &&
+                  base_range_(r0    , r1    , base_str_size - str_r0)
+               )
+            {
+               const std::size_t size = r1 - r0;
+
+               range_.n1_c.second  = size;
+               range_.cache.second = range_.n1_c.second;
+
+               value_.assign(str_base_ptr_->base() + str_r0 + r0, size);
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         std::string str() const exprtk_override
+         {
+            return value_;
+         }
+
+         char_cptr base() const exprtk_override
+         {
+            return &value_[0];
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return value_.size();
+         }
+
+         range_t& range_ref() exprtk_override
+         {
+            return range_;
+         }
+
+         const range_t& range_ref() const exprtk_override
+         {
+            return range_;
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_strgenrange;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return initialised_ && branch_.first;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
+      private:
+
+         bool                initialised_;
+         branch_t            branch_;
+         str_base_ptr        str_base_ptr_;
+         irange_ptr          str_range_ptr_;
+         mutable range_t     base_range_;
+         mutable range_t     range_;
+         mutable std::string value_;
+      };
+
+      template <typename T>
+      class string_concat_node exprtk_final
+                               : public binary_node     <T>
+                               , public string_base_node<T>
+                               , public range_interface <T>
+      {
+      public:
+
+         typedef typename range_interface<T>::range_t range_t;
+         typedef range_interface<T>   irange_t;
+         typedef irange_t*            irange_ptr;
+         typedef range_t*             range_ptr;
+         typedef expression_node <T>* expression_ptr;
+         typedef string_base_node<T>* str_base_ptr;
+
+         using binary_node<T>::branch;
+
+         string_concat_node(const operator_type& opr,
+                            expression_ptr branch0,
+                            expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1)
+         , initialised_(false)
+         , str0_base_ptr_ (0)
+         , str1_base_ptr_ (0)
+         , str0_range_ptr_(0)
+         , str1_range_ptr_(0)
+         {
+            range_.n0_c = std::make_pair<bool,std::size_t>(true,0);
+            range_.n1_c = std::make_pair<bool,std::size_t>(true,0);
+
+            range_.cache.first  = range_.n0_c.second;
+            range_.cache.second = range_.n1_c.second;
+
+            if (is_generally_string_node(branch(0)))
+            {
+               str0_base_ptr_ = dynamic_cast<str_base_ptr>(branch(0));
 
                if (0 == str0_base_ptr_)
                   return;
 
-               str0_range_ptr_ = dynamic_cast<irange_ptr>(binary_node<T>::branch_[0].first);
+               str0_range_ptr_ = dynamic_cast<irange_ptr>(branch(0));
 
                if (0 == str0_range_ptr_)
                   return;
             }
 
-            if (is_generally_string_node(binary_node<T>::branch_[1].first))
+            if (is_generally_string_node(branch(1)))
             {
-               str1_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[1].first);
+               str1_base_ptr_ = dynamic_cast<str_base_ptr>(branch(1));
 
                if (0 == str1_base_ptr_)
                   return;
 
-               str1_range_ptr_ = dynamic_cast<irange_ptr>(binary_node<T>::branch_[1].first);
+               str1_range_ptr_ = dynamic_cast<irange_ptr>(branch(1));
 
                if (0 == str1_range_ptr_)
                   return;
@@ -8543,167 +10568,171 @@ namespace exprtk
                            str1_base_ptr_  &&
                            str0_range_ptr_ &&
                            str1_range_ptr_ ;
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (initialised_)
-            {
-               assert(binary_node<T>::branch_[0].first);
-               assert(binary_node<T>::branch_[1].first);
-
-               binary_node<T>::branch_[0].first->value();
-               binary_node<T>::branch_[1].first->value();
+            branch(0)->value();
+            branch(1)->value();
 
-               std::size_t str0_r0 = 0;
-               std::size_t str0_r1 = 0;
+            std::size_t str0_r0 = 0;
+            std::size_t str0_r1 = 0;
 
-               std::size_t str1_r0 = 0;
-               std::size_t str1_r1 = 0;
+            std::size_t str1_r0 = 0;
+            std::size_t str1_r1 = 0;
 
-               const range_t& range0 = str0_range_ptr_->range_ref();
-               const range_t& range1 = str1_range_ptr_->range_ref();
+            const range_t& range0 = str0_range_ptr_->range_ref();
+            const range_t& range1 = str1_range_ptr_->range_ref();
 
-               if (
-                    range0(str0_r0, str0_r1, str0_base_ptr_->size()) &&
-                    range1(str1_r0, str1_r1, str1_base_ptr_->size())
-                  )
-               {
-                  const std::size_t size0 = (str0_r1 - str0_r0) + 1;
-                  const std::size_t size1 = (str1_r1 - str1_r0) + 1;
+            if (
+                  range0(str0_r0, str0_r1, str0_base_ptr_->size()) &&
+                  range1(str1_r0, str1_r1, str1_base_ptr_->size())
+               )
+            {
+               const std::size_t size0 = (str0_r1 - str0_r0);
+               const std::size_t size1 = (str1_r1 - str1_r0);
 
-                  value_.assign(str0_base_ptr_->base() + str0_r0, size0);
-                  value_.append(str1_base_ptr_->base() + str1_r0, size1);
+               value_.assign(str0_base_ptr_->base() + str0_r0, size0);
+               value_.append(str1_base_ptr_->base() + str1_r0, size1);
 
-                  range_.n1_c.second  = value_.size() - 1;
-                  range_.cache.second = range_.n1_c.second;
-               }
+               range_.n1_c.second  = value_.size();
+               range_.cache.second = range_.n1_c.second;
             }
 
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         std::string str() const override
+         std::string str() const exprtk_override
          {
             return value_;
          }
 
-         char_cptr base() const override
+         char_cptr base() const exprtk_override
          {
             return &value_[0];
          }
 
-         std::size_t size() const override
+         std::size_t size() const exprtk_override
          {
             return value_.size();
          }
 
-         range_t& range_ref() override
+         range_t& range_ref() exprtk_override
          {
             return range_;
          }
 
-         const range_t& range_ref() const override
+         const range_t& range_ref() const exprtk_override
          {
             return range_;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_strconcat;
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return initialised_ && binary_node<T>::valid();
+         }
+
       private:
 
-         bool initialised_;
-         str_base_ptr str0_base_ptr_;
-         str_base_ptr str1_base_ptr_;
-         irange_ptr   str0_range_ptr_;
-         irange_ptr   str1_range_ptr_;
+         bool                initialised_;
+         str_base_ptr        str0_base_ptr_;
+         str_base_ptr        str1_base_ptr_;
+         irange_ptr          str0_range_ptr_;
+         irange_ptr          str1_range_ptr_;
          mutable range_t     range_;
          mutable std::string value_;
       };
 
       template <typename T>
-      class swap_string_node : public binary_node     <T>,
-                               public string_base_node<T>,
-                               public range_interface <T>
+      class swap_string_node exprtk_final
+                             : public binary_node     <T>
+                             , public string_base_node<T>
+                             , public range_interface <T>
       {
       public:
 
-         typedef expression_node <T>*  expression_ptr;
+         typedef typename range_interface<T>::range_t range_t;
+         typedef range_t*             range_ptr;
+         typedef range_interface<T>   irange_t;
+         typedef irange_t*            irange_ptr;
+         typedef expression_node <T>* expression_ptr;
          typedef stringvar_node  <T>* strvar_node_ptr;
-         typedef string_base_node<T>*    str_base_ptr;
-         typedef range_pack      <T>          range_t;
-         typedef range_t*                   range_ptr;
-         typedef range_interface<T>          irange_t;
-         typedef irange_t*                 irange_ptr;
+         typedef string_base_node<T>* str_base_ptr;
+
+         using binary_node<T>::branch;
 
          swap_string_node(expression_ptr branch0, expression_ptr branch1)
-         : binary_node<T>(details::e_swap, branch0, branch1),
-           initialised_(false),
-           str0_node_ptr_(0),
-           str1_node_ptr_(0)
+         : binary_node<T>(details::e_swap, branch0, branch1)
+         , initialised_(false)
+         , str0_node_ptr_(0)
+         , str1_node_ptr_(0)
          {
-            if (is_string_node(binary_node<T>::branch_[0].first))
+            if (is_string_node(branch(0)))
             {
-               str0_node_ptr_ = static_cast<strvar_node_ptr>(binary_node<T>::branch_[0].first);
+               str0_node_ptr_ = static_cast<strvar_node_ptr>(branch(0));
             }
 
-            if (is_string_node(binary_node<T>::branch_[1].first))
+            if (is_string_node(branch(1)))
             {
-               str1_node_ptr_ = static_cast<strvar_node_ptr>(binary_node<T>::branch_[1].first);
+               str1_node_ptr_ = static_cast<strvar_node_ptr>(branch(1));
             }
 
             initialised_ = (str0_node_ptr_ && str1_node_ptr_);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (initialised_)
-            {
-               assert(binary_node<T>::branch_[0].first);
-               assert(binary_node<T>::branch_[1].first);
+            branch(0)->value();
+            branch(1)->value();
 
-               binary_node<T>::branch_[0].first->value();
-               binary_node<T>::branch_[1].first->value();
-
-               std::swap(str0_node_ptr_->ref(), str1_node_ptr_->ref());
-            }
+            std::swap(str0_node_ptr_->ref(), str1_node_ptr_->ref());
 
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         std::string str() const override
+         std::string str() const exprtk_override
          {
             return str0_node_ptr_->str();
          }
 
-         char_cptr base() const override
+         char_cptr base() const exprtk_override
          {
            return str0_node_ptr_->base();
          }
 
-         std::size_t size() const override
+         std::size_t size() const exprtk_override
          {
             return str0_node_ptr_->size();
          }
 
-         range_t& range_ref() override
+         range_t& range_ref() exprtk_override
          {
             return str0_node_ptr_->range_ref();
          }
 
-         const range_t& range_ref() const override
+         const range_t& range_ref() const exprtk_override
          {
             return str0_node_ptr_->range_ref();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_strswap;
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return initialised_ && binary_node<T>::valid();
+         }
+
       private:
 
          bool initialised_;
@@ -8712,34 +10741,36 @@ namespace exprtk
       };
 
       template <typename T>
-      class swap_genstrings_node : public binary_node<T>
+      class swap_genstrings_node exprtk_final : public binary_node<T>
       {
       public:
 
+         typedef typename range_interface<T>::range_t range_t;
+         typedef range_t*             range_ptr;
+         typedef range_interface<T>   irange_t;
+         typedef irange_t*            irange_ptr;
          typedef expression_node <T>* expression_ptr;
-         typedef string_base_node<T>*   str_base_ptr;
-         typedef range_pack      <T>         range_t;
-         typedef range_t*                  range_ptr;
-         typedef range_interface<T>         irange_t;
-         typedef irange_t*                irange_ptr;
+         typedef string_base_node<T>* str_base_ptr;
+
+         using binary_node<T>::branch;
 
          swap_genstrings_node(expression_ptr branch0,
                               expression_ptr branch1)
-         : binary_node<T>(details::e_default, branch0, branch1),
-           str0_base_ptr_ (0),
-           str1_base_ptr_ (0),
-           str0_range_ptr_(0),
-           str1_range_ptr_(0),
-           initialised_(false)
+         : binary_node<T>(details::e_default, branch0, branch1)
+         , str0_base_ptr_ (0)
+         , str1_base_ptr_ (0)
+         , str0_range_ptr_(0)
+         , str1_range_ptr_(0)
+         , initialised_(false)
          {
-            if (is_generally_string_node(binary_node<T>::branch_[0].first))
+            if (is_generally_string_node(branch(0)))
             {
-               str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
+               str0_base_ptr_ = dynamic_cast<str_base_ptr>(branch(0));
 
                if (0 == str0_base_ptr_)
                   return;
 
-               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[0].first);
+               irange_ptr range = dynamic_cast<irange_ptr>(branch(0));
 
                if (0 == range)
                   return;
@@ -8747,14 +10778,14 @@ namespace exprtk
                str0_range_ptr_ = &(range->range_ref());
             }
 
-            if (is_generally_string_node(binary_node<T>::branch_[1].first))
+            if (is_generally_string_node(branch(1)))
             {
-               str1_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[1].first);
+               str1_base_ptr_ = dynamic_cast<str_base_ptr>(branch(1));
 
                if (0 == str1_base_ptr_)
                   return;
 
-               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[1].first);
+               irange_ptr range = dynamic_cast<irange_ptr>(branch(1));
 
                if (0 == range)
                   return;
@@ -8766,100 +10797,101 @@ namespace exprtk
                            str1_base_ptr_  &&
                            str0_range_ptr_ &&
                            str1_range_ptr_ ;
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (initialised_)
-            {
-               assert(binary_node<T>::branch_[0].first);
-               assert(binary_node<T>::branch_[1].first);
+            branch(0)->value();
+            branch(1)->value();
 
-               binary_node<T>::branch_[0].first->value();
-               binary_node<T>::branch_[1].first->value();
+            std::size_t str0_r0 = 0;
+            std::size_t str0_r1 = 0;
 
-               std::size_t str0_r0 = 0;
-               std::size_t str0_r1 = 0;
+            std::size_t str1_r0 = 0;
+            std::size_t str1_r1 = 0;
 
-               std::size_t str1_r0 = 0;
-               std::size_t str1_r1 = 0;
+            const range_t& range0 = (*str0_range_ptr_);
+            const range_t& range1 = (*str1_range_ptr_);
 
-               const range_t& range0 = (*str0_range_ptr_);
-               const range_t& range1 = (*str1_range_ptr_);
+            if (
+                  range0(str0_r0, str0_r1, str0_base_ptr_->size()) &&
+                  range1(str1_r0, str1_r1, str1_base_ptr_->size())
+               )
+            {
+               const std::size_t size0    = range0.cache_size();
+               const std::size_t size1    = range1.cache_size();
+               const std::size_t max_size = std::min(size0,size1);
 
-               if (
-                    range0(str0_r0, str0_r1, str0_base_ptr_->size()) &&
-                    range1(str1_r0, str1_r1, str1_base_ptr_->size())
-                  )
-               {
-                  const std::size_t size0    = range0.cache_size();
-                  const std::size_t size1    = range1.cache_size();
-                  const std::size_t max_size = std::min(size0,size1);
+               char_ptr s0 = const_cast<char_ptr>(str0_base_ptr_->base() + str0_r0);
+               char_ptr s1 = const_cast<char_ptr>(str1_base_ptr_->base() + str1_r0);
 
-                  char_ptr s0 = const_cast<char_ptr>(str0_base_ptr_->base() + str0_r0);
-                  char_ptr s1 = const_cast<char_ptr>(str1_base_ptr_->base() + str1_r0);
+               loop_unroll::details lud(max_size);
+               char_cptr upper_bound = s0 + lud.upper_bound;
 
-                  loop_unroll::details lud(max_size);
-                  char_cptr upper_bound = s0 + lud.upper_bound;
+               while (s0 < upper_bound)
+               {
+                  #define exprtk_loop(N)   \
+                  std::swap(s0[N], s1[N]); \
 
-                  while (s0 < upper_bound)
-                  {
-                     #define exprtk_loop(N)   \
-                     std::swap(s0[N], s1[N]); \
-
-                     exprtk_loop( 0) exprtk_loop( 1)
-                     exprtk_loop( 2) exprtk_loop( 3)
-                     #ifndef exprtk_disable_superscalar_unroll
-                     exprtk_loop( 4) exprtk_loop( 5)
-                     exprtk_loop( 6) exprtk_loop( 7)
-                     exprtk_loop( 8) exprtk_loop( 9)
-                     exprtk_loop(10) exprtk_loop(11)
-                     exprtk_loop(12) exprtk_loop(13)
-                     exprtk_loop(14) exprtk_loop(15)
-                     #endif
-
-                     s0 += lud.batch_size;
-                     s1 += lud.batch_size;
-                  }
+                  exprtk_loop( 0) exprtk_loop( 1)
+                  exprtk_loop( 2) exprtk_loop( 3)
+                  #ifndef exprtk_disable_superscalar_unroll
+                  exprtk_loop( 4) exprtk_loop( 5)
+                  exprtk_loop( 6) exprtk_loop( 7)
+                  exprtk_loop( 8) exprtk_loop( 9)
+                  exprtk_loop(10) exprtk_loop(11)
+                  exprtk_loop(12) exprtk_loop(13)
+                  exprtk_loop(14) exprtk_loop(15)
+                  #endif
 
-                  int i = 0;
+                  s0 += lud.batch_size;
+                  s1 += lud.batch_size;
+               }
 
-                  exprtk_disable_fallthrough_begin
-                  switch (lud.remainder)
-                  {
-                     #define case_stmt(N)                       \
-                     case N : { std::swap(s0[i], s1[i]); ++i; } \
-
-                     #ifndef exprtk_disable_superscalar_unroll
-                     case_stmt(15) case_stmt(14)
-                     case_stmt(13) case_stmt(12)
-                     case_stmt(11) case_stmt(10)
-                     case_stmt( 9) case_stmt( 8)
-                     case_stmt( 7) case_stmt( 6)
-                     case_stmt( 5) case_stmt( 4)
-                     #endif
-                     case_stmt( 3) case_stmt( 2)
-                     case_stmt( 1)
-                  }
-                  exprtk_disable_fallthrough_end
+               int i = 0;
 
-                  #undef exprtk_loop
-                  #undef case_stmt
+               switch (lud.remainder)
+               {
+                  #define case_stmt(N)                       \
+                  case N : { std::swap(s0[i], s1[i]); ++i; } \
+                  exprtk_fallthrough                         \
+
+                  #ifndef exprtk_disable_superscalar_unroll
+                  case_stmt(15) case_stmt(14)
+                  case_stmt(13) case_stmt(12)
+                  case_stmt(11) case_stmt(10)
+                  case_stmt( 9) case_stmt( 8)
+                  case_stmt( 7) case_stmt( 6)
+                  case_stmt( 5) case_stmt( 4)
+                  #endif
+                  case_stmt( 3) case_stmt( 2)
+                  case_stmt( 1)
+                  default: break;
                }
+
+               #undef exprtk_loop
+               #undef case_stmt
             }
 
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_strswap;
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return initialised_ && binary_node<T>::valid();
+         }
+
       private:
 
-         swap_genstrings_node(swap_genstrings_node<T>&);
-         swap_genstrings_node<T>& operator=(swap_genstrings_node<T>&);
+         swap_genstrings_node(const swap_genstrings_node<T>&) exprtk_delete;
+         swap_genstrings_node<T>& operator=(const swap_genstrings_node<T>&) exprtk_delete;
 
          str_base_ptr str0_base_ptr_;
          str_base_ptr str1_base_ptr_;
@@ -8869,11 +10901,11 @@ namespace exprtk
       };
 
       template <typename T>
-      class stringvar_size_node : public expression_node<T>
+      class stringvar_size_node exprtk_final : public expression_node<T>
       {
       public:
 
-         static std::string null_value;
+         static const std::string null_value;
 
          explicit stringvar_size_node()
          : value_(&null_value)
@@ -8883,34 +10915,33 @@ namespace exprtk
          : value_(&v)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return T((*value_).size());
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_stringvarsize;
          }
 
       private:
 
-         std::string* value_;
+         const std::string* value_;
       };
 
       template <typename T>
-      std::string stringvar_size_node<T>::null_value = std::string("");
+      const std::string stringvar_size_node<T>::null_value = std::string("");
 
       template <typename T>
-      class string_size_node : public expression_node<T>
+      class string_size_node exprtk_final : public expression_node<T>
       {
       public:
 
-         typedef expression_node <T>*      expression_ptr;
-         typedef string_base_node<T>*        str_base_ptr;
+         typedef expression_node <T>* expression_ptr;
+         typedef string_base_node<T>* str_base_ptr;
          typedef std::pair<expression_ptr,bool>  branch_t;
 
-
          explicit string_size_node(expression_ptr branch)
          : str_base_ptr_(0)
          {
@@ -8919,43 +10950,40 @@ namespace exprtk
             if (is_generally_string_node(branch_.first))
             {
                str_base_ptr_ = dynamic_cast<str_base_ptr>(branch_.first);
-
-               if (0 == str_base_ptr_)
-                  return;
             }
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            T result = std::numeric_limits<T>::quiet_NaN();
-
-            if (str_base_ptr_)
-            {
-               branch_.first->value();
-               result = T(str_base_ptr_->size());
-            }
-
-            return result;
+            branch_.first->value();
+            return T(str_base_ptr_->size());
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_stringsize;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
+         {
+            return str_base_ptr_;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
             expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
 
-         branch_t           branch_;
+         branch_t     branch_;
          str_base_ptr str_base_ptr_;
       };
 
@@ -8972,45 +11000,47 @@ namespace exprtk
       };
 
       template <typename T, typename AssignmentProcess = asn_assignment>
-      class assignment_string_node : public binary_node     <T>,
-                                     public string_base_node<T>,
-                                     public range_interface <T>
+      class assignment_string_node exprtk_final
+                                   : public binary_node     <T>
+                                   , public string_base_node<T>
+                                   , public range_interface <T>
       {
       public:
 
-         typedef expression_node <T>*  expression_ptr;
+         typedef typename range_interface<T>::range_t range_t;
+         typedef range_t*             range_ptr;
+         typedef range_interface <T>  irange_t;
+         typedef irange_t*            irange_ptr;
+         typedef expression_node <T>* expression_ptr;
          typedef stringvar_node  <T>* strvar_node_ptr;
-         typedef string_base_node<T>*    str_base_ptr;
-         typedef range_pack      <T>          range_t;
-         typedef range_t*                   range_ptr;
-         typedef range_interface<T>          irange_t;
-         typedef irange_t*                 irange_ptr;
+         typedef string_base_node<T>* str_base_ptr;
+
+         using binary_node<T>::branch;
 
          assignment_string_node(const operator_type& opr,
                                 expression_ptr branch0,
                                 expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           initialised_(false),
-           str0_base_ptr_ (0),
-           str1_base_ptr_ (0),
-           str0_node_ptr_ (0),
-           str1_range_ptr_(0)
+         : binary_node<T>(opr, branch0, branch1)
+         , initialised_(false)
+         , str0_base_ptr_ (0)
+         , str1_base_ptr_ (0)
+         , str0_node_ptr_ (0)
+         , str1_range_ptr_(0)
          {
-            if (is_string_node(binary_node<T>::branch_[0].first))
+            if (is_string_node(branch(0)))
             {
-               str0_node_ptr_ = static_cast<strvar_node_ptr>(binary_node<T>::branch_[0].first);
-
-               str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
+               str0_node_ptr_ = static_cast<strvar_node_ptr>(branch(0));
+               str0_base_ptr_ = dynamic_cast<str_base_ptr>(branch(0));
             }
 
-            if (is_generally_string_node(binary_node<T>::branch_[1].first))
+            if (is_generally_string_node(branch(1)))
             {
-               str1_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[1].first);
+               str1_base_ptr_ = dynamic_cast<str_base_ptr>(branch(1));
 
                if (0 == str1_base_ptr_)
                   return;
 
-               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[1].first);
+               irange_ptr range = dynamic_cast<irange_ptr>(branch(1));
 
                if (0 == range)
                   return;
@@ -9022,65 +11052,66 @@ namespace exprtk
                            str1_base_ptr_  &&
                            str0_node_ptr_  &&
                            str1_range_ptr_ ;
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (initialised_)
-            {
-               assert(binary_node<T>::branch_[0].first);
-               assert(binary_node<T>::branch_[1].first);
-
-               binary_node<T>::branch_[1].first->value();
+            branch(1)->value();
 
-               std::size_t r0 = 0;
-               std::size_t r1 = 0;
+            std::size_t r0 = 0;
+            std::size_t r1 = 0;
 
-               const range_t& range = (*str1_range_ptr_);
+            const range_t& range = (*str1_range_ptr_);
 
-               if (range(r0, r1, str1_base_ptr_->size()))
-               {
-                  AssignmentProcess::execute(str0_node_ptr_->ref(),
-                                             str1_base_ptr_->base() + r0,
-                                             (r1 - r0) + 1);
+            if (range(r0, r1, str1_base_ptr_->size()))
+            {
+               AssignmentProcess::execute(
+                  str0_node_ptr_->ref(),
+                  str1_base_ptr_->base() + r0, (r1 - r0));
 
-                  binary_node<T>::branch_[0].first->value();
-               }
+               branch(0)->value();
             }
 
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         std::string str() const override
+         std::string str() const exprtk_override
          {
             return str0_node_ptr_->str();
          }
 
-         char_cptr base() const override
+         char_cptr base() const exprtk_override
          {
            return str0_node_ptr_->base();
          }
 
-         std::size_t size() const override
+         std::size_t size() const exprtk_override
          {
             return str0_node_ptr_->size();
          }
 
-         range_t& range_ref() override
+         range_t& range_ref() exprtk_override
          {
             return str0_node_ptr_->range_ref();
          }
 
-         const range_t& range_ref() const override
+         const range_t& range_ref() const exprtk_override
          {
             return str0_node_ptr_->range_ref();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_strass;
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return initialised_ && binary_node<T>::valid();
+         }
+
       private:
 
          bool            initialised_;
@@ -9091,39 +11122,40 @@ namespace exprtk
       };
 
       template <typename T, typename AssignmentProcess = asn_assignment>
-      class assignment_string_range_node : public binary_node     <T>,
-                                           public string_base_node<T>,
-                                           public range_interface <T>
+      class assignment_string_range_node exprtk_final
+                                         : public binary_node     <T>
+                                         , public string_base_node<T>
+                                         , public range_interface <T>
       {
       public:
 
-         typedef expression_node  <T>*   expression_ptr;
-         typedef stringvar_node   <T>*  strvar_node_ptr;
+         typedef typename range_interface<T>::range_t range_t;
+         typedef range_t*              range_ptr;
+         typedef range_interface  <T>  irange_t;
+         typedef irange_t*             irange_ptr;
+         typedef expression_node  <T>* expression_ptr;
+         typedef stringvar_node   <T>* strvar_node_ptr;
          typedef string_range_node<T>* str_rng_node_ptr;
-         typedef string_base_node <T>*     str_base_ptr;
-         typedef range_pack       <T>           range_t;
-         typedef range_t*                     range_ptr;
-         typedef range_interface<T>            irange_t;
-         typedef irange_t*                   irange_ptr;
+         typedef string_base_node <T>* str_base_ptr;
+
+         using binary_node<T>::branch;
 
          assignment_string_range_node(const operator_type& opr,
                                       expression_ptr branch0,
                                       expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           initialised_(false),
-           str0_base_ptr_     (0),
-           str1_base_ptr_     (0),
-           str0_rng_node_ptr_ (0),
-           str0_range_ptr_    (0),
-           str1_range_ptr_    (0)
+         : binary_node<T>(opr, branch0, branch1)
+         , initialised_(false)
+         , str0_base_ptr_    (0)
+         , str1_base_ptr_    (0)
+         , str0_rng_node_ptr_(0)
+         , str0_range_ptr_   (0)
+         , str1_range_ptr_   (0)
          {
-            if (is_string_range_node(binary_node<T>::branch_[0].first))
+            if (is_string_range_node(branch(0)))
             {
-               str0_rng_node_ptr_ = static_cast<str_rng_node_ptr>(binary_node<T>::branch_[0].first);
-
-               str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
-
-               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[0].first);
+               str0_rng_node_ptr_ = static_cast<str_rng_node_ptr>(branch(0));
+               str0_base_ptr_     = dynamic_cast<str_base_ptr>(branch(0));
+               irange_ptr range   = dynamic_cast<irange_ptr>(branch(0));
 
                if (0 == range)
                   return;
@@ -9131,14 +11163,14 @@ namespace exprtk
                str0_range_ptr_ = &(range->range_ref());
             }
 
-            if (is_generally_string_node(binary_node<T>::branch_[1].first))
+            if (is_generally_string_node(branch(1)))
             {
-               str1_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[1].first);
+               str1_base_ptr_ = dynamic_cast<str_base_ptr>(branch(1));
 
                if (0 == str1_base_ptr_)
                   return;
 
-               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[1].first);
+               irange_ptr range = dynamic_cast<irange_ptr>(branch(1));
 
                if (0 == range)
                   return;
@@ -9151,73 +11183,75 @@ namespace exprtk
                            str0_rng_node_ptr_ &&
                            str0_range_ptr_    &&
                            str1_range_ptr_    ;
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (initialised_)
-            {
-               assert(binary_node<T>::branch_[0].first);
-               assert(binary_node<T>::branch_[1].first);
-
-               binary_node<T>::branch_[0].first->value();
-               binary_node<T>::branch_[1].first->value();
+            branch(0)->value();
+            branch(1)->value();
 
-               std::size_t s0_r0 = 0;
-               std::size_t s0_r1 = 0;
+            std::size_t s0_r0 = 0;
+            std::size_t s0_r1 = 0;
 
-               std::size_t s1_r0 = 0;
-               std::size_t s1_r1 = 0;
+            std::size_t s1_r0 = 0;
+            std::size_t s1_r1 = 0;
 
-               const range_t& range0 = (*str0_range_ptr_);
-               const range_t& range1 = (*str1_range_ptr_);
+            const range_t& range0 = (*str0_range_ptr_);
+            const range_t& range1 = (*str1_range_ptr_);
 
-               if (
-                    range0(s0_r0, s0_r1, str0_base_ptr_->size()) &&
-                    range1(s1_r0, s1_r1, str1_base_ptr_->size())
-                  )
-               {
-                  const std::size_t size = std::min((s0_r1 - s0_r0), (s1_r1 - s1_r0)) + 1;
+            if (
+                  range0(s0_r0, s0_r1, str0_base_ptr_->size()) &&
+                  range1(s1_r0, s1_r1, str1_base_ptr_->size())
+               )
+            {
+               const std::size_t size = std::min((s0_r1 - s0_r0), (s1_r1 - s1_r0));
 
-                  std::copy(str1_base_ptr_->base() + s1_r0,
-                            str1_base_ptr_->base() + s1_r0 + size,
-                            const_cast<char_ptr>(base() + s0_r0));
-               }
+               std::copy(
+                  str1_base_ptr_->base() + s1_r0,
+                  str1_base_ptr_->base() + s1_r0 + size,
+                  const_cast<char_ptr>(base() + s0_r0));
             }
 
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         std::string str() const override
+         std::string str() const exprtk_override
          {
             return str0_base_ptr_->str();
          }
 
-         char_cptr base() const override
+         char_cptr base() const exprtk_override
          {
             return str0_base_ptr_->base();
          }
 
-         std::size_t size() const override
+         std::size_t size() const exprtk_override
          {
             return str0_base_ptr_->size();
          }
 
-         range_t& range_ref() override
+         range_t& range_ref() exprtk_override
          {
             return str0_rng_node_ptr_->range_ref();
          }
 
-         const range_t& range_ref() const override
+         const range_t& range_ref() const exprtk_override
          {
             return str0_rng_node_ptr_->range_ref();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_strass;
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return initialised_ && binary_node<T>::valid();
+         }
+
       private:
 
          bool             initialised_;
@@ -9229,31 +11263,32 @@ namespace exprtk
       };
 
       template <typename T>
-      class conditional_string_node : public trinary_node    <T>,
-                                      public string_base_node<T>,
-                                      public range_interface <T>
+      class conditional_string_node exprtk_final
+                                    : public trinary_node    <T>
+                                    , public string_base_node<T>
+                                    , public range_interface <T>
       {
       public:
 
+         typedef typename range_interface<T>::range_t range_t;
+         typedef range_t*             range_ptr;
+         typedef range_interface <T>  irange_t;
+         typedef irange_t*            irange_ptr;
          typedef expression_node <T>* expression_ptr;
-         typedef string_base_node<T>*   str_base_ptr;
-         typedef range_pack      <T>         range_t;
-         typedef range_t*                  range_ptr;
-         typedef range_interface<T>         irange_t;
-         typedef irange_t*                irange_ptr;
+         typedef string_base_node<T>* str_base_ptr;
 
          conditional_string_node(expression_ptr condition,
                                  expression_ptr consequent,
                                  expression_ptr alternative)
-         : trinary_node<T>(details::e_default,consequent,alternative,condition),
-           initialised_(false),
-           str0_base_ptr_ (0),
-           str1_base_ptr_ (0),
-           str0_range_ptr_(0),
-           str1_range_ptr_(0),
-           condition_    (condition),
-           consequent_  (consequent),
-           alternative_(alternative)
+         : trinary_node<T>(details::e_default, consequent, alternative, condition)
+         , initialised_(false)
+         , str0_base_ptr_ (0)
+         , str1_base_ptr_ (0)
+         , str0_range_ptr_(0)
+         , str1_range_ptr_(0)
+         , condition_  (condition  )
+         , consequent_ (consequent )
+         , alternative_(alternative)
          {
             range_.n0_c = std::make_pair<bool,std::size_t>(true,0);
             range_.n1_c = std::make_pair<bool,std::size_t>(true,0);
@@ -9292,90 +11327,93 @@ namespace exprtk
                            str0_range_ptr_ &&
                            str1_range_ptr_ ;
 
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (initialised_)
+            std::size_t r0 = 0;
+            std::size_t r1 = 0;
+
+            if (is_true(condition_))
             {
-               assert(condition_  );
-               assert(consequent_ );
-               assert(alternative_);
+               consequent_->value();
 
-               std::size_t r0 = 0;
-               std::size_t r1 = 0;
+               const range_t& range = str0_range_ptr_->range_ref();
 
-               if (is_true(condition_))
+               if (range(r0, r1, str0_base_ptr_->size()))
                {
-                  consequent_->value();
-
-                  const range_t& range = str0_range_ptr_->range_ref();
+                  const std::size_t size = (r1 - r0);
 
-                  if (range(r0, r1, str0_base_ptr_->size()))
-                  {
-                     const std::size_t size = (r1 - r0) + 1;
+                  value_.assign(str0_base_ptr_->base() + r0, size);
 
-                     value_.assign(str0_base_ptr_->base() + r0, size);
-
-                     range_.n1_c.second  = value_.size() - 1;
-                     range_.cache.second = range_.n1_c.second;
+                  range_.n1_c.second  = value_.size();
+                  range_.cache.second = range_.n1_c.second;
 
-                     return T(1);
-                  }
+                  return T(1);
                }
-               else
-               {
-                  alternative_->value();
+            }
+            else
+            {
+               alternative_->value();
 
-                  const range_t& range = str1_range_ptr_->range_ref();
+               const range_t& range = str1_range_ptr_->range_ref();
 
-                  if (range(r0, r1, str1_base_ptr_->size()))
-                  {
-                     const std::size_t size = (r1 - r0) + 1;
+               if (range(r0, r1, str1_base_ptr_->size()))
+               {
+                  const std::size_t size = (r1 - r0);
 
-                     value_.assign(str1_base_ptr_->base() + r0, size);
+                  value_.assign(str1_base_ptr_->base() + r0, size);
 
-                     range_.n1_c.second  = value_.size() - 1;
-                     range_.cache.second = range_.n1_c.second;
+                  range_.n1_c.second  = value_.size();
+                  range_.cache.second = range_.n1_c.second;
 
-                     return T(0);
-                  }
+                  return T(0);
                }
             }
 
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         std::string str() const override
+         std::string str() const exprtk_override
          {
             return value_;
          }
 
-         char_cptr base() const override
+         char_cptr base() const exprtk_override
          {
             return &value_[0];
          }
 
-         std::size_t size() const override
+         std::size_t size() const exprtk_override
          {
             return value_.size();
          }
 
-         range_t& range_ref() override
+         range_t& range_ref() exprtk_override
          {
             return range_;
          }
 
-         const range_t& range_ref() const override
+         const range_t& range_ref() const exprtk_override
          {
             return range_;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_strcondition;
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return
+               initialised_                         &&
+               condition_  && condition_  ->valid() &&
+               consequent_ && consequent_ ->valid() &&
+               alternative_&& alternative_->valid() ;
+         }
+
       private:
 
          bool initialised_;
@@ -9392,27 +11430,30 @@ namespace exprtk
       };
 
       template <typename T>
-      class cons_conditional_str_node : public binary_node     <T>,
-                                        public string_base_node<T>,
-                                        public range_interface <T>
+      class cons_conditional_str_node exprtk_final
+                                      : public binary_node     <T>
+                                      , public string_base_node<T>
+                                      , public range_interface <T>
       {
       public:
 
+         typedef typename range_interface<T>::range_t range_t;
+         typedef range_t*             range_ptr;
+         typedef range_interface <T>  irange_t;
+         typedef irange_t*            irange_ptr;
          typedef expression_node <T>* expression_ptr;
-         typedef string_base_node<T>*   str_base_ptr;
-         typedef range_pack      <T>         range_t;
-         typedef range_t*                  range_ptr;
-         typedef range_interface<T>         irange_t;
-         typedef irange_t*                irange_ptr;
+         typedef string_base_node<T>* str_base_ptr;
+
+         using binary_node<T>::branch;
 
          cons_conditional_str_node(expression_ptr condition,
                                    expression_ptr consequent)
-         : binary_node<T>(details::e_default, consequent, condition),
-           initialised_(false),
-           str0_base_ptr_ (0),
-           str0_range_ptr_(0),
-           condition_ (condition),
-           consequent_(consequent)
+         : binary_node<T>(details::e_default, consequent, condition)
+         , initialised_(false)
+         , str0_base_ptr_ (0)
+         , str0_range_ptr_(0)
+         , condition_ (condition )
+         , consequent_(consequent)
          {
             range_.n0_c = std::make_pair<bool,std::size_t>(true,0);
             range_.n1_c = std::make_pair<bool,std::size_t>(true,0);
@@ -9420,85 +11461,88 @@ namespace exprtk
             range_.cache.first  = range_.n0_c.second;
             range_.cache.second = range_.n1_c.second;
 
-            if (is_generally_string_node(binary_node<T>::branch_[0].first))
+            if (is_generally_string_node(branch(0)))
             {
-               str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
+               str0_base_ptr_ = dynamic_cast<str_base_ptr>(branch(0));
 
                if (0 == str0_base_ptr_)
                   return;
 
-               str0_range_ptr_ = dynamic_cast<irange_ptr>(binary_node<T>::branch_[0].first);
+               str0_range_ptr_ = dynamic_cast<irange_ptr>(branch(0));
 
                if (0 == str0_range_ptr_)
                   return;
             }
 
             initialised_ = str0_base_ptr_ && str0_range_ptr_ ;
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (initialised_)
+            if (is_true(condition_))
             {
-               assert(condition_ );
-               assert(consequent_);
+               consequent_->value();
 
-               if (is_true(condition_))
-               {
-                  consequent_->value();
-
-                  const range_t& range = str0_range_ptr_->range_ref();
+               const range_t& range = str0_range_ptr_->range_ref();
 
-                  std::size_t r0 = 0;
-                  std::size_t r1 = 0;
+               std::size_t r0 = 0;
+               std::size_t r1 = 0;
 
-                  if (range(r0, r1, str0_base_ptr_->size()))
-                  {
-                     const std::size_t size = (r1 - r0) + 1;
+               if (range(r0, r1, str0_base_ptr_->size()))
+               {
+                  const std::size_t size = (r1 - r0);
 
-                     value_.assign(str0_base_ptr_->base() + r0, size);
+                  value_.assign(str0_base_ptr_->base() + r0, size);
 
-                     range_.n1_c.second  = value_.size() - 1;
-                     range_.cache.second = range_.n1_c.second;
+                  range_.n1_c.second  = value_.size();
+                  range_.cache.second = range_.n1_c.second;
 
-                     return T(1);
-                  }
+                  return T(1);
                }
             }
 
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         std::string str() const override
+         std::string str() const exprtk_override
          {
             return value_;
          }
 
-         char_cptr base() const override
+         char_cptr base() const exprtk_override
          {
             return &value_[0];
          }
 
-         std::size_t size() const override
+         std::size_t size() const exprtk_override
          {
             return value_.size();
          }
 
-         range_t& range_ref() override
+         range_t& range_ref() exprtk_override
          {
             return range_;
          }
 
-         const range_t& range_ref() const override
+         const range_t& range_ref() const exprtk_override
          {
             return range_;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_strccondition;
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return
+               initialised_                         &&
+               condition_  && condition_  ->valid() &&
+               consequent_ && consequent_ ->valid() ;
+         }
+
       private:
 
          bool initialised_;
@@ -9512,26 +11556,27 @@ namespace exprtk
       };
 
       template <typename T, typename VarArgFunction>
-      class str_vararg_node  : public expression_node <T>,
-                               public string_base_node<T>,
-                               public range_interface <T>
+      class str_vararg_node exprtk_final
+                            : public expression_node <T>
+                            , public string_base_node<T>
+                            , public range_interface <T>
       {
       public:
 
-         typedef expression_node <T>*     expression_ptr;
-         typedef string_base_node<T>*       str_base_ptr;
-         typedef range_pack      <T>             range_t;
-         typedef range_t*                      range_ptr;
-         typedef range_interface<T>             irange_t;
-         typedef irange_t*                    irange_ptr;
+         typedef typename range_interface<T>::range_t range_t;
+         typedef range_t*             range_ptr;
+         typedef range_interface <T>  irange_t;
+         typedef irange_t*            irange_ptr;
+         typedef expression_node <T>* expression_ptr;
+         typedef string_base_node<T>* str_base_ptr;
          typedef std::pair<expression_ptr,bool> branch_t;
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
          explicit str_vararg_node(const Sequence<expression_ptr,Allocator>& arg_list)
-         : initialised_(false),
-           str_base_ptr_ (0),
-           str_range_ptr_(0)
+         : initialised_(false)
+         , str_base_ptr_ (0)
+         , str_range_ptr_(0)
          {
             construct_branch_pair(final_node_, const_cast<expression_ptr>(arg_list.back()));
 
@@ -9550,8 +11595,6 @@ namespace exprtk
             if (0 == str_range_ptr_)
                return;
 
-            initialised_ = str_base_ptr_  && str_range_ptr_;
-
             if (arg_list.size() > 1)
             {
                const std::size_t arg_list_size = arg_list.size() - 1;
@@ -9560,7 +11603,7 @@ namespace exprtk
 
                for (std::size_t i = 0; i < arg_list_size; ++i)
                {
-                  if (arg_list[i])
+                  if (arg_list[i] && arg_list[i]->valid())
                   {
                      construct_branch_pair(arg_list_[i], arg_list[i]);
                   }
@@ -9570,10 +11613,15 @@ namespace exprtk
                      return;
                   }
                }
+
+               initialised_ = true;
             }
+
+            initialised_ &= str_base_ptr_ && str_range_ptr_;
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             if (!arg_list_.empty())
             {
@@ -9585,43 +11633,50 @@ namespace exprtk
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         std::string str() const override
+         std::string str() const exprtk_override
          {
             return str_base_ptr_->str();
          }
 
-         char_cptr base() const override
+         char_cptr base() const exprtk_override
          {
             return str_base_ptr_->base();
          }
 
-         std::size_t size() const override
+         std::size_t size() const exprtk_override
          {
             return str_base_ptr_->size();
          }
 
-         range_t& range_ref() override
+         range_t& range_ref() exprtk_override
          {
             return str_range_ptr_->range_ref();
          }
 
-         const range_t& range_ref() const override
+         const range_t& range_ref() const exprtk_override
          {
             return str_range_ptr_->range_ref();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_stringvararg;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
+         {
+            return
+               initialised_ &&
+               final_node_.first && final_node_.first->valid();
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            expression_node<T>::ndb_t::collect(final_node_, node_delete_list);
-            expression_node<T>::ndb_t::collect(arg_list_  , node_delete_list);
+            expression_node<T>::ndb_t::collect(final_node_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(arg_list_   , node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return std::max(
                expression_node<T>::ndb_t::compute_node_depth(final_node_),
@@ -9638,15 +11693,113 @@ namespace exprtk
       };
       #endif
 
+      template <typename T>
+      class assert_node exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+         typedef string_base_node<T>* str_base_ptr;
+         typedef assert_check::assert_context assert_context_t;
+
+         assert_node(expression_ptr   assert_condition_node,
+                     expression_ptr   assert_message_node,
+                     assert_check_ptr assert_check,
+                     assert_context_t context)
+         : assert_message_str_base_(0)
+         , assert_check_(assert_check)
+         , context_(context)
+         {
+            construct_branch_pair(assert_condition_node_, assert_condition_node);
+            construct_branch_pair(assert_message_node_  , assert_message_node  );
+
+            #ifndef exprtk_disable_string_capabilities
+            if (
+                  assert_message_node_.first &&
+                  details::is_generally_string_node(assert_message_node_.first)
+               )
+            {
+               assert_message_str_base_ = dynamic_cast<str_base_ptr>(assert_message_node_.first);
+            }
+            #endif
+
+            assert(valid());
+         }
+
+         inline T value() const exprtk_override
+         {
+            if (details::is_true(assert_condition_node_.first->value()))
+            {
+               return T(1);
+            }
+
+            #ifndef exprtk_disable_string_capabilities
+            if (assert_message_node_.first)
+            {
+               assert_message_node_.first->value();
+               assert(assert_message_str_base_);
+               context_.message = assert_message_str_base_->str();
+            }
+            #endif
+
+            assert_check_->handle_assert(context_);
+            return T(0);
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_assert;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return (
+                     assert_check_ &&
+                     assert_condition_node_.first &&
+                     assert_condition_node_.first->valid()
+                   ) &&
+                   (
+                     (0 == assert_message_node_.first) ||
+                     (
+                       assert_message_node_.first &&
+                       assert_message_str_base_   &&
+                       assert_message_node_.first->valid() &&
+                       details::is_generally_string_node(assert_message_node_.first)
+                     )
+                   );
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(assert_condition_node_, node_delete_list);
+            expression_node<T>::ndb_t::collect(assert_message_node_  , node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::compute_node_depth
+               (assert_condition_node_, assert_message_node_);
+         }
+
+      private:
+
+         branch_t         assert_condition_node_;
+         branch_t         assert_message_node_;
+         str_base_ptr     assert_message_str_base_;
+         assert_check_ptr assert_check_;
+         mutable assert_context_t context_;
+      };
+
       template <typename T, std::size_t N>
-      inline T axn(T a, T x)
+      inline T axn(const T a, const T x)
       {
          // a*x^n
          return a * exprtk::details::numeric::fast_exp<T,N>::result(x);
       }
 
       template <typename T, std::size_t N>
-      inline T axnb(T a, T x, T b)
+      inline T axnb(const T a, const T x, const T b)
       {
          // a*x^n+b
          return a * exprtk::details::numeric::fast_exp<T,N>::result(x) + b;
@@ -9658,12 +11811,12 @@ namespace exprtk
          typedef typename details::functor_t<T>::Type Type;
          typedef typename details::functor_t<T> functor_t;
          typedef typename functor_t::qfunc_t quaternary_functor_t;
-         typedef typename functor_t::tfunc_t    trinary_functor_t;
-         typedef typename functor_t::bfunc_t     binary_functor_t;
-         typedef typename functor_t::ufunc_t      unary_functor_t;
+         typedef typename functor_t::tfunc_t trinary_functor_t;
+         typedef typename functor_t::bfunc_t binary_functor_t;
+         typedef typename functor_t::ufunc_t unary_functor_t;
       };
 
-      #define define_sfop3(NN,OP0,OP1)                   \
+      #define define_sfop3(NN, OP0, OP1)                 \
       template <typename T>                              \
       struct sf##NN##_op : public sf_base<T>             \
       {                                                  \
@@ -9727,7 +11880,7 @@ namespace exprtk
       define_sfop3(46,x * numeric::cos(y) - z  ,"")
       define_sfop3(47,details::is_true(x) ? y : z,"")
 
-      #define define_sfop4(NN,OP0,OP1)                           \
+      #define define_sfop4(NN, OP0, OP1)                         \
       template <typename T>                                      \
       struct sf##NN##_op : public sf_base<T>                     \
       {                                                          \
@@ -9863,7 +12016,7 @@ namespace exprtk
       #undef define_sfop4
 
       template <typename T, typename SpecialFunction>
-      class sf3_node : public trinary_node<T>
+      class sf3_node exprtk_final : public trinary_node<T>
       {
       public:
 
@@ -9876,12 +12029,8 @@ namespace exprtk
          : trinary_node<T>(opr, branch0, branch1, branch2)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(trinary_node<T>::branch_[0].first);
-            assert(trinary_node<T>::branch_[1].first);
-            assert(trinary_node<T>::branch_[2].first);
-
             const T x = trinary_node<T>::branch_[0].first->value();
             const T y = trinary_node<T>::branch_[1].first->value();
             const T z = trinary_node<T>::branch_[2].first->value();
@@ -9891,7 +12040,7 @@ namespace exprtk
       };
 
       template <typename T, typename SpecialFunction>
-      class sf4_node : public quaternary_node<T>
+      class sf4_node exprtk_final : public quaternary_node<T>
       {
       public:
 
@@ -9905,13 +12054,8 @@ namespace exprtk
          : quaternary_node<T>(opr, branch0, branch1, branch2, branch3)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(quaternary_node<T>::branch_[0].first);
-            assert(quaternary_node<T>::branch_[1].first);
-            assert(quaternary_node<T>::branch_[2].first);
-            assert(quaternary_node<T>::branch_[3].first);
-
             const T x = quaternary_node<T>::branch_[0].first->value();
             const T y = quaternary_node<T>::branch_[1].first->value();
             const T z = quaternary_node<T>::branch_[2].first->value();
@@ -9922,32 +12066,32 @@ namespace exprtk
       };
 
       template <typename T, typename SpecialFunction>
-      class sf3_var_node : public expression_node<T>
+      class sf3_var_node exprtk_final : public expression_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
 
          sf3_var_node(const T& v0, const T& v1, const T& v2)
-         : v0_(v0),
-           v1_(v1),
-           v2_(v2)
+         : v0_(v0)
+         , v1_(v1)
+         , v2_(v2)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return SpecialFunction::process(v0_, v1_, v2_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_trinary;
          }
 
       private:
 
-         sf3_var_node(sf3_var_node<T,SpecialFunction>&);
-         sf3_var_node<T,SpecialFunction>& operator=(sf3_var_node<T,SpecialFunction>&);
+         sf3_var_node(const sf3_var_node<T,SpecialFunction>&) exprtk_delete;
+         sf3_var_node<T,SpecialFunction>& operator=(const sf3_var_node<T,SpecialFunction>&) exprtk_delete;
 
          const T& v0_;
          const T& v1_;
@@ -9955,33 +12099,33 @@ namespace exprtk
       };
 
       template <typename T, typename SpecialFunction>
-      class sf4_var_node : public expression_node<T>
+      class sf4_var_node exprtk_final : public expression_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
 
          sf4_var_node(const T& v0, const T& v1, const T& v2, const T& v3)
-         : v0_(v0),
-           v1_(v1),
-           v2_(v2),
-           v3_(v3)
+         : v0_(v0)
+         , v1_(v1)
+         , v2_(v2)
+         , v3_(v3)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return SpecialFunction::process(v0_, v1_, v2_, v3_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_trinary;
          }
 
       private:
 
-         sf4_var_node(sf4_var_node<T,SpecialFunction>&);
-         sf4_var_node<T,SpecialFunction>& operator=(sf4_var_node<T,SpecialFunction>&);
+         sf4_var_node(const sf4_var_node<T,SpecialFunction>&) exprtk_delete;
+         sf4_var_node<T,SpecialFunction>& operator=(const sf4_var_node<T,SpecialFunction>&) exprtk_delete;
 
          const T& v0_;
          const T& v1_;
@@ -9990,7 +12134,7 @@ namespace exprtk
       };
 
       template <typename T, typename VarArgFunction>
-      class vararg_node : public expression_node<T>
+      class vararg_node exprtk_final : public expression_node<T>
       {
       public:
 
@@ -10000,12 +12144,13 @@ namespace exprtk
          template <typename Allocator,
                    template <typename, typename> class Sequence>
          explicit vararg_node(const Sequence<expression_ptr,Allocator>& arg_list)
+         : initialised_(false)
          {
             arg_list_.resize(arg_list.size());
 
             for (std::size_t i = 0; i < arg_list.size(); ++i)
             {
-               if (arg_list[i])
+               if (arg_list[i] && arg_list[i]->valid())
                {
                   construct_branch_pair(arg_list_[i],arg_list[i]);
                }
@@ -10015,35 +12160,54 @@ namespace exprtk
                   return;
                }
             }
+
+            initialised_ = (arg_list_.size() == arg_list.size());
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return VarArgFunction::process(arg_list_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_vararg;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
+         {
+            return initialised_;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
             expression_node<T>::ndb_t::collect(arg_list_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(arg_list_);
          }
 
+         std::size_t size() const
+         {
+            return arg_list_.size();
+         }
+
+         expression_ptr operator[](const std::size_t& index) const
+         {
+            return arg_list_[index].first;
+         }
+
       private:
 
          std::vector<branch_t> arg_list_;
+         bool initialised_;
       };
 
       template <typename T, typename VarArgFunction>
-      class vararg_varnode : public expression_node<T>
+      class vararg_varnode exprtk_final : public expression_node<T>
       {
       public:
 
@@ -10052,12 +12216,13 @@ namespace exprtk
          template <typename Allocator,
                    template <typename, typename> class Sequence>
          explicit vararg_varnode(const Sequence<expression_ptr,Allocator>& arg_list)
+         : initialised_(false)
          {
             arg_list_.resize(arg_list.size());
 
             for (std::size_t i = 0; i < arg_list.size(); ++i)
             {
-               if (arg_list[i] && is_variable_node(arg_list[i]))
+               if (arg_list[i] && arg_list[i]->valid() && is_variable_node(arg_list[i]))
                {
                   variable_node<T>* var_node_ptr = static_cast<variable_node<T>*>(arg_list[i]);
                   arg_list_[i] = (&var_node_ptr->ref());
@@ -10068,28 +12233,34 @@ namespace exprtk
                   return;
                }
             }
+
+            initialised_ = (arg_list.size() == arg_list_.size());
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (!arg_list_.empty())
-               return VarArgFunction::process(arg_list_);
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+            return VarArgFunction::process(arg_list_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_vararg;
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return initialised_;
+         }
+
       private:
 
          std::vector<const T*> arg_list_;
+         bool initialised_;
       };
 
       template <typename T, typename VecFunction>
-      class vectorize_node : public expression_node<T>
+      class vectorize_node exprtk_final : public expression_node<T>
       {
       public:
 
@@ -10105,35 +12276,30 @@ namespace exprtk
             {
                ivec_ptr_ = dynamic_cast<vector_interface<T>*>(v_.first);
             }
-            else
-               ivec_ptr_ = 0;
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (ivec_ptr_)
-            {
-               assert(v_.first);
-
-               v_.first->value();
-
-               return VecFunction::process(ivec_ptr_);
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+            v_.first->value();
+            return VecFunction::process(ivec_ptr_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_vecfunc;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
+         {
+            return ivec_ptr_ && v_.first && v_.first->valid();
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
             expression_node<T>::ndb_t::collect(v_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(v_);
          }
@@ -10145,38 +12311,36 @@ namespace exprtk
       };
 
       template <typename T>
-      class assignment_node : public binary_node<T>
+      class assignment_node exprtk_final : public binary_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
 
          assignment_node(const operator_type& opr,
                          expression_ptr branch0,
                          expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           var_node_ptr_(0)
+         : binary_node<T>(opr, branch0, branch1)
+         , var_node_ptr_(0)
          {
-            if (is_variable_node(binary_node<T>::branch_[0].first))
+            if (is_variable_node(branch(0)))
             {
-               var_node_ptr_ = static_cast<variable_node<T>*>(binary_node<T>::branch_[0].first);
+               var_node_ptr_ = static_cast<variable_node<T>*>(branch(0));
             }
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (var_node_ptr_)
-            {
-               assert(binary_node<T>::branch_[1].first);
-
-               T& result = var_node_ptr_->ref();
+            T& result = var_node_ptr_->ref();
+               result = branch(1)->value();
 
-               result = binary_node<T>::branch_[1].first->value();
+            return result;
+         }
 
-               return result;
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+         inline bool valid() const exprtk_override
+         {
+            return var_node_ptr_ && binary_node<T>::valid();
          }
 
       private:
@@ -10185,118 +12349,198 @@ namespace exprtk
       };
 
       template <typename T>
-      class assignment_vec_elem_node : public binary_node<T>
+      class assignment_vec_elem_node exprtk_final : public binary_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
 
          assignment_vec_elem_node(const operator_type& opr,
                                   expression_ptr branch0,
                                   expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           vec_node_ptr_(0)
+         : binary_node<T>(opr, branch0, branch1)
+         , vec_node_ptr_(0)
          {
-            if (is_vector_elem_node(binary_node<T>::branch_[0].first))
+            if (is_vector_elem_node(branch(0)))
             {
-               vec_node_ptr_ = static_cast<vector_elem_node<T>*>(binary_node<T>::branch_[0].first);
+               vec_node_ptr_ = static_cast<vector_elem_node<T>*>(branch(0));
             }
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (vec_node_ptr_)
-            {
-               assert(binary_node<T>::branch_[1].first);
+            T& result = vec_node_ptr_->ref();
+               result = branch(1)->value();
 
-               T& result = vec_node_ptr_->ref();
+            return result;
+         }
 
-               result = binary_node<T>::branch_[1].first->value();
+         inline bool valid() const exprtk_override
+         {
+            return vec_node_ptr_ && binary_node<T>::valid();
+         }
 
-               return result;
+      private:
+
+         vector_elem_node<T>* vec_node_ptr_;
+      };
+
+      template <typename T>
+      class assignment_vec_elem_rtc_node exprtk_final : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
+
+         assignment_vec_elem_rtc_node(const operator_type& opr,
+                                      expression_ptr branch0,
+                                      expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1)
+         , vec_node_ptr_(0)
+         {
+            if (is_vector_elem_rtc_node(branch(0)))
+            {
+               vec_node_ptr_ = static_cast<vector_elem_rtc_node<T>*>(branch(0));
             }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+
+            assert(valid());
+         }
+
+         inline T value() const exprtk_override
+         {
+            T& result = vec_node_ptr_->ref();
+               result = branch(1)->value();
+
+            return result;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return vec_node_ptr_ && binary_node<T>::valid();
          }
 
       private:
 
-         vector_elem_node<T>* vec_node_ptr_;
+         vector_elem_rtc_node<T>* vec_node_ptr_;
       };
 
       template <typename T>
-      class assignment_rebasevec_elem_node : public binary_node<T>
+      class assignment_rebasevec_elem_node exprtk_final : public binary_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         using expression_node<T>::branch;
 
          assignment_rebasevec_elem_node(const operator_type& opr,
                                         expression_ptr branch0,
                                         expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           rbvec_node_ptr_(0)
+         : binary_node<T>(opr, branch0, branch1)
+         , rbvec_node_ptr_(0)
          {
-            if (is_rebasevector_elem_node(binary_node<T>::branch_[0].first))
+            if (is_rebasevector_elem_node(branch(0)))
             {
-               rbvec_node_ptr_ = static_cast<rebasevector_elem_node<T>*>(binary_node<T>::branch_[0].first);
+               rbvec_node_ptr_ = static_cast<rebasevector_elem_node<T>*>(branch(0));
             }
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (rbvec_node_ptr_)
-            {
-               assert(binary_node<T>::branch_[1].first);
+            T& result = rbvec_node_ptr_->ref();
+               result = branch(1)->value();
 
-               T& result = rbvec_node_ptr_->ref();
+            return result;
+         }
 
-               result = binary_node<T>::branch_[1].first->value();
+         inline bool valid() const exprtk_override
+         {
+            return rbvec_node_ptr_ && binary_node<T>::valid();
+         }
 
-               return result;
+      private:
+
+         rebasevector_elem_node<T>* rbvec_node_ptr_;
+      };
+
+      template <typename T>
+      class assignment_rebasevec_elem_rtc_node exprtk_final : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         using expression_node<T>::branch;
+
+         assignment_rebasevec_elem_rtc_node(const operator_type& opr,
+                                            expression_ptr branch0,
+                                            expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1)
+         , rbvec_node_ptr_(0)
+         {
+            if (is_rebasevector_elem_rtc_node(branch(0)))
+            {
+               rbvec_node_ptr_ = static_cast<rebasevector_elem_rtc_node<T>*>(branch(0));
             }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+
+            assert(valid());
+         }
+
+         inline T value() const exprtk_override
+         {
+            T& result = rbvec_node_ptr_->ref();
+               result = branch(1)->value();
+
+            return result;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return rbvec_node_ptr_ && binary_node<T>::valid();
          }
 
       private:
 
-         rebasevector_elem_node<T>* rbvec_node_ptr_;
+         rebasevector_elem_rtc_node<T>* rbvec_node_ptr_;
       };
 
       template <typename T>
-      class assignment_rebasevec_celem_node : public binary_node<T>
+      class assignment_rebasevec_celem_node exprtk_final : public binary_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
 
          assignment_rebasevec_celem_node(const operator_type& opr,
                                          expression_ptr branch0,
                                          expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           rbvec_node_ptr_(0)
+         : binary_node<T>(opr, branch0, branch1)
+         , rbvec_node_ptr_(0)
          {
-            if (is_rebasevector_celem_node(binary_node<T>::branch_[0].first))
+            if (is_rebasevector_celem_node(branch(0)))
             {
-               rbvec_node_ptr_ = static_cast<rebasevector_celem_node<T>*>(binary_node<T>::branch_[0].first);
+               rbvec_node_ptr_ = static_cast<rebasevector_celem_node<T>*>(branch(0));
             }
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (rbvec_node_ptr_)
-            {
-               assert(binary_node<T>::branch_[1].first);
+            T& result = rbvec_node_ptr_->ref();
+               result = branch(1)->value();
 
-               T& result = rbvec_node_ptr_->ref();
-
-               result = binary_node<T>::branch_[1].first->value();
+            return result;
+         }
 
-               return result;
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+         inline bool valid() const exprtk_override
+         {
+            return rbvec_node_ptr_ && binary_node<T>::valid();
          }
 
       private:
@@ -10305,114 +12549,124 @@ namespace exprtk
       };
 
       template <typename T>
-      class assignment_vec_node : public binary_node     <T>,
-                                  public vector_interface<T>
+      class assignment_vec_node exprtk_final
+                                : public binary_node     <T>
+                                , public vector_interface<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
-         typedef vector_node<T>*    vector_node_ptr;
-         typedef vec_data_store<T>            vds_t;
+         typedef vector_node<T>*     vector_node_ptr;
+         typedef vec_data_store<T>   vds_t;
+
+         using binary_node<T>::branch;
 
          assignment_vec_node(const operator_type& opr,
                              expression_ptr branch0,
                              expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           vec_node_ptr_(0)
+         : binary_node<T>(opr, branch0, branch1)
+         , vec_node_ptr_(0)
          {
-            if (is_vector_node(binary_node<T>::branch_[0].first))
+            if (is_vector_node(branch(0)))
             {
-               vec_node_ptr_ = static_cast<vector_node<T>*>(binary_node<T>::branch_[0].first);
+               vec_node_ptr_ = static_cast<vector_node<T>*>(branch(0));
                vds()         = vec_node_ptr_->vds();
             }
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (vec_node_ptr_)
-            {
-               assert(binary_node<T>::branch_[1].first);
-
-               const T v = binary_node<T>::branch_[1].first->value();
+            const T v = branch(1)->value();
 
-               T* vec = vds().data();
+            T* vec = vds().data();
 
-               loop_unroll::details lud(size());
-               const T* upper_bound = vec + lud.upper_bound;
+            loop_unroll::details lud(size());
+            const T* upper_bound = vec + lud.upper_bound;
 
-               while (vec < upper_bound)
-               {
-                  #define exprtk_loop(N) \
-                  vec[N] = v;            \
+            while (vec < upper_bound)
+            {
+               #define exprtk_loop(N) \
+               vec[N] = v;            \
 
-                  exprtk_loop( 0) exprtk_loop( 1)
-                  exprtk_loop( 2) exprtk_loop( 3)
-                  #ifndef exprtk_disable_superscalar_unroll
-                  exprtk_loop( 4) exprtk_loop( 5)
-                  exprtk_loop( 6) exprtk_loop( 7)
-                  exprtk_loop( 8) exprtk_loop( 9)
-                  exprtk_loop(10) exprtk_loop(11)
-                  exprtk_loop(12) exprtk_loop(13)
-                  exprtk_loop(14) exprtk_loop(15)
-                  #endif
+               exprtk_loop( 0) exprtk_loop( 1)
+               exprtk_loop( 2) exprtk_loop( 3)
+               #ifndef exprtk_disable_superscalar_unroll
+               exprtk_loop( 4) exprtk_loop( 5)
+               exprtk_loop( 6) exprtk_loop( 7)
+               exprtk_loop( 8) exprtk_loop( 9)
+               exprtk_loop(10) exprtk_loop(11)
+               exprtk_loop(12) exprtk_loop(13)
+               exprtk_loop(14) exprtk_loop(15)
+               #endif
 
-                  vec += lud.batch_size;
-               }
+               vec += lud.batch_size;
+            }
 
-               exprtk_disable_fallthrough_begin
-               switch (lud.remainder)
-               {
-                  #define case_stmt(N) \
-                  case N : *vec++ = v; \
+            switch (lud.remainder)
+            {
+               #define case_stmt(N) \
+               case N : *vec++ = v; \
+               exprtk_fallthrough   \
 
-                  #ifndef exprtk_disable_superscalar_unroll
-                  case_stmt(15) case_stmt(14)
-                  case_stmt(13) case_stmt(12)
-                  case_stmt(11) case_stmt(10)
-                  case_stmt( 9) case_stmt( 8)
-                  case_stmt( 7) case_stmt( 6)
-                  case_stmt( 5) case_stmt( 4)
-                  #endif
-                  case_stmt( 3) case_stmt( 2)
-                  case_stmt( 1)
-               }
-               exprtk_disable_fallthrough_end
+               #ifndef exprtk_disable_superscalar_unroll
+               case_stmt(15) case_stmt(14)
+               case_stmt(13) case_stmt(12)
+               case_stmt(11) case_stmt(10)
+               case_stmt( 9) case_stmt( 8)
+               case_stmt( 7) case_stmt( 6)
+               case_stmt( 5) case_stmt( 4)
+               #endif
+               case_stmt( 3) case_stmt( 2)
+               case 1 : *vec++ = v;
+            }
 
-               #undef exprtk_loop
-               #undef case_stmt
+            #undef exprtk_loop
+            #undef case_stmt
 
-               return vec_node_ptr_->value();
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+            return vec_node_ptr_->value();
          }
 
-         vector_node_ptr vec() const override
+         vector_node_ptr vec() const exprtk_override
          {
             return vec_node_ptr_;
          }
 
-         vector_node_ptr vec() override
+         vector_node_ptr vec() exprtk_override
          {
             return vec_node_ptr_;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_vecvalass;
          }
 
-         std::size_t size() const override
+         inline bool valid() const exprtk_override
+         {
+            return
+               vec_node_ptr_ &&
+               (vds().size() <= vec_node_ptr_->vec_holder().base_size()) &&
+               binary_node<T>::valid();
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return vec_node_ptr_->vec_holder().size();
+         }
+
+         std::size_t base_size() const exprtk_override
          {
-            return vds().size();
+            return vec_node_ptr_->vec_holder().base_size();
          }
 
-         vds_t& vds() override
+         vds_t& vds() exprtk_override
          {
             return vds_;
          }
 
-         const vds_t& vds() const override
+         const vds_t& vds() const exprtk_override
          {
             return vds_;
          }
@@ -10424,40 +12678,43 @@ namespace exprtk
       };
 
       template <typename T>
-      class assignment_vecvec_node : public binary_node     <T>,
-                                     public vector_interface<T>
+      class assignment_vecvec_node exprtk_final
+                                   : public binary_node     <T>
+                                   , public vector_interface<T>
       {
       public:
 
-         typedef expression_node<T>*  expression_ptr;
+         typedef expression_node<T>* expression_ptr;
          typedef vector_node<T>*     vector_node_ptr;
-         typedef vec_data_store<T>             vds_t;
+         typedef vec_data_store<T>   vds_t;
+
+         using binary_node<T>::branch;
 
          assignment_vecvec_node(const operator_type& opr,
                                 expression_ptr branch0,
                                 expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           vec0_node_ptr_(0),
-           vec1_node_ptr_(0),
-           initialised_(false),
-           src_is_ivec_(false)
+         : binary_node<T>(opr, branch0, branch1)
+         , vec0_node_ptr_(0)
+         , vec1_node_ptr_(0)
+         , initialised_(false)
+         , src_is_ivec_(false)
          {
-            if (is_vector_node(binary_node<T>::branch_[0].first))
+            if (is_vector_node(branch(0)))
             {
-               vec0_node_ptr_ = static_cast<vector_node<T>*>(binary_node<T>::branch_[0].first);
+               vec0_node_ptr_ = static_cast<vector_node<T>*>(branch(0));
                vds()          = vec0_node_ptr_->vds();
             }
 
-            if (is_vector_node(binary_node<T>::branch_[1].first))
+            if (is_vector_node(branch(1)))
             {
-               vec1_node_ptr_ = static_cast<vector_node<T>*>(binary_node<T>::branch_[1].first);
+               vec1_node_ptr_ = static_cast<vector_node<T>*>(branch(1));
                vds_t::match_sizes(vds(),vec1_node_ptr_->vds());
             }
-            else if (is_ivector_node(binary_node<T>::branch_[1].first))
+            else if (is_ivector_node(branch(1)))
             {
                vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
 
-               if (0 != (vi = dynamic_cast<vector_interface<T>*>(binary_node<T>::branch_[1].first)))
+               if (0 != (vi = dynamic_cast<vector_interface<T>*>(branch(1))))
                {
                   vec1_node_ptr_ = vi->vec();
 
@@ -10471,384 +12728,570 @@ namespace exprtk
                }
             }
 
-            initialised_ = (vec0_node_ptr_ && vec1_node_ptr_);
+            initialised_ =
+               vec0_node_ptr_               &&
+               vec1_node_ptr_               &&
+               (size() <= base_size())      &&
+               (vds_.size() <= base_size()) &&
+               binary_node<T>::valid();
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (initialised_)
+            branch(1)->value();
+
+            if (src_is_ivec_)
+               return vec0_node_ptr_->value();
+
+            T* vec0 = vec0_node_ptr_->vds().data();
+            T* vec1 = vec1_node_ptr_->vds().data();
+
+            loop_unroll::details lud(size());
+            const T* upper_bound = vec0 + lud.upper_bound;
+
+            while (vec0 < upper_bound)
             {
-               assert(binary_node<T>::branch_[1].first);
+               #define exprtk_loop(N) \
+               vec0[N] = vec1[N];     \
 
-               binary_node<T>::branch_[1].first->value();
+               exprtk_loop( 0) exprtk_loop( 1)
+               exprtk_loop( 2) exprtk_loop( 3)
+               #ifndef exprtk_disable_superscalar_unroll
+               exprtk_loop( 4) exprtk_loop( 5)
+               exprtk_loop( 6) exprtk_loop( 7)
+               exprtk_loop( 8) exprtk_loop( 9)
+               exprtk_loop(10) exprtk_loop(11)
+               exprtk_loop(12) exprtk_loop(13)
+               exprtk_loop(14) exprtk_loop(15)
+               #endif
 
-               if (src_is_ivec_)
-                  return vec0_node_ptr_->value();
+               vec0 += lud.batch_size;
+               vec1 += lud.batch_size;
+            }
 
-               T* vec0 = vec0_node_ptr_->vds().data();
-               T* vec1 = vec1_node_ptr_->vds().data();
+            switch (lud.remainder)
+            {
+               #define case_stmt(N,fall_through) \
+               case N : *vec0++ = *vec1++;       \
+               fall_through                      \
 
-               loop_unroll::details lud(size());
-               const T* upper_bound = vec0 + lud.upper_bound;
+               #ifndef exprtk_disable_superscalar_unroll
+               case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough)
+               case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough)
+               case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough)
+               case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough)
+               case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough)
+               case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough)
+               #endif
+               case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough)
+               case_stmt( 1, (void)0;)
+            }
 
-               while (vec0 < upper_bound)
-               {
-                  #define exprtk_loop(N) \
-                  vec0[N] = vec1[N];     \
+            #undef exprtk_loop
+            #undef case_stmt
 
-                  exprtk_loop( 0) exprtk_loop( 1)
-                  exprtk_loop( 2) exprtk_loop( 3)
-                  #ifndef exprtk_disable_superscalar_unroll
-                  exprtk_loop( 4) exprtk_loop( 5)
-                  exprtk_loop( 6) exprtk_loop( 7)
-                  exprtk_loop( 8) exprtk_loop( 9)
-                  exprtk_loop(10) exprtk_loop(11)
-                  exprtk_loop(12) exprtk_loop(13)
-                  exprtk_loop(14) exprtk_loop(15)
-                  #endif
+            return vec0_node_ptr_->value();
+         }
 
-                  vec0 += lud.batch_size;
-                  vec1 += lud.batch_size;
-               }
+         vector_node_ptr vec() exprtk_override
+         {
+            return vec0_node_ptr_;
+         }
+
+         vector_node_ptr vec() const exprtk_override
+         {
+            return vec0_node_ptr_;
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_vecvecass;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return initialised_;
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return std::min(
+               vec0_node_ptr_->vec_holder().size(),
+               vec1_node_ptr_->vec_holder().size());
+         }
+
+         std::size_t base_size() const exprtk_override
+         {
+            return std::min(
+               vec0_node_ptr_->vec_holder().base_size(),
+               vec1_node_ptr_->vec_holder().base_size());
+         }
+
+         vds_t& vds() exprtk_override
+         {
+            return vds_;
+         }
+
+         const vds_t& vds() const exprtk_override
+         {
+            return vds_;
+         }
+
+      private:
+
+         vector_node<T>* vec0_node_ptr_;
+         vector_node<T>* vec1_node_ptr_;
+         bool            initialised_;
+         bool            src_is_ivec_;
+         vds_t           vds_;
+      };
+
+      template <typename T, typename Operation>
+      class assignment_op_node exprtk_final : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
+
+         assignment_op_node(const operator_type& opr,
+                            expression_ptr branch0,
+                            expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1)
+         , var_node_ptr_(0)
+         {
+            if (is_variable_node(branch(0)))
+            {
+               var_node_ptr_ = static_cast<variable_node<T>*>(branch(0));
+            }
+
+            assert(valid());
+         }
+
+         inline T value() const exprtk_override
+         {
+            T& v = var_node_ptr_->ref();
+            v = Operation::process(v,branch(1)->value());
+
+            return v;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return var_node_ptr_ && binary_node<T>::valid();
+         }
+
+      private:
+
+         variable_node<T>* var_node_ptr_;
+      };
+
+      template <typename T, typename Operation>
+      class assignment_vec_elem_op_node exprtk_final : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
+
+         assignment_vec_elem_op_node(const operator_type& opr,
+                                     expression_ptr branch0,
+                                     expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1)
+         , vec_node_ptr_(0)
+         {
+            if (is_vector_elem_node(branch(0)))
+            {
+               vec_node_ptr_ = static_cast<vector_elem_node<T>*>(branch(0));
+            }
+
+            assert(valid());
+         }
+
+         inline T value() const exprtk_override
+         {
+            T& v = vec_node_ptr_->ref();
+               v = Operation::process(v,branch(1)->value());
+
+            return v;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return vec_node_ptr_ && binary_node<T>::valid();
+         }
+
+      private:
 
-               exprtk_disable_fallthrough_begin
-               switch (lud.remainder)
-               {
-                  #define case_stmt(N)        \
-                  case N : *vec0++ = *vec1++; \
+         vector_elem_node<T>* vec_node_ptr_;
+      };
 
-                  #ifndef exprtk_disable_superscalar_unroll
-                  case_stmt(15) case_stmt(14)
-                  case_stmt(13) case_stmt(12)
-                  case_stmt(11) case_stmt(10)
-                  case_stmt( 9) case_stmt( 8)
-                  case_stmt( 7) case_stmt( 6)
-                  case_stmt( 5) case_stmt( 4)
-                  #endif
-                  case_stmt( 3) case_stmt( 2)
-                  case_stmt( 1)
-               }
-               exprtk_disable_fallthrough_end
+      template <typename T, typename Operation>
+      class assignment_vec_elem_op_rtc_node exprtk_final : public binary_node<T>
+      {
+      public:
 
-               #undef exprtk_loop
-               #undef case_stmt
+         typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
 
-               return vec0_node_ptr_->value();
+         assignment_vec_elem_op_rtc_node(const operator_type& opr,
+                                         expression_ptr branch0,
+                                         expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1)
+         , vec_node_ptr_(0)
+         {
+            if (is_vector_elem_rtc_node(branch(0)))
+            {
+               vec_node_ptr_ = static_cast<vector_elem_rtc_node<T>*>(branch(0));
             }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
-         }
 
-         vector_node_ptr vec() const override
-         {
-            return vec0_node_ptr_;
+            assert(valid());
          }
 
-         vector_node_ptr vec() override
+         inline T value() const exprtk_override
          {
-            return vec0_node_ptr_;
+            T& v = vec_node_ptr_->ref();
+               v = Operation::process(v,branch(1)->value());
+
+            return v;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline bool valid() const exprtk_override
          {
-            return expression_node<T>::e_vecvecass;
+            return vec_node_ptr_ && binary_node<T>::valid();
          }
 
-         std::size_t size() const override
+      private:
+
+         vector_elem_rtc_node<T>* vec_node_ptr_;
+      };
+
+      template <typename T, typename Operation>
+      class assignment_vec_celem_op_rtc_node exprtk_final : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
+
+         assignment_vec_celem_op_rtc_node(const operator_type& opr,
+                                          expression_ptr branch0,
+                                          expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1)
+         , vec_node_ptr_(0)
          {
-            return vds().size();
+            if (is_vector_celem_rtc_node(branch(0)))
+            {
+               vec_node_ptr_ = static_cast<vector_celem_rtc_node<T>*>(branch(0));
+            }
+
+            assert(valid());
          }
 
-         vds_t& vds() override
+         inline T value() const exprtk_override
          {
-            return vds_;
+            T& v = vec_node_ptr_->ref();
+               v = Operation::process(v,branch(1)->value());
+
+            return v;
          }
 
-         const vds_t& vds() const override
+         inline bool valid() const exprtk_override
          {
-            return vds_;
+            return vec_node_ptr_ && binary_node<T>::valid();
          }
 
       private:
 
-         vector_node<T>* vec0_node_ptr_;
-         vector_node<T>* vec1_node_ptr_;
-         bool            initialised_;
-         bool            src_is_ivec_;
-         vds_t           vds_;
+         vector_celem_rtc_node<T>* vec_node_ptr_;
       };
 
       template <typename T, typename Operation>
-      class assignment_op_node : public binary_node<T>
+      class assignment_rebasevec_elem_op_node exprtk_final : public binary_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
 
-         assignment_op_node(const operator_type& opr,
-                            expression_ptr branch0,
-                            expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           var_node_ptr_(0)
+         assignment_rebasevec_elem_op_node(const operator_type& opr,
+                                           expression_ptr branch0,
+                                           expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1)
+         , rbvec_node_ptr_(0)
          {
-            if (is_variable_node(binary_node<T>::branch_[0].first))
+            if (is_rebasevector_elem_node(branch(0)))
             {
-               var_node_ptr_ = static_cast<variable_node<T>*>(binary_node<T>::branch_[0].first);
+               rbvec_node_ptr_ = static_cast<rebasevector_elem_node<T>*>(branch(0));
             }
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (var_node_ptr_)
-            {
-               assert(binary_node<T>::branch_[1].first);
+            T& v = rbvec_node_ptr_->ref();
+               v = Operation::process(v,branch(1)->value());
 
-               T& v = var_node_ptr_->ref();
-               v = Operation::process(v,binary_node<T>::branch_[1].first->value());
+            return v;
+         }
 
-               return v;
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+         inline bool valid() const exprtk_override
+         {
+            return rbvec_node_ptr_ && binary_node<T>::valid();
          }
 
       private:
 
-         variable_node<T>* var_node_ptr_;
+         rebasevector_elem_node<T>* rbvec_node_ptr_;
       };
 
       template <typename T, typename Operation>
-      class assignment_vec_elem_op_node : public binary_node<T>
+      class assignment_rebasevec_celem_op_node exprtk_final : public binary_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
 
-         assignment_vec_elem_op_node(const operator_type& opr,
-                                     expression_ptr branch0,
-                                     expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           vec_node_ptr_(0)
+         assignment_rebasevec_celem_op_node(const operator_type& opr,
+                                            expression_ptr branch0,
+                                            expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1)
+         , rbvec_node_ptr_(0)
          {
-            if (is_vector_elem_node(binary_node<T>::branch_[0].first))
+            if (is_rebasevector_celem_node(branch(0)))
             {
-               vec_node_ptr_ = static_cast<vector_elem_node<T>*>(binary_node<T>::branch_[0].first);
+               rbvec_node_ptr_ = static_cast<rebasevector_celem_node<T>*>(branch(0));
             }
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (vec_node_ptr_)
-            {
-               assert(binary_node<T>::branch_[1].first);
+            T& v = rbvec_node_ptr_->ref();
+               v = Operation::process(v,branch(1)->value());
 
-               T& v = vec_node_ptr_->ref();
-                  v = Operation::process(v,binary_node<T>::branch_[1].first->value());
+            return v;
+         }
 
-               return v;
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+         inline bool valid() const exprtk_override
+         {
+            return rbvec_node_ptr_ && binary_node<T>::valid();
          }
 
       private:
 
-         vector_elem_node<T>* vec_node_ptr_;
+         rebasevector_celem_node<T>* rbvec_node_ptr_;
       };
 
       template <typename T, typename Operation>
-      class assignment_rebasevec_elem_op_node : public binary_node<T>
+      class assignment_rebasevec_elem_op_rtc_node exprtk_final : public binary_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
 
-         assignment_rebasevec_elem_op_node(const operator_type& opr,
-                                           expression_ptr branch0,
-                                           expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           rbvec_node_ptr_(0)
+         assignment_rebasevec_elem_op_rtc_node(const operator_type& opr,
+                                               expression_ptr branch0,
+                                               expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1)
+         , rbvec_node_ptr_(0)
          {
-            if (is_rebasevector_elem_node(binary_node<T>::branch_[0].first))
+            if (is_rebasevector_elem_rtc_node(branch(0)))
             {
-               rbvec_node_ptr_ = static_cast<rebasevector_elem_node<T>*>(binary_node<T>::branch_[0].first);
+               rbvec_node_ptr_ = static_cast<rebasevector_elem_rtc_node<T>*>(branch(0));
             }
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (rbvec_node_ptr_)
-            {
-               assert(binary_node<T>::branch_[1].first);
+            T& v = rbvec_node_ptr_->ref();
+               v = Operation::process(v,branch(1)->value());
 
-               T& v = rbvec_node_ptr_->ref();
-                  v = Operation::process(v,binary_node<T>::branch_[1].first->value());
+            return v;
+         }
 
-               return v;
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+         inline bool valid() const exprtk_override
+         {
+            return rbvec_node_ptr_ && binary_node<T>::valid();
          }
 
       private:
 
-         rebasevector_elem_node<T>* rbvec_node_ptr_;
+         rebasevector_elem_rtc_node<T>* rbvec_node_ptr_;
       };
 
       template <typename T, typename Operation>
-      class assignment_rebasevec_celem_op_node : public binary_node<T>
+      class assignment_rebasevec_celem_op_rtc_node exprtk_final : public binary_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
 
-         assignment_rebasevec_celem_op_node(const operator_type& opr,
-                                            expression_ptr branch0,
-                                            expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           rbvec_node_ptr_(0)
+         assignment_rebasevec_celem_op_rtc_node(const operator_type& opr,
+                                                expression_ptr branch0,
+                                                expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1)
+         , rbvec_node_ptr_(0)
          {
-            if (is_rebasevector_celem_node(binary_node<T>::branch_[0].first))
+            if (is_rebasevector_celem_rtc_node(branch(0)))
             {
-               rbvec_node_ptr_ = static_cast<rebasevector_celem_node<T>*>(binary_node<T>::branch_[0].first);
+               rbvec_node_ptr_ = static_cast<rebasevector_celem_rtc_node<T>*>(branch(0));
             }
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (rbvec_node_ptr_)
-            {
-               assert(binary_node<T>::branch_[1].first);
+            T& v = rbvec_node_ptr_->ref();
+               v = Operation::process(v,branch(1)->value());
 
-               T& v = rbvec_node_ptr_->ref();
-                  v = Operation::process(v,binary_node<T>::branch_[1].first->value());
+            return v;
+         }
 
-               return v;
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+         inline bool valid() const exprtk_override
+         {
+            return rbvec_node_ptr_ && binary_node<T>::valid();
          }
 
       private:
 
-         rebasevector_celem_node<T>* rbvec_node_ptr_;
+         rebasevector_celem_rtc_node<T>* rbvec_node_ptr_;
       };
 
       template <typename T, typename Operation>
-      class assignment_vec_op_node : public binary_node     <T>,
-                                     public vector_interface<T>
+      class assignment_vec_op_node exprtk_final
+                                   : public binary_node     <T>
+                                   , public vector_interface<T>
       {
       public:
 
-         typedef expression_node<T>*  expression_ptr;
+         typedef expression_node<T>* expression_ptr;
          typedef vector_node<T>*     vector_node_ptr;
-         typedef vec_data_store<T>             vds_t;
+         typedef vec_data_store<T>   vds_t;
+
+         using binary_node<T>::branch;
 
          assignment_vec_op_node(const operator_type& opr,
                                 expression_ptr branch0,
                                 expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           vec_node_ptr_(0)
+         : binary_node<T>(opr, branch0, branch1)
+         , vec_node_ptr_(0)
          {
-            if (is_vector_node(binary_node<T>::branch_[0].first))
+            if (is_vector_node(branch(0)))
             {
-               vec_node_ptr_ = static_cast<vector_node<T>*>(binary_node<T>::branch_[0].first);
+               vec_node_ptr_ = static_cast<vector_node<T>*>(branch(0));
                vds()         = vec_node_ptr_->vds();
             }
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (vec_node_ptr_)
-            {
-               assert(binary_node<T>::branch_[1].first);
+            const T v = branch(1)->value();
 
-               const T v = binary_node<T>::branch_[1].first->value();
+            T* vec = vds().data();
 
-               T* vec = vds().data();
-
-               loop_unroll::details lud(size());
-               const T* upper_bound = vec + lud.upper_bound;
-
-               while (vec < upper_bound)
-               {
-                  #define exprtk_loop(N)       \
-                  Operation::assign(vec[N],v); \
+            loop_unroll::details lud(size());
+            const T* upper_bound = vec + lud.upper_bound;
 
-                  exprtk_loop( 0) exprtk_loop( 1)
-                  exprtk_loop( 2) exprtk_loop( 3)
-                  #ifndef exprtk_disable_superscalar_unroll
-                  exprtk_loop( 4) exprtk_loop( 5)
-                  exprtk_loop( 6) exprtk_loop( 7)
-                  exprtk_loop( 8) exprtk_loop( 9)
-                  exprtk_loop(10) exprtk_loop(11)
-                  exprtk_loop(12) exprtk_loop(13)
-                  exprtk_loop(14) exprtk_loop(15)
-                  #endif
+            while (vec < upper_bound)
+            {
+               #define exprtk_loop(N)       \
+               Operation::assign(vec[N],v); \
 
-                  vec += lud.batch_size;
-               }
+               exprtk_loop( 0) exprtk_loop( 1)
+               exprtk_loop( 2) exprtk_loop( 3)
+               #ifndef exprtk_disable_superscalar_unroll
+               exprtk_loop( 4) exprtk_loop( 5)
+               exprtk_loop( 6) exprtk_loop( 7)
+               exprtk_loop( 8) exprtk_loop( 9)
+               exprtk_loop(10) exprtk_loop(11)
+               exprtk_loop(12) exprtk_loop(13)
+               exprtk_loop(14) exprtk_loop(15)
+               #endif
 
-               exprtk_disable_fallthrough_begin
-               switch (lud.remainder)
-               {
-                  #define case_stmt(N)                  \
-                  case N : Operation::assign(*vec++,v); \
+               vec += lud.batch_size;
+            }
 
-                  #ifndef exprtk_disable_superscalar_unroll
-                  case_stmt(15) case_stmt(14)
-                  case_stmt(13) case_stmt(12)
-                  case_stmt(11) case_stmt(10)
-                  case_stmt( 9) case_stmt( 8)
-                  case_stmt( 7) case_stmt( 6)
-                  case_stmt( 5) case_stmt( 4)
-                  #endif
-                  case_stmt( 3) case_stmt( 2)
-                  case_stmt( 1)
-               }
-               exprtk_disable_fallthrough_end
+            switch (lud.remainder)
+            {
+               #define case_stmt(N,fall_through)     \
+               case N : Operation::assign(*vec++,v); \
+               fall_through                          \
 
+               #ifndef exprtk_disable_superscalar_unroll
+               case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough)
+               case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough)
+               case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough)
+               case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough)
+               case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough)
+               case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough)
+               #endif
+               case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough)
+               case_stmt( 1, (void)0;)
+            }
 
-               #undef exprtk_loop
-               #undef case_stmt
+            #undef exprtk_loop
+            #undef case_stmt
 
-               return vec_node_ptr_->value();
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+            return vec_node_ptr_->value();
          }
 
-         vector_node_ptr vec() const override
+         vector_node_ptr vec() const exprtk_override
          {
             return vec_node_ptr_;
          }
 
-         vector_node_ptr vec() override
+         vector_node_ptr vec() exprtk_override
          {
             return vec_node_ptr_;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_vecopvalass;
          }
 
-         std::size_t size() const override
+         inline bool valid() const exprtk_override
+         {
+            return
+               vec_node_ptr_           &&
+               (size() <= base_size()) &&
+               binary_node<T>::valid() ;
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return vec_node_ptr_->vec_holder().size();
+         }
+
+         std::size_t base_size() const exprtk_override
          {
-            return vds().size();
+            return vec_node_ptr_->vec_holder().base_size();
          }
 
-         vds_t& vds() override
+         vds_t& vds() exprtk_override
          {
             return vds_;
          }
 
-         const vds_t& vds() const override
+         const vds_t& vds() const exprtk_override
          {
             return vds_;
          }
 
-         bool side_effect() const override
+         bool side_effect() const exprtk_override
          {
             return true;
          }
@@ -10860,147 +13303,161 @@ namespace exprtk
       };
 
       template <typename T, typename Operation>
-      class assignment_vecvec_op_node : public binary_node     <T>,
-                                        public vector_interface<T>
+      class assignment_vecvec_op_node exprtk_final
+                                      : public binary_node     <T>
+                                      , public vector_interface<T>
       {
       public:
 
-         typedef expression_node<T>*  expression_ptr;
+         typedef expression_node<T>* expression_ptr;
          typedef vector_node<T>*     vector_node_ptr;
-         typedef vec_data_store<T>             vds_t;
+         typedef vec_data_store<T>   vds_t;
+
+         using binary_node<T>::branch;
 
          assignment_vecvec_op_node(const operator_type& opr,
                                    expression_ptr branch0,
                                    expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           vec0_node_ptr_(0),
-           vec1_node_ptr_(0),
-           initialised_(false)
+         : binary_node<T>(opr, branch0, branch1)
+         , vec0_node_ptr_(0)
+         , vec1_node_ptr_(0)
+         , initialised_(false)
          {
-            if (is_vector_node(binary_node<T>::branch_[0].first))
+            if (is_vector_node(branch(0)))
             {
-               vec0_node_ptr_ = static_cast<vector_node<T>*>(binary_node<T>::branch_[0].first);
+               vec0_node_ptr_ = static_cast<vector_node<T>*>(branch(0));
                vds()          = vec0_node_ptr_->vds();
             }
 
-            if (is_vector_node(binary_node<T>::branch_[1].first))
+            if (is_vector_node(branch(1)))
             {
-               vec1_node_ptr_ = static_cast<vector_node<T>*>(binary_node<T>::branch_[1].first);
+               vec1_node_ptr_ = static_cast<vector_node<T>*>(branch(1));
                vec1_node_ptr_->vds() = vds();
             }
-            else if (is_ivector_node(binary_node<T>::branch_[1].first))
+            else if (is_ivector_node(branch(1)))
             {
                vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
 
-               if (0 != (vi = dynamic_cast<vector_interface<T>*>(binary_node<T>::branch_[1].first)))
+               if (0 != (vi = dynamic_cast<vector_interface<T>*>(branch(1))))
                {
                   vec1_node_ptr_ = vi->vec();
-                  vec1_node_ptr_->vds() = vds();
+                  vec1_node_ptr_->vds() = vi->vds();
                }
                else
                   vds_t::match_sizes(vds(),vec1_node_ptr_->vds());
             }
 
-            initialised_ = (vec0_node_ptr_ && vec1_node_ptr_);
+            initialised_ =
+               vec0_node_ptr_          &&
+               vec1_node_ptr_          &&
+               (size() <= base_size()) &&
+               binary_node<T>::valid();
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (initialised_)
-            {
-               assert(binary_node<T>::branch_[0].first);
-               assert(binary_node<T>::branch_[1].first);
+            branch(0)->value();
+            branch(1)->value();
 
-               binary_node<T>::branch_[0].first->value();
-               binary_node<T>::branch_[1].first->value();
+                  T* vec0 = vec0_node_ptr_->vds().data();
+            const T* vec1 = vec1_node_ptr_->vds().data();
 
-                     T* vec0 = vec0_node_ptr_->vds().data();
-               const T* vec1 = vec1_node_ptr_->vds().data();
+            loop_unroll::details lud(size());
+            const T* upper_bound = vec0 + lud.upper_bound;
 
-               loop_unroll::details lud(size());
-               const T* upper_bound = vec0 + lud.upper_bound;
-
-               while (vec0 < upper_bound)
-               {
-                  #define exprtk_loop(N)                          \
-                  vec0[N] = Operation::process(vec0[N], vec1[N]); \
+            while (vec0 < upper_bound)
+            {
+               #define exprtk_loop(N)                          \
+               vec0[N] = Operation::process(vec0[N], vec1[N]); \
 
-                  exprtk_loop( 0) exprtk_loop( 1)
-                  exprtk_loop( 2) exprtk_loop( 3)
-                  #ifndef exprtk_disable_superscalar_unroll
-                  exprtk_loop( 4) exprtk_loop( 5)
-                  exprtk_loop( 6) exprtk_loop( 7)
-                  exprtk_loop( 8) exprtk_loop( 9)
-                  exprtk_loop(10) exprtk_loop(11)
-                  exprtk_loop(12) exprtk_loop(13)
-                  exprtk_loop(14) exprtk_loop(15)
-                  #endif
+               exprtk_loop( 0) exprtk_loop( 1)
+               exprtk_loop( 2) exprtk_loop( 3)
+               #ifndef exprtk_disable_superscalar_unroll
+               exprtk_loop( 4) exprtk_loop( 5)
+               exprtk_loop( 6) exprtk_loop( 7)
+               exprtk_loop( 8) exprtk_loop( 9)
+               exprtk_loop(10) exprtk_loop(11)
+               exprtk_loop(12) exprtk_loop(13)
+               exprtk_loop(14) exprtk_loop(15)
+               #endif
 
-                  vec0 += lud.batch_size;
-                  vec1 += lud.batch_size;
-               }
+               vec0 += lud.batch_size;
+               vec1 += lud.batch_size;
+            }
 
-               int i = 0;
+            int i = 0;
 
-               exprtk_disable_fallthrough_begin
-               switch (lud.remainder)
-               {
-                  #define case_stmt(N)                                              \
-                  case N : { vec0[i] = Operation::process(vec0[i], vec1[i]); ++i; } \
+            switch (lud.remainder)
+            {
+               #define case_stmt(N,fall_through)                                 \
+               case N : { vec0[i] = Operation::process(vec0[i], vec1[i]); ++i; } \
+               fall_through                                                      \
 
-                  #ifndef exprtk_disable_superscalar_unroll
-                  case_stmt(15) case_stmt(14)
-                  case_stmt(13) case_stmt(12)
-                  case_stmt(11) case_stmt(10)
-                  case_stmt( 9) case_stmt( 8)
-                  case_stmt( 7) case_stmt( 6)
-                  case_stmt( 5) case_stmt( 4)
-                  #endif
-                  case_stmt( 3) case_stmt( 2)
-                  case_stmt( 1)
-               }
-               exprtk_disable_fallthrough_end
+               #ifndef exprtk_disable_superscalar_unroll
+               case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough)
+               case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough)
+               case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough)
+               case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough)
+               case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough)
+               case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough)
+               #endif
+               case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough)
+               case_stmt( 1, (void)0;)
+            }
 
-               #undef exprtk_loop
-               #undef case_stmt
+            #undef exprtk_loop
+            #undef case_stmt
 
-               return vec0_node_ptr_->value();
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+            return vec0_node_ptr_->value();
          }
 
-         vector_node_ptr vec() const override
+         vector_node_ptr vec() const exprtk_override
          {
             return vec0_node_ptr_;
          }
 
-         vector_node_ptr vec() override
+         vector_node_ptr vec() exprtk_override
          {
             return vec0_node_ptr_;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_vecopvecass;
          }
 
-         std::size_t size() const override
+         inline bool valid() const exprtk_override
+         {
+            return initialised_;
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return std::min(
+               vec0_node_ptr_->vec_holder().size(),
+               vec1_node_ptr_->vec_holder().size());
+         }
+
+         std::size_t base_size() const exprtk_override
          {
-            return vds().size();
+            return std::min(
+               vec0_node_ptr_->vec_holder().base_size(),
+               vec1_node_ptr_->vec_holder().base_size());
          }
 
-         vds_t& vds() override
+         vds_t& vds() exprtk_override
          {
             return vds_;
          }
 
-         const vds_t& vds() const override
+         const vds_t& vds() const exprtk_override
          {
             return vds_;
          }
 
-         bool side_effect() const override
+         bool side_effect() const exprtk_override
          {
             return true;
          }
@@ -11013,54 +13470,115 @@ namespace exprtk
          vds_t           vds_;
       };
 
+      template <typename T>
+      struct memory_context_t
+      {
+         typedef vector_node<T>*  vector_node_ptr;
+         typedef vector_holder<T> vector_holder_t;
+         typedef vector_holder_t* vector_holder_ptr;
+
+         memory_context_t()
+         : temp_(0)
+         , temp_vec_node_(0)
+         {}
+
+         void clear()
+         {
+            delete temp_vec_node_;
+            delete temp_;
+         }
+
+         vector_holder_ptr temp_;
+         vector_node_ptr   temp_vec_node_;
+      };
+
+      template <typename T>
+      inline memory_context_t<T> make_memory_context(vector_holder<T>& vec_holder,
+                                                     vec_data_store<T>& vds)
+      {
+         memory_context_t<T> result_ctxt;
+         result_ctxt.temp_  = (vec_holder.rebaseable()) ?
+                              new vector_holder<T>(vec_holder,vds) :
+                              new vector_holder<T>(vds) ;
+         result_ctxt.temp_vec_node_ = new vector_node  <T>(vds,result_ctxt.temp_);
+         return result_ctxt;
+      }
+
+      template <typename T>
+      inline memory_context_t<T> make_memory_context(vector_holder<T>& vec_holder0,
+                                                     vector_holder<T>& vec_holder1,
+                                                     vec_data_store<T>& vds)
+      {
+         memory_context_t<T> result_ctxt;
+
+         if (!vec_holder0.rebaseable() && !vec_holder1.rebaseable())
+            result_ctxt.temp_ = new vector_holder<T>(vds);
+         else if (vec_holder0.rebaseable() && !vec_holder1.rebaseable())
+            result_ctxt.temp_ = new vector_holder<T>(vec_holder0,vds);
+         else if (!vec_holder0.rebaseable() && vec_holder1.rebaseable())
+            result_ctxt.temp_ = new vector_holder<T>(vec_holder1,vds);
+         else
+         {
+            result_ctxt.temp_ = (vec_holder0.base_size() >= vec_holder1.base_size()) ?
+                                new vector_holder<T>(vec_holder0, vds) :
+                                new vector_holder<T>(vec_holder1, vds) ;
+         }
+
+         result_ctxt.temp_vec_node_ = new vector_node <T>(vds,result_ctxt.temp_);
+         return result_ctxt;
+      }
+
       template <typename T, typename Operation>
-      class vec_binop_vecvec_node : public binary_node     <T>,
-                                    public vector_interface<T>
+      class vec_binop_vecvec_node exprtk_final
+                                  : public binary_node     <T>
+                                  , public vector_interface<T>
       {
       public:
 
-         typedef expression_node<T>*    expression_ptr;
-         typedef vector_node<T>*       vector_node_ptr;
-         typedef vector_holder<T>*   vector_holder_ptr;
-         typedef vec_data_store<T>               vds_t;
+         typedef expression_node<T>* expression_ptr;
+         typedef vector_node<T>*     vector_node_ptr;
+         typedef vector_holder<T>    vector_holder_t;
+         typedef vector_holder_t*    vector_holder_ptr;
+         typedef vec_data_store<T>   vds_t;
+         typedef memory_context_t<T> memory_context;
+
+         using binary_node<T>::branch;
 
          vec_binop_vecvec_node(const operator_type& opr,
                                expression_ptr branch0,
                                expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           vec0_node_ptr_(0),
-           vec1_node_ptr_(0),
-           temp_         (0),
-           temp_vec_node_(0),
-           initialised_(false)
+         : binary_node<T>(opr, branch0, branch1)
+         , vec0_node_ptr_(0)
+         , vec1_node_ptr_(0)
+         , initialised_(false)
          {
             bool v0_is_ivec = false;
             bool v1_is_ivec = false;
 
-            if (is_vector_node(binary_node<T>::branch_[0].first))
+            if (is_vector_node(branch(0)))
             {
-               vec0_node_ptr_ = static_cast<vector_node_ptr>(binary_node<T>::branch_[0].first);
+               vec0_node_ptr_ = static_cast<vector_node_ptr>(branch(0));
             }
-            else if (is_ivector_node(binary_node<T>::branch_[0].first))
+            else if (is_ivector_node(branch(0)))
             {
                vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
 
-               if (0 != (vi = dynamic_cast<vector_interface<T>*>(binary_node<T>::branch_[0].first)))
+               if (0 != (vi = dynamic_cast<vector_interface<T>*>(branch(0))))
                {
                   vec0_node_ptr_ = vi->vec();
                   v0_is_ivec     = true;
                }
             }
 
-            if (is_vector_node(binary_node<T>::branch_[1].first))
+            if (is_vector_node(branch(1)))
             {
-               vec1_node_ptr_ = static_cast<vector_node_ptr>(binary_node<T>::branch_[1].first);
+               vec1_node_ptr_ = static_cast<vector_node_ptr>(branch(1));
             }
-            else if (is_ivector_node(binary_node<T>::branch_[1].first))
+            else if (is_ivector_node(branch(1)))
             {
                vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
 
-               if (0 != (vi = dynamic_cast<vector_interface<T>*>(binary_node<T>::branch_[1].first)))
+               if (0 != (vi = dynamic_cast<vector_interface<T>*>(branch(1))))
                {
                   vec1_node_ptr_ = vi->vec();
                   v1_is_ivec     = true;
@@ -11072,164 +13590,180 @@ namespace exprtk
                vector_holder<T>& vec0 = vec0_node_ptr_->vec_holder();
                vector_holder<T>& vec1 = vec1_node_ptr_->vec_holder();
 
-               if (v0_is_ivec && (vec0.size() <= vec1.size()))
+               if (v0_is_ivec && (vec0.base_size() <= vec1.base_size()))
+               {
                   vds_ = vds_t(vec0_node_ptr_->vds());
-               else if (v1_is_ivec && (vec1.size() <= vec0.size()))
+               }
+               else if (v1_is_ivec && (vec1.base_size() <= vec0.base_size()))
+               {
                   vds_ = vds_t(vec1_node_ptr_->vds());
+               }
                else
-                  vds_ = vds_t(std::min(vec0.size(),vec1.size()));
+               {
+                  vds_ = vds_t(std::min(vec0.base_size(),vec1.base_size()));
+               }
 
-               temp_          = new vector_holder<T>(vds().data(),vds().size());
-               temp_vec_node_ = new vector_node<T>  (vds(),temp_);
+               memory_context_ = make_memory_context(vec0, vec1, vds());
 
-               initialised_ = true;
+               initialised_ =
+                  (size() <= base_size()) &&
+                  binary_node<T>::valid();
             }
+
+            assert(valid());
          }
 
-        ~vec_binop_vecvec_node() override
+        ~vec_binop_vecvec_node() exprtk_override
          {
-            delete temp_;
-            delete temp_vec_node_;
+            memory_context_.clear();
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (initialised_)
-            {
-               assert(binary_node<T>::branch_[0].first);
-               assert(binary_node<T>::branch_[1].first);
-
-               binary_node<T>::branch_[0].first->value();
-               binary_node<T>::branch_[1].first->value();
+            branch(0)->value();
+            branch(1)->value();
 
-               const T* vec0 = vec0_node_ptr_->vds().data();
-               const T* vec1 = vec1_node_ptr_->vds().data();
-                     T* vec2 = vds().data();
+            const T* vec0 = vec0_node_ptr_->vds().data();
+            const T* vec1 = vec1_node_ptr_->vds().data();
+                  T* vec2 = vds().data();
 
-               loop_unroll::details lud(size());
-               const T* upper_bound = vec2 + lud.upper_bound;
+            loop_unroll::details lud(size());
+            const T* upper_bound = vec2 + lud.upper_bound;
 
-               while (vec2 < upper_bound)
-               {
-                  #define exprtk_loop(N)                          \
-                  vec2[N] = Operation::process(vec0[N], vec1[N]); \
+            while (vec2 < upper_bound)
+            {
+               #define exprtk_loop(N)                          \
+               vec2[N] = Operation::process(vec0[N], vec1[N]); \
 
-                  exprtk_loop( 0) exprtk_loop( 1)
-                  exprtk_loop( 2) exprtk_loop( 3)
-                  #ifndef exprtk_disable_superscalar_unroll
-                  exprtk_loop( 4) exprtk_loop( 5)
-                  exprtk_loop( 6) exprtk_loop( 7)
-                  exprtk_loop( 8) exprtk_loop( 9)
-                  exprtk_loop(10) exprtk_loop(11)
-                  exprtk_loop(12) exprtk_loop(13)
-                  exprtk_loop(14) exprtk_loop(15)
-                  #endif
+               exprtk_loop( 0) exprtk_loop( 1)
+               exprtk_loop( 2) exprtk_loop( 3)
+               #ifndef exprtk_disable_superscalar_unroll
+               exprtk_loop( 4) exprtk_loop( 5)
+               exprtk_loop( 6) exprtk_loop( 7)
+               exprtk_loop( 8) exprtk_loop( 9)
+               exprtk_loop(10) exprtk_loop(11)
+               exprtk_loop(12) exprtk_loop(13)
+               exprtk_loop(14) exprtk_loop(15)
+               #endif
 
-                  vec0 += lud.batch_size;
-                  vec1 += lud.batch_size;
-                  vec2 += lud.batch_size;
-               }
+               vec0 += lud.batch_size;
+               vec1 += lud.batch_size;
+               vec2 += lud.batch_size;
+            }
 
-               int i = 0;
+            int i = 0;
 
-               exprtk_disable_fallthrough_begin
-               switch (lud.remainder)
-               {
-                  #define case_stmt(N)                                              \
-                  case N : { vec2[i] = Operation::process(vec0[i], vec1[i]); ++i; } \
+            switch (lud.remainder)
+            {
+               #define case_stmt(N)                                              \
+               case N : { vec2[i] = Operation::process(vec0[i], vec1[i]); ++i; } \
+               exprtk_fallthrough                                                \
 
-                  #ifndef exprtk_disable_superscalar_unroll
-                  case_stmt(15) case_stmt(14)
-                  case_stmt(13) case_stmt(12)
-                  case_stmt(11) case_stmt(10)
-                  case_stmt( 9) case_stmt( 8)
-                  case_stmt( 7) case_stmt( 6)
-                  case_stmt( 5) case_stmt( 4)
-                  #endif
-                  case_stmt( 3) case_stmt( 2)
-                  case_stmt( 1)
-               }
-               exprtk_disable_fallthrough_end
+               #ifndef exprtk_disable_superscalar_unroll
+               case_stmt(15) case_stmt(14)
+               case_stmt(13) case_stmt(12)
+               case_stmt(11) case_stmt(10)
+               case_stmt( 9) case_stmt( 8)
+               case_stmt( 7) case_stmt( 6)
+               case_stmt( 5) case_stmt( 4)
+               #endif
+               case_stmt( 3) case_stmt( 2)
+               case_stmt( 1)
+               default: break;
+            }
 
-               #undef exprtk_loop
-               #undef case_stmt
+            #undef exprtk_loop
+            #undef case_stmt
 
-               return (vds().data())[0];
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+            return (vds().data())[0];
          }
 
-         vector_node_ptr vec() const override
+         vector_node_ptr vec() const exprtk_override
          {
-            return temp_vec_node_;
+            return memory_context_.temp_vec_node_;
          }
 
-         vector_node_ptr vec() override
+         vector_node_ptr vec() exprtk_override
          {
-            return temp_vec_node_;
+            return memory_context_.temp_vec_node_;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_vecvecarith;
          }
 
-         std::size_t size() const override
+         inline bool valid() const exprtk_override
+         {
+            return initialised_;
+         }
+
+         std::size_t size() const exprtk_override
          {
-            return vds_.size();
+            return std::min(
+               vec0_node_ptr_->vec_holder().size(),
+               vec1_node_ptr_->vec_holder().size());
          }
 
-         vds_t& vds() override
+         std::size_t base_size() const exprtk_override
+         {
+            return std::min(
+               vec0_node_ptr_->vec_holder().base_size(),
+               vec1_node_ptr_->vec_holder().base_size());
+         }
+
+         vds_t& vds() exprtk_override
          {
             return vds_;
          }
 
-         const vds_t& vds() const override
+         const vds_t& vds() const exprtk_override
          {
             return vds_;
          }
 
       private:
 
-         vector_node_ptr   vec0_node_ptr_;
-         vector_node_ptr   vec1_node_ptr_;
-         vector_holder_ptr temp_;
-         vector_node_ptr   temp_vec_node_;
-         bool              initialised_;
-         vds_t             vds_;
+         vector_node_ptr vec0_node_ptr_;
+         vector_node_ptr vec1_node_ptr_;
+         bool            initialised_;
+         vds_t           vds_;
+         memory_context  memory_context_;
       };
 
       template <typename T, typename Operation>
-      class vec_binop_vecval_node : public binary_node     <T>,
-                                    public vector_interface<T>
+      class vec_binop_vecval_node exprtk_final
+                                  : public binary_node     <T>
+                                  , public vector_interface<T>
       {
       public:
 
-         typedef expression_node<T>*    expression_ptr;
-         typedef vector_node<T>*       vector_node_ptr;
-         typedef vector_holder<T>*   vector_holder_ptr;
-         typedef vec_data_store<T>               vds_t;
+         typedef expression_node<T>* expression_ptr;
+         typedef vector_node<T>*     vector_node_ptr;
+         typedef vector_holder<T>    vector_holder_t;
+         typedef vector_holder_t*    vector_holder_ptr;
+         typedef vec_data_store<T>   vds_t;
+         typedef memory_context_t<T> memory_context;
+
+         using binary_node<T>::branch;
 
          vec_binop_vecval_node(const operator_type& opr,
                                expression_ptr branch0,
                                expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           vec0_node_ptr_(0),
-           temp_         (0),
-           temp_vec_node_(0)
+         : binary_node<T>(opr, branch0, branch1)
+         , vec0_node_ptr_(0)
          {
             bool v0_is_ivec = false;
 
-            if (is_vector_node(binary_node<T>::branch_[0].first))
+            if (is_vector_node(branch(0)))
             {
-               vec0_node_ptr_ = static_cast<vector_node_ptr>(binary_node<T>::branch_[0].first);
+               vec0_node_ptr_ = static_cast<vector_node_ptr>(branch(0));
             }
-            else if (is_ivector_node(binary_node<T>::branch_[0].first))
+            else if (is_ivector_node(branch(0)))
             {
                vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
 
-               if (0 != (vi = dynamic_cast<vector_interface<T>*>(binary_node<T>::branch_[0].first)))
+               if (0 != (vi = dynamic_cast<vector_interface<T>*>(branch(0))))
                {
                   vec0_node_ptr_ = vi->vec();
                   v0_is_ivec     = true;
@@ -11241,111 +13775,115 @@ namespace exprtk
                if (v0_is_ivec)
                   vds() = vec0_node_ptr_->vds();
                else
-                  vds() = vds_t(vec0_node_ptr_->size());
+                  vds() = vds_t(vec0_node_ptr_->base_size());
 
-               temp_          = new vector_holder<T>(vds());
-               temp_vec_node_ = new vector_node<T>  (vds(),temp_);
+               memory_context_ = make_memory_context(vec0_node_ptr_->vec_holder(), vds());
             }
+
+            assert(valid());
          }
 
-        ~vec_binop_vecval_node() override
+        ~vec_binop_vecval_node() exprtk_override
          {
-            delete temp_;
-            delete temp_vec_node_;
+            memory_context_.clear();
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (vec0_node_ptr_)
-            {
-               assert(binary_node<T>::branch_[0].first);
-               assert(binary_node<T>::branch_[1].first);
+                        branch(0)->value();
+            const T v = branch(1)->value();
 
-                           binary_node<T>::branch_[0].first->value();
-               const T v = binary_node<T>::branch_[1].first->value();
+            const T* vec0 = vec0_node_ptr_->vds().data();
+                  T* vec1 = vds().data();
 
-               const T* vec0 = vec0_node_ptr_->vds().data();
-                     T* vec1 = vds().data();
+            loop_unroll::details lud(size());
+            const T* upper_bound = vec0 + lud.upper_bound;
 
-               loop_unroll::details lud(size());
-               const T* upper_bound = vec0 + lud.upper_bound;
-
-               while (vec0 < upper_bound)
-               {
-                  #define exprtk_loop(N)                    \
-                  vec1[N] = Operation::process(vec0[N], v); \
+            while (vec0 < upper_bound)
+            {
+               #define exprtk_loop(N)                    \
+               vec1[N] = Operation::process(vec0[N], v); \
 
-                  exprtk_loop( 0) exprtk_loop( 1)
-                  exprtk_loop( 2) exprtk_loop( 3)
-                  #ifndef exprtk_disable_superscalar_unroll
-                  exprtk_loop( 4) exprtk_loop( 5)
-                  exprtk_loop( 6) exprtk_loop( 7)
-                  exprtk_loop( 8) exprtk_loop( 9)
-                  exprtk_loop(10) exprtk_loop(11)
-                  exprtk_loop(12) exprtk_loop(13)
-                  exprtk_loop(14) exprtk_loop(15)
-                  #endif
+               exprtk_loop( 0) exprtk_loop( 1)
+               exprtk_loop( 2) exprtk_loop( 3)
+               #ifndef exprtk_disable_superscalar_unroll
+               exprtk_loop( 4) exprtk_loop( 5)
+               exprtk_loop( 6) exprtk_loop( 7)
+               exprtk_loop( 8) exprtk_loop( 9)
+               exprtk_loop(10) exprtk_loop(11)
+               exprtk_loop(12) exprtk_loop(13)
+               exprtk_loop(14) exprtk_loop(15)
+               #endif
 
-                  vec0 += lud.batch_size;
-                  vec1 += lud.batch_size;
-               }
+               vec0 += lud.batch_size;
+               vec1 += lud.batch_size;
+            }
 
-               int i = 0;
+            int i = 0;
 
-               exprtk_disable_fallthrough_begin
-               switch (lud.remainder)
-               {
-                  #define case_stmt(N)                                        \
-                  case N : { vec1[i] = Operation::process(vec0[i], v); ++i; } \
+            switch (lud.remainder)
+            {
+               #define case_stmt(N,fall_through)                           \
+               case N : { vec1[i] = Operation::process(vec0[i], v); ++i; } \
+               fall_through                                                \
 
-                  #ifndef exprtk_disable_superscalar_unroll
-                  case_stmt(15) case_stmt(14)
-                  case_stmt(13) case_stmt(12)
-                  case_stmt(11) case_stmt(10)
-                  case_stmt( 9) case_stmt( 8)
-                  case_stmt( 7) case_stmt( 6)
-                  case_stmt( 5) case_stmt( 4)
-                  #endif
-                  case_stmt( 3) case_stmt( 2)
-                  case_stmt( 1)
-               }
-               exprtk_disable_fallthrough_end
+               #ifndef exprtk_disable_superscalar_unroll
+               case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough)
+               case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough)
+               case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough)
+               case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough)
+               case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough)
+               case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough)
+               #endif
+               case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough)
+               case_stmt( 1, (void)0;)
+            }
 
-               #undef exprtk_loop
-               #undef case_stmt
+            #undef exprtk_loop
+            #undef case_stmt
 
-               return (vds().data())[0];
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+            return (vds().data())[0];
          }
 
-         vector_node_ptr vec() const override
+         vector_node_ptr vec() const exprtk_override
          {
-            return temp_vec_node_;
+            return memory_context_.temp_vec_node_;
          }
 
-         vector_node_ptr vec() override
+         vector_node_ptr vec() exprtk_override
          {
-            return temp_vec_node_;
+            return memory_context_.temp_vec_node_;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_vecvalarith;
          }
 
-         std::size_t size() const override
+         inline bool valid() const exprtk_override
+         {
+            return
+               vec0_node_ptr_          &&
+               (size() <= base_size()) &&
+               binary_node<T>::valid();
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return vec0_node_ptr_->size();
+         }
+
+         std::size_t base_size() const exprtk_override
          {
-            return vds().size();
+            return vec0_node_ptr_->vec_holder().base_size();
          }
 
-         vds_t& vds() override
+         vds_t& vds() exprtk_override
          {
             return vds_;
          }
 
-         const vds_t& vds() const override
+         const vds_t& vds() const exprtk_override
          {
             return vds_;
          }
@@ -11353,41 +13891,43 @@ namespace exprtk
       private:
 
          vector_node_ptr   vec0_node_ptr_;
-         vector_holder_ptr temp_;
-         vector_node_ptr   temp_vec_node_;
          vds_t             vds_;
+         memory_context    memory_context_;
       };
 
       template <typename T, typename Operation>
-      class vec_binop_valvec_node : public binary_node     <T>,
-                                    public vector_interface<T>
+      class vec_binop_valvec_node exprtk_final
+                                  : public binary_node     <T>
+                                  , public vector_interface<T>
       {
       public:
 
-         typedef expression_node<T>*    expression_ptr;
-         typedef vector_node<T>*       vector_node_ptr;
-         typedef vector_holder<T>*   vector_holder_ptr;
-         typedef vec_data_store<T>               vds_t;
+         typedef expression_node<T>* expression_ptr;
+         typedef vector_node<T>*     vector_node_ptr;
+         typedef vector_holder<T>    vector_holder_t;
+         typedef vector_holder_t*    vector_holder_ptr;
+         typedef vec_data_store<T>   vds_t;
+         typedef memory_context_t<T> memory_context;
+
+         using binary_node<T>::branch;
 
          vec_binop_valvec_node(const operator_type& opr,
                                expression_ptr branch0,
                                expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           vec1_node_ptr_(0),
-           temp_         (0),
-           temp_vec_node_(0)
+         : binary_node<T>(opr, branch0, branch1)
+         , vec1_node_ptr_(0)
          {
             bool v1_is_ivec = false;
 
-            if (is_vector_node(binary_node<T>::branch_[1].first))
+            if (is_vector_node(branch(1)))
             {
-               vec1_node_ptr_ = static_cast<vector_node_ptr>(binary_node<T>::branch_[1].first);
+               vec1_node_ptr_ = static_cast<vector_node_ptr>(branch(1));
             }
-            else if (is_ivector_node(binary_node<T>::branch_[1].first))
+            else if (is_ivector_node(branch(1)))
             {
                vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
 
-               if (0 != (vi = dynamic_cast<vector_interface<T>*>(binary_node<T>::branch_[1].first)))
+               if (0 != (vi = dynamic_cast<vector_interface<T>*>(branch(1))))
                {
                   vec1_node_ptr_ = vi->vec();
                   v1_is_ivec     = true;
@@ -11399,111 +13939,116 @@ namespace exprtk
                if (v1_is_ivec)
                   vds() = vec1_node_ptr_->vds();
                else
-                  vds() = vds_t(vec1_node_ptr_->size());
+                  vds() = vds_t(vec1_node_ptr_->base_size());
 
-               temp_          = new vector_holder<T>(vds());
-               temp_vec_node_ = new vector_node<T>  (vds(),temp_);
+               memory_context_ = make_memory_context(vec1_node_ptr_->vec_holder(), vds());
             }
+
+            assert(valid());
          }
 
-        ~vec_binop_valvec_node() override
+        ~vec_binop_valvec_node() exprtk_override
          {
-            delete temp_;
-            delete temp_vec_node_;
+            memory_context_.clear();
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (vec1_node_ptr_)
-            {
-               assert(binary_node<T>::branch_[0].first);
-               assert(binary_node<T>::branch_[1].first);
+            const T v = branch(0)->value();
+                        branch(1)->value();
 
-               const T v = binary_node<T>::branch_[0].first->value();
-                           binary_node<T>::branch_[1].first->value();
+                  T* vec0 = vds().data();
+            const T* vec1 = vec1_node_ptr_->vds().data();
 
-                     T* vec0 = vds().data();
-               const T* vec1 = vec1_node_ptr_->vds().data();
+            loop_unroll::details lud(size());
+            const T* upper_bound = vec0 + lud.upper_bound;
 
-               loop_unroll::details lud(size());
-               const T* upper_bound = vec0 + lud.upper_bound;
-
-               while (vec0 < upper_bound)
-               {
-                  #define exprtk_loop(N)                    \
-                  vec0[N] = Operation::process(v, vec1[N]); \
+            while (vec0 < upper_bound)
+            {
+               #define exprtk_loop(N)                    \
+               vec0[N] = Operation::process(v, vec1[N]); \
 
-                  exprtk_loop( 0) exprtk_loop( 1)
-                  exprtk_loop( 2) exprtk_loop( 3)
-                  #ifndef exprtk_disable_superscalar_unroll
-                  exprtk_loop( 4) exprtk_loop( 5)
-                  exprtk_loop( 6) exprtk_loop( 7)
-                  exprtk_loop( 8) exprtk_loop( 9)
-                  exprtk_loop(10) exprtk_loop(11)
-                  exprtk_loop(12) exprtk_loop(13)
-                  exprtk_loop(14) exprtk_loop(15)
-                  #endif
+               exprtk_loop( 0) exprtk_loop( 1)
+               exprtk_loop( 2) exprtk_loop( 3)
+               #ifndef exprtk_disable_superscalar_unroll
+               exprtk_loop( 4) exprtk_loop( 5)
+               exprtk_loop( 6) exprtk_loop( 7)
+               exprtk_loop( 8) exprtk_loop( 9)
+               exprtk_loop(10) exprtk_loop(11)
+               exprtk_loop(12) exprtk_loop(13)
+               exprtk_loop(14) exprtk_loop(15)
+               #endif
 
-                  vec0 += lud.batch_size;
-                  vec1 += lud.batch_size;
-               }
+               vec0 += lud.batch_size;
+               vec1 += lud.batch_size;
+            }
 
-               int i = 0;
+            int i = 0;
 
-               exprtk_disable_fallthrough_begin
-               switch (lud.remainder)
-               {
-                  #define case_stmt(N)                                        \
-                  case N : { vec0[i] = Operation::process(v, vec1[i]); ++i; } \
+            switch (lud.remainder)
+            {
+               #define case_stmt(N,fall_through)                           \
+               case N : { vec0[i] = Operation::process(v, vec1[i]); ++i; } \
+               fall_through                                                \
 
-                  #ifndef exprtk_disable_superscalar_unroll
-                  case_stmt(15) case_stmt(14)
-                  case_stmt(13) case_stmt(12)
-                  case_stmt(11) case_stmt(10)
-                  case_stmt( 9) case_stmt( 8)
-                  case_stmt( 7) case_stmt( 6)
-                  case_stmt( 5) case_stmt( 4)
-                  #endif
-                  case_stmt( 3) case_stmt( 2)
-                  case_stmt( 1)
-               }
-               exprtk_disable_fallthrough_end
+               #ifndef exprtk_disable_superscalar_unroll
+               case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough)
+               case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough)
+               case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough)
+               case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough)
+               case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough)
+               case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough)
+               #endif
+               case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough)
+               case_stmt( 1, (void)0;)
+            }
 
-               #undef exprtk_loop
-               #undef case_stmt
+            #undef exprtk_loop
+            #undef case_stmt
 
-               return (vds().data())[0];
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+            return (vds().data())[0];
          }
 
-         vector_node_ptr vec() const override
+         vector_node_ptr vec() const exprtk_override
          {
-            return temp_vec_node_;
+            return memory_context_.temp_vec_node_;
          }
 
-         vector_node_ptr vec() override
+         vector_node_ptr vec() exprtk_override
          {
-            return temp_vec_node_;
+            return memory_context_.temp_vec_node_;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_vecvalarith;
          }
 
-         std::size_t size() const override
+         inline bool valid() const exprtk_override
+         {
+            return
+               vec1_node_ptr_               &&
+               (size() <= base_size())      &&
+               (vds_.size() <= base_size()) &&
+               binary_node<T>::valid();
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return vec1_node_ptr_->vec_holder().size();
+         }
+
+         std::size_t base_size() const exprtk_override
          {
-            return vds().size();
+            return vec1_node_ptr_->vec_holder().base_size();
          }
 
-         vds_t& vds() override
+         vds_t& vds() exprtk_override
          {
             return vds_;
          }
 
-         const vds_t& vds() const override
+         const vds_t& vds() const exprtk_override
          {
             return vds_;
          }
@@ -11511,39 +14056,41 @@ namespace exprtk
       private:
 
          vector_node_ptr   vec1_node_ptr_;
-         vector_holder_ptr temp_;
-         vector_node_ptr   temp_vec_node_;
          vds_t             vds_;
+         memory_context    memory_context_;
       };
 
       template <typename T, typename Operation>
-      class unary_vector_node : public unary_node      <T>,
-                                public vector_interface<T>
+      class unary_vector_node exprtk_final
+                              : public unary_node      <T>
+                              , public vector_interface<T>
       {
       public:
 
-         typedef expression_node<T>*    expression_ptr;
-         typedef vector_node<T>*       vector_node_ptr;
-         typedef vector_holder<T>*   vector_holder_ptr;
-         typedef vec_data_store<T>               vds_t;
+         typedef expression_node<T>* expression_ptr;
+         typedef vector_node<T>*     vector_node_ptr;
+         typedef vector_holder<T>    vector_holder_t;
+         typedef vector_holder_t*    vector_holder_ptr;
+         typedef vec_data_store<T>   vds_t;
+         typedef memory_context_t<T> memory_context;
+
+         using expression_node<T>::branch;
 
          unary_vector_node(const operator_type& opr, expression_ptr branch0)
-         : unary_node<T>(opr, branch0),
-           vec0_node_ptr_(0),
-           temp_         (0),
-           temp_vec_node_(0)
+         : unary_node<T>(opr, branch0)
+         , vec0_node_ptr_(0)
          {
             bool vec0_is_ivec = false;
 
-            if (is_vector_node(unary_node<T>::branch_.first))
+            if (is_vector_node(branch(0)))
             {
-               vec0_node_ptr_ = static_cast<vector_node_ptr>(unary_node<T>::branch_.first);
+               vec0_node_ptr_ = static_cast<vector_node_ptr>(branch(0));
             }
-            else if (is_ivector_node(unary_node<T>::branch_.first))
+            else if (is_ivector_node(branch(0)))
             {
                vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
 
-               if (0 != (vi = dynamic_cast<vector_interface<T>*>(unary_node<T>::branch_.first)))
+               if (0 != (vi = dynamic_cast<vector_interface<T>*>(branch(0))))
                {
                   vec0_node_ptr_ = vi->vec();
                   vec0_is_ivec   = true;
@@ -11555,188 +14102,366 @@ namespace exprtk
                if (vec0_is_ivec)
                   vds_ = vec0_node_ptr_->vds();
                else
-                  vds_ = vds_t(vec0_node_ptr_->size());
+                  vds_ = vds_t(vec0_node_ptr_->base_size());
 
-               temp_          = new vector_holder<T>(vds());
-               temp_vec_node_ = new vector_node<T>  (vds(),temp_);
+               memory_context_ = make_memory_context(vec0_node_ptr_->vec_holder(), vds());
             }
+
+            assert(valid());
          }
 
-        ~unary_vector_node() override
+        ~unary_vector_node() exprtk_override
          {
-            delete temp_;
-            delete temp_vec_node_;
+            memory_context_.clear();
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(unary_node<T>::branch_.first);
+            branch()->value();
 
-            unary_node<T>::branch_.first->value();
+            const T* vec0 = vec0_node_ptr_->vds().data();
+                  T* vec1 = vds().data();
 
-            if (vec0_node_ptr_)
+            loop_unroll::details lud(size());
+            const T* upper_bound = vec0 + lud.upper_bound;
+
+            while (vec0 < upper_bound)
             {
-               const T* vec0 = vec0_node_ptr_->vds().data();
-                     T* vec1 = vds().data();
+               #define exprtk_loop(N)                 \
+               vec1[N] = Operation::process(vec0[N]); \
 
-               loop_unroll::details lud(size());
-               const T* upper_bound = vec0 + lud.upper_bound;
+               exprtk_loop( 0) exprtk_loop( 1)
+               exprtk_loop( 2) exprtk_loop( 3)
+               #ifndef exprtk_disable_superscalar_unroll
+               exprtk_loop( 4) exprtk_loop( 5)
+               exprtk_loop( 6) exprtk_loop( 7)
+               exprtk_loop( 8) exprtk_loop( 9)
+               exprtk_loop(10) exprtk_loop(11)
+               exprtk_loop(12) exprtk_loop(13)
+               exprtk_loop(14) exprtk_loop(15)
+               #endif
 
-               while (vec0 < upper_bound)
-               {
-                  #define exprtk_loop(N)                 \
-                  vec1[N] = Operation::process(vec0[N]); \
+               vec0 += lud.batch_size;
+               vec1 += lud.batch_size;
+            }
 
-                  exprtk_loop( 0) exprtk_loop( 1)
-                  exprtk_loop( 2) exprtk_loop( 3)
-                  #ifndef exprtk_disable_superscalar_unroll
-                  exprtk_loop( 4) exprtk_loop( 5)
-                  exprtk_loop( 6) exprtk_loop( 7)
-                  exprtk_loop( 8) exprtk_loop( 9)
-                  exprtk_loop(10) exprtk_loop(11)
-                  exprtk_loop(12) exprtk_loop(13)
-                  exprtk_loop(14) exprtk_loop(15)
-                  #endif
+            int i = 0;
 
-                  vec0 += lud.batch_size;
-                  vec1 += lud.batch_size;
-               }
+            switch (lud.remainder)
+            {
+               #define case_stmt(N)                                     \
+               case N : { vec1[i] = Operation::process(vec0[i]); ++i; } \
+               exprtk_fallthrough                                       \
 
-               int i = 0;
+               #ifndef exprtk_disable_superscalar_unroll
+               case_stmt(15) case_stmt(14)
+               case_stmt(13) case_stmt(12)
+               case_stmt(11) case_stmt(10)
+               case_stmt( 9) case_stmt( 8)
+               case_stmt( 7) case_stmt( 6)
+               case_stmt( 5) case_stmt( 4)
+               #endif
+               case_stmt( 3) case_stmt( 2)
+               case_stmt( 1)
+               default: break;
+            }
 
-               exprtk_disable_fallthrough_begin
-               switch (lud.remainder)
+            #undef exprtk_loop
+            #undef case_stmt
+
+            return (vds().data())[0];
+         }
+
+         vector_node_ptr vec() const exprtk_override
+         {
+            return memory_context_.temp_vec_node_;
+         }
+
+         vector_node_ptr vec() exprtk_override
+         {
+            return memory_context_.temp_vec_node_;
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_vecunaryop;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return vec0_node_ptr_ && unary_node<T>::valid();
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return vec0_node_ptr_->vec_holder().size();
+         }
+
+         std::size_t base_size() const exprtk_override
+         {
+            return vec0_node_ptr_->vec_holder().base_size();
+         }
+
+         vds_t& vds() exprtk_override
+         {
+            return vds_;
+         }
+
+         const vds_t& vds() const exprtk_override
+         {
+            return vds_;
+         }
+
+      private:
+
+         vector_node_ptr vec0_node_ptr_;
+         vds_t           vds_;
+         memory_context  memory_context_;
+      };
+
+      template <typename T>
+      class conditional_vector_node exprtk_final
+                                    : public expression_node <T>
+                                    , public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node <T>* expression_ptr;
+         typedef vector_interface<T>* vec_interface_ptr;
+         typedef vector_node     <T>* vector_node_ptr;
+         typedef vector_holder   <T>  vector_holder_t;
+         typedef vector_holder_t*     vector_holder_ptr;
+         typedef vec_data_store  <T>  vds_t;
+         typedef memory_context_t<T> memory_context;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         conditional_vector_node(expression_ptr condition,
+                                 expression_ptr consequent,
+                                 expression_ptr alternative)
+         : consequent_node_ptr_ (0)
+         , alternative_node_ptr_(0)
+         , temp_vec_node_       (0)
+         , temp_                (0)
+         , result_vec_size_     (0)
+         , initialised_         (false)
+         {
+            construct_branch_pair(condition_  , condition  );
+            construct_branch_pair(consequent_ , consequent );
+            construct_branch_pair(alternative_, alternative);
+
+            if (details::is_ivector_node(consequent_.first))
+            {
+               vec_interface_ptr ivec_ptr = dynamic_cast<vec_interface_ptr>(consequent_.first);
+
+               if (0 != ivec_ptr)
                {
-                  #define case_stmt(N)                                     \
-                  case N : { vec1[i] = Operation::process(vec0[i]); ++i; } \
+                  consequent_node_ptr_ = ivec_ptr->vec();
+               }
+            }
 
-                  #ifndef exprtk_disable_superscalar_unroll
-                  case_stmt(15) case_stmt(14)
-                  case_stmt(13) case_stmt(12)
-                  case_stmt(11) case_stmt(10)
-                  case_stmt( 9) case_stmt( 8)
-                  case_stmt( 7) case_stmt( 6)
-                  case_stmt( 5) case_stmt( 4)
-                  #endif
-                  case_stmt( 3) case_stmt( 2)
-                  case_stmt( 1)
+            if (details::is_ivector_node(alternative_.first))
+            {
+               vec_interface_ptr ivec_ptr = dynamic_cast<vec_interface_ptr>(alternative_.first);
+
+               if (0 != ivec_ptr)
+               {
+                  alternative_node_ptr_ = ivec_ptr->vec();
                }
-               exprtk_disable_fallthrough_end
+            }
 
-               #undef exprtk_loop
-               #undef case_stmt
+            if (consequent_node_ptr_ && alternative_node_ptr_)
+            {
+               const std::size_t vec_size =
+                  std::max(consequent_node_ptr_ ->vec_holder().base_size(),
+                           alternative_node_ptr_->vec_holder().base_size());
+
+               vds_            = vds_t(vec_size);
+               memory_context_ = make_memory_context(
+                  consequent_node_ptr_ ->vec_holder(),
+                  alternative_node_ptr_->vec_holder(),
+                  vds());
+
+               initialised_ = (vec_size > 0);
+            }
+
+            assert(initialised_);
+         }
+
+        ~conditional_vector_node() exprtk_override
+         {
+            memory_context_.clear();
+         }
+
+         inline T value() const exprtk_override
+         {
+            T result = T(0);
+            T* source_vector = 0;
+            T* result_vector = vds().data();
 
-               return (vds().data())[0];
+            if (is_true(condition_))
+            {
+               result           = consequent_.first->value();
+               source_vector    = consequent_node_ptr_->vds().data();
+               result_vec_size_ = consequent_node_ptr_->size();
             }
             else
-               return std::numeric_limits<T>::quiet_NaN();
+            {
+               result           = alternative_.first->value();
+               source_vector    = alternative_node_ptr_->vds().data();
+               result_vec_size_ = alternative_node_ptr_->size();
+            }
+
+            for (std::size_t i = 0; i < result_vec_size_; ++i)
+            {
+               result_vector[i] = source_vector[i];
+            }
+
+            return result;
          }
 
-         vector_node_ptr vec() const override
+         vector_node_ptr vec() const exprtk_override
          {
-            return temp_vec_node_;
+            return memory_context_.temp_vec_node_;
          }
 
-         vector_node_ptr vec() override
+         vector_node_ptr vec() exprtk_override
          {
-            return temp_vec_node_;
+            return memory_context_.temp_vec_node_;
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
-            return expression_node<T>::e_vecunaryop;
+            return expression_node<T>::e_vecondition;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return
+               initialised_                                      &&
+               condition_  .first && condition_  .first->valid() &&
+               consequent_ .first && consequent_ .first->valid() &&
+               alternative_.first && alternative_.first->valid() &&
+               size() <= base_size();
+         }
+
+         std::size_t size() const exprtk_override
+         {
+            return result_vec_size_;
          }
 
-         std::size_t size() const override
+         std::size_t base_size() const exprtk_override
          {
-            return vds().size();
+            return std::min(
+               consequent_node_ptr_ ->vec_holder().base_size(),
+               alternative_node_ptr_->vec_holder().base_size());
          }
 
-         vds_t& vds() override
+         vds_t& vds() exprtk_override
          {
             return vds_;
          }
 
-         const vds_t& vds() const override
+         const vds_t& vds() const exprtk_override
          {
             return vds_;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(condition_   , node_delete_list);
+            expression_node<T>::ndb_t::collect(consequent_  , node_delete_list);
+            expression_node<T>::ndb_t::collect(alternative_ , node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::compute_node_depth
+               (condition_, consequent_, alternative_);
+         }
+
       private:
 
-         vector_node_ptr   vec0_node_ptr_;
-         vector_holder_ptr temp_;
-         vector_node_ptr   temp_vec_node_;
-         vds_t             vds_;
+         branch_t            condition_;
+         branch_t            consequent_;
+         branch_t            alternative_;
+         vector_node_ptr     consequent_node_ptr_;
+         vector_node_ptr     alternative_node_ptr_;
+         vector_node_ptr     temp_vec_node_;
+         vector_holder_ptr   temp_;
+         vds_t               vds_;
+         mutable std::size_t result_vec_size_;
+         bool                initialised_;
+         memory_context      memory_context_;
       };
 
       template <typename T>
-      class scand_node : public binary_node<T>
+      class scand_node exprtk_final : public binary_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
 
          scand_node(const operator_type& opr,
                     expression_ptr branch0,
                     expression_ptr branch1)
          : binary_node<T>(opr, branch0, branch1)
-         {}
-
-         inline T value() const override
          {
-            assert(binary_node<T>::branch_[0].first);
-            assert(binary_node<T>::branch_[1].first);
+            assert(binary_node<T>::valid());
+         }
 
+         inline T value() const exprtk_override
+         {
             return (
                      std::not_equal_to<T>()
-                        (T(0),binary_node<T>::branch_[0].first->value()) &&
+                        (T(0),branch(0)->value()) &&
                      std::not_equal_to<T>()
-                        (T(0),binary_node<T>::branch_[1].first->value())
+                        (T(0),branch(1)->value())
                    ) ? T(1) : T(0);
          }
       };
 
       template <typename T>
-      class scor_node : public binary_node<T>
+      class scor_node exprtk_final : public binary_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         using binary_node<T>::branch;
 
          scor_node(const operator_type& opr,
                    expression_ptr branch0,
                    expression_ptr branch1)
          : binary_node<T>(opr, branch0, branch1)
-         {}
-
-         inline T value() const override
          {
-            assert(binary_node<T>::branch_[0].first);
-            assert(binary_node<T>::branch_[1].first);
+            assert(binary_node<T>::valid());
+         }
 
+         inline T value() const exprtk_override
+         {
             return (
                      std::not_equal_to<T>()
-                        (T(0),binary_node<T>::branch_[0].first->value()) ||
+                        (T(0),branch(0)->value()) ||
                      std::not_equal_to<T>()
-                        (T(0),binary_node<T>::branch_[1].first->value())
+                        (T(0),branch(1)->value())
                    ) ? T(1) : T(0);
          }
       };
 
       template <typename T, typename IFunction, std::size_t N>
-      class function_N_node : public expression_node<T>
+      class function_N_node exprtk_final : public expression_node<T>
       {
       public:
 
-         // Function of N paramters.
+         // Function of N parameters.
          typedef expression_node<T>* expression_ptr;
          typedef std::pair<expression_ptr,bool> branch_t;
          typedef IFunction ifunction;
 
          explicit function_N_node(ifunction* func)
-         : function_((N == func->param_count) ? func : reinterpret_cast<ifunction*>(0)),
-           parameter_count_(func->param_count)
+         : function_((N == func->param_count) ? func : reinterpret_cast<ifunction*>(0))
+         , parameter_count_(func->param_count)
+         , initialised_(false)
          {}
 
          template <std::size_t NumBranches>
@@ -11747,19 +14472,24 @@ namespace exprtk
              #pragma warning(push)
              #pragma warning(disable: 4127)
             #endif
+
             if (N != NumBranches)
+            {
                return false;
-            else
+            }
+
+            for (std::size_t i = 0; i < NumBranches; ++i)
             {
-               for (std::size_t i = 0; i < NumBranches; ++i)
-               {
-                  if (b[i])
-                     branch_[i] = std::make_pair(b[i],branch_deletable(b[i]));
-                  else
-                     return false;
-               }
-               return true;
+               if (b[i] && b[i]->valid())
+                  branch_[i] = std::make_pair(b[i],branch_deletable(b[i]));
+               else
+                  return false;
             }
+
+            initialised_ = function_;
+            assert(valid());
+            return initialised_;
+
             #ifdef _MSC_VER
              #pragma warning(pop)
             #endif
@@ -11770,26 +14500,43 @@ namespace exprtk
             return this < (&fn);
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             // Needed for incompetent and broken msvc compiler versions
             #ifdef _MSC_VER
              #pragma warning(push)
              #pragma warning(disable: 4127)
             #endif
-            if ((0 == function_) || (0 == N))
-               return std::numeric_limits<T>::quiet_NaN();
-            else
-            {
-               T v[N];
-               evaluate_branches<T,N>::execute(v,branch_);
-               return invoke<T,N>::execute(*function_,v);
-            }
+
+            T v[N];
+            evaluate_branches<T,N>::execute(v,branch_);
+            return invoke<T,N>::execute(*function_,v);
+
             #ifdef _MSC_VER
              #pragma warning(pop)
             #endif
          }
 
+         inline typename expression_node<T>::node_type type() const exprtk_override
+         {
+            return expression_node<T>::e_function;
+         }
+
+         inline bool valid() const exprtk_override
+         {
+            return initialised_;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_override
+         {
+            return expression_node<T>::ndb_t::template compute_node_depth<N>(branch_);
+         }
+
          template <typename T_, std::size_t BranchCount>
          struct evaluate_branches
          {
@@ -11802,6 +14549,20 @@ namespace exprtk
             }
          };
 
+         template <typename T_>
+         struct evaluate_branches <T_,6>
+         {
+            static inline void execute(T_ (&v)[6], const branch_t (&b)[6])
+            {
+               v[0] = b[0].first->value();
+               v[1] = b[1].first->value();
+               v[2] = b[2].first->value();
+               v[3] = b[3].first->value();
+               v[4] = b[4].first->value();
+               v[5] = b[5].first->value();
+            }
+         };
+
          template <typename T_>
          struct evaluate_branches <T_,5>
          {
@@ -11878,119 +14639,119 @@ namespace exprtk
          struct invoke<T_,18>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[18])
-            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13],v[14],v[15],v[16],v[17]); }
+            { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15], v[16], v[17]); }
          };
 
          template <typename T_>
          struct invoke<T_,17>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[17])
-            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13],v[14],v[15],v[16]); }
+            { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15], v[16]); }
          };
 
          template <typename T_>
          struct invoke<T_,16>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[16])
-            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13],v[14],v[15]); }
+            { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]); }
          };
 
          template <typename T_>
          struct invoke<T_,15>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[15])
-            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13],v[14]); }
+            { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14]); }
          };
 
          template <typename T_>
          struct invoke<T_,14>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[14])
-            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13]); }
+            { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13]); }
          };
 
          template <typename T_>
          struct invoke<T_,13>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[13])
-            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12]); }
+            { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12]); }
          };
 
          template <typename T_>
          struct invoke<T_,12>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[12])
-            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11]); }
+            { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11]); }
          };
 
          template <typename T_>
          struct invoke<T_,11>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[11])
-            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10]); }
+            { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10]); }
          };
 
          template <typename T_>
          struct invoke<T_,10>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[10])
-            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9]); }
+            { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9]); }
          };
 
          template <typename T_>
          struct invoke<T_,9>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[9])
-            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8]); }
+            { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]); }
          };
 
          template <typename T_>
          struct invoke<T_,8>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[8])
-            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]); }
+            { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); }
          };
 
          template <typename T_>
          struct invoke<T_,7>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[7])
-            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6]); }
+            { return f(v[0], v[1], v[2], v[3], v[4], v[5], v[6]); }
          };
 
          template <typename T_>
          struct invoke<T_,6>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[6])
-            { return f(v[0],v[1],v[2],v[3],v[4],v[5]); }
+            { return f(v[0], v[1], v[2], v[3], v[4], v[5]); }
          };
 
          template <typename T_>
          struct invoke<T_,5>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[5])
-            { return f(v[0],v[1],v[2],v[3],v[4]); }
+            { return f(v[0], v[1], v[2], v[3], v[4]); }
          };
 
          template <typename T_>
          struct invoke<T_,4>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[4])
-            { return f(v[0],v[1],v[2],v[3]); }
+            { return f(v[0], v[1], v[2], v[3]); }
          };
 
          template <typename T_>
          struct invoke<T_,3>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[3])
-            { return f(v[0],v[1],v[2]); }
+            { return f(v[0], v[1], v[2]); }
          };
 
          template <typename T_>
          struct invoke<T_,2>
          {
             static inline T_ execute(ifunction& f, T_ (&v)[2])
-            { return f(v[0],v[1]); }
+            { return f(v[0], v[1]); }
          };
 
          template <typename T_>
@@ -12000,30 +14761,16 @@ namespace exprtk
             { return f(v[0]); }
          };
 
-         inline typename expression_node<T>::node_type type() const override
-         {
-            return expression_node<T>::e_function;
-         }
-
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
-         {
-            expression_node<T>::ndb_t::template collect<>(branch_, node_delete_list);
-         }
-
-         std::size_t node_depth() const override
-         {
-            return expression_node<T>::ndb_t::template compute_node_depth<N>(branch_);
-         }
-
       private:
 
          ifunction*  function_;
          std::size_t parameter_count_;
          branch_t    branch_[N];
+         bool        initialised_;
       };
 
       template <typename T, typename IFunction>
-      class function_N_node<T,IFunction,0> : public expression_node<T>
+      class function_N_node<T,IFunction,0> exprtk_final : public expression_node<T>
       {
       public:
 
@@ -12032,33 +14779,37 @@ namespace exprtk
 
          explicit function_N_node(ifunction* func)
          : function_((0 == func->param_count) ? func : reinterpret_cast<ifunction*>(0))
-         {}
+         {
+            assert(valid());
+         }
 
          inline bool operator <(const function_N_node<T,IFunction,0>& fn) const
          {
             return this < (&fn);
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (function_)
-               return (*function_)();
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+            return (*function_)();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_function;
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return function_;
+         }
+
       private:
 
          ifunction* function_;
       };
 
       template <typename T, typename VarArgFunction>
-      class vararg_function_node : public expression_node<T>
+      class vararg_function_node exprtk_final : public expression_node<T>
       {
       public:
 
@@ -12066,10 +14817,11 @@ namespace exprtk
 
          vararg_function_node(VarArgFunction*  func,
                               const std::vector<expression_ptr>& arg_list)
-         : function_(func),
-           arg_list_(arg_list)
+         : function_(func)
+         , arg_list_(arg_list)
          {
             value_list_.resize(arg_list.size(),std::numeric_limits<T>::quiet_NaN());
+            assert(valid());
          }
 
          inline bool operator <(const vararg_function_node<T,VarArgFunction>& fn) const
@@ -12077,23 +14829,23 @@ namespace exprtk
             return this < (&fn);
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (function_)
-            {
-               populate_value_list();
-               return (*function_)(value_list_);
-            }
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+            populate_value_list();
+            return (*function_)(value_list_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_vafunction;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
+         {
+            return function_;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
             for (std::size_t i = 0; i < arg_list_.size(); ++i)
             {
@@ -12104,7 +14856,7 @@ namespace exprtk
             }
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(arg_list_);
          }
@@ -12129,45 +14881,60 @@ namespace exprtk
       {
       public:
 
-         typedef type_store<T>                         type_store_t;
-         typedef expression_node<T>*                 expression_ptr;
-         typedef variable_node<T>                   variable_node_t;
-         typedef vector_node<T>                       vector_node_t;
-         typedef variable_node_t*               variable_node_ptr_t;
-         typedef vector_node_t*                   vector_node_ptr_t;
-         typedef range_interface<T>               range_interface_t;
-         typedef range_data_type<T>               range_data_type_t;
-         typedef range_pack<T>                              range_t;
-         typedef std::pair<expression_ptr,bool>            branch_t;
-         typedef std::pair<void*,std::size_t>                void_t;
-         typedef std::vector<T>                            tmp_vs_t;
-         typedef std::vector<type_store_t>         typestore_list_t;
-         typedef std::vector<range_data_type_t>        range_list_t;
+         typedef type_store<T>       type_store_t;
+         typedef expression_node<T>* expression_ptr;
+         typedef variable_node<T>    variable_node_t;
+         typedef vector_node<T>      vector_node_t;
+         typedef variable_node_t*    variable_node_ptr_t;
+         typedef vector_node_t*      vector_node_ptr_t;
+         typedef range_interface<T>  range_interface_t;
+         typedef range_data_type<T>  range_data_type_t;
+         typedef typename range_interface<T>::range_t range_t;
+
+         typedef std::pair<expression_ptr,bool> branch_t;
+         typedef vector_holder<T>* vh_t;
+         typedef vector_view<T>*   vecview_t;
+
+         typedef std::vector<T>                 tmp_vs_t;
+         typedef std::vector<type_store_t>      typestore_list_t;
+         typedef std::vector<range_data_type_t> range_list_t;
 
          explicit generic_function_node(const std::vector<expression_ptr>& arg_list,
                                         GenericFunction* func = reinterpret_cast<GenericFunction*>(0))
-         : function_(func),
-           arg_list_(arg_list)
+         : function_(func)
+         , arg_list_(arg_list)
          {}
 
-         ~generic_function_node() override = default;
+         ~generic_function_node() exprtk_override
+         {
+            for (std::size_t i = 0; i < vv_list_.size(); ++i)
+            {
+               vecview_t& vv = vv_list_[i];
+               if (vv && typestore_list_[i].vec_data)
+               {
+                  vv->remove_ref(&typestore_list_[i].vec_data);
+                  typestore_list_[i].vec_data = 0;
+               }
+            }
+         }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
             expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_final
          {
             return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
          virtual bool init_branches()
          {
-            expr_as_vec1_store_.resize(arg_list_.size(),T(0)               );
-            typestore_list_    .resize(arg_list_.size(),type_store_t()     );
-            range_list_        .resize(arg_list_.size(),range_data_type_t());
-            branch_            .resize(arg_list_.size(),branch_t(reinterpret_cast<expression_ptr>(0),false));
+            expr_as_vec1_store_.resize(arg_list_.size(), T(0)               );
+            typestore_list_    .resize(arg_list_.size(), type_store_t()     );
+            range_list_        .resize(arg_list_.size(), range_data_type_t());
+            branch_            .resize(arg_list_.size(), branch_t(reinterpret_cast<expression_ptr>(0),false));
+            vv_list_           .resize(arg_list_.size(), vecview_t(0));
 
             for (std::size_t i = 0; i < arg_list_.size(); ++i)
             {
@@ -12185,7 +14952,15 @@ namespace exprtk
                   ts.size = vi->size();
                   ts.data = vi->vds().data();
                   ts.type = type_store_t::e_vector;
-                  vi->vec()->vec_holder().set_ref(&ts.vec_data);
+
+                  if (
+                       vi->vec()->vec_holder().rebaseable() &&
+                       vi->vec()->vec_holder().rebaseable_instance()
+                     )
+                  {
+                     vv_list_[i] = vi->vec()->vec_holder().rebaseable_instance();
+                     vv_list_[i]->set_ref(&ts.vec_data);
+                  }
                }
                #ifndef exprtk_disable_string_capabilities
                else if (is_generally_string_node(arg_list_[i]))
@@ -12221,7 +14996,10 @@ namespace exprtk
                      range_list_[i].range = reinterpret_cast<range_t*>(0);
                   }
                   else
+                  {
                      range_list_[i].range = &(ri->range_ref());
+                     range_param_list_.push_back(i);
+                  }
                }
                #endif
                else if (is_variable_node(arg_list_[i]))
@@ -12253,26 +15031,28 @@ namespace exprtk
             return this < (&fn);
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (function_)
+            if (populate_value_list())
             {
-               if (populate_value_list())
-               {
-                  typedef typename GenericFunction::parameter_list_t parameter_list_t;
+               typedef typename GenericFunction::parameter_list_t parameter_list_t;
 
-                  return (*function_)(parameter_list_t(typestore_list_));
-               }
+               return (*function_)(parameter_list_t(typestore_list_));
             }
 
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_genfunction;
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return function_;
+         }
+
       protected:
 
          inline virtual bool populate_value_list() const
@@ -12282,30 +15062,40 @@ namespace exprtk
                expr_as_vec1_store_[i] = branch_[i].first->value();
             }
 
-            for (std::size_t i = 0; i < branch_.size(); ++i)
+            if (!range_param_list_.empty())
             {
-               range_data_type_t& rdt = range_list_[i];
+               assert(range_param_list_.size() <= branch_.size());
 
-               if (rdt.range)
+               for (std::size_t i = 0; i < range_param_list_.size(); ++i)
                {
+                  const std::size_t  index = range_param_list_[i];
+                  range_data_type_t& rdt   = range_list_[index];
+
                   const range_t& rp = (*rdt.range);
                   std::size_t r0    = 0;
                   std::size_t r1    = 0;
 
-                  if (rp(r0, r1, rdt.size))
-                  {
-                     type_store_t& ts = typestore_list_[i];
+                  const std::size_t data_size =
+                  #ifndef exprtk_disable_string_capabilities
+                     rdt.str_node ? rdt.str_node->size() : rdt.size;
+                  #else
+                     rdt.size;
+                  #endif
 
-                     ts.size = rp.cache_size();
-                     #ifndef exprtk_disable_string_capabilities
-                     if (ts.type == type_store_t::e_string)
-                        ts.data = const_cast<char_ptr>(rdt.str_node->base()) + rp.cache.first;
-                     else
-                     #endif
-                        ts.data = static_cast<char_ptr>(rdt.data) + (rp.cache.first * rdt.type_size);
+                  if (!rp(r0, r1, data_size))
+                  {
+                     return false;
                   }
+
+                  type_store_t& ts = typestore_list_[index];
+
+                  ts.size = rp.cache_size();
+                  #ifndef exprtk_disable_string_capabilities
+                  if (ts.type == type_store_t::e_string)
+                     ts.data = const_cast<char_ptr>(rdt.str_node->base()) + rp.cache.first;
                   else
-                     return false;
+                  #endif
+                     ts.data = static_cast<char_ptr>(rdt.data) + (rp.cache.first * rdt.type_size);
                }
             }
 
@@ -12318,21 +15108,23 @@ namespace exprtk
       private:
 
          std::vector<expression_ptr> arg_list_;
-         std::vector<branch_t>         branch_;
-         mutable tmp_vs_t  expr_as_vec1_store_;
-         mutable range_list_t      range_list_;
+         std::vector<branch_t>       branch_;
+         std::vector<vecview_t>      vv_list_;
+         mutable tmp_vs_t            expr_as_vec1_store_;
+         mutable range_list_t        range_list_;
+         std::vector<std::size_t>    range_param_list_;
       };
 
       #ifndef exprtk_disable_string_capabilities
       template <typename T, typename StringFunction>
-      class string_function_node : public generic_function_node<T,StringFunction>,
-                                   public string_base_node<T>,
-                                   public range_interface <T>
+      class string_function_node : public generic_function_node<T,StringFunction>
+                                 , public string_base_node<T>
+                                 , public range_interface <T>
       {
       public:
 
          typedef generic_function_node<T,StringFunction> gen_function_t;
-         typedef range_pack<T> range_t;
+         typedef typename range_interface<T>::range_t range_t;
 
          string_function_node(StringFunction* func,
                               const std::vector<typename gen_function_t::expression_ptr>& arg_list)
@@ -12342,6 +15134,7 @@ namespace exprtk
             range_.n1_c = std::make_pair<bool,std::size_t>(true,0);
             range_.cache.first  = range_.n0_c.second;
             range_.cache.second = range_.n1_c.second;
+            assert(valid());
          }
 
          inline bool operator <(const string_function_node<T,StringFunction>& fn) const
@@ -12349,56 +15142,59 @@ namespace exprtk
             return this < (&fn);
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (gen_function_t::function_)
+            if (gen_function_t::populate_value_list())
             {
-               if (gen_function_t::populate_value_list())
-               {
-                  typedef typename StringFunction::parameter_list_t parameter_list_t;
+               typedef typename StringFunction::parameter_list_t parameter_list_t;
 
-                  const T result = (*gen_function_t::function_)
-                                      (
-                                        ret_string_,
-                                        parameter_list_t(gen_function_t::typestore_list_)
-                                      );
+               const T result =
+                  (*gen_function_t::function_)
+                  (
+                     ret_string_,
+                     parameter_list_t(gen_function_t::typestore_list_)
+                  );
 
-                  range_.n1_c.second  = ret_string_.size() - 1;
-                  range_.cache.second = range_.n1_c.second;
+               range_.n1_c.second  = ret_string_.size();
+               range_.cache.second = range_.n1_c.second;
 
-                  return result;
-               }
+               return result;
             }
 
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_strfunction;
          }
 
-         std::string str() const override
+         inline bool valid() const exprtk_override
+         {
+            return gen_function_t::function_;
+         }
+
+         std::string str() const exprtk_override
          {
             return ret_string_;
          }
 
-         char_cptr base() const override
+         char_cptr base() const exprtk_override
          {
            return &ret_string_[0];
          }
 
-         std::size_t size() const override
+         std::size_t size() const exprtk_override
          {
             return ret_string_.size();
          }
 
-         range_t& range_ref() override
+         range_t& range_ref() exprtk_override
          {
             return range_;
          }
 
-         const range_t& range_ref() const override
+         const range_t& range_ref() const exprtk_override
          {
             return range_;
          }
@@ -12416,35 +15212,35 @@ namespace exprtk
       public:
 
          typedef generic_function_node<T,GenericFunction> gen_function_t;
-         typedef range_pack<T> range_t;
+         typedef typename gen_function_t::range_t         range_t;
 
          multimode_genfunction_node(GenericFunction* func,
                                     const std::size_t& param_seq_index,
                                     const std::vector<typename gen_function_t::expression_ptr>& arg_list)
-         : gen_function_t(arg_list,func),
-           param_seq_index_(param_seq_index)
+         : gen_function_t(arg_list,func)
+         , param_seq_index_(param_seq_index)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (gen_function_t::function_)
+            assert(gen_function_t::valid());
+
+            if (gen_function_t::populate_value_list())
             {
-               if (gen_function_t::populate_value_list())
-               {
-                  typedef typename GenericFunction::parameter_list_t parameter_list_t;
+               typedef typename GenericFunction::parameter_list_t parameter_list_t;
 
-                  return (*gen_function_t::function_)
-                            (
-                              param_seq_index_,
-                              parameter_list_t(gen_function_t::typestore_list_)
-                            );
-               }
+               return
+                  (*gen_function_t::function_)
+                  (
+                     param_seq_index_,
+                     parameter_list_t(gen_function_t::typestore_list_)
+                  );
             }
 
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_final
          {
             return expression_node<T>::e_genfunction;
          }
@@ -12456,46 +15252,44 @@ namespace exprtk
 
       #ifndef exprtk_disable_string_capabilities
       template <typename T, typename StringFunction>
-      class multimode_strfunction_node : public string_function_node<T,StringFunction>
+      class multimode_strfunction_node exprtk_final : public string_function_node<T,StringFunction>
       {
       public:
 
          typedef string_function_node<T,StringFunction> str_function_t;
-         typedef range_pack<T> range_t;
+         typedef typename str_function_t::range_t range_t;
 
          multimode_strfunction_node(StringFunction* func,
                                     const std::size_t& param_seq_index,
                                     const std::vector<typename str_function_t::expression_ptr>& arg_list)
-         : str_function_t(func,arg_list),
-           param_seq_index_(param_seq_index)
+         : str_function_t(func,arg_list)
+         , param_seq_index_(param_seq_index)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (str_function_t::function_)
+            if (str_function_t::populate_value_list())
             {
-               if (str_function_t::populate_value_list())
-               {
-                  typedef typename StringFunction::parameter_list_t parameter_list_t;
+               typedef typename StringFunction::parameter_list_t parameter_list_t;
 
-                  const T result = (*str_function_t::function_)
-                                      (
-                                        param_seq_index_,
-                                        str_function_t::ret_string_,
-                                        parameter_list_t(str_function_t::typestore_list_)
-                                      );
+               const T result =
+                  (*str_function_t::function_)
+                  (
+                     param_seq_index_,
+                     str_function_t::ret_string_,
+                     parameter_list_t(str_function_t::typestore_list_)
+                  );
 
-                  str_function_t::range_.n1_c.second  = str_function_t::ret_string_.size() - 1;
-                  str_function_t::range_.cache.second = str_function_t::range_.n1_c.second;
+               str_function_t::range_.n1_c.second  = str_function_t::ret_string_.size();
+               str_function_t::range_.cache.second = str_function_t::range_.n1_c.second;
 
-                  return result;
-               }
+               return result;
             }
 
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_strfunction;
          }
@@ -12506,15 +15300,14 @@ namespace exprtk
       };
       #endif
 
-      class return_exception
-      {};
+      class return_exception {};
 
       template <typename T>
       class null_igenfunc
       {
       public:
 
-         virtual ~null_igenfunc() = default;
+         virtual ~null_igenfunc() exprtk_default;
 
          typedef type_store<T> generic_type;
          typedef typename generic_type::parameter_list parameter_list_t;
@@ -12527,27 +15320,26 @@ namespace exprtk
 
       #ifndef exprtk_disable_return_statement
       template <typename T>
-      class return_node : public generic_function_node<T,null_igenfunc<T> >
+      class return_node exprtk_final : public generic_function_node<T,null_igenfunc<T> >
       {
       public:
 
-         typedef null_igenfunc<T> igeneric_function_t;
+         typedef results_context<T>   results_context_t;
+         typedef null_igenfunc<T>     igeneric_function_t;
          typedef igeneric_function_t* igeneric_function_ptr;
          typedef generic_function_node<T,igeneric_function_t> gen_function_t;
-         typedef results_context<T> results_context_t;
 
          return_node(const std::vector<typename gen_function_t::expression_ptr>& arg_list,
                      results_context_t& rc)
-         : gen_function_t  (arg_list),
-           results_context_(&rc)
-         {}
+         : gen_function_t  (arg_list)
+         , results_context_(&rc)
+         {
+            assert(valid());
+         }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (
-                 (0 != results_context_) &&
-                 gen_function_t::populate_value_list()
-               )
+            if (gen_function_t::populate_value_list())
             {
                typedef typename type_store<T>::parameter_list parameter_list_t;
 
@@ -12560,18 +15352,23 @@ namespace exprtk
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_return;
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return results_context_;
+         }
+
       private:
 
          results_context_t* results_context_;
       };
 
       template <typename T>
-      class return_envelope_node : public expression_node<T>
+      class return_envelope_node exprtk_final : public expression_node<T>
       {
       public:
 
@@ -12580,16 +15377,15 @@ namespace exprtk
          typedef std::pair<expression_ptr,bool> branch_t;
 
          return_envelope_node(expression_ptr body, results_context_t& rc)
-         : results_context_(&rc  ),
-           return_invoked_ (false)
+         : results_context_(&rc  )
+         , return_invoked_ (false)
          {
             construct_branch_pair(body_, body);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(body_.first);
-
             try
             {
                return_invoked_ = false;
@@ -12600,26 +15396,32 @@ namespace exprtk
             catch(const return_exception&)
             {
                return_invoked_ = true;
+
                return std::numeric_limits<T>::quiet_NaN();
             }
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_retenv;
          }
 
+         inline bool valid() const exprtk_override
+         {
+            return results_context_ && body_.first;
+         }
+
          inline bool* retinvk_ptr()
          {
             return &return_invoked_;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
             expression_node<T>::ndb_t::collect(body_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(body_);
          }
@@ -12702,11 +15504,11 @@ namespace exprtk
       {
          typedef typename details::functor_t<T>::Type    Type;
          typedef typename details::functor_t<T>::RefType RefType;
-         typedef typename details::functor_t<T>          functor_t;
-         typedef typename functor_t::qfunc_t  quaternary_functor_t;
-         typedef typename functor_t::tfunc_t     trinary_functor_t;
-         typedef typename functor_t::bfunc_t      binary_functor_t;
-         typedef typename functor_t::ufunc_t       unary_functor_t;
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::qfunc_t    quaternary_functor_t;
+         typedef typename functor_t::tfunc_t    trinary_functor_t;
+         typedef typename functor_t::bfunc_t    binary_functor_t;
+         typedef typename functor_t::ufunc_t    unary_functor_t;
       };
 
       template <typename T>
@@ -12993,7 +15795,7 @@ namespace exprtk
       }
 
       template <typename T>
-      struct vararg_add_op : public opr_base<T>
+      struct vararg_add_op exprtk_final : public opr_base<T>
       {
          typedef typename opr_base<T>::Type Type;
 
@@ -13016,7 +15818,7 @@ namespace exprtk
 
                             for (std::size_t i = 0; i < arg_list.size(); ++i)
                             {
-                              result += value(arg_list[i]);
+                               result += value(arg_list[i]);
                             }
 
                             return result;
@@ -13060,7 +15862,7 @@ namespace exprtk
       };
 
       template <typename T>
-      struct vararg_mul_op : public opr_base<T>
+      struct vararg_mul_op exprtk_final : public opr_base<T>
       {
          typedef typename opr_base<T>::Type Type;
 
@@ -13127,7 +15929,7 @@ namespace exprtk
       };
 
       template <typename T>
-      struct vararg_avg_op : public opr_base<T>
+      struct vararg_avg_op exprtk_final : public opr_base<T>
       {
          typedef typename opr_base<T>::Type Type;
 
@@ -13144,7 +15946,7 @@ namespace exprtk
                case 3  : return process_3(arg_list);
                case 4  : return process_4(arg_list);
                case 5  : return process_5(arg_list);
-               default : return vararg_add_op<T>::process(arg_list) / arg_list.size();
+               default : return vararg_add_op<T>::process(arg_list) / T(arg_list.size());
             }
          }
 
@@ -13183,7 +15985,7 @@ namespace exprtk
       };
 
       template <typename T>
-      struct vararg_min_op : public opr_base<T>
+      struct vararg_min_op exprtk_final : public opr_base<T>
       {
          typedef typename opr_base<T>::Type Type;
 
@@ -13254,7 +16056,7 @@ namespace exprtk
       };
 
       template <typename T>
-      struct vararg_max_op : public opr_base<T>
+      struct vararg_max_op exprtk_final : public opr_base<T>
       {
          typedef typename opr_base<T>::Type Type;
 
@@ -13325,7 +16127,7 @@ namespace exprtk
       };
 
       template <typename T>
-      struct vararg_mand_op : public opr_base<T>
+      struct vararg_mand_op exprtk_final : public opr_base<T>
       {
          typedef typename opr_base<T>::Type Type;
 
@@ -13405,7 +16207,7 @@ namespace exprtk
       };
 
       template <typename T>
-      struct vararg_mor_op : public opr_base<T>
+      struct vararg_mor_op exprtk_final : public opr_base<T>
       {
          typedef typename opr_base<T>::Type Type;
 
@@ -13485,7 +16287,7 @@ namespace exprtk
       };
 
       template <typename T>
-      struct vararg_multi_op : public opr_base<T>
+      struct vararg_multi_op exprtk_final : public opr_base<T>
       {
          typedef typename opr_base<T>::Type Type;
 
@@ -13506,14 +16308,13 @@ namespace exprtk
                case 7  : return process_7(arg_list);
                case 8  : return process_8(arg_list);
                default :
-                         {
-                            for (std::size_t i = 0; i < (arg_list.size() - 1); ++i)
-                            {
-                               value(arg_list[i]);
-                            }
-
-                            return value(arg_list.back());
-                         }
+                        {
+                           for (std::size_t i = 0; i < (arg_list.size() - 1); ++i)
+                           {
+                              value(arg_list[i]);
+                           }
+                           return value(arg_list.back());
+                        }
             }
          }
 
@@ -13602,7 +16403,7 @@ namespace exprtk
          static inline T process(const ivector_ptr v)
          {
             const T* vec = v->vec()->vds().data();
-            const std::size_t vec_size = v->vec()->vds().size();
+            const std::size_t vec_size = v->size();
 
             loop_unroll::details lud(vec_size);
 
@@ -13611,24 +16412,24 @@ namespace exprtk
                T result = T(0);
                int i    = 0;
 
-               exprtk_disable_fallthrough_begin
                switch (vec_size)
                {
-                  #define case_stmt(N)         \
-                  case N : result += vec[i++]; \
+                  #define case_stmt(N,fall_through) \
+                  case N : result += vec[i++];      \
+                  fall_through                      \
 
                   #ifndef exprtk_disable_superscalar_unroll
-                  case_stmt(16) case_stmt(15)
-                  case_stmt(14) case_stmt(13)
-                  case_stmt(12) case_stmt(11)
-                  case_stmt(10) case_stmt( 9)
-                  case_stmt( 8) case_stmt( 7)
-                  case_stmt( 6) case_stmt( 5)
+                  case_stmt(16, exprtk_fallthrough) case_stmt(15, exprtk_fallthrough)
+                  case_stmt(14, exprtk_fallthrough) case_stmt(13, exprtk_fallthrough)
+                  case_stmt(12, exprtk_fallthrough) case_stmt(11, exprtk_fallthrough)
+                  case_stmt(10, exprtk_fallthrough) case_stmt( 9, exprtk_fallthrough)
+                  case_stmt( 8, exprtk_fallthrough) case_stmt( 7, exprtk_fallthrough)
+                  case_stmt( 6, exprtk_fallthrough) case_stmt( 5, exprtk_fallthrough)
+
                   #endif
-                  case_stmt( 4) case_stmt( 3)
-                  case_stmt( 2) case_stmt( 1)
+                  case_stmt( 4, exprtk_fallthrough) case_stmt( 3, exprtk_fallthrough)
+                  case_stmt( 2, exprtk_fallthrough) case_stmt( 1, (void)0;)
                }
-               exprtk_disable_fallthrough_end
 
                #undef case_stmt
 
@@ -13663,24 +16464,23 @@ namespace exprtk
 
             int i = 0;
 
-            exprtk_disable_fallthrough_begin
             switch (lud.remainder)
             {
-               #define case_stmt(N)       \
-               case N : r[0] += vec[i++]; \
+               #define case_stmt(N,fall_through) \
+               case N : r[0] += vec[i++];        \
+               fall_through                      \
 
                #ifndef exprtk_disable_superscalar_unroll
-               case_stmt(15) case_stmt(14)
-               case_stmt(13) case_stmt(12)
-               case_stmt(11) case_stmt(10)
-               case_stmt( 9) case_stmt( 8)
-               case_stmt( 7) case_stmt( 6)
-               case_stmt( 5) case_stmt( 4)
+               case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough)
+               case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough)
+               case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough)
+               case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough)
+               case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough)
+               case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough)
                #endif
-               case_stmt( 3) case_stmt( 2)
-               case_stmt( 1)
+               case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough)
+               case_stmt( 1, (void)0;)
             }
-            exprtk_disable_fallthrough_end
 
             #undef exprtk_loop
             #undef case_stmt
@@ -13703,7 +16503,7 @@ namespace exprtk
          static inline T process(const ivector_ptr v)
          {
             const T* vec = v->vec()->vds().data();
-            const std::size_t vec_size = v->vec()->vds().size();
+            const std::size_t vec_size = v->vec()->size();
 
             loop_unroll::details lud(vec_size);
 
@@ -13712,24 +16512,23 @@ namespace exprtk
                T result = T(1);
                int i    = 0;
 
-               exprtk_disable_fallthrough_begin
                switch (vec_size)
                {
-                  #define case_stmt(N)         \
-                  case N : result *= vec[i++]; \
+                  #define case_stmt(N,fall_through) \
+                  case N : result *= vec[i++];      \
+                  fall_through                      \
 
                   #ifndef exprtk_disable_superscalar_unroll
-                  case_stmt(16) case_stmt(15)
-                  case_stmt(14) case_stmt(13)
-                  case_stmt(12) case_stmt(11)
-                  case_stmt(10) case_stmt( 9)
-                  case_stmt( 8) case_stmt( 7)
-                  case_stmt( 6) case_stmt( 5)
+                  case_stmt(16, exprtk_fallthrough) case_stmt(15, exprtk_fallthrough)
+                  case_stmt(14, exprtk_fallthrough) case_stmt(13, exprtk_fallthrough)
+                  case_stmt(12, exprtk_fallthrough) case_stmt(11, exprtk_fallthrough)
+                  case_stmt(10, exprtk_fallthrough) case_stmt( 9, exprtk_fallthrough)
+                  case_stmt( 8, exprtk_fallthrough) case_stmt( 7, exprtk_fallthrough)
+                  case_stmt( 6, exprtk_fallthrough) case_stmt( 5, exprtk_fallthrough)
                   #endif
-                  case_stmt( 4) case_stmt( 3)
-                  case_stmt( 2) case_stmt( 1)
+                  case_stmt( 4, exprtk_fallthrough) case_stmt( 3, exprtk_fallthrough)
+                  case_stmt( 2, exprtk_fallthrough) case_stmt( 1, (void)0;)
                }
-               exprtk_disable_fallthrough_end
 
                #undef case_stmt
 
@@ -13764,33 +16563,32 @@ namespace exprtk
 
             int i = 0;
 
-            exprtk_disable_fallthrough_begin
             switch (lud.remainder)
             {
-               #define case_stmt(N)       \
-               case N : r[0] *= vec[i++]; \
+               #define case_stmt(N,fall_through) \
+               case N : r[0] *= vec[i++];        \
+               fall_through                      \
 
                #ifndef exprtk_disable_superscalar_unroll
-               case_stmt(15) case_stmt(14)
-               case_stmt(13) case_stmt(12)
-               case_stmt(11) case_stmt(10)
-               case_stmt( 9) case_stmt( 8)
-               case_stmt( 7) case_stmt( 6)
-               case_stmt( 5) case_stmt( 4)
+               case_stmt(15, exprtk_fallthrough) case_stmt(14, exprtk_fallthrough)
+               case_stmt(13, exprtk_fallthrough) case_stmt(12, exprtk_fallthrough)
+               case_stmt(11, exprtk_fallthrough) case_stmt(10, exprtk_fallthrough)
+               case_stmt( 9, exprtk_fallthrough) case_stmt( 8, exprtk_fallthrough)
+               case_stmt( 7, exprtk_fallthrough) case_stmt( 6, exprtk_fallthrough)
+               case_stmt( 5, exprtk_fallthrough) case_stmt( 4, exprtk_fallthrough)
                #endif
-               case_stmt( 3) case_stmt( 2)
-               case_stmt( 1)
+               case_stmt( 3, exprtk_fallthrough) case_stmt( 2, exprtk_fallthrough)
+               case_stmt( 1, (void)0;)
             }
-            exprtk_disable_fallthrough_end
 
             #undef exprtk_loop
             #undef case_stmt
 
             return (r[ 0] * r[ 1] * r[ 2] * r[ 3])
                    #ifndef exprtk_disable_superscalar_unroll
-                 + (r[ 4] * r[ 5] * r[ 6] * r[ 7])
-                 + (r[ 8] * r[ 9] * r[10] * r[11])
-                 + (r[12] * r[13] * r[14] * r[15])
+                 * (r[ 4] * r[ 5] * r[ 6] * r[ 7])
+                 * (r[ 8] * r[ 9] * r[10] * r[11])
+                 * (r[12] * r[13] * r[14] * r[15])
                    #endif
                    ;
          }
@@ -13803,8 +16601,7 @@ namespace exprtk
 
          static inline T process(const ivector_ptr v)
          {
-            const std::size_t vec_size = v->vec()->vds().size();
-
+            const T vec_size = T(v->vec()->size());
             return vec_add_op<T>::process(v) / vec_size;
          }
       };
@@ -13817,7 +16614,7 @@ namespace exprtk
          static inline T process(const ivector_ptr v)
          {
             const T* vec = v->vec()->vds().data();
-            const std::size_t vec_size = v->vec()->vds().size();
+            const std::size_t vec_size = v->vec()->size();
 
             T result = vec[0];
 
@@ -13841,7 +16638,7 @@ namespace exprtk
          static inline T process(const ivector_ptr v)
          {
             const T* vec = v->vec()->vds().data();
-            const std::size_t vec_size = v->vec()->vds().size();
+            const std::size_t vec_size = v->vec()->size();
 
             T result = vec[0];
 
@@ -13862,7 +16659,7 @@ namespace exprtk
       {
       public:
 
-         ~vov_base_node() override = default;
+         ~vov_base_node() exprtk_override exprtk_default;
 
          inline virtual operator_type operation() const
          {
@@ -13879,7 +16676,7 @@ namespace exprtk
       {
       public:
 
-         ~cov_base_node() override = default;
+         ~cov_base_node() exprtk_override exprtk_default;
 
          inline virtual operator_type operation() const
          {
@@ -13896,7 +16693,7 @@ namespace exprtk
       {
       public:
 
-         ~voc_base_node() override = default;
+         ~voc_base_node() exprtk_override exprtk_default;
 
          inline virtual operator_type operation() const
          {
@@ -13913,7 +16710,7 @@ namespace exprtk
       {
       public:
 
-         ~vob_base_node() override = default;
+         ~vob_base_node() exprtk_override exprtk_default;
 
          virtual const T& v() const = 0;
       };
@@ -13923,7 +16720,7 @@ namespace exprtk
       {
       public:
 
-         ~bov_base_node() override = default;
+         ~bov_base_node() exprtk_override exprtk_default;
 
          virtual const T& v() const = 0;
       };
@@ -13933,7 +16730,7 @@ namespace exprtk
       {
       public:
 
-         ~cob_base_node() override = default;
+         ~cob_base_node() exprtk_override exprtk_default;
 
          inline virtual operator_type operation() const
          {
@@ -13952,7 +16749,7 @@ namespace exprtk
       {
       public:
 
-         ~boc_base_node() override = default;
+         ~boc_base_node() exprtk_override exprtk_default;
 
          inline virtual operator_type operation() const
          {
@@ -13971,7 +16768,7 @@ namespace exprtk
       {
       public:
 
-         ~uv_base_node() override = default;
+         ~uv_base_node() exprtk_override exprtk_default;
 
          inline virtual operator_type operation() const
          {
@@ -13986,7 +16783,7 @@ namespace exprtk
       {
       public:
 
-         ~sos_base_node() override = default;
+         ~sos_base_node() exprtk_override exprtk_default;
 
          inline virtual operator_type operation() const
          {
@@ -13999,7 +16796,7 @@ namespace exprtk
       {
       public:
 
-         ~sosos_base_node() override = default;
+         ~sosos_base_node() exprtk_override exprtk_default;
 
          inline virtual operator_type operation() const
          {
@@ -14012,7 +16809,7 @@ namespace exprtk
       {
       public:
 
-         ~T0oT1oT2_base_node() override = default;
+         ~T0oT1oT2_base_node() exprtk_override exprtk_default;
 
          virtual std::string type_id() const = 0;
       };
@@ -14022,13 +16819,13 @@ namespace exprtk
       {
       public:
 
-         ~T0oT1oT2oT3_base_node() override = default;
+         ~T0oT1oT2oT3_base_node() exprtk_override exprtk_default;
 
          virtual std::string type_id() const = 0;
       };
 
       template <typename T, typename Operation>
-      class unary_variable_node : public uv_base_node<T>
+      class unary_variable_node exprtk_final : public uv_base_node<T>
       {
       public:
 
@@ -14039,70 +16836,64 @@ namespace exprtk
          : v_(var)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return Operation::process(v_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return Operation::type();
          }
 
-         inline operator_type operation() const override
+         inline operator_type operation() const exprtk_override
          {
             return Operation::operation();
          }
 
-         inline const T& v() const override
+         inline const T& v() const exprtk_override
          {
             return v_;
          }
 
       private:
 
-         unary_variable_node(unary_variable_node<T,Operation>&);
-         unary_variable_node<T,Operation>& operator=(unary_variable_node<T,Operation>&);
+         unary_variable_node(const unary_variable_node<T,Operation>&) exprtk_delete;
+         unary_variable_node<T,Operation>& operator=(const unary_variable_node<T,Operation>&) exprtk_delete;
 
          const T& v_;
       };
 
       template <typename T>
-      class uvouv_node : public expression_node<T>
+      class uvouv_node exprtk_final : public expression_node<T>
       {
       public:
 
          // UOpr1(v0) Op UOpr2(v1)
-
-         typedef expression_node<T>* expression_ptr;
          typedef typename details::functor_t<T> functor_t;
-         typedef typename functor_t::bfunc_t      bfunc_t;
-         typedef typename functor_t::ufunc_t      ufunc_t;
+         typedef typename functor_t::bfunc_t    bfunc_t;
+         typedef typename functor_t::ufunc_t    ufunc_t;
+         typedef expression_node<T>*            expression_ptr;
 
          explicit uvouv_node(const T& var0,const T& var1,
                              ufunc_t uf0, ufunc_t uf1, bfunc_t bf)
-         : v0_(var0),
-           v1_(var1),
-           u0_(uf0 ),
-           u1_(uf1 ),
-           f_ (bf  )
+         : v0_(var0)
+         , v1_(var1)
+         , u0_(uf0 )
+         , u1_(uf1 )
+         , f_ (bf  )
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return f_(u0_(v0_),u1_(v1_));
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_uvouv;
          }
 
-         inline operator_type operation() const
-         {
-            return details::e_default;
-         }
-
          inline const T& v0()
          {
             return v0_;
@@ -14130,8 +16921,8 @@ namespace exprtk
 
       private:
 
-         uvouv_node(uvouv_node<T>&);
-         uvouv_node<T>& operator=(uvouv_node<T>&);
+         uvouv_node(const uvouv_node<T>&) exprtk_delete;
+         uvouv_node<T>& operator=(const uvouv_node<T>&) exprtk_delete;
 
          const T& v0_;
          const T& v1_;
@@ -14141,35 +16932,40 @@ namespace exprtk
       };
 
       template <typename T, typename Operation>
-      class unary_branch_node : public expression_node<T>
+      class unary_branch_node exprtk_final : public expression_node<T>
       {
       public:
 
-         typedef expression_node<T>* expression_ptr;
+         typedef Operation                      operation_t;
+         typedef expression_node<T>*            expression_ptr;
          typedef std::pair<expression_ptr,bool> branch_t;
-         typedef Operation operation_t;
 
          explicit unary_branch_node(expression_ptr branch)
          {
             construct_branch_pair(branch_, branch);
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return Operation::process(branch_.first->value());
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return Operation::type();
          }
 
-         inline operator_type operation() const
+         inline bool valid() const exprtk_override
+         {
+            return branch_.first && branch_.first->valid();
+         }
+
+         inline operator_type operation()
          {
             return Operation::operation();
          }
 
-         inline expression_node<T>* branch(const std::size_t&) const override
+         inline expression_node<T>* branch(const std::size_t&) const exprtk_override
          {
             return branch_.first;
          }
@@ -14179,20 +16975,20 @@ namespace exprtk
             branch_.second = false;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
             expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
 
-         unary_branch_node(unary_branch_node<T,Operation>&);
-         unary_branch_node<T,Operation>& operator=(unary_branch_node<T,Operation>&);
+         unary_branch_node(const unary_branch_node<T,Operation>&) exprtk_delete;
+         unary_branch_node<T,Operation>& operator=(const unary_branch_node<T,Operation>&) exprtk_delete;
 
          branch_t branch_;
       };
@@ -14218,7 +17014,7 @@ namespace exprtk
       struct T0oT1oT2process
       {
          typedef typename details::functor_t<T> functor_t;
-         typedef typename functor_t::bfunc_t      bfunc_t;
+         typedef typename functor_t::bfunc_t    bfunc_t;
 
          struct mode0
          {
@@ -14261,7 +17057,7 @@ namespace exprtk
       struct T0oT1oT20T3process
       {
          typedef typename details::functor_t<T> functor_t;
-         typedef typename functor_t::bfunc_t      bfunc_t;
+         typedef typename functor_t::bfunc_t    bfunc_t;
 
          struct mode0
          {
@@ -14375,7 +17171,7 @@ namespace exprtk
       template <typename T, typename T0, typename T1>
       const typename expression_node<T>::node_type nodetype_T0oT1<T,T0,T1>::result = expression_node<T>::e_none;
 
-      #define synthesis_node_type_define(T0_,T1_,v_)                                                            \
+      #define synthesis_node_type_define(T0_, T1_, v_)                                                          \
       template <typename T, typename T0, typename T1>                                                           \
       struct nodetype_T0oT1<T,T0_,T1_> { static const typename expression_node<T>::node_type result; };         \
       template <typename T, typename T0, typename T1>                                                           \
@@ -14397,7 +17193,7 @@ namespace exprtk
       template <typename T, typename T0, typename T1, typename T2>
       const typename expression_node<T>::node_type nodetype_T0oT1oT2<T,T0,T1,T2>::result = expression_node<T>::e_none;
 
-      #define synthesis_node_type_define(T0_,T1_,T2_,v_)                                                               \
+      #define synthesis_node_type_define(T0_, T1_, T2_, v_)                                                            \
       template <typename T, typename T0, typename T1, typename T2>                                                     \
       struct nodetype_T0oT1oT2<T,T0_,T1_,T2_> { static const typename expression_node<T>::node_type result; };         \
       template <typename T, typename T0, typename T1, typename T2>                                                     \
@@ -14419,7 +17215,7 @@ namespace exprtk
       template <typename T, typename T0, typename T1, typename T2, typename T3>
       const typename expression_node<T>::node_type nodetype_T0oT1oT2oT3<T,T0,T1,T2,T3>::result = expression_node<T>::e_none;
 
-      #define synthesis_node_type_define(T0_,T1_,T2_,T3_,v_)                                                                  \
+      #define synthesis_node_type_define(T0_, T1_, T2_, T3_, v_)                                                              \
       template <typename T, typename T0, typename T1, typename T2, typename T3>                                               \
       struct nodetype_T0oT1oT2oT3<T,T0_,T1_,T2_,T3_> { static const typename expression_node<T>::node_type result; };         \
       template <typename T, typename T0, typename T1, typename T2, typename T3>                                               \
@@ -14444,33 +17240,33 @@ namespace exprtk
       #undef synthesis_node_type_define
 
       template <typename T, typename T0, typename T1>
-      class T0oT1 : public expression_node<T>
+      class T0oT1 exprtk_final : public expression_node<T>
       {
       public:
 
          typedef typename details::functor_t<T> functor_t;
-         typedef typename functor_t::bfunc_t      bfunc_t;
+         typedef typename functor_t::bfunc_t    bfunc_t;
          typedef T value_type;
          typedef T0oT1<T,T0,T1> node_type;
 
          T0oT1(T0 p0, T1 p1, const bfunc_t p2)
-         : t0_(p0),
-           t1_(p1),
-           f_ (p2)
+         : t0_(p0)
+         , t1_(p1)
+         , f_ (p2)
          {}
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             static const typename expression_node<T>::node_type result = nodetype_T0oT1<T,T0,T1>::result;
             return result;
          }
 
-         inline operator_type operation() const
+         inline operator_type operation() const exprtk_override
          {
             return e_default;
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return f_(t0_,t1_);
          }
@@ -14502,8 +17298,8 @@ namespace exprtk
 
       private:
 
-         T0oT1(T0oT1<T,T0,T1>&) {}
-         T0oT1<T,T0,T1>& operator=(T0oT1<T,T0,T1>&) { return (*this); }
+         T0oT1(const T0oT1<T,T0,T1>&) exprtk_delete;
+         T0oT1<T,T0,T1>& operator=(const T0oT1<T,T0,T1>&) { return (*this); }
 
          T0 t0_;
          T1 t1_;
@@ -14511,36 +17307,36 @@ namespace exprtk
       };
 
       template <typename T, typename T0, typename T1, typename T2, typename ProcessMode>
-      class T0oT1oT2 : public T0oT1oT2_base_node<T>
+      class T0oT1oT2 exprtk_final : public T0oT1oT2_base_node<T>
       {
       public:
 
          typedef typename details::functor_t<T> functor_t;
-         typedef typename functor_t::bfunc_t      bfunc_t;
+         typedef typename functor_t::bfunc_t    bfunc_t;
          typedef T value_type;
          typedef T0oT1oT2<T,T0,T1,T2,ProcessMode> node_type;
          typedef ProcessMode process_mode_t;
 
          T0oT1oT2(T0 p0, T1 p1, T2 p2, const bfunc_t p3, const bfunc_t p4)
-         : t0_(p0),
-           t1_(p1),
-           t2_(p2),
-           f0_(p3),
-           f1_(p4)
+         : t0_(p0)
+         , t1_(p1)
+         , t2_(p2)
+         , f0_(p3)
+         , f1_(p4)
          {}
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             static const typename expression_node<T>::node_type result = nodetype_T0oT1oT2<T,T0,T1,T2>::result;
             return result;
          }
 
-         inline operator_type operation() const
+         inline operator_type operation()
          {
             return e_default;
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return ProcessMode::process(t0_, t1_, t2_, f0_, f1_);
          }
@@ -14570,7 +17366,7 @@ namespace exprtk
             return f1_;
          }
 
-         std::string type_id() const override
+         std::string type_id() const exprtk_override
          {
             return id();
          }
@@ -14590,8 +17386,8 @@ namespace exprtk
 
       private:
 
-         T0oT1oT2(node_type&) {}
-         node_type& operator=(node_type&) { return (*this); }
+         T0oT1oT2(const node_type&) exprtk_delete;
+         node_type& operator=(const node_type&) exprtk_delete;
 
          T0 t0_;
          T1 t1_;
@@ -14601,12 +17397,12 @@ namespace exprtk
       };
 
       template <typename T, typename T0_, typename T1_, typename T2_, typename T3_, typename ProcessMode>
-      class T0oT1oT2oT3 : public T0oT1oT2oT3_base_node<T>
+      class T0oT1oT2oT3 exprtk_final : public T0oT1oT2oT3_base_node<T>
       {
       public:
 
          typedef typename details::functor_t<T> functor_t;
-         typedef typename functor_t::bfunc_t      bfunc_t;
+         typedef typename functor_t::bfunc_t    bfunc_t;
          typedef T value_type;
          typedef T0_ T0;
          typedef T1_ T1;
@@ -14616,16 +17412,16 @@ namespace exprtk
          typedef ProcessMode process_mode_t;
 
          T0oT1oT2oT3(T0 p0, T1 p1, T2 p2, T3 p3, bfunc_t p4, bfunc_t p5, bfunc_t p6)
-         : t0_(p0),
-           t1_(p1),
-           t2_(p2),
-           t3_(p3),
-           f0_(p4),
-           f1_(p5),
-           f2_(p6)
+         : t0_(p0)
+         , t1_(p1)
+         , t2_(p2)
+         , t3_(p3)
+         , f0_(p4)
+         , f1_(p5)
+         , f2_(p6)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return ProcessMode::process(t0_, t1_, t2_, t3_, f0_, f1_, f2_);
          }
@@ -14665,7 +17461,7 @@ namespace exprtk
             return f2_;
          }
 
-         inline std::string type_id() const override
+         inline std::string type_id() const exprtk_override
          {
             return id();
          }
@@ -14687,8 +17483,8 @@ namespace exprtk
 
       private:
 
-         T0oT1oT2oT3(node_type&) {}
-         node_type& operator=(node_type&) { return (*this); }
+         T0oT1oT2oT3(const node_type&) exprtk_delete;
+         node_type& operator=(const node_type&) exprtk_delete;
 
          T0 t0_;
          T1 t1_;
@@ -14700,34 +17496,34 @@ namespace exprtk
       };
 
       template <typename T, typename T0, typename T1, typename T2>
-      class T0oT1oT2_sf3 : public T0oT1oT2_base_node<T>
+      class T0oT1oT2_sf3 exprtk_final : public T0oT1oT2_base_node<T>
       {
       public:
 
          typedef typename details::functor_t<T> functor_t;
-         typedef typename functor_t::tfunc_t      tfunc_t;
+         typedef typename functor_t::tfunc_t    tfunc_t;
          typedef T value_type;
          typedef T0oT1oT2_sf3<T,T0,T1,T2> node_type;
 
          T0oT1oT2_sf3(T0 p0, T1 p1, T2 p2, const tfunc_t p3)
-         : t0_(p0),
-           t1_(p1),
-           t2_(p2),
-           f_ (p3)
+         : t0_(p0)
+         , t1_(p1)
+         , t2_(p2)
+         , f_ (p3)
          {}
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             static const typename expression_node<T>::node_type result = nodetype_T0oT1oT2<T,T0,T1,T2>::result;
             return result;
          }
 
-         inline operator_type operation() const
+         inline operator_type operation() const exprtk_override
          {
             return e_default;
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return f_(t0_, t1_, t2_);
          }
@@ -14752,7 +17548,7 @@ namespace exprtk
             return f_;
          }
 
-         std::string type_id() const override
+         std::string type_id() const exprtk_override
          {
             return id();
          }
@@ -14772,8 +17568,8 @@ namespace exprtk
 
       private:
 
-         T0oT1oT2_sf3(node_type&) {}
-         node_type& operator=(node_type&) { return (*this); }
+         T0oT1oT2_sf3(const node_type&) exprtk_delete;
+         node_type& operator=(const node_type&) exprtk_delete;
 
          T0 t0_;
          T1 t1_;
@@ -14786,7 +17582,7 @@ namespace exprtk
       {
       public:
 
-         ~sf3ext_type_node() override = default;
+         ~sf3ext_type_node() exprtk_override exprtk_default;
 
          virtual T0 t0() const = 0;
 
@@ -14796,53 +17592,51 @@ namespace exprtk
       };
 
       template <typename T, typename T0, typename T1, typename T2, typename SF3Operation>
-      class T0oT1oT2_sf3ext : public sf3ext_type_node<T,T0,T1,T2>
+      class T0oT1oT2_sf3ext exprtk_final : public sf3ext_type_node<T,T0,T1,T2>
       {
       public:
 
-         typedef typename details::functor_t<T> functor_t;
-         typedef typename functor_t::tfunc_t      tfunc_t;
          typedef T value_type;
-         typedef T0oT1oT2_sf3ext<T,T0,T1,T2,SF3Operation> node_type;
+         typedef T0oT1oT2_sf3ext<T, T0, T1, T2, SF3Operation> node_type;
 
          T0oT1oT2_sf3ext(T0 p0, T1 p1, T2 p2)
-         : t0_(p0),
-           t1_(p1),
-           t2_(p2)
+         : t0_(p0)
+         , t1_(p1)
+         , t2_(p2)
          {}
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             static const typename expression_node<T>::node_type result = nodetype_T0oT1oT2<T,T0,T1,T2>::result;
             return result;
          }
 
-         inline operator_type operation() const
+         inline operator_type operation()
          {
             return e_default;
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return SF3Operation::process(t0_, t1_, t2_);
          }
 
-         T0 t0() const override
+         T0 t0() const exprtk_override
          {
             return t0_;
          }
 
-         T1 t1() const override
+         T1 t1() const exprtk_override
          {
             return t1_;
          }
 
-         T2 t2() const override
+         T2 t2() const exprtk_override
          {
             return t2_;
          }
 
-         std::string type_id() const override
+         std::string type_id() const exprtk_override
          {
             return id();
          }
@@ -14862,8 +17656,8 @@ namespace exprtk
 
       private:
 
-         T0oT1oT2_sf3ext(node_type&) {}
-         node_type& operator=(node_type&) { return (*this); }
+         T0oT1oT2_sf3ext(const node_type&) exprtk_delete;
+         node_type& operator=(const node_type&) exprtk_delete;
 
          T0 t0_;
          T1 t1_;
@@ -14885,37 +17679,37 @@ namespace exprtk
       }
 
       template <typename T, typename T0, typename T1, typename T2, typename T3>
-      class T0oT1oT2oT3_sf4 : public T0oT1oT2_base_node<T>
+      class T0oT1oT2oT3_sf4 exprtk_final : public T0oT1oT2_base_node<T>
       {
       public:
 
-         ~T0oT1oT2oT3_sf4() override = default;
+         ~T0oT1oT2oT3_sf4() exprtk_override exprtk_default;
 
          typedef typename details::functor_t<T> functor_t;
-         typedef typename functor_t::qfunc_t      qfunc_t;
+         typedef typename functor_t::qfunc_t    qfunc_t;
          typedef T value_type;
-         typedef T0oT1oT2oT3_sf4<T,T0,T1,T2,T3> node_type;
+         typedef T0oT1oT2oT3_sf4<T, T0, T1, T2, T3> node_type;
 
          T0oT1oT2oT3_sf4(T0 p0, T1 p1, T2 p2, T3 p3, const qfunc_t p4)
-         : t0_(p0),
-           t1_(p1),
-           t2_(p2),
-           t3_(p3),
-           f_ (p4)
+         : t0_(p0)
+         , t1_(p1)
+         , t2_(p2)
+         , t3_(p3)
+         , f_ (p4)
          {}
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             static const typename expression_node<T>::node_type result = nodetype_T0oT1oT2oT3<T,T0,T1,T2,T3>::result;
             return result;
          }
 
-         inline operator_type operation() const
+         inline operator_type operation() const exprtk_override
          {
             return e_default;
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return f_(t0_, t1_, t2_, t3_);
          }
@@ -14945,7 +17739,7 @@ namespace exprtk
             return f_;
          }
 
-         std::string type_id() const override
+         std::string type_id() const exprtk_override
          {
             return id();
          }
@@ -14965,8 +17759,8 @@ namespace exprtk
 
       private:
 
-         T0oT1oT2oT3_sf4(node_type&) {}
-         node_type& operator=(node_type&) { return (*this); }
+         T0oT1oT2oT3_sf4(const node_type&) exprtk_delete;
+         node_type& operator=(const node_type&) exprtk_delete;
 
          T0 t0_;
          T1 t1_;
@@ -14976,34 +17770,27 @@ namespace exprtk
       };
 
       template <typename T, typename T0, typename T1, typename T2, typename T3, typename SF4Operation>
-      class T0oT1oT2oT3_sf4ext : public T0oT1oT2oT3_base_node<T>
+      class T0oT1oT2oT3_sf4ext exprtk_final : public T0oT1oT2oT3_base_node<T>
       {
       public:
 
-         typedef typename details::functor_t<T> functor_t;
-         typedef typename functor_t::tfunc_t      tfunc_t;
          typedef T value_type;
-         typedef T0oT1oT2oT3_sf4ext<T,T0,T1,T2,T3,SF4Operation> node_type;
+         typedef T0oT1oT2oT3_sf4ext<T, T0, T1, T2, T3, SF4Operation> node_type;
 
          T0oT1oT2oT3_sf4ext(T0 p0, T1 p1, T2 p2, T3 p3)
-         : t0_(p0),
-           t1_(p1),
-           t2_(p2),
-           t3_(p3)
+         : t0_(p0)
+         , t1_(p1)
+         , t2_(p2)
+         , t3_(p3)
          {}
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             static const typename expression_node<T>::node_type result = nodetype_T0oT1oT2oT3<T,T0,T1,T2,T3>::result;
             return result;
          }
 
-         inline operator_type operation() const
-         {
-            return e_default;
-         }
-
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return SF4Operation::process(t0_, t1_, t2_, t3_);
          }
@@ -15028,7 +17815,7 @@ namespace exprtk
             return t3_;
          }
 
-         std::string type_id() const override
+         std::string type_id() const exprtk_override
          {
             return id();
          }
@@ -15048,8 +17835,8 @@ namespace exprtk
 
       private:
 
-         T0oT1oT2oT3_sf4ext(node_type&) {}
-         node_type& operator=(node_type&) { return (*this); }
+         T0oT1oT2oT3_sf4ext(const node_type&) exprtk_delete;
+         node_type& operator=(const node_type&) exprtk_delete;
 
          T0 t0_;
          T1 t1_;
@@ -15102,7 +17889,7 @@ namespace exprtk
       };
 
       template <typename T, typename Operation>
-      class vov_node : public vov_base_node<T>
+      class vov_node exprtk_final : public vov_base_node<T>
       {
       public:
 
@@ -15111,31 +17898,31 @@ namespace exprtk
 
          // variable op variable node
          explicit vov_node(const T& var0, const T& var1)
-         : v0_(var0),
-           v1_(var1)
+         : v0_(var0)
+         , v1_(var1)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return Operation::process(v0_,v1_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return Operation::type();
          }
 
-         inline operator_type operation() const override
+         inline operator_type operation() const exprtk_override
          {
             return Operation::operation();
          }
 
-         inline const T& v0() const override
+         inline const T& v0() const exprtk_override
          {
             return v0_;
          }
 
-         inline const T& v1() const override
+         inline const T& v1() const exprtk_override
          {
             return v1_;
          }
@@ -15147,12 +17934,12 @@ namespace exprtk
 
       private:
 
-         vov_node(vov_node<T,Operation>&);
-         vov_node<T,Operation>& operator=(vov_node<T,Operation>&);
+         vov_node(const vov_node<T,Operation>&) exprtk_delete;
+         vov_node<T,Operation>& operator=(const vov_node<T,Operation>&) exprtk_delete;
       };
 
       template <typename T, typename Operation>
-      class cov_node : public cov_base_node<T>
+      class cov_node exprtk_final : public cov_base_node<T>
       {
       public:
 
@@ -15161,31 +17948,31 @@ namespace exprtk
 
          // constant op variable node
          explicit cov_node(const T& const_var, const T& var)
-         : c_(const_var),
-           v_(var)
+         : c_(const_var)
+         , v_(var)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return Operation::process(c_,v_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return Operation::type();
          }
 
-         inline operator_type operation() const override
+         inline operator_type operation() const exprtk_override
          {
             return Operation::operation();
          }
 
-         inline const T c() const override
+         inline const T c() const exprtk_override
          {
             return c_;
          }
 
-         inline const T& v() const override
+         inline const T& v() const exprtk_override
          {
             return v_;
          }
@@ -15197,12 +17984,12 @@ namespace exprtk
 
       private:
 
-         cov_node(const cov_node<T,Operation>&);
-         cov_node<T,Operation>& operator=(const cov_node<T,Operation>&);
+         cov_node(const cov_node<T,Operation>&) exprtk_delete;
+         cov_node<T,Operation>& operator=(const cov_node<T,Operation>&) exprtk_delete;
       };
 
       template <typename T, typename Operation>
-      class voc_node : public voc_base_node<T>
+      class voc_node exprtk_final : public voc_base_node<T>
       {
       public:
 
@@ -15211,26 +17998,26 @@ namespace exprtk
 
          // variable op constant node
          explicit voc_node(const T& var, const T& const_var)
-         : v_(var),
-           c_(const_var)
+         : v_(var)
+         , c_(const_var)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return Operation::process(v_,c_);
          }
 
-         inline operator_type operation() const override
+         inline operator_type operation() const exprtk_override
          {
             return Operation::operation();
          }
 
-         inline const T c() const override
+         inline const T c() const exprtk_override
          {
             return c_;
          }
 
-         inline const T& v() const override
+         inline const T& v() const exprtk_override
          {
             return v_;
          }
@@ -15242,12 +18029,12 @@ namespace exprtk
 
       private:
 
-         voc_node(const voc_node<T,Operation>&);
-         voc_node<T,Operation>& operator=(const voc_node<T,Operation>&);
+         voc_node(const voc_node<T,Operation>&) exprtk_delete;
+         voc_node<T,Operation>& operator=(const voc_node<T,Operation>&) exprtk_delete;
       };
 
       template <typename T, typename Operation>
-      class vob_node : public vob_base_node<T>
+      class vob_node exprtk_final : public vob_base_node<T>
       {
       public:
 
@@ -15255,55 +18042,55 @@ namespace exprtk
          typedef std::pair<expression_ptr,bool> branch_t;
          typedef Operation operation_t;
 
-         // variable op constant node
+         // variable op binary node
          explicit vob_node(const T& var, const expression_ptr branch)
          : v_(var)
          {
             construct_branch_pair(branch_, branch);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(branch_.first);
             return Operation::process(v_,branch_.first->value());
          }
 
-         inline operator_type operation() const
+         inline const T& v() const exprtk_override
          {
-            return Operation::operation();
+            return v_;
          }
 
-         inline const T& v() const override
+         inline bool valid() const exprtk_override
          {
-            return v_;
+            return branch_.first && branch_.first->valid();
          }
 
-         inline expression_node<T>* branch(const std::size_t&) const override
+         inline expression_node<T>* branch(const std::size_t&) const exprtk_override
          {
             return branch_.first;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            expression_node<T>::ndb_t::template collect<>(branch_, node_delete_list);
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
 
-         vob_node(const vob_node<T,Operation>&);
-         vob_node<T,Operation>& operator=(const vob_node<T,Operation>&);
+         vob_node(const vob_node<T,Operation>&) exprtk_delete;
+         vob_node<T,Operation>& operator=(const vob_node<T,Operation>&) exprtk_delete;
 
          const T& v_;
          branch_t branch_;
       };
 
       template <typename T, typename Operation>
-      class bov_node : public bov_base_node<T>
+      class bov_node exprtk_final : public bov_base_node<T>
       {
       public:
 
@@ -15311,55 +18098,55 @@ namespace exprtk
          typedef std::pair<expression_ptr,bool> branch_t;
          typedef Operation operation_t;
 
-         // variable op constant node
+         // binary node op variable node
          explicit bov_node(const expression_ptr branch, const T& var)
          : v_(var)
          {
             construct_branch_pair(branch_, branch);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(branch_.first);
             return Operation::process(branch_.first->value(),v_);
          }
 
-         inline operator_type operation() const
+         inline const T& v() const exprtk_override
          {
-            return Operation::operation();
+            return v_;
          }
 
-         inline const T& v() const override
+         inline bool valid() const exprtk_override
          {
-            return v_;
+            return branch_.first && branch_.first->valid();
          }
 
-         inline expression_node<T>* branch(const std::size_t&) const override
+         inline expression_node<T>* branch(const std::size_t&) const exprtk_override
          {
             return branch_.first;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            expression_node<T>::ndb_t::template collect<>(branch_, node_delete_list);
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
 
-         bov_node(const bov_node<T,Operation>&);
-         bov_node<T,Operation>& operator=(const bov_node<T,Operation>&);
+         bov_node(const bov_node<T,Operation>&) exprtk_delete;
+         bov_node<T,Operation>& operator=(const bov_node<T,Operation>&) exprtk_delete;
 
          const T& v_;
          branch_t branch_;
       };
 
       template <typename T, typename Operation>
-      class cob_node : public cob_base_node<T>
+      class cob_node exprtk_final : public cob_base_node<T>
       {
       public:
 
@@ -15367,66 +18154,71 @@ namespace exprtk
          typedef std::pair<expression_ptr,bool> branch_t;
          typedef Operation operation_t;
 
-         // variable op constant node
+         // constant op variable node
          explicit cob_node(const T const_var, const expression_ptr branch)
          : c_(const_var)
          {
             construct_branch_pair(branch_, branch);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(branch_.first);
             return Operation::process(c_,branch_.first->value());
          }
 
-         inline operator_type operation() const override
+         inline operator_type operation() const exprtk_override
          {
             return Operation::operation();
          }
 
-         inline const T c() const override
+         inline const T c() const exprtk_override
          {
             return c_;
          }
 
-         inline void set_c(const T new_c) override
+         inline void set_c(const T new_c) exprtk_override
          {
             (*const_cast<T*>(&c_)) = new_c;
          }
 
-         inline expression_node<T>* branch(const std::size_t&) const override
+         inline bool valid() const exprtk_override
+         {
+            return branch_.first && branch_.first->valid();
+         }
+
+         inline expression_node<T>* branch(const std::size_t&) const exprtk_override
          {
             return branch_.first;
          }
 
-         inline expression_node<T>* move_branch(const std::size_t&) override
+         inline expression_node<T>* move_branch(const std::size_t&) exprtk_override
          {
             branch_.second = false;
             return branch_.first;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            expression_node<T>::ndb_t::template collect<>(branch_, node_delete_list);
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
 
-         cob_node(const cob_node<T,Operation>&);
-         cob_node<T,Operation>& operator=(const cob_node<T,Operation>&);
+         cob_node(const cob_node<T,Operation>&) exprtk_delete;
+         cob_node<T,Operation>& operator=(const cob_node<T,Operation>&) exprtk_delete;
 
          const T  c_;
          branch_t branch_;
       };
 
       template <typename T, typename Operation>
-      class boc_node : public boc_base_node<T>
+      class boc_node exprtk_final : public boc_base_node<T>
       {
       public:
 
@@ -15434,59 +18226,64 @@ namespace exprtk
          typedef std::pair<expression_ptr,bool> branch_t;
          typedef Operation operation_t;
 
-         // variable op constant node
+         // binary node op constant node
          explicit boc_node(const expression_ptr branch, const T const_var)
          : c_(const_var)
          {
             construct_branch_pair(branch_, branch);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(branch_.first);
             return Operation::process(branch_.first->value(),c_);
          }
 
-         inline operator_type operation() const override
+         inline operator_type operation() const exprtk_override
          {
             return Operation::operation();
          }
 
-         inline const T c() const override
+         inline const T c() const exprtk_override
          {
             return c_;
          }
 
-         inline void set_c(const T new_c) override
+         inline void set_c(const T new_c) exprtk_override
          {
             (*const_cast<T*>(&c_)) = new_c;
          }
 
-         inline expression_node<T>* branch(const std::size_t&) const override
+         inline bool valid() const exprtk_override
+         {
+            return branch_.first && branch_.first->valid();
+         }
+
+         inline expression_node<T>* branch(const std::size_t&) const exprtk_override
          {
             return branch_.first;
          }
 
-         inline expression_node<T>* move_branch(const std::size_t&) override
+         inline expression_node<T>* move_branch(const std::size_t&) exprtk_override
          {
             branch_.second = false;
             return branch_.first;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            expression_node<T>::ndb_t::template collect<>(branch_, node_delete_list);
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
 
-         boc_node(const boc_node<T,Operation>&);
-         boc_node<T,Operation>& operator=(const boc_node<T,Operation>&);
+         boc_node(const boc_node<T,Operation>&) exprtk_delete;
+         boc_node<T,Operation>& operator=(const boc_node<T,Operation>&) exprtk_delete;
 
          const T  c_;
          branch_t branch_;
@@ -15494,7 +18291,7 @@ namespace exprtk
 
       #ifndef exprtk_disable_string_capabilities
       template <typename T, typename SType0, typename SType1, typename Operation>
-      class sos_node : public sos_base_node<T>
+      class sos_node exprtk_final : public sos_base_node<T>
       {
       public:
 
@@ -15503,21 +18300,21 @@ namespace exprtk
 
          // string op string node
          explicit sos_node(SType0 p0, SType1 p1)
-         : s0_(p0),
-           s1_(p1)
+         : s0_(p0)
+         , s1_(p1)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return Operation::process(s0_,s1_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return Operation::type();
          }
 
-         inline operator_type operation() const override
+         inline operator_type operation() const exprtk_override
          {
             return Operation::operation();
          }
@@ -15539,31 +18336,32 @@ namespace exprtk
 
       private:
 
-         sos_node(sos_node<T,SType0,SType1,Operation>&);
-         sos_node<T,SType0,SType1,Operation>& operator=(sos_node<T,SType0,SType1,Operation>&);
+         sos_node(const sos_node<T,SType0,SType1,Operation>&) exprtk_delete;
+         sos_node<T,SType0,SType1,Operation>& operator=(const sos_node<T,SType0,SType1,Operation>&) exprtk_delete;
       };
 
       template <typename T, typename SType0, typename SType1, typename RangePack, typename Operation>
-      class str_xrox_node : public sos_base_node<T>
+      class str_xrox_node exprtk_final : public sos_base_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
          typedef Operation operation_t;
+         typedef str_xrox_node<T,SType0,SType1,RangePack,Operation> node_type;
 
          // string-range op string node
          explicit str_xrox_node(SType0 p0, SType1 p1, RangePack rp0)
-         : s0_ (p0 ),
-           s1_ (p1 ),
-           rp0_(rp0)
+         : s0_ (p0 )
+         , s1_ (p1 )
+         , rp0_(rp0)
          {}
 
-        ~str_xrox_node() override
+        ~str_xrox_node() exprtk_override
          {
             rp0_.free();
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             std::size_t r0 = 0;
             std::size_t r1 = 0;
@@ -15574,12 +18372,12 @@ namespace exprtk
                return T(0);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return Operation::type();
          }
 
-         inline operator_type operation() const override
+         inline operator_type operation() const exprtk_override
          {
             return Operation::operation();
          }
@@ -15602,47 +18400,54 @@ namespace exprtk
 
       private:
 
-         str_xrox_node(str_xrox_node<T,SType0,SType1,RangePack,Operation>&);
-         str_xrox_node<T,SType0,SType1,RangePack,Operation>& operator=(str_xrox_node<T,SType0,SType1,RangePack,Operation>&);
+         str_xrox_node(const node_type&) exprtk_delete;
+         node_type& operator=(const node_type&) exprtk_delete;
       };
 
       template <typename T, typename SType0, typename SType1, typename RangePack, typename Operation>
-      class str_xoxr_node : public sos_base_node<T>
+      class str_xoxr_node exprtk_final : public sos_base_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
          typedef Operation operation_t;
+         typedef str_xoxr_node<T,SType0,SType1,RangePack,Operation> node_type;
 
          // string op string range node
          explicit str_xoxr_node(SType0 p0, SType1 p1, RangePack rp1)
-         : s0_ (p0 ),
-           s1_ (p1 ),
-           rp1_(rp1)
+         : s0_ (p0 )
+         , s1_ (p1 )
+         , rp1_(rp1)
          {}
 
-        ~str_xoxr_node() override
+        ~str_xoxr_node() exprtk_override
          {
             rp1_.free();
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             std::size_t r0 = 0;
             std::size_t r1 = 0;
 
             if (rp1_(r0, r1, s1_.size()))
-               return Operation::process(s0_, s1_.substr(r0, (r1 - r0) + 1));
+            {
+               return Operation::process
+                      (
+                         s0_,
+                         s1_.substr(r0, (r1 - r0) + 1)
+                      );
+            }
             else
                return T(0);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return Operation::type();
          }
 
-         inline operator_type operation() const override
+         inline operator_type operation() const exprtk_override
          {
             return Operation::operation();
          }
@@ -15665,33 +18470,34 @@ namespace exprtk
 
       private:
 
-         str_xoxr_node(str_xoxr_node<T,SType0,SType1,RangePack,Operation>&);
-         str_xoxr_node<T,SType0,SType1,RangePack,Operation>& operator=(str_xoxr_node<T,SType0,SType1,RangePack,Operation>&);
+         str_xoxr_node(const node_type&) exprtk_delete;
+         node_type& operator=(const node_type&) exprtk_delete;
       };
 
       template <typename T, typename SType0, typename SType1, typename RangePack, typename Operation>
-      class str_xroxr_node : public sos_base_node<T>
+      class str_xroxr_node exprtk_final : public sos_base_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
          typedef Operation operation_t;
+         typedef str_xroxr_node<T,SType0,SType1,RangePack,Operation> node_type;
 
          // string-range op string-range node
          explicit str_xroxr_node(SType0 p0, SType1 p1, RangePack rp0, RangePack rp1)
-         : s0_ (p0 ),
-           s1_ (p1 ),
-           rp0_(rp0),
-           rp1_(rp1)
+         : s0_ (p0 )
+         , s1_ (p1 )
+         , rp0_(rp0)
+         , rp1_(rp1)
          {}
 
-        ~str_xroxr_node() override
+        ~str_xroxr_node() exprtk_override
          {
             rp0_.free();
             rp1_.free();
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             std::size_t r0_0 = 0;
             std::size_t r0_1 = 0;
@@ -15703,21 +18509,22 @@ namespace exprtk
                  rp1_(r0_1, r1_1, s1_.size())
                )
             {
-               return Operation::process(
-                                          s0_.substr(r0_0, (r1_0 - r0_0) + 1),
-                                          s1_.substr(r0_1, (r1_1 - r0_1) + 1)
-                                        );
+               return Operation::process
+                      (
+                         s0_.substr(r0_0, (r1_0 - r0_0) + 1),
+                         s1_.substr(r0_1, (r1_1 - r0_1) + 1)
+                      );
             }
             else
                return T(0);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return Operation::type();
          }
 
-         inline operator_type operation() const override
+         inline operator_type operation() const exprtk_override
          {
             return Operation::operation();
          }
@@ -15741,39 +18548,42 @@ namespace exprtk
 
       private:
 
-         str_xroxr_node(str_xroxr_node<T,SType0,SType1,RangePack,Operation>&);
-         str_xroxr_node<T,SType0,SType1,RangePack,Operation>& operator=(str_xroxr_node<T,SType0,SType1,RangePack,Operation>&);
+         str_xroxr_node(const node_type&) exprtk_delete;
+         node_type& operator=(const node_type&) exprtk_delete;
       };
 
       template <typename T, typename Operation>
-      class str_sogens_node : public binary_node<T>
+      class str_sogens_node exprtk_final : public binary_node<T>
       {
       public:
 
          typedef expression_node <T>* expression_ptr;
-         typedef string_base_node<T>*   str_base_ptr;
-         typedef range_pack      <T>         range_t;
-         typedef range_t*                  range_ptr;
-         typedef range_interface<T>         irange_t;
-         typedef irange_t*                irange_ptr;
+         typedef string_base_node<T>* str_base_ptr;
+         typedef range_pack      <T>  range_t;
+         typedef range_t*             range_ptr;
+         typedef range_interface <T>  irange_t;
+         typedef irange_t*            irange_ptr;
+
+         using binary_node<T>::branch;
 
          str_sogens_node(const operator_type& opr,
                          expression_ptr branch0,
                          expression_ptr branch1)
-         : binary_node<T>(opr, branch0, branch1),
-           str0_base_ptr_ (0),
-           str1_base_ptr_ (0),
-           str0_range_ptr_(0),
-           str1_range_ptr_(0)
+         : binary_node<T>(opr, branch0, branch1)
+         , str0_base_ptr_ (0)
+         , str1_base_ptr_ (0)
+         , str0_range_ptr_(0)
+         , str1_range_ptr_(0)
+         , initialised_   (false)
          {
-            if (is_generally_string_node(binary_node<T>::branch_[0].first))
+            if (is_generally_string_node(branch(0)))
             {
-               str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
+               str0_base_ptr_ = dynamic_cast<str_base_ptr>(branch(0));
 
                if (0 == str0_base_ptr_)
                   return;
 
-               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[0].first);
+               irange_ptr range = dynamic_cast<irange_ptr>(branch(0));
 
                if (0 == range)
                   return;
@@ -15781,105 +18591,108 @@ namespace exprtk
                str0_range_ptr_ = &(range->range_ref());
             }
 
-            if (is_generally_string_node(binary_node<T>::branch_[1].first))
+            if (is_generally_string_node(branch(1)))
             {
-               str1_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[1].first);
+               str1_base_ptr_ = dynamic_cast<str_base_ptr>(branch(1));
 
                if (0 == str1_base_ptr_)
                   return;
 
-               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[1].first);
+               irange_ptr range = dynamic_cast<irange_ptr>(branch(1));
 
                if (0 == range)
                   return;
 
                str1_range_ptr_ = &(range->range_ref());
             }
+
+            initialised_ =
+               str0_base_ptr_  &&
+               str1_base_ptr_  &&
+               str0_range_ptr_ &&
+               str1_range_ptr_;
+
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            if (
-                 str0_base_ptr_  &&
-                 str1_base_ptr_  &&
-                 str0_range_ptr_ &&
-                 str1_range_ptr_
-               )
-            {
-               binary_node<T>::branch_[0].first->value();
-               binary_node<T>::branch_[1].first->value();
+            branch(0)->value();
+            branch(1)->value();
 
-               std::size_t str0_r0 = 0;
-               std::size_t str0_r1 = 0;
+            std::size_t str0_r0 = 0;
+            std::size_t str0_r1 = 0;
 
-               std::size_t str1_r0 = 0;
-               std::size_t str1_r1 = 0;
+            std::size_t str1_r0 = 0;
+            std::size_t str1_r1 = 0;
 
-               const range_t& range0 = (*str0_range_ptr_);
-               const range_t& range1 = (*str1_range_ptr_);
+            const range_t& range0 = (*str0_range_ptr_);
+            const range_t& range1 = (*str1_range_ptr_);
 
-               if (
-                    range0(str0_r0, str0_r1, str0_base_ptr_->size()) &&
-                    range1(str1_r0, str1_r1, str1_base_ptr_->size())
-                  )
-               {
-                  return Operation::process(
-                                             str0_base_ptr_->str().substr(str0_r0,(str0_r1 - str0_r0) + 1),
-                                             str1_base_ptr_->str().substr(str1_r0,(str1_r1 - str1_r0) + 1)
-                                           );
-               }
+            if (
+                 range0(str0_r0, str0_r1, str0_base_ptr_->size()) &&
+                 range1(str1_r0, str1_r1, str1_base_ptr_->size())
+               )
+            {
+               return Operation::process
+                      (
+                         str0_base_ptr_->str().substr(str0_r0,(str0_r1 - str0_r0)),
+                         str1_base_ptr_->str().substr(str1_r0,(str1_r1 - str1_r0))
+                      );
             }
 
             return std::numeric_limits<T>::quiet_NaN();
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return Operation::type();
          }
 
-         inline operator_type operation() const
+         inline bool valid() const exprtk_override
          {
-            return Operation::operation();
+            return initialised_;
          }
 
       private:
 
-         str_sogens_node(str_sogens_node<T,Operation>&);
-         str_sogens_node<T,Operation>& operator=(str_sogens_node<T,Operation>&);
+         str_sogens_node(const str_sogens_node<T,Operation>&) exprtk_delete;
+         str_sogens_node<T,Operation>& operator=(const str_sogens_node<T,Operation>&) exprtk_delete;
 
          str_base_ptr str0_base_ptr_;
          str_base_ptr str1_base_ptr_;
          range_ptr    str0_range_ptr_;
          range_ptr    str1_range_ptr_;
+         bool         initialised_;
       };
 
       template <typename T, typename SType0, typename SType1, typename SType2, typename Operation>
-      class sosos_node : public sosos_base_node<T>
+      class sosos_node exprtk_final : public sosos_base_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
          typedef Operation operation_t;
+         typedef sosos_node<T, SType0, SType1, SType2, Operation> node_type;
 
-         // variable op variable node
+         // string op string op string node
          explicit sosos_node(SType0 p0, SType1 p1, SType2 p2)
-         : s0_(p0),
-           s1_(p1),
-           s2_(p2)
+         : s0_(p0)
+         , s1_(p1)
+         , s2_(p2)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            return Operation::process(s0_,s1_,s2_);
+            return Operation::process(s0_, s1_, s2_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return Operation::type();
          }
 
-         inline operator_type operation() const override
+         inline operator_type operation() const exprtk_override
          {
             return Operation::operation();
          }
@@ -15907,13 +18720,13 @@ namespace exprtk
 
       private:
 
-         sosos_node(sosos_node<T,SType0,SType1,SType2,Operation>&);
-         sosos_node<T,SType0,SType1,SType2,Operation>& operator=(sosos_node<T,SType0,SType1,SType2,Operation>&);
+         sosos_node(const node_type&) exprtk_delete;
+         node_type& operator=(const node_type&) exprtk_delete;
       };
       #endif
 
       template <typename T, typename PowOp>
-      class ipow_node : public expression_node<T>
+      class ipow_node exprtk_final: public expression_node<T>
       {
       public:
 
@@ -15924,26 +18737,26 @@ namespace exprtk
          : v_(v)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return PowOp::result(v_);
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_ipow;
          }
 
       private:
 
-         ipow_node(const ipow_node<T,PowOp>&);
-         ipow_node<T,PowOp>& operator=(const ipow_node<T,PowOp>&);
+         ipow_node(const ipow_node<T,PowOp>&) exprtk_delete;
+         ipow_node<T,PowOp>& operator=(const ipow_node<T,PowOp>&) exprtk_delete;
 
          const T& v_;
       };
 
       template <typename T, typename PowOp>
-      class bipow_node : public expression_node<T>
+      class bipow_node exprtk_final : public expression_node<T>
       {
       public:
 
@@ -15954,39 +18767,44 @@ namespace exprtk
          explicit bipow_node(expression_ptr branch)
          {
             construct_branch_pair(branch_, branch);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(branch_.first);
             return PowOp::result(branch_.first->value());
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_ipow;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
+         {
+            return branch_.first && branch_.first->valid();
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
             expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
 
-         bipow_node(const bipow_node<T,PowOp>&);
-         bipow_node<T,PowOp>& operator=(const bipow_node<T,PowOp>&);
+         bipow_node(const bipow_node<T,PowOp>&) exprtk_delete;
+         bipow_node<T,PowOp>& operator=(const bipow_node<T,PowOp>&) exprtk_delete;
 
          branch_t branch_;
       };
 
       template <typename T, typename PowOp>
-      class ipowinv_node : public expression_node<T>
+      class ipowinv_node exprtk_final : public expression_node<T>
       {
       public:
 
@@ -15997,26 +18815,26 @@ namespace exprtk
          : v_(v)
          {}
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
             return (T(1) / PowOp::result(v_));
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_ipowinv;
          }
 
       private:
 
-         ipowinv_node(const ipowinv_node<T,PowOp>&);
-         ipowinv_node<T,PowOp>& operator=(const ipowinv_node<T,PowOp>&);
+         ipowinv_node(const ipowinv_node<T,PowOp>&) exprtk_delete;
+         ipowinv_node<T,PowOp>& operator=(const ipowinv_node<T,PowOp>&) exprtk_delete;
 
          const T& v_;
       };
 
       template <typename T, typename PowOp>
-      class bipowninv_node : public expression_node<T>
+      class bipowinv_node exprtk_final : public expression_node<T>
       {
       public:
 
@@ -16024,36 +18842,41 @@ namespace exprtk
          typedef std::pair<expression_ptr, bool> branch_t;
          typedef PowOp operation_t;
 
-         explicit bipowninv_node(expression_ptr branch)
+         explicit bipowinv_node(expression_ptr branch)
          {
             construct_branch_pair(branch_, branch);
+            assert(valid());
          }
 
-         inline T value() const override
+         inline T value() const exprtk_override
          {
-            assert(branch_.first);
             return (T(1) / PowOp::result(branch_.first->value()));
          }
 
-         inline typename expression_node<T>::node_type type() const override
+         inline typename expression_node<T>::node_type type() const exprtk_override
          {
             return expression_node<T>::e_ipowinv;
          }
 
-         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) override
+         inline bool valid() const exprtk_override
+         {
+            return branch_.first && branch_.first->valid();
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list) exprtk_override
          {
-            expression_node<T>::ndb_t::template collect<>(branch_, node_delete_list);
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
 
-         std::size_t node_depth() const override
+         std::size_t node_depth() const exprtk_override
          {
             return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
 
-         bipowninv_node(const bipowninv_node<T,PowOp>&);
-         bipowninv_node<T,PowOp>& operator=(const bipowninv_node<T,PowOp>&);
+         bipowinv_node(const bipowinv_node<T,PowOp>&) exprtk_delete;
+         bipowinv_node<T,PowOp>& operator=(const bipowinv_node<T,PowOp>&) exprtk_delete;
 
          branch_t branch_;
       };
@@ -16197,6 +19020,46 @@ namespace exprtk
          return false;
       }
 
+      template <typename T>
+      inline bool is_loop_node(const expression_node<T>* node)
+      {
+         if (node)
+         {
+            switch (node->type())
+            {
+               case expression_node<T>::e_for    :
+               case expression_node<T>::e_repeat :
+               case expression_node<T>::e_while  : return true;
+               default                           : return false;
+            }
+         }
+
+         return false;
+      }
+
+      template <typename T>
+      inline bool is_block_node(const expression_node<T>* node)
+      {
+         if (node)
+         {
+            if (is_loop_node(node))
+            {
+               return true;
+            }
+
+            switch (node->type())
+            {
+               case expression_node<T>::e_conditional :
+               case expression_node<T>::e_mswitch     :
+               case expression_node<T>::e_switch      :
+               case expression_node<T>::e_vararg      : return true;
+               default                                : return false;
+            }
+         }
+
+         return false;
+      }
+
       class node_allocator
       {
       public:
@@ -16585,61 +19448,61 @@ namespace exprtk
 
       inline void load_operations_map(std::multimap<std::string,details::base_operation_t,details::ilesscompare>& m)
       {
-         #define register_op(Symbol,Type,Args)                                               \
+         #define register_op(Symbol, Type, Args)                                             \
          m.insert(std::make_pair(std::string(Symbol),details::base_operation_t(Type,Args))); \
 
-         register_op(      "abs", e_abs     , 1)
-         register_op(     "acos", e_acos    , 1)
-         register_op(    "acosh", e_acosh   , 1)
-         register_op(     "asin", e_asin    , 1)
-         register_op(    "asinh", e_asinh   , 1)
-         register_op(     "atan", e_atan    , 1)
-         register_op(    "atanh", e_atanh   , 1)
-         register_op(     "ceil", e_ceil    , 1)
-         register_op(      "cos", e_cos     , 1)
-         register_op(     "cosh", e_cosh    , 1)
-         register_op(      "exp", e_exp     , 1)
-         register_op(    "expm1", e_expm1   , 1)
-         register_op(    "floor", e_floor   , 1)
-         register_op(      "log", e_log     , 1)
-         register_op(    "log10", e_log10   , 1)
-         register_op(     "log2", e_log2    , 1)
-         register_op(    "log1p", e_log1p   , 1)
-         register_op(    "round", e_round   , 1)
-         register_op(      "sin", e_sin     , 1)
-         register_op(     "sinc", e_sinc    , 1)
-         register_op(     "sinh", e_sinh    , 1)
-         register_op(      "sec", e_sec     , 1)
-         register_op(      "csc", e_csc     , 1)
-         register_op(     "sqrt", e_sqrt    , 1)
-         register_op(      "tan", e_tan     , 1)
-         register_op(     "tanh", e_tanh    , 1)
-         register_op(      "cot", e_cot     , 1)
-         register_op(  "rad2deg", e_r2d     , 1)
-         register_op(  "deg2rad", e_d2r     , 1)
-         register_op( "deg2grad", e_d2g     , 1)
-         register_op( "grad2deg", e_g2d     , 1)
-         register_op(      "sgn", e_sgn     , 1)
-         register_op(      "not", e_notl    , 1)
-         register_op(      "erf", e_erf     , 1)
-         register_op(     "erfc", e_erfc    , 1)
-         register_op(     "ncdf", e_ncdf    , 1)
-         register_op(     "frac", e_frac    , 1)
-         register_op(    "trunc", e_trunc   , 1)
-         register_op(    "atan2", e_atan2   , 2)
-         register_op(      "mod", e_mod     , 2)
-         register_op(     "logn", e_logn    , 2)
-         register_op(      "pow", e_pow     , 2)
-         register_op(     "root", e_root    , 2)
-         register_op(   "roundn", e_roundn  , 2)
-         register_op(    "equal", e_equal   , 2)
-         register_op("not_equal", e_nequal  , 2)
-         register_op(    "hypot", e_hypot   , 2)
-         register_op(      "shr", e_shr     , 2)
-         register_op(      "shl", e_shl     , 2)
-         register_op(    "clamp", e_clamp   , 3)
-         register_op(   "iclamp", e_iclamp  , 3)
-         register_op(  "inrange", e_inrange , 3)
+         register_op("abs"       , e_abs     , 1)
+         register_op("acos"      , e_acos    , 1)
+         register_op("acosh"     , e_acosh   , 1)
+         register_op("asin"      , e_asin    , 1)
+         register_op("asinh"     , e_asinh   , 1)
+         register_op("atan"      , e_atan    , 1)
+         register_op("atanh"     , e_atanh   , 1)
+         register_op("ceil"      , e_ceil    , 1)
+         register_op("cos"       , e_cos     , 1)
+         register_op("cosh"      , e_cosh    , 1)
+         register_op("exp"       , e_exp     , 1)
+         register_op("expm1"     , e_expm1   , 1)
+         register_op("floor"     , e_floor   , 1)
+         register_op("log"       , e_log     , 1)
+         register_op("log10"     , e_log10   , 1)
+         register_op("log2"      , e_log2    , 1)
+         register_op("log1p"     , e_log1p   , 1)
+         register_op("round"     , e_round   , 1)
+         register_op("sin"       , e_sin     , 1)
+         register_op("sinc"      , e_sinc    , 1)
+         register_op("sinh"      , e_sinh    , 1)
+         register_op("sec"       , e_sec     , 1)
+         register_op("csc"       , e_csc     , 1)
+         register_op("sqrt"      , e_sqrt    , 1)
+         register_op("tan"       , e_tan     , 1)
+         register_op("tanh"      , e_tanh    , 1)
+         register_op("cot"       , e_cot     , 1)
+         register_op("rad2deg"   , e_r2d     , 1)
+         register_op("deg2rad"   , e_d2r     , 1)
+         register_op("deg2grad"  , e_d2g     , 1)
+         register_op("grad2deg"  , e_g2d     , 1)
+         register_op("sgn"       , e_sgn     , 1)
+         register_op("not"       , e_notl    , 1)
+         register_op("erf"       , e_erf     , 1)
+         register_op("erfc"      , e_erfc    , 1)
+         register_op("ncdf"      , e_ncdf    , 1)
+         register_op("frac"      , e_frac    , 1)
+         register_op("trunc"     , e_trunc   , 1)
+         register_op("atan2"     , e_atan2   , 2)
+         register_op("mod"       , e_mod     , 2)
+         register_op("logn"      , e_logn    , 2)
+         register_op("pow"       , e_pow     , 2)
+         register_op("root"      , e_root    , 2)
+         register_op("roundn"    , e_roundn  , 2)
+         register_op("equal"     , e_equal   , 2)
+         register_op("not_equal" , e_nequal  , 2)
+         register_op("hypot"     , e_hypot   , 2)
+         register_op("shr"       , e_shr     , 2)
+         register_op("shl"       , e_shl     , 2)
+         register_op("clamp"     , e_clamp   , 3)
+         register_op("iclamp"    , e_iclamp  , 3)
+         register_op("inrange"   , e_inrange , 3)
          #undef register_op
       }
 
@@ -16650,10 +19513,10 @@ namespace exprtk
    public:
 
       function_traits()
-      : allow_zero_parameters_(false),
-        has_side_effects_(true),
-        min_num_args_(0),
-        max_num_args_(std::numeric_limits<std::size_t>::max())
+      : allow_zero_parameters_(false)
+      , has_side_effects_(true)
+      , min_num_args_(0)
+      , max_num_args_(std::numeric_limits<std::size_t>::max())
       {}
 
       inline bool& allow_zero_parameters()
@@ -16737,7 +19600,7 @@ namespace exprtk
       : param_count(pc)
       {}
 
-      virtual ~ifunction() = default;
+      virtual ~ifunction() exprtk_default;
 
       #define empty_method_body(N)                   \
       {                                              \
@@ -16828,7 +19691,7 @@ namespace exprtk
    {
    public:
 
-      virtual ~ivararg_function() = default;
+      virtual ~ivararg_function() exprtk_default;
 
       inline virtual T operator() (const std::vector<T>&)
       {
@@ -16853,12 +19716,12 @@ namespace exprtk
       typedef type_store<T> generic_type;
       typedef typename generic_type::parameter_list parameter_list_t;
 
-      igeneric_function(const std::string& param_seq = "", const return_type rtr_type = e_rtrn_scalar)
-      : parameter_sequence(param_seq),
-        rtrn_type(rtr_type)
+      explicit igeneric_function(const std::string& param_seq = "", const return_type rtr_type = e_rtrn_scalar)
+      : parameter_sequence(param_seq)
+      , rtrn_type(rtr_type)
       {}
 
-      virtual ~igeneric_function() = default;
+      virtual ~igeneric_function() exprtk_default;
 
       #define igeneric_function_empty_body(N)        \
       {                                              \
@@ -16882,10 +19745,49 @@ namespace exprtk
       inline virtual T operator() (const std::size_t&, std::string&, parameter_list_t)
       igeneric_function_empty_body(4)
 
+      #undef igeneric_function_empty_body
+
       std::string parameter_sequence;
       return_type rtrn_type;
    };
 
+   #ifndef exprtk_disable_string_capabilities
+   template <typename T>
+   class stringvar_base
+   {
+   public:
+
+      typedef typename details::stringvar_node<T> stringvar_node_t;
+
+      stringvar_base(const std::string& name, stringvar_node_t* svn)
+      : name_(name)
+      , string_varnode_(svn)
+      {}
+
+      bool valid() const
+      {
+         return !name_.empty() && (0 != string_varnode_);
+      }
+
+      std::string name() const
+      {
+         assert(string_varnode_);
+         return name_;
+      }
+
+      void rebase(std::string& s)
+      {
+         assert(string_varnode_);
+         string_varnode_->rebase(s);
+      }
+
+   private:
+
+      std::string name_;
+      stringvar_node_t* string_varnode_;
+   };
+   #endif
+
    template <typename T> class parser;
    template <typename T> class expression_helper;
 
@@ -16894,71 +19796,78 @@ namespace exprtk
    {
    public:
 
-      typedef T (*ff00_functor)();
-      typedef T (*ff01_functor)(T);
-      typedef T (*ff02_functor)(T, T);
-      typedef T (*ff03_functor)(T, T, T);
-      typedef T (*ff04_functor)(T, T, T, T);
-      typedef T (*ff05_functor)(T, T, T, T, T);
-      typedef T (*ff06_functor)(T, T, T, T, T, T);
-      typedef T (*ff07_functor)(T, T, T, T, T, T, T);
-      typedef T (*ff08_functor)(T, T, T, T, T, T, T, T);
-      typedef T (*ff09_functor)(T, T, T, T, T, T, T, T, T);
-      typedef T (*ff10_functor)(T, T, T, T, T, T, T, T, T, T);
-      typedef T (*ff11_functor)(T, T, T, T, T, T, T, T, T, T, T);
-      typedef T (*ff12_functor)(T, T, T, T, T, T, T, T, T, T, T, T);
-      typedef T (*ff13_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T);
-      typedef T (*ff14_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T, T);
-      typedef T (*ff15_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T);
+     enum symtab_mutability_type
+     {
+       e_unknown   = 0,
+       e_mutable   = 1,
+       e_immutable = 2
+     };
+
+     typedef T (*ff00_functor)();
+     typedef T (*ff01_functor)(T);
+     typedef T (*ff02_functor)(T, T);
+     typedef T (*ff03_functor)(T, T, T);
+     typedef T (*ff04_functor)(T, T, T, T);
+     typedef T (*ff05_functor)(T, T, T, T, T);
+     typedef T (*ff06_functor)(T, T, T, T, T, T);
+     typedef T (*ff07_functor)(T, T, T, T, T, T, T);
+     typedef T (*ff08_functor)(T, T, T, T, T, T, T, T);
+     typedef T (*ff09_functor)(T, T, T, T, T, T, T, T, T);
+     typedef T (*ff10_functor)(T, T, T, T, T, T, T, T, T, T);
+     typedef T (*ff11_functor)(T, T, T, T, T, T, T, T, T, T, T);
+     typedef T (*ff12_functor)(T, T, T, T, T, T, T, T, T, T, T, T);
+     typedef T (*ff13_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T);
+     typedef T (*ff14_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T, T);
+     typedef T (*ff15_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T);
 
    protected:
 
-       struct freefunc00 : public exprtk::ifunction<T>
+       struct freefunc00 exprtk_final : public exprtk::ifunction<T>
        {
           using exprtk::ifunction<T>::operator();
 
           explicit freefunc00(ff00_functor ff) : exprtk::ifunction<T>(0), f(ff) {}
-          inline T operator() ()
+          inline T operator() () exprtk_override
           { return f(); }
           ff00_functor f;
        };
 
-      struct freefunc01 : public exprtk::ifunction<T>
+      struct freefunc01 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc01(ff01_functor ff) : exprtk::ifunction<T>(1), f(ff) {}
-         inline T operator() (const T& v0)
+         inline T operator() (const T& v0) exprtk_override
          { return f(v0); }
          ff01_functor f;
       };
 
-      struct freefunc02 : public exprtk::ifunction<T>
+      struct freefunc02 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc02(ff02_functor ff) : exprtk::ifunction<T>(2), f(ff) {}
-         inline T operator() (const T& v0, const T& v1)
+         inline T operator() (const T& v0, const T& v1) exprtk_override
          { return f(v0, v1); }
          ff02_functor f;
       };
 
-      struct freefunc03 : public exprtk::ifunction<T>
+      struct freefunc03 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc03(ff03_functor ff) : exprtk::ifunction<T>(3), f(ff) {}
-         inline T operator() (const T& v0, const T& v1, const T& v2)
+         inline T operator() (const T& v0, const T& v1, const T& v2) exprtk_override
          { return f(v0, v1, v2); }
          ff03_functor f;
       };
 
-      struct freefunc04 : public exprtk::ifunction<T>
+      struct freefunc04 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc04(ff04_functor ff) : exprtk::ifunction<T>(4), f(ff) {}
-         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3)
+         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3) exprtk_override
          { return f(v0, v1, v2, v3); }
          ff04_functor f;
       };
@@ -16968,120 +19877,120 @@ namespace exprtk
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc05(ff05_functor ff) : exprtk::ifunction<T>(5), f(ff) {}
-         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4)
+         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4) exprtk_override
          { return f(v0, v1, v2, v3, v4); }
          ff05_functor f;
       };
 
-      struct freefunc06 : public exprtk::ifunction<T>
+      struct freefunc06 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc06(ff06_functor ff) : exprtk::ifunction<T>(6), f(ff) {}
-         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, const T& v5)
+         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, const T& v5) exprtk_override
          { return f(v0, v1, v2, v3, v4, v5); }
          ff06_functor f;
       };
 
-      struct freefunc07 : public exprtk::ifunction<T>
+      struct freefunc07 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc07(ff07_functor ff) : exprtk::ifunction<T>(7), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
-                              const T& v5, const T& v6)
+                              const T& v5, const T& v6) exprtk_override
          { return f(v0, v1, v2, v3, v4, v5, v6); }
          ff07_functor f;
       };
 
-      struct freefunc08 : public exprtk::ifunction<T>
+      struct freefunc08 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc08(ff08_functor ff) : exprtk::ifunction<T>(8), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
-                              const T& v5, const T& v6, const T& v7)
+                              const T& v5, const T& v6, const T& v7) exprtk_override
          { return f(v0, v1, v2, v3, v4, v5, v6, v7); }
          ff08_functor f;
       };
 
-      struct freefunc09 : public exprtk::ifunction<T>
+      struct freefunc09 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc09(ff09_functor ff) : exprtk::ifunction<T>(9), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
-                              const T& v5, const T& v6, const T& v7, const T& v8)
+                              const T& v5, const T& v6, const T& v7, const T& v8) exprtk_override
          { return f(v0, v1, v2, v3, v4, v5, v6, v7, v8); }
          ff09_functor f;
       };
 
-      struct freefunc10 : public exprtk::ifunction<T>
+      struct freefunc10 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc10(ff10_functor ff) : exprtk::ifunction<T>(10), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
-                              const T& v5, const T& v6, const T& v7, const T& v8, const T& v9)
+                              const T& v5, const T& v6, const T& v7, const T& v8, const T& v9) exprtk_override
          { return f(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); }
          ff10_functor f;
       };
 
-      struct freefunc11 : public exprtk::ifunction<T>
+      struct freefunc11 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc11(ff11_functor ff) : exprtk::ifunction<T>(11), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
-                              const T& v5, const T& v6, const T& v7, const T& v8, const T& v9, const T& v10)
+                              const T& v5, const T& v6, const T& v7, const T& v8, const T& v9, const T& v10) exprtk_override
          { return f(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10); }
          ff11_functor f;
       };
 
-      struct freefunc12 : public exprtk::ifunction<T>
+      struct freefunc12 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc12(ff12_functor ff) : exprtk::ifunction<T>(12), f(ff) {}
          inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04,
                               const T& v05, const T& v06, const T& v07, const T& v08, const T& v09,
-                              const T& v10, const T& v11)
+                              const T& v10, const T& v11) exprtk_override
          { return f(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11); }
          ff12_functor f;
       };
 
-      struct freefunc13 : public exprtk::ifunction<T>
+      struct freefunc13 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc13(ff13_functor ff) : exprtk::ifunction<T>(13), f(ff) {}
          inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04,
                               const T& v05, const T& v06, const T& v07, const T& v08, const T& v09,
-                              const T& v10, const T& v11, const T& v12)
+                              const T& v10, const T& v11, const T& v12) exprtk_override
          { return f(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12); }
          ff13_functor f;
       };
 
-      struct freefunc14 : public exprtk::ifunction<T>
+      struct freefunc14 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc14(ff14_functor ff) : exprtk::ifunction<T>(14), f(ff) {}
          inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04,
                               const T& v05, const T& v06, const T& v07, const T& v08, const T& v09,
-                              const T& v10, const T& v11, const T& v12, const T& v13)
+                              const T& v10, const T& v11, const T& v12, const T& v13) exprtk_override
          { return f(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12, v13); }
          ff14_functor f;
       };
 
-      struct freefunc15 : public exprtk::ifunction<T>
+      struct freefunc15 exprtk_final : public exprtk::ifunction<T>
       {
          using exprtk::ifunction<T>::operator();
 
          explicit freefunc15(ff15_functor ff) : exprtk::ifunction<T>(15), f(ff) {}
          inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04,
                               const T& v05, const T& v06, const T& v07, const T& v08, const T& v09,
-                              const T& v10, const T& v11, const T& v12, const T& v13, const T& v14)
+                              const T& v10, const T& v11, const T& v12, const T& v13, const T& v14) exprtk_override
          { return f(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12, v13, v14); }
          ff15_functor f;
       };
@@ -17274,19 +20183,19 @@ namespace exprtk
                       (symbol_name, v, is_const);
          }
 
-         inline bool add(const std::string& symbol_name, RawType& t, const bool is_const = false)
+         inline bool add(const std::string& symbol_name, RawType& t_, const bool is_const = false)
          {
             struct tie
             {
-               static inline std::pair<bool,variable_node_t*> make(T& t,const bool is_const = false)
+               static inline std::pair<bool,variable_node_t*> make(T& t, const bool is_constant = false)
                {
-                  return std::make_pair(is_const, new variable_node_t(t));
+                  return std::make_pair(is_constant, new variable_node_t(t));
                }
 
                #ifndef exprtk_disable_string_capabilities
-               static inline std::pair<bool,stringvar_node_t*> make(std::string& t,const bool is_const = false)
+               static inline std::pair<bool,stringvar_node_t*> make(std::string& t, const bool is_constant = false)
                {
-                  return std::make_pair(is_const, new stringvar_node_t(t));
+                  return std::make_pair(is_constant, new stringvar_node_t(t));
                }
                #endif
 
@@ -17295,9 +20204,9 @@ namespace exprtk
                   return std::make_pair(is_constant,&t);
                }
 
-               static inline std::pair<bool,vararg_function_t*> make(vararg_function_t& t, const bool is_const = false)
+               static inline std::pair<bool,vararg_function_t*> make(vararg_function_t& t, const bool is_constant = false)
                {
-                  return std::make_pair(is_const,&t);
+                  return std::make_pair(is_constant,&t);
                }
 
                static inline std::pair<bool,generic_function_t*> make(generic_function_t& t, const bool is_constant = false)
@@ -17310,7 +20219,7 @@ namespace exprtk
 
             if (map.end() == itr)
             {
-               map[symbol_name] = tie::make(t,is_const);
+               map[symbol_name] = tie::make(t_,is_const);
                ++size;
             }
 
@@ -17341,7 +20250,7 @@ namespace exprtk
          {
             static inline bool test(const variable_node_t* p, const void* ptr)
             {
-               exprtk_debug(("ptr_match::test() - %p <--> %p\n",(void*)(&(p->ref())),ptr));
+               exprtk_debug(("ptr_match::test() - %p <--> %p\n", reinterpret_cast<const void*>(&(p->ref())), ptr));
                return (&(p->ref()) == ptr);
             }
          };
@@ -17549,13 +20458,15 @@ namespace exprtk
          };
 
          control_block()
-         : ref_count(1),
-           data_(st_data::create())
+         : ref_count(1)
+         , data_(st_data::create())
+         , mutability_(e_mutable)
          {}
 
          explicit control_block(st_data* data)
-         : ref_count(1),
-           data_(data)
+         : ref_count(1)
+         , data_(data)
+         , mutability_(e_mutable)
          {}
 
         ~control_block()
@@ -17591,21 +20502,29 @@ namespace exprtk
             }
          }
 
+         void set_mutability(const symtab_mutability_type mutability)
+         {
+            mutability_ = mutability;
+         }
+
          std::size_t ref_count;
          st_data* data_;
+         symtab_mutability_type mutability_;
       };
 
    public:
 
-      symbol_table()
+      explicit symbol_table(const symtab_mutability_type mutability = e_mutable)
       : control_block_(control_block::create())
       {
+         control_block_->set_mutability(mutability);
          clear();
       }
 
      ~symbol_table()
       {
-         control_block::destroy(control_block_,this);
+         exprtk::details::dump_ptr("~symbol_table", this);
+         control_block::destroy(control_block_, this);
       }
 
       symbol_table(const symbol_table<T>& st)
@@ -17632,6 +20551,11 @@ namespace exprtk
          return (this == &st) || (control_block_ == st.control_block_);
       }
 
+      inline symtab_mutability_type mutability() const
+      {
+         return valid() ? control_block_->mutability_ : e_unknown;
+      }
+
       inline void clear_variables(const bool delete_node = true)
       {
          local_data().variable_store.clear(delete_node);
@@ -17732,6 +20656,24 @@ namespace exprtk
          else
             return local_data().stringvar_store.get(string_name);
       }
+
+      inline stringvar_base<T> get_stringvar_base(const std::string& string_name) const
+      {
+         static stringvar_base<T> null_stringvar_base("",reinterpret_cast<stringvar_ptr>(0));
+         if (!valid())
+            return null_stringvar_base;
+         else if (!valid_symbol(string_name))
+            return null_stringvar_base;
+
+         stringvar_ptr stringvar = local_data().stringvar_store.get(string_name);
+
+         if (0 == stringvar)
+         {
+            return null_stringvar_base;
+         }
+
+         return stringvar_base<T>(string_name,stringvar);
+      }
       #endif
 
       inline function_ptr get_function(const std::string& function_name) const
@@ -18051,6 +20993,34 @@ namespace exprtk
          return false;
       }
 
+      #define exprtk_define_reserved_function(NN)                                                    \
+      inline bool add_reserved_function(const std::string& function_name, ff##NN##_functor function) \
+      {                                                                                              \
+         if (!valid())                                                                               \
+         { return false; }                                                                           \
+         if (!valid_symbol(function_name,false))                                                     \
+         { return false; }                                                                           \
+         if (symbol_exists(function_name,false))                                                     \
+         { return false; }                                                                           \
+                                                                                                     \
+         exprtk::ifunction<T>* ifunc = new freefunc##NN(function);                                   \
+                                                                                                     \
+         local_data().free_function_list_.push_back(ifunc);                                          \
+                                                                                                     \
+         return add_reserved_function(function_name,(*local_data().free_function_list_.back()));     \
+      }                                                                                              \
+
+      exprtk_define_reserved_function(00) exprtk_define_reserved_function(01)
+      exprtk_define_reserved_function(02) exprtk_define_reserved_function(03)
+      exprtk_define_reserved_function(04) exprtk_define_reserved_function(05)
+      exprtk_define_reserved_function(06) exprtk_define_reserved_function(07)
+      exprtk_define_reserved_function(08) exprtk_define_reserved_function(09)
+      exprtk_define_reserved_function(10) exprtk_define_reserved_function(11)
+      exprtk_define_reserved_function(12) exprtk_define_reserved_function(13)
+      exprtk_define_reserved_function(14) exprtk_define_reserved_function(15)
+
+      #undef exprtk_define_reserved_function
+
       template <std::size_t N>
       inline bool add_vector(const std::string& vector_name, T (&v)[N])
       {
@@ -18087,7 +21057,7 @@ namespace exprtk
             return false;
          else if (symbol_exists(vector_name))
             return false;
-         else if (v.empty())
+         else if (0 == v.size())
             return false;
          else
             return local_data().vector_store.add(vector_name,v);
@@ -18101,7 +21071,7 @@ namespace exprtk
             return false;
          else if (symbol_exists(vector_name))
             return false;
-         else if (v.empty())
+         else if (0 == v.size())
             return false;
          else
             return local_data().vector_store.add(vector_name,v);
@@ -18225,12 +21195,48 @@ namespace exprtk
 
       template <typename Allocator,
                 template <typename, typename> class Sequence>
-      inline std::size_t get_vector_list(Sequence<std::string,Allocator>& vlist) const
+      inline std::size_t get_vector_list(Sequence<std::string,Allocator>& vec_list) const
       {
          if (!valid())
             return 0;
          else
-            return local_data().vector_store.get_list(vlist);
+            return local_data().vector_store.get_list(vec_list);
+      }
+
+      template <typename Allocator,
+                template <typename, typename> class Sequence>
+      inline std::size_t get_function_list(Sequence<std::string,Allocator>& function_list) const
+      {
+         if (!valid())
+            return 0;
+
+         std::vector<std::string> function_names;
+         std::size_t count = 0;
+
+         count += local_data().function_store         .get_list(function_names);
+         count += local_data().vararg_function_store  .get_list(function_names);
+         count += local_data().generic_function_store .get_list(function_names);
+         count += local_data().string_function_store  .get_list(function_names);
+         count += local_data().overload_function_store.get_list(function_names);
+
+         std::set<std::string> function_set;
+
+         for (std::size_t i = 0; i < function_names.size(); ++i)
+         {
+            function_set.insert(function_names[i]);
+         }
+
+         std::copy(function_set.begin(), function_set.end(),
+                   std::back_inserter(function_list));
+
+         return count;
+      }
+
+      inline std::vector<std::string> get_function_list() const
+      {
+         std::vector<std::string> result;
+         get_function_list(result);
+         return result;
       }
 
       inline bool symbol_exists(const std::string& symbol_name, const bool check_reserved_symb = true) const
@@ -18421,6 +21427,38 @@ namespace exprtk
          }
       }
 
+      inline void load_variables_from(const symbol_table<T>& st)
+      {
+         std::vector<std::string> name_list;
+
+         st.local_data().variable_store.get_list(name_list);
+
+         if (!name_list.empty())
+         {
+            for (std::size_t i = 0; i < name_list.size(); ++i)
+            {
+               T& variable = st.get_variable(name_list[i])->ref();
+               add_variable(name_list[i], variable);
+            }
+         }
+      }
+
+      inline void load_vectors_from(const symbol_table<T>& st)
+      {
+         std::vector<std::string> name_list;
+
+         st.local_data().vector_store.get_list(name_list);
+
+         if (!name_list.empty())
+         {
+            for (std::size_t i = 0; i < name_list.size(); ++i)
+            {
+               vector_holder_t& vecholder = *st.get_vector(name_list[i]);
+               add_vector(name_list[i], vecholder.data(), vecholder.size());
+            }
+         }
+      }
+
    private:
 
       inline bool valid_symbol(const std::string& symbol, const bool check_reserved_symb = true) const
@@ -18490,7 +21528,7 @@ namespace exprtk
       control_block* control_block_;
 
       friend class parser<T>;
-   };
+   }; // class symbol_table
 
    template <typename T>
    class function_compositor;
@@ -18501,8 +21539,8 @@ namespace exprtk
    private:
 
       typedef details::expression_node<T>*  expression_ptr;
-      typedef details::vector_holder<T>* vector_holder_ptr;
-      typedef std::vector<symbol_table<T> >  symtab_list_t;
+      typedef details::vector_holder<T>*    vector_holder_ptr;
+      typedef std::vector<symbol_table<T> > symtab_list_t;
 
       struct control_block
       {
@@ -18516,18 +21554,33 @@ namespace exprtk
             e_string
          };
 
+         static std::string to_str(data_type dt)
+         {
+            switch(dt)
+            {
+               case e_unknown   : return "e_unknown  ";
+               case e_expr      : return "e_expr"     ;
+               case e_vecholder : return "e_vecholder";
+               case e_data      : return "e_data"     ;
+               case e_vecdata   : return "e_vecdata"  ;
+               case e_string    : return "e_string"   ;
+            }
+
+            return "";
+         }
+
          struct data_pack
          {
             data_pack()
-            : pointer(0),
-              type(e_unknown),
-              size(0)
+            : pointer(0)
+            , type(e_unknown)
+            , size(0)
             {}
 
             data_pack(void* ptr, const data_type dt, const std::size_t sz = 0)
-            : pointer(ptr),
-              type(dt),
-              size(sz)
+            : pointer(ptr)
+            , type(dt)
+            , size(sz)
             {}
 
             void*       pointer;
@@ -18537,21 +21590,22 @@ namespace exprtk
 
          typedef std::vector<data_pack> local_data_list_t;
          typedef results_context<T>     results_context_t;
+         typedef control_block*         cntrl_blck_ptr_t;
 
          control_block()
-         : ref_count(0),
-           expr     (0),
-           results  (0),
-           retinv_null(false),
-           return_invoked(&retinv_null)
+         : ref_count(0)
+         , expr     (0)
+         , results  (0)
+         , retinv_null(false)
+         , return_invoked(&retinv_null)
          {}
 
          explicit control_block(expression_ptr e)
-         : ref_count(1),
-           expr     (e),
-           results  (0),
-           retinv_null(false),
-           return_invoked(&retinv_null)
+         : ref_count(1)
+         , expr     (e)
+         , results  (0)
+         , retinv_null(false)
+         , return_invoked(&retinv_null)
          {}
 
         ~control_block()
@@ -18590,12 +21644,12 @@ namespace exprtk
             delete results;
          }
 
-         static inline control_block* create(expression_ptr e)
+         static inline cntrl_blck_ptr_t create(expression_ptr e)
          {
             return new control_block(e);
          }
 
-         static inline void destroy(control_block*& cntrl_blck)
+         static inline void destroy(cntrl_blck_ptr_t& cntrl_blck)
          {
             if (cntrl_blck)
             {
@@ -18630,8 +21684,8 @@ namespace exprtk
       }
 
       expression(const expression<T>& e)
-      : control_block_    (e.control_block_    ),
-        symbol_table_list_(e.symbol_table_list_)
+      : control_block_    (e.control_block_    )
+      , symbol_table_list_(e.symbol_table_list_)
       {
          control_block_->ref_count++;
       }
@@ -18683,6 +21737,7 @@ namespace exprtk
 
       inline expression<T>& release()
       {
+         exprtk::details::dump_ptr("expression::release", this);
          control_block::destroy(control_block_);
 
          return (*this);
@@ -18716,9 +21771,18 @@ namespace exprtk
          return details::is_true(value());
       }
 
-      inline void register_symbol_table(symbol_table<T>& st)
+      inline bool register_symbol_table(symbol_table<T>& st)
       {
+         for (std::size_t i = 0; i < symbol_table_list_.size(); ++i)
+         {
+            if (st == symbol_table_list_[i])
+            {
+               return false;
+            }
+         }
+
          symbol_table_list_.push_back(st);
+         return true;
       }
 
       inline const symbol_table<T>& get_symbol_table(const std::size_t& index = 0) const
@@ -18731,6 +21795,11 @@ namespace exprtk
          return symbol_table_list_[index];
       }
 
+      std::size_t num_symbol_tables() const
+      {
+         return symbol_table_list_.size();
+      }
+
       typedef results_context<T> results_context_t;
 
       inline const results_context_t& results() const
@@ -18846,7 +21915,7 @@ namespace exprtk
          }
       }
 
-      inline void set_retinvk(bool* retinvk_ptr)
+      inline void set_retinvk(bool* const retinvk_ptr)
       {
          if (control_block_)
          {
@@ -18860,70 +21929,182 @@ namespace exprtk
       friend class parser<T>;
       friend class expression_helper<T>;
       friend class function_compositor<T>;
-   };
+      template <typename TT>
+      friend bool is_valid(const expression<TT>& expr);
+   }; // class expression
 
    template <typename T>
    class expression_helper
    {
    public:
 
-      static inline bool is_constant(const expression<T>& expr)
+      enum node_types
+      {
+         e_literal,
+         e_variable,
+         e_string,
+         e_unary,
+         e_binary,
+         e_function,
+         e_vararg,
+         e_null,
+         e_assert,
+         e_sf3ext,
+         e_sf4ext
+      };
+
+      static inline bool is_literal(const expression<T>& expr)
       {
-         return details::is_constant_node(expr.control_block_->expr);
+         return expr.control_block_ && details::is_literal_node(expr.control_block_->expr);
       }
 
       static inline bool is_variable(const expression<T>& expr)
       {
-         return details::is_variable_node(expr.control_block_->expr);
+         return expr.control_block_ && details::is_variable_node(expr.control_block_->expr);
+      }
+
+      static inline bool is_string(const expression<T>& expr)
+      {
+         return expr.control_block_ && details::is_generally_string_node(expr.control_block_->expr);
       }
 
       static inline bool is_unary(const expression<T>& expr)
       {
-         return details::is_unary_node(expr.control_block_->expr);
+         return expr.control_block_ && details::is_unary_node(expr.control_block_->expr);
       }
 
       static inline bool is_binary(const expression<T>& expr)
       {
-         return details::is_binary_node(expr.control_block_->expr);
+         return expr.control_block_ && details::is_binary_node(expr.control_block_->expr);
       }
 
       static inline bool is_function(const expression<T>& expr)
       {
-         return details::is_function(expr.control_block_->expr);
+         return expr.control_block_ && details::is_function(expr.control_block_->expr);
+      }
+
+      static inline bool is_vararg(const expression<T>& expr)
+      {
+         return expr.control_block_ && details::is_vararg_node(expr.control_block_->expr);
       }
 
       static inline bool is_null(const expression<T>& expr)
       {
-         return details::is_null_node(expr.control_block_->expr);
+         return expr.control_block_ && details::is_null_node(expr.control_block_->expr);
+      }
+
+      static inline bool is_assert(const expression<T>& expr)
+      {
+         return expr.control_block_ && details::is_assert_node(expr.control_block_->expr);
+      }
+
+      static inline bool is_sf3ext(const expression<T>& expr)
+      {
+         return expr.control_block_ && details::is_sf3ext_node(expr.control_block_->expr);
+      }
+
+      static inline bool is_sf4ext(const expression<T>& expr)
+      {
+         return expr.control_block_ && details::is_sf4ext_node(expr.control_block_->expr);
+      }
+
+      static inline bool is_type(const expression<T>& expr, const node_types node_type)
+      {
+         if (0 == expr.control_block_)
+         {
+            return false;
+         }
+
+         switch (node_type)
+         {
+            case e_literal  : return is_literal_node(expr);
+            case e_variable : return is_variable    (expr);
+            case e_string   : return is_string      (expr);
+            case e_unary    : return is_unary       (expr);
+            case e_binary   : return is_binary      (expr);
+            case e_function : return is_function    (expr);
+            case e_null     : return is_null        (expr);
+            case e_assert   : return is_assert      (expr);
+            case e_sf3ext   : return is_sf3ext      (expr);
+            case e_sf4ext   : return is_sf4ext      (expr);
+         };
+
+         return false;
+      }
+
+      static inline bool match_type_sequence(const expression<T>& expr, const std::vector<node_types>& type_seq)
+      {
+         if ((0 == expr.control_block_) || !is_vararg(expr))
+         {
+            return false;
+         }
+
+         typedef details::vararg_node<T, exprtk::details::vararg_multi_op<T> > mo_vararg_t;
+
+         mo_vararg_t* vnode = dynamic_cast<mo_vararg_t*>(expr.control_block_->expr);
+
+         if (
+              (0 == vnode) ||
+              type_seq.empty() ||
+              (vnode->size() < type_seq.size())
+            )
+         {
+            return false;
+         }
+
+         for (std::size_t i = 0; i < type_seq.size(); ++i)
+         {
+            assert((*vnode)[i]);
+
+            switch(type_seq[i])
+            {
+               case e_literal  : { if (details::is_literal_node         ((*vnode)[i])) continue; } break;
+               case e_variable : { if (details::is_variable_node        ((*vnode)[i])) continue; } break;
+               case e_string   : { if (details::is_generally_string_node((*vnode)[i])) continue; } break;
+               case e_unary    : { if (details::is_unary_node           ((*vnode)[i])) continue; } break;
+               case e_binary   : { if (details::is_binary_node          ((*vnode)[i])) continue; } break;
+               case e_function : { if (details::is_function             ((*vnode)[i])) continue; } break;
+               case e_null     : { if (details::is_null_node            ((*vnode)[i])) continue; } break;
+               case e_assert   : { if (details::is_assert_node          ((*vnode)[i])) continue; } break;
+               case e_sf3ext   : { if (details::is_sf3ext_node          ((*vnode)[i])) continue; } break;
+               case e_sf4ext   : { if (details::is_sf4ext_node          ((*vnode)[i])) continue; } break;
+               case e_vararg   : break;
+            }
+
+            return false;
+         }
+
+         return true;
       }
    };
 
    template <typename T>
    inline bool is_valid(const expression<T>& expr)
    {
-      return !expression_helper<T>::is_null(expr);
+      return expr.control_block_ && !expression_helper<T>::is_null(expr);
    }
 
    namespace parser_error
    {
       enum error_mode
       {
-         e_unknown = 0,
-         e_syntax  = 1,
-         e_token   = 2,
-         e_numeric = 4,
-         e_symtab  = 5,
-         e_lexer   = 6,
-         e_helper  = 7,
-         e_parser  = 8
+         e_unknown   = 0,
+         e_syntax    = 1,
+         e_token     = 2,
+         e_numeric   = 4,
+         e_symtab    = 5,
+         e_lexer     = 6,
+         e_synthesis = 7,
+         e_helper    = 8,
+         e_parser    = 9
       };
 
       struct type
       {
          type()
-         : mode(parser_error::e_unknown),
-           line_no  (0),
-           column_no(0)
+         : mode(parser_error::e_unknown)
+         , line_no  (0)
+         , column_no(0)
          {}
 
          lexer::token token;
@@ -18944,7 +22125,7 @@ namespace exprtk
          t.token.type   = lexer::token::e_error;
          t.diagnostic   = diagnostic;
          t.src_location = src_location;
-         exprtk_debug(("%s\n",diagnostic .c_str()));
+         exprtk_debug(("%s\n", diagnostic .c_str()));
          return t;
       }
 
@@ -18954,11 +22135,11 @@ namespace exprtk
                              const std::string& src_location = "")
       {
          type t;
-         t.mode       = mode;
-         t.token      = tk;
-         t.diagnostic = diagnostic;
+         t.mode         = mode;
+         t.token        = tk;
+         t.diagnostic   = diagnostic;
          t.src_location = src_location;
-         exprtk_debug(("%s\n",diagnostic .c_str()));
+         exprtk_debug(("%s\n", diagnostic .c_str()));
          return t;
       }
 
@@ -19045,92 +22226,96 @@ namespace exprtk
 
       enum precedence_level
       {
-         e_level00,
-         e_level01,
-         e_level02,
-         e_level03,
-         e_level04,
-         e_level05,
-         e_level06,
-         e_level07,
-         e_level08,
-         e_level09,
-         e_level10,
-         e_level11,
-         e_level12,
-         e_level13,
-         e_level14
-      };
-
-      typedef const T&                                               cref_t;
-      typedef const T                                               const_t;
-      typedef ifunction                <T>                                F;
-      typedef ivararg_function         <T>                              VAF;
-      typedef igeneric_function        <T>                               GF;
-      typedef ifunction                <T>                      ifunction_t;
-      typedef ivararg_function         <T>               ivararg_function_t;
-      typedef igeneric_function        <T>              igeneric_function_t;
-      typedef details::expression_node <T>                expression_node_t;
-      typedef details::literal_node    <T>                   literal_node_t;
-      typedef details::unary_node      <T>                     unary_node_t;
-      typedef details::binary_node     <T>                    binary_node_t;
-      typedef details::trinary_node    <T>                   trinary_node_t;
-      typedef details::quaternary_node <T>                quaternary_node_t;
-      typedef details::conditional_node<T>               conditional_node_t;
-      typedef details::cons_conditional_node<T>     cons_conditional_node_t;
-      typedef details::while_loop_node <T>                while_loop_node_t;
-      typedef details::repeat_until_loop_node<T>   repeat_until_loop_node_t;
-      typedef details::for_loop_node   <T>                  for_loop_node_t;
+         e_level00, e_level01, e_level02, e_level03, e_level04,
+         e_level05, e_level06, e_level07, e_level08, e_level09,
+         e_level10, e_level11, e_level12, e_level13, e_level14
+      };
+
+      typedef const T&                                       cref_t;
+      typedef const T                                        const_t;
+      typedef ifunction<T>                                   F;
+      typedef ivararg_function<T>                            VAF;
+      typedef igeneric_function<T>                           GF;
+      typedef ifunction<T>                                   ifunction_t;
+      typedef ivararg_function<T>                            ivararg_function_t;
+      typedef igeneric_function<T>                           igeneric_function_t;
+      typedef details::expression_node<T>                    expression_node_t;
+      typedef details::literal_node<T>                       literal_node_t;
+      typedef details::unary_node<T>                         unary_node_t;
+      typedef details::binary_node<T>                        binary_node_t;
+      typedef details::trinary_node<T>                       trinary_node_t;
+      typedef details::quaternary_node<T>                    quaternary_node_t;
+      typedef details::conditional_node<T>                   conditional_node_t;
+      typedef details::cons_conditional_node<T>              cons_conditional_node_t;
+      typedef details::while_loop_node<T>                    while_loop_node_t;
+      typedef details::repeat_until_loop_node<T>             repeat_until_loop_node_t;
+      typedef details::for_loop_node<T>                      for_loop_node_t;
+      typedef details::while_loop_rtc_node<T>                while_loop_rtc_node_t;
+      typedef details::repeat_until_loop_rtc_node<T>         repeat_until_loop_rtc_node_t;
+      typedef details::for_loop_rtc_node<T>                  for_loop_rtc_node_t;
       #ifndef exprtk_disable_break_continue
-      typedef details::while_loop_bc_node <T>          while_loop_bc_node_t;
-      typedef details::repeat_until_loop_bc_node<T> repeat_until_loop_bc_node_t;
-      typedef details::for_loop_bc_node<T>               for_loop_bc_node_t;
+      typedef details::while_loop_bc_node<T>                 while_loop_bc_node_t;
+      typedef details::repeat_until_loop_bc_node<T>          repeat_until_loop_bc_node_t;
+      typedef details::for_loop_bc_node<T>                   for_loop_bc_node_t;
+      typedef details::while_loop_bc_rtc_node<T>             while_loop_bc_rtc_node_t;
+      typedef details::repeat_until_loop_bc_rtc_node<T>      repeat_until_loop_bc_rtc_node_t;
+      typedef details::for_loop_bc_rtc_node<T>               for_loop_bc_rtc_node_t;
       #endif
-      typedef details::switch_node     <T>                    switch_node_t;
-      typedef details::variable_node   <T>                  variable_node_t;
-      typedef details::vector_elem_node<T>               vector_elem_node_t;
-      typedef details::rebasevector_elem_node<T>   rebasevector_elem_node_t;
-      typedef details::rebasevector_celem_node<T> rebasevector_celem_node_t;
-      typedef details::vector_node     <T>                    vector_node_t;
-      typedef details::range_pack      <T>                          range_t;
+      typedef details::switch_node<T>                        switch_node_t;
+      typedef details::variable_node<T>                      variable_node_t;
+      typedef details::vector_elem_node<T>                   vector_elem_node_t;
+      typedef details::vector_celem_node<T>                  vector_celem_node_t;
+      typedef details::vector_elem_rtc_node<T>               vector_elem_rtc_node_t;
+      typedef details::vector_celem_rtc_node<T>              vector_celem_rtc_node_t;
+      typedef details::rebasevector_elem_node<T>             rebasevector_elem_node_t;
+      typedef details::rebasevector_celem_node<T>            rebasevector_celem_node_t;
+      typedef details::rebasevector_elem_rtc_node<T>         rebasevector_elem_rtc_node_t;
+      typedef details::rebasevector_celem_rtc_node<T>        rebasevector_celem_rtc_node_t;
+      typedef details::vector_node<T>                        vector_node_t;
+      typedef details::vector_size_node<T>                   vector_size_node_t;
+      typedef details::range_pack<T>                         range_t;
       #ifndef exprtk_disable_string_capabilities
-      typedef details::stringvar_node     <T>              stringvar_node_t;
-      typedef details::string_literal_node<T>         string_literal_node_t;
-      typedef details::string_range_node  <T>           string_range_node_t;
-      typedef details::const_string_range_node<T> const_string_range_node_t;
-      typedef details::generic_string_range_node<T> generic_string_range_node_t;
-      typedef details::string_concat_node <T>          string_concat_node_t;
-      typedef details::assignment_string_node<T>   assignment_string_node_t;
-      typedef details::assignment_string_range_node<T> assignment_string_range_node_t;
-      typedef details::conditional_string_node<T>  conditional_string_node_t;
-      typedef details::cons_conditional_str_node<T> cons_conditional_str_node_t;
+      typedef details::stringvar_node<T>                     stringvar_node_t;
+      typedef details::string_literal_node<T>                string_literal_node_t;
+      typedef details::string_range_node<T>                  string_range_node_t;
+      typedef details::const_string_range_node<T>            const_string_range_node_t;
+      typedef details::generic_string_range_node<T>          generic_string_range_node_t;
+      typedef details::string_concat_node<T>                 string_concat_node_t;
+      typedef details::assignment_string_node<T>             assignment_string_node_t;
+      typedef details::assignment_string_range_node<T>       assignment_string_range_node_t;
+      typedef details::conditional_string_node<T>            conditional_string_node_t;
+      typedef details::cons_conditional_str_node<T>          cons_conditional_str_node_t;
       #endif
-      typedef details::assignment_node<T>                 assignment_node_t;
-      typedef details::assignment_vec_elem_node       <T> assignment_vec_elem_node_t;
-      typedef details::assignment_rebasevec_elem_node <T> assignment_rebasevec_elem_node_t;
-      typedef details::assignment_rebasevec_celem_node<T> assignment_rebasevec_celem_node_t;
-      typedef details::assignment_vec_node     <T>    assignment_vec_node_t;
-      typedef details::assignment_vecvec_node  <T> assignment_vecvec_node_t;
-      typedef details::scand_node<T>                           scand_node_t;
-      typedef details::scor_node<T>                             scor_node_t;
-      typedef lexer::token                                          token_t;
-      typedef expression_node_t*                        expression_node_ptr;
-      typedef expression<T>                                    expression_t;
+      typedef details::assignment_node<T>                    assignment_node_t;
+      typedef details::assignment_vec_elem_node<T>           assignment_vec_elem_node_t;
+      typedef details::assignment_vec_elem_rtc_node<T>       assignment_vec_elem_rtc_node_t;
+      typedef details::assignment_rebasevec_elem_node<T>     assignment_rebasevec_elem_node_t;
+      typedef details::assignment_rebasevec_elem_rtc_node<T> assignment_rebasevec_elem_rtc_node_t;
+      typedef details::assignment_rebasevec_celem_node<T>    assignment_rebasevec_celem_node_t;
+      typedef details::assignment_vec_node<T>                assignment_vec_node_t;
+      typedef details::assignment_vecvec_node<T>             assignment_vecvec_node_t;
+      typedef details::conditional_vector_node<T>            conditional_vector_node_t;
+      typedef details::scand_node<T>                         scand_node_t;
+      typedef details::scor_node<T>                          scor_node_t;
+      typedef lexer::token                                   token_t;
+      typedef expression_node_t*                             expression_node_ptr;
+      typedef expression<T>                                  expression_t;
       typedef symbol_table<T>                                symbol_table_t;
-      typedef typename expression<T>::symtab_list_t     symbol_table_list_t;
-      typedef details::vector_holder<T>*                  vector_holder_ptr;
+      typedef typename expression<T>::symtab_list_t          symbol_table_list_t;
+      typedef details::vector_holder<T>                      vector_holder_t;
+      typedef vector_holder_t*                               vector_holder_ptr;
 
-      typedef typename details::functor_t<T>            functor_t;
+      typedef typename details::functor_t<T> functor_t;
       typedef typename functor_t::qfunc_t    quaternary_functor_t;
-      typedef typename functor_t::tfunc_t       trinary_functor_t;
-      typedef typename functor_t::bfunc_t        binary_functor_t;
-      typedef typename functor_t::ufunc_t         unary_functor_t;
+      typedef typename functor_t::tfunc_t    trinary_functor_t;
+      typedef typename functor_t::bfunc_t    binary_functor_t;
+      typedef typename functor_t::ufunc_t    unary_functor_t;
 
       typedef details::operator_type operator_t;
 
-      typedef std::map<operator_t,  unary_functor_t>   unary_op_map_t;
-      typedef std::map<operator_t, binary_functor_t>  binary_op_map_t;
-      typedef std::map<operator_t,trinary_functor_t> trinary_op_map_t;
+      typedef std::map<operator_t, unary_functor_t  > unary_op_map_t;
+      typedef std::map<operator_t, binary_functor_t > binary_op_map_t;
+      typedef std::map<operator_t, trinary_functor_t> trinary_op_map_t;
 
       typedef std::map<std::string,std::pair<trinary_functor_t   ,operator_t> > sf3_map_t;
       typedef std::map<std::string,std::pair<quaternary_functor_t,operator_t> > sf4_map_t;
@@ -19139,28 +22324,28 @@ namespace exprtk
       typedef std::multimap<std::string,details::base_operation_t,details::ilesscompare> base_ops_map_t;
       typedef std::set<std::string,details::ilesscompare> disabled_func_set_t;
 
-      typedef details::T0oT1_define<T,  cref_t,  cref_t> vov_t;
-      typedef details::T0oT1_define<T, const_t,  cref_t> cov_t;
-      typedef details::T0oT1_define<T,  cref_t, const_t> voc_t;
-
-      typedef details::T0oT1oT2_define<T,  cref_t,  cref_t,  cref_t> vovov_t;
-      typedef details::T0oT1oT2_define<T,  cref_t,  cref_t, const_t> vovoc_t;
-      typedef details::T0oT1oT2_define<T,  cref_t, const_t,  cref_t> vocov_t;
-      typedef details::T0oT1oT2_define<T, const_t,  cref_t,  cref_t> covov_t;
-      typedef details::T0oT1oT2_define<T, const_t,  cref_t, const_t> covoc_t;
-      typedef details::T0oT1oT2_define<T, const_t, const_t,  cref_t> cocov_t;
-      typedef details::T0oT1oT2_define<T,  cref_t, const_t, const_t> vococ_t;
-
-      typedef details::T0oT1oT2oT3_define<T,  cref_t,  cref_t,  cref_t,  cref_t> vovovov_t;
-      typedef details::T0oT1oT2oT3_define<T,  cref_t,  cref_t,  cref_t, const_t> vovovoc_t;
-      typedef details::T0oT1oT2oT3_define<T,  cref_t,  cref_t, const_t,  cref_t> vovocov_t;
-      typedef details::T0oT1oT2oT3_define<T,  cref_t, const_t,  cref_t,  cref_t> vocovov_t;
-      typedef details::T0oT1oT2oT3_define<T, const_t,  cref_t,  cref_t,  cref_t> covovov_t;
-
-      typedef details::T0oT1oT2oT3_define<T, const_t,  cref_t, const_t,  cref_t> covocov_t;
-      typedef details::T0oT1oT2oT3_define<T,  cref_t, const_t,  cref_t, const_t> vocovoc_t;
-      typedef details::T0oT1oT2oT3_define<T, const_t,  cref_t,  cref_t, const_t> covovoc_t;
-      typedef details::T0oT1oT2oT3_define<T,  cref_t, const_t, const_t,  cref_t> vococov_t;
+      typedef details::T0oT1_define<T, cref_t , cref_t > vov_t;
+      typedef details::T0oT1_define<T, const_t, cref_t > cov_t;
+      typedef details::T0oT1_define<T, cref_t , const_t> voc_t;
+
+      typedef details::T0oT1oT2_define<T, cref_t , cref_t , cref_t > vovov_t;
+      typedef details::T0oT1oT2_define<T, cref_t , cref_t , const_t> vovoc_t;
+      typedef details::T0oT1oT2_define<T, cref_t , const_t, cref_t > vocov_t;
+      typedef details::T0oT1oT2_define<T, const_t, cref_t , cref_t > covov_t;
+      typedef details::T0oT1oT2_define<T, const_t, cref_t , const_t> covoc_t;
+      typedef details::T0oT1oT2_define<T, const_t, const_t, cref_t > cocov_t;
+      typedef details::T0oT1oT2_define<T, cref_t , const_t, const_t> vococ_t;
+
+      typedef details::T0oT1oT2oT3_define<T, cref_t , cref_t , cref_t , cref_t > vovovov_t;
+      typedef details::T0oT1oT2oT3_define<T, cref_t , cref_t , cref_t , const_t> vovovoc_t;
+      typedef details::T0oT1oT2oT3_define<T, cref_t , cref_t , const_t, cref_t > vovocov_t;
+      typedef details::T0oT1oT2oT3_define<T, cref_t , const_t, cref_t , cref_t > vocovov_t;
+      typedef details::T0oT1oT2oT3_define<T, const_t, cref_t , cref_t , cref_t > covovov_t;
+
+      typedef details::T0oT1oT2oT3_define<T, const_t, cref_t , const_t, cref_t > covocov_t;
+      typedef details::T0oT1oT2oT3_define<T, cref_t , const_t, cref_t , const_t> vocovoc_t;
+      typedef details::T0oT1oT2oT3_define<T, const_t, cref_t , cref_t , const_t> covovoc_t;
+      typedef details::T0oT1oT2oT3_define<T, cref_t , const_t, const_t, cref_t > vococov_t;
 
       typedef results_context<T> results_context_t;
 
@@ -19171,6 +22356,7 @@ namespace exprtk
          enum element_type
          {
             e_none    ,
+            e_literal ,
             e_variable,
             e_vector  ,
             e_vecelem ,
@@ -19178,28 +22364,29 @@ namespace exprtk
          };
 
          typedef details::vector_holder<T> vector_holder_t;
-         typedef variable_node_t*        variable_node_ptr;
-         typedef vector_holder_t*        vector_holder_ptr;
-         typedef expression_node_t*    expression_node_ptr;
+         typedef literal_node_t*           literal_node_ptr;
+         typedef variable_node_t*          variable_node_ptr;
+         typedef vector_holder_t*          vector_holder_ptr;
+         typedef expression_node_t*        expression_node_ptr;
          #ifndef exprtk_disable_string_capabilities
-         typedef stringvar_node_t*      stringvar_node_ptr;
+         typedef stringvar_node_t*         stringvar_node_ptr;
          #endif
 
          scope_element()
-         : name("???"),
-           size (std::numeric_limits<std::size_t>::max()),
-           index(std::numeric_limits<std::size_t>::max()),
-           depth(std::numeric_limits<std::size_t>::max()),
-           ref_count(0),
-           ip_index (0),
-           type (e_none),
-           active(false),
-           data    (0),
-           var_node(0),
-           vec_node(0)
-           #ifndef exprtk_disable_string_capabilities
-           ,str_node(0)
-           #endif
+         : name("???")
+         , size (std::numeric_limits<std::size_t>::max())
+         , index(std::numeric_limits<std::size_t>::max())
+         , depth(std::numeric_limits<std::size_t>::max())
+         , ref_count(0)
+         , ip_index (0)
+         , type     (e_none)
+         , active   (false)
+         , data     (0)
+         , var_node (0)
+         , vec_node (0)
+         #ifndef exprtk_disable_string_capabilities
+         , str_node(0)
+         #endif
          {}
 
          bool operator < (const scope_element& se) const
@@ -19259,12 +22446,12 @@ namespace exprtk
       public:
 
          typedef expression_node_t* expression_node_ptr;
-         typedef variable_node_t*     variable_node_ptr;
-         typedef parser<T>                     parser_t;
+         typedef variable_node_t*   variable_node_ptr;
+         typedef parser<T>          parser_t;
 
          explicit scope_element_manager(parser<T>& p)
-         : parser_(p),
-           input_param_cnt_(0)
+         : parser_(p)
+         , input_param_cnt_(0)
          {}
 
          inline std::size_t size() const
@@ -19377,6 +22564,10 @@ namespace exprtk
 
             switch (se.type)
             {
+               case scope_element::e_literal    : delete reinterpret_cast<T*>(se.data);
+                                                  delete se.var_node;
+                                                  break;
+
                case scope_element::e_variable   : delete reinterpret_cast<T*>(se.data);
                                                   delete se.var_node;
                                                   break;
@@ -19441,9 +22632,29 @@ namespace exprtk
             return expression_node_ptr(0);
          }
 
+         inline std::string get_vector_name(const T* data)
+         {
+            for (std::size_t i = 0; i < element_.size(); ++i)
+            {
+               scope_element& se = element_[i];
+
+               if (
+                    se.active   &&
+                    se.vec_node &&
+                    (se.vec_node->data() == data)
+                  )
+               {
+                  return se.name;
+               }
+            }
+
+            return "neo-vector";
+         }
+
       private:
 
-         scope_element_manager& operator=(const scope_element_manager&);
+         scope_element_manager(const scope_element_manager&) exprtk_delete;
+         scope_element_manager& operator=(const scope_element_manager&) exprtk_delete;
 
          parser_t& parser_;
          std::vector<scope_element> element_;
@@ -19483,11 +22694,136 @@ namespace exprtk
 
       private:
 
-         scope_handler& operator=(const scope_handler&);
+         scope_handler(const scope_handler&) exprtk_delete;
+         scope_handler& operator=(const scope_handler&) exprtk_delete;
 
          parser_t& parser_;
       };
 
+      template <typename T_>
+      struct halfopen_range_policy
+      {
+         static inline bool is_within(const T_& v, const T_& begin, const T_& end)
+         {
+            assert(begin <= end);
+            return (begin <= v) && (v < end);
+         }
+
+         static inline bool is_less(const T_& v, const T_& begin)
+         {
+            return (v < begin);
+         }
+
+         static inline bool is_greater(const T_& v, const T_& end)
+         {
+            return (end <= v);
+         }
+
+         static inline bool end_inclusive()
+         {
+            return false;
+         }
+      };
+
+      template <typename T_>
+      struct closed_range_policy
+      {
+         static inline bool is_within(const T_& v, const T_& begin, const T_& end)
+         {
+            assert(begin <= end);
+            return (begin <= v) && (v <= end);
+         }
+
+         static inline bool is_less(const T_& v, const T_& begin)
+         {
+            return (v < begin);
+         }
+
+         static inline bool is_greater(const T_& v, const T_& end)
+         {
+            return (end < v);
+         }
+
+         static inline bool end_inclusive()
+         {
+            return true;
+         }
+      };
+
+      template <typename IntervalPointType,
+                typename RangePolicy = halfopen_range_policy<IntervalPointType> >
+      class interval_container_t
+      {
+      public:
+
+         typedef IntervalPointType interval_point_t;
+         typedef std::pair<interval_point_t, interval_point_t> interval_t;
+         typedef std::map<interval_point_t, interval_t> interval_map_t;
+         typedef typename interval_map_t::const_iterator interval_map_citr_t;
+
+         std::size_t size() const
+         {
+            return interval_map_.size();
+         }
+
+         void reset()
+         {
+            interval_map_.clear();
+         }
+
+         bool in_interval(const interval_point_t point, interval_t& interval) const
+         {
+            interval_map_citr_t itr = RangePolicy::end_inclusive() ?
+                                      interval_map_.lower_bound(point):
+                                      interval_map_.upper_bound(point);
+
+            for (; itr != interval_map_.end(); ++itr)
+            {
+               const interval_point_t& begin = itr->second.first;
+               const interval_point_t& end   = itr->second.second;
+
+               if (RangePolicy::is_within(point, begin, end))
+               {
+                  interval = interval_t(begin,end);
+                  return true;
+               }
+               else if (RangePolicy::is_greater(point, end))
+               {
+                  break;
+               }
+            }
+
+            return false;
+         }
+
+         bool in_interval(const interval_point_t point) const
+         {
+            interval_t interval;
+            return in_interval(point,interval);
+         }
+
+         bool add_interval(const interval_point_t begin, const interval_point_t end)
+         {
+            if ((end <= begin) || in_interval(begin) || in_interval(end))
+            {
+               return false;
+            }
+
+            interval_map_[end] = std::make_pair(begin, end);
+
+            return true;
+         }
+
+         bool add_interval(const interval_t interval)
+         {
+            return add_interval(interval.first, interval.second);
+         }
+
+      private:
+
+         interval_map_t interval_map_;
+      };
+
       class stack_limit_handler
       {
       public:
@@ -19495,22 +22831,23 @@ namespace exprtk
          typedef parser<T> parser_t;
 
          explicit stack_limit_handler(parser<T>& p)
-            : parser_(p),
-              limit_exceeded_(false)
+         : parser_(p)
+         , limit_exceeded_(false)
          {
             if (++parser_.state_.stack_depth > parser_.settings_.max_stack_depth_)
             {
                limit_exceeded_ = true;
-               parser_.set_error(
-                  make_error(parser_error::e_parser,
-                     "ERR000 - Current stack depth " + details::to_str(parser_.state_.stack_depth) +
-                     " exceeds maximum allowed stack depth of " + details::to_str(parser_.settings_.max_stack_depth_),
-                     exprtk_error_location));
+               parser_.set_error(make_error(
+                  parser_error::e_parser,
+                  "ERR000 - Current stack depth " + details::to_str(parser_.state_.stack_depth) +
+                  " exceeds maximum allowed stack depth of " + details::to_str(parser_.settings_.max_stack_depth_),
+                  exprtk_error_location));
             }
          }
 
         ~stack_limit_handler()
          {
+            assert(parser_.state_.stack_depth > 0);
             parser_.state_.stack_depth--;
          }
 
@@ -19521,7 +22858,8 @@ namespace exprtk
 
       private:
 
-         stack_limit_handler& operator=(const stack_limit_handler&);
+         stack_limit_handler(const stack_limit_handler&) exprtk_delete;
+         stack_limit_handler& operator=(const stack_limit_handler&) exprtk_delete;
 
          parser_t& parser_;
          bool limit_exceeded_;
@@ -19531,16 +22869,51 @@ namespace exprtk
       {
          symbol_table_list_t symtab_list_;
 
-         typedef typename symbol_table_t::local_data_t   local_data_t;
-         typedef typename symbol_table_t::variable_ptr   variable_ptr;
-         typedef typename symbol_table_t::function_ptr   function_ptr;
+         typedef typename symbol_table_t::local_data_t local_data_t;
+         typedef typename symbol_table_t::variable_ptr variable_ptr;
+         typedef typename symbol_table_t::function_ptr function_ptr;
          #ifndef exprtk_disable_string_capabilities
          typedef typename symbol_table_t::stringvar_ptr stringvar_ptr;
          #endif
-         typedef typename symbol_table_t::vector_holder_ptr       vector_holder_ptr;
-         typedef typename symbol_table_t::vararg_function_ptr   vararg_function_ptr;
+         typedef typename symbol_table_t::vector_holder_ptr    vector_holder_ptr;
+         typedef typename symbol_table_t::vararg_function_ptr  vararg_function_ptr;
          typedef typename symbol_table_t::generic_function_ptr generic_function_ptr;
 
+         struct variable_context
+         {
+            variable_context()
+            : symbol_table(0)
+            , variable(0)
+            {}
+
+            const symbol_table_t* symbol_table;
+            variable_ptr variable;
+         };
+
+         struct vector_context
+         {
+            vector_context()
+            : symbol_table(0)
+            , vector_holder(0)
+            {}
+
+            const symbol_table_t* symbol_table;
+            vector_holder_ptr vector_holder;
+         };
+
+         #ifndef exprtk_disable_string_capabilities
+         struct string_context
+         {
+            string_context()
+            : symbol_table(0)
+            , str_var(0)
+            {}
+
+            const symbol_table_t* symbol_table;
+            stringvar_ptr str_var;
+         };
+         #endif
+
          inline bool empty() const
          {
             return symtab_list_.empty();
@@ -19581,6 +22954,32 @@ namespace exprtk
                return false;
          }
 
+         inline variable_context get_variable_context(const std::string& variable_name) const
+         {
+            variable_context result;
+
+            if (valid_symbol(variable_name))
+            {
+               for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+               {
+                  if (!symtab_list_[i].valid())
+                  {
+                     continue;
+                  }
+
+                  result.variable = local_data(i)
+                                       .variable_store.get(variable_name);
+                  if (result.variable)
+                  {
+                     result.symbol_table = &symtab_list_[i];
+                     break;
+                  }
+               }
+            }
+
+            return result;
+         }
+
          inline variable_ptr get_variable(const std::string& variable_name) const
          {
             if (!valid_symbol(variable_name))
@@ -19621,6 +23020,32 @@ namespace exprtk
          }
 
          #ifndef exprtk_disable_string_capabilities
+         inline string_context get_string_context(const std::string& string_name) const
+         {
+            string_context result;
+
+            if (!valid_symbol(string_name))
+               return result;
+
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+               {
+                  continue;
+               }
+
+               result.str_var = local_data(i).stringvar_store.get(string_name);
+
+               if (result.str_var)
+               {
+                  result.symbol_table = &symtab_list_[i];
+                  break;
+               }
+            }
+
+            return result;
+         }
+
          inline stringvar_ptr get_stringvar(const std::string& string_name) const
          {
             if (!valid_symbol(string_name))
@@ -19748,6 +23173,31 @@ namespace exprtk
             return result;
          }
 
+         inline vector_context get_vector_context(const std::string& vector_name) const
+         {
+            vector_context result;
+            if (!valid_symbol(vector_name))
+               return result;
+
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+               {
+                  continue;
+               }
+
+               result.vector_holder = local_data(i).vector_store.get(vector_name);
+
+               if (result.vector_holder)
+               {
+                  result.symbol_table = &symtab_list_[i];
+                  break;
+               }
+            }
+
+            return result;
+         }
+
          inline vector_holder_ptr get_vector(const std::string& vector_name) const
          {
             if (!valid_symbol(vector_name))
@@ -19758,12 +23208,16 @@ namespace exprtk
             for (std::size_t i = 0; i < symtab_list_.size(); ++i)
             {
                if (!symtab_list_[i].valid())
+               {
                   continue;
-               else
-                  result =
-                     local_data(i).vector_store.get(vector_name);
+               }
 
-               if (result) break;
+               result = local_data(i).vector_store.get(vector_name);
+
+               if (result)
+               {
+                  break;
+               }
             }
 
             return result;
@@ -19777,9 +23231,14 @@ namespace exprtk
             for (std::size_t i = 0; i < symtab_list_.size(); ++i)
             {
                if (!symtab_list_[i].valid())
+               {
                   continue;
-               else if (local_data(i).variable_store.is_constant(symbol_name))
+               }
+
+               if (local_data(i).variable_store.is_constant(symbol_name))
+               {
                   return true;
+               }
             }
 
             return false;
@@ -19797,7 +23256,7 @@ namespace exprtk
                   continue;
                else if (!local_data(i).stringvar_store.symbol_exists(symbol_name))
                   continue;
-               else if ( local_data(i).stringvar_store.is_constant(symbol_name))
+               else if (local_data(i).stringvar_store.is_constant(symbol_name))
                   return true;
             }
 
@@ -19810,9 +23269,14 @@ namespace exprtk
             for (std::size_t i = 0; i < symtab_list_.size(); ++i)
             {
                if (!symtab_list_[i].valid())
+               {
                   continue;
-               else if (symtab_list_[i].symbol_exists(symbol))
+               }
+
+               if (symtab_list_[i].symbol_exists(symbol))
+               {
                   return true;
+               }
             }
 
             return false;
@@ -19972,6 +23436,7 @@ namespace exprtk
          {
             parsing_return_stmt     = false;
             parsing_break_stmt      = false;
+            parsing_assert_stmt     = false;
             return_stmt_present     = false;
             side_effect_present     = false;
             scope_depth             = 0;
@@ -19989,12 +23454,13 @@ namespace exprtk
             {
                side_effect_present = true;
 
-               exprtk_debug(("activate_side_effect() - caller: %s\n",source.c_str()));
+               exprtk_debug(("activate_side_effect() - caller: %s\n", source.c_str()));
             }
          }
 
          bool parsing_return_stmt;
          bool parsing_break_stmt;
+         bool parsing_assert_stmt;
          bool return_stmt_present;
          bool side_effect_present;
          bool type_check_enabled;
@@ -20027,7 +23493,7 @@ namespace exprtk
          : mode(m)
          {}
 
-         virtual ~unknown_symbol_resolver() = default;
+         virtual ~unknown_symbol_resolver() exprtk_default;
 
          virtual bool process(const std::string& /*unknown_symbol*/,
                               usr_symbol_type&   st,
@@ -20081,12 +23547,12 @@ namespace exprtk
          typedef std::vector<symbol_t> symbol_list_t;
 
          dependent_entity_collector(const std::size_t options = e_ct_none)
-         : options_(options),
-           collect_variables_  ((options_ & e_ct_variables  ) == e_ct_variables  ),
-           collect_functions_  ((options_ & e_ct_functions  ) == e_ct_functions  ),
-           collect_assignments_((options_ & e_ct_assignments) == e_ct_assignments),
-           return_present_   (false),
-           final_stmt_return_(false)
+         : options_(options)
+         , collect_variables_  ((options_ & e_ct_variables  ) == e_ct_variables  )
+         , collect_functions_  ((options_ & e_ct_functions  ) == e_ct_functions  )
+         , collect_assignments_((options_ & e_ct_assignments) == e_ct_assignments)
+         , return_present_   (false)
+         , final_stmt_return_(false)
          {}
 
          template <typename Allocator,
@@ -20103,11 +23569,14 @@ namespace exprtk
                details::case_normalise(symbol_name_list_[i].first);
             }
 
-            std::sort(symbol_name_list_.begin(),symbol_name_list_.end());
+            std::sort(symbol_name_list_.begin(), symbol_name_list_.end());
 
-            std::unique_copy(symbol_name_list_.begin(),
-                             symbol_name_list_.end  (),
-                             std::back_inserter(symbols_list));
+            std::unique_copy
+            (
+               symbol_name_list_.begin(),
+               symbol_name_list_.end  (),
+               std::back_inserter(symbols_list)
+            );
 
             return symbols_list.size();
          }
@@ -20128,9 +23597,12 @@ namespace exprtk
 
             std::sort(assignment_name_list_.begin(),assignment_name_list_.end());
 
-            std::unique_copy(assignment_name_list_.begin(),
-                             assignment_name_list_.end  (),
-                             std::back_inserter(assignment_list));
+            std::unique_copy
+            (
+               assignment_name_list_.begin(),
+               assignment_name_list_.end  (),
+               std::back_inserter(assignment_list)
+            );
 
             return assignment_list.size();
          }
@@ -20259,20 +23731,20 @@ namespace exprtk
          enum settings_base_funcs
          {
             e_bf_unknown = 0,
-            e_bf_abs       , e_bf_acos     , e_bf_acosh    , e_bf_asin   ,
-            e_bf_asinh     , e_bf_atan     , e_bf_atan2    , e_bf_atanh  ,
-            e_bf_avg       , e_bf_ceil     , e_bf_clamp    , e_bf_cos    ,
-            e_bf_cosh      , e_bf_cot      , e_bf_csc      , e_bf_equal  ,
-            e_bf_erf       , e_bf_erfc     , e_bf_exp      , e_bf_expm1  ,
-            e_bf_floor     , e_bf_frac     , e_bf_hypot    , e_bf_iclamp ,
-            e_bf_like      , e_bf_log      , e_bf_log10    , e_bf_log1p  ,
-            e_bf_log2      , e_bf_logn     , e_bf_mand     , e_bf_max    ,
-            e_bf_min       , e_bf_mod      , e_bf_mor      , e_bf_mul    ,
-            e_bf_ncdf      , e_bf_pow      , e_bf_root     , e_bf_round  ,
-            e_bf_roundn    , e_bf_sec      , e_bf_sgn      , e_bf_sin    ,
-            e_bf_sinc      , e_bf_sinh     , e_bf_sqrt     , e_bf_sum    ,
-            e_bf_swap      , e_bf_tan      , e_bf_tanh     , e_bf_trunc  ,
-            e_bf_not_equal , e_bf_inrange  , e_bf_deg2grad , e_bf_deg2rad,
+            e_bf_abs       , e_bf_acos     , e_bf_acosh    , e_bf_asin    ,
+            e_bf_asinh     , e_bf_atan     , e_bf_atan2    , e_bf_atanh   ,
+            e_bf_avg       , e_bf_ceil     , e_bf_clamp    , e_bf_cos     ,
+            e_bf_cosh      , e_bf_cot      , e_bf_csc      , e_bf_equal   ,
+            e_bf_erf       , e_bf_erfc     , e_bf_exp      , e_bf_expm1   ,
+            e_bf_floor     , e_bf_frac     , e_bf_hypot    , e_bf_iclamp  ,
+            e_bf_like      , e_bf_log      , e_bf_log10    , e_bf_log1p   ,
+            e_bf_log2      , e_bf_logn     , e_bf_mand     , e_bf_max     ,
+            e_bf_min       , e_bf_mod      , e_bf_mor      , e_bf_mul     ,
+            e_bf_ncdf      , e_bf_pow      , e_bf_root     , e_bf_round   ,
+            e_bf_roundn    , e_bf_sec      , e_bf_sgn      , e_bf_sin     ,
+            e_bf_sinc      , e_bf_sinh     , e_bf_sqrt     , e_bf_sum     ,
+            e_bf_swap      , e_bf_tan      , e_bf_tanh     , e_bf_trunc   ,
+            e_bf_not_equal , e_bf_inrange  , e_bf_deg2grad , e_bf_deg2rad ,
             e_bf_rad2deg   , e_bf_grad2deg
          };
 
@@ -20290,8 +23762,8 @@ namespace exprtk
          enum settings_logic_opr
          {
             e_logic_unknown = 0,
-            e_logic_and, e_logic_nand,  e_logic_nor,
-            e_logic_not, e_logic_or,    e_logic_xnor,
+            e_logic_and, e_logic_nand , e_logic_nor ,
+            e_logic_not, e_logic_or   , e_logic_xnor,
             e_logic_xor, e_logic_scand, e_logic_scor
          };
 
@@ -20312,22 +23784,24 @@ namespace exprtk
          enum settings_inequality_opr
          {
             e_ineq_unknown = 0,
-            e_ineq_lt,    e_ineq_lte, e_ineq_eq,
-            e_ineq_equal, e_ineq_ne,  e_ineq_nequal,
-            e_ineq_gte,   e_ineq_gt
+            e_ineq_lt   , e_ineq_lte, e_ineq_eq    ,
+            e_ineq_equal, e_ineq_ne , e_ineq_nequal,
+            e_ineq_gte  , e_ineq_gt
          };
 
-         static const std::size_t compile_all_opts = e_replacer          +
-                                                     e_joiner            +
-                                                     e_numeric_check     +
-                                                     e_bracket_check     +
-                                                     e_sequence_check    +
-                                                     e_commutative_check +
-                                                     e_strength_reduction;
+         static const std::size_t default_compile_all_opts =
+                                     e_replacer          +
+                                     e_joiner            +
+                                     e_numeric_check     +
+                                     e_bracket_check     +
+                                     e_sequence_check    +
+                                     e_commutative_check +
+                                     e_strength_reduction;
 
-         settings_store(const std::size_t compile_options = compile_all_opts)
-         : max_stack_depth_(400),
-           max_node_depth_(10000)
+         settings_store(const std::size_t compile_options = default_compile_all_opts)
+         : max_stack_depth_(400)
+         , max_node_depth_(10000)
+         , max_local_vector_size_(2000000000)
          {
            load_compile_options(compile_options);
          }
@@ -20374,12 +23848,24 @@ namespace exprtk
             return (*this);
          }
 
+         settings_store& enable_commutative_check()
+         {
+            enable_commutative_check_ = true;
+            return (*this);
+         }
+
+         settings_store& enable_strength_reduction()
+         {
+            enable_strength_reduction_ = true;
+            return (*this);
+         }
+
          settings_store& disable_all_base_functions()
          {
             std::copy(details::base_function_list,
                       details::base_function_list + details::base_function_list_size,
                       std::insert_iterator<disabled_entity_set_t>
-                        (disabled_func_set_, disabled_func_set_.begin()));
+                         (disabled_func_set_, disabled_func_set_.begin()));
             return (*this);
          }
 
@@ -20388,7 +23874,7 @@ namespace exprtk
             std::copy(details::cntrl_struct_list,
                       details::cntrl_struct_list + details::cntrl_struct_list_size,
                       std::insert_iterator<disabled_entity_set_t>
-                        (disabled_ctrl_set_, disabled_ctrl_set_.begin()));
+                         (disabled_ctrl_set_, disabled_ctrl_set_.begin()));
             return (*this);
          }
 
@@ -20406,7 +23892,7 @@ namespace exprtk
             std::copy(details::arithmetic_ops_list,
                       details::arithmetic_ops_list + details::arithmetic_ops_list_size,
                       std::insert_iterator<disabled_entity_set_t>
-                        (disabled_arithmetic_set_, disabled_arithmetic_set_.begin()));
+                         (disabled_arithmetic_set_, disabled_arithmetic_set_.begin()));
             return (*this);
          }
 
@@ -20415,7 +23901,7 @@ namespace exprtk
             std::copy(details::assignment_ops_list,
                       details::assignment_ops_list + details::assignment_ops_list_size,
                       std::insert_iterator<disabled_entity_set_t>
-                        (disabled_assignment_set_, disabled_assignment_set_.begin()));
+                         (disabled_assignment_set_, disabled_assignment_set_.begin()));
             return (*this);
          }
 
@@ -20424,7 +23910,7 @@ namespace exprtk
             std::copy(details::inequality_ops_list,
                       details::inequality_ops_list + details::inequality_ops_list_size,
                       std::insert_iterator<disabled_entity_set_t>
-                        (disabled_inequality_set_, disabled_inequality_set_.begin()));
+                         (disabled_inequality_set_, disabled_inequality_set_.begin()));
             return (*this);
          }
 
@@ -20434,6 +23920,18 @@ namespace exprtk
             return (*this);
          }
 
+         settings_store& disable_commutative_check()
+         {
+            enable_commutative_check_ = false;
+            return (*this);
+         }
+
+         settings_store& disable_strength_reduction()
+         {
+            enable_strength_reduction_ = false;
+            return (*this);
+         }
+
          bool replacer_enabled           () const { return enable_replacer_;           }
          bool commutative_check_enabled  () const { return enable_commutative_check_;  }
          bool joiner_enabled             () const { return enable_joiner_;             }
@@ -20559,7 +24057,7 @@ namespace exprtk
                                                            .find(inequality_opr_to_string(inequality));
          }
 
-         settings_store& disable_base_function(settings_base_funcs bf)
+         settings_store& disable_base_function(const settings_base_funcs bf)
          {
             if (
                  (e_bf_unknown != bf) &&
@@ -20572,7 +24070,7 @@ namespace exprtk
             return (*this);
          }
 
-         settings_store& disable_control_structure(settings_control_structs ctrl_struct)
+         settings_store& disable_control_structure(const settings_control_structs ctrl_struct)
          {
             if (
                  (e_ctrl_unknown != ctrl_struct) &&
@@ -20585,7 +24083,7 @@ namespace exprtk
             return (*this);
          }
 
-         settings_store& disable_logic_operation(settings_logic_opr logic)
+         settings_store& disable_logic_operation(const settings_logic_opr logic)
          {
             if (
                  (e_logic_unknown != logic) &&
@@ -20598,7 +24096,7 @@ namespace exprtk
             return (*this);
          }
 
-         settings_store& disable_arithmetic_operation(settings_arithmetic_opr arithmetic)
+         settings_store& disable_arithmetic_operation(const settings_arithmetic_opr arithmetic)
          {
             if (
                  (e_arith_unknown != arithmetic) &&
@@ -20611,7 +24109,7 @@ namespace exprtk
             return (*this);
          }
 
-         settings_store& disable_assignment_operation(settings_assignment_opr assignment)
+         settings_store& disable_assignment_operation(const settings_assignment_opr assignment)
          {
             if (
                  (e_assign_unknown != assignment) &&
@@ -20624,7 +24122,7 @@ namespace exprtk
             return (*this);
          }
 
-         settings_store& disable_inequality_operation(settings_inequality_opr inequality)
+         settings_store& disable_inequality_operation(const settings_inequality_opr inequality)
          {
             if (
                  (e_ineq_unknown != inequality) &&
@@ -20637,7 +24135,7 @@ namespace exprtk
             return (*this);
          }
 
-         settings_store& enable_base_function(settings_base_funcs bf)
+         settings_store& enable_base_function(const settings_base_funcs bf)
          {
             if (
                  (e_bf_unknown != bf) &&
@@ -20655,7 +24153,7 @@ namespace exprtk
             return (*this);
          }
 
-         settings_store& enable_control_structure(settings_control_structs ctrl_struct)
+         settings_store& enable_control_structure(const settings_control_structs ctrl_struct)
          {
             if (
                  (e_ctrl_unknown != ctrl_struct) &&
@@ -20673,7 +24171,7 @@ namespace exprtk
             return (*this);
          }
 
-         settings_store& enable_logic_operation(settings_logic_opr logic)
+         settings_store& enable_logic_operation(const settings_logic_opr logic)
          {
             if (
                  (e_logic_unknown != logic) &&
@@ -20691,7 +24189,7 @@ namespace exprtk
             return (*this);
          }
 
-         settings_store& enable_arithmetic_operation(settings_arithmetic_opr arithmetic)
+         settings_store& enable_arithmetic_operation(const settings_arithmetic_opr arithmetic)
          {
             if (
                  (e_arith_unknown != arithmetic) &&
@@ -20709,7 +24207,7 @@ namespace exprtk
             return (*this);
          }
 
-         settings_store& enable_assignment_operation(settings_assignment_opr assignment)
+         settings_store& enable_assignment_operation(const settings_assignment_opr assignment)
          {
             if (
                  (e_assign_unknown != assignment) &&
@@ -20727,7 +24225,7 @@ namespace exprtk
             return (*this);
          }
 
-         settings_store& enable_inequality_operation(settings_inequality_opr inequality)
+         settings_store& enable_inequality_operation(const settings_inequality_opr inequality)
          {
             if (
                  (e_ineq_unknown != inequality) &&
@@ -20745,9 +24243,9 @@ namespace exprtk
             return (*this);
          }
 
-         void set_max_stack_depth(const std::size_t mx_stack_depth)
+         void set_max_stack_depth(const std::size_t max_stack_depth)
          {
-            max_stack_depth_ = mx_stack_depth;
+            max_stack_depth_ = max_stack_depth;
          }
 
          void set_max_node_depth(const std::size_t max_node_depth)
@@ -20755,6 +24253,26 @@ namespace exprtk
             max_node_depth_ = max_node_depth;
          }
 
+         void set_max_local_vector_size(const std::size_t max_local_vector_size)
+         {
+            max_local_vector_size_ = max_local_vector_size;
+         }
+
+         std::size_t max_stack_depth() const
+         {
+            return max_stack_depth_;
+         }
+
+         std::size_t max_node_depth() const
+         {
+            return max_node_depth_;
+         }
+
+         std::size_t max_local_vector_size() const
+         {
+            return max_local_vector_size_;
+         }
+
       private:
 
          void load_compile_options(const std::size_t compile_options)
@@ -20784,7 +24302,7 @@ namespace exprtk
                case details::e_mulass : return "*=";
                case details::e_divass : return "/=";
                case details::e_modass : return "%=";
-               default                : return   "";
+               default                : return ""  ;
             }
          }
 
@@ -20797,7 +24315,8 @@ namespace exprtk
                case details::e_mul : return "*";
                case details::e_div : return "/";
                case details::e_mod : return "%";
-               default             : return  "";
+               case details::e_pow : return "^";
+               default             : return "" ;
             }
          }
 
@@ -20805,15 +24324,15 @@ namespace exprtk
          {
             switch (opr)
             {
-               case details::e_lt    : return  "<";
+               case details::e_lt    : return "<" ;
                case details::e_lte   : return "<=";
                case details::e_eq    : return "==";
-               case details::e_equal : return  "=";
+               case details::e_equal : return "=" ;
                case details::e_ne    : return "!=";
                case details::e_nequal: return "<>";
                case details::e_gte   : return ">=";
-               case details::e_gt    : return  ">";
-               default               : return   "";
+               case details::e_gt    : return ">" ;
+               default               : return ""  ;
             }
          }
 
@@ -20855,28 +24374,32 @@ namespace exprtk
 
          std::size_t max_stack_depth_;
          std::size_t max_node_depth_;
+         std::size_t max_local_vector_size_;
 
          friend class parser<T>;
       };
 
       typedef settings_store settings_t;
 
-      parser(const settings_t& settings = settings_t())
-      : settings_(settings),
-        resolve_unknown_symbol_(false),
-        results_context_(0),
-        unknown_symbol_resolver_(reinterpret_cast<unknown_symbol_resolver*>(0)),
+      explicit parser(const settings_t& settings = settings_t())
+      : settings_(settings)
+      , resolve_unknown_symbol_(false)
+      , results_context_(0)
+      , unknown_symbol_resolver_(reinterpret_cast<unknown_symbol_resolver*>(0))
         #ifdef _MSC_VER
         #pragma warning(push)
         #pragma warning (disable:4355)
         #endif
-        sem_(*this),
+      , sem_(*this)
         #ifdef _MSC_VER
         #pragma warning(pop)
         #endif
-        operator_joiner_2_(2),
-        operator_joiner_3_(3),
-        loop_runtime_check_(0)
+      , operator_joiner_2_(2)
+      , operator_joiner_3_(3)
+      , loop_runtime_check_(0)
+      , vector_access_runtime_check_(0)
+      , compilation_check_ptr_(0)
+      , assert_check_(0)
       {
          init_precompilation();
 
@@ -20889,26 +24412,26 @@ namespace exprtk
 
          expression_generator_.init_synthesize_map();
          expression_generator_.set_parser(*this);
-         expression_generator_.set_uom(unary_op_map_);
-         expression_generator_.set_bom(binary_op_map_);
+         expression_generator_.set_uom (unary_op_map_     );
+         expression_generator_.set_bom (binary_op_map_    );
          expression_generator_.set_ibom(inv_binary_op_map_);
-         expression_generator_.set_sf3m(sf3_map_);
-         expression_generator_.set_sf4m(sf4_map_);
+         expression_generator_.set_sf3m(sf3_map_          );
+         expression_generator_.set_sf4m(sf4_map_          );
          expression_generator_.set_strength_reduction_state(settings_.strength_reduction_enabled());
       }
 
-     ~parser() = default;
+     ~parser() exprtk_default;
 
       inline void init_precompilation()
       {
-         if (settings_.collect_variables_enabled())
-            dec_.collect_variables() = true;
+         dec_.collect_variables() =
+            settings_.collect_variables_enabled();
 
-         if (settings_.collect_functions_enabled())
-            dec_.collect_functions() = true;
+         dec_.collect_functions() =
+            settings_.collect_functions_enabled();
 
-         if (settings_.collect_assignments_enabled())
-            dec_.collect_assignments() = true;
+         dec_.collect_assignments() =
+            settings_.collect_assignments_enabled();
 
          if (settings_.replacer_enabled())
          {
@@ -20965,11 +24488,15 @@ namespace exprtk
 
       inline bool compile(const std::string& expression_string, expression<T>& expr)
       {
-         state_          .reset();
-         error_list_     .clear();
-         brkcnt_list_    .clear();
-         synthesis_error_.clear();
-         sem_            .cleanup();
+         state_               .reset();
+         error_list_          .clear();
+         brkcnt_list_         .clear();
+         synthesis_error_     .clear();
+         immutable_memory_map_.reset();
+         immutable_symtok_map_.clear();
+         current_state_stack_ .clear();
+         assert_ids_          .clear();
+         sem_                 .cleanup();
 
          return_cleanup();
 
@@ -20977,10 +24504,10 @@ namespace exprtk
 
          if (expression_string.empty())
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          "ERR001 - Empty expression!",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               "ERR001 - Empty expression!",
+               exprtk_error_location));
 
             return false;
          }
@@ -20993,11 +24520,17 @@ namespace exprtk
 
          if (lexer().empty())
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          "ERR002 - Empty expression!",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               "ERR002 - Empty expression!",
+               exprtk_error_location));
+
+            return false;
+         }
 
+         if (halt_compilation_check())
+         {
+            exprtk_debug(("halt_compilation_check() - compile checkpoint 0\n"));
             return false;
          }
 
@@ -21006,6 +24539,12 @@ namespace exprtk
             return false;
          }
 
+         if (halt_compilation_check())
+         {
+            exprtk_debug(("halt_compilation_check() - compile checkpoint 1\n"));
+            return false;
+         }
+
          symtab_store_.symtab_list_ = expr.get_symbol_table_list();
          dec_.clear();
 
@@ -21039,11 +24578,11 @@ namespace exprtk
          {
             if (error_list_.empty())
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR003 - Invalid expression encountered",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR003 - Invalid expression encountered",
+                  exprtk_error_location));
             }
 
             if ((0 != e) && branch_deletable(e))
@@ -21062,11 +24601,8 @@ namespace exprtk
       inline expression_t compile(const std::string& expression_string, symbol_table_t& symtab)
       {
          expression_t expression;
-
          expression.register_symbol_table(symtab);
-
          compile(expression_string,expression);
-
          return expression;
       }
 
@@ -21098,11 +24634,11 @@ namespace exprtk
                   default                         : diagnostic += "Unknown compiler error";
                }
 
-               set_error(
-                  make_error(parser_error::e_lexer,
-                             lexer()[i],
-                             diagnostic + ": " + lexer()[i].value,
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_lexer,
+                  lexer()[i],
+                  diagnostic + ": " + lexer()[i].value,
+                  exprtk_error_location));
             }
          }
       }
@@ -21135,29 +24671,29 @@ namespace exprtk
                if (helper_assembly_.error_token_scanner)
                {
                   lexer::helper::bracket_checker*            bracket_checker_ptr     = 0;
-                  lexer::helper::numeric_checker*            numeric_checker_ptr     = 0;
+                  lexer::helper::numeric_checker<T>*         numeric_checker_ptr     = 0;
                   lexer::helper::sequence_validator*         sequence_validator_ptr  = 0;
                   lexer::helper::sequence_validator_3tokens* sequence_validator3_ptr = 0;
 
                   if (0 != (bracket_checker_ptr = dynamic_cast<lexer::helper::bracket_checker*>(helper_assembly_.error_token_scanner)))
                   {
-                     set_error(
-                        make_error(parser_error::e_token,
-                                   bracket_checker_ptr->error_token(),
-                                   "ERR005 - Mismatched brackets: '" + bracket_checker_ptr->error_token().value + "'",
-                                   exprtk_error_location));
+                     set_error(make_error(
+                        parser_error::e_token,
+                        bracket_checker_ptr->error_token(),
+                        "ERR005 - Mismatched brackets: '" + bracket_checker_ptr->error_token().value + "'",
+                        exprtk_error_location));
                   }
-                  else if (0 != (numeric_checker_ptr = dynamic_cast<lexer::helper::numeric_checker*>(helper_assembly_.error_token_scanner)))
+                  else if (0 != (numeric_checker_ptr = dynamic_cast<lexer::helper::numeric_checker<T>*>(helper_assembly_.error_token_scanner)))
                   {
                      for (std::size_t i = 0; i < numeric_checker_ptr->error_count(); ++i)
                      {
                         lexer::token error_token = lexer()[numeric_checker_ptr->error_index(i)];
 
-                        set_error(
-                           make_error(parser_error::e_token,
-                                      error_token,
-                                      "ERR006 - Invalid numeric token: '" + error_token.value + "'",
-                                      exprtk_error_location));
+                        set_error(make_error(
+                           parser_error::e_token,
+                           error_token,
+                           "ERR006 - Invalid numeric token: '" + error_token.value + "'",
+                           exprtk_error_location));
                      }
 
                      if (numeric_checker_ptr->error_count())
@@ -21171,13 +24707,13 @@ namespace exprtk
                      {
                         std::pair<lexer::token,lexer::token> error_token = sequence_validator_ptr->error(i);
 
-                        set_error(
-                           make_error(parser_error::e_token,
-                                      error_token.first,
-                                      "ERR007 - Invalid token sequence: '" +
-                                      error_token.first.value  + "' and '" +
-                                      error_token.second.value + "'",
-                                      exprtk_error_location));
+                        set_error(make_error(
+                           parser_error::e_token,
+                           error_token.first,
+                           "ERR007 - Invalid token sequence: '" +
+                           error_token.first.value  + "' and '" +
+                           error_token.second.value + "'",
+                           exprtk_error_location));
                      }
 
                      if (sequence_validator_ptr->error_count())
@@ -21191,13 +24727,13 @@ namespace exprtk
                      {
                         std::pair<lexer::token,lexer::token> error_token = sequence_validator3_ptr->error(i);
 
-                        set_error(
-                           make_error(parser_error::e_token,
-                                      error_token.first,
-                                      "ERR008 - Invalid token sequence: '" +
-                                      error_token.first.value  + "' and '" +
-                                      error_token.second.value + "'",
-                                      exprtk_error_location));
+                        set_error(make_error(
+                           parser_error::e_token,
+                           error_token.first,
+                           "ERR008 - Invalid token sequence: '" +
+                           error_token.first.value  + "' and '" +
+                           error_token.second.value + "'",
+                           exprtk_error_location));
                      }
 
                      if (sequence_validator3_ptr->error_count())
@@ -21222,9 +24758,11 @@ namespace exprtk
       inline parser_error::type get_error(const std::size_t& index) const
       {
          if (index < error_list_.size())
+         {
             return error_list_[index];
-         else
-            throw std::invalid_argument("parser::get_error() - Invalid error index specificed");
+         }
+
+         throw std::invalid_argument("parser::get_error() - Invalid error index specified");
       }
 
       inline std::string error() const
@@ -21293,11 +24831,41 @@ namespace exprtk
          loop_runtime_check_ = &lrtchk;
       }
 
+      inline void register_vector_access_runtime_check(vector_access_runtime_check& vartchk)
+      {
+         vector_access_runtime_check_ = &vartchk;
+      }
+
+      inline void register_compilation_timeout_check(compilation_check& compchk)
+      {
+         compilation_check_ptr_ = &compchk;
+      }
+
+      inline void register_assert_check(assert_check& assrt_chck)
+      {
+         assert_check_ = &assrt_chck;
+      }
+
       inline void clear_loop_runtime_check()
       {
          loop_runtime_check_ = loop_runtime_check_ptr(0);
       }
 
+      inline void clear_vector_access_runtime_check()
+      {
+         vector_access_runtime_check_ = vector_access_runtime_check_ptr(0);
+      }
+
+      inline void clear_compilation_timeout_check()
+      {
+         compilation_check_ptr_ = compilation_check_ptr(0);
+      }
+
+      inline void clear_assert_check()
+      {
+         assert_check_ = assert_check_ptr(0);
+      }
+
    private:
 
       inline bool valid_base_operation(const std::string& symbol) const
@@ -21387,7 +24955,7 @@ namespace exprtk
          scoped_vec_delete<expression_node_t> sdd((*this),arg_list);
 
          lexer::token begin_token;
-         lexer::token   end_token;
+         lexer::token end_token;
 
          for ( ; ; )
          {
@@ -21401,11 +24969,11 @@ namespace exprtk
             {
                if (error_list_.empty())
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR009 - Invalid expression encountered",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR009 - Invalid expression encountered",
+                     exprtk_error_location));
                }
 
                return error_node();
@@ -21433,15 +25001,31 @@ namespace exprtk
                exprtk_debug(("-------------------------------------------------\n"));
             }
 
-            if (lexer().finished())
-               break;
-            else if (token_is(token_t::e_eof,prsrhlpr_t::e_hold))
+            if (token_is(token_t::e_eof,prsrhlpr_t::e_hold))
             {
                if (lexer().finished())
                   break;
                else
                   next_token();
             }
+            else if (
+                      !settings_.commutative_check_enabled() &&
+                      (
+                        current_token().type == token_t::e_symbol ||
+                        current_token().type == token_t::e_number ||
+                        current_token().type == token_t::e_string ||
+                        token_is_bracket(prsrhlpr_t::e_hold)
+                      )
+                    )
+            {
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR010 - Invalid syntax '" + current_token().value  + "' possible missing operator or context",
+                  exprtk_error_location));
+
+               return error_node();
+            }
          }
 
          if (
@@ -21459,13 +25043,17 @@ namespace exprtk
          return result;
       }
 
-      std::string construct_subexpr(lexer::token& begin_token, lexer::token& end_token)
+      std::string construct_subexpr(lexer::token& begin_token,
+                                    lexer::token& end_token,
+                                    const bool cleanup_whitespace = true)
       {
          std::string result = lexer().substr(begin_token.position,end_token.position);
-
-         for (std::size_t i = 0; i < result.size(); ++i)
+         if (cleanup_whitespace)
          {
-            if (details::is_whitespace(result[i])) result[i] = ' ';
+            for (std::size_t i = 0; i < result.size(); ++i)
+            {
+               if (details::is_whitespace(result[i])) result[i] = ' ';
+            }
          }
 
          return result;
@@ -21477,11 +25065,13 @@ namespace exprtk
       {
          inline void set(const precedence_level& l,
                          const precedence_level& r,
-                         const details::operator_type& o)
+                         const details::operator_type& o,
+                         const token_t tkn = token_t())
          {
-            left  = l;
-            right = r;
+            left      = l;
+            right     = r;
             operation = o;
+            token     = tkn;
          }
 
          inline void reset()
@@ -21494,10 +25084,58 @@ namespace exprtk
          precedence_level left;
          precedence_level right;
          details::operator_type operation;
+         token_t token;
       };
 
+      inline void push_current_state(const state_t current_state)
+      {
+         current_state_stack_.push_back(current_state);
+      }
+
+      inline void pop_current_state()
+      {
+         if (!current_state_stack_.empty())
+         {
+            current_state_stack_.pop_back();
+         }
+      }
+
+      inline state_t current_state() const
+      {
+         return (!current_state_stack_.empty()) ?
+                current_state_stack_.back()     :
+                state_t();
+      }
+
+      inline bool halt_compilation_check()
+      {
+         compilation_check::compilation_context context;
+
+         if (compilation_check_ptr_ && !compilation_check_ptr_->continue_compilation(context))
+         {
+            const std::string error_message =
+               !context.error_message.empty() ? " Details: " + context.error_message : "";
+
+            set_error(make_error(
+               parser_error::e_parser,
+               token_t(),
+               "ERR011 - Internal compilation check failed." + error_message,
+               exprtk_error_location));
+
+            return true;
+         }
+
+         return false;
+      }
+
       inline expression_node_ptr parse_expression(precedence_level precedence = e_level00)
       {
+         if (halt_compilation_check())
+         {
+            exprtk_debug(("halt_compilation_check() - parse_expression checkpoint 2\n"));
+            return error_node();
+         }
+
          stack_limit_handler slh(*this);
 
          if (!slh)
@@ -21512,6 +25150,11 @@ namespace exprtk
             return error_node();
          }
 
+         if (token_is(token_t::e_eof,prsrhlpr_t::e_hold))
+         {
+            return expression;
+         }
+
          bool break_loop = false;
 
          state_t current_state;
@@ -21522,110 +25165,111 @@ namespace exprtk
 
             switch (current_token().type)
             {
-               case token_t::e_assign : current_state.set(e_level00,e_level00, details::e_assign); break;
-               case token_t::e_addass : current_state.set(e_level00,e_level00, details::e_addass); break;
-               case token_t::e_subass : current_state.set(e_level00,e_level00, details::e_subass); break;
-               case token_t::e_mulass : current_state.set(e_level00,e_level00, details::e_mulass); break;
-               case token_t::e_divass : current_state.set(e_level00,e_level00, details::e_divass); break;
-               case token_t::e_modass : current_state.set(e_level00,e_level00, details::e_modass); break;
-               case token_t::e_swap   : current_state.set(e_level00,e_level00, details::e_swap  ); break;
-               case token_t::e_lt     : current_state.set(e_level05,e_level06, details::    e_lt); break;
-               case token_t::e_lte    : current_state.set(e_level05,e_level06, details::   e_lte); break;
-               case token_t::e_eq     : current_state.set(e_level05,e_level06, details::    e_eq); break;
-               case token_t::e_ne     : current_state.set(e_level05,e_level06, details::    e_ne); break;
-               case token_t::e_gte    : current_state.set(e_level05,e_level06, details::   e_gte); break;
-               case token_t::e_gt     : current_state.set(e_level05,e_level06, details::    e_gt); break;
-               case token_t::e_add    : current_state.set(e_level07,e_level08, details::   e_add); break;
-               case token_t::e_sub    : current_state.set(e_level07,e_level08, details::   e_sub); break;
-               case token_t::e_div    : current_state.set(e_level10,e_level11, details::   e_div); break;
-               case token_t::e_mul    : current_state.set(e_level10,e_level11, details::   e_mul); break;
-               case token_t::e_mod    : current_state.set(e_level10,e_level11, details::   e_mod); break;
-               case token_t::e_pow    : current_state.set(e_level12,e_level12, details::   e_pow); break;
-               default                : if (token_t::e_symbol == current_token().type)
-                                        {
-                                           static const std::string s_and   =   "and";
-                                           static const std::string s_nand  =  "nand";
-                                           static const std::string s_or    =    "or";
-                                           static const std::string s_nor   =   "nor";
-                                           static const std::string s_xor   =   "xor";
-                                           static const std::string s_xnor  =  "xnor";
-                                           static const std::string s_in    =    "in";
-                                           static const std::string s_like  =  "like";
-                                           static const std::string s_ilike = "ilike";
-                                           static const std::string s_and1  =     "&";
-                                           static const std::string s_or1   =     "|";
-                                           static const std::string s_not   =   "not";
-
-                                           if (details::imatch(current_token().value,s_and))
-                                           {
-                                              current_state.set(e_level03, e_level04, details::e_and);
-                                              break;
-                                           }
-                                           else if (details::imatch(current_token().value,s_and1))
-                                           {
-                                              #ifndef exprtk_disable_sc_andor
-                                              current_state.set(e_level03, e_level04, details::e_scand);
-                                              #else
-                                              current_state.set(e_level03, e_level04, details::e_and);
-                                              #endif
-                                              break;
-                                           }
-                                           else if (details::imatch(current_token().value,s_nand))
-                                           {
-                                              current_state.set(e_level03, e_level04, details::e_nand);
-                                              break;
-                                           }
-                                           else if (details::imatch(current_token().value,s_or))
-                                           {
-                                              current_state.set(e_level01, e_level02, details::e_or);
-                                              break;
-                                           }
-                                           else if (details::imatch(current_token().value,s_or1))
-                                           {
-                                              #ifndef exprtk_disable_sc_andor
-                                              current_state.set(e_level01, e_level02, details::e_scor);
-                                              #else
-                                              current_state.set(e_level01, e_level02, details::e_or);
-                                              #endif
-                                              break;
-                                           }
-                                           else if (details::imatch(current_token().value,s_nor))
-                                           {
-                                              current_state.set(e_level01, e_level02, details::e_nor);
-                                              break;
-                                           }
-                                           else if (details::imatch(current_token().value,s_xor))
-                                           {
-                                              current_state.set(e_level01, e_level02, details::e_xor);
-                                              break;
-                                           }
-                                           else if (details::imatch(current_token().value,s_xnor))
-                                           {
-                                              current_state.set(e_level01, e_level02, details::e_xnor);
-                                              break;
-                                           }
-                                           else if (details::imatch(current_token().value,s_in))
-                                           {
-                                              current_state.set(e_level04, e_level04, details::e_in);
-                                              break;
-                                           }
-                                           else if (details::imatch(current_token().value,s_like))
-                                           {
-                                              current_state.set(e_level04, e_level04, details::e_like);
-                                              break;
-                                           }
-                                           else if (details::imatch(current_token().value,s_ilike))
-                                           {
-                                              current_state.set(e_level04, e_level04, details::e_ilike);
-                                              break;
-                                           }
-                                           else if (details::imatch(current_token().value,s_not))
-                                           {
-                                              break;
-                                           }
-                                        }
-
-                                        break_loop = true;
+               case token_t::e_assign : current_state.set(e_level00, e_level00, details::e_assign, current_token()); break;
+               case token_t::e_addass : current_state.set(e_level00, e_level00, details::e_addass, current_token()); break;
+               case token_t::e_subass : current_state.set(e_level00, e_level00, details::e_subass, current_token()); break;
+               case token_t::e_mulass : current_state.set(e_level00, e_level00, details::e_mulass, current_token()); break;
+               case token_t::e_divass : current_state.set(e_level00, e_level00, details::e_divass, current_token()); break;
+               case token_t::e_modass : current_state.set(e_level00, e_level00, details::e_modass, current_token()); break;
+               case token_t::e_swap   : current_state.set(e_level00, e_level00, details::e_swap  , current_token()); break;
+               case token_t::e_lt     : current_state.set(e_level05, e_level06, details::e_lt    , current_token()); break;
+               case token_t::e_lte    : current_state.set(e_level05, e_level06, details::e_lte   , current_token()); break;
+               case token_t::e_eq     : current_state.set(e_level05, e_level06, details::e_eq    , current_token()); break;
+               case token_t::e_ne     : current_state.set(e_level05, e_level06, details::e_ne    , current_token()); break;
+               case token_t::e_gte    : current_state.set(e_level05, e_level06, details::e_gte   , current_token()); break;
+               case token_t::e_gt     : current_state.set(e_level05, e_level06, details::e_gt    , current_token()); break;
+               case token_t::e_add    : current_state.set(e_level07, e_level08, details::e_add   , current_token()); break;
+               case token_t::e_sub    : current_state.set(e_level07, e_level08, details::e_sub   , current_token()); break;
+               case token_t::e_div    : current_state.set(e_level10, e_level11, details::e_div   , current_token()); break;
+               case token_t::e_mul    : current_state.set(e_level10, e_level11, details::e_mul   , current_token()); break;
+               case token_t::e_mod    : current_state.set(e_level10, e_level11, details::e_mod   , current_token()); break;
+               case token_t::e_pow    : current_state.set(e_level12, e_level12, details::e_pow   , current_token()); break;
+               default                :
+                  if (token_t::e_symbol == current_token().type)
+                  {
+                     static const std::string s_and   = "and"  ;
+                     static const std::string s_nand  = "nand" ;
+                     static const std::string s_or    = "or"   ;
+                     static const std::string s_nor   = "nor"  ;
+                     static const std::string s_xor   = "xor"  ;
+                     static const std::string s_xnor  = "xnor" ;
+                     static const std::string s_in    = "in"   ;
+                     static const std::string s_like  = "like" ;
+                     static const std::string s_ilike = "ilike";
+                     static const std::string s_and1  = "&"    ;
+                     static const std::string s_or1   = "|"    ;
+                     static const std::string s_not   = "not"  ;
+
+                     if (details::imatch(current_token().value,s_and))
+                     {
+                        current_state.set(e_level03, e_level04, details::e_and, current_token());
+                        break;
+                     }
+                     else if (details::imatch(current_token().value,s_and1))
+                     {
+                        #ifndef exprtk_disable_sc_andor
+                        current_state.set(e_level03, e_level04, details::e_scand, current_token());
+                        #else
+                        current_state.set(e_level03, e_level04, details::e_and, current_token());
+                        #endif
+                        break;
+                     }
+                     else if (details::imatch(current_token().value,s_nand))
+                     {
+                        current_state.set(e_level03, e_level04, details::e_nand, current_token());
+                        break;
+                     }
+                     else if (details::imatch(current_token().value,s_or))
+                     {
+                        current_state.set(e_level01, e_level02, details::e_or, current_token());
+                        break;
+                     }
+                     else if (details::imatch(current_token().value,s_or1))
+                     {
+                        #ifndef exprtk_disable_sc_andor
+                        current_state.set(e_level01, e_level02, details::e_scor, current_token());
+                        #else
+                        current_state.set(e_level01, e_level02, details::e_or, current_token());
+                        #endif
+                        break;
+                     }
+                     else if (details::imatch(current_token().value,s_nor))
+                     {
+                        current_state.set(e_level01, e_level02, details::e_nor, current_token());
+                        break;
+                     }
+                     else if (details::imatch(current_token().value,s_xor))
+                     {
+                        current_state.set(e_level01, e_level02, details::e_xor, current_token());
+                        break;
+                     }
+                     else if (details::imatch(current_token().value,s_xnor))
+                     {
+                        current_state.set(e_level01, e_level02, details::e_xnor, current_token());
+                        break;
+                     }
+                     else if (details::imatch(current_token().value,s_in))
+                     {
+                        current_state.set(e_level04, e_level04, details::e_in, current_token());
+                        break;
+                     }
+                     else if (details::imatch(current_token().value,s_like))
+                     {
+                        current_state.set(e_level04, e_level04, details::e_like, current_token());
+                        break;
+                     }
+                     else if (details::imatch(current_token().value,s_ilike))
+                     {
+                        current_state.set(e_level04, e_level04, details::e_ilike, current_token());
+                        break;
+                     }
+                     else if (details::imatch(current_token().value,s_not))
+                     {
+                        break;
+                     }
+                  }
+
+                  break_loop = true;
             }
 
             if (break_loop)
@@ -21645,49 +25289,49 @@ namespace exprtk
 
             if (is_invalid_logic_operation(current_state.operation))
             {
-               free_node(node_allocator_,expression);
+               free_node(node_allocator_, expression);
 
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             prev_token,
-                             "ERR010 - Invalid or disabled logic operation '" + details::to_str(current_state.operation) + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  prev_token,
+                  "ERR012 - Invalid or disabled logic operation '" + details::to_str(current_state.operation) + "'",
+                  exprtk_error_location));
 
                return error_node();
             }
             else if (is_invalid_arithmetic_operation(current_state.operation))
             {
-               free_node(node_allocator_,expression);
+               free_node(node_allocator_, expression);
 
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             prev_token,
-                             "ERR011 - Invalid or disabled arithmetic operation '" + details::to_str(current_state.operation) + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  prev_token,
+                  "ERR013 - Invalid or disabled arithmetic operation '" + details::to_str(current_state.operation) + "'",
+                  exprtk_error_location));
 
                return error_node();
             }
             else if (is_invalid_inequality_operation(current_state.operation))
             {
-               free_node(node_allocator_,expression);
+               free_node(node_allocator_, expression);
 
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             prev_token,
-                             "ERR012 - Invalid inequality operation '" + details::to_str(current_state.operation) + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  prev_token,
+                  "ERR014 - Invalid inequality operation '" + details::to_str(current_state.operation) + "'",
+                  exprtk_error_location));
 
                return error_node();
             }
             else if (is_invalid_assignment_operation(current_state.operation))
             {
-               free_node(node_allocator_,expression);
+               free_node(node_allocator_, expression);
 
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             prev_token,
-                             "ERR013 - Invalid or disabled assignment operation '" + details::to_str(current_state.operation) + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  prev_token,
+                  "ERR015 - Invalid or disabled assignment operation '" + details::to_str(current_state.operation) + "'",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -21695,44 +25339,48 @@ namespace exprtk
             if (0 != (right_branch = parse_expression(current_state.right)))
             {
                if (
-                    details::is_return_node(  expression) ||
+                    details::is_return_node(expression  ) ||
                     details::is_return_node(right_branch)
                   )
                {
-                  free_node(node_allocator_,   expression);
+                  free_node(node_allocator_, expression  );
                   free_node(node_allocator_, right_branch);
 
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                prev_token,
-                                "ERR014 - Return statements cannot be part of sub-expressions",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     prev_token,
+                     "ERR016 - Return statements cannot be part of sub-expressions",
+                     exprtk_error_location));
 
                   return error_node();
                }
 
+               push_current_state(current_state);
+
                new_expression = expression_generator_
                                   (
                                     current_state.operation,
                                     expression,
                                     right_branch
                                   );
+
+               pop_current_state();
             }
 
             if (0 == new_expression)
             {
                if (error_list_.empty())
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                prev_token,
-                                !synthesis_error_.empty() ?
-                                synthesis_error_ :
-                                "ERR015 - General parsing error at token: '" + prev_token.value + "'",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     prev_token,
+                     !synthesis_error_.empty() ?
+                     synthesis_error_ :
+                     "ERR017 - General parsing error at token: '" + prev_token.value + "'",
+                     exprtk_error_location));
                }
 
-               free_node(node_allocator_,   expression);
+               free_node(node_allocator_, expression  );
                free_node(node_allocator_, right_branch);
 
                return error_node();
@@ -21741,7 +25389,7 @@ namespace exprtk
             {
                if (
                     token_is(token_t::e_ternary,prsrhlpr_t::e_hold) &&
-                    (precedence == e_level00)
+                    (e_level00 == precedence)
                   )
                {
                   expression = parse_ternary_conditional_statement(new_expression);
@@ -21755,14 +25403,35 @@ namespace exprtk
 
          if ((0 != expression) && (expression->node_depth() > settings_.max_node_depth_))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                  current_token(),
-                  "ERR016 - Expression depth of " + details::to_str(static_cast<int>(expression->node_depth())) +
-                  " exceeds maximum allowed expression depth of " + details::to_str(static_cast<int>(settings_.max_node_depth_)),
-                  exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR018 - Expression depth of " + details::to_str(static_cast<int>(expression->node_depth())) +
+               " exceeds maximum allowed expression depth of " + details::to_str(static_cast<int>(settings_.max_node_depth_)),
+               exprtk_error_location));
 
-            free_node(node_allocator_,expression);
+            free_node(node_allocator_, expression);
+
+            return error_node();
+         }
+         else if (
+                   !settings_.commutative_check_enabled()          &&
+                   !details::is_logic_opr(current_token().value)   &&
+                   (current_state.operation == details::e_default) &&
+                   (
+                     current_token().type == token_t::e_symbol ||
+                     current_token().type == token_t::e_number ||
+                     current_token().type == token_t::e_string
+                   )
+                 )
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR019 - Invalid syntax '" + current_token().value  + "' possible missing operator or context",
+               exprtk_error_location));
+
+            free_node(node_allocator_, expression);
 
             return error_node();
          }
@@ -21780,7 +25449,7 @@ namespace exprtk
             {
                expression_node_ptr un_r = n->branch(0);
                n->release();
-               free_node(node_allocator_,node);
+               free_node(node_allocator_, node);
                node = un_r;
 
                return true;
@@ -21802,20 +25471,20 @@ namespace exprtk
                     (0 != (return_node = sem_         .get_variable(v)))
                   )
                {
-                  free_node(node_allocator_,node);
+                  free_node(node_allocator_, node);
                   node = return_node;
 
                   return true;
                }
                else
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR017 - Failed to find variable node in symbol table",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR020 - Failed to find variable node in symbol table",
+                     exprtk_error_location));
 
-                  free_node(node_allocator_,node);
+                  free_node(node_allocator_, node);
 
                   return false;
                }
@@ -21833,9 +25502,9 @@ namespace exprtk
       struct scoped_expression_delete
       {
          scoped_expression_delete(parser<T>& pr, expression_node_ptr& expression)
-         : delete_ptr(true),
-           parser_(pr),
-           expression_(expression)
+         : delete_ptr(true)
+         , parser_(pr)
+         , expression_(expression)
          {}
 
         ~scoped_expression_delete()
@@ -21852,7 +25521,8 @@ namespace exprtk
 
       private:
 
-         scoped_expression_delete& operator=(const scoped_expression_delete&);
+         scoped_expression_delete(const scoped_expression_delete&) exprtk_delete;
+         scoped_expression_delete& operator=(const scoped_expression_delete&) exprtk_delete;
       };
 
       template <typename Type, std::size_t N>
@@ -21861,15 +25531,15 @@ namespace exprtk
          typedef Type* ptr_t;
 
          scoped_delete(parser<T>& pr, ptr_t& p)
-         : delete_ptr(true),
-           parser_(pr),
-           p_(&p)
+         : delete_ptr(true)
+         , parser_(pr)
+         , p_(&p)
          {}
 
          scoped_delete(parser<T>& pr, ptr_t (&p)[N])
-         : delete_ptr(true),
-           parser_(pr),
-           p_(&p[0])
+         : delete_ptr(true)
+         , parser_(pr)
+         , p_(&p[0])
          {}
 
         ~scoped_delete()
@@ -21889,7 +25559,8 @@ namespace exprtk
 
       private:
 
-         scoped_delete<Type,N>& operator=(const scoped_delete<Type,N>&);
+         scoped_delete(const scoped_delete<Type,N>&) exprtk_delete;
+         scoped_delete<Type,N>& operator=(const scoped_delete<Type,N>&) exprtk_delete;
       };
 
       template <typename Type>
@@ -21898,9 +25569,9 @@ namespace exprtk
          typedef Type* ptr_t;
 
          scoped_deq_delete(parser<T>& pr, std::deque<ptr_t>& deq)
-         : delete_ptr(true),
-           parser_(pr),
-           deq_(deq)
+         : delete_ptr(true)
+         , parser_(pr)
+         , deq_(deq)
          {}
 
         ~scoped_deq_delete()
@@ -21909,6 +25580,7 @@ namespace exprtk
             {
                for (std::size_t i = 0; i < deq_.size(); ++i)
                {
+                  exprtk_debug(("~scoped_deq_delete() - deleting node: %p\n", reinterpret_cast<void*>(deq_[i])));
                   free_node(parser_.node_allocator_,deq_[i]);
                }
 
@@ -21922,7 +25594,8 @@ namespace exprtk
 
       private:
 
-         scoped_deq_delete<Type>& operator=(const scoped_deq_delete<Type>&);
+         scoped_deq_delete(const scoped_deq_delete<Type>&) exprtk_delete;
+         scoped_deq_delete<Type>& operator=(const scoped_deq_delete<Type>&) exprtk_delete;
       };
 
       template <typename Type>
@@ -21931,9 +25604,9 @@ namespace exprtk
          typedef Type* ptr_t;
 
          scoped_vec_delete(parser<T>& pr, std::vector<ptr_t>& vec)
-         : delete_ptr(true),
-           parser_(pr),
-           vec_(vec)
+         : delete_ptr(true)
+         , parser_(pr)
+         , vec_(vec)
          {}
 
         ~scoped_vec_delete()
@@ -21942,6 +25615,7 @@ namespace exprtk
             {
                for (std::size_t i = 0; i < vec_.size(); ++i)
                {
+                  exprtk_debug(("~scoped_vec_delete() - deleting node: %p\n", reinterpret_cast<void*>(vec_[i])));
                   free_node(parser_.node_allocator_,vec_[i]);
                }
 
@@ -21949,13 +25623,19 @@ namespace exprtk
             }
          }
 
+         ptr_t operator[](const std::size_t index)
+         {
+            return vec_[index];
+         }
+
          bool delete_ptr;
          parser<T>& parser_;
          std::vector<ptr_t>& vec_;
 
       private:
 
-         scoped_vec_delete<Type>& operator=(const scoped_vec_delete<Type>&);
+         scoped_vec_delete(const scoped_vec_delete<Type>&) exprtk_delete;
+         scoped_vec_delete<Type>& operator=(const scoped_vec_delete<Type>&) exprtk_delete;
       };
 
       struct scoped_bool_negator
@@ -21973,8 +25653,8 @@ namespace exprtk
       struct scoped_bool_or_restorer
       {
          explicit scoped_bool_or_restorer(bool& bb)
-         : b(bb),
-           original_value_(bb)
+         : b(bb)
+         , original_value_(bb)
          {}
 
         ~scoped_bool_or_restorer()
@@ -22029,11 +25709,11 @@ namespace exprtk
             case 19 : func_node = parse_function_call<19>(function,function_name); break;
             case 20 : func_node = parse_function_call<20>(function,function_name); break;
             default : {
-                         set_error(
-                            make_error(parser_error::e_syntax,
-                                       current_token(),
-                                       "ERR018 - Invalid number of parameters for function: '" + function_name + "'",
-                                       exprtk_error_location));
+                         set_error(make_error(
+                           parser_error::e_syntax,
+                           current_token(),
+                           "ERR021 - Invalid number of parameters for function: '" + function_name + "'",
+                           exprtk_error_location));
 
                          return error_node();
                       }
@@ -22043,11 +25723,11 @@ namespace exprtk
             return func_node;
          else
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR019 - Failed to generate call to function: '" + function_name + "'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR022 - Failed to generate call to function: '" + function_name + "'",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -22062,11 +25742,11 @@ namespace exprtk
          #endif
          if (0 == NumberofParameters)
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR020 - Expecting ifunction '" + function_name + "' to have non-zero parameter count",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR023 - Expecting ifunction '" + function_name + "' to have non-zero parameter count",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -22085,11 +25765,11 @@ namespace exprtk
 
          if (!token_is(token_t::e_lbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR021 - Expecting argument list for function: '" + function_name + "'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR024 - Expecting argument list for function: '" + function_name + "'",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -22100,11 +25780,11 @@ namespace exprtk
 
             if (0 == branch[i])
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR022 - Failed to parse argument " + details::to_str(i) + " for function: '" + function_name + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR025 - Failed to parse argument " + details::to_str(i) + " for function: '" + function_name + "'",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -22112,11 +25792,11 @@ namespace exprtk
             {
                if (!token_is(token_t::e_comma))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR023 - Invalid number of arguments for function: '" + function_name + "'",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR026 - Invalid number of arguments for function: '" + function_name + "'",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -22125,11 +25805,11 @@ namespace exprtk
 
          if (!token_is(token_t::e_rbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR024 - Invalid number of arguments for function: '" + function_name + "'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR027 - Invalid number of arguments for function: '" + function_name + "'",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -22154,13 +25834,13 @@ namespace exprtk
               !token_is(token_t::e_rbracket)
             )
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR025 - Expecting '()' to proceed call to function: '" + function_name + "'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR028 - Expecting '()' to proceed call to function: '" + function_name + "'",
+               exprtk_error_location));
 
-            free_node(node_allocator_,result);
+            free_node(node_allocator_, result);
 
             return error_node();
          }
@@ -22179,23 +25859,23 @@ namespace exprtk
 
          if (!token_is(token_t::e_lbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR026 - Expected a '(' at start of function call to '" + function_name  +
-                          "', instead got: '" + current_token().value + "'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR029 - Expected a '(' at start of function call to '" + function_name  +
+               "', instead got: '" + current_token().value + "'",
+               exprtk_error_location));
 
             return 0;
          }
 
          if (token_is(token_t::e_rbracket, e_hold))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR027 - Expected at least one input parameter for function call '" + function_name + "'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR030 - Expected at least one input parameter for function call '" + function_name + "'",
+               exprtk_error_location));
 
             return 0;
          }
@@ -22217,11 +25897,11 @@ namespace exprtk
                continue;
             else
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR028 - Expected a ',' between function input parameters, instead got: '" + current_token().value + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR031 - Expected a ',' between function input parameters, instead got: '" + current_token().value + "'",
+                  exprtk_error_location));
 
                return 0;
             }
@@ -22229,11 +25909,11 @@ namespace exprtk
 
          if (sd.delete_ptr)
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR029 - Invalid number of input parameters passed to function '" + function_name  + "'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR032 - Invalid number of input parameters passed to function '" + function_name  + "'",
+               exprtk_error_location));
 
             return 0;
          }
@@ -22252,11 +25932,11 @@ namespace exprtk
 
          if (0 == std::distance(itr_range.first,itr_range.second))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          diagnostic_token,
-                          "ERR030 - No entry found for base operation: " + operation_name,
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               diagnostic_token,
+               "ERR033 - No entry found for base operation: " + operation_name,
+               exprtk_error_location));
 
             return error_node();
          }
@@ -22299,11 +25979,11 @@ namespace exprtk
             free_node(node_allocator_, param_list[i]);
          }
 
-         set_error(
-            make_error(parser_error::e_syntax,
-                       diagnostic_token,
-                       "ERR031 - Invalid number of input parameters for call to function: '" + operation_name + "'",
-                       exprtk_error_location));
+         set_error(make_error(
+            parser_error::e_syntax,
+            diagnostic_token,
+            "ERR034 - Invalid number of input parameters for call to function: '" + operation_name + "'",
+            exprtk_error_location));
 
          return error_node();
       }
@@ -22319,75 +25999,118 @@ namespace exprtk
 
          if (!token_is(token_t::e_comma))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR032 - Expected ',' between if-statement condition and consequent",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR035 - Expected ',' between if-statement condition and consequent",
+               exprtk_error_location));
+
             result = false;
          }
          else if (0 == (consequent = parse_expression()))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR033 - Failed to parse consequent for if-statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR036 - Failed to parse consequent for if-statement",
+               exprtk_error_location));
+
             result = false;
          }
          else if (!token_is(token_t::e_comma))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR034 - Expected ',' between if-statement consequent and alternative",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR037 - Expected ',' between if-statement consequent and alternative",
+               exprtk_error_location));
+
             result = false;
          }
          else if (0 == (alternative = parse_expression()))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR035 - Failed to parse alternative for if-statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR038 - Failed to parse alternative for if-statement",
+               exprtk_error_location));
+
             result = false;
          }
          else if (!token_is(token_t::e_rbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR036 - Expected ')' at the end of if-statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR039 - Expected ')' at the end of if-statement",
+               exprtk_error_location));
+
             result = false;
          }
 
          #ifndef exprtk_disable_string_capabilities
          if (result)
          {
-            const bool consq_is_str = is_generally_string_node( consequent);
+            const bool consq_is_str = is_generally_string_node(consequent );
             const bool alter_is_str = is_generally_string_node(alternative);
 
             if (consq_is_str || alter_is_str)
             {
                if (consq_is_str && alter_is_str)
                {
-                  return expression_generator_
-                           .conditional_string(condition, consequent, alternative);
+                  expression_node_ptr result_node =
+                     expression_generator_
+                        .conditional_string(condition, consequent, alternative);
+
+                  if (result_node && result_node->valid())
+                  {
+                     return result_node;
+                  }
+
+                  set_error(make_error(
+                     parser_error::e_synthesis,
+                     current_token(),
+                     "ERR040 - Failed to synthesize node: conditional_string",
+                     exprtk_error_location));
+
+                  free_node(node_allocator_, result_node);
+                  return error_node();
                }
 
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR037 - Return types of ternary if-statement differ",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR041 - Return types of if-statement differ: string/non-string",
+                  exprtk_error_location));
 
                result = false;
             }
          }
          #endif
 
+         if (result)
+         {
+            const bool consq_is_vec = is_ivector_node(consequent );
+            const bool alter_is_vec = is_ivector_node(alternative);
+
+            if (consq_is_vec || alter_is_vec)
+            {
+               if (consq_is_vec && alter_is_vec)
+               {
+                  return expression_generator_
+                           .conditional_vector(condition, consequent, alternative);
+               }
+
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR042 - Return types of if-statement differ: vector/non-vector",
+                  exprtk_error_location));
+
+               result = false;
+            }
+         }
+
          if (!result)
          {
             free_node(node_allocator_, condition  );
@@ -22412,11 +26135,31 @@ namespace exprtk
          {
             if (0 == (consequent = parse_multi_sequence("if-statement-01")))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR038 - Failed to parse body of consequent for if-statement",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR043 - Failed to parse body of consequent for if-statement",
+                  exprtk_error_location));
+
+               result = false;
+            }
+            else if
+            (
+              !settings_.commutative_check_enabled()           &&
+              !token_is("else",prsrhlpr_t::e_hold)             &&
+              !token_is_loop(prsrhlpr_t::e_hold)               &&
+              !token_is_arithmetic_opr(prsrhlpr_t::e_hold)     &&
+              !token_is_right_bracket (prsrhlpr_t::e_hold)     &&
+              !token_is_ineq_opr      (prsrhlpr_t::e_hold)     &&
+              !token_is(token_t::e_ternary,prsrhlpr_t::e_hold) &&
+              !token_is(token_t::e_eof    ,prsrhlpr_t::e_hold)
+            )
+            {
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR044 - Expected ';' at the end of the consequent for if-statement (1)",
+                  exprtk_error_location));
 
                result = false;
             }
@@ -22433,24 +26176,24 @@ namespace exprtk
 
             if (0 != (consequent = parse_expression()))
             {
-               if (!token_is(token_t::e_eof))
+               if (!token_is(token_t::e_eof, prsrhlpr_t::e_hold))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR039 - Expected ';' at the end of the consequent for if-statement",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR045 - Expected ';' at the end of the consequent for if-statement (2)",
+                     exprtk_error_location));
 
                   result = false;
                }
             }
             else
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR040 - Failed to parse body of consequent for if-statement",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR046 - Failed to parse body of consequent for if-statement",
+                  exprtk_error_location));
 
                result = false;
             }
@@ -22458,19 +26201,27 @@ namespace exprtk
 
          if (result)
          {
-            if (details::imatch(current_token().value,"else"))
+            if (
+                 details::imatch(current_token().value,"else") ||
+                 (token_is(token_t::e_eof, prsrhlpr_t::e_hold) && peek_token_is("else"))
+               )
             {
                next_token();
 
+               if (details::imatch(current_token().value,"else"))
+               {
+                  next_token();
+               }
+
                if (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold))
                {
                   if (0 == (alternative = parse_multi_sequence("else-statement-01")))
                   {
-                     set_error(
-                        make_error(parser_error::e_syntax,
-                                   current_token(),
-                                   "ERR041 - Failed to parse body of the 'else' for if-statement",
-                                   exprtk_error_location));
+                     set_error(make_error(
+                        parser_error::e_syntax,
+                        current_token(),
+                        "ERR047 - Failed to parse body of the 'else' for if-statement",
+                        exprtk_error_location));
 
                      result = false;
                   }
@@ -22479,35 +26230,39 @@ namespace exprtk
                {
                   if (0 == (alternative = parse_conditional_statement()))
                   {
-                     set_error(
-                        make_error(parser_error::e_syntax,
-                                   current_token(),
-                                   "ERR042 - Failed to parse body of if-else statement",
-                                   exprtk_error_location));
+                     set_error(make_error(
+                        parser_error::e_syntax,
+                        current_token(),
+                        "ERR048 - Failed to parse body of if-else statement",
+                        exprtk_error_location));
 
                      result = false;
                   }
                }
                else if (0 != (alternative = parse_expression()))
                {
-                  if (!token_is(token_t::e_eof))
+                  if (
+                       !token_is(token_t::e_ternary    , prsrhlpr_t::e_hold) &&
+                       !token_is(token_t::e_rcrlbracket, prsrhlpr_t::e_hold) &&
+                       !token_is(token_t::e_eof)
+                     )
                   {
-                     set_error(
-                        make_error(parser_error::e_syntax,
-                                   current_token(),
-                                   "ERR043 - Expected ';' at the end of the 'else-if' for the if-statement",
-                                   exprtk_error_location));
+                     set_error(make_error(
+                        parser_error::e_syntax,
+                        current_token(),
+                        "ERR049 - Expected ';' at the end of the 'else-if' for the if-statement",
+                        exprtk_error_location));
 
                      result = false;
                   }
                }
                else
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR044 - Failed to parse body of the 'else' for if-statement",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR050 - Failed to parse body of the 'else' for if-statement",
+                     exprtk_error_location));
 
                   result = false;
                }
@@ -22517,7 +26272,7 @@ namespace exprtk
          #ifndef exprtk_disable_string_capabilities
          if (result)
          {
-            const bool consq_is_str = is_generally_string_node( consequent);
+            const bool consq_is_str = is_generally_string_node(consequent );
             const bool alter_is_str = is_generally_string_node(alternative);
 
             if (consq_is_str || alter_is_str)
@@ -22528,21 +26283,44 @@ namespace exprtk
                            .conditional_string(condition, consequent, alternative);
                }
 
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR045 - Return types of ternary if-statement differ",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR051 - Return types of if-statement differ: string/non-string",
+                  exprtk_error_location));
 
                result = false;
             }
          }
          #endif
 
+         if (result)
+         {
+            const bool consq_is_vec = is_ivector_node(consequent );
+            const bool alter_is_vec = is_ivector_node(alternative);
+
+            if (consq_is_vec || alter_is_vec)
+            {
+               if (consq_is_vec && alter_is_vec)
+               {
+                  return expression_generator_
+                           .conditional_vector(condition, consequent, alternative);
+               }
+
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR052 - Return types of if-statement differ: vector/non-vector",
+                  exprtk_error_location));
+
+               result = false;
+            }
+         }
+
          if (!result)
          {
-            free_node(node_allocator_,   condition);
-            free_node(node_allocator_,  consequent);
+            free_node(node_allocator_, condition  );
+            free_node(node_allocator_, consequent );
             free_node(node_allocator_, alternative);
 
             return error_node();
@@ -22560,21 +26338,21 @@ namespace exprtk
 
          if (!token_is(token_t::e_lbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR046 - Expected '(' at start of if-statement, instead got: '" + current_token().value + "'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR053 - Expected '(' at start of if-statement, instead got: '" + current_token().value + "'",
+               exprtk_error_location));
 
             return error_node();
          }
          else if (0 == (condition = parse_expression()))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR047 - Failed to parse condition for if-statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR054 - Failed to parse condition for if-statement",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -22585,30 +26363,32 @@ namespace exprtk
          }
          else if (token_is(token_t::e_rbracket))
          {
-            // 00. if (x) y;
-            // 01. if (x) y; else z;
-            // 02. if (x) y; else {z0; ... zn;}
-            // 03. if (x) y; else if (z) w;
-            // 04. if (x) y; else if (z) w; else u;
-            // 05. if (x) y; else if (z) w; else {u0; ... un;}
-            // 06. if (x) y; else if (z) {w0; ... wn;}
-            // 07. if (x) {y0; ... yn;}
-            // 08. if (x) {y0; ... yn;} else z;
-            // 09. if (x) {y0; ... yn;} else {z0; ... zn;};
-            // 10. if (x) {y0; ... yn;} else if (z) w;
-            // 11. if (x) {y0; ... yn;} else if (z) w; else u;
-            // 12. if (x) {y0; ... nex;} else if (z) w; else {u0 ... un;}
-            // 13. if (x) {y0; ... yn;} else if (z) {w0; ... wn;}
+            /*
+               00. if (x) y;
+               01. if (x) y; else z;
+               02. if (x) y; else {z0; ... zn;}
+               03. if (x) y; else if (z) w;
+               04. if (x) y; else if (z) w; else u;
+               05. if (x) y; else if (z) w; else {u0; ... un;}
+               06. if (x) y; else if (z) {w0; ... wn;}
+               07. if (x) {y0; ... yn;}
+               08. if (x) {y0; ... yn;} else z;
+               09. if (x) {y0; ... yn;} else {z0; ... zn;};
+               10. if (x) {y0; ... yn;} else if (z) w;
+               11. if (x) {y0; ... yn;} else if (z) w; else u;
+               12. if (x) {y0; ... nex;} else if (z) w; else {u0 ... un;}
+               13. if (x) {y0; ... yn;} else if (z) {w0; ... wn;}
+            */
             return parse_conditional_statement_02(condition);
          }
 
-         set_error(
-            make_error(parser_error::e_syntax,
-                       current_token(),
-                       "ERR048 - Invalid if-statement",
-                       exprtk_error_location));
+         set_error(make_error(
+            parser_error::e_syntax,
+            current_token(),
+            "ERR055 - Invalid if-statement",
+            exprtk_error_location));
 
-         free_node(node_allocator_,condition);
+         free_node(node_allocator_, condition);
 
          return error_node();
       }
@@ -22623,51 +26403,51 @@ namespace exprtk
 
          if (0 == condition)
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR049 - Encountered invalid condition branch for ternary if-statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR056 - Encountered invalid condition branch for ternary if-statement",
+               exprtk_error_location));
 
             return error_node();
          }
          else if (!token_is(token_t::e_ternary))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR050 - Expected '?' after condition of ternary if-statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR057 - Expected '?' after condition of ternary if-statement",
+               exprtk_error_location));
 
             result = false;
          }
          else if (0 == (consequent = parse_expression()))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR051 - Failed to parse consequent for ternary if-statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR058 - Failed to parse consequent for ternary if-statement",
+               exprtk_error_location));
 
             result = false;
          }
          else if (!token_is(token_t::e_colon))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR052 - Expected ':' between ternary if-statement consequent and alternative",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR059 - Expected ':' between ternary if-statement consequent and alternative",
+               exprtk_error_location));
 
             result = false;
          }
          else if (0 == (alternative = parse_expression()))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR053 - Failed to parse alternative for ternary if-statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR060 - Failed to parse alternative for ternary if-statement",
+               exprtk_error_location));
 
             result = false;
          }
@@ -22675,7 +26455,7 @@ namespace exprtk
          #ifndef exprtk_disable_string_capabilities
          if (result)
          {
-            const bool consq_is_str = is_generally_string_node( consequent);
+            const bool consq_is_str = is_generally_string_node(consequent );
             const bool alter_is_str = is_generally_string_node(alternative);
 
             if (consq_is_str || alter_is_str)
@@ -22686,21 +26466,44 @@ namespace exprtk
                            .conditional_string(condition, consequent, alternative);
                }
 
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR054 - Return types of ternary if-statement differ",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR061 - Return types of ternary differ: string/non-string",
+                  exprtk_error_location));
 
                result = false;
             }
          }
          #endif
 
+         if (result)
+         {
+            const bool consq_is_vec = is_ivector_node(consequent );
+            const bool alter_is_vec = is_ivector_node(alternative);
+
+            if (consq_is_vec || alter_is_vec)
+            {
+               if (consq_is_vec && alter_is_vec)
+               {
+                  return expression_generator_
+                           .conditional_vector(condition, consequent, alternative);
+               }
+
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR062 - Return types of ternary differ: vector/non-vector",
+                  exprtk_error_location));
+
+               result = false;
+            }
+         }
+
          if (!result)
          {
-            free_node(node_allocator_,   condition);
-            free_node(node_allocator_,  consequent);
+            free_node(node_allocator_, condition  );
+            free_node(node_allocator_, consequent );
             free_node(node_allocator_, alternative);
 
             return error_node();
@@ -22714,11 +26517,11 @@ namespace exprtk
       {
          if (settings_.logic_disabled("not"))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR055 - Invalid or disabled logic operation 'not'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR063 - Invalid or disabled logic operation 'not'",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -22726,6 +26529,12 @@ namespace exprtk
          return parse_base_operation();
       }
 
+      void handle_brkcnt_scope_exit()
+      {
+         assert(!brkcnt_list_.empty());
+         brkcnt_list_.pop_front();
+      }
+
       inline expression_node_ptr parse_while_loop()
       {
          // Parse: [while][(][test expr][)][{][expression][}]
@@ -22739,31 +26548,31 @@ namespace exprtk
 
          if (!token_is(token_t::e_lbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR056 - Expected '(' at start of while-loop condition statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR064 - Expected '(' at start of while-loop condition statement",
+               exprtk_error_location));
 
             return error_node();
          }
          else if (0 == (condition = parse_expression()))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR057 - Failed to parse condition for while-loop",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR065 - Failed to parse condition for while-loop",
+               exprtk_error_location));
 
             return error_node();
          }
          else if (!token_is(token_t::e_rbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR058 - Expected ')' at end of while-loop condition statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR066 - Expected ')' at end of while-loop condition statement",
+               exprtk_error_location));
 
             result = false;
          }
@@ -22774,40 +26583,53 @@ namespace exprtk
          {
             scoped_inc_dec sid(state_.parsing_loop_stmt_count);
 
-            if (0 == (branch = parse_multi_sequence("while-loop")))
+            if (0 == (branch = parse_multi_sequence("while-loop", true)))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR059 - Failed to parse body of while-loop"));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR067 - Failed to parse body of while-loop"));
                result = false;
             }
             else if (0 == (result_node = expression_generator_.while_loop(condition,
                                                                           branch,
                                                                           brkcnt_list_.front())))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR060 - Failed to synthesize while-loop",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR068 - Failed to synthesize while-loop",
+                  exprtk_error_location));
 
                result = false;
             }
          }
 
+         handle_brkcnt_scope_exit();
+
          if (!result)
          {
-            free_node(node_allocator_,      branch);
-            free_node(node_allocator_,   condition);
+            free_node(node_allocator_, branch     );
+            free_node(node_allocator_, condition  );
             free_node(node_allocator_, result_node);
 
-            brkcnt_list_.pop_front();
-
             return error_node();
          }
-         else
+
+         if (result_node && result_node->valid())
+         {
             return result_node;
+         }
+
+         set_error(make_error(
+            parser_error::e_synthesis,
+            current_token(),
+            "ERR069 - Failed to synthesize 'valid' while-loop",
+            exprtk_error_location));
+
+         free_node(node_allocator_, result_node);
+
+         return error_node();
       }
 
       inline expression_node_ptr parse_repeat_until_loop()
@@ -22831,7 +26653,7 @@ namespace exprtk
          }
          else
          {
-            const token_t::token_type seperator = token_t::e_eof;
+            const token_t::token_type separator = token_t::e_eof;
 
             scope_handler sh(*this);
 
@@ -22862,13 +26684,13 @@ namespace exprtk
                const bool is_next_until = peek_token_is(token_t::e_symbol) &&
                                           peek_token_is("until");
 
-               if (!token_is(seperator) && is_next_until)
+               if (!token_is(separator) && is_next_until)
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR061 - Expected '" + token_t::to_str(seperator) + "' in body of repeat until loop",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR070 - Expected '" + token_t::to_str(separator) + "' in body of repeat until loop",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -22886,13 +26708,11 @@ namespace exprtk
 
             if (sdd.delete_ptr)
             {
-               brkcnt_list_.pop_front();
-
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR062 - Failed to parse body of repeat until loop",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR071 - Failed to parse body of repeat until loop",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -22900,72 +26720,76 @@ namespace exprtk
 
          if (!token_is(token_t::e_lbracket))
          {
-            brkcnt_list_.pop_front();
-
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR063 - Expected '(' before condition statement of repeat until loop",
-                          exprtk_error_location));
-
-            free_node(node_allocator_,branch);
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR072 - Expected '(' before condition statement of repeat until loop",
+               exprtk_error_location));
 
+            free_node(node_allocator_, branch);
             return error_node();
          }
          else if (0 == (condition = parse_expression()))
          {
-            brkcnt_list_.pop_front();
-
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR064 - Failed to parse condition for repeat until loop",
-                          exprtk_error_location));
-
-            free_node(node_allocator_,branch);
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR073 - Failed to parse condition for repeat until loop",
+               exprtk_error_location));
 
+            free_node(node_allocator_, branch);
             return error_node();
          }
          else if (!token_is(token_t::e_rbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR065 - Expected ')' after condition of repeat until loop",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR074 - Expected ')' after condition of repeat until loop",
+               exprtk_error_location));
 
-            free_node(node_allocator_,    branch);
+            free_node(node_allocator_, branch   );
             free_node(node_allocator_, condition);
 
-            brkcnt_list_.pop_front();
-
             return error_node();
          }
 
-         expression_node_ptr result;
-
-         result = expression_generator_
-                     .repeat_until_loop(condition, branch, brkcnt_list_.front());
+         expression_node_ptr result_node =
+            expression_generator_
+               .repeat_until_loop(
+                  condition,
+                  branch,
+                  brkcnt_list_.front());
 
-         if (0 == result)
+         if (0 == result_node)
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR066 - Failed to synthesize repeat until loop",
-                          exprtk_error_location));
-
-            free_node(node_allocator_,condition);
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR075 - Failed to synthesize repeat until loop",
+               exprtk_error_location));
 
-            brkcnt_list_.pop_front();
+            free_node(node_allocator_, condition);
 
             return error_node();
          }
-         else
+
+         handle_brkcnt_scope_exit();
+
+         if (result_node && result_node->valid())
          {
-            brkcnt_list_.pop_front();
-            return result;
+            return result_node;
          }
+
+         set_error(make_error(
+            parser_error::e_synthesis,
+            current_token(),
+            "ERR076 - Failed to synthesize 'valid' repeat until loop",
+            exprtk_error_location));
+
+         free_node(node_allocator_, result_node);
+
+         return error_node();
       }
 
       inline expression_node_ptr parse_for_loop()
@@ -22984,11 +26808,11 @@ namespace exprtk
 
          if (!token_is(token_t::e_lbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR067 - Expected '(' at start of for-loop",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR077 - Expected '(' at start of for-loop",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -23004,21 +26828,21 @@ namespace exprtk
 
                if (!token_is(token_t::e_symbol,prsrhlpr_t::e_hold))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR068 - Expected a variable at the start of initialiser section of for-loop",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR078 - Expected a variable at the start of initialiser section of for-loop",
+                     exprtk_error_location));
 
                   return error_node();
                }
                else if (!peek_token_is(token_t::e_assign))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR069 - Expected variable assignment of initialiser section of for-loop",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR079 - Expected variable assignment of initialiser section of for-loop",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -23029,11 +26853,11 @@ namespace exprtk
 
                if ((se->name == loop_counter_symbol) && se->active)
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR070 - For-loop variable '" + loop_counter_symbol+ "' is being shadowed by a previous declaration",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR080 - For-loop variable '" + loop_counter_symbol+ "' is being shadowed by a previous declaration",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -23042,7 +26866,7 @@ namespace exprtk
                   if (
                        !se->active &&
                        (se->name == loop_counter_symbol) &&
-                       (se->type ==  scope_element::e_variable)
+                       (se->type == scope_element::e_variable)
                      )
                   {
                      se->active = true;
@@ -23061,11 +26885,11 @@ namespace exprtk
 
                      if (!sem_.add_element(nse))
                      {
-                        set_error(
-                           make_error(parser_error::e_syntax,
-                                      current_token(),
-                                      "ERR071 - Failed to add new local variable '" + loop_counter_symbol + "' to SEM",
-                                      exprtk_error_location));
+                        set_error(make_error(
+                           parser_error::e_syntax,
+                           current_token(),
+                           "ERR081 - Failed to add new local variable '" + loop_counter_symbol + "' to SEM",
+                           exprtk_error_location));
 
                         sem_.free_element(nse);
 
@@ -23073,7 +26897,7 @@ namespace exprtk
                      }
                      else
                      {
-                        exprtk_debug(("parse_for_loop() - INFO - Added new local variable: %s\n",nse.name.c_str()));
+                        exprtk_debug(("parse_for_loop() - INFO - Added new local variable: %s\n", nse.name.c_str()));
 
                         state_.activate_side_effect("parse_for_loop()");
                      }
@@ -23083,21 +26907,21 @@ namespace exprtk
 
             if (0 == (initialiser = parse_expression()))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR072 - Failed to parse initialiser of for-loop",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR082 - Failed to parse initialiser of for-loop",
+                  exprtk_error_location));
 
                result = false;
             }
             else if (!token_is(token_t::e_eof))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR073 - Expected ';' after initialiser of for-loop",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR083 - Expected ';' after initialiser of for-loop",
+                  exprtk_error_location));
 
                result = false;
             }
@@ -23107,21 +26931,21 @@ namespace exprtk
          {
             if (0 == (condition = parse_expression()))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR074 - Failed to parse condition of for-loop",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR084 - Failed to parse condition of for-loop",
+                  exprtk_error_location));
 
                result = false;
             }
             else if (!token_is(token_t::e_eof))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR075 - Expected ';' after condition section of for-loop",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR085 - Expected ';' after condition section of for-loop",
+                  exprtk_error_location));
 
                result = false;
             }
@@ -23131,21 +26955,21 @@ namespace exprtk
          {
             if (0 == (incrementor = parse_expression()))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR076 - Failed to parse incrementor of for-loop",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR086 - Failed to parse incrementor of for-loop",
+                  exprtk_error_location));
 
                result = false;
             }
             else if (!token_is(token_t::e_rbracket))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR077 - Expected ')' after incrementor section of for-loop",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR087 - Expected ')' after incrementor section of for-loop",
+                  exprtk_error_location));
 
                result = false;
             }
@@ -23157,13 +26981,13 @@ namespace exprtk
 
             scoped_inc_dec sid(state_.parsing_loop_stmt_count);
 
-            if (0 == (loop_body = parse_multi_sequence("for-loop")))
+            if (0 == (loop_body = parse_multi_sequence("for-loop", true)))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR078 - Failed to parse body of for-loop",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR088 - Failed to parse body of for-loop",
+                  exprtk_error_location));
 
                result = false;
             }
@@ -23177,29 +27001,34 @@ namespace exprtk
             }
 
             free_node(node_allocator_, initialiser);
-            free_node(node_allocator_,   condition);
+            free_node(node_allocator_, condition  );
             free_node(node_allocator_, incrementor);
-            free_node(node_allocator_,   loop_body);
-
-            if (!brkcnt_list_.empty())
-            {
-               brkcnt_list_.pop_front();
-            }
-
+            free_node(node_allocator_, loop_body  );
             return error_node();
          }
-         else
-         {
-            expression_node_ptr result_node =
-               expression_generator_.for_loop(initialiser,
-                                              condition,
-                                              incrementor,
-                                              loop_body,
-                                              brkcnt_list_.front());
-            brkcnt_list_.pop_front();
 
+         expression_node_ptr result_node =
+            expression_generator_.for_loop(initialiser,
+                                           condition,
+                                           incrementor,
+                                           loop_body,
+                                           brkcnt_list_.front());
+         handle_brkcnt_scope_exit();
+
+         if (result_node && result_node->valid())
+         {
             return result_node;
          }
+
+         set_error(make_error(
+            parser_error::e_synthesis,
+            current_token(),
+            "ERR089 - Failed to synthesize 'valid' for-loop",
+            exprtk_error_location));
+
+         free_node(node_allocator_, result_node);
+
+         return error_node();
       }
 
       inline expression_node_ptr parse_switch_statement()
@@ -23209,11 +27038,11 @@ namespace exprtk
 
          if (!details::imatch(current_token().value,"switch"))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR079 - Expected keyword 'switch'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR090 - Expected keyword 'switch'",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -23224,11 +27053,11 @@ namespace exprtk
 
          if (!token_is(token_t::e_lcrlbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR080 - Expected '{' for call to switch statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR091 - Expected '{' for call to switch statement",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -23249,18 +27078,21 @@ namespace exprtk
                   return error_node();
                else if (!token_is(token_t::e_colon))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR081 - Expected ':' for case of switch statement",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR092 - Expected ':' for case of switch statement",
+                     exprtk_error_location));
 
                   free_node(node_allocator_, condition);
 
                   return error_node();
                }
 
-               expression_node_ptr consequent = parse_expression();
+               expression_node_ptr consequent =
+                  (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold)) ?
+                  parse_multi_sequence("switch-consequent") :
+                  parse_expression();
 
                if (0 == consequent)
                {
@@ -23270,13 +27102,13 @@ namespace exprtk
                }
                else if (!token_is(token_t::e_eof))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR082 - Expected ';' at end of case for switch statement",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR093 - Expected ';' at end of case for switch statement",
+                     exprtk_error_location));
 
-                  free_node(node_allocator_,  condition);
+                  free_node(node_allocator_, condition );
                   free_node(node_allocator_, consequent);
 
                   return error_node();
@@ -23285,12 +27117,12 @@ namespace exprtk
                // Can we optimise away the case statement?
                if (is_constant_node(condition) && is_false(condition))
                {
-                  free_node(node_allocator_,  condition);
+                  free_node(node_allocator_, condition );
                   free_node(node_allocator_, consequent);
                }
                else
                {
-                  arg_list.push_back( condition);
+                  arg_list.push_back(condition );
                   arg_list.push_back(consequent);
                }
 
@@ -23299,11 +27131,11 @@ namespace exprtk
             {
                if (0 != default_statement)
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR083 - Multiple default cases for switch statement",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR094 - Multiple default cases for switch statement",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -23312,29 +27144,29 @@ namespace exprtk
 
                if (!token_is(token_t::e_colon))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR084 - Expected ':' for default of switch statement",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR095 - Expected ':' for default of switch statement",
+                     exprtk_error_location));
 
                   return error_node();
                }
 
-               if (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold))
-                  default_statement = parse_multi_sequence("switch-default");
-               else
-                  default_statement = parse_expression();
+               default_statement =
+                  (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold)) ?
+                  parse_multi_sequence("switch-default"):
+                  parse_expression();
 
                if (0 == default_statement)
                   return error_node();
                else if (!token_is(token_t::e_eof))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR085 - Expected ';' at end of default for switch statement",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR096 - Expected ';' at end of default for switch statement",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -23343,11 +27175,11 @@ namespace exprtk
                break;
             else
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR086 - Expected '}' at end of switch statement",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR097 - Expected '}' at end of switch statement",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -23359,6 +27191,10 @@ namespace exprtk
          {
             arg_list.push_back(default_statement);
          }
+         else
+         {
+            arg_list.push_back(node_allocator_.allocate_c<literal_node_t>(std::numeric_limits<T>::quiet_NaN()));
+         }
 
          result = expression_generator_.switch_statement(arg_list, (0 != default_statement));
 
@@ -23374,11 +27210,11 @@ namespace exprtk
 
          if (!details::imatch(current_token().value,"[*]"))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR087 - Expected token '[*]'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR098 - Expected token '[*]'",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -23389,11 +27225,11 @@ namespace exprtk
 
          if (!token_is(token_t::e_lcrlbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR088 - Expected '{' for call to [*] statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR099 - Expected '{' for call to [*] statement",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -23402,11 +27238,11 @@ namespace exprtk
          {
             if (!details::imatch("case",current_token().value))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR089 - Expected a 'case' statement for multi-switch",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR100 - Expected a 'case' statement for multi-switch",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -23420,27 +27256,30 @@ namespace exprtk
 
             if (!token_is(token_t::e_colon))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR090 - Expected ':' for case of [*] statement",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR101 - Expected ':' for case of [*] statement",
+                  exprtk_error_location));
 
                return error_node();
             }
 
-            expression_node_ptr consequent = parse_expression();
+            expression_node_ptr consequent =
+               (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold)) ?
+               parse_multi_sequence("multi-switch-consequent") :
+               parse_expression();
 
             if (0 == consequent)
                return error_node();
 
             if (!token_is(token_t::e_eof))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR091 - Expected ';' at end of case for [*] statement",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR102 - Expected ';' at end of case for [*] statement",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -23448,12 +27287,12 @@ namespace exprtk
             // Can we optimise away the case statement?
             if (is_constant_node(condition) && is_false(condition))
             {
-               free_node(node_allocator_,  condition);
+               free_node(node_allocator_, condition );
                free_node(node_allocator_, consequent);
             }
             else
             {
-               arg_list.push_back( condition);
+               arg_list.push_back(condition );
                arg_list.push_back(consequent);
             }
 
@@ -23465,11 +27304,11 @@ namespace exprtk
 
          if (!token_is(token_t::e_rcrlbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR092 - Expected '}' at end of [*] statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR103 - Expected '}' at end of [*] statement",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -23491,11 +27330,11 @@ namespace exprtk
          if (details::imatch(symbol,"~"))
          {
             next_token();
-            return parse_multi_sequence();
+            return check_block_statement_closure(parse_multi_sequence());
          }
          else if (details::imatch(symbol,"[*]"))
          {
-            return parse_multi_switch_statement();
+            return check_block_statement_closure(parse_multi_switch_statement());
          }
          else if (details::imatch(symbol, "avg" )) opt_type = details::e_avg ;
          else if (details::imatch(symbol, "mand")) opt_type = details::e_mand;
@@ -23506,11 +27345,11 @@ namespace exprtk
          else if (details::imatch(symbol, "sum" )) opt_type = details::e_sum ;
          else
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR093 - Unsupported vararg function: " + symbol,
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR104 - Unsupported built-in vararg function: " + symbol,
+               exprtk_error_location));
 
             return error_node();
          }
@@ -23523,11 +27362,23 @@ namespace exprtk
 
          if (!token_is(token_t::e_lbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR094 - Expected '(' for call to vararg function: " + symbol,
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR105 - Expected '(' for call to vararg function: " + symbol,
+               exprtk_error_location));
+
+            return error_node();
+         }
+
+         if (token_is(token_t::e_rbracket))
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR106 - vararg function: " + symbol +
+               " requires at least one input parameter",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -23545,11 +27396,11 @@ namespace exprtk
                break;
             else if (!token_is(token_t::e_comma))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR095 - Expected ',' for call to vararg function: " + symbol,
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR107 - Expected ',' for call to vararg function: " + symbol,
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -23566,13 +27417,13 @@ namespace exprtk
       {
          if (!token_is(token_t::e_lsqrbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR096 - Expected '[' as start of string range definition",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR108 - Expected '[' as start of string range definition",
+               exprtk_error_location));
 
-            free_node(node_allocator_,expression);
+            free_node(node_allocator_, expression);
 
             return error_node();
          }
@@ -23585,7 +27436,7 @@ namespace exprtk
 
          if (!parse_range(rp,true))
          {
-            free_node(node_allocator_,expression);
+            free_node(node_allocator_, expression);
 
             return error_node();
          }
@@ -23594,19 +27445,32 @@ namespace exprtk
 
          if (0 == result)
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR097 - Failed to generate string range node",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR109 - Failed to generate string range node",
+               exprtk_error_location));
 
-            free_node(node_allocator_,expression);
+            free_node(node_allocator_, expression);
             rp.free();
          }
 
          rp.clear();
 
-         return result;
+         if (result && result->valid())
+         {
+            return result;
+         }
+
+         set_error(make_error(
+            parser_error::e_synthesis,
+            current_token(),
+            "ERR110 - Failed to synthesize node: string_range_node",
+            exprtk_error_location));
+
+         free_node(node_allocator_, result);
+         rp.free();
+         return error_node();
       }
       #else
       inline expression_node_ptr parse_string_range_statement(expression_node_ptr&)
@@ -23615,7 +27479,7 @@ namespace exprtk
       }
       #endif
 
-      inline void parse_pending_string_rangesize(expression_node_ptr& expression)
+      inline bool parse_pending_string_rangesize(expression_node_ptr& expression)
       {
          // Allow no more than 100 range calls, eg: s[][][]...[][]
          const std::size_t max_rangesize_parses = 100;
@@ -23633,6 +27497,61 @@ namespace exprtk
          {
             expression = parse_string_range_statement(expression);
          }
+
+         return (i > 1);
+      }
+
+      inline void parse_pending_vector_index_operator(expression_node_ptr& expression)
+      {
+         if
+            (
+              (0 != expression)           &&
+              error_list_.empty()         &&
+              is_ivector_node(expression)
+            )
+         {
+            if (
+                 settings_.commutative_check_enabled()       &&
+                 token_is(token_t::e_mul,prsrhlpr_t::e_hold) &&
+                 peek_token_is(token_t::e_lsqrbracket)
+               )
+            {
+               token_is(token_t::e_mul);
+               token_is(token_t::e_lsqrbracket);
+            }
+            else if (token_is(token_t::e_lsqrbracket,prsrhlpr_t::e_hold))
+            {
+               token_is(token_t::e_lsqrbracket);
+            }
+            else if (
+                      token_is(token_t::e_rbracket,prsrhlpr_t::e_hold) &&
+                      peek_token_is(token_t::e_lsqrbracket)
+                    )
+            {
+               token_is(token_t::e_rbracket   );
+               token_is(token_t::e_lsqrbracket);
+            }
+            else
+               return;
+
+            details::vector_interface<T>* vi = dynamic_cast<details::vector_interface<T>*>(expression);
+
+            if (vi)
+            {
+               details::vector_holder<T>& vec = vi->vec()->vec_holder();
+               const std::string vector_name  = sem_.get_vector_name(vec.data());
+               expression_node_ptr index      = parse_vector_index(vector_name);
+
+               if (index)
+               {
+                  expression = synthesize_vector_element(vector_name, &vec, expression, index);
+                  return;
+               }
+            }
+
+            free_node(node_allocator_, expression);
+            expression = error_node();
+         }
       }
 
       template <typename Allocator1,
@@ -23649,6 +27568,10 @@ namespace exprtk
 
          Sequence<expression_node_ptr,Allocator1> tmp_expression_list;
 
+         exprtk_debug(("simplify() - expression_list.size: %d  side_effect_list.size(): %d\n",
+                       static_cast<int>(expression_list .size()),
+                       static_cast<int>(side_effect_list.size())));
+
          bool return_node_present = false;
 
          for (std::size_t i = 0; i < (expression_list.size() - 1); ++i)
@@ -23668,7 +27591,7 @@ namespace exprtk
 
                for (std::size_t j = i + 1; j < expression_list.size(); ++j)
                {
-                  free_node(node_allocator_,expression_list[j]);
+                  free_node(node_allocator_, expression_list[j]);
                }
 
                return_node_present = true;
@@ -23681,7 +27604,7 @@ namespace exprtk
                       !side_effect_list[i]
                     )
             {
-               free_node(node_allocator_,expression_list[i]);
+               free_node(node_allocator_, expression_list[i]);
                continue;
             }
             else
@@ -23703,8 +27626,8 @@ namespace exprtk
          }
 
          if (
-              return_node_present          ||
-              side_effect_list.back()      ||
+              return_node_present     ||
+              side_effect_list.back() ||
               (expression_list.size() > 1)
             )
             state_.activate_side_effect("simplify()");
@@ -23717,31 +27640,34 @@ namespace exprtk
             return expression_generator_.vararg_function(details::e_multi,expression_list);
       }
 
-      inline expression_node_ptr parse_multi_sequence(const std::string& source = "")
+      inline expression_node_ptr parse_multi_sequence(const std::string& source = "",
+                                                      const bool enforce_crlbrackets = false)
       {
+         token_t::token_type open_bracket  = token_t::e_lcrlbracket;
          token_t::token_type close_bracket = token_t::e_rcrlbracket;
-         token_t::token_type seperator     = token_t::e_eof;
+         token_t::token_type separator     = token_t::e_eof;
 
-         if (!token_is(token_t::e_lcrlbracket))
+         if (!token_is(open_bracket))
          {
-            if (token_is(token_t::e_lbracket))
+            if (!enforce_crlbrackets && token_is(token_t::e_lbracket))
             {
+               open_bracket  = token_t::e_lbracket;
                close_bracket = token_t::e_rbracket;
-               seperator     = token_t::e_comma;
+               separator     = token_t::e_comma;
             }
             else
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR098 - Expected '" + token_t::to_str(close_bracket) + "' for call to multi-sequence" +
-                             ((!source.empty()) ? std::string(" section of " + source): ""),
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR111 - Expected '" + token_t::to_str(open_bracket) + "' for call to multi-sequence" +
+                  ((!source.empty()) ? std::string(" section of " + source): ""),
+                  exprtk_error_location));
 
                return error_node();
             }
          }
-         else if (token_is(token_t::e_rcrlbracket))
+         else if (token_is(close_bracket))
          {
             return node_allocator_.allocate<details::null_node<T> >();
          }
@@ -23776,13 +27702,13 @@ namespace exprtk
 
             const bool is_next_close = peek_token_is(close_bracket);
 
-            if (!token_is(seperator) && is_next_close)
+            if (!token_is(separator) && is_next_close)
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR099 - Expected '" + details::to_str(seperator) + "' for call to multi-sequence section of " + source,
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR112 - Expected '" + lexer::token::seperator_to_str(separator) + "' for call to multi-sequence section of " + source,
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -23791,7 +27717,7 @@ namespace exprtk
                break;
          }
 
-         result = simplify(arg_list,side_effect_list,source.empty());
+         result = simplify(arg_list, side_effect_list, source.empty());
 
          sdd.delete_ptr = (0 == result);
          return result;
@@ -23800,23 +27726,23 @@ namespace exprtk
       inline bool parse_range(range_t& rp, const bool skip_lsqr = false)
       {
          // Examples of valid ranges:
-         // 1. [1:5]     -> 1..5
-         // 2. [ :5]     -> 0..5
-         // 3. [1: ]     -> 1..end
-         // 4. [x:y]     -> x..y where x <= y
-         // 5. [x+1:y/2] -> x+1..y/2 where x+1 <= y/2
-         // 6. [ :y]     -> 0..y where 0 <= y
-         // 7. [x: ]     -> x..end where x <= end
+         // 1. [1:5]     -> [1,5)
+         // 2. [ :5]     -> [0,5)
+         // 3. [1: ]     -> [1,end)
+         // 4. [x:y]     -> [x,y) where x <= y
+         // 5. [x+1:y/2] -> [x+1,y/2) where x+1 <= y/2
+         // 6. [ :y]     -> [0,y) where 0 <= y
+         // 7. [x: ]     -> [x,end) where x <= end
 
          rp.clear();
 
          if (!skip_lsqr && !token_is(token_t::e_lsqrbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR100 - Expected '[' for start of range",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR113 - Expected '[' for start of range",
+               exprtk_error_location));
 
             return false;
          }
@@ -23833,11 +27759,11 @@ namespace exprtk
 
             if (0 == r0)
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR101 - Failed parse begin section of range",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR114 - Failed parse begin section of range",
+                  exprtk_error_location));
 
                return false;
             }
@@ -23852,15 +27778,15 @@ namespace exprtk
                   rp.cache.first = rp.n0_c.second;
                }
 
-               free_node(node_allocator_,r0);
+               free_node(node_allocator_, r0);
 
                if (r0_value < T(0))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR102 - Range lower bound less than zero! Constraint: r0 >= 0",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR115 - Range lower bound less than zero! Constraint: r0 >= 0",
+                     exprtk_error_location));
 
                   return false;
                }
@@ -23873,11 +27799,11 @@ namespace exprtk
 
             if (!token_is(token_t::e_colon))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR103 - Expected ':' for break  in range",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR116 - Expected ':' for break  in range",
+                  exprtk_error_location));
 
                rp.free();
 
@@ -23896,11 +27822,11 @@ namespace exprtk
 
             if (0 == r1)
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR104 - Failed parse end section of range",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR117 - Failed parse end section of range",
+                  exprtk_error_location));
 
                rp.free();
 
@@ -23917,15 +27843,15 @@ namespace exprtk
                   rp.cache.second = rp.n1_c.second;
                }
 
-               free_node(node_allocator_,r1);
+               free_node(node_allocator_, r1);
 
                if (r1_value < T(0))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR105 - Range upper bound less than zero! Constraint: r1 >= 0",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR118 - Range upper bound less than zero! Constraint: r1 >= 0",
+                     exprtk_error_location));
 
                   rp.free();
 
@@ -23940,11 +27866,11 @@ namespace exprtk
 
             if (!token_is(token_t::e_rsqrbracket))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR106 - Expected ']' for start of range",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR119 - Expected ']' for start of range",
+                  exprtk_error_location));
 
                rp.free();
 
@@ -23968,11 +27894,11 @@ namespace exprtk
 
             if (!rp_result || (r0 > r1))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR107 - Invalid range, Constraint: r0 <= r1",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR120 - Invalid range, Constraint: r0 <= r1",
+                  exprtk_error_location));
 
                return false;
             }
@@ -24007,24 +27933,36 @@ namespace exprtk
          }
          else
          {
-            if (!symtab_store_.is_conststr_stringvar(symbol))
+            typedef typename symtab_store::string_context str_ctxt_t;
+            str_ctxt_t str_ctx = symtab_store_.get_string_context(symbol);
+
+            if ((0 == str_ctx.str_var) || !symtab_store_.is_conststr_stringvar(symbol))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR108 - Unknown string symbol",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR121 - Unknown string symbol",
+                  exprtk_error_location));
 
                return error_node();
             }
 
-            result = symtab_store_.get_stringvar(symbol);
+            assert(str_ctx.str_var != 0);
+            assert(str_ctx.symbol_table != 0);
+
+            result = str_ctx.str_var;
 
             if (symtab_store_.is_constant_string(symbol))
             {
                const_str_node = static_cast<strvar_node_t>(result);
                result = expression_generator_(const_str_node->str());
             }
+            else if (symbol_table_t::e_immutable == str_ctx.symbol_table->mutability())
+            {
+               lodge_immutable_symbol(
+                  current_token(),
+                  make_memory_range(str_ctx.str_var->base(), str_ctx.str_var->size()));
+            }
 
             lodge_symbol(symbol, e_st_string);
          }
@@ -24040,7 +27978,7 @@ namespace exprtk
 
                if (const_str_node)
                {
-                  free_node(node_allocator_,result);
+                  free_node(node_allocator_, result);
 
                   return expression_generator_(T(const_str_node->size()));
                }
@@ -24053,13 +27991,13 @@ namespace exprtk
 
             if (!parse_range(rp))
             {
-               free_node(node_allocator_,result);
+               free_node(node_allocator_, result);
 
                return error_node();
             }
             else if (const_str_node)
             {
-               free_node(node_allocator_,result);
+               free_node(node_allocator_, result);
                result = expression_generator_(const_str_node->ref(),rp);
             }
             else
@@ -24096,7 +28034,7 @@ namespace exprtk
                next_token();
                next_token();
 
-               free_node(node_allocator_,result);
+               free_node(node_allocator_, result);
 
                return expression_generator_(T(const_str.size()));
             }
@@ -24105,13 +28043,13 @@ namespace exprtk
 
             if (!parse_range(rp))
             {
-               free_node(node_allocator_,result);
+               free_node(node_allocator_, result);
                rp.free();
 
                return error_node();
             }
 
-            free_node(node_allocator_,result);
+            free_node(node_allocator_, result);
 
             if (rp.n1_c.first && (rp.n1_c.second == std::numeric_limits<std::size_t>::max()))
             {
@@ -24124,13 +28062,13 @@ namespace exprtk
                  (rp.n1_c.first && (rp.n1_c.second >= const_str.size()))
                )
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR109 - Overflow in range for string: '" + const_str + "'[" +
-                             (rp.n0_c.first ? details::to_str(static_cast<int>(rp.n0_c.second)) : "?") + ":" +
-                             (rp.n1_c.first ? details::to_str(static_cast<int>(rp.n1_c.second)) : "?") + "]",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR122 - Overflow in range for string: '" + const_str + "'[" +
+                  (rp.n0_c.first ? details::to_str(static_cast<int>(rp.n0_c.second)) : "?") + ":" +
+                  (rp.n1_c.first ? details::to_str(static_cast<int>(rp.n1_c.second)) : "?") + "]",
+                  exprtk_error_location));
 
                rp.free();
 
@@ -24154,35 +28092,82 @@ namespace exprtk
       }
       #endif
 
+      inline expression_node_ptr parse_vector_index(const std::string& vector_name = "")
+      {
+         expression_node_ptr index_expr = error_node();
+
+         if (0 == (index_expr = parse_expression()))
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR123 - Failed to parse index for vector: '" + vector_name + "'",
+               exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!token_is(token_t::e_rsqrbracket))
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR124 - Expected ']' for index of vector: '" + vector_name + "'",
+               exprtk_error_location));
+
+            free_node(node_allocator_, index_expr);
+
+            return error_node();
+         }
+
+         return index_expr;
+      }
+
       inline expression_node_ptr parse_vector()
       {
-         const std::string symbol = current_token().value;
+         const std::string vector_name = current_token().value;
 
          vector_holder_ptr vec = vector_holder_ptr(0);
 
-         const scope_element& se = sem_.get_active_element(symbol);
+         const scope_element& se = sem_.get_active_element(vector_name);
 
          if (
-              !details::imatch(se.name, symbol) ||
+              !details::imatch(se.name, vector_name) ||
               (se.depth > state_.scope_depth)   ||
               (scope_element::e_vector != se.type)
             )
          {
-            if (0 == (vec = symtab_store_.get_vector(symbol)))
+            typedef typename symtab_store::vector_context vec_ctxt_t;
+            vec_ctxt_t vec_ctx = symtab_store_.get_vector_context(vector_name);
+
+            if (0 == vec_ctx.vector_holder)
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR110 - Symbol '" + symbol+ " not a vector",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR125 - Symbol '" + vector_name + " not a vector",
+                  exprtk_error_location));
 
                return error_node();
             }
+
+            assert(0 != vec_ctx.vector_holder);
+            assert(0 != vec_ctx.symbol_table );
+
+            vec = vec_ctx.vector_holder;
+
+            if (symbol_table_t::e_immutable == vec_ctx.symbol_table->mutability())
+            {
+               lodge_immutable_symbol(
+                  current_token(),
+                  make_memory_range(vec->data(), vec->size()));
+            }
          }
          else
+         {
             vec = se.vec_node;
+         }
 
-         expression_node_ptr index_expr = error_node();
+         assert(0 != vec);
 
          next_token();
 
@@ -24192,31 +28177,28 @@ namespace exprtk
          }
          else if (token_is(token_t::e_rsqrbracket))
          {
-            return expression_generator_(T(vec->size()));
+            return (vec->rebaseable()) ?
+               node_allocator_.allocate<vector_size_node_t>(vec) :
+               expression_generator_(T(vec->size()));
          }
-         else if (0 == (index_expr = parse_expression()))
-         {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR111 - Failed to parse index for vector: '" + symbol + "'",
-                          exprtk_error_location));
 
-            return error_node();
-         }
-         else if (!token_is(token_t::e_rsqrbracket))
-         {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR112 - Expected ']' for index of vector: '" + symbol + "'",
-                          exprtk_error_location));
+         expression_node_ptr index_expr = parse_vector_index(vector_name);
 
-            free_node(node_allocator_,index_expr);
+         if (index_expr)
+         {
+            expression_node_ptr vec_node = node_allocator_.allocate<vector_node_t>(vec);
 
-            return error_node();
+            return synthesize_vector_element(vector_name, vec, vec_node, index_expr);
          }
 
+         return error_node();
+      }
+
+      inline expression_node_ptr synthesize_vector_element(const std::string& vector_name,
+                                                           vector_holder_ptr vec,
+                                                           expression_node_ptr vec_node,
+                                                           expression_node_ptr index_expr)
+      {
          // Perform compile-time range check
          if (details::is_constant_node(index_expr))
          {
@@ -24225,20 +28207,21 @@ namespace exprtk
 
             if (index >= vec_size)
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR113 - Index of " + details::to_str(index) + " out of range for "
-                             "vector '" + symbol + "' of size " + details::to_str(vec_size),
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR126 - Index of " + details::to_str(index) + " out of range for "
+                  "vector '" + vector_name + "' of size " + details::to_str(vec_size),
+                  exprtk_error_location));
 
-               free_node(node_allocator_,index_expr);
+               free_node(node_allocator_, vec_node  );
+               free_node(node_allocator_, index_expr);
 
                return error_node();
             }
          }
 
-         return expression_generator_.vector_element(symbol, vec, index_expr);
+         return expression_generator_.vector_element(vector_name, vec, vec_node, index_expr);
       }
 
       inline expression_node_ptr parse_vararg_function_call(ivararg_function<T>* vararg_function, const std::string& vararg_function_name)
@@ -24257,12 +28240,12 @@ namespace exprtk
             {
                if (!vararg_function->allow_zero_parameters())
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR114 - Zero parameter call to vararg function: "
-                                + vararg_function_name + " not allowed",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR127 - Zero parameter call to vararg function: "
+                     + vararg_function_name + " not allowed",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -24282,12 +28265,12 @@ namespace exprtk
                      break;
                   else if (!token_is(token_t::e_comma))
                   {
-                     set_error(
-                        make_error(parser_error::e_syntax,
-                                   current_token(),
-                                   "ERR115 - Expected ',' for call to vararg function: "
-                                   + vararg_function_name,
-                                   exprtk_error_location));
+                     set_error(make_error(
+                        parser_error::e_syntax,
+                        current_token(),
+                        "ERR128 - Expected ',' for call to vararg function: "
+                        + vararg_function_name,
+                        exprtk_error_location));
 
                      return error_node();
                   }
@@ -24296,37 +28279,37 @@ namespace exprtk
          }
          else if (!vararg_function->allow_zero_parameters())
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR116 - Zero parameter call to vararg function: "
-                          + vararg_function_name + " not allowed",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR129 - Zero parameter call to vararg function: "
+               + vararg_function_name + " not allowed",
+               exprtk_error_location));
 
             return error_node();
          }
 
          if (arg_list.size() < vararg_function->min_num_args())
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR117 - Invalid number of parameters to call to vararg function: "
-                          + vararg_function_name + ", require at least "
-                          + details::to_str(static_cast<int>(vararg_function->min_num_args())) + " parameters",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR130 - Invalid number of parameters to call to vararg function: "
+               + vararg_function_name + ", require at least "
+               + details::to_str(static_cast<int>(vararg_function->min_num_args())) + " parameters",
+               exprtk_error_location));
 
             return error_node();
          }
          else if (arg_list.size() > vararg_function->max_num_args())
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR118 - Invalid number of parameters to call to vararg function: "
-                          + vararg_function_name + ", require no more than "
-                          + details::to_str(static_cast<int>(vararg_function->max_num_args())) + " parameters",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR131 - Invalid number of parameters to call to vararg function: "
+               + vararg_function_name + ", require no more than "
+               + details::to_str(static_cast<int>(vararg_function->max_num_args())) + " parameters",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -24362,10 +28345,10 @@ namespace exprtk
                       const std::string& func_name,
                       const std::string& func_prototypes,
                       const return_type_t default_return_type)
-         : invalid_state_(true),
-           parser_(p),
-           function_name_(func_name),
-           default_return_type_(default_return_type)
+         : invalid_state_(true)
+         , parser_(p)
+         , function_name_(func_name)
+         , default_return_type_(default_return_type)
          {
             parse_function_prototypes(func_prototypes);
          }
@@ -24397,14 +28380,13 @@ namespace exprtk
 
             if (1 == error_list.size())
             {
-               parser_.
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                parser_.current_token(),
-                                "ERR119 - Failed parameter type check for function '" + function_name_ + "', "
-                                "Expected '" + function_definition_list_[0].param_seq +
-                                "'  call set: '" + param_seq + "'",
-                                exprtk_error_location));
+               parser_.set_error(make_error(
+                  parser_error::e_syntax,
+                  parser_.current_token(),
+                  "ERR132 - Failed parameter type check for function '" + function_name_ + "', "
+                  "Expected '" + function_definition_list_[0].param_seq +
+                  "' call set: '" + param_seq + "'",
+                  exprtk_error_location));
             }
             else
             {
@@ -24419,14 +28401,13 @@ namespace exprtk
                   }
                }
 
-               parser_.
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                parser_.current_token(),
-                                "ERR120 - Failed parameter type check for function '" + function_name_ + "', "
-                                "Best match: '" + function_definition_list_[max_diff_index].param_seq +
-                                "'  call set: '" + param_seq + "'",
-                                exprtk_error_location));
+               parser_.set_error(make_error(
+                  parser_error::e_syntax,
+                  parser_.current_token(),
+                  "ERR133 - Failed parameter type check for function '" + function_name_ + "', "
+                  "Best match: '" + function_definition_list_[max_diff_index].param_seq +
+                  "' call set: '" + param_seq + "'",
+                  exprtk_error_location));
             }
 
             return false;
@@ -24561,13 +28542,12 @@ namespace exprtk
                {
                   invalid_state_ = false;
 
-                  parser_.
-                     set_error(
-                        make_error(parser_error::e_syntax,
-                                   parser_.current_token(),
-                                   "ERR121 - Invalid parameter sequence of '" + param_seq_list[i] +
-                                   "' for function: " + function_name_,
-                                   exprtk_error_location));
+                  parser_.set_error(make_error(
+                     parser_error::e_syntax,
+                     parser_.current_token(),
+                     "ERR134 - Invalid parameter sequence of '" + param_seq_list[i] +
+                     "' for function: " + function_name_,
+                     exprtk_error_location));
                   return;
                }
 
@@ -24577,15 +28557,14 @@ namespace exprtk
                {
                   invalid_state_ = false;
 
-                  parser_.
-                     set_error(
-                        make_error(parser_error::e_syntax,
-                                   parser_.current_token(),
-                                   "ERR122 - Function '" + function_name_ + "' has a parameter sequence conflict between " +
-                                   "pseq_idx[" + details::to_str(seq_itr->second) + "] and" +
-                                   "pseq_idx[" + details::to_str(i) + "] " +
-                                   "param seq: " + param_seq_list[i],
-                                   exprtk_error_location));
+                  parser_.set_error(make_error(
+                     parser_error::e_syntax,
+                     parser_.current_token(),
+                     "ERR135 - Function '" + function_name_ + "' has a parameter sequence conflict between " +
+                     "pseq_idx[" + details::to_str(seq_itr->second) + "] and" +
+                     "pseq_idx[" + details::to_str(i) + "] " +
+                     "param seq: " + param_seq_list[i],
+                     exprtk_error_location));
                   return;
                }
 
@@ -24593,8 +28572,8 @@ namespace exprtk
             }
          }
 
-         type_checker(const type_checker&);
-         type_checker& operator=(const type_checker&);
+         type_checker(const type_checker&) exprtk_delete;
+         type_checker& operator=(const type_checker&) exprtk_delete;
 
          bool invalid_state_;
          parser_t& parser_;
@@ -24613,15 +28592,19 @@ namespace exprtk
 
          std::string param_type_list;
 
-         type_checker tc((*this), function_name, function->parameter_sequence, type_checker::e_string);
+         type_checker tc(
+            (*this),
+            function_name,
+            function->parameter_sequence,
+            type_checker::e_string);
 
          if (tc.invalid())
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR123 - Type checker instantiation failure for generic function: " + function_name,
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR136 - Type checker instantiation failure for generic function: " + function_name,
+               exprtk_error_location));
 
             return error_node();
          }
@@ -24635,12 +28618,12 @@ namespace exprtk
                     !tc       .allow_zero_parameters()
                   )
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR124 - Zero parameter call to generic function: "
-                                + function_name + " not allowed",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR137 - Zero parameter call to generic function: "
+                     + function_name + " not allowed",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -24667,11 +28650,11 @@ namespace exprtk
                      break;
                   else if (!token_is(token_t::e_comma))
                   {
-                     set_error(
-                        make_error(parser_error::e_syntax,
-                                   current_token(),
-                                   "ERR125 - Expected ',' for call to generic function: " + function_name,
-                                   exprtk_error_location));
+                     set_error(make_error(
+                        parser_error::e_syntax,
+                        current_token(),
+                        "ERR138 - Expected ',' for call to generic function: " + function_name,
+                        exprtk_error_location));
 
                      return error_node();
                   }
@@ -24684,12 +28667,12 @@ namespace exprtk
                    !tc      .allow_zero_parameters    ()
                  )
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR126 - Zero parameter call to generic function: "
-                          + function_name + " not allowed",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR139 - Zero parameter call to generic function: "
+               + function_name + " not allowed",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -24701,22 +28684,21 @@ namespace exprtk
               !tc.verify(param_type_list, param_seq_index)
             )
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR127 - Invalid input parameter sequence for call to generic function: " + function_name,
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR140 - Invalid input parameter sequence for call to generic function: " + function_name,
+               exprtk_error_location));
 
             return error_node();
          }
 
          expression_node_ptr result = error_node();
 
-         if (tc.paramseq_count() <= 1)
-            result = expression_generator_
-                       .generic_function_call(function, arg_list);
-         else
-            result = expression_generator_
+         result = (tc.paramseq_count() <= 1) ?
+                  expression_generator_
+                       .generic_function_call(function, arg_list) :
+                  expression_generator_
                        .generic_function_call(function, arg_list, param_seq_index);
 
          sdd.delete_ptr = (0 == result);
@@ -24739,12 +28721,12 @@ namespace exprtk
                     !tc       .allow_zero_parameters()
                   )
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR128 - Zero parameter call to generic function: "
-                                + function_name + " not allowed",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR141 - Zero parameter call to generic function: "
+                     + function_name + " not allowed",
+                     exprtk_error_location));
 
                   return false;
                }
@@ -24771,11 +28753,11 @@ namespace exprtk
                      break;
                   else if (!token_is(token_t::e_comma))
                   {
-                     set_error(
-                        make_error(parser_error::e_syntax,
-                                   current_token(),
-                                   "ERR129 - Expected ',' for call to string function: " + function_name,
-                                   exprtk_error_location));
+                     set_error(make_error(
+                        parser_error::e_syntax,
+                        current_token(),
+                        "ERR142 - Expected ',' for call to string function: " + function_name,
+                        exprtk_error_location));
 
                      return false;
                   }
@@ -24818,22 +28800,21 @@ namespace exprtk
 
          if (!tc.verify(param_type_list, param_seq_index))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR130 - Invalid input parameter sequence for call to string function: " + function_name,
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR143 - Invalid input parameter sequence for call to string function: " + function_name,
+               exprtk_error_location));
 
             return error_node();
          }
 
          expression_node_ptr result = error_node();
 
-         if (tc.paramseq_count() <= 1)
-            result = expression_generator_
-                       .string_function_call(function, arg_list);
-         else
-            result = expression_generator_
+         result = (tc.paramseq_count() <= 1) ?
+                  expression_generator_
+                       .string_function_call(function, arg_list) :
+                  expression_generator_
                        .string_function_call(function, arg_list, param_seq_index);
 
          sdd.delete_ptr = (0 == result);
@@ -24870,11 +28851,11 @@ namespace exprtk
 
          if (!tc.verify(param_type_list, param_seq_index))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR131 - Invalid input parameter sequence for call to overloaded function: " + function_name,
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR144 - Invalid input parameter sequence for call to overloaded function: " + function_name,
+               exprtk_error_location));
 
             return error_node();
          }
@@ -24901,11 +28882,11 @@ namespace exprtk
          }
          else
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR132 - Invalid return type for call to overloaded function: " + function_name,
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR145 - Invalid return type for call to overloaded function: " + function_name,
+               exprtk_error_location));
          }
 
          sdd.delete_ptr = (0 == result);
@@ -24929,11 +28910,11 @@ namespace exprtk
 
             if (!p.token_is(token_t::e_lbracket))
             {
-               p.set_error(
-                    make_error(parser_error::e_syntax,
-                               p.current_token(),
-                               "ERR133 - Expected '(' for special function '" + sf_name + "'",
-                               exprtk_error_location));
+               p.set_error(make_error(
+                  parser_error::e_syntax,
+                  p.current_token(),
+                  "ERR146 - Expected '(' for special function '" + sf_name + "'",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -24950,11 +28931,11 @@ namespace exprtk
                {
                   if (!p.token_is(token_t::e_comma))
                   {
-                     p.set_error(
-                          make_error(parser_error::e_syntax,
-                                     p.current_token(),
-                                     "ERR134 - Expected ',' before next parameter of special function '" + sf_name + "'",
-                                     exprtk_error_location));
+                     p.set_error(make_error(
+                        parser_error::e_syntax,
+                        p.current_token(),
+                        "ERR147 - Expected ',' before next parameter of special function '" + sf_name + "'",
+                        exprtk_error_location));
 
                      return p.error_node();
                   }
@@ -24963,11 +28944,11 @@ namespace exprtk
 
             if (!p.token_is(token_t::e_rbracket))
             {
-               p.set_error(
-                    make_error(parser_error::e_syntax,
-                               p.current_token(),
-                               "ERR135 - Invalid number of parameters for special function '" + sf_name + "'",
-                               exprtk_error_location));
+               p.set_error(make_error(
+                  parser_error::e_syntax,
+                  p.current_token(),
+                  "ERR148 - Invalid number of parameters for special function '" + sf_name + "'",
+                  exprtk_error_location));
 
                return p.error_node();
             }
@@ -24990,11 +28971,11 @@ namespace exprtk
               !details::is_digit(sf_name[3])
             )
          {
-            set_error(
-               make_error(parser_error::e_token,
-                          current_token(),
-                          "ERR136 - Invalid special function[1]: " + sf_name,
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_token,
+               current_token(),
+               "ERR149 - Invalid special function[1]: " + sf_name,
+               exprtk_error_location));
 
             return error_node();
          }
@@ -25004,11 +28985,11 @@ namespace exprtk
 
          if (id >= details::e_sffinal)
          {
-            set_error(
-               make_error(parser_error::e_token,
-                          current_token(),
-                          "ERR137 - Invalid special function[2]: " + sf_name,
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_token,
+               current_token(),
+               "ERR150 - Invalid special function[2]: " + sf_name,
+               exprtk_error_location));
 
             return error_node();
          }
@@ -25036,21 +29017,21 @@ namespace exprtk
       {
          if (state_.parsing_break_stmt)
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR138 - Invoking 'break' within a break call is not allowed",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR151 - Invoking 'break' within a break call is not allowed",
+               exprtk_error_location));
 
             return error_node();
          }
          else if (0 == state_.parsing_loop_stmt_count)
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR139 - Invalid use of 'break', allowed only in the scope of a loop",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR152 - Invalid use of 'break', allowed only in the scope of a loop",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -25069,23 +29050,23 @@ namespace exprtk
             {
                if (0 == (return_expr = parse_expression()))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR140 - Failed to parse return expression for 'break' statement",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR153 - Failed to parse return expression for 'break' statement",
+                     exprtk_error_location));
 
                   return error_node();
                }
                else if (!token_is(token_t::e_rsqrbracket))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR141 - Expected ']' at the completion of break's return expression",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR154 - Expected ']' at the completion of break's return expression",
+                     exprtk_error_location));
 
-                  free_node(node_allocator_,return_expr);
+                  free_node(node_allocator_, return_expr);
 
                   return error_node();
                }
@@ -25097,11 +29078,11 @@ namespace exprtk
          }
          else
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR142 - Invalid use of 'break', allowed only in the scope of a loop",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR155 - Invalid use of 'break', allowed only in the scope of a loop",
+               exprtk_error_location));
          }
 
          return error_node();
@@ -25111,11 +29092,11 @@ namespace exprtk
       {
          if (0 == state_.parsing_loop_stmt_count)
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR143 - Invalid use of 'continue', allowed only in the scope of a loop",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR156 - Invalid use of 'continue', allowed only in the scope of a loop",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -25133,123 +29114,222 @@ namespace exprtk
 
       inline expression_node_ptr parse_define_vector_statement(const std::string& vec_name)
       {
-         expression_node_ptr size_expr = error_node();
+         expression_node_ptr size_expression_node = error_node();
 
          if (!token_is(token_t::e_lsqrbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR144 - Expected '[' as part of vector size definition",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR157 - Expected '[' as part of vector size definition",
+               exprtk_error_location));
 
             return error_node();
          }
-         else if (0 == (size_expr = parse_expression()))
+         else if (0 == (size_expression_node = parse_expression()))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR145 - Failed to determine size of vector '" + vec_name + "'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR158 - Failed to determine size of vector '" + vec_name + "'",
+               exprtk_error_location));
 
             return error_node();
          }
-         else if (!is_constant_node(size_expr))
+         else if (!is_constant_node(size_expression_node))
          {
-            free_node(node_allocator_,size_expr);
+            const bool is_rebaseble_vector =
+               (size_expression_node->type() == details::expression_node<T>::e_vecsize) &&
+               static_cast<details::vector_size_node<T>*>(size_expression_node)->vec_holder()->rebaseable();
 
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR146 - Expected a literal number as size of vector '" + vec_name + "'",
-                          exprtk_error_location));
+            free_node(node_allocator_, size_expression_node);
+
+            const std::string error_msg = (is_rebaseble_vector) ?
+                                          std::string("Rebasable/Resizable vector cannot be used to define the size of vector") :
+                                          std::string("Expected a constant literal number as size of vector");
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR159 - " + error_msg + " '" + vec_name + "'",
+               exprtk_error_location));
 
             return error_node();
          }
 
-         const T vector_size = size_expr->value();
+         const T vector_size = size_expression_node->value();
 
-         free_node(node_allocator_,size_expr);
+         free_node(node_allocator_, size_expression_node);
 
-         const T max_vector_size = T(2000000000.0);
+         const std::size_t max_vector_size = settings_.max_local_vector_size();
 
          if (
               (vector_size <= T(0)) ||
               std::not_equal_to<T>()
               (T(0),vector_size - details::numeric::trunc(vector_size)) ||
-              (vector_size > max_vector_size)
+              (static_cast<std::size_t>(vector_size) > max_vector_size)
             )
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR147 - Invalid vector size. Must be an integer in the range [0,2e9], size: " +
-                          details::to_str(details::numeric::to_int32(vector_size)),
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR160 - Invalid vector size. Must be an integer in the "
+               "range [0," + details::to_str(static_cast<std::size_t>(max_vector_size)) + "], size: " +
+               details::to_str(details::numeric::to_int32(vector_size)),
+               exprtk_error_location));
 
             return error_node();
          }
 
+         typename symbol_table_t::vector_holder_ptr vec_holder = typename symbol_table_t::vector_holder_ptr(0);
+
+         const std::size_t vec_size = static_cast<std::size_t>(details::numeric::to_int32(vector_size));
+
+         scope_element& se = sem_.get_element(vec_name);
+
+         if (se.name == vec_name)
+         {
+            if (se.active)
+            {
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR161 - Illegal redefinition of local vector: '" + vec_name + "'",
+                  exprtk_error_location));
+
+               return error_node();
+            }
+            else if (
+                      (se.size == vec_size) &&
+                      (scope_element::e_vector == se.type)
+                    )
+            {
+               vec_holder = se.vec_node;
+               se.active  = true;
+               se.depth   = state_.scope_depth;
+               se.ref_count++;
+            }
+         }
+
+         if (0 == vec_holder)
+         {
+            scope_element nse;
+            nse.name      = vec_name;
+            nse.active    = true;
+            nse.ref_count = 1;
+            nse.type      = scope_element::e_vector;
+            nse.depth     = state_.scope_depth;
+            nse.size      = vec_size;
+            nse.data      = new T[vec_size];
+            nse.vec_node  = new typename scope_element::vector_holder_t(reinterpret_cast<T*>(nse.data),nse.size);
+
+            details::set_zero_value(reinterpret_cast<T*>(nse.data),vec_size);
+
+            if (!sem_.add_element(nse))
+            {
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR162 - Failed to add new local vector '" + vec_name + "' to SEM",
+                  exprtk_error_location));
+
+               sem_.free_element(nse);
+
+               return error_node();
+            }
+
+            vec_holder = nse.vec_node;
+
+            exprtk_debug(("parse_define_vector_statement() - INFO - Added new local vector: %s[%d]\n",
+                          nse.name.c_str(),
+                          static_cast<int>(nse.size)));
+         }
+
+         state_.activate_side_effect("parse_define_vector_statement()");
+
+         lodge_symbol(vec_name, e_st_local_vector);
+
          std::vector<expression_node_ptr> vec_initilizer_list;
 
          scoped_vec_delete<expression_node_t> svd((*this),vec_initilizer_list);
 
          bool single_value_initialiser = false;
+         bool range_value_initialiser  = false;
          bool vec_to_vec_initialiser   = false;
          bool null_initialisation      = false;
 
          if (!token_is(token_t::e_rsqrbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR148 - Expected ']' as part of vector size definition",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR163 - Expected ']' as part of vector size definition",
+               exprtk_error_location));
 
             return error_node();
          }
-         else if (!token_is(token_t::e_eof))
+         else if (!token_is(token_t::e_eof, prsrhlpr_t::e_hold))
          {
             if (!token_is(token_t::e_assign))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR149 - Expected ':=' as part of vector definition",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR164 - Expected ':=' as part of vector definition",
+                  exprtk_error_location));
 
                return error_node();
             }
             else if (token_is(token_t::e_lsqrbracket))
             {
-               expression_node_ptr initialiser = parse_expression();
+               expression_node_ptr initialiser_component = parse_expression();
 
-               if (0 == initialiser)
+               if (0 == initialiser_component)
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR150 - Failed to parse single vector initialiser",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR165 - Failed to parse first component of vector initialiser for vector: " + vec_name,
+                     exprtk_error_location));
 
                   return error_node();
                }
 
-               vec_initilizer_list.push_back(initialiser);
+               vec_initilizer_list.push_back(initialiser_component);
+
+               if (token_is(token_t::e_colon))
+               {
+                  initialiser_component = parse_expression();
+
+                  if (0 == initialiser_component)
+                  {
+                     set_error(make_error(
+                        parser_error::e_syntax,
+                        current_token(),
+                        "ERR166 - Failed to parse second component of vector initialiser for vector: " + vec_name,
+                        exprtk_error_location));
+
+                     return error_node();
+                  }
+
+                  vec_initilizer_list.push_back(initialiser_component);
+               }
 
                if (!token_is(token_t::e_rsqrbracket))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR151 - Expected ']' to close single value vector initialiser",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR167 - Expected ']' to close single value vector initialiser",
+                     exprtk_error_location));
 
                   return error_node();
                }
 
-               single_value_initialiser = true;
+               switch (vec_initilizer_list.size())
+               {
+                  case 1 : single_value_initialiser = true; break;
+                  case 2 : range_value_initialiser  = true; break;
+               }
             }
             else if (!token_is(token_t::e_lcrlbracket))
             {
@@ -25259,9 +29339,9 @@ namespace exprtk
                if (token_t::e_symbol == current_token().type)
                {
                   // Is it a locally defined vector?
-                  const scope_element& se = sem_.get_active_element(current_token().value);
+                  const scope_element& lcl_se = sem_.get_active_element(current_token().value);
 
-                  if (scope_element::e_vector == se.type)
+                  if (scope_element::e_vector == lcl_se.type)
                   {
                      if (0 != (initialiser = parse_expression()))
                         vec_initilizer_list.push_back(initialiser);
@@ -25287,11 +29367,11 @@ namespace exprtk
                {
                   if (0 == initialiser)
                   {
-                     set_error(
-                        make_error(parser_error::e_syntax,
-                                   current_token(),
-                                   "ERR152 - Expected '{' as part of vector initialiser list",
-                                   exprtk_error_location));
+                     set_error(make_error(
+                        parser_error::e_syntax,
+                        current_token(),
+                        "ERR168 - Expected '{' as part of vector initialiser list",
+                        exprtk_error_location));
 
                      return error_node();
                   }
@@ -25307,11 +29387,11 @@ namespace exprtk
 
                   if (0 == initialiser)
                   {
-                     set_error(
-                        make_error(parser_error::e_syntax,
-                                   current_token(),
-                                   "ERR153 - Expected '{' as part of vector initialiser list",
-                                   exprtk_error_location));
+                     set_error(make_error(
+                        parser_error::e_syntax,
+                        current_token(),
+                        "ERR169 - Expected '{' as part of vector initialiser list",
+                        exprtk_error_location));
 
                      return error_node();
                   }
@@ -25325,11 +29405,11 @@ namespace exprtk
 
                   if (!token_is(token_t::e_comma) && is_next_close)
                   {
-                     set_error(
-                        make_error(parser_error::e_syntax,
-                                   current_token(),
-                                   "ERR154 - Expected ',' between vector initialisers",
-                                   exprtk_error_location));
+                     set_error(make_error(
+                        parser_error::e_syntax,
+                        current_token(),
+                        "ERR170 - Expected ',' between vector initialisers",
+                        exprtk_error_location));
 
                      return error_node();
                   }
@@ -25345,99 +29425,118 @@ namespace exprtk
                  !token_is(token_t::e_rsqrbracket, prsrhlpr_t::e_hold)
                )
             {
-               if (!token_is(token_t::e_eof))
+               if (!token_is(token_t::e_eof,prsrhlpr_t::e_hold))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR155 - Expected ';' at end of vector definition",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR171 - Expected ';' at end of vector definition",
+                     exprtk_error_location));
 
                   return error_node();
                }
             }
 
-            if (vec_initilizer_list.size() > vector_size)
+            if (
+                 !single_value_initialiser &&
+                 !range_value_initialiser  &&
+                 (T(vec_initilizer_list.size()) > vector_size)
+               )
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR156 - Initialiser list larger than the number of elements in the vector: '" + vec_name + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR172 - Initialiser list larger than the number of elements in the vector: '" + vec_name + "'",
+                  exprtk_error_location));
 
                return error_node();
             }
          }
 
-         typename symbol_table_t::vector_holder_ptr vec_holder = typename symbol_table_t::vector_holder_ptr(0);
-
-         const std::size_t vec_size = static_cast<std::size_t>(details::numeric::to_int32(vector_size));
-
-         scope_element& se = sem_.get_element(vec_name);
+         expression_node_ptr result = error_node();
 
-         if (se.name == vec_name)
+         if (
+              (vec_initilizer_list.size() == 1) &&
+              single_value_initialiser
+            )
          {
-            if (se.active)
+            if (details::is_constant_node(vec_initilizer_list[0]))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR157 - Illegal redefinition of local vector: '" + vec_name + "'",
-                             exprtk_error_location));
-
-               return error_node();
+              // vector_init_zero_value_node   var v[10] := [0]
+               if (T(0) == vec_initilizer_list[0]->value())
+               {
+                  result = node_allocator_
+                              .allocate<details::vector_init_zero_value_node<T> >(
+                                 (*vec_holder)[0],
+                                 vec_size,
+                                 vec_initilizer_list);
+               }
+               else
+               {
+                  // vector_init_single_constvalue_node   var v[10] := [123]
+                  result = node_allocator_
+                              .allocate<details::vector_init_single_constvalue_node<T> >(
+                                 (*vec_holder)[0],
+                                 vec_size,
+                                 vec_initilizer_list);
+               }
             }
-            else if (
-                      (se.size == vec_size) &&
-                      (scope_element::e_vector == se.type)
-                    )
+            else
             {
-               vec_holder = se.vec_node;
-               se.active  = true;
-               se.depth   = state_.scope_depth;
-               se.ref_count++;
+               // vector_init_single_value_node   var v[10] := [123 + (x / y)]
+               result = node_allocator_
+                           .allocate<details::vector_init_single_value_node<T> >(
+                              (*vec_holder)[0],
+                              vec_size,
+                              vec_initilizer_list);
             }
          }
-
-         if (0 == vec_holder)
+         else if (
+                   (vec_initilizer_list.size() == 2) &&
+                   range_value_initialiser
+                 )
          {
-            scope_element nse;
-            nse.name      = vec_name;
-            nse.active    = true;
-            nse.ref_count = 1;
-            nse.type      = scope_element::e_vector;
-            nse.depth     = state_.scope_depth;
-            nse.size      = vec_size;
-            nse.data      = new T[vec_size];
-            nse.vec_node  = new typename scope_element::vector_holder_t(reinterpret_cast<T*>(nse.data),nse.size);
+            bool base_const = details::is_constant_node(vec_initilizer_list[0]);
+            bool inc_const  = details::is_constant_node(vec_initilizer_list[1]);
 
-            if (!sem_.add_element(nse))
+            if (base_const && inc_const)
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR158 - Failed to add new local vector '" + vec_name + "' to SEM",
-                             exprtk_error_location));
-
-               sem_.free_element(nse);
-
-               return error_node();
+               // vector_init_single_value_node   var v[10] := [1 : 3.5]
+               result = node_allocator_
+                           .allocate<details::vector_init_iota_constconst_node<T> >(
+                              (*vec_holder)[0],
+                              vec_size,
+                              vec_initilizer_list);
+            }
+            else if (base_const && !inc_const)
+            {
+               // vector_init_single_value_node   var v[10] := [1 : x + y]
+               result = node_allocator_
+                           .allocate<details::vector_init_iota_constnconst_node<T> >(
+                              (*vec_holder)[0],
+                              vec_size,
+                              vec_initilizer_list);
+            }
+            else if (!base_const && inc_const)
+            {
+               // vector_init_single_value_node   var v[10] := [x + y : 3]
+               result = node_allocator_
+                           .allocate<details::vector_init_iota_nconstconst_node<T> >(
+                              (*vec_holder)[0],
+                              vec_size,
+                              vec_initilizer_list);
+            }
+            else if (!base_const && !inc_const)
+            {
+               // vector_init_single_value_node   var v[10] := [x + y :  z / w]
+               result = node_allocator_
+                           .allocate<details::vector_init_iota_nconstnconst_node<T> >(
+                              (*vec_holder)[0],
+                              vec_size,
+                              vec_initilizer_list);
             }
-
-            vec_holder = nse.vec_node;
-
-            exprtk_debug(("parse_define_vector_statement() - INFO - Added new local vector: %s[%d]\n",
-                          nse.name.c_str(),
-                          static_cast<int>(nse.size)));
          }
-
-         state_.activate_side_effect("parse_define_vector_statement()");
-
-         lodge_symbol(vec_name, e_st_local_vector);
-
-         expression_node_ptr result = error_node();
-
-         if (null_initialisation)
+         else if (null_initialisation)
             result = expression_generator_(T(0.0));
          else if (vec_to_vec_initialiser)
          {
@@ -25449,16 +29548,31 @@ namespace exprtk
                         vec_initilizer_list[0]);
          }
          else
+         {
             result = node_allocator_
-                        .allocate<details::vector_assignment_node<T> >(
+                        .allocate<details::vector_initialisation_node<T> >(
                            (*vec_holder)[0],
                            vec_size,
                            vec_initilizer_list,
                            single_value_initialiser);
+         }
 
-         svd.delete_ptr = (0 == result);
+         svd.delete_ptr = false;
 
-         return result;
+         if (result && result->valid())
+         {
+            return result;
+         }
+
+         details::free_node(node_allocator_, result);
+
+         set_error(make_error(
+            parser_error::e_synthesis,
+            current_token(),
+            "ERR173 - Failed to generate initialisation node for vector: " + vec_name,
+            exprtk_error_location));
+
+         return error_node();
       }
 
       #ifndef exprtk_disable_string_capabilities
@@ -25472,13 +29586,13 @@ namespace exprtk
          {
             if (se.active)
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR159 - Illegal redefinition of local variable: '" + str_name + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR174 - Illegal redefinition of local variable: '" + str_name + "'",
+                  exprtk_error_location));
 
-               free_node(node_allocator_,initialisation_expression);
+               free_node(node_allocator_, initialisation_expression);
 
                return error_node();
             }
@@ -25504,13 +29618,13 @@ namespace exprtk
 
             if (!sem_.add_element(nse))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR160 - Failed to add new local string variable '" + str_name + "' to SEM",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR175 - Failed to add new local string variable '" + str_name + "' to SEM",
+                  exprtk_error_location));
 
-               free_node(node_allocator_,initialisation_expression);
+               free_node(node_allocator_, initialisation_expression);
 
                sem_.free_element(nse);
 
@@ -25519,7 +29633,7 @@ namespace exprtk
 
             str_node = nse.str_node;
 
-            exprtk_debug(("parse_define_string_statement() - INFO - Added new local string variable: %s\n",nse.name.c_str()));
+            exprtk_debug(("parse_define_string_statement() - INFO - Added new local string variable: %s\n", nse.name.c_str()));
          }
 
          lodge_symbol(str_name, e_st_local_string);
@@ -25550,11 +29664,11 @@ namespace exprtk
       {
          if (settings_.vardef_disabled())
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR161 - Illegal variable definition",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR176 - Illegal variable definition",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -25571,41 +29685,41 @@ namespace exprtk
 
          if (!token_is(token_t::e_symbol))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR162 - Expected a symbol for variable definition",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR177 - Expected a symbol for variable definition",
+               exprtk_error_location));
 
             return error_node();
          }
          else if (details::is_reserved_symbol(var_name))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR163 - Illegal redefinition of reserved keyword: '" + var_name + "'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR178 - Illegal redefinition of reserved keyword: '" + var_name + "'",
+               exprtk_error_location));
 
             return error_node();
          }
          else if (symtab_store_.symbol_exists(var_name))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR164 - Illegal redefinition of variable '" + var_name + "'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR179 - Illegal redefinition of variable '" + var_name + "'",
+               exprtk_error_location));
 
             return error_node();
          }
          else if (local_variable_is_shadowed(var_name))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR165 - Illegal redefinition of local variable: '" + var_name + "'",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR180 - Illegal redefinition of local variable: '" + var_name + "'",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -25621,11 +29735,11 @@ namespace exprtk
          {
             if (0 == (initialisation_expression = parse_expression()))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR166 - Failed to parse initialisation expression",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR181 - Failed to parse initialisation expression for variable '" + var_name + "'",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -25639,13 +29753,13 @@ namespace exprtk
          {
             if (!token_is(token_t::e_eof,prsrhlpr_t::e_hold))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR167 - Expected ';' after variable definition",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR182 - Expected ';' after variable '" + var_name + "' definition",
+                  exprtk_error_location));
 
-               free_node(node_allocator_,initialisation_expression);
+               free_node(node_allocator_, initialisation_expression);
 
                return error_node();
             }
@@ -25667,11 +29781,11 @@ namespace exprtk
          {
             if (se.active)
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR168 - Illegal redefinition of local variable: '" + var_name + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR183 - Illegal redefinition of local variable: '" + var_name + "'",
+                  exprtk_error_location));
 
                free_node(node_allocator_, initialisation_expression);
 
@@ -25699,11 +29813,11 @@ namespace exprtk
 
             if (!sem_.add_element(nse))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR169 - Failed to add new local variable '" + var_name + "' to SEM",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR184 - Failed to add new local variable '" + var_name + "' to SEM",
+                  exprtk_error_location));
 
                free_node(node_allocator_, initialisation_expression);
 
@@ -25714,7 +29828,7 @@ namespace exprtk
 
             var_node = nse.var_node;
 
-            exprtk_debug(("parse_define_var_statement() - INFO - Added new local variable: %s\n",nse.name.c_str()));
+            exprtk_debug(("parse_define_var_statement() - INFO - Added new local variable: %s\n", nse.name.c_str()));
          }
 
          state_.activate_side_effect("parse_define_var_statement()");
@@ -25729,6 +29843,174 @@ namespace exprtk
          return expression_generator_(details::e_assign,branch);
       }
 
+      inline expression_node_ptr parse_define_constvar_statement()
+      {
+         if (settings_.vardef_disabled())
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR185 - Illegal const variable definition",
+               exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!token_is("const"))
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR186 - Expected 'const' keyword for const-variable definition",
+               exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!token_is("var"))
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR187 - Expected 'var' keyword for const-variable definition",
+               exprtk_error_location));
+
+            return error_node();
+         }
+
+         const std::string var_name = current_token().value;
+
+         expression_node_ptr initialisation_expression = error_node();
+
+         if (!token_is(token_t::e_symbol))
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR188 - Expected a symbol for const-variable definition",
+               exprtk_error_location));
+
+            return error_node();
+         }
+         else if (details::is_reserved_symbol(var_name))
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR189 - Illegal redefinition of reserved keyword: '" + var_name + "'",
+               exprtk_error_location));
+
+            return error_node();
+         }
+         else if (symtab_store_.symbol_exists(var_name))
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR190 - Illegal redefinition of variable '" + var_name + "'",
+               exprtk_error_location));
+
+            return error_node();
+         }
+         else if (local_variable_is_shadowed(var_name))
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR191 - Illegal redefinition of local variable: '" + var_name + "'",
+               exprtk_error_location));
+
+            return error_node();
+         }
+         else if (token_is(token_t::e_assign))
+         {
+            if (0 == (initialisation_expression = parse_expression()))
+            {
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR192 - Failed to parse initialisation expression for const-variable: '" + var_name + "'",
+                  exprtk_error_location));
+
+               return error_node();
+            }
+            else if (!details::is_literal_node(initialisation_expression))
+            {
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR193 - initialisation expression for const-variable: '" + var_name + "' must be a constant/literal",
+                  exprtk_error_location));
+
+               free_node(node_allocator_, initialisation_expression);
+
+               return error_node();
+            }
+         }
+
+         const T init_value = initialisation_expression->value();
+
+         free_node(node_allocator_, initialisation_expression);
+
+         expression_node_ptr var_node = reinterpret_cast<expression_node_ptr>(0);
+
+         scope_element& se = sem_.get_element(var_name);
+
+         if (se.name == var_name)
+         {
+            if (se.active)
+            {
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR194 - Illegal redefinition of local variable: '" + var_name + "'",
+                  exprtk_error_location));
+
+               return error_node();
+            }
+            else if (scope_element::e_literal == se.type)
+            {
+               var_node  = se.var_node;
+               se.active = true;
+               se.depth  = state_.scope_depth;
+               se.ref_count++;
+            }
+         }
+
+         if (0 == var_node)
+         {
+            scope_element nse;
+            nse.name      = var_name;
+            nse.active    = true;
+            nse.ref_count = 1;
+            nse.type      = scope_element::e_literal;
+            nse.depth     = state_.scope_depth;
+            nse.data      = 0;
+            nse.var_node  = node_allocator_.allocate<literal_node_t>(init_value);
+
+            if (!sem_.add_element(nse))
+            {
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR195 - Failed to add new local const-variable '" + var_name + "' to SEM",
+                  exprtk_error_location));
+
+               sem_.free_element(nse);
+
+               return error_node();
+            }
+
+            var_node = nse.var_node;
+
+            exprtk_debug(("parse_define_constvar_statement() - INFO - Added new local const-variable: %s\n", nse.name.c_str()));
+         }
+
+         state_.activate_side_effect("parse_define_constvar_statement()");
+
+         lodge_symbol(var_name, e_st_local_variable);
+
+         return expression_generator_(var_node->value());
+      }
+
       inline expression_node_ptr parse_uninitialised_var_statement(const std::string& var_name)
       {
          if (
@@ -25736,21 +30018,21 @@ namespace exprtk
               !token_is(token_t::e_rcrlbracket)
             )
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR170 - Expected a '{}' for uninitialised var definition",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR196 - Expected a '{}' for uninitialised var definition",
+               exprtk_error_location));
 
             return error_node();
          }
          else if (!token_is(token_t::e_eof,prsrhlpr_t::e_hold))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR171 - Expected ';' after uninitialised variable definition",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR197 - Expected ';' after uninitialised variable definition",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -25763,11 +30045,11 @@ namespace exprtk
          {
             if (se.active)
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR172 - Illegal redefinition of local variable: '" + var_name + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR198 - Illegal redefinition of local variable: '" + var_name + "'",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -25793,11 +30075,11 @@ namespace exprtk
 
             if (!sem_.add_element(nse))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR173 - Failed to add new local variable '" + var_name + "' to SEM",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR199 - Failed to add new local variable '" + var_name + "' to SEM",
+                  exprtk_error_location));
 
                sem_.free_element(nse);
 
@@ -25826,11 +30108,11 @@ namespace exprtk
 
          if (!token_is(token_t::e_lbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR174 - Expected '(' at start of swap statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR200 - Expected '(' at start of swap statement",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -25845,11 +30127,11 @@ namespace exprtk
 
          if (!token_is(token_t::e_symbol,prsrhlpr_t::e_hold))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR175 - Expected a symbol for variable or vector element definition",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR201 - Expected a symbol for variable or vector element definition",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -25857,11 +30139,11 @@ namespace exprtk
          {
             if (0 == (variable0 = parse_vector()))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR176 - First parameter to swap is an invalid vector element: '" + var0_name + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR202 - First parameter to swap is an invalid vector element: '" + var0_name + "'",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -25890,11 +30172,11 @@ namespace exprtk
 
             if (0 == variable0)
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR177 - First parameter to swap is an invalid variable: '" + var0_name + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR203 - First parameter to swap is an invalid variable: '" + var0_name + "'",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -25904,15 +30186,15 @@ namespace exprtk
 
          if (!token_is(token_t::e_comma))
          {
-            set_error(
-                make_error(parser_error::e_syntax,
-                           current_token(),
-                           "ERR178 - Expected ',' between parameters to swap",
-                           exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR204 - Expected ',' between parameters to swap",
+               exprtk_error_location));
 
             if (variable0_generated)
             {
-               free_node(node_allocator_,variable0);
+               free_node(node_allocator_, variable0);
             }
 
             return error_node();
@@ -25922,15 +30204,15 @@ namespace exprtk
 
          if (!token_is(token_t::e_symbol,prsrhlpr_t::e_hold))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR179 - Expected a symbol for variable or vector element definition",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR205 - Expected a symbol for variable or vector element definition",
+               exprtk_error_location));
 
             if (variable0_generated)
             {
-               free_node(node_allocator_,variable0);
+               free_node(node_allocator_, variable0);
             }
 
             return error_node();
@@ -25939,15 +30221,15 @@ namespace exprtk
          {
             if (0 == (variable1 = parse_vector()))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR180 - Second parameter to swap is an invalid vector element: '" + var1_name + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR206 - Second parameter to swap is an invalid vector element: '" + var1_name + "'",
+                  exprtk_error_location));
 
                if (variable0_generated)
                {
-                  free_node(node_allocator_,variable0);
+                  free_node(node_allocator_, variable0);
                }
 
                return error_node();
@@ -25977,15 +30259,15 @@ namespace exprtk
 
             if (0 == variable1)
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR181 - Second parameter to swap is an invalid variable: '" + var1_name + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR207 - Second parameter to swap is an invalid variable: '" + var1_name + "'",
+                  exprtk_error_location));
 
                if (variable0_generated)
                {
-                  free_node(node_allocator_,variable0);
+                  free_node(node_allocator_, variable0);
                }
 
                return error_node();
@@ -25996,20 +30278,20 @@ namespace exprtk
 
          if (!token_is(token_t::e_rbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR182 - Expected ')' at end of swap statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR208 - Expected ')' at end of swap statement",
+               exprtk_error_location));
 
             if (variable0_generated)
             {
-               free_node(node_allocator_,variable0);
+               free_node(node_allocator_, variable0);
             }
 
             if (variable1_generated)
             {
-               free_node(node_allocator_,variable1);
+               free_node(node_allocator_, variable1);
             }
 
             return error_node();
@@ -26031,12 +30313,12 @@ namespace exprtk
 
             if (variable0_generated)
             {
-               free_node(node_allocator_,variable0);
+               free_node(node_allocator_, variable0);
             }
 
             if (variable1_generated)
             {
-               free_node(node_allocator_,variable1);
+               free_node(node_allocator_, variable1);
             }
          }
          else
@@ -26053,11 +30335,11 @@ namespace exprtk
       {
          if (state_.parsing_return_stmt)
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR183 - Return call within a return call is not allowed",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR209 - Return call within a return call is not allowed",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -26077,11 +30359,11 @@ namespace exprtk
 
          if (!token_is(token_t::e_lsqrbracket))
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR184 - Expected '[' at start of return statement",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR210 - Expected '[' at start of return statement",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -26100,11 +30382,11 @@ namespace exprtk
                   break;
                else if (!token_is(token_t::e_comma))
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR185 - Expected ',' between values during call to return",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR211 - Expected ',' between values during call to return",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -26112,11 +30394,11 @@ namespace exprtk
          }
          else if (settings_.zero_return_disabled())
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR186 - Zero parameter return statement not allowed",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR212 - Zero parameter return statement not allowed",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -26127,11 +30409,11 @@ namespace exprtk
          {
             if (!arg_list.empty())
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             prev_token,
-                             "ERR187 - Invalid ']' found during return call",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  prev_token,
+                  "ERR213 - Invalid ']' found during return call",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -26170,6 +30452,194 @@ namespace exprtk
       }
       #endif
 
+      inline expression_node_ptr parse_assert_statement()
+      {
+         assert(details::imatch(current_token().value, "assert"));
+
+         if (state_.parsing_assert_stmt)
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR214 - Assert statement within an assert statement is not allowed",
+               exprtk_error_location));
+
+            return error_node();
+         }
+
+         scoped_bool_negator sbn(state_.parsing_assert_stmt);
+
+         next_token();
+
+         std::vector<expression_node_ptr> assert_arg_list(3, error_node());
+         scoped_vec_delete<expression_node_t> sdd((*this), assert_arg_list);
+
+         expression_node_ptr& assert_condition = assert_arg_list[0];
+         expression_node_ptr& assert_message   = assert_arg_list[1];
+         expression_node_ptr& assert_id        = assert_arg_list[2];
+
+         if (!token_is(token_t::e_lbracket))
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR215 - Expected '(' at start of assert statement",
+               exprtk_error_location));
+
+            return error_node();
+         }
+
+         const token_t start_token = current_token();
+
+         // Parse the assert condition
+         if (0 == (assert_condition = parse_expression()))
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR216 - Failed to parse condition for assert statement",
+               exprtk_error_location));
+
+            return error_node();
+         }
+
+         const token_t end_token = current_token();
+
+         if (!token_is(token_t::e_rbracket))
+         {
+            if (!token_is(token_t::e_comma))
+            {
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR217 - Expected ',' between condition and message for assert statement",
+                  exprtk_error_location));
+
+               return error_node();
+            }
+            // Parse the assert message
+            else if (
+                      (0 == (assert_message = parse_expression())) ||
+                      !details::is_generally_string_node(assert_message)
+                    )
+            {
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR218 - " +
+                  (assert_message ?
+                  std::string("Expected string for assert message") :
+                  std::string("Failed to parse message for assert statement")),
+                  exprtk_error_location));
+
+               return error_node();
+            }
+            else if (!token_is(token_t::e_rbracket))
+            {
+               if (!token_is(token_t::e_comma))
+               {
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR219 - Expected ',' between message and ID for assert statement",
+                     exprtk_error_location));
+
+                  return error_node();
+               }
+               // Parse assert ID
+               else if (
+                         (0 == (assert_id = parse_expression())) ||
+                         !details::is_const_string_node(assert_id)
+                       )
+               {
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR220 - " +
+                     (assert_id ?
+                     std::string("Expected literal string for assert ID") :
+                     std::string("Failed to parse string for assert ID")),
+                     exprtk_error_location));
+
+                  return error_node();
+               }
+               else if (!token_is(token_t::e_rbracket))
+               {
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR221 - Expected ')' at start of assert statement",
+                     exprtk_error_location));
+
+                  return error_node();
+               }
+            }
+         }
+
+         exprtk::assert_check::assert_context context;
+         context.condition = lexer().substr(start_token.position, end_token.position);
+         context.offet     = start_token.position;
+
+         if (0 == assert_check_)
+         {
+            exprtk_debug(("parse_assert_statement() - assert functionality is disabled. assert condition: %s\n",
+                          context.condition.c_str()));
+
+            return new details::null_node<T>();
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         if (assert_message && details::is_const_string_node(assert_message))
+         {
+            context.message = dynamic_cast<details::string_base_node<T>*>(assert_message)->str();
+         }
+
+         if (assert_id && details::is_const_string_node(assert_id))
+         {
+            context.id = dynamic_cast<details::string_base_node<T>*>(assert_id)->str();
+
+            if (assert_ids_.end() != assert_ids_.find(context.id))
+            {
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR222 - Duplicate assert ID: " + context.id,
+                  exprtk_error_location));
+
+               return error_node();
+            }
+
+            assert_ids_.insert(context.id);
+            free_node(node_allocator_, assert_id);
+         }
+         #endif
+
+         expression_node_ptr result_node =
+            expression_generator_.assert_call(
+               assert_condition,
+               assert_message,
+               context);
+
+         exprtk_debug(("parse_assert_statement() - assert condition: [%s]\n", context.condition.c_str()      ));
+         exprtk_debug(("parse_assert_statement() - assert message:   [%s]\n", context.message  .c_str()      ));
+         exprtk_debug(("parse_assert_statement() - assert id:        [%s]\n", context.id       .c_str()      ));
+         exprtk_debug(("parse_assert_statement() - assert offset:    [%d]\n", static_cast<int>(context.offet)));
+
+         if (0 == result_node)
+         {
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR223 - Failed to synthesize assert",
+               exprtk_error_location));
+
+            return error_node();
+         }
+
+         sdd.delete_ptr = false;
+         return result_node;
+      }
+
       inline bool post_variable_process(const std::string& symbol)
       {
          if (
@@ -26180,11 +30650,11 @@ namespace exprtk
          {
             if (!settings_.commutative_check_enabled())
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR188 - Invalid sequence of variable '"+ symbol + "' and bracket",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR224 - Invalid sequence of variable '" + symbol + "' and bracket",
+                  exprtk_error_location));
 
                return false;
             }
@@ -26199,26 +30669,29 @@ namespace exprtk
       {
          bool implied_mul = false;
 
-         if (is_generally_string_node(branch))
+         if (details::is_generally_string_node(branch))
+            return true;
+
+         if (details::is_ivector_node(branch))
             return true;
 
          const lexer::parser_helper::token_advance_mode hold = prsrhlpr_t::e_hold;
 
          switch (token)
          {
-            case token_t::e_lcrlbracket : implied_mul = token_is(token_t::e_lbracket   ,hold) ||
-                                                        token_is(token_t::e_lcrlbracket,hold) ||
-                                                        token_is(token_t::e_lsqrbracket,hold) ;
+            case token_t::e_lcrlbracket : implied_mul = token_is(token_t::e_lbracket   , hold) ||
+                                                        token_is(token_t::e_lcrlbracket, hold) ||
+                                                        token_is(token_t::e_lsqrbracket, hold) ;
                                           break;
 
-            case token_t::e_lbracket    : implied_mul = token_is(token_t::e_lbracket   ,hold) ||
-                                                        token_is(token_t::e_lcrlbracket,hold) ||
-                                                        token_is(token_t::e_lsqrbracket,hold) ;
+            case token_t::e_lbracket    : implied_mul = token_is(token_t::e_lbracket   , hold) ||
+                                                        token_is(token_t::e_lcrlbracket, hold) ||
+                                                        token_is(token_t::e_lsqrbracket, hold) ;
                                           break;
 
-            case token_t::e_lsqrbracket : implied_mul = token_is(token_t::e_lbracket   ,hold) ||
-                                                        token_is(token_t::e_lcrlbracket,hold) ||
-                                                        token_is(token_t::e_lsqrbracket,hold) ;
+            case token_t::e_lsqrbracket : implied_mul = token_is(token_t::e_lbracket   , hold) ||
+                                                        token_is(token_t::e_lcrlbracket, hold) ||
+                                                        token_is(token_t::e_lsqrbracket, hold) ;
                                           break;
 
             default                     : return true;
@@ -26228,11 +30701,11 @@ namespace exprtk
          {
             if (!settings_.commutative_check_enabled())
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR189 - Invalid sequence of brackets",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR225 - Invalid sequence of brackets",
+                  exprtk_error_location));
 
                return false;
             }
@@ -26247,27 +30720,65 @@ namespace exprtk
          return true;
       }
 
+      typedef typename interval_container_t<const void*>::interval_t interval_t;
+      typedef interval_container_t<const void*> immutable_memory_map_t;
+      typedef std::map<interval_t,token_t> immutable_symtok_map_t;
+
+      inline interval_t make_memory_range(const T& t)
+      {
+         const T* begin = reinterpret_cast<const T*>(&t);
+         const T* end   = begin + 1;
+         return interval_t(begin, end);
+      }
+
+      inline interval_t make_memory_range(const T* begin, const std::size_t size)
+      {
+         return interval_t(begin, begin + size);
+      }
+
+      inline interval_t make_memory_range(details::char_cptr begin, const std::size_t size)
+      {
+         return interval_t(begin, begin + size);
+      }
+
+      void lodge_immutable_symbol(const lexer::token& token, const interval_t interval)
+      {
+         immutable_memory_map_.add_interval(interval);
+         immutable_symtok_map_[interval] = token;
+      }
+
       inline expression_node_ptr parse_symtab_symbol()
       {
          const std::string symbol = current_token().value;
 
          // Are we dealing with a variable or a special constant?
-         expression_node_ptr variable = symtab_store_.get_variable(symbol);
+         typedef typename symtab_store::variable_context var_ctxt_t;
+         var_ctxt_t var_ctx = symtab_store_.get_variable_context(symbol);
 
-         if (variable)
+         if (var_ctx.variable)
          {
+            assert(var_ctx.symbol_table);
+
+            expression_node_ptr result_variable = var_ctx.variable;
+
             if (symtab_store_.is_constant_node(symbol))
             {
-               variable = expression_generator_(variable->value());
+               result_variable = expression_generator_(var_ctx.variable->value());
+            }
+            else if (symbol_table_t::e_immutable == var_ctx.symbol_table->mutability())
+            {
+               lodge_immutable_symbol(current_token(), make_memory_range(var_ctx.variable->ref()));
+               result_variable = var_ctx.variable;
             }
 
             if (!post_variable_process(symbol))
                return error_node();
 
             lodge_symbol(symbol, e_st_variable);
+
             next_token();
 
-            return variable;
+            return result_variable;
          }
 
          // Are we dealing with a locally defined variable, vector or string?
@@ -26277,7 +30788,10 @@ namespace exprtk
 
             if (se.active && details::imatch(se.name, symbol))
             {
-               if (scope_element::e_variable == se.type)
+               if (
+                    (scope_element::e_variable == se.type) ||
+                    (scope_element::e_literal  == se.type)
+                  )
                {
                   se.active = true;
                   lodge_symbol(symbol, e_st_local_variable);
@@ -26287,7 +30801,9 @@ namespace exprtk
 
                   next_token();
 
-                  return se.var_node;
+                  return (scope_element::e_variable == se.type) ?
+                           se.var_node :
+                           expression_generator_(se.var_node->value());
                }
                else if (scope_element::e_vector == se.type)
                {
@@ -26325,11 +30841,11 @@ namespace exprtk
                   return func_node;
                else
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR190 - Failed to generate node for function: '" + symbol + "'",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR226 - Failed to generate node for function: '" + symbol + "'",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -26351,11 +30867,11 @@ namespace exprtk
                   return vararg_func_node;
                else
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR191 - Failed to generate node for vararg function: '" + symbol + "'",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR227 - Failed to generate node for vararg function: '" + symbol + "'",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -26377,11 +30893,11 @@ namespace exprtk
                   return genericfunc_node;
                else
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR192 - Failed to generate node for generic function: '" + symbol + "'",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR228 - Failed to generate node for generic function: '" + symbol + "'",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -26404,11 +30920,11 @@ namespace exprtk
                   return stringfunc_node;
                else
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR193 - Failed to generate node for string function: '" + symbol + "'",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR229 - Failed to generate node for string function: '" + symbol + "'",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -26430,11 +30946,11 @@ namespace exprtk
                   return overloadfunc_node;
                else
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR194 - Failed to generate node for overload function: '" + symbol + "'",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR230 - Failed to generate node for overload function: '" + symbol + "'",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -26456,11 +30972,11 @@ namespace exprtk
                     !details::is_base_function(symbol)
                   )
                {
-                  set_error(
-                     make_error(parser_error::e_syntax,
-                                current_token(),
-                                "ERR195 - Invalid use of reserved symbol '" + symbol + "'",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_syntax,
+                     current_token(),
+                     "ERR231 - Invalid use of reserved symbol '" + symbol + "'",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -26487,11 +31003,13 @@ namespace exprtk
 
                      switch (usr_symbol_type)
                      {
-                        case unknown_symbol_resolver::e_usr_variable_type : create_result = symtab.create_variable(symbol, default_value);
-                                                                            break;
+                        case unknown_symbol_resolver::e_usr_variable_type :
+                           create_result = symtab.create_variable(symbol, default_value);
+                           break;
 
-                        case unknown_symbol_resolver::e_usr_constant_type : create_result = symtab.add_constant(symbol, default_value);
-                                                                            break;
+                        case unknown_symbol_resolver::e_usr_constant_type :
+                           create_result = symtab.add_constant(symbol, default_value);
+                           break;
 
                         default                                           : create_result = false;
                      }
@@ -26519,12 +31037,12 @@ namespace exprtk
                      }
                   }
 
-                  set_error(
-                     make_error(parser_error::e_symtab,
-                                current_token(),
-                                "ERR196 - Failed to create variable: '" + symbol + "'" +
-                                (error_message.empty() ? "" : " - " + error_message),
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_symtab,
+                     current_token(),
+                     "ERR232 - Failed to create variable: '" + symbol + "'" +
+                     (error_message.empty() ? "" : " - " + error_message),
+                     exprtk_error_location));
 
                }
                else if (unknown_symbol_resolver::e_usrmode_extended == unknown_symbol_resolver_->mode)
@@ -26539,27 +31057,51 @@ namespace exprtk
                      }
                   }
 
-                  set_error(
-                     make_error(parser_error::e_symtab,
-                                current_token(),
-                                "ERR197 - Failed to resolve symbol: '" + symbol + "'" +
-                                (error_message.empty() ? "" : " - " + error_message),
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_symtab,
+                     current_token(),
+                     "ERR233 - Failed to resolve symbol: '" + symbol + "'" +
+                     (error_message.empty() ? "" : " - " + error_message),
+                     exprtk_error_location));
                }
 
                return error_node();
             }
          }
 
-         set_error(
-            make_error(parser_error::e_syntax,
-                       current_token(),
-                       "ERR198 - Undefined symbol: '" + symbol + "'",
-                       exprtk_error_location));
+         set_error(make_error(
+            parser_error::e_syntax,
+            current_token(),
+            "ERR234 - Undefined symbol: '" + symbol + "'",
+            exprtk_error_location));
 
          return error_node();
       }
 
+      inline expression_node_ptr check_block_statement_closure(expression_node_ptr expression)
+      {
+         if (
+              expression &&
+              (
+                (current_token().type == token_t::e_symbol) ||
+                (current_token().type == token_t::e_number)
+              )
+            )
+         {
+            free_node(node_allocator_, expression);
+
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR235 - Invalid syntax '" + current_token().value  + "' possible missing operator or context",
+               exprtk_error_location));
+
+               return error_node();
+         }
+
+         return expression;
+      }
+
       inline expression_node_ptr parse_symbol()
       {
          static const std::string symbol_if       = "if"      ;
@@ -26571,103 +31113,116 @@ namespace exprtk
          static const std::string symbol_break    = "break"   ;
          static const std::string symbol_continue = "continue";
          static const std::string symbol_var      = "var"     ;
+         static const std::string symbol_const    = "const"   ;
          static const std::string symbol_swap     = "swap"    ;
          static const std::string symbol_return   = "return"  ;
          static const std::string symbol_not      = "not"     ;
+         static const std::string symbol_assert   = "assert"  ;
 
-         if (valid_vararg_operation(current_token().value))
+         const std::string symbol = current_token().value;
+
+         if (valid_vararg_operation(symbol))
          {
             return parse_vararg_function();
          }
-         else if (details::imatch(current_token().value, symbol_not))
+         else if (details::imatch(symbol, symbol_not))
          {
             return parse_not_statement();
          }
-         else if (valid_base_operation(current_token().value))
+         else if (valid_base_operation(symbol))
          {
             return parse_base_operation();
          }
          else if (
-                   details::imatch(current_token().value, symbol_if) &&
-                   settings_.control_struct_enabled(current_token().value)
+                   details::imatch(symbol, symbol_if) &&
+                   settings_.control_struct_enabled(symbol)
                  )
          {
             return parse_conditional_statement();
          }
          else if (
-                   details::imatch(current_token().value, symbol_while) &&
-                   settings_.control_struct_enabled(current_token().value)
+                   details::imatch(symbol, symbol_while) &&
+                   settings_.control_struct_enabled(symbol)
                  )
          {
-            return parse_while_loop();
+            return check_block_statement_closure(parse_while_loop());
          }
          else if (
-                   details::imatch(current_token().value, symbol_repeat) &&
-                   settings_.control_struct_enabled(current_token().value)
+                   details::imatch(symbol, symbol_repeat) &&
+                   settings_.control_struct_enabled(symbol)
                  )
          {
-            return parse_repeat_until_loop();
+            return check_block_statement_closure(parse_repeat_until_loop());
          }
          else if (
-                   details::imatch(current_token().value, symbol_for) &&
-                   settings_.control_struct_enabled(current_token().value)
+                   details::imatch(symbol, symbol_for) &&
+                   settings_.control_struct_enabled(symbol)
                  )
          {
-            return parse_for_loop();
+            return check_block_statement_closure(parse_for_loop());
          }
          else if (
-                   details::imatch(current_token().value, symbol_switch) &&
-                   settings_.control_struct_enabled(current_token().value)
+                   details::imatch(symbol, symbol_switch) &&
+                   settings_.control_struct_enabled(symbol)
                  )
          {
-            return parse_switch_statement();
+            return check_block_statement_closure(parse_switch_statement());
          }
-         else if (details::is_valid_sf_symbol(current_token().value))
+         else if (details::is_valid_sf_symbol(symbol))
          {
             return parse_special_function();
          }
-         else if (details::imatch(current_token().value, symbol_null))
+         else if (details::imatch(symbol, symbol_null))
          {
             return parse_null_statement();
          }
          #ifndef exprtk_disable_break_continue
-         else if (details::imatch(current_token().value, symbol_break))
+         else if (details::imatch(symbol, symbol_break))
          {
             return parse_break_statement();
          }
-         else if (details::imatch(current_token().value, symbol_continue))
+         else if (details::imatch(symbol, symbol_continue))
          {
             return parse_continue_statement();
          }
          #endif
-         else if (details::imatch(current_token().value, symbol_var))
+         else if (details::imatch(symbol, symbol_var))
          {
             return parse_define_var_statement();
          }
-         else if (details::imatch(current_token().value, symbol_swap))
+         else if (details::imatch(symbol, symbol_const))
+         {
+            return parse_define_constvar_statement();
+         }
+         else if (details::imatch(symbol, symbol_swap))
          {
             return parse_swap_statement();
          }
          #ifndef exprtk_disable_return_statement
          else if (
-                   details::imatch(current_token().value, symbol_return) &&
-                   settings_.control_struct_enabled(current_token().value)
+                   details::imatch(symbol, symbol_return) &&
+                   settings_.control_struct_enabled(symbol)
                  )
          {
-            return parse_return_statement();
+            return check_block_statement_closure(parse_return_statement());
          }
          #endif
+         else if (details::imatch(symbol, symbol_assert))
+         {
+            return parse_assert_statement();
+         }
          else if (symtab_store_.valid() || !sem_.empty())
          {
             return parse_symtab_symbol();
          }
          else
          {
-            set_error(
-               make_error(parser_error::e_symtab,
-                          current_token(),
-                          "ERR199 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token().value,
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_symtab,
+               current_token(),
+               "ERR236 - Unknown variable or function encountered. Symbol table(s) "
+               "is either invalid or does not contain symbol: '" + symbol + "'",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -26694,11 +31249,11 @@ namespace exprtk
 
                if (0 == literal_exp)
                {
-                  set_error(
-                     make_error(parser_error::e_numeric,
-                                current_token(),
-                                "ERR200 - Failed generate node for scalar: '" + current_token().value + "'",
-                                exprtk_error_location));
+                  set_error(make_error(
+                     parser_error::e_numeric,
+                     current_token(),
+                     "ERR237 - Failed generate node for scalar: '" + current_token().value + "'",
+                     exprtk_error_location));
 
                   return error_node();
                }
@@ -26708,11 +31263,11 @@ namespace exprtk
             }
             else
             {
-               set_error(
-                  make_error(parser_error::e_numeric,
-                             current_token(),
-                             "ERR201 - Failed to convert '" + current_token().value + "' to a number",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_numeric,
+                  current_token(),
+                  "ERR238 - Failed to convert '" + current_token().value + "' to a number",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -26732,25 +31287,32 @@ namespace exprtk
             next_token();
 
             if (0 == (branch = parse_expression()))
+            {
                return error_node();
-            else if (!token_is(token_t::e_rbracket))
+            }
+
+            token_is(token_t::e_eof);
+
+            if (!token_is(token_t::e_rbracket))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR202 - Expected ')' instead of: '" + current_token().value + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR239 - Expected ')' instead of: '" + current_token().value + "'",
+                  exprtk_error_location));
 
-               free_node(node_allocator_,branch);
+               details::free_node(node_allocator_, branch);
 
                return error_node();
             }
             else if (!post_bracket_process(token_t::e_lbracket,branch))
             {
-               free_node(node_allocator_,branch);
+               details::free_node(node_allocator_, branch);
 
                return error_node();
             }
+
+            parse_pending_vector_index_operator(branch);
          }
          else if (token_t::e_lsqrbracket == current_token().type)
          {
@@ -26760,19 +31322,19 @@ namespace exprtk
                return error_node();
             else if (!token_is(token_t::e_rsqrbracket))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR203 - Expected ']' instead of: '" + current_token().value + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR240 - Expected ']' instead of: '" + current_token().value + "'",
+                  exprtk_error_location));
 
-               free_node(node_allocator_,branch);
+               details::free_node(node_allocator_, branch);
 
                return error_node();
             }
             else if (!post_bracket_process(token_t::e_lsqrbracket,branch))
             {
-               free_node(node_allocator_,branch);
+               details::free_node(node_allocator_, branch);
 
                return error_node();
             }
@@ -26785,19 +31347,19 @@ namespace exprtk
                return error_node();
             else if (!token_is(token_t::e_rcrlbracket))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR204 - Expected '}' instead of: '" + current_token().value + "'",
-                             exprtk_error_location));
+               set_error(make_error(
+                  parser_error::e_syntax,
+                  current_token(),
+                  "ERR241 - Expected '}' instead of: '" + current_token().value + "'",
+                  exprtk_error_location));
 
-               free_node(node_allocator_,branch);
+               details::free_node(node_allocator_, branch);
 
                return error_node();
             }
             else if (!post_bracket_process(token_t::e_lcrlbracket,branch))
             {
-               free_node(node_allocator_,branch);
+               details::free_node(node_allocator_, branch);
 
                return error_node();
             }
@@ -26819,7 +31381,7 @@ namespace exprtk
 
                if (0 == result)
                {
-                  free_node(node_allocator_,branch);
+                  details::free_node(node_allocator_, branch);
 
                   return error_node();
                }
@@ -26834,21 +31396,21 @@ namespace exprtk
          }
          else if (token_t::e_eof == current_token().type)
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR205 - Premature end of expression[1]",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR242 - Premature end of expression[1]",
+               exprtk_error_location));
 
             return error_node();
          }
          else
          {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR206 - Premature end of expression[2]",
-                          exprtk_error_location));
+            set_error(make_error(
+               parser_error::e_syntax,
+               current_token(),
+               "ERR243 - Premature end of expression[2]",
+               exprtk_error_location));
 
             return error_node();
          }
@@ -26949,6 +31511,8 @@ namespace exprtk
             register_synthezier(synthesize_covocov_expression4)
             register_synthezier(synthesize_vocovoc_expression4)
             register_synthezier(synthesize_covovoc_expression4)
+
+            #undef register_synthezier
             #endif
          }
 
@@ -27001,7 +31565,7 @@ namespace exprtk
          {
             typename binary_op_map_t::iterator bop_itr = binary_op_map_->find(operation);
 
-            if ((*binary_op_map_).end() == bop_itr)
+            if (binary_op_map_->end() == bop_itr)
                return false;
 
             bop = bop_itr->second;
@@ -27413,13 +31977,20 @@ namespace exprtk
 
                if (details::is_string_node(branch[0]))
                   return !b1_is_genstring;
+               else if (details::is_literal_node(branch[0]))
+                  return true;
                else
                   return (
-                           !details::is_variable_node          (branch[0]) &&
-                           !details::is_vector_elem_node       (branch[0]) &&
-                           !details::is_rebasevector_elem_node (branch[0]) &&
-                           !details::is_rebasevector_celem_node(branch[0]) &&
-                           !details::is_vector_node            (branch[0])
+                           !details::is_variable_node              (branch[0]) &&
+                           !details::is_vector_elem_node           (branch[0]) &&
+                           !details::is_vector_celem_node          (branch[0]) &&
+                           !details::is_vector_elem_rtc_node       (branch[0]) &&
+                           !details::is_vector_celem_rtc_node      (branch[0]) &&
+                           !details::is_rebasevector_elem_node     (branch[0]) &&
+                           !details::is_rebasevector_celem_node    (branch[0]) &&
+                           !details::is_rebasevector_elem_rtc_node (branch[0]) &&
+                           !details::is_rebasevector_celem_rtc_node(branch[0]) &&
+                           !details::is_vector_node                (branch[0])
                          )
                          || b1_is_genstring;
             }
@@ -27549,9 +32120,9 @@ namespace exprtk
                         (details::e_equal == operation) ||
                         (details::e_and   == operation) ||
                         (details::e_nand  == operation) ||
-                        (details::  e_or  == operation) ||
-                        (details:: e_nor  == operation) ||
-                        (details:: e_xor  == operation) ||
+                        (details::e_or    == operation) ||
+                        (details::e_nor   == operation) ||
+                        (details::e_xor   == operation) ||
                         (details::e_xnor  == operation)
                       );
          }
@@ -27574,18 +32145,42 @@ namespace exprtk
          {
             if ((0 == branch[0]) || (0 == branch[1]))
             {
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_syntax,
+                  parser_->current_state().token,
+                  "ERR244 - Invalid branches received for operator '" + details::to_str(operation) + "'",
+                  exprtk_error_location));
+
                return error_node();
             }
             else if (is_invalid_string_op(operation,branch))
             {
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_syntax,
+                  parser_->current_state().token,
+                  "ERR245 - Invalid branch pair for string operator '" + details::to_str(operation) + "'",
+                  exprtk_error_location));
+
                return error_node();
             }
             else if (is_invalid_assignment_op(operation,branch))
             {
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_syntax,
+                  parser_->current_state().token,
+                  "ERR246 - Invalid branch pair for assignment operator '" + details::to_str(operation) + "'",
+                  exprtk_error_location));
+
                return error_node();
             }
             else if (is_invalid_break_continue_op(branch))
             {
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_syntax,
+                  parser_->current_state().token,
+                  "ERR247 - Invalid branch pair for break/continue operator '" + details::to_str(operation) + "'",
+                  exprtk_error_location));
+
                return error_node();
             }
             else if (details::e_assign == operation)
@@ -27704,10 +32299,22 @@ namespace exprtk
             {
                details::free_all_nodes(*node_allocator_,branch);
 
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_syntax,
+                  parser_->current_state().token,
+                  "ERR248 - Invalid branches operator '" + details::to_str(operation) + "'",
+                  exprtk_error_location));
+
                return error_node();
             }
             else if (is_invalid_string_op(operation, branch))
             {
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_syntax,
+                  parser_->current_state().token,
+                  "ERR249 - Invalid branches for string operator '" + details::to_str(operation) + "'",
+                  exprtk_error_location));
+
                return error_node();
             }
             else if (is_string_operation(operation, branch))
@@ -27750,9 +32357,19 @@ namespace exprtk
          {
             if ((0 == condition) || (0 == consequent))
             {
-               free_node(*node_allocator_,   condition);
-               free_node(*node_allocator_,  consequent);
-               free_node(*node_allocator_, alternative);
+               details::free_node(*node_allocator_, condition  );
+               details::free_node(*node_allocator_, consequent );
+               details::free_node(*node_allocator_, alternative);
+
+               const std::string invalid_branches =
+                                 ((0 == condition ) ? std::string("condition ") : "") +
+                                 ((0 == consequent) ? std::string("consequent") : "") ;
+
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_parser,
+                  parser_->current_state().token,
+                  "ERR250 - Invalid " + invalid_branches + " for conditional statement",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -27762,16 +32379,16 @@ namespace exprtk
                // True branch
                if (details::is_true(condition))
                {
-                  free_node(*node_allocator_,   condition);
-                  free_node(*node_allocator_, alternative);
+                  details::free_node(*node_allocator_, condition  );
+                  details::free_node(*node_allocator_, alternative);
 
                   return consequent;
                }
                // False branch
                else
                {
-                  free_node(*node_allocator_,  condition);
-                  free_node(*node_allocator_, consequent);
+                  details::free_node(*node_allocator_, condition );
+                  details::free_node(*node_allocator_, consequent);
 
                   if (alternative)
                      return alternative;
@@ -27779,14 +32396,34 @@ namespace exprtk
                      return node_allocator_->allocate<details::null_node<T> >();
                }
             }
-            else if ((0 != consequent) && (0 != alternative))
+
+            expression_node_ptr result = error_node();
+            std::string node_name      = "Unknown!";
+
+            if ((0 != consequent) && (0 != alternative))
             {
-               return node_allocator_->
-                        allocate<conditional_node_t>(condition, consequent, alternative);
+               result = node_allocator_->allocate<conditional_node_t>(condition, consequent, alternative);
+               node_name = "conditional_node_t";
             }
             else
-               return node_allocator_->
-                        allocate<cons_conditional_node_t>(condition, consequent);
+            {
+               result = node_allocator_->allocate<cons_conditional_node_t>(condition, consequent);
+               node_name = "cons_conditional_node_t";
+            }
+
+            if (result && result->valid())
+            {
+               return result;
+            }
+
+            parser_->set_error(parser_error::make_error(
+               parser_error::e_parser,
+               token_t(),
+               "ERR251 - Failed to synthesize node: " + node_name,
+               exprtk_error_location));
+
+            details::free_node(*node_allocator_, result);
+            return error_node();
          }
 
          #ifndef exprtk_disable_string_capabilities
@@ -27796,9 +32433,19 @@ namespace exprtk
          {
             if ((0 == condition) || (0 == consequent))
             {
-               free_node(*node_allocator_,   condition);
-               free_node(*node_allocator_,  consequent);
-               free_node(*node_allocator_, alternative);
+               details::free_node(*node_allocator_, condition  );
+               details::free_node(*node_allocator_, consequent );
+               details::free_node(*node_allocator_, alternative);
+
+               const std::string invalid_branches =
+                                 ((0 == condition ) ? std::string("condition ") : "") +
+                                 ((0 == consequent) ? std::string("consequent") : "") ;
+
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_parser,
+                  parser_->current_state().token,
+                  "ERR252 - Invalid " + invalid_branches + " for string conditional statement",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -27808,16 +32455,16 @@ namespace exprtk
                // True branch
                if (details::is_true(condition))
                {
-                  free_node(*node_allocator_,   condition);
-                  free_node(*node_allocator_, alternative);
+                  details::free_node(*node_allocator_, condition  );
+                  details::free_node(*node_allocator_, alternative);
 
                   return consequent;
                }
                // False branch
                else
                {
-                  free_node(*node_allocator_,  condition);
-                  free_node(*node_allocator_, consequent);
+                  details::free_node(*node_allocator_, condition );
+                  details::free_node(*node_allocator_, consequent);
 
                   if (alternative)
                      return alternative;
@@ -27827,10 +32474,25 @@ namespace exprtk
                }
             }
             else if ((0 != consequent) && (0 != alternative))
-               return node_allocator_->
-                        allocate<conditional_string_node_t>(condition, consequent, alternative);
-            else
-               return error_node();
+            {
+               expression_node_ptr result =
+                  node_allocator_->allocate<conditional_string_node_t>(condition, consequent, alternative);
+
+               if (result && result->valid())
+               {
+                  return result;
+               }
+
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_parser,
+                  token_t(),
+                  "ERR253 - Failed to synthesize node: conditional_string_node_t",
+                  exprtk_error_location));
+
+               details::free_node(*node_allocator_, result);
+            }
+
+            return error_node();
          }
          #else
          inline expression_node_ptr conditional_string(expression_node_ptr,
@@ -27841,6 +32503,61 @@ namespace exprtk
          }
          #endif
 
+         inline expression_node_ptr conditional_vector(expression_node_ptr condition,
+                                                       expression_node_ptr consequent,
+                                                       expression_node_ptr alternative) const
+         {
+            if ((0 == condition) || (0 == consequent))
+            {
+               details::free_node(*node_allocator_, condition  );
+               details::free_node(*node_allocator_, consequent );
+               details::free_node(*node_allocator_, alternative);
+
+               const std::string invalid_branches =
+                                 ((0 == condition ) ? std::string("condition ") : "") +
+                                 ((0 == consequent) ? std::string("consequent") : "") ;
+
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_parser,
+                  parser_->current_state().token,
+                  "ERR254 - Invalid " + invalid_branches + " for vector conditional statement",
+                  exprtk_error_location));
+
+               return error_node();
+            }
+            // Can the condition be immediately evaluated? if so optimise.
+            else if (details::is_constant_node(condition))
+            {
+               // True branch
+               if (details::is_true(condition))
+               {
+                  details::free_node(*node_allocator_, condition  );
+                  details::free_node(*node_allocator_, alternative);
+
+                  return consequent;
+               }
+               // False branch
+               else
+               {
+                  details::free_node(*node_allocator_, condition );
+                  details::free_node(*node_allocator_, consequent);
+
+                  if (alternative)
+                     return alternative;
+                  else
+                     return node_allocator_->allocate<details::null_node<T> >();
+
+               }
+            }
+            else if ((0 != consequent) && (0 != alternative))
+            {
+               return node_allocator_->
+                        allocate<conditional_vector_node_t>(condition, consequent, alternative);
+            }
+            else
+               return error_node();
+         }
+
          inline loop_runtime_check_ptr get_loop_runtime_check(const loop_runtime_check::loop_types loop_type) const
          {
             if (
@@ -27854,45 +32571,70 @@ namespace exprtk
             return loop_runtime_check_ptr(0);
          }
 
+         inline vector_access_runtime_check_ptr get_vector_access_runtime_check() const
+         {
+            return parser_->vector_access_runtime_check_;
+         }
+
          inline expression_node_ptr while_loop(expression_node_ptr& condition,
                                                expression_node_ptr& branch,
-                                               const bool brkcont = false) const
+                                               const bool break_continue_present = false) const
          {
-            if (!brkcont && details::is_constant_node(condition))
+            if (
+                 !break_continue_present              &&
+                 !parser_->state_.return_stmt_present &&
+                 details::is_constant_node(condition)
+               )
             {
                expression_node_ptr result = error_node();
                if (details::is_true(condition))
+               {
                   // Infinite loops are not allowed.
+
+                  parser_->set_error(parser_error::make_error(
+                     parser_error::e_parser,
+                     parser_->current_state().token,
+                     "ERR255 - Infinite loop condition without 'break' or 'return' not allowed in while-loops",
+                     exprtk_error_location));
+
                   result = error_node();
+               }
                else
                   result = node_allocator_->allocate<details::null_node<Type> >();
 
-               free_node(*node_allocator_, condition);
-               free_node(*node_allocator_,    branch);
+               details::free_node(*node_allocator_, condition);
+               details::free_node(*node_allocator_, branch   );
 
                return result;
             }
             else if (details::is_null_node(condition))
             {
-               free_node(*node_allocator_,condition);
+               details::free_node(*node_allocator_,condition);
 
                return branch;
             }
-            else if (!brkcont)
-               return node_allocator_->allocate<while_loop_node_t>
-                        (
-                          condition,
-                          branch,
-                          get_loop_runtime_check(loop_runtime_check::e_while_loop)
-                        );
+
+            loop_runtime_check_ptr rtc = get_loop_runtime_check(loop_runtime_check::e_while_loop);
+
+            if (!break_continue_present)
+            {
+               if (rtc)
+                  return node_allocator_->allocate<while_loop_rtc_node_t>
+                           (condition, branch,  rtc);
+               else
+                  return node_allocator_->allocate<while_loop_node_t>
+                           (condition, branch);
+            }
             #ifndef exprtk_disable_break_continue
             else
-               return node_allocator_->allocate<while_loop_bc_node_t>
-                        (
-                          condition,
-                          branch,
-                          get_loop_runtime_check(loop_runtime_check::e_while_loop)
-                        );
+            {
+               if (rtc)
+                  return node_allocator_->allocate<while_loop_bc_rtc_node_t>
+                           (condition, branch, rtc);
+               else
+                  return node_allocator_->allocate<while_loop_bc_node_t>
+                           (condition, branch);
+            }
             #else
                return error_node();
             #endif
@@ -27900,9 +32642,9 @@ namespace exprtk
 
          inline expression_node_ptr repeat_until_loop(expression_node_ptr& condition,
                                                       expression_node_ptr& branch,
-                                                      const bool brkcont = false) const
+                                                      const bool break_continue_present = false) const
          {
-            if (!brkcont && details::is_constant_node(condition))
+            if (!break_continue_present && details::is_constant_node(condition))
             {
                if (
                     details::is_true(condition) &&
@@ -27914,32 +32656,39 @@ namespace exprtk
                   return branch;
                }
 
-               free_node(*node_allocator_, condition);
-               free_node(*node_allocator_,    branch);
+               details::free_node(*node_allocator_, condition);
+               details::free_node(*node_allocator_, branch   );
 
                return error_node();
             }
             else if (details::is_null_node(condition))
             {
-               free_node(*node_allocator_,condition);
+               details::free_node(*node_allocator_,condition);
 
                return branch;
             }
-            else if (!brkcont)
-               return node_allocator_->allocate<repeat_until_loop_node_t>
-                        (
-                          condition,
-                          branch,
-                          get_loop_runtime_check(loop_runtime_check::e_repeat_until_loop)
-                        );
+
+            loop_runtime_check_ptr rtc = get_loop_runtime_check(loop_runtime_check::e_repeat_until_loop);
+
+            if (!break_continue_present)
+            {
+               if (rtc)
+                  return node_allocator_->allocate<repeat_until_loop_rtc_node_t>
+                           (condition, branch,  rtc);
+               else
+                  return node_allocator_->allocate<repeat_until_loop_node_t>
+                           (condition, branch);
+            }
             #ifndef exprtk_disable_break_continue
             else
-               return node_allocator_->allocate<repeat_until_loop_bc_node_t>
-                        (
-                          condition,
-                          branch,
-                          get_loop_runtime_check(loop_runtime_check::e_repeat_until_loop)
-                        );
+            {
+               if (rtc)
+                  return node_allocator_->allocate<repeat_until_loop_bc_rtc_node_t>
+                           (condition, branch, rtc);
+               else
+                  return node_allocator_->allocate<repeat_until_loop_bc_node_t>
+                           (condition, branch);
+            }
             #else
                return error_node();
             #endif
@@ -27949,55 +32698,92 @@ namespace exprtk
                                              expression_node_ptr& condition,
                                              expression_node_ptr& incrementor,
                                              expression_node_ptr& loop_body,
-                                             bool brkcont = false) const
+                                             bool break_continue_present = false) const
          {
-            if (!brkcont && details::is_constant_node(condition))
+            if (
+                 !break_continue_present              &&
+                 !parser_->state_.return_stmt_present &&
+                 details::is_constant_node(condition)
+               )
             {
                expression_node_ptr result = error_node();
 
                if (details::is_true(condition))
+               {
                   // Infinite loops are not allowed.
+
+                  parser_->set_error(parser_error::make_error(
+                     parser_error::e_parser,
+                     parser_->current_state().token,
+                     "ERR256 - Infinite loop condition without 'break' or 'return' not allowed in for-loop",
+                     exprtk_error_location));
+
                   result = error_node();
+               }
                else
                   result = node_allocator_->allocate<details::null_node<Type> >();
 
-               free_node(*node_allocator_, initialiser);
-               free_node(*node_allocator_,   condition);
-               free_node(*node_allocator_, incrementor);
-               free_node(*node_allocator_,   loop_body);
+               details::free_node(*node_allocator_, initialiser);
+               details::free_node(*node_allocator_, condition  );
+               details::free_node(*node_allocator_, incrementor);
+               details::free_node(*node_allocator_, loop_body  );
 
                return result;
             }
             else if (details::is_null_node(condition) || (0 == condition))
             {
-               free_node(*node_allocator_, initialiser);
-               free_node(*node_allocator_,   condition);
-               free_node(*node_allocator_, incrementor);
+               details::free_node(*node_allocator_, initialiser);
+               details::free_node(*node_allocator_, condition  );
+               details::free_node(*node_allocator_, incrementor);
 
                return loop_body;
             }
-            else if (!brkcont)
-               return node_allocator_->allocate<for_loop_node_t>
-                                       (
-                                         initialiser,
-                                         condition,
-                                         incrementor,
-                                         loop_body,
-                                         get_loop_runtime_check(loop_runtime_check::e_for_loop)
-                                       );
 
+            loop_runtime_check_ptr rtc = get_loop_runtime_check(loop_runtime_check::e_for_loop);
+
+            if (!break_continue_present)
+            {
+               if (rtc)
+                  return node_allocator_->allocate<for_loop_rtc_node_t>
+                                          (
+                                             initialiser,
+                                             condition,
+                                             incrementor,
+                                             loop_body,
+                                             rtc
+                                          );
+               else
+                  return node_allocator_->allocate<for_loop_node_t>
+                                          (
+                                             initialiser,
+                                             condition,
+                                             incrementor,
+                                             loop_body
+                                          );
+            }
             #ifndef exprtk_disable_break_continue
             else
-               return node_allocator_->allocate<for_loop_bc_node_t>
-                                       (
-                                         initialiser,
-                                         condition,
-                                         incrementor,
-                                         loop_body,
-                                         get_loop_runtime_check(loop_runtime_check::e_for_loop)
-                                       );
+            {
+               if (rtc)
+                  return node_allocator_->allocate<for_loop_bc_rtc_node_t>
+                                          (
+                                             initialiser,
+                                             condition,
+                                             incrementor,
+                                             loop_body,
+                                             rtc
+                                          );
+               else
+                  return node_allocator_->allocate<for_loop_bc_node_t>
+                                          (
+                                             initialiser,
+                                             condition,
+                                             incrementor,
+                                             loop_body
+                                          );
+            }
             #else
-            return error_node();
+               return error_node();
             #endif
          }
 
@@ -28056,8 +32842,8 @@ namespace exprtk
 
             if (0 == result)
             {
-               T zero = T(0);
-               result = node_allocator_->allocate<literal_node_t>(zero);
+               const T zero = T(0);
+               result       = node_allocator_->allocate<literal_node_t>(zero);
             }
 
             for (std::size_t i = 0; i < arg_list.size(); ++i)
@@ -28066,7 +32852,7 @@ namespace exprtk
 
                if (current_expr && (current_expr != result))
                {
-                  free_node(*node_allocator_,current_expr);
+                  details::free_node(*node_allocator_,current_expr);
                }
             }
 
@@ -28080,49 +32866,57 @@ namespace exprtk
             #define case_stmt(N)                                                         \
             if (is_true(arg[(2 * N)].first)) { return arg[(2 * N) + 1].first->value(); } \
 
-            struct switch_1
+            struct switch_impl_1
             {
                static inline T process(const arg_list_t& arg)
                {
                   case_stmt(0)
 
+                  assert(arg.size() == ((2 * 1) + 1));
+
                   return arg.back().first->value();
                }
             };
 
-            struct switch_2
+            struct switch_impl_2
             {
                static inline T process(const arg_list_t& arg)
                {
                   case_stmt(0) case_stmt(1)
 
+                  assert(arg.size() == ((2 * 2) + 1));
+
                   return arg.back().first->value();
                }
             };
 
-            struct switch_3
+            struct switch_impl_3
             {
                static inline T process(const arg_list_t& arg)
                {
                   case_stmt(0) case_stmt(1)
                   case_stmt(2)
 
+                  assert(arg.size() == ((2 * 3) + 1));
+
                   return arg.back().first->value();
                }
             };
 
-            struct switch_4
+            struct switch_impl_4
             {
                static inline T process(const arg_list_t& arg)
                {
                   case_stmt(0) case_stmt(1)
                   case_stmt(2) case_stmt(3)
 
+                  assert(arg.size() == ((2 * 4) + 1));
+
                   return arg.back().first->value();
                }
             };
 
-            struct switch_5
+            struct switch_impl_5
             {
                static inline T process(const arg_list_t& arg)
                {
@@ -28130,11 +32924,13 @@ namespace exprtk
                   case_stmt(2) case_stmt(3)
                   case_stmt(4)
 
+                  assert(arg.size() == ((2 * 5) + 1));
+
                   return arg.back().first->value();
                }
             };
 
-            struct switch_6
+            struct switch_impl_6
             {
                static inline T process(const arg_list_t& arg)
                {
@@ -28142,11 +32938,13 @@ namespace exprtk
                   case_stmt(2) case_stmt(3)
                   case_stmt(4) case_stmt(5)
 
+                  assert(arg.size() == ((2 * 6) + 1));
+
                   return arg.back().first->value();
                }
             };
 
-            struct switch_7
+            struct switch_impl_7
             {
                static inline T process(const arg_list_t& arg)
                {
@@ -28155,6 +32953,8 @@ namespace exprtk
                   case_stmt(4) case_stmt(5)
                   case_stmt(6)
 
+                  assert(arg.size() == ((2 * 7) + 1));
+
                   return arg.back().first->value();
                }
             };
@@ -28182,11 +32982,11 @@ namespace exprtk
 
             switch ((arg_list.size() - 1) / 2)
             {
-               #define case_stmt(N)                                                 \
-               case N :                                                             \
-                  return node_allocator_->                                          \
-                            allocate<details::switch_n_node                         \
-                              <Type,typename switch_nodes::switch_##N> >(arg_list); \
+               #define case_stmt(N)                                                       \
+               case N :                                                                   \
+                  return node_allocator_->                                                \
+                            allocate<details::switch_n_node                               \
+                              <Type,typename switch_nodes::switch_impl_##N > >(arg_list); \
 
                case_stmt(1)
                case_stmt(2)
@@ -28217,47 +33017,69 @@ namespace exprtk
                return node_allocator_->allocate<details::multi_switch_node<Type> >(arg_list);
          }
 
-         #define unary_opr_switch_statements            \
-         case_stmt(details::  e_abs, details::  abs_op) \
-         case_stmt(details:: e_acos, details:: acos_op) \
-         case_stmt(details::e_acosh, details::acosh_op) \
-         case_stmt(details:: e_asin, details:: asin_op) \
-         case_stmt(details::e_asinh, details::asinh_op) \
-         case_stmt(details:: e_atan, details:: atan_op) \
-         case_stmt(details::e_atanh, details::atanh_op) \
-         case_stmt(details:: e_ceil, details:: ceil_op) \
-         case_stmt(details::  e_cos, details::  cos_op) \
-         case_stmt(details:: e_cosh, details:: cosh_op) \
-         case_stmt(details::  e_exp, details::  exp_op) \
-         case_stmt(details::e_expm1, details::expm1_op) \
-         case_stmt(details::e_floor, details::floor_op) \
-         case_stmt(details::  e_log, details::  log_op) \
-         case_stmt(details::e_log10, details::log10_op) \
-         case_stmt(details:: e_log2, details:: log2_op) \
-         case_stmt(details::e_log1p, details::log1p_op) \
-         case_stmt(details::  e_neg, details::  neg_op) \
-         case_stmt(details::  e_pos, details::  pos_op) \
-         case_stmt(details::e_round, details::round_op) \
-         case_stmt(details::  e_sin, details::  sin_op) \
-         case_stmt(details:: e_sinc, details:: sinc_op) \
-         case_stmt(details:: e_sinh, details:: sinh_op) \
-         case_stmt(details:: e_sqrt, details:: sqrt_op) \
-         case_stmt(details::  e_tan, details::  tan_op) \
-         case_stmt(details:: e_tanh, details:: tanh_op) \
-         case_stmt(details::  e_cot, details::  cot_op) \
-         case_stmt(details::  e_sec, details::  sec_op) \
-         case_stmt(details::  e_csc, details::  csc_op) \
-         case_stmt(details::  e_r2d, details::  r2d_op) \
-         case_stmt(details::  e_d2r, details::  d2r_op) \
-         case_stmt(details::  e_d2g, details::  d2g_op) \
-         case_stmt(details::  e_g2d, details::  g2d_op) \
-         case_stmt(details:: e_notl, details:: notl_op) \
-         case_stmt(details::  e_sgn, details::  sgn_op) \
-         case_stmt(details::  e_erf, details::  erf_op) \
-         case_stmt(details:: e_erfc, details:: erfc_op) \
-         case_stmt(details:: e_ncdf, details:: ncdf_op) \
-         case_stmt(details:: e_frac, details:: frac_op) \
-         case_stmt(details::e_trunc, details::trunc_op) \
+         inline expression_node_ptr assert_call(expression_node_ptr& assert_condition,
+                                                expression_node_ptr& assert_message,
+                                                const assert_check::assert_context& context)
+         {
+            typedef details::assert_node<Type> alloc_type;
+
+            expression_node_ptr result = node_allocator_->allocate_rrrr<alloc_type>
+               (assert_condition, assert_message, parser_->assert_check_, context);
+
+            if (result && result->valid())
+            {
+               parser_->state_.activate_side_effect("assert_call()");
+               return result;
+            }
+
+            details::free_node(*node_allocator_, result          );
+            details::free_node(*node_allocator_, assert_condition);
+            details::free_node(*node_allocator_, assert_message  );
+
+            return error_node();
+         }
+
+         #define unary_opr_switch_statements             \
+         case_stmt(details::e_abs   , details::abs_op  ) \
+         case_stmt(details::e_acos  , details::acos_op ) \
+         case_stmt(details::e_acosh , details::acosh_op) \
+         case_stmt(details::e_asin  , details::asin_op ) \
+         case_stmt(details::e_asinh , details::asinh_op) \
+         case_stmt(details::e_atan  , details::atan_op ) \
+         case_stmt(details::e_atanh , details::atanh_op) \
+         case_stmt(details::e_ceil  , details::ceil_op ) \
+         case_stmt(details::e_cos   , details::cos_op  ) \
+         case_stmt(details::e_cosh  , details::cosh_op ) \
+         case_stmt(details::e_exp   , details::exp_op  ) \
+         case_stmt(details::e_expm1 , details::expm1_op) \
+         case_stmt(details::e_floor , details::floor_op) \
+         case_stmt(details::e_log   , details::log_op  ) \
+         case_stmt(details::e_log10 , details::log10_op) \
+         case_stmt(details::e_log2  , details::log2_op ) \
+         case_stmt(details::e_log1p , details::log1p_op) \
+         case_stmt(details::e_neg   , details::neg_op  ) \
+         case_stmt(details::e_pos   , details::pos_op  ) \
+         case_stmt(details::e_round , details::round_op) \
+         case_stmt(details::e_sin   , details::sin_op  ) \
+         case_stmt(details::e_sinc  , details::sinc_op ) \
+         case_stmt(details::e_sinh  , details::sinh_op ) \
+         case_stmt(details::e_sqrt  , details::sqrt_op ) \
+         case_stmt(details::e_tan   , details::tan_op  ) \
+         case_stmt(details::e_tanh  , details::tanh_op ) \
+         case_stmt(details::e_cot   , details::cot_op  ) \
+         case_stmt(details::e_sec   , details::sec_op  ) \
+         case_stmt(details::e_csc   , details::csc_op  ) \
+         case_stmt(details::e_r2d   , details::r2d_op  ) \
+         case_stmt(details::e_d2r   , details::d2r_op  ) \
+         case_stmt(details::e_d2g   , details::d2g_op  ) \
+         case_stmt(details::e_g2d   , details::g2d_op  ) \
+         case_stmt(details::e_notl  , details::notl_op ) \
+         case_stmt(details::e_sgn   , details::sgn_op  ) \
+         case_stmt(details::e_erf   , details::erf_op  ) \
+         case_stmt(details::e_erfc  , details::erfc_op ) \
+         case_stmt(details::e_ncdf  , details::ncdf_op ) \
+         case_stmt(details::e_frac  , details::frac_op ) \
+         case_stmt(details::e_trunc , details::trunc_op) \
 
          inline expression_node_ptr synthesize_uv_expression(const details::operator_type& operation,
                                                              expression_node_ptr (&branch)[1])
@@ -28266,7 +33088,7 @@ namespace exprtk
 
             switch (operation)
             {
-               #define case_stmt(op0,op1)                                                          \
+               #define case_stmt(op0, op1)                                                         \
                case op0 : return node_allocator_->                                                 \
                              allocate<typename details::unary_variable_node<Type,op1<Type> > >(v); \
 
@@ -28281,7 +33103,7 @@ namespace exprtk
          {
             switch (operation)
             {
-               #define case_stmt(op0,op1)                                                    \
+               #define case_stmt(op0, op1)                                                   \
                case op0 : return node_allocator_->                                           \
                              allocate<typename details::unary_vector_node<Type,op1<Type> > > \
                                 (operation, branch[0]);                                      \
@@ -28297,7 +33119,7 @@ namespace exprtk
          {
             switch (operation)
             {
-               #define case_stmt(op0,op1)                                                                \
+               #define case_stmt(op0, op1)                                                               \
                case op0 : return node_allocator_->                                                       \
                              allocate<typename details::unary_branch_node<Type,op1<Type> > >(branch[0]); \
 
@@ -28336,6 +33158,8 @@ namespace exprtk
                default : return error_node();
             }
 
+            assert(temp_node);
+
             const T v = temp_node->value();
 
             details::free_node(*node_allocator_,temp_node);
@@ -28439,6 +33263,8 @@ namespace exprtk
                default : return error_node();
             }
 
+            assert(temp_node);
+
             const T v = temp_node->value();
 
             details::free_node(*node_allocator_,temp_node);
@@ -28521,7 +33347,7 @@ namespace exprtk
 
             switch (operation)
             {
-               #define case_stmt(op0,op1)                                                 \
+               #define case_stmt(op0, op1)                                                \
                case op0 : temp_node = node_allocator_->                                   \
                                          allocate<details::vararg_node<Type,op1<Type> > > \
                                             (arg_list);                                   \
@@ -28559,11 +33385,12 @@ namespace exprtk
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
-         inline expression_node_ptr varnode_optimise_varargfunc(const details::operator_type& operation, Sequence<expression_node_ptr,Allocator>& arg_list)
+         inline expression_node_ptr varnode_optimise_varargfunc(const details::operator_type& operation,
+                                                                Sequence<expression_node_ptr,Allocator>& arg_list)
          {
             switch (operation)
             {
-               #define case_stmt(op0,op1)                                                   \
+               #define case_stmt(op0, op1)                                                  \
                case op0 : return node_allocator_->                                          \
                              allocate<details::vararg_varnode<Type,op1<Type> > >(arg_list); \
 
@@ -28582,13 +33409,14 @@ namespace exprtk
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
-         inline expression_node_ptr vectorize_func(const details::operator_type& operation, Sequence<expression_node_ptr,Allocator>& arg_list)
+         inline expression_node_ptr vectorize_func(const details::operator_type& operation,
+                                                   Sequence<expression_node_ptr,Allocator>& arg_list)
          {
             if (1 == arg_list.size())
             {
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                      \
+                  #define case_stmt(op0, op1)                                                     \
                   case op0 : return node_allocator_->                                             \
                                 allocate<details::vectorize_node<Type,op1<Type> > >(arg_list[0]); \
 
@@ -28607,7 +33435,8 @@ namespace exprtk
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
-         inline expression_node_ptr vararg_function(const details::operator_type& operation, Sequence<expression_node_ptr,Allocator>& arg_list)
+         inline expression_node_ptr vararg_function(const details::operator_type& operation,
+                                                    Sequence<expression_node_ptr,Allocator>& arg_list)
          {
             if (!all_nodes_valid(arg_list))
             {
@@ -28617,9 +33446,9 @@ namespace exprtk
             }
             else if (is_constant_foldable(arg_list))
                return const_optimise_varargfunc(operation,arg_list);
-            else if ((arg_list.size() == 1) && details::is_ivector_node(arg_list[0]))
+            else if ((1 == arg_list.size()) && details::is_ivector_node(arg_list[0]))
                return vectorize_func(operation,arg_list);
-            else if ((arg_list.size() == 1) && special_one_parameter_vararg(operation))
+            else if ((1 == arg_list.size()) && special_one_parameter_vararg(operation))
                return arg_list[0];
             else if (all_nodes_variables(arg_list))
                return varnode_optimise_varargfunc(operation,arg_list);
@@ -28627,17 +33456,32 @@ namespace exprtk
             #ifndef exprtk_disable_string_capabilities
             if (details::e_smulti == operation)
             {
-               return node_allocator_->
+               expression_node_ptr result = node_allocator_->
                  allocate<details::str_vararg_node<Type,details::vararg_multi_op<Type> > >(arg_list);
+               if (result && result->valid())
+               {
+                  return result;
+               }
+
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_synthesis,
+                  token_t(),
+                  "ERR257 - Failed to synthesize node: str_vararg_node<vararg_multi_op>",
+                  exprtk_error_location));
+
+               details::free_node(*node_allocator_, result);
             }
             else
             #endif
             {
+               expression_node_ptr result = error_node();
+
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                \
-                  case op0 : return node_allocator_->                                       \
+                  #define case_stmt(op0, op1)                                               \
+                  case op0 : result = node_allocator_->                                     \
                                 allocate<details::vararg_node<Type,op1<Type> > >(arg_list); \
+                             break;                                                         \
 
                   case_stmt(details::e_sum   , details::vararg_add_op  )
                   case_stmt(details::e_prod  , details::vararg_mul_op  )
@@ -28650,7 +33494,22 @@ namespace exprtk
                   #undef case_stmt
                   default : return error_node();
                }
+
+               if (result && result->valid())
+               {
+                  return result;
+               }
+
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_synthesis,
+                  token_t(),
+                  "ERR258 - Failed to synthesize node: vararg_node",
+                  exprtk_error_location));
+
+               details::free_node(*node_allocator_, result);
             }
+
+            return error_node();
          }
 
          template <std::size_t N>
@@ -28691,7 +33550,19 @@ namespace exprtk
                   return error_node();
                }
 
-               return result;
+               if (result && result->valid())
+               {
+                  return result;
+               }
+
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_synthesis,
+                  token_t(),
+                  "ERR259 - Failed to synthesize node: function_N_node_t",
+                  exprtk_error_location));
+
+               details::free_node(*node_allocator_, result);
+               return error_node();
             }
          }
 
@@ -28728,7 +33599,19 @@ namespace exprtk
 
             parser_->state_.activate_side_effect("vararg_function_call()");
 
-            return result;
+            if (result && result->valid())
+            {
+               return result;
+            }
+
+            parser_->set_error(parser_error::make_error(
+               parser_error::e_synthesis,
+               token_t(),
+               "ERR260 - Failed to synthesize node: vararg_function_node<ivararg_function_t>",
+               exprtk_error_location));
+
+            details::free_node(*node_allocator_, result);
+            return error_node();
          }
 
          inline expression_node_ptr generic_function_call(igeneric_function_t* gf,
@@ -28747,14 +33630,23 @@ namespace exprtk
             const std::size_t no_psi = std::numeric_limits<std::size_t>::max();
 
             expression_node_ptr result = error_node();
+            std::string node_name = "Unknown";
 
             if (no_psi == param_seq_index)
+            {
                result = node_allocator_->allocate<alloc_type1>(arg_list,gf);
+               node_name = "generic_function_node<igeneric_function_t>";
+            }
             else
+            {
                result = node_allocator_->allocate<alloc_type2>(gf, param_seq_index, arg_list);
+               node_name = "multimode_genfunction_node<igeneric_function_t>";
+            }
 
             alloc_type1* genfunc_node_ptr = static_cast<alloc_type1*>(result);
 
+            assert(genfunc_node_ptr);
+
             if (
                  !arg_list.empty()                  &&
                  !gf->has_side_effects()            &&
@@ -28772,9 +33664,20 @@ namespace exprtk
             }
             else if (genfunc_node_ptr->init_branches())
             {
-               parser_->state_.activate_side_effect("generic_function_call()");
+               if (result && result->valid())
+               {
+                  parser_->state_.activate_side_effect("generic_function_call()");
+                  return result;
+               }
 
-               return result;
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_synthesis,
+                  token_t(),
+                  "ERR261 - Failed to synthesize node: " + node_name,
+                  exprtk_error_location));
+
+               details::free_node(*node_allocator_, result);
+               return error_node();
             }
             else
             {
@@ -28802,14 +33705,23 @@ namespace exprtk
             const std::size_t no_psi = std::numeric_limits<std::size_t>::max();
 
             expression_node_ptr result = error_node();
+            std::string node_name = "Unknown";
 
             if (no_psi == param_seq_index)
+            {
                result = node_allocator_->allocate<alloc_type1>(gf,arg_list);
+               node_name = "string_function_node<igeneric_function_t>";
+            }
             else
+            {
                result = node_allocator_->allocate<alloc_type2>(gf, param_seq_index, arg_list);
+               node_name = "multimode_strfunction_node<igeneric_function_t>";
+            }
 
             alloc_type1* strfunc_node_ptr = static_cast<alloc_type1*>(result);
 
+            assert(strfunc_node_ptr);
+
             if (
                  !arg_list.empty()       &&
                  !gf->has_side_effects() &&
@@ -28826,9 +33738,20 @@ namespace exprtk
             }
             else if (strfunc_node_ptr->init_branches())
             {
-               parser_->state_.activate_side_effect("string_function_call()");
+               if (result && result->valid())
+               {
+                  parser_->state_.activate_side_effect("string_function_call()");
+                  return result;
+               }
 
-               return result;
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_synthesis,
+                  token_t(),
+                  "ERR262 - Failed to synthesize node: " + node_name,
+                  exprtk_error_location));
+
+               details::free_node(*node_allocator_, result);
+               return error_node();
             }
             else
             {
@@ -28856,16 +33779,29 @@ namespace exprtk
 
             alloc_type* return_node_ptr = static_cast<alloc_type*>(result);
 
+            assert(return_node_ptr);
+
             if (return_node_ptr->init_branches())
             {
-               parser_->state_.activate_side_effect("return_call()");
+               if (result && result->valid())
+               {
+                  parser_->state_.activate_side_effect("return_call()");
+                  return result;
+               }
 
-               return result;
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_synthesis,
+                  token_t(),
+                  "ERR263 - Failed to synthesize node: return_node",
+                  exprtk_error_location));
+
+               details::free_node(*node_allocator_, result);
+               return error_node();
             }
             else
             {
-               details::free_node     (*node_allocator_,result  );
-               details::free_all_nodes(*node_allocator_,arg_list);
+               details::free_node     (*node_allocator_, result  );
+               details::free_all_nodes(*node_allocator_, arg_list);
 
                return error_node();
             }
@@ -28898,28 +33834,93 @@ namespace exprtk
          }
          #endif
 
-         inline expression_node_ptr vector_element(const std::string& symbol,
-                                                   vector_holder_ptr vector_base,
+         inline expression_node_ptr vector_element(const std::string&  symbol,
+                                                   vector_holder_ptr   vector_base,
+                                                   expression_node_ptr vec_node,
                                                    expression_node_ptr index)
          {
             expression_node_ptr result = error_node();
+            std::string node_name = "Unknown";
 
             if (details::is_constant_node(index))
             {
-               std::size_t i = static_cast<std::size_t>(details::numeric::to_int64(index->value()));
+               const std::size_t vec_index = static_cast<std::size_t>(details::numeric::to_int64(index->value()));
 
                details::free_node(*node_allocator_,index);
 
+               if (vec_index >= vector_base->size())
+               {
+                  parser_->set_error(parser_error::make_error(
+                     parser_error::e_parser,
+                     token_t(),
+                     "ERR264 - Index of " + details::to_str(vec_index) + " out of range for "
+                     "vector '" + symbol + "' of size " + details::to_str(vector_base->size()),
+                     exprtk_error_location));
+
+                  details::free_node(*node_allocator_,vec_node);
+
+                  return error_node();
+               }
+
                if (vector_base->rebaseable())
                {
-                  return node_allocator_->allocate<rebasevector_celem_node_t>(i,vector_base);
+                  vector_access_runtime_check_ptr rtc = get_vector_access_runtime_check();
+
+                  result = (rtc) ?
+                     node_allocator_->allocate<rebasevector_celem_rtc_node_t>(vec_node, vec_index, vector_base, rtc) :
+                     node_allocator_->allocate<rebasevector_celem_node_t    >(vec_node, vec_index, vector_base     ) ;
+
+                  node_name = (rtc) ?
+                     "rebasevector_elem_rtc_node_t" :
+                     "rebasevector_elem_node_t"     ;
+
+                  if (result && result->valid())
+                  {
+                     return result;
+                  }
+
+                  parser_->set_error(parser_error::make_error(
+                     parser_error::e_synthesis,
+                     token_t(),
+                     "ERR265 - Failed to synthesize node: " + node_name + " for vector: " + symbol,
+                     exprtk_error_location));
+
+                  details::free_node(*node_allocator_, result);
+                  return error_node();
+               }
+               else if (details::is_ivector_node(vec_node) && !details::is_vector_node(vec_node))
+               {
+                  vector_access_runtime_check_ptr rtc = get_vector_access_runtime_check();
+
+                  result = (rtc) ?
+                     node_allocator_->allocate<vector_celem_rtc_node_t>(vec_node, vec_index, vector_base, rtc) :
+                     node_allocator_->allocate<vector_celem_node_t    >(vec_node, vec_index, vector_base     ) ;
+
+                  node_name = (rtc) ?
+                     "vector_elem_rtc_node_t" :
+                     "vector_elem_node_t"     ;
+
+                  if (result && result->valid())
+                  {
+                     return result;
+                  }
+
+                  parser_->set_error(parser_error::make_error(
+                     parser_error::e_synthesis,
+                     token_t(),
+                     "ERR266 - Failed to synthesize node: " + node_name + " for vector: " + symbol,
+                     exprtk_error_location));
+
+                  details::free_node(*node_allocator_, result);
+                  return error_node();
                }
 
-               const scope_element& se = parser_->sem_.get_element(symbol,i);
+               const scope_element& se = parser_->sem_.get_element(symbol,vec_index);
 
-               if (se.index == i)
+               if (se.index == vec_index)
                {
                   result = se.var_node;
+                  details::free_node(*node_allocator_,vec_node);
                }
                else
                {
@@ -28928,10 +33929,10 @@ namespace exprtk
                   nse.active    = true;
                   nse.ref_count = 1;
                   nse.type      = scope_element::e_vecelem;
-                  nse.index     = i;
+                  nse.index     = vec_index;
                   nse.depth     = parser_->state_.scope_depth;
                   nse.data      = 0;
-                  nse.var_node  = node_allocator_->allocate<variable_node_t>((*(*vector_base)[i]));
+                  nse.var_node  = node_allocator_->allocate<variable_node_t>((*(*vector_base)[vec_index]));
 
                   if (!parser_->sem_.add_element(nse))
                   {
@@ -28942,19 +33943,55 @@ namespace exprtk
                      result = error_node();
                   }
 
-                  exprtk_debug(("vector_element() - INFO - Added new local vector element: %s\n",nse.name.c_str()));
+                  details::free_node(*node_allocator_,vec_node);
+
+                  exprtk_debug(("vector_element() - INFO - Added new local vector element: %s\n", nse.name.c_str()));
 
                   parser_->state_.activate_side_effect("vector_element()");
 
                   result = nse.var_node;
+                  node_name = "variable_node_t";
                }
             }
-            else if (vector_base->rebaseable())
-               result = node_allocator_->allocate<rebasevector_elem_node_t>(index,vector_base);
             else
-               result = node_allocator_->allocate<vector_elem_node_t>(index,vector_base);
+            {
+               vector_access_runtime_check_ptr rtc = get_vector_access_runtime_check();
 
-            return result;
+               if (vector_base->rebaseable())
+               {
+                  result = (rtc) ?
+                     node_allocator_->allocate<rebasevector_elem_rtc_node_t>(vec_node, index, vector_base, rtc) :
+                     node_allocator_->allocate<rebasevector_elem_node_t    >(vec_node, index, vector_base     ) ;
+
+                  node_name = (rtc) ?
+                     "rebasevector_elem_rtc_node_t" :
+                     "rebasevector_elem_node_t"     ;
+               }
+               else
+               {
+                  result = rtc ?
+                     node_allocator_->allocate<vector_elem_rtc_node_t>(vec_node, index, vector_base, rtc) :
+                     node_allocator_->allocate<vector_elem_node_t    >(vec_node, index, vector_base     ) ;
+
+                  node_name = (rtc) ?
+                     "vector_elem_rtc_node_t" :
+                     "vector_elem_node_t"     ;
+               }
+            }
+
+            if (result && result->valid())
+            {
+               return result;
+            }
+
+            parser_->set_error(parser_error::make_error(
+               parser_error::e_synthesis,
+               token_t(),
+               "ERR267 - Failed to synthesize node: " + node_name,
+               exprtk_error_location));
+
+            details::free_node(*node_allocator_, result);
+            return error_node();
          }
 
       private:
@@ -29039,43 +34076,130 @@ namespace exprtk
             }
          }
 
+         const void* base_ptr(expression_node_ptr node)
+         {
+            if (node)
+            {
+               switch(node->type())
+               {
+                  case details::expression_node<T>::e_variable:
+                     return reinterpret_cast<const void*>(&static_cast<variable_node_t*>(node)->ref());
+
+                  case details::expression_node<T>::e_vecelem:
+                     return reinterpret_cast<const void*>(&static_cast<vector_elem_node_t*>(node)->ref());
+
+                  case details::expression_node<T>::e_veccelem:
+                     return reinterpret_cast<const void*>(&static_cast<vector_celem_node_t*>(node)->ref());
+
+                  case details::expression_node<T>::e_vecelemrtc:
+                     return reinterpret_cast<const void*>(&static_cast<vector_elem_rtc_node_t*>(node)->ref());
+
+                  case details::expression_node<T>::e_veccelemrtc:
+                     return reinterpret_cast<const void*>(&static_cast<vector_celem_rtc_node_t*>(node)->ref());
+
+                  case details::expression_node<T>::e_rbvecelem:
+                     return reinterpret_cast<const void*>(&static_cast<rebasevector_elem_node_t*>(node)->ref());
+
+                  case details::expression_node<T>::e_rbvecelemrtc:
+                     return reinterpret_cast<const void*>(&static_cast<rebasevector_elem_rtc_node_t*>(node)->ref());
+
+                  case details::expression_node<T>::e_rbveccelem:
+                     return reinterpret_cast<const void*>(&static_cast<rebasevector_celem_node_t*>(node)->ref());
+
+                  case details::expression_node<T>::e_rbveccelemrtc:
+                     return reinterpret_cast<const void*>(&static_cast<rebasevector_celem_rtc_node_t*>(node)->ref());
+
+                  case details::expression_node<T>::e_vector:
+                     return reinterpret_cast<const void*>(static_cast<vector_node_t*>(node)->vec_holder().data());
+
+                  #ifndef exprtk_disable_string_capabilities
+                  case details::expression_node<T>::e_stringvar:
+                     return reinterpret_cast<const void*>((static_cast<stringvar_node_t*>(node)->base()));
+
+                  case details::expression_node<T>::e_stringvarrng:
+                     return reinterpret_cast<const void*>((static_cast<string_range_node_t*>(node)->base()));
+                  #endif
+                  default : return reinterpret_cast<const void*>(0);
+               }
+            }
+
+            return reinterpret_cast<const void*>(0);
+         }
+
+         bool assign_immutable_symbol(expression_node_ptr node)
+         {
+            interval_t interval;
+            const void* baseptr_addr = base_ptr(node);
+
+            exprtk_debug(("assign_immutable_symbol - base ptr addr: %p\n", baseptr_addr));
+
+            if (parser_->immutable_memory_map_.in_interval(baseptr_addr,interval))
+            {
+               typename immutable_symtok_map_t::iterator itr = parser_->immutable_symtok_map_.find(interval);
+
+               if (parser_->immutable_symtok_map_.end() != itr)
+               {
+                  token_t& token = itr->second;
+                  parser_->set_error(parser_error::make_error(
+                     parser_error::e_parser,
+                     token,
+                     "ERR268 - Symbol '" + token.value + "' cannot be assigned-to as it is immutable.",
+                     exprtk_error_location));
+               }
+               else
+                  parser_->set_synthesis_error("Unable to assign symbol is immutable.");
+
+               return true;
+            }
+
+            return false;
+         }
+
          inline expression_node_ptr synthesize_assignment_expression(const details::operator_type& operation, expression_node_ptr (&branch)[2])
          {
-            if (details::is_variable_node(branch[0]))
+            if (assign_immutable_symbol(branch[0]))
+            {
+               return error_node();
+            }
+            else if (details::is_variable_node(branch[0]))
             {
                lodge_assignment(e_st_variable,branch[0]);
-
                return synthesize_expression<assignment_node_t,2>(operation,branch);
             }
-            else if (details::is_vector_elem_node(branch[0]))
+            else if (details::is_vector_elem_node(branch[0]) || details::is_vector_celem_node(branch[0]))
             {
                lodge_assignment(e_st_vecelem,branch[0]);
-
                return synthesize_expression<assignment_vec_elem_node_t, 2>(operation, branch);
             }
+            else if (details::is_vector_elem_rtc_node(branch[0]) || details::is_vector_celem_rtc_node(branch[0]))
+            {
+               lodge_assignment(e_st_vecelem,branch[0]);
+               return synthesize_expression<assignment_vec_elem_rtc_node_t, 2>(operation, branch);
+            }
             else if (details::is_rebasevector_elem_node(branch[0]))
             {
                lodge_assignment(e_st_vecelem,branch[0]);
-
                return synthesize_expression<assignment_rebasevec_elem_node_t, 2>(operation, branch);
             }
+            else if (details::is_rebasevector_elem_rtc_node(branch[0]))
+            {
+               lodge_assignment(e_st_vecelem,branch[0]);
+               return synthesize_expression<assignment_rebasevec_elem_rtc_node_t, 2>(operation, branch);
+            }
             else if (details::is_rebasevector_celem_node(branch[0]))
             {
                lodge_assignment(e_st_vecelem,branch[0]);
-
                return synthesize_expression<assignment_rebasevec_celem_node_t, 2>(operation, branch);
             }
             #ifndef exprtk_disable_string_capabilities
             else if (details::is_string_node(branch[0]))
             {
                lodge_assignment(e_st_string,branch[0]);
-
                return synthesize_expression<assignment_string_node_t,2>(operation, branch);
             }
             else if (details::is_string_range_node(branch[0]))
             {
                lodge_assignment(e_st_string,branch[0]);
-
                return synthesize_expression<assignment_string_range_node_t,2>(operation, branch);
             }
             #endif
@@ -29085,12 +34209,26 @@ namespace exprtk
 
                if (details::is_ivector_node(branch[1]))
                   return synthesize_expression<assignment_vecvec_node_t,2>(operation, branch);
-              else
+               else
                   return synthesize_expression<assignment_vec_node_t,2>(operation, branch);
             }
+            else if (details::is_literal_node(branch[0]))
+            {
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_syntax,
+                  parser_->current_state().token,
+                  "ERR269 - Cannot assign value to const variable",
+                  exprtk_error_location));
+
+               return error_node();
+            }
             else
             {
-               parser_->set_synthesis_error("Invalid assignment operation.[1]");
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_syntax,
+                  parser_->current_state().token,
+                  "ERR270 - Invalid branches for assignment operator '" + details::to_str(operation) + "'",
+                  exprtk_error_location));
 
                return error_node();
             }
@@ -29099,22 +34237,32 @@ namespace exprtk
          inline expression_node_ptr synthesize_assignment_operation_expression(const details::operator_type& operation,
                                                                                expression_node_ptr (&branch)[2])
          {
+            if (assign_immutable_symbol(branch[0]))
+            {
+               return error_node();
+            }
+
+            expression_node_ptr result = error_node();
+            std::string node_name = "Unknown";
+
             if (details::is_variable_node(branch[0]))
             {
                lodge_assignment(e_st_variable,branch[0]);
 
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                                  \
-                  case op0 : return node_allocator_->                                                         \
+                  #define case_stmt(op0, op1)                                                                 \
+                  case op0 : result = node_allocator_->                                                       \
                                 template allocate_rrr<typename details::assignment_op_node<Type,op1<Type> > > \
                                    (operation, branch[0], branch[1]);                                         \
-
-                  case_stmt(details::e_addass,details::add_op)
-                  case_stmt(details::e_subass,details::sub_op)
-                  case_stmt(details::e_mulass,details::mul_op)
-                  case_stmt(details::e_divass,details::div_op)
-                  case_stmt(details::e_modass,details::mod_op)
+                             node_name = "assignment_op_node";                                                \
+                             break;                                                                           \
+
+                  case_stmt(details::e_addass , details::add_op)
+                  case_stmt(details::e_subass , details::sub_op)
+                  case_stmt(details::e_mulass , details::mul_op)
+                  case_stmt(details::e_divass , details::div_op)
+                  case_stmt(details::e_modass , details::mod_op)
                   #undef case_stmt
                   default : return error_node();
                }
@@ -29125,16 +34273,62 @@ namespace exprtk
 
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                                            \
-                  case op0 : return node_allocator_->                                                                   \
+                  #define case_stmt(op0, op1)                                                                           \
+                  case op0 : result = node_allocator_->                                                                 \
                                  template allocate_rrr<typename details::assignment_vec_elem_op_node<Type,op1<Type> > > \
                                     (operation, branch[0], branch[1]);                                                  \
+                             node_name = "assignment_vec_elem_op_node";                                                 \
+                             break;                                                                                     \
+
+                  case_stmt(details::e_addass , details::add_op)
+                  case_stmt(details::e_subass , details::sub_op)
+                  case_stmt(details::e_mulass , details::mul_op)
+                  case_stmt(details::e_divass , details::div_op)
+                  case_stmt(details::e_modass , details::mod_op)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else if (details::is_vector_elem_rtc_node(branch[0]))
+            {
+               lodge_assignment(e_st_vecelem,branch[0]);
 
-                  case_stmt(details::e_addass,details::add_op)
-                  case_stmt(details::e_subass,details::sub_op)
-                  case_stmt(details::e_mulass,details::mul_op)
-                  case_stmt(details::e_divass,details::div_op)
-                  case_stmt(details::e_modass,details::mod_op)
+               switch (operation)
+               {
+                  #define case_stmt(op0, op1)                                                                               \
+                  case op0 : result = node_allocator_->                                                                     \
+                                 template allocate_rrr<typename details::assignment_vec_elem_op_rtc_node<Type,op1<Type> > > \
+                                    (operation, branch[0], branch[1]);                                                      \
+                             node_name = "assignment_vec_elem_op_rtc_node";                                                 \
+                             break;                                                                                         \
+
+                  case_stmt(details::e_addass , details::add_op)
+                  case_stmt(details::e_subass , details::sub_op)
+                  case_stmt(details::e_mulass , details::mul_op)
+                  case_stmt(details::e_divass , details::div_op)
+                  case_stmt(details::e_modass , details::mod_op)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else if (details::is_vector_celem_rtc_node(branch[0]))
+            {
+               lodge_assignment(e_st_vecelem,branch[0]);
+
+               switch (operation)
+               {
+                  #define case_stmt(op0, op1)                                                                                \
+                  case op0 : result = node_allocator_->                                                                      \
+                                 template allocate_rrr<typename details::assignment_vec_celem_op_rtc_node<Type,op1<Type> > > \
+                                    (operation, branch[0], branch[1]);                                                       \
+                             node_name = "assignment_vec_celem_op_rtc_node";                                                 \
+                             break;                                                                                          \
+
+                  case_stmt(details::e_addass , details::add_op)
+                  case_stmt(details::e_subass , details::sub_op)
+                  case_stmt(details::e_mulass , details::mul_op)
+                  case_stmt(details::e_divass , details::div_op)
+                  case_stmt(details::e_modass , details::mod_op)
                   #undef case_stmt
                   default : return error_node();
                }
@@ -29145,16 +34339,18 @@ namespace exprtk
 
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                                                  \
-                  case op0 : return node_allocator_->                                                                         \
+                  #define case_stmt(op0, op1)                                                                                 \
+                  case op0 : result = node_allocator_->                                                                       \
                                  template allocate_rrr<typename details::assignment_rebasevec_elem_op_node<Type,op1<Type> > > \
                                     (operation, branch[0], branch[1]);                                                        \
-
-                  case_stmt(details::e_addass,details::add_op)
-                  case_stmt(details::e_subass,details::sub_op)
-                  case_stmt(details::e_mulass,details::mul_op)
-                  case_stmt(details::e_divass,details::div_op)
-                  case_stmt(details::e_modass,details::mod_op)
+                             node_name = "assignment_rebasevec_elem_op_node";                                                 \
+                             break;                                                                                           \
+
+                  case_stmt(details::e_addass , details::add_op)
+                  case_stmt(details::e_subass , details::sub_op)
+                  case_stmt(details::e_mulass , details::mul_op)
+                  case_stmt(details::e_divass , details::div_op)
+                  case_stmt(details::e_modass , details::mod_op)
                   #undef case_stmt
                   default : return error_node();
                }
@@ -29165,16 +34361,62 @@ namespace exprtk
 
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                                                   \
-                  case op0 : return node_allocator_->                                                                          \
+                  #define case_stmt(op0, op1)                                                                                  \
+                  case op0 : result = node_allocator_->                                                                        \
                                  template allocate_rrr<typename details::assignment_rebasevec_celem_op_node<Type,op1<Type> > > \
                                     (operation, branch[0], branch[1]);                                                         \
+                             node_name = "assignment_rebasevec_celem_op_node";                                                 \
+                             break;                                                                                            \
+
+                  case_stmt(details::e_addass , details::add_op)
+                  case_stmt(details::e_subass , details::sub_op)
+                  case_stmt(details::e_mulass , details::mul_op)
+                  case_stmt(details::e_divass , details::div_op)
+                  case_stmt(details::e_modass , details::mod_op)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else if (details::is_rebasevector_elem_rtc_node(branch[0]))
+            {
+               lodge_assignment(e_st_vecelem,branch[0]);
 
-                  case_stmt(details::e_addass,details::add_op)
-                  case_stmt(details::e_subass,details::sub_op)
-                  case_stmt(details::e_mulass,details::mul_op)
-                  case_stmt(details::e_divass,details::div_op)
-                  case_stmt(details::e_modass,details::mod_op)
+               switch (operation)
+               {
+                  #define case_stmt(op0, op1)                                                                                     \
+                  case op0 : result = node_allocator_->                                                                           \
+                                 template allocate_rrr<typename details::assignment_rebasevec_elem_op_rtc_node<Type,op1<Type> > > \
+                                    (operation, branch[0], branch[1]);                                                            \
+                             node_name = "assignment_rebasevec_elem_op_rtc_node";                                                 \
+                             break;                                                                                               \
+
+                  case_stmt(details::e_addass , details::add_op)
+                  case_stmt(details::e_subass , details::sub_op)
+                  case_stmt(details::e_mulass , details::mul_op)
+                  case_stmt(details::e_divass , details::div_op)
+                  case_stmt(details::e_modass , details::mod_op)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else if (details::is_rebasevector_celem_rtc_node(branch[0]))
+            {
+               lodge_assignment(e_st_vecelem,branch[0]);
+
+               switch (operation)
+               {
+                  #define case_stmt(op0, op1)                                                                                      \
+                  case op0 : result = node_allocator_->                                                                            \
+                                 template allocate_rrr<typename details::assignment_rebasevec_celem_op_rtc_node<Type,op1<Type> > > \
+                                    (operation, branch[0], branch[1]);                                                             \
+                             node_name = "assignment_rebasevec_celem_op_rtc_node";                                                 \
+                             break;                                                                                                \
+
+                  case_stmt(details::e_addass , details::add_op)
+                  case_stmt(details::e_subass , details::sub_op)
+                  case_stmt(details::e_mulass , details::mul_op)
+                  case_stmt(details::e_divass , details::div_op)
+                  case_stmt(details::e_modass , details::mod_op)
                   #undef case_stmt
                   default : return error_node();
                }
@@ -29187,16 +34429,18 @@ namespace exprtk
                {
                   switch (operation)
                   {
-                     #define case_stmt(op0,op1)                                                                         \
-                     case op0 : return node_allocator_->                                                                \
+                     #define case_stmt(op0, op1)                                                                        \
+                     case op0 : result = node_allocator_->                                                              \
                                    template allocate_rrr<typename details::assignment_vecvec_op_node<Type,op1<Type> > > \
                                       (operation, branch[0], branch[1]);                                                \
-
-                     case_stmt(details::e_addass,details::add_op)
-                     case_stmt(details::e_subass,details::sub_op)
-                     case_stmt(details::e_mulass,details::mul_op)
-                     case_stmt(details::e_divass,details::div_op)
-                     case_stmt(details::e_modass,details::mod_op)
+                                node_name = "assignment_rebasevec_celem_op_node";                                       \
+                                break;                                                                                  \
+
+                     case_stmt(details::e_addass , details::add_op)
+                     case_stmt(details::e_subass , details::sub_op)
+                     case_stmt(details::e_mulass , details::mul_op)
+                     case_stmt(details::e_divass , details::div_op)
+                     case_stmt(details::e_modass , details::mod_op)
                      #undef case_stmt
                      default : return error_node();
                   }
@@ -29205,16 +34449,18 @@ namespace exprtk
                {
                   switch (operation)
                   {
-                     #define case_stmt(op0,op1)                                                                      \
-                     case op0 : return node_allocator_->                                                             \
+                     #define case_stmt(op0, op1)                                                                     \
+                     case op0 : result = node_allocator_->                                                           \
                                    template allocate_rrr<typename details::assignment_vec_op_node<Type,op1<Type> > > \
                                       (operation, branch[0], branch[1]);                                             \
-
-                     case_stmt(details::e_addass,details::add_op)
-                     case_stmt(details::e_subass,details::sub_op)
-                     case_stmt(details::e_mulass,details::mul_op)
-                     case_stmt(details::e_divass,details::div_op)
-                     case_stmt(details::e_modass,details::mod_op)
+                                node_name = "assignment_vec_op_node";                                                \
+                                break;                                                                               \
+
+                     case_stmt(details::e_addass , details::add_op)
+                     case_stmt(details::e_subass , details::sub_op)
+                     case_stmt(details::e_mulass , details::mul_op)
+                     case_stmt(details::e_divass , details::div_op)
+                     case_stmt(details::e_modass , details::mod_op)
                      #undef case_stmt
                      default : return error_node();
                   }
@@ -29230,15 +34476,34 @@ namespace exprtk
 
                lodge_assignment(e_st_string,branch[0]);
 
-               return synthesize_expression<addass_t,2>(operation,branch);
+               result = synthesize_expression<addass_t,2>(operation,branch);
+               node_name = "assignment_string_node<T,details::asn_addassignment>";
             }
             #endif
             else
             {
-               parser_->set_synthesis_error("Invalid assignment operation[2]");
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_syntax,
+                  parser_->current_state().token,
+                  "ERR271 - Invalid branches for assignment operator '" + details::to_str(operation) + "'",
+                  exprtk_error_location));
 
                return error_node();
             }
+
+            if (result && result->valid())
+            {
+               return result;
+            }
+
+            parser_->set_error(parser_error::make_error(
+               parser_error::e_synthesis,
+               token_t(),
+               "ERR272 - Failed to synthesize node: " + node_name,
+               exprtk_error_location));
+
+            details::free_node(*node_allocator_, result);
+            return error_node();
          }
 
          inline expression_node_ptr synthesize_veceqineqlogic_operation_expression(const details::operator_type& operation,
@@ -29247,29 +34512,34 @@ namespace exprtk
             const bool is_b0_ivec = details::is_ivector_node(branch[0]);
             const bool is_b1_ivec = details::is_ivector_node(branch[1]);
 
-            #define batch_eqineq_logic_case                \
-            case_stmt(details::   e_lt, details::   lt_op) \
-            case_stmt(details::  e_lte, details::  lte_op) \
-            case_stmt(details::   e_gt, details::   gt_op) \
-            case_stmt(details::  e_gte, details::  gte_op) \
-            case_stmt(details::   e_eq, details::   eq_op) \
-            case_stmt(details::   e_ne, details::   ne_op) \
-            case_stmt(details::e_equal, details::equal_op) \
-            case_stmt(details::  e_and, details::  and_op) \
-            case_stmt(details:: e_nand, details:: nand_op) \
-            case_stmt(details::   e_or, details::   or_op) \
-            case_stmt(details::  e_nor, details::  nor_op) \
-            case_stmt(details::  e_xor, details::  xor_op) \
-            case_stmt(details:: e_xnor, details:: xnor_op) \
+            #define batch_eqineq_logic_case                 \
+            case_stmt(details::e_lt    , details::lt_op   ) \
+            case_stmt(details::e_lte   , details::lte_op  ) \
+            case_stmt(details::e_gt    , details::gt_op   ) \
+            case_stmt(details::e_gte   , details::gte_op  ) \
+            case_stmt(details::e_eq    , details::eq_op   ) \
+            case_stmt(details::e_ne    , details::ne_op   ) \
+            case_stmt(details::e_equal , details::equal_op) \
+            case_stmt(details::e_and   , details::and_op  ) \
+            case_stmt(details::e_nand  , details::nand_op ) \
+            case_stmt(details::e_or    , details::or_op   ) \
+            case_stmt(details::e_nor   , details::nor_op  ) \
+            case_stmt(details::e_xor   , details::xor_op  ) \
+            case_stmt(details::e_xnor  , details::xnor_op ) \
+
+            expression_node_ptr result = error_node();
+            std::string node_name = "Unknown";
 
             if (is_b0_ivec && is_b1_ivec)
             {
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                                     \
-                  case op0 : return node_allocator_->                                                            \
+                  #define case_stmt(op0, op1)                                                                    \
+                  case op0 : result = node_allocator_->                                                          \
                                 template allocate_rrr<typename details::vec_binop_vecvec_node<Type,op1<Type> > > \
                                    (operation, branch[0], branch[1]);                                            \
+                             node_name = "vec_binop_vecvec_node";                                                \
+                             break;                                                                              \
 
                   batch_eqineq_logic_case
                   #undef case_stmt
@@ -29280,10 +34550,12 @@ namespace exprtk
             {
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                                     \
-                  case op0 : return node_allocator_->                                                            \
+                  #define case_stmt(op0, op1)                                                                    \
+                  case op0 : result = node_allocator_->                                                          \
                                 template allocate_rrr<typename details::vec_binop_vecval_node<Type,op1<Type> > > \
                                    (operation, branch[0], branch[1]);                                            \
+                             node_name = "vec_binop_vecval_node";                                                \
+                             break;                                                                              \
 
                   batch_eqineq_logic_case
                   #undef case_stmt
@@ -29294,10 +34566,12 @@ namespace exprtk
             {
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                                     \
-                  case op0 : return node_allocator_->                                                            \
+                  #define case_stmt(op0, op1)                                                                    \
+                  case op0 : result = node_allocator_->                                                          \
                                 template allocate_rrr<typename details::vec_binop_valvec_node<Type,op1<Type> > > \
                                    (operation, branch[0], branch[1]);                                            \
+                             node_name = "vec_binop_valvec_node";                                                \
+                             break;                                                                              \
 
                   batch_eqineq_logic_case
                   #undef case_stmt
@@ -29307,6 +34581,20 @@ namespace exprtk
             else
                return error_node();
 
+            if (result && result->valid())
+            {
+               return result;
+            }
+
+            parser_->set_error(parser_error::make_error(
+               parser_error::e_synthesis,
+               token_t(),
+               "ERR273 - Failed to synthesize node: " + node_name,
+               exprtk_error_location));
+
+            details::free_node(*node_allocator_, result);
+            return error_node();
+
             #undef batch_eqineq_logic_case
          }
 
@@ -29316,21 +34604,26 @@ namespace exprtk
             const bool is_b0_ivec = details::is_ivector_node(branch[0]);
             const bool is_b1_ivec = details::is_ivector_node(branch[1]);
 
-            #define vector_ops                        \
-            case_stmt(details::e_add,details::add_op) \
-            case_stmt(details::e_sub,details::sub_op) \
-            case_stmt(details::e_mul,details::mul_op) \
-            case_stmt(details::e_div,details::div_op) \
-            case_stmt(details::e_mod,details::mod_op) \
+            #define vector_ops                          \
+            case_stmt(details::e_add , details::add_op) \
+            case_stmt(details::e_sub , details::sub_op) \
+            case_stmt(details::e_mul , details::mul_op) \
+            case_stmt(details::e_div , details::div_op) \
+            case_stmt(details::e_mod , details::mod_op) \
+
+            expression_node_ptr result = error_node();
+            std::string node_name = "Unknown";
 
             if (is_b0_ivec && is_b1_ivec)
             {
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                                     \
-                  case op0 : return node_allocator_->                                                            \
+                  #define case_stmt(op0, op1)                                                                    \
+                  case op0 : result = node_allocator_->                                                          \
                                 template allocate_rrr<typename details::vec_binop_vecvec_node<Type,op1<Type> > > \
                                    (operation, branch[0], branch[1]);                                            \
+                             node_name = "vec_binop_vecvec_node";                                                \
+                             break;                                                                              \
 
                   vector_ops
                   case_stmt(details::e_pow,details:: pow_op)
@@ -29342,10 +34635,12 @@ namespace exprtk
             {
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                                     \
-                  case op0 : return node_allocator_->                                                            \
+                  #define case_stmt(op0, op1)                                                                    \
+                  case op0 : result = node_allocator_->                                                          \
                                 template allocate_rrr<typename details::vec_binop_vecval_node<Type,op1<Type> > > \
                                    (operation, branch[0], branch[1]);                                            \
+                             node_name = "vec_binop_vecval_node(b0ivec,!b1ivec)";                                \
+                             break;                                                                              \
 
                   vector_ops
                   case_stmt(details::e_pow,details:: pow_op)
@@ -29357,10 +34652,12 @@ namespace exprtk
             {
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                                     \
-                  case op0 : return node_allocator_->                                                            \
+                  #define case_stmt(op0, op1)                                                                    \
+                  case op0 : result = node_allocator_->                                                          \
                                 template allocate_rrr<typename details::vec_binop_valvec_node<Type,op1<Type> > > \
                                    (operation, branch[0], branch[1]);                                            \
+                             node_name = "vec_binop_vecval_node(!b0ivec,b1ivec)";                                \
+                             break;                                                                              \
 
                   vector_ops
                   #undef case_stmt
@@ -29370,6 +34667,20 @@ namespace exprtk
             else
                return error_node();
 
+            if (result && result->valid())
+            {
+               return result;
+            }
+
+            parser_->set_error(parser_error::make_error(
+               parser_error::e_synthesis,
+               token_t(),
+               "ERR274 - Failed to synthesize node: " + node_name,
+               exprtk_error_location));
+
+            details::free_node(*node_allocator_, result);
+            return error_node();
+
             #undef vector_ops
          }
 
@@ -29387,6 +34698,7 @@ namespace exprtk
             #endif
 
             expression_node_ptr result = error_node();
+            std::string node_name      = "Unknown";
 
             if (v0_is_ivar && v1_is_ivar)
             {
@@ -29400,36 +34712,57 @@ namespace exprtk
                     (0 != (v1 = dynamic_cast<variable_node_ptr>(branch[1])))
                   )
                {
-                  result = node_allocator_->allocate<details::swap_node<T> >(v0,v1);
+                  result    = node_allocator_->allocate<details::swap_node<T> >(v0,v1);
+                  node_name = "swap_node";
                }
                else
-                  result = node_allocator_->allocate<details::swap_generic_node<T> >(branch[0],branch[1]);
+               {
+                  result    = node_allocator_->allocate<details::swap_generic_node<T> >(branch[0],branch[1]);
+                  node_name = "swap_generic_node";
+               }
             }
             else if (v0_is_ivec && v1_is_ivec)
             {
-               result = node_allocator_->allocate<details::swap_vecvec_node<T> >(branch[0],branch[1]);
+               result    = node_allocator_->allocate<details::swap_vecvec_node<T> >(branch[0],branch[1]);
+               node_name = "swap_vecvec_node";
             }
             #ifndef exprtk_disable_string_capabilities
             else if (v0_is_str && v1_is_str)
             {
                if (is_string_node(branch[0]) && is_string_node(branch[1]))
+               {
                   result = node_allocator_->allocate<details::swap_string_node<T> >
                                                (branch[0], branch[1]);
+                  node_name = "swap_string_node";
+               }
                else
+               {
                   result = node_allocator_->allocate<details::swap_genstrings_node<T> >
                                                (branch[0], branch[1]);
+                  node_name = "swap_genstrings_node";
+               }
             }
             #endif
             else
             {
                parser_->set_synthesis_error("Only variables, strings, vectors or vector elements can be swapped");
-
                return error_node();
             }
 
-            parser_->state_.activate_side_effect("synthesize_swap_expression()");
+            if (result && result->valid())
+            {
+               parser_->state_.activate_side_effect("synthesize_swap_expression()");
+               return result;
+            }
 
-            return result;
+            parser_->set_error(parser_error::make_error(
+               parser_error::e_synthesis,
+               token_t(),
+               "ERR275 - Failed to synthesize node: " + node_name,
+               exprtk_error_location));
+
+            details::free_node(*node_allocator_, result);
+            return error_node();
          }
 
          #ifndef exprtk_disable_sc_andor
@@ -29467,8 +34800,8 @@ namespace exprtk
 
             if (result)
             {
-               free_node(*node_allocator_, branch[0]);
-               free_node(*node_allocator_, branch[1]);
+               details::free_node(*node_allocator_, branch[0]);
+               details::free_node(*node_allocator_, branch[1]);
 
                return result;
             }
@@ -29490,27 +34823,27 @@ namespace exprtk
          }
          #endif
 
-         #define basic_opr_switch_statements        \
-         case_stmt(details::e_add, details::add_op) \
-         case_stmt(details::e_sub, details::sub_op) \
-         case_stmt(details::e_mul, details::mul_op) \
-         case_stmt(details::e_div, details::div_op) \
-         case_stmt(details::e_mod, details::mod_op) \
-         case_stmt(details::e_pow, details::pow_op) \
-
-         #define extended_opr_switch_statements       \
-         case_stmt(details::  e_lt, details::  lt_op) \
-         case_stmt(details:: e_lte, details:: lte_op) \
-         case_stmt(details::  e_gt, details::  gt_op) \
-         case_stmt(details:: e_gte, details:: gte_op) \
-         case_stmt(details::  e_eq, details::  eq_op) \
-         case_stmt(details::  e_ne, details::  ne_op) \
-         case_stmt(details:: e_and, details:: and_op) \
-         case_stmt(details::e_nand, details::nand_op) \
-         case_stmt(details::  e_or, details::  or_op) \
-         case_stmt(details:: e_nor, details:: nor_op) \
-         case_stmt(details:: e_xor, details:: xor_op) \
-         case_stmt(details::e_xnor, details::xnor_op) \
+         #define basic_opr_switch_statements         \
+         case_stmt(details::e_add , details::add_op) \
+         case_stmt(details::e_sub , details::sub_op) \
+         case_stmt(details::e_mul , details::mul_op) \
+         case_stmt(details::e_div , details::div_op) \
+         case_stmt(details::e_mod , details::mod_op) \
+         case_stmt(details::e_pow , details::pow_op) \
+
+         #define extended_opr_switch_statements        \
+         case_stmt(details::e_lt   , details::lt_op  ) \
+         case_stmt(details::e_lte  , details::lte_op ) \
+         case_stmt(details::e_gt   , details::gt_op  ) \
+         case_stmt(details::e_gte  , details::gte_op ) \
+         case_stmt(details::e_eq   , details::eq_op  ) \
+         case_stmt(details::e_ne   , details::ne_op  ) \
+         case_stmt(details::e_and  , details::and_op ) \
+         case_stmt(details::e_nand , details::nand_op) \
+         case_stmt(details::e_or   , details::or_op  ) \
+         case_stmt(details::e_nor  , details::nor_op ) \
+         case_stmt(details::e_xor  , details::xor_op ) \
+         case_stmt(details::e_xnor , details::xnor_op) \
 
          #ifndef exprtk_disable_cardinal_pow_optimisation
          template <typename TType, template <typename, typename> class IPowNode>
@@ -29585,7 +34918,7 @@ namespace exprtk
             else if (not_recipricol)
                return cardinal_pow_optimisation_impl<expression_node_ptr,details::bipow_node>(branch[0],p);
             else
-               return cardinal_pow_optimisation_impl<expression_node_ptr,details::bipowninv_node>(branch[0],p);
+               return cardinal_pow_optimisation_impl<expression_node_ptr,details::bipowinv_node>(branch[0],p);
          }
          #else
          inline expression_node_ptr cardinal_pow_optimisation(T&, const T&)
@@ -29741,7 +35074,7 @@ namespace exprtk
 
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                           \
+                  #define case_stmt(op0, op1)                                                          \
                   case op0 : return expr_gen.node_allocator_->                                         \
                                 template allocate<typename details::binary_ext_node<Type,op1<Type> > > \
                                    (branch[0], branch[1]);                                             \
@@ -29773,7 +35106,7 @@ namespace exprtk
 
                   if (synthesis_result)
                   {
-                     free_node(*expr_gen.node_allocator_,branch[1]);
+                     details::free_node(*expr_gen.node_allocator_,branch[1]);
                      return result;
                   }
                }
@@ -29794,7 +35127,7 @@ namespace exprtk
                      {
                         const Type& v1 = static_cast<uvbn_ptr_t>(branch[1])->v();
 
-                        free_node(*expr_gen.node_allocator_,branch[1]);
+                        details::free_node(*expr_gen.node_allocator_,branch[1]);
 
                         switch (operation)
                         {
@@ -29816,7 +35149,7 @@ namespace exprtk
 
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                       \
+                  #define case_stmt(op0, op1)                                                      \
                   case op0 : return expr_gen.node_allocator_->                                     \
                                 template allocate_rc<typename details::vob_node<Type,op1<Type> > > \
                                    (v, branch[1]);                                                 \
@@ -29848,7 +35181,7 @@ namespace exprtk
 
                   if (synthesis_result)
                   {
-                     free_node(*expr_gen.node_allocator_, branch[0]);
+                     details::free_node(*expr_gen.node_allocator_, branch[0]);
 
                      return result;
                   }
@@ -29872,7 +35205,7 @@ namespace exprtk
                      {
                         const Type& v0 = static_cast<uvbn_ptr_t>(branch[0])->v();
 
-                        free_node(*expr_gen.node_allocator_,branch[0]);
+                        details::free_node(*expr_gen.node_allocator_,branch[0]);
 
                         switch (operation)
                         {
@@ -29902,7 +35235,7 @@ namespace exprtk
 
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                       \
+                  #define case_stmt(op0, op1)                                                      \
                   case op0 : return expr_gen.node_allocator_->                                     \
                                 template allocate_cr<typename details::bov_node<Type,op1<Type> > > \
                                    (branch[0], v);                                                 \
@@ -29923,17 +35256,17 @@ namespace exprtk
             {
                const Type c = static_cast<details::literal_node<Type>*>(branch[0])->value();
 
-               free_node(*expr_gen.node_allocator_,branch[0]);
+               details::free_node(*expr_gen.node_allocator_,branch[0]);
 
                if (std::equal_to<T>()(T(0),c) && (details::e_mul == operation))
                {
-                  free_node(*expr_gen.node_allocator_,branch[1]);
+                  details::free_node(*expr_gen.node_allocator_,branch[1]);
 
                   return expr_gen(T(0));
                }
                else if (std::equal_to<T>()(T(0),c) && (details::e_div == operation))
                {
-                  free_node(*expr_gen.node_allocator_, branch[1]);
+                  details::free_node(*expr_gen.node_allocator_, branch[1]);
 
                   return expr_gen(T(0));
                }
@@ -29948,8 +35281,8 @@ namespace exprtk
                   // 1. (1 * (2 * (3 * (4 * (5 * (6 * (7 * (8 * (9 + x))))))))) --> 40320 * (9 + x)
                   // 2. (1 + (2 + (3 + (4 + (5 + (6 + (7 + (8 + (9 + x))))))))) --> 45 + x
                   if (
-                       (operation == details::e_mul) ||
-                       (operation == details::e_add)
+                       (details::e_mul == operation) ||
+                       (details::e_add == operation)
                      )
                   {
                      details::cob_base_node<Type>* cobnode = static_cast<details::cob_base_node<Type>*>(branch[1]);
@@ -30014,7 +35347,7 @@ namespace exprtk
                            default             : return error_node();
                         }
 
-                        free_node(*expr_gen.node_allocator_,branch[1]);
+                        details::free_node(*expr_gen.node_allocator_,branch[1]);
 
                         return new_cobnode;
                      }
@@ -30031,7 +35364,7 @@ namespace exprtk
 
                   if (synthesis_result)
                   {
-                     free_node(*expr_gen.node_allocator_,branch[1]);
+                     details::free_node(*expr_gen.node_allocator_,branch[1]);
 
                      return result;
                   }
@@ -30040,7 +35373,7 @@ namespace exprtk
 
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                       \
+                  #define case_stmt(op0, op1)                                                      \
                   case op0 : return expr_gen.node_allocator_->                                     \
                                 template allocate_tt<typename details::cob_node<Type,op1<Type> > > \
                                    (c,  branch[1]);                                                \
@@ -30065,13 +35398,13 @@ namespace exprtk
 
                if (std::equal_to<T>()(T(0),c) && (details::e_mul == operation))
                {
-                  free_node(*expr_gen.node_allocator_, branch[0]);
+                  details::free_node(*expr_gen.node_allocator_, branch[0]);
 
                   return expr_gen(T(0));
                }
                else if (std::equal_to<T>()(T(0),c) && (details::e_div == operation))
                {
-                  free_node(*expr_gen.node_allocator_, branch[0]);
+                  details::free_node(*expr_gen.node_allocator_, branch[0]);
 
                   return expr_gen(std::numeric_limits<T>::quiet_NaN());
                }
@@ -30086,8 +35419,8 @@ namespace exprtk
                   // 1. (((((((((x + 9) * 8) * 7) * 6) * 5) * 4) * 3) * 2) * 1) --> (x + 9) * 40320
                   // 2. (((((((((x + 9) + 8) + 7) + 6) + 5) + 4) + 3) + 2) + 1) --> x + 45
                   if (
-                       (operation == details::e_mul) ||
-                       (operation == details::e_add)
+                       (details::e_mul == operation) ||
+                       (details::e_add == operation)
                      )
                   {
                      details::boc_base_node<Type>* bocnode = static_cast<details::boc_base_node<Type>*>(branch[0]);
@@ -30159,7 +35492,7 @@ namespace exprtk
 
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                       \
+                  #define case_stmt(op0, op1)                                                      \
                   case op0 : return expr_gen.node_allocator_->                                     \
                                 template allocate_cr<typename details::boc_node<Type,op1<Type> > > \
                                    (branch[0], c);                                                 \
@@ -30189,33 +35522,33 @@ namespace exprtk
 
                   if (std::equal_to<T>()(T(0),c) && (details::e_mul == operation))
                   {
-                     free_node(*expr_gen.node_allocator_, branch[0]);
-                     free_node(*expr_gen.node_allocator_, branch[1]);
+                     details::free_node(*expr_gen.node_allocator_, branch[0]);
+                     details::free_node(*expr_gen.node_allocator_, branch[1]);
 
                      return expr_gen(T(0));
                   }
                   else if (std::equal_to<T>()(T(0),c) && (details::e_div == operation))
                   {
-                     free_node(*expr_gen.node_allocator_, branch[0]);
-                     free_node(*expr_gen.node_allocator_, branch[1]);
+                     details::free_node(*expr_gen.node_allocator_, branch[0]);
+                     details::free_node(*expr_gen.node_allocator_, branch[1]);
 
                      return expr_gen(T(std::numeric_limits<T>::quiet_NaN()));
                   }
                   else if (std::equal_to<T>()(T(0),c) && (details::e_add == operation))
                   {
-                     free_node(*expr_gen.node_allocator_, branch[1]);
+                     details::free_node(*expr_gen.node_allocator_, branch[1]);
 
                      return branch[0];
                   }
                   else if (std::equal_to<T>()(T(1),c) && (details::e_mul == operation))
                   {
-                     free_node(*expr_gen.node_allocator_, branch[1]);
+                     details::free_node(*expr_gen.node_allocator_, branch[1]);
 
                      return branch[0];
                   }
                   else if (std::equal_to<T>()(T(1),c) && (details::e_div == operation))
                   {
-                     free_node(*expr_gen.node_allocator_, branch[1]);
+                     details::free_node(*expr_gen.node_allocator_, branch[1]);
 
                      return branch[0];
                   }
@@ -30258,13 +35591,13 @@ namespace exprtk
                                     template allocate_tt<typename details::cob_node<Type,details::div_op<Type> > >
                                        (cobnode->c() / c, cobnode->move_branch(0));
 
-                        free_node(*expr_gen.node_allocator_, branch[0]);
+                        details::free_node(*expr_gen.node_allocator_, branch[0]);
                      }
                   }
 
                   if (result)
                   {
-                     free_node(*expr_gen.node_allocator_,branch[1]);
+                     details::free_node(*expr_gen.node_allocator_,branch[1]);
                   }
                }
 
@@ -30277,27 +35610,27 @@ namespace exprtk
 
                   if (std::equal_to<T>()(T(0),c) && (details::e_mul == operation))
                   {
-                     free_node(*expr_gen.node_allocator_, branch[0]);
-                     free_node(*expr_gen.node_allocator_, branch[1]);
+                     details::free_node(*expr_gen.node_allocator_, branch[0]);
+                     details::free_node(*expr_gen.node_allocator_, branch[1]);
 
                      return expr_gen(T(0));
                   }
                   else if (std::equal_to<T>()(T(0),c) && (details::e_div == operation))
                   {
-                     free_node(*expr_gen.node_allocator_, branch[0]);
-                     free_node(*expr_gen.node_allocator_, branch[1]);
+                     details::free_node(*expr_gen.node_allocator_, branch[0]);
+                     details::free_node(*expr_gen.node_allocator_, branch[1]);
 
                      return expr_gen(T(0));
                   }
                   else if (std::equal_to<T>()(T(0),c) && (details::e_add == operation))
                   {
-                     free_node(*expr_gen.node_allocator_, branch[0]);
+                     details::free_node(*expr_gen.node_allocator_, branch[0]);
 
                      return branch[1];
                   }
                   else if (std::equal_to<T>()(T(1),c) && (details::e_mul == operation))
                   {
-                     free_node(*expr_gen.node_allocator_, branch[0]);
+                     details::free_node(*expr_gen.node_allocator_, branch[0]);
 
                      return branch[1];
                   }
@@ -30315,7 +35648,7 @@ namespace exprtk
                                     template allocate_tt<typename details::cob_node<Type,details::sub_op<Type> > >
                                        (c - cobnode->c(), cobnode->move_branch(0));
 
-                        free_node(*expr_gen.node_allocator_,branch[1]);
+                        details::free_node(*expr_gen.node_allocator_,branch[1]);
                      }
                   }
                   else if (details::e_sub == cobnode->operation())
@@ -30331,7 +35664,7 @@ namespace exprtk
                                     template allocate_tt<typename details::cob_node<Type,details::add_op<Type> > >
                                        (c - cobnode->c(), cobnode->move_branch(0));
 
-                        free_node(*expr_gen.node_allocator_,branch[1]);
+                        details::free_node(*expr_gen.node_allocator_,branch[1]);
                      }
                   }
                   else if (details::e_mul == cobnode->operation())
@@ -30347,7 +35680,7 @@ namespace exprtk
                                     template allocate_tt<typename details::cob_node<Type,details::div_op<Type> > >
                                        (c / cobnode->c(), cobnode->move_branch(0));
 
-                        free_node(*expr_gen.node_allocator_,branch[1]);
+                        details::free_node(*expr_gen.node_allocator_,branch[1]);
                      }
                   }
                   else if (details::e_div == cobnode->operation())
@@ -30363,13 +35696,13 @@ namespace exprtk
                                     template allocate_tt<typename details::cob_node<Type,details::mul_op<Type> > >
                                        (c / cobnode->c(), cobnode->move_branch(0));
 
-                        free_node(*expr_gen.node_allocator_,branch[1]);
+                        details::free_node(*expr_gen.node_allocator_,branch[1]);
                      }
                   }
 
                   if (result)
                   {
-                     free_node(*expr_gen.node_allocator_,branch[0]);
+                     details::free_node(*expr_gen.node_allocator_,branch[0]);
                   }
                }
 
@@ -30422,7 +35755,7 @@ namespace exprtk
                                     template allocate_tt<typename details::boc_node<Type,details::add_op<Type> > >
                                        (bocnode->move_branch(0), c - bocnode->c());
 
-                        free_node(*expr_gen.node_allocator_,branch[0]);
+                        details::free_node(*expr_gen.node_allocator_,branch[0]);
                      }
                      else if (details::e_sub == operation)
                      {
@@ -30444,7 +35777,7 @@ namespace exprtk
 
                   if (result)
                   {
-                     free_node(*expr_gen.node_allocator_, branch[1]);
+                     details::free_node(*expr_gen.node_allocator_, branch[1]);
                   }
                }
 
@@ -30468,7 +35801,7 @@ namespace exprtk
                                     template allocate_tt<typename details::cob_node<Type,details::sub_op<Type> > >
                                        (c - bocnode->c(), bocnode->move_branch(0));
 
-                        free_node(*expr_gen.node_allocator_,branch[1]);
+                        details::free_node(*expr_gen.node_allocator_,branch[1]);
                      }
                   }
                   else if (details::e_sub == bocnode->operation())
@@ -30479,7 +35812,7 @@ namespace exprtk
                                     template allocate_tt<typename details::boc_node<Type,details::add_op<Type> > >
                                        (bocnode->move_branch(0), c - bocnode->c());
 
-                        free_node(*expr_gen.node_allocator_,branch[1]);
+                        details::free_node(*expr_gen.node_allocator_,branch[1]);
                      }
                      else if (details::e_sub == operation)
                      {
@@ -30487,7 +35820,7 @@ namespace exprtk
                                     template allocate_tt<typename details::cob_node<Type,details::sub_op<Type> > >
                                        (c + bocnode->c(), bocnode->move_branch(0));
 
-                        free_node(*expr_gen.node_allocator_,branch[1]);
+                        details::free_node(*expr_gen.node_allocator_,branch[1]);
                      }
                   }
                   else if (details::e_mul == bocnode->operation())
@@ -30503,7 +35836,7 @@ namespace exprtk
                                     template allocate_tt<typename details::cob_node<Type,details::div_op<Type> > >
                                        (c / bocnode->c(), bocnode->move_branch(0));
 
-                        free_node(*expr_gen.node_allocator_,branch[1]);
+                        details::free_node(*expr_gen.node_allocator_,branch[1]);
                      }
                   }
                   else if (details::e_div == bocnode->operation())
@@ -30519,13 +35852,13 @@ namespace exprtk
                                     template allocate_tt<typename details::cob_node<Type,details::div_op<Type> > >
                                        (c * bocnode->c(), bocnode->move_branch(0));
 
-                        free_node(*expr_gen.node_allocator_,branch[1]);
+                        details::free_node(*expr_gen.node_allocator_,branch[1]);
                      }
                   }
 
                   if (result)
                   {
-                     free_node(*expr_gen.node_allocator_,branch[0]);
+                     details::free_node(*expr_gen.node_allocator_,branch[0]);
                   }
                }
 
@@ -30568,7 +35901,7 @@ namespace exprtk
 
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                       \
+                  #define case_stmt(op0, op1)                                                      \
                   case op0 : return expr_gen.node_allocator_->                                     \
                                 template allocate_rr<typename details::vov_node<Type,op1<Type> > > \
                                    (v1, v2);                                                       \
@@ -30603,7 +35936,7 @@ namespace exprtk
 
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                       \
+                  #define case_stmt(op0, op1)                                                      \
                   case op0 : return expr_gen.node_allocator_->                                     \
                                 template allocate_cr<typename details::cov_node<Type,op1<Type> > > \
                                    (c, v);                                                         \
@@ -30647,7 +35980,7 @@ namespace exprtk
 
                switch (operation)
                {
-                  #define case_stmt(op0,op1)                                                       \
+                  #define case_stmt(op0, op1)                                                      \
                   case op0 : return expr_gen.node_allocator_->                                     \
                                 template allocate_rc<typename details::voc_node<Type,op1<Type> > > \
                                    (v, c);                                                         \
@@ -30716,7 +36049,6 @@ namespace exprtk
                   case details::e_sf##op : return details::T0oT1oT2oT3_sf4ext<Type,T0,T1,T2,T3,details::sf##op##_op<Type> >:: \
                                 allocate(*(expr_gen.node_allocator_), t0, t1, t2, t3);                                        \
 
-
                   #define case_stmt1(op)                                                                                             \
                   case details::e_sf4ext##op : return details::T0oT1oT2oT3_sf4ext<Type,T0,T1,T2,T3,details::sfext##op##_op<Type> >:: \
                                 allocate(*(expr_gen.node_allocator_), t0, t1, t2, t3);                                               \
@@ -30917,9 +36249,6 @@ namespace exprtk
                const details::operator_type o0 = vov->operation();
                const details::operator_type o1 = operation;
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
 
                expression_node_ptr result = error_node();
@@ -30931,7 +36260,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<vtype,vtype,vtype>(expr_gen, "t/(t*t)", v0, v1, v2, result);
+                           template compile<vtype, vtype, vtype>(expr_gen, "t/(t*t)", v0, v1, v2, result);
 
                      exprtk_debug(("(v0 / v1) / v2 --> (vovov) v0 / (v1 * v2)\n"));
 
@@ -30945,7 +36274,11 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -30954,12 +36287,13 @@ namespace exprtk
             }
 
             static inline std::string id(expression_generator<Type>& expr_gen,
-                                         const details::operator_type o0, const details::operator_type o1)
+                                         const details::operator_type o0,
+                                         const details::operator_type o1)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "t";
             }
          };
 
@@ -30980,9 +36314,6 @@ namespace exprtk
                const details::operator_type o0 = operation;
                const details::operator_type o1 = vov->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
                expression_node_ptr result = error_node();
@@ -30994,7 +36325,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<vtype,vtype,vtype>(expr_gen, "(t*t)/t", v0, v2, v1, result);
+                           template compile<vtype, vtype, vtype>(expr_gen, "(t*t)/t", v0, v2, v1, result);
 
                      exprtk_debug(("v0 / (v1 / v2) --> (vovov) (v0 * v2) / v1\n"));
 
@@ -31008,7 +36339,11 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -31017,12 +36352,13 @@ namespace exprtk
             }
 
             static inline std::string id(expression_generator<Type>& expr_gen,
-                                         const details::operator_type o0, const details::operator_type o1)
+                                         const details::operator_type o0,
+                                         const details::operator_type o1)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)";
             }
          };
 
@@ -31043,9 +36379,6 @@ namespace exprtk
                const details::operator_type o0 = vov->operation();
                const details::operator_type o1 = operation;
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -31058,7 +36391,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<vtype,vtype,ctype>(expr_gen, "t/(t*t)", v0, v1, c, result);
+                           template compile<vtype, vtype, ctype>(expr_gen, "t/(t*t)", v0, v1, c, result);
 
                      exprtk_debug(("(v0 / v1) / c --> (vovoc) v0 / (v1 * c)\n"));
 
@@ -31072,7 +36405,11 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -31081,12 +36418,13 @@ namespace exprtk
             }
 
             static inline std::string id(expression_generator<Type>& expr_gen,
-                                         const details::operator_type o0, const details::operator_type o1)
+                                         const details::operator_type o0,
+                                         const details::operator_type o1)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "t";
             }
          };
 
@@ -31107,9 +36445,6 @@ namespace exprtk
                const details::operator_type o0 = operation;
                const details::operator_type o1 = voc->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
                expression_node_ptr result = error_node();
@@ -31121,7 +36456,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<vtype,ctype,vtype>(expr_gen, "(t*t)/t", v0, c, v1, result);
+                           template compile<vtype, ctype, vtype>(expr_gen, "(t*t)/t", v0, c, v1, result);
 
                      exprtk_debug(("v0 / (v1 / c) --> (vocov) (v0 * c) / v1\n"));
 
@@ -31135,7 +36470,11 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -31144,12 +36483,13 @@ namespace exprtk
             }
 
             static inline std::string id(expression_generator<Type>& expr_gen,
-                                         const details::operator_type o0, const details::operator_type o1)
+                                         const details::operator_type o0,
+                                         const details::operator_type o1)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)";
             }
          };
 
@@ -31170,9 +36510,6 @@ namespace exprtk
                const details::operator_type o0 = voc->operation();
                const details::operator_type o1 = operation;
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
 
                expression_node_ptr result = error_node();
@@ -31184,7 +36521,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<vtype,vtype,ctype>(expr_gen, "t/(t*t)", v0, v1, c, result);
+                           template compile<vtype, vtype, ctype>(expr_gen, "t/(t*t)", v0, v1, c, result);
 
                      exprtk_debug(("(v0 / c) / v1 --> (vovoc) v0 / (v1 * c)\n"));
 
@@ -31198,7 +36535,11 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -31207,12 +36548,13 @@ namespace exprtk
             }
 
             static inline std::string id(expression_generator<Type>& expr_gen,
-                                         const details::operator_type o0, const details::operator_type o1)
+                                         const details::operator_type o0,
+                                         const details::operator_type o1)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "t";
             }
          };
 
@@ -31233,9 +36575,6 @@ namespace exprtk
                const details::operator_type o0 = operation;
                const details::operator_type o1 = cov->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
                expression_node_ptr result = error_node();
@@ -31261,7 +36600,11 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -31270,12 +36613,13 @@ namespace exprtk
             }
 
             static inline std::string id(expression_generator<Type>& expr_gen,
-                                         const details::operator_type o0, const details::operator_type o1)
+                                         const details::operator_type o0,
+                                         const details::operator_type o1)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)";
             }
          };
 
@@ -31296,9 +36640,6 @@ namespace exprtk
                const details::operator_type o0 = cov->operation();
                const details::operator_type o1 = operation;
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
 
                expression_node_ptr result = error_node();
@@ -31324,7 +36665,11 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -31333,12 +36678,13 @@ namespace exprtk
             }
 
             static inline std::string id(expression_generator<Type>& expr_gen,
-                                         const details::operator_type o0, const details::operator_type o1)
+                                         const details::operator_type o0,
+                                         const details::operator_type o1)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "t";
             }
          };
 
@@ -31359,9 +36705,6 @@ namespace exprtk
                const details::operator_type o0 = operation;
                const details::operator_type o1 = vov->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -31388,7 +36731,11 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -31396,12 +36743,14 @@ namespace exprtk
                   return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, f0, f1);
             }
 
-            static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1)
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)";
             }
          };
 
@@ -31422,9 +36771,6 @@ namespace exprtk
                const details::operator_type o0 = cov->operation();
                const details::operator_type o1 = operation;
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -31504,7 +36850,11 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -31513,12 +36863,13 @@ namespace exprtk
             }
 
             static inline std::string id(expression_generator<Type>& expr_gen,
-                                         const details::operator_type o0, const details::operator_type o1)
+                                         const details::operator_type o0,
+                                         const details::operator_type o1)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "t";
             }
          };
 
@@ -31539,9 +36890,6 @@ namespace exprtk
                const details::operator_type o0 = operation;
                const details::operator_type o1 = voc->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -31621,7 +36969,11 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -31630,19 +36982,22 @@ namespace exprtk
             }
 
             static inline std::string id(expression_generator<Type>& expr_gen,
-                                         const details::operator_type o0, const details::operator_type o1)
+                                         const details::operator_type o0,
+                                         const details::operator_type o1)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)";
             }
          };
 
          struct synthesize_cocov_expression0
          {
             typedef typename cocov_t::type0 node_type;
-            static inline expression_node_ptr process(expression_generator<Type>&, const details::operator_type&, expression_node_ptr (&)[2])
+            static inline expression_node_ptr process(expression_generator<Type>&,
+                                                      const details::operator_type&,
+                                                      expression_node_ptr (&)[2])
             {
                // (c0 o0 c1) o1 (v) - Not possible.
                return error_node();
@@ -31666,9 +37021,6 @@ namespace exprtk
                const details::operator_type o0 = operation;
                const details::operator_type o1 = cov->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -31748,7 +37100,11 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -31756,12 +37112,14 @@ namespace exprtk
                   return node_type::allocate(*(expr_gen.node_allocator_), c0, c1, v, f0, f1);
             }
 
-            static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1)
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)";
             }
          };
 
@@ -31782,9 +37140,6 @@ namespace exprtk
                const details::operator_type o0 = voc->operation();
                const details::operator_type o1 = operation;
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -31872,7 +37227,11 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -31881,12 +37240,13 @@ namespace exprtk
             }
 
             static inline std::string id(expression_generator<Type>& expr_gen,
-                                         const details::operator_type o0, const details::operator_type o1)
+                                         const details::operator_type o0,
+                                         const details::operator_type o1)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "t";
             }
          };
 
@@ -31894,7 +37254,9 @@ namespace exprtk
          {
             typedef typename vococ_t::type0 node_type;
 
-            static inline expression_node_ptr process(expression_generator<Type>&, const details::operator_type&, expression_node_ptr (&)[2])
+            static inline expression_node_ptr process(expression_generator<Type>&,
+                                                      const details::operator_type&,
+                                                      expression_node_ptr (&)[2])
             {
                // (v) o0 (c0 o1 c1) - Not possible.
                exprtk_debug(("(v) o0 (c0 o1 c1) - Not possible.\n"));
@@ -31926,10 +37288,6 @@ namespace exprtk
                const details::operator_type o1 = operation;
                const details::operator_type o2 = vov1->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -31942,7 +37300,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<vtype,vtype,vtype,vtype>(expr_gen, "(t*t)/(t*t)", v0, v2, v1, v3, result);
+                           template compile<vtype, vtype, vtype, vtype>(expr_gen, "(t*t)/(t*t)", v0, v2, v1, v3, result);
 
                      exprtk_debug(("(v0 / v1) * (v2 / v3) --> (vovovov) (v0 * v2) / (v1 * v3)\n"));
 
@@ -31953,7 +37311,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<vtype,vtype,vtype,vtype>(expr_gen, "(t*t)/(t*t)", v0, v3, v1, v2, result);
+                           template compile<vtype, vtype, vtype, vtype>(expr_gen, "(t*t)/(t*t)", v0, v3, v1, v2, result);
 
                      exprtk_debug(("(v0 / v1) / (v2 / v3) --> (vovovov) (v0 * v3) / (v1 * v2)\n"));
 
@@ -31964,7 +37322,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<vtype,vtype,vtype,vtype>(expr_gen, "(t+t)*(t/t)", v0, v1, v3, v2, result);
+                           template compile<vtype, vtype, vtype, vtype>(expr_gen, "(t+t)*(t/t)", v0, v1, v3, v2, result);
 
                      exprtk_debug(("(v0 + v1) / (v2 / v3) --> (vovovov) (v0 + v1) * (v3 / v2)\n"));
 
@@ -31975,7 +37333,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<vtype,vtype,vtype,vtype>(expr_gen, "(t-t)*(t/t)", v0, v1, v3, v2, result);
+                           template compile<vtype, vtype, vtype, vtype>(expr_gen, "(t-t)*(t/t)", v0, v1, v3, v2, result);
 
                      exprtk_debug(("(v0 - v1) / (v2 / v3) --> (vovovov) (v0 - v1) * (v3 / v2)\n"));
 
@@ -31986,7 +37344,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<vtype,vtype,vtype,vtype>(expr_gen, "((t*t)*t)/t", v0, v1, v3, v2, result);
+                           template compile<vtype, vtype, vtype, vtype>(expr_gen, "((t*t)*t)/t", v0, v1, v3, v2, result);
 
                      exprtk_debug(("(v0 * v1) / (v2 / v3) --> (vovovov) ((v0 * v1) * v3) / v2\n"));
 
@@ -32000,7 +37358,12 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -32016,10 +37379,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t)";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -32047,10 +37410,6 @@ namespace exprtk
                const details::operator_type o1 = operation;
                const details::operator_type o2 = voc->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -32063,7 +37422,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<vtype,vtype,vtype,ctype>(expr_gen, "(t*t)/(t*t)", v0, v2, v1, c, result);
+                           template compile<vtype, vtype, vtype, ctype>(expr_gen, "(t*t)/(t*t)", v0, v2, v1, c, result);
 
                      exprtk_debug(("(v0 / v1) * (v2 / c) --> (vovovoc) (v0 * v2) / (v1 * c)\n"));
 
@@ -32074,7 +37433,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<vtype,ctype,vtype,vtype>(expr_gen, "(t*t)/(t*t)", v0, c, v1, v2, result);
+                           template compile<vtype, ctype, vtype, vtype>(expr_gen, "(t*t)/(t*t)", v0, c, v1, v2, result);
 
                      exprtk_debug(("(v0 / v1) / (v2 / c) --> (vocovov) (v0 * c) / (v1 * v2)\n"));
 
@@ -32088,7 +37447,12 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -32104,10 +37468,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t)";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -32135,10 +37499,6 @@ namespace exprtk
                const details::operator_type o1 = operation;
                const details::operator_type o2 = cov->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -32151,7 +37511,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<vtype,ctype,vtype,vtype>(expr_gen, "(t*t)/(t*t)", v0, c, v1, v2, result);
+                           template compile<vtype, ctype, vtype, vtype>(expr_gen, "(t*t)/(t*t)", v0, c, v1, v2, result);
 
                      exprtk_debug(("(v0 / v1) * (c / v2) --> (vocovov) (v0 * c) / (v1 * v2)\n"));
 
@@ -32162,7 +37522,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<vtype,vtype,vtype,ctype>(expr_gen, "(t*t)/(t*t)", v0, v2, v1, c, result);
+                           template compile<vtype, vtype, vtype, ctype>(expr_gen, "(t*t)/(t*t)", v0, v2, v1, c, result);
 
                      exprtk_debug(("(v0 / v1) / (c / v2) --> (vovovoc) (v0 * v2) / (v1 * c)\n"));
 
@@ -32176,7 +37536,12 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -32192,10 +37557,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t)";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -32223,10 +37588,6 @@ namespace exprtk
                const details::operator_type o1 = operation;
                const details::operator_type o2 = vov->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -32239,7 +37600,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<vtype,vtype,ctype,vtype>(expr_gen, "(t*t)/(t*t)", v0, v1, c, v2, result);
+                           template compile<vtype, vtype, ctype, vtype>(expr_gen, "(t*t)/(t*t)", v0, v1, c, v2, result);
 
                      exprtk_debug(("(v0 / c) * (v1 / v2) --> (vovocov) (v0 * v1) / (c * v2)\n"));
 
@@ -32250,7 +37611,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<vtype,vtype,ctype,vtype>(expr_gen, "(t*t)/(t*t)", v0, v2, c, v1, result);
+                           template compile<vtype, vtype, ctype, vtype>(expr_gen, "(t*t)/(t*t)", v0, v2, c, v1, result);
 
                      exprtk_debug(("(v0 / c) / (v1 / v2) --> (vovocov) (v0 * v2) / (c * v1)\n"));
 
@@ -32264,7 +37625,12 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -32280,10 +37646,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t)";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -32311,10 +37677,6 @@ namespace exprtk
                const details::operator_type o1 = operation;
                const details::operator_type o2 = vov->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -32327,7 +37689,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<ctype,vtype,vtype,vtype>(expr_gen, "(t*t)/(t*t)", c, v1, v0, v2, result);
+                           template compile<ctype, vtype, vtype, vtype>(expr_gen, "(t*t)/(t*t)", c, v1, v0, v2, result);
 
                      exprtk_debug(("(c / v0) * (v1 / v2) --> (covovov) (c * v1) / (v0 * v2)\n"));
 
@@ -32338,7 +37700,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<ctype,vtype,vtype,vtype>(expr_gen, "(t*t)/(t*t)", c, v2, v0, v1, result);
+                           template compile<ctype, vtype, vtype, vtype>(expr_gen, "(t*t)/(t*t)", c, v2, v0, v1, result);
 
                      exprtk_debug(("(c / v0) / (v1 / v2) --> (covovov) (c * v2) / (v0 * v1)\n"));
 
@@ -32352,7 +37714,12 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -32368,10 +37735,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t)";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -32399,10 +37766,6 @@ namespace exprtk
                const details::operator_type o1 = operation;
                const details::operator_type o2 = cov1->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -32415,7 +37778,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result);
 
                      exprtk_debug(("(c0 + v0) + (c1 + v1) --> (covov) (c0 + c1) + v0 + v1\n"));
 
@@ -32426,7 +37789,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result);
 
                      exprtk_debug(("(c0 + v0) - (c1 + v1) --> (covov) (c0 - c1) + v0 - v1\n"));
 
@@ -32437,7 +37800,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t-t)+t", (c0 - c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t-t)+t", (c0 - c1), v0, v1, result);
 
                      exprtk_debug(("(c0 - v0) - (c1 - v1) --> (covov) (c0 - c1) - v0 + v1\n"));
 
@@ -32448,7 +37811,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result);
 
                      exprtk_debug(("(c0 * v0) * (c1 * v1) --> (covov) (c0 * c1) * v0 * v1\n"));
 
@@ -32459,7 +37822,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result);
 
                      exprtk_debug(("(c0 * v0) / (c1 * v1) --> (covov) (c0 / c1) * (v0 / v1)\n"));
 
@@ -32470,7 +37833,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "t/(t*t)", (c0 * c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "t/(t*t)", (c0 * c1), v0, v1, result);
 
                      exprtk_debug(("(c0 / v0) * (c1 / v1) --> (covov) (c0 * c1) / (v0 * v1)\n"));
 
@@ -32481,7 +37844,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c0 / c1), v1, v0, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)/t", (c0 / c1), v1, v0, result);
 
                      exprtk_debug(("(c0 / v0) / (c1 / v1) --> (covov) ((c0 / c1) * v1) / v0\n"));
 
@@ -32492,7 +37855,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "t*(t*t)", (c0 / c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "t*(t*t)", (c0 / c1), v0, v1, result);
 
                      exprtk_debug(("(c0 * v0) / (c1 / v1) --> (covov) (c0 / c1) * (v0 * v1)\n"));
 
@@ -32503,7 +37866,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "t/(t*t)", (c0 / c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "t/(t*t)", (c0 / c1), v0, v1, result);
 
                      exprtk_debug(("(c0 / v0) / (c1 * v1) --> (covov) (c0 / c1) / (v0 * v1)\n"));
 
@@ -32545,7 +37908,12 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -32561,10 +37929,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t)";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -32592,10 +37960,6 @@ namespace exprtk
                const details::operator_type o1 = operation;
                const details::operator_type o2 = voc1->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -32608,7 +37972,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result);
 
                      exprtk_debug(("(v0 + c0) + (v1 + c1) --> (covov) (c0 + c1) + v0 + v1\n"));
 
@@ -32619,7 +37983,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result);
 
                      exprtk_debug(("(v0 + c0) - (v1 + c1) --> (covov) (c0 - c1) + v0 - v1\n"));
 
@@ -32630,7 +37994,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)-t", (c1 - c0), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t+t)-t", (c1 - c0), v0, v1, result);
 
                      exprtk_debug(("(v0 - c0) - (v1 - c1) --> (covov) (c1 - c0) + v0 - v1\n"));
 
@@ -32641,7 +38005,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result);
 
                      exprtk_debug(("(v0 * c0) * (v1 * c1) --> (covov) (c0 * c1) * v0 * v1\n"));
 
@@ -32652,7 +38016,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result);
 
                      exprtk_debug(("(v0 * c0) / (v1 * c1) --> (covov) (c0 / c1) * (v0 / v1)\n"));
 
@@ -32663,7 +38027,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)*t", Type(1) / (c0 * c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)*t", Type(1) / (c0 * c1), v0, v1, result);
 
                      exprtk_debug(("(v0 / c0) * (v1 / c1) --> (covov) (1 / (c0 * c1)) * v0 * v1\n"));
 
@@ -32674,7 +38038,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c1 / c0), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)/t", (c1 / c0), v0, v1, result);
 
                      exprtk_debug(("(v0 / c0) / (v1 / c1) --> (covov) ((c1 / c0) * v0) / v1\n"));
 
@@ -32685,7 +38049,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "t*(t/t)", (c0 * c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "t*(t/t)", (c0 * c1), v0, v1, result);
 
                      exprtk_debug(("(v0 * c0) / (v1 / c1) --> (covov) (c0 * c1) * (v0 / v1)\n"));
 
@@ -32696,7 +38060,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "t*(t/t)", Type(1) / (c0 * c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "t*(t/t)", Type(1) / (c0 * c1), v0, v1, result);
 
                      exprtk_debug(("(v0 / c0) / (v1 * c1) --> (covov) (1 / (c0 * c1)) * v0 / v1\n"));
 
@@ -32707,7 +38071,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<vtype,ctype,vtype,ctype>(expr_gen, "(t*t)*(t+t)", v0, T(1) / c0, v1, c1, result);
+                           template compile<vtype, ctype, vtype, ctype>(expr_gen, "(t*t)*(t+t)", v0, T(1) / c0, v1, c1, result);
 
                      exprtk_debug(("(v0 / c0) * (v1 + c1) --> (vocovoc) (v0 * (1 / c0)) * (v1 + c1)\n"));
 
@@ -32718,7 +38082,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf4ext_expression::
-                           template compile<vtype,ctype,vtype,ctype>(expr_gen, "(t*t)*(t-t)", v0, T(1) / c0, v1, c1, result);
+                           template compile<vtype, ctype, vtype, ctype>(expr_gen, "(t*t)*(t-t)", v0, T(1) / c0, v1, c1, result);
 
                      exprtk_debug(("(v0 / c0) * (v1 - c1) --> (vocovoc) (v0 * (1 / c0)) * (v1 - c1)\n"));
 
@@ -32746,7 +38110,7 @@ namespace exprtk
 
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, specfunc, c0, v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, specfunc, c0, v0, v1, result);
 
                      exprtk_debug(("(v0 * c) +/- (v1 * c) --> (covov) c * (v0 +/- v1)\n"));
 
@@ -32774,7 +38138,7 @@ namespace exprtk
 
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<vtype,vtype,ctype>(expr_gen, specfunc, v0, v1, c0, result);
+                           template compile<vtype, vtype, ctype>(expr_gen, specfunc, v0, v1, c0, result);
 
                      exprtk_debug(("(v0 / c) +/- (v1 / c) --> (vovoc) (v0 +/- v1) / c\n"));
 
@@ -32788,7 +38152,12 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -32804,10 +38173,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t)";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -32835,10 +38204,6 @@ namespace exprtk
                const details::operator_type o1 = operation;
                const details::operator_type o2 = voc->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -32851,7 +38216,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result);
 
                      exprtk_debug(("(c0 + v0) + (v1 + c1) --> (covov) (c0 + c1) + v0 + v1\n"));
 
@@ -32862,7 +38227,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result);
 
                      exprtk_debug(("(c0 + v0) - (v1 + c1) --> (covov) (c0 - c1) + v0 - v1\n"));
 
@@ -32873,7 +38238,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "t-(t+t)", (c0 + c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "t-(t+t)", (c0 + c1), v0, v1, result);
 
                      exprtk_debug(("(c0 - v0) - (v1 - c1) --> (covov) (c0 + c1) - v0 - v1\n"));
 
@@ -32884,7 +38249,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result);
 
                      exprtk_debug(("(c0 * v0) * (v1 * c1) --> (covov) (c0 * c1) * v0 * v1\n"));
 
@@ -32895,7 +38260,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result);
 
                      exprtk_debug(("(c0 * v0) / (v1 * c1) --> (covov) (c0 / c1) * (v0 / v1)\n"));
 
@@ -32906,7 +38271,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "t*(t/t)", (c0 / c1), v1, v0, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "t*(t/t)", (c0 / c1), v1, v0, result);
 
                      exprtk_debug(("(c0 / v0) * (v1 / c1) --> (covov) (c0 / c1) * (v1 / v0)\n"));
 
@@ -32917,7 +38282,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "t/(t*t)", (c0 * c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "t/(t*t)", (c0 * c1), v0, v1, result);
 
                      exprtk_debug(("(c0 / v0) / (v1 / c1) --> (covov) (c0 * c1) / (v0 * v1)\n"));
 
@@ -32928,7 +38293,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c0 * c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)/t", (c0 * c1), v0, v1, result);
 
                      exprtk_debug(("(c0 * v0) / (v1 / c1) --> (covov) (c0 * c1) * (v0 / v1)\n"));
 
@@ -32939,7 +38304,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "t/(t*t)", (c0 / c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "t/(t*t)", (c0 / c1), v0, v1, result);
 
                      exprtk_debug(("(c0 / v0) / (v1 * c1) --> (covov) (c0 / c1) / (v0 * v1)\n"));
 
@@ -32967,7 +38332,7 @@ namespace exprtk
 
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen,specfunc, c0, v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, specfunc, c0, v0, v1, result);
 
                      exprtk_debug(("(c * v0) +/- (v1 * c) --> (covov) c * (v0 +/- v1)\n"));
 
@@ -32981,7 +38346,12 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -32997,10 +38367,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t)";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -33028,10 +38398,6 @@ namespace exprtk
                const details::operator_type o1 = operation;
                const details::operator_type o2 = cov->operation();
 
-               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
-               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
-
                details::free_node(*(expr_gen.node_allocator_),branch[0]);
                details::free_node(*(expr_gen.node_allocator_),branch[1]);
 
@@ -33044,7 +38410,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result);
 
                      exprtk_debug(("(v0 + c0) + (c1 + v1) --> (covov) (c0 + c1) + v0 + v1\n"));
 
@@ -33055,7 +38421,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result);
 
                      exprtk_debug(("(v0 + c0) - (c1 + v1) --> (covov) (c0 - c1) + v0 - v1\n"));
 
@@ -33066,7 +38432,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<vtype,vtype,ctype>(expr_gen, "(t+t)-t", v0, v1, (c1 + c0), result);
+                           template compile<vtype, vtype, ctype>(expr_gen, "(t+t)-t", v0, v1, (c1 + c0), result);
 
                      exprtk_debug(("(v0 - c0) - (c1 - v1) --> (vovoc) v0 + v1 - (c1 + c0)\n"));
 
@@ -33077,7 +38443,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result);
 
                      exprtk_debug(("(v0 * c0) * (c1 * v1) --> (covov) (c0 * c1) * v0 * v1\n"));
 
@@ -33088,7 +38454,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result);
 
                      exprtk_debug(("(v0 * c0) / (c1 * v1) --> (covov) (c0 / c1) * (v0 * v1)\n"));
 
@@ -33099,7 +38465,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c1 / c0), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)/t", (c1 / c0), v0, v1, result);
 
                      exprtk_debug(("(v0 / c0) * (c1 / v1) --> (covov) (c1 / c0) * (v0 / v1)\n"));
 
@@ -33110,7 +38476,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)*t", (c0 / c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)*t", (c0 / c1), v0, v1, result);
 
                      exprtk_debug(("(v0 * c0) / (c1 / v1) --> (covov) (c0 / c1) * (v0 * v1)\n"));
 
@@ -33121,7 +38487,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", Type(1) / (c0 * c1), v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)/t", Type(1) / (c0 * c1), v0, v1, result);
 
                      exprtk_debug(("(v0 / c0) / (c1 * v1) --> (covov) (1 / (c0 * c1)) * (v0 / v1)\n"));
 
@@ -33132,7 +38498,7 @@ namespace exprtk
                   {
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<vtype,vtype,ctype>(expr_gen, "(t*t)*t", v0, v1, Type(1) / (c0 * c1), result);
+                           template compile<vtype, vtype, ctype>(expr_gen, "(t*t)*t", v0, v1, Type(1) / (c0 * c1), result);
 
                      exprtk_debug(("(v0 / c0) / (c1 / v1) --> (vovoc) (v0 * v1) * (1 / (c0 * c1))\n"));
 
@@ -33159,7 +38525,7 @@ namespace exprtk
 
                      const bool synthesis_result =
                         synthesize_sf3ext_expression::
-                           template compile<ctype,vtype,vtype>(expr_gen, specfunc, c0, v0, v1, result);
+                           template compile<ctype, vtype, vtype>(expr_gen, specfunc, c0, v0, v1, result);
 
                      exprtk_debug(("(v0 * c) +/- (c * v1) --> (covov) c * (v0 +/- v1)\n"));
 
@@ -33173,7 +38539,12 @@ namespace exprtk
 
                if (synthesis_result)
                   return result;
-               else if (!expr_gen.valid_operator(o0,f0))
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
                else if (!expr_gen.valid_operator(o1,f1))
                   return error_node();
@@ -33189,10 +38560,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "t)" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t)";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "t)" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -33229,14 +38600,18 @@ namespace exprtk
 
                expression_node_ptr result = error_node();
 
-               if (synthesize_sf4ext_expression::template compile<T0,T1,T2,T3>(expr_gen,id(expr_gen,o0,o1,o2),v0,v1,v2,v3,result))
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen,id(expr_gen, o0, o1, o2), v0, v1, v2, v3, result);
+
+               if (synthesis_result)
                   return result;
                else if (!expr_gen.valid_operator(o0,f0))
                   return error_node();
 
                exprtk_debug(("v0 o0 (v1 o1 (v2 o2 v3))\n"));
 
-               return node_type::allocate(*(expr_gen.node_allocator_),v0,v1,v2,v3,f0,f1,f2);
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, v3, f0, f1, f2);
             }
 
             static inline std::string id(expression_generator<Type>& expr_gen,
@@ -33245,10 +38620,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t))";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t))";
             }
          };
 
@@ -33305,10 +38680,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t))";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t))";
             }
          };
 
@@ -33365,10 +38740,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t))";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t))";
             }
          };
 
@@ -33425,10 +38800,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t))";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t))";
             }
          };
 
@@ -33486,10 +38861,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t))";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t))";
             }
          };
 
@@ -33547,10 +38922,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t))";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t))";
             }
          };
 
@@ -33607,10 +38982,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t))";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t))";
             }
          };
 
@@ -33667,10 +39042,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t))";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t))";
             }
          };
 
@@ -33727,10 +39102,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"  << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "(t" << expr_gen.to_str(o2)
-                         << "t))";
+                  << "t"  << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "(t" << expr_gen.to_str(o2)
+                  << "t))";
             }
          };
 
@@ -33787,10 +39162,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"   << expr_gen.to_str(o0)
-                         << "((t" << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t)";
+                  << "t"   << expr_gen.to_str(o0)
+                  << "((t" << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -33847,10 +39222,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"   << expr_gen.to_str(o0)
-                         << "((t" << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t)";
+                  << "t"   << expr_gen.to_str(o0)
+                  << "((t" << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -33907,10 +39282,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"   << expr_gen.to_str(o0)
-                         << "((t" << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t)";
+                  << "t"   << expr_gen.to_str(o0)
+                  << "((t" << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -33967,10 +39342,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"   << expr_gen.to_str(o0)
-                         << "((t" << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t)";
+                  << "t"   << expr_gen.to_str(o0)
+                  << "((t" << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -34028,10 +39403,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"   << expr_gen.to_str(o0)
-                         << "((t" << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t)";
+                  << "t"   << expr_gen.to_str(o0)
+                  << "((t" << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t)";
             }
         };
 
@@ -34089,10 +39464,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"   << expr_gen.to_str(o0)
-                         << "((t" << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t)";
+                  << "t"   << expr_gen.to_str(o0)
+                  << "((t" << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -34149,10 +39524,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"   << expr_gen.to_str(o0)
-                         << "((t" << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t)";
+                  << "t"   << expr_gen.to_str(o0)
+                  << "((t" << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
@@ -34210,17 +39585,19 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "t"   << expr_gen.to_str(o0)
-                         << "((t" << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t)";
+                  << "t"   << expr_gen.to_str(o0)
+                  << "((t" << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t)";
             }
          };
 
          struct synthesize_vococov_expression2
          {
             typedef typename vococov_t::type2 node_type;
-            static inline expression_node_ptr process(expression_generator<Type>&, const details::operator_type&, expression_node_ptr (&)[2])
+            static inline expression_node_ptr process(expression_generator<Type>&,
+                                                      const details::operator_type&,
+                                                      expression_node_ptr (&)[2])
             {
                // v0 o0 ((c0 o1 c1) o2 v1) - Not possible
                exprtk_debug(("v0 o0 ((c0 o1 c1) o2 v1) - Not possible\n"));
@@ -34228,7 +39605,9 @@ namespace exprtk
             }
 
             static inline std::string id(expression_generator<Type>&,
-                                         const details::operator_type, const details::operator_type, const details::operator_type)
+                                         const details::operator_type,
+                                         const details::operator_type,
+                                         const details::operator_type)
             {
                return "INVALID";
             }
@@ -34287,10 +39666,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "((t" << expr_gen.to_str(o0)
-                         << "t)"  << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t";
+                  << "((t" << expr_gen.to_str(o0)
+                  << "t)"  << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -34348,10 +39727,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "((t" << expr_gen.to_str(o0)
-                         << "t)"  << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t";
+                  << "((t" << expr_gen.to_str(o0)
+                  << "t)"  << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -34408,10 +39787,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "((t" << expr_gen.to_str(o0)
-                         << "t)"  << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t";
+                  << "((t" << expr_gen.to_str(o0)
+                  << "t)"  << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -34468,10 +39847,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "((t" << expr_gen.to_str(o0)
-                         << "t)"  << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t";
+                  << "((t" << expr_gen.to_str(o0)
+                  << "t)"  << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -34528,10 +39907,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "((t" << expr_gen.to_str(o0)
-                         << "t)"  << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t";
+                  << "((t" << expr_gen.to_str(o0)
+                  << "t)"  << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -34588,10 +39967,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "((t" << expr_gen.to_str(o0)
-                         << "t)"  << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t";
+                  << "((t" << expr_gen.to_str(o0)
+                  << "t)"  << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -34649,10 +40028,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "((t" << expr_gen.to_str(o0)
-                         << "t)"  << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t";
+                  << "((t" << expr_gen.to_str(o0)
+                  << "t)"  << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -34710,10 +40089,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "((t" << expr_gen.to_str(o0)
-                         << "t)"  << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t";
+                  << "((t" << expr_gen.to_str(o0)
+                  << "t)"  << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -34770,10 +40149,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "((t" << expr_gen.to_str(o0)
-                         << "t)"  << expr_gen.to_str(o1)
-                         << "t)"  << expr_gen.to_str(o2)
-                         << "t";
+                  << "((t" << expr_gen.to_str(o0)
+                  << "t)"  << expr_gen.to_str(o1)
+                  << "t)"  << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -34830,10 +40209,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)" << expr_gen.to_str(o2)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)" << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -34891,10 +40270,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)" << expr_gen.to_str(o2)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)" << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -34951,10 +40330,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)" << expr_gen.to_str(o2)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)" << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -35010,10 +40389,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)" << expr_gen.to_str(o2)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)" << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -35070,10 +40449,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)" << expr_gen.to_str(o2)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)" << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -35130,10 +40509,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)" << expr_gen.to_str(o2)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)" << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -35191,10 +40570,10 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)" << expr_gen.to_str(o2)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)" << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
@@ -35252,17 +40631,19 @@ namespace exprtk
                                          const details::operator_type o2)
             {
                return details::build_string()
-                         << "(t" << expr_gen.to_str(o0)
-                         << "(t" << expr_gen.to_str(o1)
-                         << "t)" << expr_gen.to_str(o2)
-                         << "t";
+                  << "(t" << expr_gen.to_str(o0)
+                  << "(t" << expr_gen.to_str(o1)
+                  << "t)" << expr_gen.to_str(o2)
+                  << "t";
             }
          };
 
          struct synthesize_vococov_expression4
          {
             typedef typename vococov_t::type4 node_type;
-            static inline expression_node_ptr process(expression_generator<Type>&, const details::operator_type&, expression_node_ptr (&)[2])
+            static inline expression_node_ptr process(expression_generator<Type>&,
+                                                      const details::operator_type&,
+                                                      expression_node_ptr (&)[2])
             {
                // ((v0 o0 (c0 o1 c1)) o2 v1) - Not possible
                exprtk_debug(("((v0 o0 (c0 o1 c1)) o2 v1) - Not possible\n"));
@@ -35270,7 +40651,9 @@ namespace exprtk
             }
 
             static inline std::string id(expression_generator<Type>&,
-                                         const details::operator_type, const details::operator_type, const details::operator_type)
+                                         const details::operator_type,
+                                         const details::operator_type,
+                                         const details::operator_type)
             {
                return "INVALID";
             }
@@ -35353,16 +40736,16 @@ namespace exprtk
 
          #ifndef exprtk_disable_string_capabilities
 
-         #define string_opr_switch_statements          \
-         case_stmt(details::  e_lt ,details::   lt_op) \
-         case_stmt(details:: e_lte ,details::  lte_op) \
-         case_stmt(details::  e_gt ,details::   gt_op) \
-         case_stmt(details:: e_gte ,details::  gte_op) \
-         case_stmt(details::  e_eq ,details::   eq_op) \
-         case_stmt(details::  e_ne ,details::   ne_op) \
-         case_stmt(details::e_in   ,details::   in_op) \
-         case_stmt(details::e_like ,details:: like_op) \
-         case_stmt(details::e_ilike,details::ilike_op) \
+         #define string_opr_switch_statements            \
+         case_stmt(details::e_lt    , details::lt_op   ) \
+         case_stmt(details::e_lte   , details::lte_op  ) \
+         case_stmt(details::e_gt    , details::gt_op   ) \
+         case_stmt(details::e_gte   , details::gte_op  ) \
+         case_stmt(details::e_eq    , details::eq_op   ) \
+         case_stmt(details::e_ne    , details::ne_op   ) \
+         case_stmt(details::e_in    , details::in_op   ) \
+         case_stmt(details::e_like  , details::like_op ) \
+         case_stmt(details::e_ilike , details::ilike_op) \
 
          template <typename T0, typename T1>
          inline expression_node_ptr synthesize_str_xrox_expression_impl(const details::operator_type& opr,
@@ -35371,7 +40754,7 @@ namespace exprtk
          {
             switch (opr)
             {
-               #define case_stmt(op0,op1)                                                                       \
+               #define case_stmt(op0, op1)                                                                      \
                case op0 : return node_allocator_->                                                              \
                              allocate_ttt<typename details::str_xrox_node<Type,T0,T1,range_t,op1<Type> >,T0,T1> \
                                 (s0, s1, rp0);                                                                  \
@@ -35389,7 +40772,7 @@ namespace exprtk
          {
             switch (opr)
             {
-               #define case_stmt(op0,op1)                                                                       \
+               #define case_stmt(op0, op1)                                                                      \
                case op0 : return node_allocator_->                                                              \
                              allocate_ttt<typename details::str_xoxr_node<Type,T0,T1,range_t,op1<Type> >,T0,T1> \
                                 (s0, s1, rp1);                                                                  \
@@ -35407,7 +40790,7 @@ namespace exprtk
          {
             switch (opr)
             {
-               #define case_stmt(op0,op1)                                                                         \
+               #define case_stmt(op0, op1)                                                                        \
                case op0 : return node_allocator_->                                                                \
                              allocate_tttt<typename details::str_xroxr_node<Type,T0,T1,range_t,op1<Type> >,T0,T1> \
                                 (s0, s1, rp0, rp1);                                                               \
@@ -35423,7 +40806,7 @@ namespace exprtk
          {
             switch (opr)
             {
-               #define case_stmt(op0,op1)                                                                  \
+               #define case_stmt(op0, op1)                                                                 \
                case op0 : return node_allocator_->                                                         \
                              allocate_tt<typename details::sos_node<Type,T0,T1,op1<Type> >,T0,T1>(s0, s1); \
 
@@ -35449,7 +40832,7 @@ namespace exprtk
 
             static_cast<details::string_range_node<Type>*>(branch[0])->range_ref().clear();
 
-            free_node(*node_allocator_,branch[0]);
+            details::free_node(*node_allocator_,branch[0]);
 
             return synthesize_str_xrox_expression_impl<std::string&,std::string&>(opr, s0, s1, rp0);
          }
@@ -35462,7 +40845,7 @@ namespace exprtk
 
             static_cast<details::string_range_node<Type>*>(branch[1])->range_ref().clear();
 
-            free_node(*node_allocator_,branch[1]);
+            details::free_node(*node_allocator_,branch[1]);
 
             return synthesize_str_xoxr_expression_impl<std::string&,std::string&>(opr, s0, s1, rp1);
          }
@@ -35475,7 +40858,7 @@ namespace exprtk
 
             static_cast<details::const_string_range_node<Type>*>(branch[1])->range_ref().clear();
 
-            free_node(*node_allocator_,branch[1]);
+            details::free_node(*node_allocator_,branch[1]);
 
             return synthesize_str_xoxr_expression_impl<std::string&, const std::string>(opr, s0, s1, rp1);
          }
@@ -35509,7 +40892,7 @@ namespace exprtk
          inline expression_node_ptr synthesize_csos_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
          {
             std::string  s0 = static_cast<details::string_literal_node<Type>*>(branch[0])->str();
-            std::string& s1 = static_cast<     details::stringvar_node<Type>*>(branch[1])->ref();
+            std::string& s1 = static_cast<details::stringvar_node<Type>*     >(branch[1])->ref();
 
             details::free_node(*node_allocator_,branch[0]);
 
@@ -35518,9 +40901,9 @@ namespace exprtk
 
          inline expression_node_ptr synthesize_csosr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
          {
-            std::string   s0 = static_cast<details::string_literal_node<Type>*>(branch[0])->str  ();
-            std::string&  s1 = static_cast<details::string_range_node<Type>*>  (branch[1])->ref  ();
-            range_t      rp1 = static_cast<details::string_range_node<Type>*>  (branch[1])->range();
+            std::string  s0  = static_cast<details::string_literal_node<Type>*>(branch[0])->str  ();
+            std::string& s1  = static_cast<details::string_range_node<Type>*  >(branch[1])->ref  ();
+            range_t      rp1 = static_cast<details::string_range_node<Type>*  >(branch[1])->range();
 
             static_cast<details::string_range_node<Type>*>(branch[1])->range_ref().clear();
 
@@ -35532,9 +40915,9 @@ namespace exprtk
 
          inline expression_node_ptr synthesize_srocs_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
          {
-            std::string&  s0 = static_cast<details::string_range_node<Type>*>  (branch[0])->ref  ();
+            std::string&  s0 = static_cast<details::string_range_node<Type>*  >(branch[0])->ref  ();
             std::string   s1 = static_cast<details::string_literal_node<Type>*>(branch[1])->str  ();
-            range_t      rp0 = static_cast<details::string_range_node<Type>*>  (branch[0])->range();
+            range_t      rp0 = static_cast<details::string_range_node<Type>*  >(branch[0])->range();
 
             static_cast<details::string_range_node<Type>*>(branch[0])->range_ref().clear();
 
@@ -35546,9 +40929,9 @@ namespace exprtk
 
          inline expression_node_ptr synthesize_srocsr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
          {
-            std::string&  s0 = static_cast<details::string_range_node<Type>*>      (branch[0])->ref  ();
+            std::string&  s0 = static_cast<details::string_range_node<Type>*      >(branch[0])->ref  ();
             std::string   s1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->str  ();
-            range_t      rp0 = static_cast<details::string_range_node<Type>*>      (branch[0])->range();
+            range_t      rp0 = static_cast<details::string_range_node<Type>*      >(branch[0])->range();
             range_t      rp1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->range();
 
             static_cast<details::string_range_node<Type>*>      (branch[0])->range_ref().clear();
@@ -35593,14 +40976,14 @@ namespace exprtk
 
          inline expression_node_ptr synthesize_csocsr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
          {
-            const std::string s0 = static_cast<details::string_literal_node<Type>*>    (branch[0])->str  ();
+            const std::string s0 = static_cast<details::string_literal_node<Type>*    >(branch[0])->str  ();
                   std::string s1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->str  ();
             range_t          rp1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->range();
 
             static_cast<details::const_string_range_node<Type>*>(branch[1])->range_ref().clear();
 
-            free_node(*node_allocator_,branch[0]);
-            free_node(*node_allocator_,branch[1]);
+            details::free_node(*node_allocator_,branch[0]);
+            details::free_node(*node_allocator_,branch[1]);
 
             return synthesize_str_xoxr_expression_impl<const std::string, const std::string>(opr, s0, s1, rp1);
          }
@@ -35608,12 +40991,12 @@ namespace exprtk
          inline expression_node_ptr synthesize_csros_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
          {
             std::string   s0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->str  ();
-            std::string&  s1 = static_cast<details::stringvar_node<Type>*>         (branch[1])->ref  ();
+            std::string&  s1 = static_cast<details::stringvar_node<Type>*         >(branch[1])->ref  ();
             range_t      rp0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->range();
 
             static_cast<details::const_string_range_node<Type>*>(branch[0])->range_ref().clear();
 
-            free_node(*node_allocator_,branch[0]);
+            details::free_node(*node_allocator_,branch[0]);
 
             return synthesize_str_xrox_expression_impl<const std::string,std::string&>(opr, s0, s1, rp0);
          }
@@ -35621,15 +41004,15 @@ namespace exprtk
          inline expression_node_ptr synthesize_csrosr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
          {
             const std::string  s0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->str  ();
-                  std::string& s1 = static_cast<details::string_range_node<Type>*>      (branch[1])->ref  ();
+                  std::string& s1 = static_cast<details::string_range_node<Type>*      >(branch[1])->ref  ();
             const range_t     rp0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->range();
-            const range_t     rp1 = static_cast<details::string_range_node<Type>*>      (branch[1])->range();
+            const range_t     rp1 = static_cast<details::string_range_node<Type>*      >(branch[1])->range();
 
             static_cast<details::const_string_range_node<Type>*>(branch[0])->range_ref().clear();
             static_cast<details::string_range_node<Type>*>      (branch[1])->range_ref().clear();
 
-            free_node(*node_allocator_,branch[0]);
-            free_node(*node_allocator_,branch[1]);
+            details::free_node(*node_allocator_,branch[0]);
+            details::free_node(*node_allocator_,branch[1]);
 
             return synthesize_str_xroxr_expression_impl<const std::string,std::string&>(opr, s0, s1, rp0, rp1);
          }
@@ -35637,7 +41020,7 @@ namespace exprtk
          inline expression_node_ptr synthesize_csrocs_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
          {
             const std::string s0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->str  ();
-            const std::string s1 = static_cast<details::string_literal_node<Type>*>    (branch[1])->str  ();
+            const std::string s1 = static_cast<details::string_literal_node<Type>*    >(branch[1])->str  ();
             const range_t    rp0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->range();
 
             static_cast<details::const_string_range_node<Type>*>(branch[0])->range_ref().clear();
@@ -35666,7 +41049,7 @@ namespace exprtk
          {
             switch (opr)
             {
-               #define case_stmt(op0,op1)                                                       \
+               #define case_stmt(op0, op1)                                                      \
                case op0 : return node_allocator_->                                              \
                              allocate_ttt<typename details::str_sogens_node<Type,op1<Type> > >  \
                                 (opr, branch[0], branch[1]);                                    \
@@ -35676,6 +41059,8 @@ namespace exprtk
                default : return error_node();
             }
          }
+
+         #undef string_opr_switch_statements
          #endif
 
          #ifndef exprtk_disable_string_capabilities
@@ -35728,28 +41113,28 @@ namespace exprtk
             }
             else if (b0_is_s)
             {
-                    if (b1_is_s  ) return synthesize_sos_expression   (opr,branch);
+               if      (b1_is_s  ) return synthesize_sos_expression   (opr,branch);
                else if (b1_is_cs ) return synthesize_socs_expression  (opr,branch);
                else if (b1_is_sr ) return synthesize_sosr_expression  (opr,branch);
                else if (b1_is_csr) return synthesize_socsr_expression (opr,branch);
             }
             else if (b0_is_cs)
             {
-                    if (b1_is_s  ) return synthesize_csos_expression  (opr,branch);
+               if      (b1_is_s  ) return synthesize_csos_expression  (opr,branch);
                else if (b1_is_cs ) return synthesize_csocs_expression (opr,branch);
                else if (b1_is_sr ) return synthesize_csosr_expression (opr,branch);
                else if (b1_is_csr) return synthesize_csocsr_expression(opr,branch);
             }
             else if (b0_is_sr)
             {
-                    if (b1_is_s  ) return synthesize_sros_expression  (opr,branch);
+               if      (b1_is_s  ) return synthesize_sros_expression  (opr,branch);
                else if (b1_is_sr ) return synthesize_srosr_expression (opr,branch);
                else if (b1_is_cs ) return synthesize_srocs_expression (opr,branch);
                else if (b1_is_csr) return synthesize_srocsr_expression(opr,branch);
             }
             else if (b0_is_csr)
             {
-                    if (b1_is_s  ) return synthesize_csros_expression  (opr,branch);
+               if      (b1_is_s  ) return synthesize_csros_expression  (opr,branch);
                else if (b1_is_sr ) return synthesize_csrosr_expression (opr,branch);
                else if (b1_is_cs ) return synthesize_csrocs_expression (opr,branch);
                else if (b1_is_csr) return synthesize_csrocsr_expression(opr,branch);
@@ -35813,7 +41198,7 @@ namespace exprtk
                     )
             {
                std::string  s0 = static_cast<details::string_literal_node<Type>*>(branch[0])->str();
-               std::string& s1 = static_cast<     details::stringvar_node<Type>*>(branch[1])->ref();
+               std::string& s1 = static_cast<details::stringvar_node<Type>*     >(branch[1])->ref();
                std::string  s2 = static_cast<details::string_literal_node<Type>*>(branch[2])->str();
 
                typedef typename details::sosos_node<Type, std::string, std::string&, std::string, details::inrange_op<Type> > inrange_t;
@@ -35829,9 +41214,9 @@ namespace exprtk
                             details::is_string_node(branch[2])
                     )
             {
-               std::string&  s0 = static_cast<     details::stringvar_node<Type>*>(branch[0])->ref();
+               std::string&  s0 = static_cast<details::stringvar_node<Type>*     >(branch[0])->ref();
                std::string   s1 = static_cast<details::string_literal_node<Type>*>(branch[1])->str();
-               std::string&  s2 = static_cast<     details::stringvar_node<Type>*>(branch[2])->ref();
+               std::string&  s2 = static_cast<details::stringvar_node<Type>*     >(branch[2])->ref();
 
                typedef typename details::sosos_node<Type, std::string&, std::string, std::string&, details::inrange_op<Type> > inrange_t;
 
@@ -35845,8 +41230,8 @@ namespace exprtk
                       details::is_const_string_node(branch[2])
                     )
             {
-               std::string& s0 = static_cast<     details::stringvar_node<Type>*>(branch[0])->ref();
-               std::string& s1 = static_cast<     details::stringvar_node<Type>*>(branch[1])->ref();
+               std::string& s0 = static_cast<details::stringvar_node<Type>*     >(branch[0])->ref();
+               std::string& s1 = static_cast<details::stringvar_node<Type>*     >(branch[1])->ref();
                std::string  s2 = static_cast<details::string_literal_node<Type>*>(branch[2])->str();
 
                typedef typename details::sosos_node<Type, std::string&, std::string&, std::string, details::inrange_op<Type> > inrange_t;
@@ -35862,8 +41247,8 @@ namespace exprtk
                     )
             {
                std::string  s0 = static_cast<details::string_literal_node<Type>*>(branch[0])->str();
-               std::string& s1 = static_cast<     details::stringvar_node<Type>*>(branch[1])->ref();
-               std::string& s2 = static_cast<     details::stringvar_node<Type>*>(branch[2])->ref();
+               std::string& s1 = static_cast<details::stringvar_node<Type>*     >(branch[1])->ref();
+               std::string& s2 = static_cast<details::stringvar_node<Type>*     >(branch[2])->ref();
 
                typedef typename details::sosos_node<Type, std::string, std::string&, std::string&, details::inrange_op<Type> > inrange_t;
 
@@ -36009,11 +41394,22 @@ namespace exprtk
 
                   return node_allocator_->allocate<literal_node_t>(v);
                }
-               else
+
+               if (expression_point && expression_point->valid())
+               {
                   return expression_point;
+               }
+
+               parser_->set_error(parser_error::make_error(
+                  parser_error::e_parser,
+                  token_t(),
+                  "ERR276 - Failed to synthesize node: NodeType",
+                  exprtk_error_location));
+
+               details::free_node(*node_allocator_, expression_point);
             }
-            else
-               return error_node();
+
+            return error_node();
          }
 
          template <typename NodeType, std::size_t N>
@@ -36064,7 +41460,7 @@ namespace exprtk
          sf3_map_t*               sf3_map_;
          sf4_map_t*               sf4_map_;
          parser_t*                parser_;
-      };
+      }; // class expression_generator
 
       inline void set_error(const parser_error::type& error_type)
       {
@@ -36093,8 +41489,11 @@ namespace exprtk
          {
             scope_element& se = sem_.get_element(i);
 
+            exprtk_debug(("register_local_vars() - se[%s]\n", se.name.c_str()));
+
             if (
                  (scope_element::e_variable == se.type) ||
+                 (scope_element::e_literal  == se.type) ||
                  (scope_element::e_vecelem  == se.type)
                )
             {
@@ -36154,48 +41553,48 @@ namespace exprtk
 
       inline void load_unary_operations_map(unary_op_map_t& m)
       {
-         #define register_unary_op(Op,UnaryFunctor)             \
+         #define register_unary_op(Op, UnaryFunctor)            \
          m.insert(std::make_pair(Op,UnaryFunctor<T>::process)); \
 
-         register_unary_op(details::  e_abs, details::  abs_op)
-         register_unary_op(details:: e_acos, details:: acos_op)
-         register_unary_op(details::e_acosh, details::acosh_op)
-         register_unary_op(details:: e_asin, details:: asin_op)
-         register_unary_op(details::e_asinh, details::asinh_op)
-         register_unary_op(details::e_atanh, details::atanh_op)
-         register_unary_op(details:: e_ceil, details:: ceil_op)
-         register_unary_op(details::  e_cos, details::  cos_op)
-         register_unary_op(details:: e_cosh, details:: cosh_op)
-         register_unary_op(details::  e_exp, details::  exp_op)
-         register_unary_op(details::e_expm1, details::expm1_op)
-         register_unary_op(details::e_floor, details::floor_op)
-         register_unary_op(details::  e_log, details::  log_op)
-         register_unary_op(details::e_log10, details::log10_op)
-         register_unary_op(details:: e_log2, details:: log2_op)
-         register_unary_op(details::e_log1p, details::log1p_op)
-         register_unary_op(details::  e_neg, details::  neg_op)
-         register_unary_op(details::  e_pos, details::  pos_op)
-         register_unary_op(details::e_round, details::round_op)
-         register_unary_op(details::  e_sin, details::  sin_op)
-         register_unary_op(details:: e_sinc, details:: sinc_op)
-         register_unary_op(details:: e_sinh, details:: sinh_op)
-         register_unary_op(details:: e_sqrt, details:: sqrt_op)
-         register_unary_op(details::  e_tan, details::  tan_op)
-         register_unary_op(details:: e_tanh, details:: tanh_op)
-         register_unary_op(details::  e_cot, details::  cot_op)
-         register_unary_op(details::  e_sec, details::  sec_op)
-         register_unary_op(details::  e_csc, details::  csc_op)
-         register_unary_op(details::  e_r2d, details::  r2d_op)
-         register_unary_op(details::  e_d2r, details::  d2r_op)
-         register_unary_op(details::  e_d2g, details::  d2g_op)
-         register_unary_op(details::  e_g2d, details::  g2d_op)
-         register_unary_op(details:: e_notl, details:: notl_op)
-         register_unary_op(details::  e_sgn, details::  sgn_op)
-         register_unary_op(details::  e_erf, details::  erf_op)
-         register_unary_op(details:: e_erfc, details:: erfc_op)
-         register_unary_op(details:: e_ncdf, details:: ncdf_op)
-         register_unary_op(details:: e_frac, details:: frac_op)
-         register_unary_op(details::e_trunc, details::trunc_op)
+         register_unary_op(details::e_abs   , details::abs_op  )
+         register_unary_op(details::e_acos  , details::acos_op )
+         register_unary_op(details::e_acosh , details::acosh_op)
+         register_unary_op(details::e_asin  , details::asin_op )
+         register_unary_op(details::e_asinh , details::asinh_op)
+         register_unary_op(details::e_atanh , details::atanh_op)
+         register_unary_op(details::e_ceil  , details::ceil_op )
+         register_unary_op(details::e_cos   , details::cos_op  )
+         register_unary_op(details::e_cosh  , details::cosh_op )
+         register_unary_op(details::e_exp   , details::exp_op  )
+         register_unary_op(details::e_expm1 , details::expm1_op)
+         register_unary_op(details::e_floor , details::floor_op)
+         register_unary_op(details::e_log   , details::log_op  )
+         register_unary_op(details::e_log10 , details::log10_op)
+         register_unary_op(details::e_log2  , details::log2_op )
+         register_unary_op(details::e_log1p , details::log1p_op)
+         register_unary_op(details::e_neg   , details::neg_op  )
+         register_unary_op(details::e_pos   , details::pos_op  )
+         register_unary_op(details::e_round , details::round_op)
+         register_unary_op(details::e_sin   , details::sin_op  )
+         register_unary_op(details::e_sinc  , details::sinc_op )
+         register_unary_op(details::e_sinh  , details::sinh_op )
+         register_unary_op(details::e_sqrt  , details::sqrt_op )
+         register_unary_op(details::e_tan   , details::tan_op  )
+         register_unary_op(details::e_tanh  , details::tanh_op )
+         register_unary_op(details::e_cot   , details::cot_op  )
+         register_unary_op(details::e_sec   , details::sec_op  )
+         register_unary_op(details::e_csc   , details::csc_op  )
+         register_unary_op(details::e_r2d   , details::r2d_op  )
+         register_unary_op(details::e_d2r   , details::d2r_op  )
+         register_unary_op(details::e_d2g   , details::d2g_op  )
+         register_unary_op(details::e_g2d   , details::g2d_op  )
+         register_unary_op(details::e_notl  , details::notl_op )
+         register_unary_op(details::e_sgn   , details::sgn_op  )
+         register_unary_op(details::e_erf   , details::erf_op  )
+         register_unary_op(details::e_erfc  , details::erfc_op )
+         register_unary_op(details::e_ncdf  , details::ncdf_op )
+         register_unary_op(details::e_frac  , details::frac_op )
+         register_unary_op(details::e_trunc , details::trunc_op)
          #undef register_unary_op
       }
 
@@ -36203,27 +41602,27 @@ namespace exprtk
       {
          typedef typename binary_op_map_t::value_type value_type;
 
-         #define register_binary_op(Op,BinaryFunctor)        \
+         #define register_binary_op(Op, BinaryFunctor)       \
          m.insert(value_type(Op,BinaryFunctor<T>::process)); \
 
-         register_binary_op(details:: e_add, details:: add_op)
-         register_binary_op(details:: e_sub, details:: sub_op)
-         register_binary_op(details:: e_mul, details:: mul_op)
-         register_binary_op(details:: e_div, details:: div_op)
-         register_binary_op(details:: e_mod, details:: mod_op)
-         register_binary_op(details:: e_pow, details:: pow_op)
-         register_binary_op(details::  e_lt, details::  lt_op)
-         register_binary_op(details:: e_lte, details:: lte_op)
-         register_binary_op(details::  e_gt, details::  gt_op)
-         register_binary_op(details:: e_gte, details:: gte_op)
-         register_binary_op(details::  e_eq, details::  eq_op)
-         register_binary_op(details::  e_ne, details::  ne_op)
-         register_binary_op(details:: e_and, details:: and_op)
-         register_binary_op(details::e_nand, details::nand_op)
-         register_binary_op(details::  e_or, details::  or_op)
-         register_binary_op(details:: e_nor, details:: nor_op)
-         register_binary_op(details:: e_xor, details:: xor_op)
-         register_binary_op(details::e_xnor, details::xnor_op)
+         register_binary_op(details::e_add  , details::add_op )
+         register_binary_op(details::e_sub  , details::sub_op )
+         register_binary_op(details::e_mul  , details::mul_op )
+         register_binary_op(details::e_div  , details::div_op )
+         register_binary_op(details::e_mod  , details::mod_op )
+         register_binary_op(details::e_pow  , details::pow_op )
+         register_binary_op(details::e_lt   , details::lt_op  )
+         register_binary_op(details::e_lte  , details::lte_op )
+         register_binary_op(details::e_gt   , details::gt_op  )
+         register_binary_op(details::e_gte  , details::gte_op )
+         register_binary_op(details::e_eq   , details::eq_op  )
+         register_binary_op(details::e_ne   , details::ne_op  )
+         register_binary_op(details::e_and  , details::and_op )
+         register_binary_op(details::e_nand , details::nand_op)
+         register_binary_op(details::e_or   , details::or_op  )
+         register_binary_op(details::e_nor  , details::nor_op )
+         register_binary_op(details::e_xor  , details::xor_op )
+         register_binary_op(details::e_xnor , details::xnor_op)
          #undef register_binary_op
       }
 
@@ -36231,27 +41630,27 @@ namespace exprtk
       {
          typedef typename inv_binary_op_map_t::value_type value_type;
 
-         #define register_binary_op(Op,BinaryFunctor)        \
+         #define register_binary_op(Op, BinaryFunctor)       \
          m.insert(value_type(BinaryFunctor<T>::process,Op)); \
 
-         register_binary_op(details:: e_add, details:: add_op)
-         register_binary_op(details:: e_sub, details:: sub_op)
-         register_binary_op(details:: e_mul, details:: mul_op)
-         register_binary_op(details:: e_div, details:: div_op)
-         register_binary_op(details:: e_mod, details:: mod_op)
-         register_binary_op(details:: e_pow, details:: pow_op)
-         register_binary_op(details::  e_lt, details::  lt_op)
-         register_binary_op(details:: e_lte, details:: lte_op)
-         register_binary_op(details::  e_gt, details::  gt_op)
-         register_binary_op(details:: e_gte, details:: gte_op)
-         register_binary_op(details::  e_eq, details::  eq_op)
-         register_binary_op(details::  e_ne, details::  ne_op)
-         register_binary_op(details:: e_and, details:: and_op)
-         register_binary_op(details::e_nand, details::nand_op)
-         register_binary_op(details::  e_or, details::  or_op)
-         register_binary_op(details:: e_nor, details:: nor_op)
-         register_binary_op(details:: e_xor, details:: xor_op)
-         register_binary_op(details::e_xnor, details::xnor_op)
+         register_binary_op(details::e_add  , details::add_op )
+         register_binary_op(details::e_sub  , details::sub_op )
+         register_binary_op(details::e_mul  , details::mul_op )
+         register_binary_op(details::e_div  , details::div_op )
+         register_binary_op(details::e_mod  , details::mod_op )
+         register_binary_op(details::e_pow  , details::pow_op )
+         register_binary_op(details::e_lt   , details::lt_op  )
+         register_binary_op(details::e_lte  , details::lte_op )
+         register_binary_op(details::e_gt   , details::gt_op  )
+         register_binary_op(details::e_gte  , details::gte_op )
+         register_binary_op(details::e_eq   , details::eq_op  )
+         register_binary_op(details::e_ne   , details::ne_op  )
+         register_binary_op(details::e_and  , details::and_op )
+         register_binary_op(details::e_nand , details::nand_op)
+         register_binary_op(details::e_or   , details::or_op  )
+         register_binary_op(details::e_nor  , details::nor_op )
+         register_binary_op(details::e_xor  , details::xor_op )
+         register_binary_op(details::e_xnor , details::xnor_op)
          #undef register_binary_op
       }
 
@@ -36344,8 +41743,8 @@ namespace exprtk
 
    private:
 
-      parser(const parser<T>&);
-      parser<T>& operator=(const parser<T>&);
+      parser(const parser<T>&) exprtk_delete;
+      parser<T>& operator=(const parser<T>&) exprtk_delete;
 
       settings_store settings_;
       expression_generator<T> expression_generator_;
@@ -36367,6 +41766,10 @@ namespace exprtk
       sf4_map_t sf4_map_;
       std::string synthesis_error_;
       scope_element_manager sem_;
+      std::vector<state_t> current_state_stack_;
+
+      immutable_memory_map_t immutable_memory_map_;
+      immutable_symtok_map_t immutable_symtok_map_;
 
       lexer::helper::helper_assembly helper_assembly_;
 
@@ -36375,15 +41778,19 @@ namespace exprtk
       lexer::helper::operator_joiner            operator_joiner_3_;
       lexer::helper::symbol_replacer            symbol_replacer_;
       lexer::helper::bracket_checker            bracket_checker_;
-      lexer::helper::numeric_checker            numeric_checker_;
+      lexer::helper::numeric_checker<T>         numeric_checker_;
       lexer::helper::sequence_validator         sequence_validator_;
       lexer::helper::sequence_validator_3tokens sequence_validator_3tkns_;
 
-      loop_runtime_check_ptr loop_runtime_check_;
+      loop_runtime_check_ptr          loop_runtime_check_;
+      vector_access_runtime_check_ptr vector_access_runtime_check_;
+      compilation_check_ptr           compilation_check_ptr_;
+      assert_check_ptr                assert_check_;
+      std::set<std::string>           assert_ids_;
 
       template <typename ParserType>
       friend void details::disable_type_checking(ParserType& p);
-   };
+   }; // class parser
 
    namespace details
    {
@@ -36391,22 +41798,24 @@ namespace exprtk
       struct collector_helper
       {
          typedef exprtk::symbol_table<T> symbol_table_t;
-         typedef exprtk::expression<T>     expression_t;
-         typedef exprtk::parser<T>             parser_t;
+         typedef exprtk::expression<T>   expression_t;
+         typedef exprtk::parser<T>       parser_t;
          typedef typename parser_t::dependent_entity_collector::symbol_t symbol_t;
          typedef typename parser_t::unknown_symbol_resolver usr_t;
 
-         struct resolve_as_vector : public parser_t::unknown_symbol_resolver
+         struct resolve_as_vector : public usr_t
          {
             typedef exprtk::parser<T> parser_t;
 
+            using usr_t::process;
+
             resolve_as_vector()
             : usr_t(usr_t::e_usrmode_extended)
             {}
 
-            virtual bool process(const std::string& unknown_symbol,
-                                 symbol_table_t& symbol_table,
-                                 std::string&)
+            bool process(const std::string& unknown_symbol,
+                         symbol_table_t& symbol_table,
+                         std::string&) exprtk_override
             {
                static T v[1];
                symbol_table.add_vector(unknown_symbol,v);
@@ -36618,7 +42027,9 @@ namespace exprtk
       const symbol_table<T>& sym_table = e.get_symbol_table();
 
       if (!sym_table.valid())
+      {
          return std::numeric_limits<T>::quiet_NaN();
+      }
 
       details::variable_node<T>* var = sym_table.get_variable(variable_name);
 
@@ -36631,8 +42042,8 @@ namespace exprtk
 
          return result;
       }
-      else
-         return std::numeric_limits<T>::quiet_NaN();
+
+      return std::numeric_limits<T>::quiet_NaN();
    }
 
    template <typename T>
@@ -36645,9 +42056,9 @@ namespace exprtk
 
       x = x_init + _2h;
       const T y0 = e.value();
-      x = x_init +   h;
+      x = x_init + h;
       const T y1 = e.value();
-      x = x_init -   h;
+      x = x_init - h;
       const T y2 = e.value();
       x = x_init - _2h;
       const T y3 = e.value();
@@ -36667,9 +42078,9 @@ namespace exprtk
       const T y = e.value();
       x = x_init + _2h;
       const T y0 = e.value();
-      x = x_init +   h;
+      x = x_init + h;
       const T y1 = e.value();
-      x = x_init -   h;
+      x = x_init - h;
       const T y2 = e.value();
       x = x_init - _2h;
       const T y3 = e.value();
@@ -36688,9 +42099,9 @@ namespace exprtk
 
       x = x_init + _2h;
       const T y0 = e.value();
-      x = x_init +   h;
+      x = x_init + h;
       const T y1 = e.value();
-      x = x_init -   h;
+      x = x_init - h;
       const T y2 = e.value();
       x = x_init - _2h;
       const T y3 = e.value();
@@ -36722,8 +42133,8 @@ namespace exprtk
 
          return result;
       }
-      else
-         return std::numeric_limits<T>::quiet_NaN();
+
+      return std::numeric_limits<T>::quiet_NaN();
    }
 
    template <typename T>
@@ -36749,8 +42160,8 @@ namespace exprtk
 
          return result;
       }
-      else
-         return std::numeric_limits<T>::quiet_NaN();
+
+      return std::numeric_limits<T>::quiet_NaN();
    }
 
    template <typename T>
@@ -36776,8 +42187,8 @@ namespace exprtk
 
          return result;
       }
-      else
-         return std::numeric_limits<T>::quiet_NaN();
+
+      return std::numeric_limits<T>::quiet_NaN();
    }
 
    /*
@@ -37056,84 +42467,97 @@ namespace exprtk
          disable_has_side_effects(*this);
       }
 
-      ~polynomial() override = default;
+      ~polynomial() exprtk_override exprtk_default;
 
       #define poly_rtrn(NN) \
       return (NN != N) ? std::numeric_limits<T>::quiet_NaN() :
 
-      inline virtual T operator() (const T& x, const T& c1, const T& c0)
+      inline T operator() (const T& x, const T& c1, const T& c0) exprtk_override
       {
          poly_rtrn(1) (poly_impl<T,1>::evaluate(x, c1, c0));
       }
 
-      inline virtual T operator() (const T& x, const T& c2, const T& c1, const T& c0)
+      inline T operator() (const T& x, const T& c2, const T& c1, const T& c0) exprtk_override
       {
          poly_rtrn(2) (poly_impl<T,2>::evaluate(x, c2, c1, c0));
       }
 
-      inline virtual T operator() (const T& x, const T& c3, const T& c2, const T& c1, const T& c0)
+      inline T operator() (const T& x, const T& c3, const T& c2, const T& c1, const T& c0) exprtk_override
       {
          poly_rtrn(3) (poly_impl<T,3>::evaluate(x, c3, c2, c1, c0));
       }
 
-      inline virtual T operator() (const T& x, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      inline T operator() (const T& x, const T& c4, const T& c3, const T& c2, const T& c1,
+                           const T& c0) exprtk_override
       {
          poly_rtrn(4) (poly_impl<T,4>::evaluate(x, c4, c3, c2, c1, c0));
       }
 
-      inline virtual T operator() (const T& x, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      inline T operator() (const T& x, const T& c5, const T& c4, const T& c3, const T& c2,
+                           const T& c1, const T& c0) exprtk_override
       {
          poly_rtrn(5) (poly_impl<T,5>::evaluate(x, c5, c4, c3, c2, c1, c0));
       }
 
-      inline virtual T operator() (const T& x, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      inline T operator() (const T& x, const T& c6, const T& c5, const T& c4, const T& c3,
+                           const T& c2, const T& c1, const T& c0) exprtk_override
       {
          poly_rtrn(6) (poly_impl<T,6>::evaluate(x, c6, c5, c4, c3, c2, c1, c0));
       }
 
-      inline virtual T operator() (const T& x, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      inline T operator() (const T& x, const T& c7, const T& c6, const T& c5, const T& c4,
+                           const T& c3, const T& c2, const T& c1, const T& c0) exprtk_override
       {
          poly_rtrn(7) (poly_impl<T,7>::evaluate(x, c7, c6, c5, c4, c3, c2, c1, c0));
       }
 
-      inline virtual T operator() (const T& x, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      inline T operator() (const T& x, const T& c8, const T& c7, const T& c6, const T& c5,
+                           const T& c4, const T& c3, const T& c2, const T& c1, const T& c0) exprtk_override
       {
          poly_rtrn(8) (poly_impl<T,8>::evaluate(x, c8, c7, c6, c5, c4, c3, c2, c1, c0));
       }
 
-      inline virtual T operator() (const T& x, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      inline T operator() (const T& x, const T& c9, const T& c8, const T& c7, const T& c6,
+                           const T& c5, const T& c4, const T& c3, const T& c2, const T& c1,
+                           const T& c0) exprtk_override
       {
          poly_rtrn(9) (poly_impl<T,9>::evaluate(x, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0));
       }
 
-      inline virtual T operator() (const T& x, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      inline T operator() (const T& x, const T& c10, const T& c9, const T& c8, const T& c7,
+                           const T& c6, const T& c5, const T& c4, const T& c3, const T& c2,
+                           const T& c1, const T& c0) exprtk_override
       {
          poly_rtrn(10) (poly_impl<T,10>::evaluate(x, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0));
       }
 
-      inline virtual T operator() (const T& x, const T& c11, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      inline T operator() (const T& x, const T& c11, const T& c10, const T& c9, const T& c8,
+                           const T& c7, const T& c6, const T& c5, const T& c4, const T& c3,
+                           const T& c2, const T& c1, const T& c0) exprtk_override
       {
          poly_rtrn(11) (poly_impl<T,11>::evaluate(x, c11, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0));
       }
 
-      inline virtual T operator() (const T& x, const T& c12, const T& c11, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      inline T operator() (const T& x, const T& c12, const T& c11, const T& c10, const T& c9,
+                           const T& c8, const T& c7, const T& c6, const T& c5, const T& c4,
+                           const T& c3, const T& c2, const T& c1, const T& c0) exprtk_override
       {
          poly_rtrn(12) (poly_impl<T,12>::evaluate(x, c12, c11, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0));
       }
 
       #undef poly_rtrn
 
-      inline virtual T operator() ()
+      inline T operator() () exprtk_override
       {
          return std::numeric_limits<T>::quiet_NaN();
       }
 
-      inline virtual T operator() (const T&)
+      inline T operator() (const T&) exprtk_override
       {
          return std::numeric_limits<T>::quiet_NaN();
       }
 
-      inline virtual T operator() (const T&, const T&)
+      inline T operator() (const T&, const T&) exprtk_override
       {
          return std::numeric_limits<T>::quiet_NaN();
       }
@@ -37151,7 +42575,7 @@ namespace exprtk
 
       struct function
       {
-         function() = default;
+         function() exprtk_default;
 
          function(const std::string& n)
          : name_(n)
@@ -37159,15 +42583,15 @@ namespace exprtk
 
          function(const std::string& name,
                   const std::string& expression)
-         : name_(name),
-           expression_(expression)
+         : name_(name)
+         , expression_(expression)
          {}
 
          function(const std::string& name,
                   const std::string& expression,
                   const std::string& v0)
-         : name_(name),
-           expression_(expression)
+         : name_(name)
+         , expression_(expression)
          {
             v_.push_back(v0);
          }
@@ -37175,8 +42599,8 @@ namespace exprtk
          function(const std::string& name,
                   const std::string& expression,
                   const std::string& v0, const std::string& v1)
-         : name_(name),
-           expression_(expression)
+         : name_(name)
+         , expression_(expression)
          {
             v_.push_back(v0); v_.push_back(v1);
          }
@@ -37185,8 +42609,8 @@ namespace exprtk
                   const std::string& expression,
                   const std::string& v0, const std::string& v1,
                   const std::string& v2)
-         : name_(name),
-           expression_(expression)
+         : name_(name)
+         , expression_(expression)
          {
             v_.push_back(v0); v_.push_back(v1);
             v_.push_back(v2);
@@ -37196,8 +42620,8 @@ namespace exprtk
                   const std::string& expression,
                   const std::string& v0, const std::string& v1,
                   const std::string& v2, const std::string& v3)
-         : name_(name),
-           expression_(expression)
+         : name_(name)
+         , expression_(expression)
          {
             v_.push_back(v0); v_.push_back(v1);
             v_.push_back(v2); v_.push_back(v3);
@@ -37208,8 +42632,8 @@ namespace exprtk
                   const std::string& v0, const std::string& v1,
                   const std::string& v2, const std::string& v3,
                   const std::string& v4)
-         : name_(name),
-           expression_(expression)
+         : name_(name)
+         , expression_(expression)
          {
             v_.push_back(v0); v_.push_back(v1);
             v_.push_back(v2); v_.push_back(v3);
@@ -37234,6 +42658,50 @@ namespace exprtk
             return (*this);
          }
 
+         inline function& vars(const std::string& v0,
+                               const std::string& v1)
+         {
+            v_.push_back(v0);
+            v_.push_back(v1);
+            return (*this);
+         }
+
+         inline function& vars(const std::string& v0,
+                               const std::string& v1,
+                               const std::string& v2)
+         {
+            v_.push_back(v0);
+            v_.push_back(v1);
+            v_.push_back(v2);
+            return (*this);
+         }
+
+         inline function& vars(const std::string& v0,
+                               const std::string& v1,
+                               const std::string& v2,
+                               const std::string& v3)
+         {
+            v_.push_back(v0);
+            v_.push_back(v1);
+            v_.push_back(v2);
+            v_.push_back(v3);
+            return (*this);
+         }
+
+         inline function& vars(const std::string& v0,
+                               const std::string& v1,
+                               const std::string& v2,
+                               const std::string& v3,
+                               const std::string& v4)
+         {
+            v_.push_back(v0);
+            v_.push_back(v1);
+            v_.push_back(v2);
+            v_.push_back(v3);
+            v_.push_back(v4);
+            return (*this);
+         }
+
          std::string name_;
          std::string expression_;
          std::deque<std::string> v_;
@@ -37243,76 +42711,101 @@ namespace exprtk
 
       struct base_func : public exprtk::ifunction<T>
       {
-         typedef const T&                       type;
-         typedef exprtk::ifunction<T>     function_t;
-         typedef std::vector<T*>            varref_t;
-         typedef std::vector<T>                var_t;
+         typedef const T&                  type;
+         typedef exprtk::ifunction<T>      function_t;
+         typedef std::vector<T*>           varref_t;
+         typedef std::vector<T>            var_t;
+         typedef std::vector<std::string>  str_t;
          typedef std::pair<T*,std::size_t> lvarref_t;
          typedef std::vector<lvarref_t>    lvr_vec_t;
+         typedef std::vector<std::string*> lstr_vec_t;
 
          using exprtk::ifunction<T>::operator();
 
          base_func(const std::size_t& pc = 0)
-         : exprtk::ifunction<T>(pc),
-           local_var_stack_size(0),
-           stack_depth(0)
+         : exprtk::ifunction<T>(pc)
+         , local_var_stack_size(0)
+         , stack_depth(0)
          {
             v.resize(pc);
          }
 
-         ~base_func() override = default;
+         ~base_func() exprtk_override exprtk_default;
+
+         #define exprtk_assign(Index) \
+         (*v[Index]) = v##Index;      \
 
          inline void update(const T& v0)
          {
-            (*v[0]) = v0;
+            exprtk_assign(0)
          }
 
          inline void update(const T& v0, const T& v1)
          {
-            (*v[0]) = v0; (*v[1]) = v1;
+            exprtk_assign(0) exprtk_assign(1)
          }
 
          inline void update(const T& v0, const T& v1, const T& v2)
          {
-            (*v[0]) = v0; (*v[1]) = v1;
-            (*v[2]) = v2;
+            exprtk_assign(0) exprtk_assign(1)
+            exprtk_assign(2)
          }
 
          inline void update(const T& v0, const T& v1, const T& v2, const T& v3)
          {
-            (*v[0]) = v0; (*v[1]) = v1;
-            (*v[2]) = v2; (*v[3]) = v3;
+            exprtk_assign(0) exprtk_assign(1)
+            exprtk_assign(2) exprtk_assign(3)
          }
 
          inline void update(const T& v0, const T& v1, const T& v2, const T& v3, const T& v4)
          {
-            (*v[0]) = v0; (*v[1]) = v1;
-            (*v[2]) = v2; (*v[3]) = v3;
-            (*v[4]) = v4;
+            exprtk_assign(0) exprtk_assign(1)
+            exprtk_assign(2) exprtk_assign(3)
+            exprtk_assign(4)
          }
 
          inline void update(const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, const T& v5)
          {
-            (*v[0]) = v0; (*v[1]) = v1;
-            (*v[2]) = v2; (*v[3]) = v3;
-            (*v[4]) = v4; (*v[5]) = v5;
+            exprtk_assign(0) exprtk_assign(1)
+            exprtk_assign(2) exprtk_assign(3)
+            exprtk_assign(4) exprtk_assign(5)
          }
 
+         #ifdef exprtk_assign
+         #undef exprtk_assign
+         #endif
+
          inline function_t& setup(expression_t& expr)
          {
             expression = expr;
 
-            typedef typename expression_t::control_block::local_data_list_t ldl_t;
+            typedef typename expression_t::control_block  ctrlblk_t;
+            typedef typename ctrlblk_t::local_data_list_t ldl_t;
+            typedef typename ctrlblk_t::data_type         data_t;
+            typedef typename ldl_t::value_type            ldl_value_type;
 
             const ldl_t ldl = expr.local_data_list();
 
-            std::vector<std::size_t> index_list;
+            std::vector<std::pair<std::size_t,data_t> > index_list;
 
             for (std::size_t i = 0; i < ldl.size(); ++i)
             {
+               exprtk_debug(("base_func::setup() - element[%02d] type: %s size: %d\n",
+                             static_cast<int>(i),
+                             expression_t::control_block::to_str(ldl[i].type).c_str(),
+                             static_cast<int>(ldl[i].size)));
+
+               switch (ldl[i].type)
+               {
+                  case ctrlblk_t::e_unknown   : continue;
+                  case ctrlblk_t::e_expr      : continue;
+                  case ctrlblk_t::e_vecholder : continue;
+                  default                     : break;
+               }
+
                if (ldl[i].size)
                {
-                  index_list.push_back(i);
+                  index_list.push_back(std::make_pair(i,ldl[i].type));
                }
             }
 
@@ -37320,19 +42813,34 @@ namespace exprtk
 
             for (std::size_t i = 0; i < index_list.size(); ++i)
             {
-               const std::size_t index = index_list[i];
+               const std::size_t index         = index_list[i].first;
+               const ldl_value_type& local_var = ldl[index];
+
+               assert(local_var.pointer);
 
                if (i < (index_list.size() - v.size()))
                {
-                  lv.push_back(
-                        std::make_pair(
-                           reinterpret_cast<T*>(ldl[index].pointer),
-                           ldl[index].size));
+                  if (local_var.type == ctrlblk_t::e_string)
+                  {
+                     local_str_vars.push_back(
+                        reinterpret_cast<std::string*>(local_var.pointer));
+                  }
+                  else if (
+                            (local_var.type == ctrlblk_t::e_data   ) ||
+                            (local_var.type == ctrlblk_t::e_vecdata)
+                          )
+                  {
+                     local_vars.push_back(std::make_pair(
+                        reinterpret_cast<T*>(local_var.pointer),
+                        local_var.size));
 
-                  local_var_stack_size += ldl[index].size;
+                     local_var_stack_size += local_var.size;
+                  }
                }
                else
-                  v[input_param_count++] = reinterpret_cast<T*>(ldl[index].pointer);
+               {
+                  v[input_param_count++] = reinterpret_cast<T*>(local_var.pointer);
+               }
             }
 
             clear_stack();
@@ -37348,14 +42856,21 @@ namespace exprtk
                {
                   var_t var_stack(v.size(),T(0));
                   copy(v,var_stack);
-                  param_stack.push_back(var_stack);
+                  input_params_stack.push_back(var_stack);
+               }
+
+               if (!local_vars.empty())
+               {
+                  var_t local_vec_frame(local_var_stack_size,T(0));
+                  copy(local_vars,local_vec_frame);
+                  local_var_stack.push_back(local_vec_frame);
                }
 
-               if (!lv.empty())
+               if (!local_str_vars.empty())
                {
-                  var_t local_var_stack(local_var_stack_size,T(0));
-                  copy(lv,local_var_stack);
-                  local_stack.push_back(local_var_stack);
+                  str_t local_str_frame(local_str_vars.size());
+                  copy(local_str_vars,local_str_frame);
+                  local_str_stack.push_back(local_str_frame);
                }
             }
          }
@@ -37366,14 +42881,20 @@ namespace exprtk
             {
                if (!v.empty())
                {
-                  copy(param_stack.back(),v);
-                  param_stack.pop_back();
+                  copy(input_params_stack.back(), v);
+                  input_params_stack.pop_back();
+               }
+
+               if (!local_vars.empty())
+               {
+                  copy(local_var_stack.back(), local_vars);
+                  local_var_stack.pop_back();
                }
 
-               if (!lv.empty())
+               if (!local_str_vars.empty())
                {
-                  copy(local_stack.back(),lv);
-                  local_stack.pop_back();
+                  copy(local_str_stack.back(), local_str_vars);
+                  local_str_stack.pop_back();
                }
             }
          }
@@ -37386,6 +42907,14 @@ namespace exprtk
             }
          }
 
+         void copy(const lstr_vec_t& src_v, str_t& dest_v)
+         {
+            for (std::size_t i = 0; i < src_v.size(); ++i)
+            {
+               dest_v[i] = (*src_v[i]);
+            }
+         }
+
          void copy(const var_t& src_v, varref_t& dest_v)
          {
             for (std::size_t i = 0; i < src_v.size(); ++i)
@@ -37418,9 +42947,12 @@ namespace exprtk
             typename var_t::const_iterator itr = src_v.begin();
             typedef  typename std::iterator_traits<typename var_t::iterator>::difference_type diff_t;
 
-            for (std::size_t i = 0; i < src_v.size(); ++i)
+            for (std::size_t i = 0; i < dest_v.size(); ++i)
             {
-               lvarref_t vr = dest_v[i];
+               lvarref_t& vr = dest_v[i];
+
+               assert(vr.first != 0);
+               assert(vr.second > 0);
 
                if (1 == vr.second)
                   (*vr.first) = *itr++;
@@ -37432,6 +42964,16 @@ namespace exprtk
             }
          }
 
+         void copy(const str_t& src_str, lstr_vec_t& dest_str)
+         {
+            assert(src_str.size() == dest_str.size());
+
+            for (std::size_t i = 0; i < dest_str.size(); ++i)
+            {
+               *dest_str[i] = src_str[i];
+            }
+         }
+
          inline void clear_stack()
          {
             for (std::size_t i = 0; i < v.size(); ++i)
@@ -37445,29 +42987,19 @@ namespace exprtk
             return e.value();
          }
 
-         expression_t expression;
-         varref_t v;
-         lvr_vec_t lv;
-         std::size_t local_var_stack_size;
-         std::size_t stack_depth;
-         std::deque<var_t> param_stack;
-         std::deque<var_t> local_stack;
+         expression_t      expression;
+         varref_t          v;
+         lvr_vec_t         local_vars;
+         lstr_vec_t        local_str_vars;
+         std::size_t       local_var_stack_size;
+         std::size_t       stack_depth;
+         std::deque<var_t> input_params_stack;
+         std::deque<var_t> local_var_stack;
+         std::deque<str_t> local_str_stack;
       };
 
       typedef std::map<std::string,base_func*> funcparam_t;
 
-      struct func_0param : public base_func
-      {
-         using exprtk::ifunction<T>::operator();
-
-         func_0param() : base_func(0) {}
-
-         inline T operator() ()
-         {
-            return this->value(base_func::expression);
-         }
-      };
-
       typedef const T& type;
 
       template <typename BaseFuncType>
@@ -37488,8 +43020,21 @@ namespace exprtk
 
       private:
 
-         scoped_bft(scoped_bft&);
-         scoped_bft& operator=(scoped_bft&);
+         scoped_bft(const scoped_bft&) exprtk_delete;
+         scoped_bft& operator=(const scoped_bft&) exprtk_delete;
+      };
+
+      struct func_0param : public base_func
+      {
+         using exprtk::ifunction<T>::operator();
+
+         func_0param() : base_func(0) {}
+
+         inline T operator() () exprtk_override
+         {
+            scoped_bft<func_0param> sb(*this);
+            return this->value(base_func::expression);
+         }
       };
 
       struct func_1param : public base_func
@@ -37498,7 +43043,7 @@ namespace exprtk
 
          func_1param() : base_func(1) {}
 
-         inline T operator() (type v0)
+         inline T operator() (type v0) exprtk_override
          {
             scoped_bft<func_1param> sb(*this);
             base_func::update(v0);
@@ -37512,7 +43057,7 @@ namespace exprtk
 
          func_2param() : base_func(2) {}
 
-         inline T operator() (type v0, type v1)
+         inline T operator() (type v0, type v1) exprtk_override
          {
             scoped_bft<func_2param> sb(*this);
             base_func::update(v0, v1);
@@ -37526,7 +43071,7 @@ namespace exprtk
 
          func_3param() : base_func(3) {}
 
-         inline T operator() (type v0, type v1, type v2)
+         inline T operator() (type v0, type v1, type v2) exprtk_override
          {
             scoped_bft<func_3param> sb(*this);
             base_func::update(v0, v1, v2);
@@ -37540,7 +43085,7 @@ namespace exprtk
 
          func_4param() : base_func(4) {}
 
-         inline T operator() (type v0, type v1, type v2, type v3)
+         inline T operator() (type v0, type v1, type v2, type v3) exprtk_override
          {
             scoped_bft<func_4param> sb(*this);
             base_func::update(v0, v1, v2, v3);
@@ -37554,7 +43099,7 @@ namespace exprtk
 
          func_5param() : base_func(5) {}
 
-         inline T operator() (type v0, type v1, type v2, type v3, type v4)
+         inline T operator() (type v0, type v1, type v2, type v3, type v4) exprtk_override
          {
             scoped_bft<func_5param> sb(*this);
             base_func::update(v0, v1, v2, v3, v4);
@@ -37568,7 +43113,7 @@ namespace exprtk
 
          func_6param() : base_func(6) {}
 
-         inline T operator() (type v0, type v1, type v2, type v3, type v4, type v5)
+         inline T operator() (type v0, type v1, type v2, type v3, type v4, type v5) exprtk_override
          {
             scoped_bft<func_6param> sb(*this);
             base_func::update(v0, v1, v2, v3, v4, v5);
@@ -37595,14 +43140,14 @@ namespace exprtk
          return result;
       }
 
-      #define def_fp_retval(N)                               \
-      struct func_##N##param_retval : public func_##N##param \
-      {                                                      \
-         inline T value(expression_t& e)                     \
-         {                                                   \
-            return return_value(e);                          \
-         }                                                   \
-      };                                                     \
+      #define def_fp_retval(N)                                            \
+      struct func_##N##param_retval exprtk_final : public func_##N##param \
+      {                                                                   \
+         inline T value(expression_t& e) exprtk_override                  \
+         {                                                                \
+            return return_value(e);                                       \
+         }                                                                \
+      };                                                                  \
 
       def_fp_retval(0)
       def_fp_retval(1)
@@ -37612,18 +43157,20 @@ namespace exprtk
       def_fp_retval(5)
       def_fp_retval(6)
 
+      #undef def_fp_retval
+
       template <typename Allocator,
                 template <typename, typename> class Sequence>
       inline bool add(const std::string& name,
                       const std::string& expression,
                       const Sequence<std::string,Allocator>& var_list,
-                      const bool override = false)
+                      const bool allow_override = false)
       {
          const typename std::map<std::string,expression_t>::iterator itr = expr_map_.find(name);
 
          if (expr_map_.end() != itr)
          {
-            if (!override)
+            if (!allow_override)
             {
                exprtk_debug(("Compositor error(add): function '%s' already defined\n",
                              name.c_str()));
@@ -37634,7 +43181,7 @@ namespace exprtk
             remove(name, var_list.size());
          }
 
-         if (compile_expression(name,expression,var_list))
+         if (compile_expression(name, expression, var_list))
          {
             const std::size_t n = var_list.size();
 
@@ -37654,16 +43201,20 @@ namespace exprtk
    public:
 
       function_compositor()
-      : parser_(settings_t::compile_all_opts +
-                settings_t::e_disable_zero_return),
-        fp_map_(7)
+      : parser_(settings_t::default_compile_all_opts +
+                settings_t::e_disable_zero_return)
+      , fp_map_(7)
+      , load_variables_(false)
+      , load_vectors_(false)
       {}
 
-      function_compositor(const symbol_table_t& st)
-      : symbol_table_(st),
-        parser_(settings_t::compile_all_opts +
-                settings_t::e_disable_zero_return),
-        fp_map_(7)
+      explicit function_compositor(const symbol_table_t& st)
+      : symbol_table_(st)
+      , parser_(settings_t::default_compile_all_opts +
+                settings_t::e_disable_zero_return)
+      , fp_map_(7)
+      , load_variables_(false)
+      , load_vectors_(false)
       {}
 
      ~function_compositor()
@@ -37686,6 +43237,46 @@ namespace exprtk
          auxiliary_symtab_list_.push_back(&symtab);
       }
 
+      void load_variables(const bool load = true)
+      {
+         load_variables_ = load;
+      }
+
+      void load_vectors(const bool load = true)
+      {
+         load_vectors_ = load;
+      }
+
+      inline void register_loop_runtime_check(loop_runtime_check& lrtchk)
+      {
+         parser_.register_loop_runtime_check(lrtchk);
+      }
+
+      inline void register_vector_access_runtime_check(vector_access_runtime_check& vartchk)
+      {
+         parser_.register_vector_access_runtime_check(vartchk);
+      }
+
+      inline void register_compilation_timeout_check(compilation_check& compchk)
+      {
+         parser_.register_compilation_timeout_check(compchk);
+      }
+
+      inline void clear_loop_runtime_check()
+      {
+         parser_.clear_loop_runtime_check();
+      }
+
+      inline void clear_vector_access_runtime_check()
+      {
+         parser_.clear_vector_access_runtime_check();
+      }
+
+      inline void clear_compilation_timeout_check()
+      {
+         parser_.clear_compilation_timeout_check();
+      }
+
       void clear()
       {
          symbol_table_.clear();
@@ -37704,11 +43295,40 @@ namespace exprtk
 
             fp_map_[i].clear();
          }
+
+         clear_loop_runtime_check         ();
+         clear_vector_access_runtime_check();
+         clear_compilation_timeout_check  ();
       }
 
-      inline bool add(const function& f, const bool override = false)
+      inline bool add(const function& f, const bool allow_override = false)
       {
-         return add(f.name_, f.expression_, f.v_,override);
+         return add(f.name_, f.expression_, f.v_, allow_override);
+      }
+
+      inline std::string error() const
+      {
+         if (!error_list_.empty())
+         {
+            return error_list_[0].diagnostic;
+         }
+         else
+            return std::string("No Error");
+      }
+
+      inline std::size_t error_count() const
+      {
+         return error_list_.size();
+      }
+
+      inline parser_error::type get_error(const std::size_t& index) const
+      {
+         if (index < error_list_.size())
+         {
+            return error_list_[index];
+         }
+
+         throw std::invalid_argument("compositor::get_error() - Invalid error index specified");
       }
 
    private:
@@ -37726,8 +43346,30 @@ namespace exprtk
          local_symbol_table.load_from(symbol_table_);
          local_symbol_table.add_constants();
 
+         if (load_variables_)
+         {
+            local_symbol_table.load_variables_from(symbol_table_);
+         }
+
+         if (load_vectors_)
+         {
+            local_symbol_table.load_vectors_from(symbol_table_);
+         }
+
+         error_list_.clear();
+
          if (!valid(name,input_var_list.size()))
+         {
+            parser_error::type error =
+               parser_error::make_error(
+                  parser_error::e_parser,
+                  lexer::token(),
+                  "ERR277 - Function '" + name + "' is an invalid overload",
+                  exprtk_error_location);
+
+            error_list_.push_back(error);
             return false;
+         }
 
          if (!forward(name,
                       input_var_list.size(),
@@ -37759,18 +43401,22 @@ namespace exprtk
 
          if (!parser_.compile(mod_expression,compiled_expression))
          {
-            exprtk_debug(("Compositor Error: %s\n",parser_.error().c_str()));
-            exprtk_debug(("Compositor modified expression: \n%s\n",mod_expression.c_str()));
+            exprtk_debug(("Compositor Error: %s\n", parser_.error().c_str()));
+            exprtk_debug(("Compositor modified expression: \n%s\n", mod_expression.c_str()));
 
             remove(name,input_var_list.size());
 
+            for (std::size_t err_index = 0; err_index < parser_.error_count(); ++err_index)
+            {
+               error_list_.push_back(parser_.get_error(err_index));
+            }
+
             return false;
          }
 
          if (!return_present && parser_.dec().return_present())
          {
             remove(name,input_var_list.size());
-
             return compile_expression(name, expression, input_var_list, true);
          }
 
@@ -37891,206 +43537,14 @@ namespace exprtk
       std::map<std::string,expression_t> expr_map_;
       std::vector<funcparam_t> fp_map_;
       std::vector<symbol_table_t*> auxiliary_symtab_list_;
-   };
-
-   template <typename T>
-   inline bool pgo_primer()
-   {
-      static const std::string expression_list[] =
-             {
-                "(y + x)",
-                "2 * (y + x)",
-                "(2 * y + 2 * x)",
-                "(y + x / y) * (x - y / x)",
-                "x / ((x + y) * (x - y)) / y",
-                "1 - ((x * y) + (y / x)) - 3",
-                "sin(2 * x) + cos(pi / y)",
-                "1 - sin(2 * x) + cos(pi / y)",
-                "sqrt(1 - sin(2 * x) + cos(pi / y) / 3)",
-                "(x^2 / sin(2 * pi / y)) -x / 2",
-                "x + (cos(y - sin(2 / x * pi)) - sin(x - cos(2 * y / pi))) - y",
-                "clamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0)",
-                "iclamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0)",
-                "max(3.33, min(sqrt(1 - sin(2 * x) + cos(pi / y) / 3), 1.11))",
-                "if(avg(x,y) <= x + y, x - y, x * y) + 2 * pi / x",
-                "1.1x^1 + 2.2y^2 - 3.3x^3 + 4.4y^4 - 5.5x^5 + 6.6y^6 - 7.7x^27 + 8.8y^55",
-                "(yy + xx)",
-                "2 * (yy + xx)",
-                "(2 * yy + 2 * xx)",
-                "(yy + xx / yy) * (xx - yy / xx)",
-                "xx / ((xx + yy) * (xx - yy)) / yy",
-                "1 - ((xx * yy) + (yy / xx)) - 3",
-                "sin(2 * xx) + cos(pi / yy)",
-                "1 - sin(2 * xx) + cos(pi / yy)",
-                "sqrt(1 - sin(2 * xx) + cos(pi / yy) / 3)",
-                "(xx^2 / sin(2 * pi / yy)) -xx / 2",
-                "xx + (cos(yy - sin(2 / xx * pi)) - sin(xx - cos(2 * yy / pi))) - yy",
-                "clamp(-1.0, sin(2 * pi * xx) + cos(yy / 2 * pi), +1.0)",
-                "max(3.33, min(sqrt(1 - sin(2 * xx) + cos(pi / yy) / 3), 1.11))",
-                "if(avg(xx,yy) <= xx + yy, xx - yy, xx * yy) + 2 * pi / xx",
-                "1.1xx^1 + 2.2yy^2 - 3.3xx^3 + 4.4yy^4 - 5.5xx^5 + 6.6yy^6 - 7.7xx^27 + 8.8yy^55",
-                "(1.1*(2.2*(3.3*(4.4*(5.5*(6.6*(7.7*(8.8*(9.9+x)))))))))",
-                "(((((((((x+9.9)*8.8)*7.7)*6.6)*5.5)*4.4)*3.3)*2.2)*1.1)",
-                "(x + y) * z", "x + (y * z)", "(x + y) * 7", "x + (y * 7)",
-                "(x + 7) * y", "x + (7 * y)", "(7 + x) * y", "7 + (x * y)",
-                "(2 + x) * 3", "2 + (x * 3)", "(2 + 3) * x", "2 + (3 * x)",
-                "(x + 2) * 3", "x + (2 * 3)",
-                "(x + y) * (z / w)", "(x + y) * (z / 7)", "(x + y) * (7 / z)", "(x + 7) * (y / z)",
-                "(7 + x) * (y / z)", "(2 + x) * (y / z)", "(x + 2) * (y / 3)", "(2 + x) * (y / 3)",
-                "(x + 2) * (3 / y)", "x + (y * (z / w))", "x + (y * (z / 7))", "x + (y * (7 / z))",
-                "x + (7 * (y / z))", "7 + (x * (y / z))", "2 + (x * (3 / y))", "x + (2 * (y / 4))",
-                "2 + (x * (y / 3))", "x + (2 * (3 / y))",
-                "x + ((y * z) / w)", "x + ((y * z) / 7)", "x + ((y * 7) / z)", "x + ((7 * y) / z)",
-                "7 + ((y * z) / w)", "2 + ((x * 3) / y)", "x + ((2 * y) / 3)", "2 + ((x * y) / 3)",
-                "x + ((2 * 3) / y)", "(((x + y) * z) / w)",
-                "(((x + y) * z) / 7)", "(((x + y) * 7) / z)", "(((x + 7) * y) / z)", "(((7 + x) * y) / z)",
-                "(((2 + x) * 3) / y)", "(((x + 2) * y) / 3)", "(((2 + x) * y) / 3)", "(((x + 2) * 3) / y)",
-                "((x + (y * z)) / w)", "((x + (y * z)) / 7)", "((x + (y * 7)) / y)", "((x + (7 * y)) / z)",
-                "((7 + (x * y)) / z)", "((2 + (x * 3)) / y)", "((x + (2 * y)) / 3)", "((2 + (x * y)) / 3)",
-                "((x + (2 * 3)) / y)",
-                "(xx + yy) * zz", "xx + (yy * zz)",
-                "(xx + yy) * 7", "xx + (yy * 7)",
-                "(xx + 7) * yy", "xx + (7 * yy)",
-                "(7 + xx) * yy", "7 + (xx * yy)",
-                "(2 + x) * 3", "2 + (x * 3)",
-                "(2 + 3) * x", "2 + (3 * x)",
-                "(x + 2) * 3", "x + (2 * 3)",
-                "(xx + yy) * (zz / ww)", "(xx + yy) * (zz / 7)",
-                "(xx + yy) * (7 / zz)", "(xx + 7) * (yy / zz)",
-                "(7 + xx) * (yy / zz)", "(2 + xx) * (yy / zz)",
-                "(xx + 2) * (yy / 3)", "(2 + xx) * (yy / 3)",
-                "(xx + 2) * (3 / yy)", "xx + (yy * (zz / ww))",
-                "xx + (yy * (zz / 7))", "xx + (yy * (7 / zz))",
-                "xx + (7 * (yy / zz))", "7 + (xx * (yy / zz))",
-                "2 + (xx * (3 / yy))", "xx + (2 * (yy / 4))",
-                "2 + (xx * (yy / 3))", "xx + (2 * (3 / yy))",
-                "xx + ((yy * zz) / ww)", "xx + ((yy * zz) / 7)",
-                "xx + ((yy * 7) / zz)", "xx + ((7 * yy) / zz)",
-                "7 + ((yy * zz) / ww)", "2 + ((xx * 3) / yy)",
-                "xx + ((2 * yy) / 3)", "2 + ((xx * yy) / 3)",
-                "xx + ((2 * 3) / yy)", "(((xx + yy) * zz) / ww)",
-                "(((xx + yy) * zz) / 7)", "(((xx + yy) * 7) / zz)",
-                "(((xx + 7) * yy) / zz)", "(((7 + xx) * yy) / zz)",
-                "(((2 + xx) * 3) / yy)", "(((xx + 2) * yy) / 3)",
-                "(((2 + xx) * yy) / 3)", "(((xx + 2) * 3) / yy)",
-                "((xx + (yy * zz)) / ww)", "((xx + (yy * zz)) / 7)",
-                "((xx + (yy * 7)) / yy)", "((xx + (7 * yy)) / zz)",
-                "((7 + (xx * yy)) / zz)", "((2 + (xx * 3)) / yy)",
-                "((xx + (2 * yy)) / 3)", "((2 + (xx * yy)) / 3)",
-                "((xx + (2 * 3)) / yy)"
-             };
-
-      static const std::size_t expression_list_size = sizeof(expression_list) / sizeof(std::string);
-
-      T  x = T(0);
-      T  y = T(0);
-      T  z = T(0);
-      T  w = T(0);
-      T xx = T(0);
-      T yy = T(0);
-      T zz = T(0);
-      T ww = T(0);
-
-      exprtk::symbol_table<T> symbol_table;
-      symbol_table.add_constants();
-      symbol_table.add_variable( "x", x);
-      symbol_table.add_variable( "y", y);
-      symbol_table.add_variable( "z", z);
-      symbol_table.add_variable( "w", w);
-      symbol_table.add_variable("xx",xx);
-      symbol_table.add_variable("yy",yy);
-      symbol_table.add_variable("zz",zz);
-      symbol_table.add_variable("ww",ww);
-
-      typedef typename std::deque<exprtk::expression<T> > expr_list_t;
-      expr_list_t expr_list;
-
-      const std::size_t rounds = 50;
-
-      {
-         for (std::size_t r = 0; r < rounds; ++r)
-         {
-            expr_list.clear();
-            exprtk::parser<T> parser;
-
-            for (std::size_t i = 0; i < expression_list_size; ++i)
-            {
-               exprtk::expression<T> expression;
-               expression.register_symbol_table(symbol_table);
-
-               if (!parser.compile(expression_list[i],expression))
-               {
-                  return false;
-               }
-
-               expr_list.push_back(expression);
-            }
-         }
-      }
-
-      struct execute
-      {
-         static inline T process(T& x, T& y, expression<T>& expression)
-         {
-            static const T lower_bound = T(-20);
-            static const T upper_bound = T(+20);
-            static const T delta       = T(0.1);
-
-            T total = T(0);
-
-            for (x = lower_bound; x <= upper_bound; x += delta)
-            {
-               for (y = lower_bound; y <= upper_bound; y += delta)
-               {
-                  total += expression.value();
-               }
-            }
-
-            return total;
-         }
-      };
-
-      for (std::size_t i = 0; i < expr_list.size(); ++i)
-      {
-         execute::process( x,  y, expr_list[i]);
-         execute::process(xx, yy, expr_list[i]);
-      }
-
-      {
-         for (std::size_t i = 0; i < 10000; ++i)
-         {
-            const T v = T(123.456 + i);
-
-            if (details::is_true(details::numeric::nequal(details::numeric::fast_exp<T, 1>::result(v),details::numeric::pow(v,T( 1)))))
-               return false;
-
-            #define else_stmt(N)                                                                                                           \
-            else if (details::is_true(details::numeric::nequal(details::numeric::fast_exp<T,N>::result(v),details::numeric::pow(v,T(N))))) \
-               return false;                                                                                                               \
-
-            else_stmt( 2) else_stmt( 3) else_stmt( 4) else_stmt( 5)
-            else_stmt( 6) else_stmt( 7) else_stmt( 8) else_stmt( 9)
-            else_stmt(10) else_stmt(11) else_stmt(12) else_stmt(13)
-            else_stmt(14) else_stmt(15) else_stmt(16) else_stmt(17)
-            else_stmt(18) else_stmt(19) else_stmt(20) else_stmt(21)
-            else_stmt(22) else_stmt(23) else_stmt(24) else_stmt(25)
-            else_stmt(26) else_stmt(27) else_stmt(28) else_stmt(29)
-            else_stmt(30) else_stmt(31) else_stmt(32) else_stmt(33)
-            else_stmt(34) else_stmt(35) else_stmt(36) else_stmt(37)
-            else_stmt(38) else_stmt(39) else_stmt(40) else_stmt(41)
-            else_stmt(42) else_stmt(43) else_stmt(44) else_stmt(45)
-            else_stmt(46) else_stmt(47) else_stmt(48) else_stmt(49)
-            else_stmt(50) else_stmt(51) else_stmt(52) else_stmt(53)
-            else_stmt(54) else_stmt(55) else_stmt(56) else_stmt(57)
-            else_stmt(58) else_stmt(59) else_stmt(60) else_stmt(61)
-         }
-      }
+      std::deque<parser_error::type> error_list_;
+      bool load_variables_;
+      bool load_vectors_;
+   }; // class function_compositor
 
-      return true;
-   }
-}
+} // namespace exprtk
 
-#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+#if defined(_MSC_VER) || defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
 #   ifndef NOMINMAX
 #      define NOMINMAX
 #   endif
@@ -38111,9 +43565,11 @@ namespace exprtk
    {
    public:
 
-      #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+      #if defined(_MSC_VER) || defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
       timer()
       : in_use_(false)
+      , start_time_{ {0, 0} }
+      , stop_time_ { {0, 0} }
       {
          QueryPerformanceFrequency(&clock_frequency_);
       }
@@ -38142,6 +43598,7 @@ namespace exprtk
       {
          start_time_.tv_sec  = 0;
          start_time_.tv_usec = 0;
+
          stop_time_.tv_sec   = 0;
          stop_time_.tv_usec  = 0;
       }
@@ -38190,7 +43647,7 @@ namespace exprtk
 
       bool in_use_;
 
-      #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+      #if defined(_MSC_VER) || defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
          LARGE_INTEGER start_time_;
          LARGE_INTEGER stop_time_;
          LARGE_INTEGER clock_frequency_;
@@ -38200,6 +43657,17 @@ namespace exprtk
       #endif
    };
 
+   template <typename T>
+   struct type_defs
+   {
+      typedef symbol_table<T>         symbol_table_t;
+      typedef expression<T>           expression_t;
+      typedef parser<T>               parser_t;
+      typedef parser_error::type      error_t;
+      typedef function_compositor<T>  compositor_t;
+      typedef typename compositor_t::function function_t;
+   };
+
 } // namespace exprtk
 
 #ifndef exprtk_disable_rtl_io
@@ -38212,7 +43680,23 @@ namespace exprtk
                              const T v,
                              exprtk::details::numeric::details::real_type_tag)
       {
-         printf(fmt.c_str(),v);
+         #if defined(__clang__)
+            #pragma clang diagnostic push
+            #pragma clang diagnostic ignored "-Wformat-nonliteral"
+         #elif defined(__GNUC__) || defined(__GNUG__)
+            #pragma GCC diagnostic push
+            #pragma GCC diagnostic ignored "-Wformat-nonliteral"
+         #elif defined(_MSC_VER)
+         #endif
+
+         printf(fmt.c_str(), v);
+
+         #if defined(__clang__)
+            #pragma clang diagnostic pop
+         #elif defined(__GNUC__) || defined(__GNUG__)
+            #pragma GCC diagnostic pop
+         #elif defined(_MSC_VER)
+         #endif
       }
 
       template <typename T>
@@ -38272,19 +43756,19 @@ namespace exprtk
    } // namespace exprtk::rtl::io::details
 
    template <typename T>
-   struct print : public exprtk::igeneric_function<T>
+   struct print exprtk_final : public exprtk::igeneric_function<T>
    {
       typedef typename igeneric_function<T>::parameter_list_t parameter_list_t;
 
       using exprtk::igeneric_function<T>::operator();
 
-      print(const std::string& scalar_format = "%10.5f")
+      explicit print(const std::string& scalar_format = "%10.5f")
       : scalar_format_(scalar_format)
       {
          exprtk::enable_zero_parameters(*this);
       }
 
-      inline T operator() (parameter_list_t parameters)
+      inline T operator() (parameter_list_t parameters) exprtk_override
       {
          details::print_impl<T>::process(scalar_format_,parameters);
          return T(0);
@@ -38294,19 +43778,19 @@ namespace exprtk
    };
 
    template <typename T>
-   struct println : public exprtk::igeneric_function<T>
+   struct println exprtk_final : public exprtk::igeneric_function<T>
    {
       typedef typename igeneric_function<T>::parameter_list_t parameter_list_t;
 
       using exprtk::igeneric_function<T>::operator();
 
-      println(const std::string& scalar_format = "%10.5f")
+      explicit println(const std::string& scalar_format = "%10.5f")
       : scalar_format_(scalar_format)
       {
          exprtk::enable_zero_parameters(*this);
       }
 
-      inline T operator() (parameter_list_t parameters)
+      inline T operator() (parameter_list_t parameters) exprtk_override
       {
          details::print_impl<T>::process(scalar_format_,parameters);
          printf("\n");
@@ -38324,7 +43808,7 @@ namespace exprtk
 
       bool register_package(exprtk::symbol_table<T>& symtab)
       {
-         #define exprtk_register_function(FunctionName,FunctionType)              \
+         #define exprtk_register_function(FunctionName, FunctionType)             \
          if (!symtab.add_function(FunctionName,FunctionType))                     \
          {                                                                        \
             exprtk_debug((                                                        \
@@ -38333,7 +43817,7 @@ namespace exprtk
             return false;                                                         \
          }                                                                        \
 
-         exprtk_register_function("print"  ,  p)
+         exprtk_register_function("print"  , p )
          exprtk_register_function("println", pl)
          #undef exprtk_register_function
 
@@ -38352,6 +43836,9 @@ namespace exprtk
 {
    namespace rtl { namespace io { namespace file { namespace details
    {
+      using ::exprtk::details::char_ptr;
+      using ::exprtk::details::char_cptr;
+
       enum file_mode
       {
          e_error = 0,
@@ -38363,9 +43850,9 @@ namespace exprtk
       struct file_descriptor
       {
          file_descriptor(const std::string& fname, const std::string& access)
-         : stream_ptr(0),
-           mode(get_file_mode(access)),
-           file_name(fname)
+         : stream_ptr(0)
+         , mode(get_file_mode(access))
+         , file_name(fname)
          {}
 
          void*       stream_ptr;
@@ -38385,8 +43872,8 @@ namespace exprtk
 
                   return false;
                }
-               else
-                  stream_ptr = stream;
+
+               stream_ptr = stream;
 
                return true;
             }
@@ -38401,8 +43888,8 @@ namespace exprtk
 
                   return false;
                }
-               else
-                  stream_ptr = stream;
+
+               stream_ptr = stream;
 
                return true;
             }
@@ -38417,13 +43904,13 @@ namespace exprtk
 
                   return false;
                }
-               else
-                  stream_ptr = stream;
+
+               stream_ptr = stream;
 
                return true;
             }
-            else
-               return false;
+
+            return false;
          }
 
          template <typename Stream, typename Ptr>
@@ -38460,11 +43947,11 @@ namespace exprtk
             switch (mode)
             {
                case e_write : reinterpret_cast<std::ofstream*>(stream_ptr)->
-                                 write(reinterpret_cast<const char*>(view.begin() + offset), amount * sizeof(typename View::value_t));
+                                 write(reinterpret_cast<char_cptr>(view.begin() + offset), amount * sizeof(typename View::value_t));
                               break;
 
                case e_rdwrt : reinterpret_cast<std::fstream*>(stream_ptr)->
-                                 write(reinterpret_cast<const char*>(view.begin() + offset) , amount * sizeof(typename View::value_t));
+                                 write(reinterpret_cast<char_cptr>(view.begin() + offset) , amount * sizeof(typename View::value_t));
                               break;
 
                default      : return false;
@@ -38479,11 +43966,11 @@ namespace exprtk
             switch (mode)
             {
                case e_read  : reinterpret_cast<std::ifstream*>(stream_ptr)->
-                                 read(reinterpret_cast<char*>(view.begin() + offset), amount * sizeof(typename View::value_t));
+                                 read(reinterpret_cast<char_ptr>(view.begin() + offset), amount * sizeof(typename View::value_t));
                               break;
 
                case e_rdwrt : reinterpret_cast<std::fstream*>(stream_ptr)->
-                                 read(reinterpret_cast<char*>(view.begin() + offset) , amount * sizeof(typename View::value_t));
+                                 read(reinterpret_cast<char_ptr>(view.begin() + offset) , amount * sizeof(typename View::value_t));
                               break;
 
                default      : return false;
@@ -38547,11 +44034,12 @@ namespace exprtk
       template <typename T>
       file_descriptor* make_handle(T v)
       {
-         file_descriptor* fd = reinterpret_cast<file_descriptor*>(0);
+         const std::size_t fd_size    = sizeof(details::file_descriptor*);
+         details::file_descriptor* fd = reinterpret_cast<file_descriptor*>(0);
 
-         std::memcpy(reinterpret_cast<char*>(&fd),
-                     reinterpret_cast<const char*>(&v),
-                     sizeof(void*));
+         std::memcpy(reinterpret_cast<char_ptr >(&fd),
+                     reinterpret_cast<char_cptr>(&v ),
+                     fd_size);
          return fd;
       }
 
@@ -38569,12 +44057,13 @@ namespace exprtk
          #ifdef _MSC_VER
          #pragma warning(pop)
          #endif
+         assert(sizeof(T) <= sizeof(void*));
       }
 
    } // namespace exprtk::rtl::io::file::details
 
    template <typename T>
-   class open : public exprtk::igeneric_function<T>
+   class open exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -38583,26 +44072,28 @@ namespace exprtk
       typedef typename igfun_t::generic_type        generic_type;
       typedef typename generic_type::string_view    string_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       open()
       : exprtk::igeneric_function<T>("S|SS")
       { details::perform_check<T>(); }
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
-         std::string file_name = to_str(string_t(parameters[0]));
-         std::string access;
+         const std::string file_name = to_str(string_t(parameters[0]));
 
          if (file_name.empty())
+         {
             return T(0);
+         }
 
-         if (0 == ps_index)
-            access = "r";
-         else if (0 == string_t(parameters[1]).size())
+         if ((1 == ps_index) && (0 == string_t(parameters[1]).size()))
+         {
             return T(0);
-         else
-            access = to_str(string_t(parameters[1]));
+         }
+
+         const std::string access =
+            (0 == ps_index) ? "r" : to_str(string_t(parameters[1]));
 
          details::file_descriptor* fd = new details::file_descriptor(file_name,access);
 
@@ -38610,9 +44101,11 @@ namespace exprtk
          {
             T t = T(0);
 
+            const std::size_t fd_size = sizeof(details::file_descriptor*);
+
             std::memcpy(reinterpret_cast<char*>(&t ),
                         reinterpret_cast<char*>(&fd),
-                        sizeof(void*));
+                        fd_size);
             return t;
          }
          else
@@ -38624,7 +44117,7 @@ namespace exprtk
    };
 
    template <typename T>
-   struct close : public exprtk::ifunction<T>
+   struct close exprtk_final : public exprtk::ifunction<T>
    {
       using exprtk::ifunction<T>::operator();
 
@@ -38632,7 +44125,7 @@ namespace exprtk
       : exprtk::ifunction<T>(1)
       { details::perform_check<T>(); }
 
-      inline T operator() (const T& v)
+      inline T operator() (const T& v) exprtk_override
       {
          details::file_descriptor* fd = details::make_handle(v);
 
@@ -38646,7 +44139,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class write : public exprtk::igeneric_function<T>
+   class write exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -38657,42 +44150,42 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       write()
       : igfun_t("TS|TST|TV|TVT")
       { details::perform_check<T>(); }
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          details::file_descriptor* fd = details::make_handle(scalar_t(parameters[0])());
 
-         std::size_t amount = 0;
-
          switch (ps_index)
          {
             case 0  : {
                          const string_t buffer(parameters[1]);
-                         amount = buffer.size();
+                         const std::size_t amount = buffer.size();
                          return T(fd->write(buffer, amount) ? 1 : 0);
                       }
 
             case 1  : {
                          const string_t buffer(parameters[1]);
-                         amount = std::min(buffer.size(),
+                         const std::size_t amount =
+                                  std::min(buffer.size(),
                                            static_cast<std::size_t>(scalar_t(parameters[2])()));
                          return T(fd->write(buffer, amount) ? 1 : 0);
                       }
 
             case 2  : {
                          const vector_t vec(parameters[1]);
-                         amount = vec.size();
+                         const std::size_t amount = vec.size();
                          return T(fd->write(vec, amount) ? 1 : 0);
                       }
 
             case 3  : {
                          const vector_t vec(parameters[1]);
-                         amount = std::min(vec.size(),
+                         const std::size_t amount =
+                                  std::min(vec.size(),
                                            static_cast<std::size_t>(scalar_t(parameters[2])()));
                          return T(fd->write(vec, amount) ? 1 : 0);
                       }
@@ -38703,7 +44196,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class read : public exprtk::igeneric_function<T>
+   class read exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -38714,42 +44207,42 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       read()
       : igfun_t("TS|TST|TV|TVT")
       { details::perform_check<T>(); }
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          details::file_descriptor* fd = details::make_handle(scalar_t(parameters[0])());
 
-         std::size_t amount = 0;
-
          switch (ps_index)
          {
             case 0  : {
                          string_t buffer(parameters[1]);
-                         amount = buffer.size();
+                         const std::size_t amount = buffer.size();
                          return T(fd->read(buffer,amount) ? 1 : 0);
                       }
 
             case 1  : {
                          string_t buffer(parameters[1]);
-                         amount = std::min(buffer.size(),
+                         const std::size_t amount =
+                                  std::min(buffer.size(),
                                            static_cast<std::size_t>(scalar_t(parameters[2])()));
                          return T(fd->read(buffer,amount) ? 1 : 0);
                       }
 
             case 2  : {
                          vector_t vec(parameters[1]);
-                         amount = vec.size();
+                         const std::size_t amount = vec.size();
                          return T(fd->read(vec,amount) ? 1 : 0);
                       }
 
             case 3  : {
                          vector_t vec(parameters[1]);
-                         amount = std::min(vec.size(),
+                         const std::size_t amount =
+                                  std::min(vec.size(),
                                            static_cast<std::size_t>(scalar_t(parameters[2])()));
                          return T(fd->read(vec,amount) ? 1 : 0);
                       }
@@ -38760,7 +44253,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class getline : public exprtk::igeneric_function<T>
+   class getline exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -38770,14 +44263,13 @@ namespace exprtk
       typedef typename generic_type::string_view    string_t;
       typedef typename generic_type::scalar_view    scalar_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       getline()
       : igfun_t("T",igfun_t::e_rtrn_string)
       { details::perform_check<T>(); }
 
-      inline T operator() (std::string& result,
-                           parameter_list_t parameters)
+      inline T operator() (std::string& result, parameter_list_t parameters) exprtk_override
       {
          details::file_descriptor* fd = details::make_handle(scalar_t(parameters[0])());
          return T(fd->getline(result) ? 1 : 0);
@@ -38785,7 +44277,7 @@ namespace exprtk
    };
 
    template <typename T>
-   struct eof : public exprtk::ifunction<T>
+   struct eof exprtk_final : public exprtk::ifunction<T>
    {
       using exprtk::ifunction<T>::operator();
 
@@ -38793,10 +44285,9 @@ namespace exprtk
       : exprtk::ifunction<T>(1)
       { details::perform_check<T>(); }
 
-      inline T operator() (const T& v)
+      inline T operator() (const T& v) exprtk_override
       {
          details::file_descriptor* fd = details::make_handle(v);
-
          return (fd->eof() ? T(1) : T(0));
       }
    };
@@ -38813,7 +44304,7 @@ namespace exprtk
 
       bool register_package(exprtk::symbol_table<T>& symtab)
       {
-         #define exprtk_register_function(FunctionName,FunctionType)                    \
+         #define exprtk_register_function(FunctionName, FunctionType)                   \
          if (!symtab.add_function(FunctionName,FunctionType))                           \
          {                                                                              \
             exprtk_debug((                                                              \
@@ -38822,12 +44313,12 @@ namespace exprtk
             return false;                                                               \
          }                                                                              \
 
-         exprtk_register_function("open"   ,o)
-         exprtk_register_function("close"  ,c)
-         exprtk_register_function("write"  ,w)
-         exprtk_register_function("read"   ,r)
-         exprtk_register_function("getline",g)
-         exprtk_register_function("eof"    ,e)
+         exprtk_register_function("open"    , o)
+         exprtk_register_function("close"   , c)
+         exprtk_register_function("write"   , w)
+         exprtk_register_function("read"    , r)
+         exprtk_register_function("getline" , g)
+         exprtk_register_function("eof"     , e)
          #undef exprtk_register_function
 
          return true;
@@ -38906,44 +44397,61 @@ namespace exprtk
    } // namespace exprtk::rtl::details
 
    template <typename T>
-   class all_true : public exprtk::igeneric_function<T>
+   class all_true exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
       typedef typename exprtk::igeneric_function<T> igfun_t;
       typedef typename igfun_t::parameter_list_t    parameter_list_t;
       typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       all_true()
-      : exprtk::igeneric_function<T>("V|VTT")
+      : exprtk::igeneric_function<T>("V|VTT|T*")
         /*
            Overloads:
            0. V   - vector
            1. VTT - vector, r0, r1
+           2. T*  - T....T
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
-         const vector_t vec(parameters[0]);
+         if (2 == ps_index)
+         {
+            for (std::size_t i = 0; i < parameters.size(); ++i)
+            {
+               if (scalar_t(parameters[i])() == T(0))
+               {
+                  return T(0);
+               }
+            }
+         }
+         else
+         {
+            const vector_t vec(parameters[0]);
 
-         std::size_t r0 = 0;
-         std::size_t r1 = vec.size() - 1;
+            std::size_t r0 = 0;
+            std::size_t r1 = vec.size() - 1;
 
-         if (
-              (1 == ps_index) &&
-              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
-            )
-            return std::numeric_limits<T>::quiet_NaN();
+            if (
+                 (1 == ps_index) &&
+                 !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+               )
+            {
+               return std::numeric_limits<T>::quiet_NaN();
+            }
 
-         for (std::size_t i = r0; i <= r1; ++i)
-         {
-            if (vec[i] == T(0))
+            for (std::size_t i = r0; i <= r1; ++i)
             {
-               return T(0);
+               if (vec[i] == T(0))
+               {
+                  return T(0);
+               }
             }
          }
 
@@ -38952,44 +44460,61 @@ namespace exprtk
    };
 
    template <typename T>
-   class all_false : public exprtk::igeneric_function<T>
+   class all_false exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
       typedef typename exprtk::igeneric_function<T> igfun_t;
       typedef typename igfun_t::parameter_list_t    parameter_list_t;
       typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       all_false()
-      : exprtk::igeneric_function<T>("V|VTT")
+      : exprtk::igeneric_function<T>("V|VTT|T*")
         /*
            Overloads:
            0. V   - vector
            1. VTT - vector, r0, r1
+           2. T*  - T....T
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
-         const vector_t vec(parameters[0]);
+         if (2 == ps_index)
+         {
+            for (std::size_t i = 0; i < parameters.size(); ++i)
+            {
+               if (scalar_t(parameters[i])() != T(0))
+               {
+                  return T(0);
+               }
+            }
+         }
+         else
+         {
+            const vector_t vec(parameters[0]);
 
-         std::size_t r0 = 0;
-         std::size_t r1 = vec.size() - 1;
+            std::size_t r0 = 0;
+            std::size_t r1 = vec.size() - 1;
 
-         if (
-              (1 == ps_index) &&
-              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
-            )
-            return std::numeric_limits<T>::quiet_NaN();
+            if (
+                 (1 == ps_index) &&
+                 !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+               )
+            {
+               return std::numeric_limits<T>::quiet_NaN();
+            }
 
-         for (std::size_t i = r0; i <= r1; ++i)
-         {
-            if (vec[i] != T(0))
+            for (std::size_t i = r0; i <= r1; ++i)
             {
-               return T(0);
+               if (vec[i] != T(0))
+               {
+                  return T(0);
+               }
             }
          }
 
@@ -38998,44 +44523,61 @@ namespace exprtk
    };
 
    template <typename T>
-   class any_true : public exprtk::igeneric_function<T>
+   class any_true exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
       typedef typename exprtk::igeneric_function<T> igfun_t;
       typedef typename igfun_t::parameter_list_t    parameter_list_t;
       typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       any_true()
-      : exprtk::igeneric_function<T>("V|VTT")
+      : exprtk::igeneric_function<T>("V|VTT|T*")
         /*
            Overloads:
            0. V   - vector
            1. VTT - vector, r0, r1
+           2. T*  - T....T
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
-         const vector_t vec(parameters[0]);
+         if (2 == ps_index)
+         {
+            for (std::size_t i = 0; i < parameters.size(); ++i)
+            {
+               if (scalar_t(parameters[i])() != T(0))
+               {
+                  return T(1);
+               }
+            }
+         }
+         else
+         {
+            const vector_t vec(parameters[0]);
 
-         std::size_t r0 = 0;
-         std::size_t r1 = vec.size() - 1;
+            std::size_t r0 = 0;
+            std::size_t r1 = vec.size() - 1;
 
-         if (
-              (1 == ps_index) &&
-              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
-            )
-            return std::numeric_limits<T>::quiet_NaN();
+            if (
+                 (1 == ps_index) &&
+                 !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+               )
+            {
+               return std::numeric_limits<T>::quiet_NaN();
+            }
 
-         for (std::size_t i = r0; i <= r1; ++i)
-         {
-            if (vec[i] != T(0))
+            for (std::size_t i = r0; i <= r1; ++i)
             {
-               return T(1);
+               if (vec[i] != T(0))
+               {
+                  return T(1);
+               }
             }
          }
 
@@ -39044,44 +44586,61 @@ namespace exprtk
    };
 
    template <typename T>
-   class any_false : public exprtk::igeneric_function<T>
+   class any_false exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
       typedef typename exprtk::igeneric_function<T> igfun_t;
       typedef typename igfun_t::parameter_list_t    parameter_list_t;
       typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       any_false()
-      : exprtk::igeneric_function<T>("V|VTT")
+      : exprtk::igeneric_function<T>("V|VTT|T*")
         /*
            Overloads:
            0. V   - vector
            1. VTT - vector, r0, r1
+           2. T*  - T....T
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
-         const vector_t vec(parameters[0]);
+         if (2 == ps_index)
+         {
+            for (std::size_t i = 0; i < parameters.size(); ++i)
+            {
+               if (scalar_t(parameters[i])() == T(0))
+               {
+                  return T(1);
+               }
+            }
+         }
+         else
+         {
+            const vector_t vec(parameters[0]);
 
-         std::size_t r0 = 0;
-         std::size_t r1 = vec.size() - 1;
+            std::size_t r0 = 0;
+            std::size_t r1 = vec.size() - 1;
 
-         if (
-              (1 == ps_index) &&
-              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
-            )
-            return std::numeric_limits<T>::quiet_NaN();
+            if (
+                 (1 == ps_index) &&
+                 !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+               )
+            {
+               return std::numeric_limits<T>::quiet_NaN();
+            }
 
-         for (std::size_t i = r0; i <= r1; ++i)
-         {
-            if (vec[i] == T(0))
+            for (std::size_t i = r0; i <= r1; ++i)
             {
-               return T(1);
+               if (vec[i] == T(0))
+               {
+                  return T(1);
+               }
             }
          }
 
@@ -39090,44 +44649,58 @@ namespace exprtk
    };
 
    template <typename T>
-   class count : public exprtk::igeneric_function<T>
+   class count exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
       typedef typename exprtk::igeneric_function<T> igfun_t;
       typedef typename igfun_t::parameter_list_t    parameter_list_t;
       typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       count()
-      : exprtk::igeneric_function<T>("V|VTT")
+      : exprtk::igeneric_function<T>("V|VTT|T*")
         /*
            Overloads:
            0. V   - vector
            1. VTT - vector, r0, r1
+           2. T*  - T....T
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
-         const vector_t vec(parameters[0]);
+         std::size_t cnt = 0;
 
-         std::size_t r0 = 0;
-         std::size_t r1 = vec.size() - 1;
+         if (2 == ps_index)
+         {
+            for (std::size_t i = 0; i < parameters.size(); ++i)
+            {
+               if (scalar_t(parameters[i])() != T(0)) ++cnt;
+            }
+         }
+         else
+         {
+            const vector_t vec(parameters[0]);
 
-         if (
-              (1 == ps_index) &&
-              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
-            )
-            return std::numeric_limits<T>::quiet_NaN();
+            std::size_t r0 = 0;
+            std::size_t r1 = vec.size() - 1;
 
-         std::size_t cnt = 0;
+            if (
+                 (1 == ps_index) &&
+                 !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+               )
+            {
+               return std::numeric_limits<T>::quiet_NaN();
+            }
 
-         for (std::size_t i = r0; i <= r1; ++i)
-         {
-            if (vec[i] != T(0)) ++cnt;
+            for (std::size_t i = r0; i <= r1; ++i)
+            {
+               if (vec[i] != T(0)) ++cnt;
+            }
          }
 
          return T(cnt);
@@ -39135,7 +44708,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class copy : public exprtk::igeneric_function<T>
+   class copy exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39145,7 +44718,7 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       copy()
       : exprtk::igeneric_function<T>("VV|VTTVTT")
@@ -39156,7 +44729,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          const vector_t x(parameters[0]);
                vector_t y(parameters[(0 == ps_index) ? 1 : 3]);
@@ -39178,14 +44751,17 @@ namespace exprtk
 
          const std::size_t n = std::min(xr1 - xr0 + 1, yr1 - yr0 + 1);
 
-         std::copy(x.begin() + xr0, x.begin() + xr0 + n, y.begin() + yr0);
+         std::copy(
+            x.begin() + xr0,
+            x.begin() + xr0 + n,
+            y.begin() + yr0);
 
          return T(n);
       }
    };
 
    template <typename T>
-   class rol : public exprtk::igeneric_function<T>
+   class rol exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39195,7 +44771,7 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       rol()
       : exprtk::igeneric_function<T>("VT|VTTT")
@@ -39206,7 +44782,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          vector_t vec(parameters[0]);
 
@@ -39236,7 +44812,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class ror : public exprtk::igeneric_function<T>
+   class ror exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39246,7 +44822,7 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       ror()
       : exprtk::igeneric_function<T>("VT|VTTT")
@@ -39257,7 +44833,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          vector_t vec(parameters[0]);
 
@@ -39274,8 +44850,8 @@ namespace exprtk
             )
             return T(0);
 
-         std::size_t dist  = r1 - r0 + 1;
-         std::size_t shift = (dist - (n % dist)) % dist;
+         const std::size_t dist  = r1 - r0 + 1;
+         const std::size_t shift = (dist - (n % dist)) % dist;
 
          std::rotate(
             vec.begin() + r0,
@@ -39287,7 +44863,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class shift_left : public exprtk::igeneric_function<T>
+   class reverse exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39297,7 +44873,48 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
+
+      reverse()
+      : exprtk::igeneric_function<T>("V|VTT")
+        /*
+           Overloads:
+           0. V   - vector
+           1. VTT - vector, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
+      {
+         vector_t vec(parameters[0]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (
+              (1 == ps_index) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+            )
+            return T(0);
+
+         std::reverse(vec.begin() + r0, vec.begin() + r1 + 1);
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class shift_left exprtk_final : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using igfun_t::operator();
 
       shift_left()
       : exprtk::igeneric_function<T>("VT|VTTT")
@@ -39308,7 +44925,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          vector_t vec(parameters[0]);
 
@@ -39325,7 +44942,7 @@ namespace exprtk
             )
             return T(0);
 
-         const std::size_t dist  = r1 - r0 + 1;
+         const std::size_t dist = r1 - r0 + 1;
 
          if (n > dist)
             return T(0);
@@ -39335,7 +44952,7 @@ namespace exprtk
             vec.begin() + r0 + n,
             vec.begin() + r1 + 1);
 
-         for (std::size_t i = r1 - n + 1; i <= r1; ++i)
+         for (std::size_t i = r1 - n + 1ULL; i <= r1; ++i)
          {
             vec[i] = T(0);
          }
@@ -39345,7 +44962,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class shift_right : public exprtk::igeneric_function<T>
+   class shift_right exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39355,7 +44972,7 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       shift_right()
       : exprtk::igeneric_function<T>("VT|VTTT")
@@ -39366,7 +44983,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          vector_t vec(parameters[0]);
 
@@ -39383,7 +45000,7 @@ namespace exprtk
             )
             return T(0);
 
-         const std::size_t dist  = r1 - r0 + 1;
+         const std::size_t dist = r1 - r0 + 1;
 
          if (n > dist)
             return T(0);
@@ -39405,7 +45022,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class sort : public exprtk::igeneric_function<T>
+   class sort exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39415,7 +45032,7 @@ namespace exprtk
       typedef typename generic_type::string_view    string_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       sort()
       : exprtk::igeneric_function<T>("V|VTT|VS|VSTT")
@@ -39428,7 +45045,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          vector_t vec(parameters[0]);
 
@@ -39453,16 +45070,22 @@ namespace exprtk
          }
 
          if (ascending)
-            std::sort(vec.begin() + r0, vec.begin() + r1 + 1, std::less<T>   ());
+            std::sort(
+               vec.begin() + r0,
+               vec.begin() + r1 + 1,
+               std::less<T>());
          else
-            std::sort(vec.begin() + r0, vec.begin() + r1 + 1, std::greater<T>());
+            std::sort(
+               vec.begin() + r0,
+               vec.begin() + r1 + 1,
+               std::greater<T>());
 
          return T(1);
       }
    };
 
    template <typename T>
-   class nthelement : public exprtk::igeneric_function<T>
+   class nthelement exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39472,7 +45095,7 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       nthelement()
       : exprtk::igeneric_function<T>("VT|VTTT")
@@ -39483,7 +45106,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          vector_t vec(parameters[0]);
 
@@ -39495,16 +45118,21 @@ namespace exprtk
             return T(0);
 
          if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0))
+         {
             return std::numeric_limits<T>::quiet_NaN();
+         }
 
-         std::nth_element(vec.begin() + r0, vec.begin() + r0 + n , vec.begin() + r1 + 1);
+         std::nth_element(
+            vec.begin() + r0,
+            vec.begin() + r0 + n ,
+            vec.begin() + r1 + 1);
 
          return T(1);
       }
    };
 
    template <typename T>
-   class iota : public exprtk::igeneric_function<T>
+   class assign exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39514,41 +45142,101 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
+
+      assign()
+      : exprtk::igeneric_function<T>("VT|VTTT|VTTTT")
+        /*
+           Overloads:
+           0. VT    - vector, V
+           1. VTTT  - vector, V, r0, r1
+           2. VTTTT - vector, V, r0, r1, SS
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
+      {
+         vector_t vec(parameters[0]);
+
+         const T assign_value = scalar_t(parameters[1]);
+
+         const std::size_t step_size = (2 != ps_index) ? 1 :
+                                       static_cast<std::size_t>(scalar_t(parameters.back())());
+
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (
+              ((ps_index == 1) || (ps_index == 2)) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0)
+            )
+         {
+            return T(0);
+         }
+
+         for (std::size_t i = r0; i <= r1; i += step_size)
+         {
+            vec[i] = assign_value;
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class iota exprtk_final : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using igfun_t::operator();
 
       iota()
-      : exprtk::igeneric_function<T>("VT|VTT|VTTT|VTTTT")
+      : exprtk::igeneric_function<T>("VTT|VT|VTTTT|VTTT")
         /*
            Overloads:
-           0. VT    - vector, increment
-           1. VTT   - vector, increment, base
-           2. VTTTT - vector, increment, r0, r1
-           3. VTTTT - vector, increment, base, r0, r1
+           0. VTT  - vector, SV, SS
+           1. VT   - vector, SV, SS (+1)
+           2. VTTT - vector, r0, r1, SV, SS
+           3. VTT  - vector, r0, r1, SV, SS (+1)
+
+           Where:
+           1. SV - Start value
+           2. SS - Step size
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          vector_t vec(parameters[0]);
 
-         T increment = scalar_t(parameters[1])();
-         T base      = ((1 == ps_index) || (3 == ps_index)) ? scalar_t(parameters[2])() : T(0);
+         const T start_value = (ps_index <= 1) ?
+                               scalar_t(parameters[1]) :
+                               scalar_t(parameters[3]) ;
+
+         const T step_size = ((0 == ps_index) || (2 == ps_index)) ?
+                             scalar_t(parameters.back())() :
+                             T(1) ;
 
          std::size_t r0 = 0;
          std::size_t r1 = vec.size() - 1;
 
-         if ((2 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0))
-            return std::numeric_limits<T>::quiet_NaN();
-         else if ((3 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 3, 4, 0))
-            return std::numeric_limits<T>::quiet_NaN();
-         else
+         if (
+              ((ps_index == 2) || (ps_index == 3)) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+            )
          {
-            long long j = 0;
+            return T(0);
+         }
 
-            for (std::size_t i = r0; i <= r1; ++i, ++j)
-            {
-               vec[i] = base + (increment * j);
-            }
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            vec[i] = start_value + ((i - r0) * step_size);
          }
 
          return T(1);
@@ -39556,40 +45244,50 @@ namespace exprtk
    };
 
    template <typename T>
-   class sumk : public exprtk::igeneric_function<T>
+   class sumk exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
       typedef typename exprtk::igeneric_function<T> igfun_t;
       typedef typename igfun_t::parameter_list_t    parameter_list_t;
       typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       sumk()
-      : exprtk::igeneric_function<T>("V|VTT")
+      : exprtk::igeneric_function<T>("V|VTT|VTTT")
         /*
            Overloads:
-           0. V   - vector
-           1. VTT - vector, r0, r1
+           0. V    - vector
+           1. VTT  - vector, r0, r1
+           2. VTTT - vector, r0, r1, stride
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          const vector_t vec(parameters[0]);
 
+         const std::size_t stride = (2 != ps_index) ? 1 :
+                                    static_cast<std::size_t>(scalar_t(parameters[3])());
+
          std::size_t r0 = 0;
          std::size_t r1 = vec.size() - 1;
 
-         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0))
+         if (
+              ((1 == ps_index) || (2 == ps_index)) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+            )
+         {
             return std::numeric_limits<T>::quiet_NaN();
+         }
 
          T result = T(0);
          T error  = T(0);
 
-         for (std::size_t i = r0; i <= r1; ++i)
+         for (std::size_t i = r0; i <= r1; i += stride)
          {
             details::kahan_sum(result, error, vec[i]);
          }
@@ -39599,7 +45297,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class axpy : public exprtk::igeneric_function<T>
+   class axpy exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39609,7 +45307,7 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       axpy()
       : exprtk::igeneric_function<T>("TVV|TVVTT")
@@ -39621,7 +45319,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          const vector_t x(parameters[1]);
                vector_t y(parameters[2]);
@@ -39646,7 +45344,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class axpby : public exprtk::igeneric_function<T>
+   class axpby exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39656,7 +45354,7 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       axpby()
       : exprtk::igeneric_function<T>("TVTV|TVTVTT")
@@ -39668,7 +45366,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          const vector_t x(parameters[1]);
                vector_t y(parameters[3]);
@@ -39694,7 +45392,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class axpyz : public exprtk::igeneric_function<T>
+   class axpyz exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39704,7 +45402,7 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       axpyz()
       : exprtk::igeneric_function<T>("TVVV|TVVVTT")
@@ -39716,7 +45414,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          const vector_t x(parameters[1]);
          const vector_t y(parameters[2]);
@@ -39725,7 +45423,7 @@ namespace exprtk
          std::size_t r0 = 0;
          std::size_t r1 = std::min(x.size(),y.size()) - 1;
 
-         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 3, 4, 1))
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 4, 5, 1))
             return std::numeric_limits<T>::quiet_NaN();
          else if (helper::invalid_range(y, r0, r1))
             return std::numeric_limits<T>::quiet_NaN();
@@ -39744,7 +45442,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class axpbyz : public exprtk::igeneric_function<T>
+   class axpbyz exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39754,7 +45452,7 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       axpbyz()
       : exprtk::igeneric_function<T>("TVTVV|TVTVVTT")
@@ -39766,7 +45464,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          const vector_t x(parameters[1]);
          const vector_t y(parameters[3]);
@@ -39775,7 +45473,7 @@ namespace exprtk
          std::size_t r0 = 0;
          std::size_t r1 = std::min(x.size(),y.size()) - 1;
 
-         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 4, 5, 1))
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 5, 6, 1))
             return std::numeric_limits<T>::quiet_NaN();
          else if (helper::invalid_range(y, r0, r1))
             return std::numeric_limits<T>::quiet_NaN();
@@ -39795,7 +45493,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class axpbz : public exprtk::igeneric_function<T>
+   class axpbsy exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39805,7 +45503,110 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
+
+      axpbsy()
+      : exprtk::igeneric_function<T>("TVTTV|TVTTVTT")
+        /*
+           y <- ax + by
+           Overloads:
+           0. TVTVV   - a, x(vector), b, shift, y(vector), z(vector)
+           1. TVTVVTT - a, x(vector), b, shift, y(vector), z(vector), r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
+      {
+         const vector_t x(parameters[1]);
+               vector_t y(parameters[4]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = std::min(x.size(),y.size()) - 1;
+
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 5, 6, 1))
+            return std::numeric_limits<T>::quiet_NaN();
+         else if (helper::invalid_range(y, r0, r1))
+            return std::numeric_limits<T>::quiet_NaN();
+
+         const T a = scalar_t(parameters[0])();
+         const T b = scalar_t(parameters[2])();
+
+         const std::size_t s = static_cast<std::size_t>(scalar_t(parameters[3])());
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            y[i] = (a * x[i]) + (b * y[i + s]);
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class axpbsyz exprtk_final : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using igfun_t::operator();
+
+      axpbsyz()
+      : exprtk::igeneric_function<T>("TVTTVV|TVTTVVTT")
+        /*
+           z <- ax + by
+           Overloads:
+           0. TVTVV   - a, x(vector), b, shift, y(vector), z(vector)
+           1. TVTVVTT - a, x(vector), b, shift, y(vector), z(vector), r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
+      {
+         const vector_t x(parameters[1]);
+         const vector_t y(parameters[4]);
+               vector_t z(parameters[5]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = std::min(x.size(),y.size()) - 1;
+
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 6, 7, 1))
+            return std::numeric_limits<T>::quiet_NaN();
+         else if (helper::invalid_range(y, r0, r1))
+            return std::numeric_limits<T>::quiet_NaN();
+         else if (helper::invalid_range(z, r0, r1))
+            return std::numeric_limits<T>::quiet_NaN();
+
+         const T a = scalar_t(parameters[0])();
+         const T b = scalar_t(parameters[2])();
+
+         const std::size_t s = static_cast<std::size_t>(scalar_t(parameters[3])());
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            z[i] = (a * x[i]) + (b * y[i + s]);
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class axpbz exprtk_final : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using igfun_t::operator();
 
       axpbz()
       : exprtk::igeneric_function<T>("TVTV|TVTVTT")
@@ -39817,7 +45618,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          const vector_t x(parameters[1]);
                vector_t z(parameters[3]);
@@ -39843,7 +45644,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class dot : public exprtk::igeneric_function<T>
+   class diff exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39853,7 +45654,55 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
+
+      diff()
+      : exprtk::igeneric_function<T>("VV|VVT")
+        /*
+           x_(i - stride) - x_i
+           Overloads:
+           0. VV  - x(vector), y(vector)
+           1. VVT - x(vector), y(vector), stride
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
+      {
+         const vector_t x(parameters[0]);
+               vector_t y(parameters[1]);
+
+         const std::size_t r0 = 0;
+         const std::size_t r1 = std::min(x.size(),y.size()) - 1;
+
+         const std::size_t stride = (1 != ps_index) ? 1 :
+                                    std::min(r1,static_cast<std::size_t>(scalar_t(parameters[2])()));
+
+         for (std::size_t i = 0; i < stride; ++i)
+         {
+            y[i] = std::numeric_limits<T>::quiet_NaN();
+         }
+
+         for (std::size_t i = (r0 + stride); i <= r1; ++i)
+         {
+            y[i] = x[i] - x[i - stride];
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class dot exprtk_final : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using igfun_t::operator();
 
       dot()
       : exprtk::igeneric_function<T>("VV|VVTT")
@@ -39864,7 +45713,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          const vector_t x(parameters[0]);
          const vector_t y(parameters[1]);
@@ -39889,7 +45738,7 @@ namespace exprtk
    };
 
    template <typename T>
-   class dotk : public exprtk::igeneric_function<T>
+   class dotk exprtk_final : public exprtk::igeneric_function<T>
    {
    public:
 
@@ -39899,7 +45748,7 @@ namespace exprtk
       typedef typename generic_type::scalar_view    scalar_t;
       typedef typename generic_type::vector_view    vector_t;
 
-      using exprtk::igeneric_function<T>::operator();
+      using igfun_t::operator();
 
       dotk()
       : exprtk::igeneric_function<T>("VV|VVTT")
@@ -39910,7 +45759,7 @@ namespace exprtk
         */
       {}
 
-      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
       {
          const vector_t x(parameters[0]);
          const vector_t y(parameters[1]);
@@ -39935,34 +45784,159 @@ namespace exprtk
       }
    };
 
+   template <typename T>
+   class threshold_below exprtk_final : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using igfun_t::operator();
+
+      threshold_below()
+      : exprtk::igeneric_function<T>("VTT|VTTTT")
+      /*
+         Overloads:
+         0. VTT   - vector, TV, SV
+         1. VTTTT - vector, r0, r1, TV, SV
+
+         Where:
+         TV - Threshold value
+         SV - Snap-to value
+      */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
+      {
+         vector_t vec(parameters[0]);
+
+         const T threshold_value = (0 == ps_index) ?
+                                   scalar_t(parameters[1]) :
+                                   scalar_t(parameters[3]) ;
+
+         const T snap_value = scalar_t(parameters.back());
+
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (
+              (1 == ps_index) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+            )
+         {
+            return T(0);
+         }
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            if (vec[i] < threshold_value)
+            {
+               vec[i] = snap_value;
+            }
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class threshold_above exprtk_final : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using igfun_t::operator();
+
+      threshold_above()
+      : exprtk::igeneric_function<T>("VTT|VTTTT")
+      /*
+         Overloads:
+         0. VTT   - vector, TV, SV
+         1. VTTTT - vector, r0, r1, TV, SV
+
+         Where:
+         TV - Threshold value
+         SV - Snap-to value
+      */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters) exprtk_override
+      {
+         vector_t vec(parameters[0]);
+
+         const T threshold_value = (0 == ps_index) ?
+                                   scalar_t(parameters[1]) :
+                                   scalar_t(parameters[3]) ;
+
+         const T snap_value = scalar_t(parameters.back());
+
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (
+              (1 == ps_index) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+            )
+         {
+            return T(0);
+         }
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            if (vec[i] > threshold_value)
+            {
+               vec[i] = snap_value;
+            }
+         }
+
+         return T(1);
+      }
+   };
+
    template <typename T>
    struct package
    {
-      all_true   <T> at;
-      all_false  <T> af;
-      any_true   <T> nt;
-      any_false  <T> nf;
-      count      <T>  c;
-      copy       <T> cp;
-      rol        <T> rl;
-      ror        <T> rr;
-      shift_left <T> sl;
-      shift_right<T> sr;
-      sort       <T> st;
-      nthelement <T> ne;
-      iota       <T> ia;
-      sumk       <T> sk;
-      axpy       <T> b1_axpy;
-      axpby      <T> b1_axpby;
-      axpyz      <T> b1_axpyz;
-      axpbyz     <T> b1_axpbyz;
-      axpbz      <T> b1_axpbz;
-      dot        <T> dt;
-      dotk       <T> dtk;
+      all_true       <T> at;
+      all_false      <T> af;
+      any_true       <T> nt;
+      any_false      <T> nf;
+      count          <T>  c;
+      copy           <T> cp;
+      rol            <T> rl;
+      ror            <T> rr;
+      reverse        <T> rev;
+      shift_left     <T> sl;
+      shift_right    <T> sr;
+      sort           <T> st;
+      nthelement     <T> ne;
+      assign         <T> an;
+      iota           <T> ia;
+      sumk           <T> sk;
+      axpy           <T> b1_axpy;
+      axpby          <T> b1_axpby;
+      axpyz          <T> b1_axpyz;
+      axpbyz         <T> b1_axpbyz;
+      axpbsy         <T> b1_axpbsy;
+      axpbsyz        <T> b1_axpbsyz;
+      axpbz          <T> b1_axpbz;
+      diff           <T> df;
+      dot            <T> dt;
+      dotk           <T> dtk;
+      threshold_above<T> ta;
+      threshold_below<T> tb;
 
       bool register_package(exprtk::symbol_table<T>& symtab)
       {
-         #define exprtk_register_function(FunctionName,FunctionType)                  \
+         #define exprtk_register_function(FunctionName, FunctionType)                 \
          if (!symtab.add_function(FunctionName,FunctionType))                         \
          {                                                                            \
             exprtk_debug((                                                            \
@@ -39971,29 +45945,36 @@ namespace exprtk
             return false;                                                             \
          }                                                                            \
 
-         exprtk_register_function("all_true"     ,at)
-         exprtk_register_function("all_false"    ,af)
-         exprtk_register_function("any_true"     ,nt)
-         exprtk_register_function("any_false"    ,nf)
-         exprtk_register_function("count"        , c)
-         exprtk_register_function("copy"         ,cp)
-         exprtk_register_function("rotate_left"  ,rl)
-         exprtk_register_function("rol"          ,rl)
-         exprtk_register_function("rotate_right" ,rr)
-         exprtk_register_function("ror"          ,rr)
-         exprtk_register_function("shftl"        ,sl)
-         exprtk_register_function("shftr"        ,sr)
-         exprtk_register_function("sort"         ,st)
-         exprtk_register_function("nth_element"  ,ne)
-         exprtk_register_function("iota"         ,ia)
-         exprtk_register_function("sumk"         ,sk)
-         exprtk_register_function("axpy"    ,b1_axpy)
-         exprtk_register_function("axpby"  ,b1_axpby)
-         exprtk_register_function("axpyz"  ,b1_axpyz)
-         exprtk_register_function("axpbyz",b1_axpbyz)
-         exprtk_register_function("axpbz"  ,b1_axpbz)
-         exprtk_register_function("dot"          ,dt)
-         exprtk_register_function("dotk"        ,dtk)
+         exprtk_register_function("all_true"        , at        )
+         exprtk_register_function("all_false"       , af        )
+         exprtk_register_function("any_true"        , nt        )
+         exprtk_register_function("any_false"       , nf        )
+         exprtk_register_function("count"           , c         )
+         exprtk_register_function("copy"            , cp        )
+         exprtk_register_function("rotate_left"     , rl        )
+         exprtk_register_function("rol"             , rl        )
+         exprtk_register_function("rotate_right"    , rr        )
+         exprtk_register_function("ror"             , rr        )
+         exprtk_register_function("reverse"         , rev       )
+         exprtk_register_function("shftl"           , sl        )
+         exprtk_register_function("shftr"           , sr        )
+         exprtk_register_function("sort"            , st        )
+         exprtk_register_function("nth_element"     , ne        )
+         exprtk_register_function("assign"          , an        )
+         exprtk_register_function("iota"            , ia        )
+         exprtk_register_function("sumk"            , sk        )
+         exprtk_register_function("axpy"            , b1_axpy   )
+         exprtk_register_function("axpby"           , b1_axpby  )
+         exprtk_register_function("axpyz"           , b1_axpyz  )
+         exprtk_register_function("axpbyz"          , b1_axpbyz )
+         exprtk_register_function("axpbsy"          , b1_axpbsy )
+         exprtk_register_function("axpbsyz"         , b1_axpbsyz)
+         exprtk_register_function("axpbz"           , b1_axpbz  )
+         exprtk_register_function("diff"            , df        )
+         exprtk_register_function("dot"             , dt        )
+         exprtk_register_function("dotk"            , dtk       )
+         exprtk_register_function("threshold_above" , ta        )
+         exprtk_register_function("threshold_below" , tb        )
          #undef exprtk_register_function
 
          return true;
@@ -40009,18 +45990,22 @@ namespace exprtk
 {
    namespace information
    {
-      static const char* library = "Mathematical Expression Toolkit";
-      static const char* version = "2.718281828459045235360287471352"
-                                   "66249775724709369995957496696762"
-                                   "77240766303535475945713821785251"
-                                   "66427427466391932003059921817413";
-      static const char* date    = "20210101";
+      using ::exprtk::details::char_cptr;
+
+      static char_cptr library = "Mathematical Expression Toolkit";
+      static char_cptr version = "2.718281828459045235360287471352662497757"
+                                 "24709369995957496696762772407663035354759"
+                                 "45713821785251664274274663919320030599218"
+                                 "17413596629043572900334295260595630738132";
+      static char_cptr date    = "20240101";
+      static char_cptr min_cpp = "199711L";
 
       static inline std::string data()
       {
          static const std::string info_str = std::string(library) +
                                              std::string(" v") + std::string(version) +
-                                             std::string(" (") + date + std::string(")");
+                                             std::string(" (") + date + std::string(")") +
+                                             std::string(" (") + min_cpp + std::string(")");
          return info_str;
       }
 
@@ -40034,16 +46019,28 @@ namespace exprtk
    #undef exprtk_error_location
    #endif
 
-   #ifdef exprtk_disable_fallthrough_begin
-   #undef exprtk_disable_fallthrough_begin
+   #ifdef exprtk_fallthrough
+   #undef exprtk_fallthrough
+   #endif
+
+   #ifdef exprtk_override
+   #undef exprtk_override
+   #endif
+
+   #ifdef exprtk_final
+   #undef exprtk_final
+   #endif
+
+   #ifdef exprtk_delete
+   #undef exprtk_delete
    #endif
 
-   #ifdef exprtk_disable_fallthrough_end
-   #undef exprtk_disable_fallthrough_end
+   #ifdef exprtk_default
+   #undef exprtk_default
    #endif
 
 } // namespace exprtk
 
-//NOLINTEND(modernize-use-nullptr)
+//NOLINTEND(modernize-*,readability-*,performance-no-int-to-ptr,bugprone-empty-catch,bugprone-inc-dec-in-conditions)
 
 #endif
diff --git a/src/gpu/FieldIndexing.impl.h b/src/gpu/FieldIndexing.impl.h
index a8c9feccfbed0e12b015fe37dadac4aeaa803450..7ec1bb86a00d265c4158f3d37f170caa9b09735d 100644
--- a/src/gpu/FieldIndexing.impl.h
+++ b/src/gpu/FieldIndexing.impl.h
@@ -48,11 +48,11 @@ FieldIndexing<T>::FieldIndexing ( const GPUField<T> & field,
       {
          gpuDeviceProp prop;
          int count;
-         gpuGetDeviceCount(&count);
+         WALBERLA_GPU_CHECK(gpuGetDeviceCount(&count));
          int threadsPerBlock = std::numeric_limits< int >::max();
          for (int i = 0; i < count; i++)
          {
-            gpuGetDeviceProperties(&prop, i);
+            WALBERLA_GPU_CHECK(gpuGetDeviceProperties(&prop, i));
             threadsPerBlock = std::min(prop.maxThreadsPerBlock, threadsPerBlock);
          }
          WALBERLA_ASSERT_LESS(int_c(blockDim_.x), threadsPerBlock,
diff --git a/src/gpu/GPUWrapper.h b/src/gpu/GPUWrapper.h
index d4893da8b7ed123596fe18be62186670f990491d..7d103c298f691488817441132d3f0413b26d9ac9 100644
--- a/src/gpu/GPUWrapper.h
+++ b/src/gpu/GPUWrapper.h
@@ -35,6 +35,7 @@
 
     #define gpuMalloc cudaMalloc
     #define gpuMallocHost cudaMallocHost
+    #define gpuMallocManaged cudaMallocManaged
     #define gpuHostAllocDefault cudaHostAllocDefault
     #define gpuHostAlloc cudaHostAlloc
     #define gpuMemcpyHostToDevice cudaMemcpyHostToDevice
@@ -45,6 +46,7 @@
     #define gpuMemcpy3D cudaMemcpy3D
     #define gpuMemcpy3DParms cudaMemcpy3DParms
     #define gpuMemcpy3DAsync cudaMemcpy3DAsync
+    #define gpuMemset cudaMemset
 
     #define gpuMemset cudaMemset
     #define gpuMemsetAsync cudaMemsetAsync
@@ -99,6 +101,7 @@
 
     #define gpuMalloc hipMalloc
     #define gpuMallocHost hipHostMalloc
+    #define gpuMallocManaged hipMallocManaged
     #define gpuHostAllocDefault hipHostMallocDefault
     // warning: 'hipHostAlloc' is deprecated: use hipHostMalloc insteadwarning: 'hipHostAlloc' is deprecated: use hipHostMalloc instead
     #define gpuHostAlloc hipHostMalloc
@@ -110,6 +113,7 @@
     #define gpuMemcpy3D hipMemcpy3D
     #define gpuMemcpy3DParms hipMemcpy3DParms
     #define gpuMemcpy3DAsync hipMemcpy3DAsync
+    #define gpuMemset hipMemset
 
     #define gpuMemset hipMemset
     #define gpuMemsetAsync hipMemsetAsync
@@ -144,7 +148,7 @@
 
     #define gpuGetDeviceCount hipGetDeviceCount
     #define gpuSetDevice hipSetDevice
-    #define gpuDeviceProp hipDeviceProp
+    #define gpuDeviceProp hipDeviceProp_t
     #define gpuGetDeviceProperties hipGetDeviceProperties
 
     #define gpuLaunchKernel hipLaunchKernel
diff --git a/src/gpu/Kernel.h b/src/gpu/Kernel.h
index f6c2eb687a1d54e6aea2b21f80bfd200d05b371f..59366e5bc6d2bcdbd990cfd8d968a07121efb904 100644
--- a/src/gpu/Kernel.h
+++ b/src/gpu/Kernel.h
@@ -145,6 +145,18 @@ namespace gpu
       CHECK_PARAMETER_FUNC(5)
       CHECK_PARAMETER_FUNC(6)
       CHECK_PARAMETER_FUNC(7)
+      CHECK_PARAMETER_FUNC(8)
+      CHECK_PARAMETER_FUNC(9)
+      CHECK_PARAMETER_FUNC(10)
+      CHECK_PARAMETER_FUNC(11)
+      CHECK_PARAMETER_FUNC(12)
+      CHECK_PARAMETER_FUNC(13)
+      CHECK_PARAMETER_FUNC(14)
+      CHECK_PARAMETER_FUNC(15)
+      CHECK_PARAMETER_FUNC(16)
+      CHECK_PARAMETER_FUNC(17)
+      CHECK_PARAMETER_FUNC(18)
+      CHECK_PARAMETER_FUNC(19)
 
       #undef CHECK_PARAMETER_FUNC
 
@@ -256,6 +268,18 @@ namespace gpu
          case 5: return checkParameter5<T>();
          case 6: return checkParameter6<T>();
          case 7: return checkParameter7<T>();
+         case 8: return checkParameter8<T>();
+         case 9: return checkParameter9<T>();
+         case 10: return checkParameter10<T>();
+         case 11: return checkParameter11<T>();
+         case 12: return checkParameter12<T>();
+         case 13: return checkParameter13<T>();
+         case 14: return checkParameter14<T>();
+         case 15: return checkParameter15<T>();
+         case 16: return checkParameter16<T>();
+         case 17: return checkParameter17<T>();
+         case 18: return checkParameter18<T>();
+         case 19: return checkParameter19<T>();
          default:
             WALBERLA_ABORT("Too many parameters passed to kernel")
       }
diff --git a/src/lbm/PerformanceLogger.h b/src/lbm/PerformanceLogger.h
index 337cf6832e13ab68adb17697ffa1a9f17ab0f7de..13b2a4601cb306bbd2a870381c7a9b0164bd482f 100644
--- a/src/lbm/PerformanceLogger.h
+++ b/src/lbm/PerformanceLogger.h
@@ -167,7 +167,9 @@ void PerformanceLogger<FlagField_T>::getBestResultsForSQLOnRoot( std::map< std::
                                                                  std::map< std::string, double > &      realProperties,
                                                                  std::map< std::string, std::string > & stringProperties )
 {
+   timer_.end();
    performanceEvaluation_.getResultsForSQLOnRoot( integerProperties, realProperties, stringProperties, interval_, getTiming( MAX ) );
+   timer_.start();
 }
 
 
diff --git a/src/lbm_mesapd_coupling/CMakeLists.txt b/src/lbm_mesapd_coupling/CMakeLists.txt
index bde22da3f4a2f4b4f6edcaadc6de8f562292b54f..1e04ba7fd21b3dcf4e4ffc3518c833a4460205ed 100644
--- a/src/lbm_mesapd_coupling/CMakeLists.txt
+++ b/src/lbm_mesapd_coupling/CMakeLists.txt
@@ -12,8 +12,20 @@ target_sources( lbm_mesapd_coupling
       DataTypes.h
       )
 
+# Maximum number of particles that may overlap with a cell. For fully resolved particles, 2 should normally be
+# sufficient (for a sufficiently high stiffness in the DEM).
+set(MAX_PARTICLES_PER_CELL 2)
+add_custom_target(MAX_PARTICLES_PER_CELL) # Make it a target such that the code generation runs again if changed
+target_sources( lbm_mesapd_coupling
+        PRIVATE
+        DataTypesCodegen.h
+        )
+target_compile_definitions(lbm_mesapd_coupling PUBLIC MAX_PARTICLES_PER_CELL=${MAX_PARTICLES_PER_CELL})
+
+
 add_subdirectory( amr )
 add_subdirectory( momentum_exchange_method )
+add_subdirectory( overlapping )
 add_subdirectory( partially_saturated_cells_method )
 add_subdirectory( utility )
 add_subdirectory( mapping )
diff --git a/src/lbm_mesapd_coupling/DataTypesCodegen.h b/src/lbm_mesapd_coupling/DataTypesCodegen.h
new file mode 100644
index 0000000000000000000000000000000000000000..99583a6131a846193826d968e9b8695c5d51d4f6
--- /dev/null
+++ b/src/lbm_mesapd_coupling/DataTypesCodegen.h
@@ -0,0 +1,132 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file DataTypesGPU.h
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/DataTypes.h"
+
+#include "field/AddToStorage.h"
+#include "field/GhostLayerField.h"
+
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+#   include "gpu/AddGPUFieldToStorage.h"
+#   include "gpu/GPUField.h"
+#endif
+
+namespace walberla
+{
+namespace lbm_mesapd_coupling
+{
+namespace psm
+{
+namespace gpu
+{
+
+const uint MaxParticlesPerCell = MAX_PARTICLES_PER_CELL; // MAX_PARTICLES_PER_CELL comes from CMake
+
+// nOverlappingParticlesField is used to store the amount of overlapping particles per cell
+// B denotes the local weighting factor and is calculated by taking the sum of all local particle
+// weighting factor Bs. The naming of the variables is based on the following paper:
+// https://doi.org/10.1016/j.compfluid.2017.05.033
+// idxField is used to store the indices of the overlapping particles
+// particleVelocitiesField is used to store the velocities of the overlapping particles evaluated at the cell center
+// particleForcesField is used to store the hydrodynamic forces of the cell acting on the overlapping particles
+
+using nOverlappingParticlesField_T = GhostLayerField< uint_t, 1 >;
+using BsField_T                    = GhostLayerField< real_t, MaxParticlesPerCell >;
+using idxField_T                   = GhostLayerField< size_t, MaxParticlesPerCell >;
+using BField_T                     = GhostLayerField< real_t, 1 >;
+using particleVelocitiesField_T    = GhostLayerField< real_t, MaxParticlesPerCell * 3 >;
+using particleForcesField_T        = GhostLayerField< real_t, MaxParticlesPerCell * 3 >;
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+using nOverlappingParticlesFieldGPU_T = walberla::gpu::GPUField< uint_t >;
+using BsFieldGPU_T                    = walberla::gpu::GPUField< real_t >;
+using idxFieldGPU_T                   = walberla::gpu::GPUField< size_t >;
+using BFieldGPU_T                     = walberla::gpu::GPUField< real_t >;
+using particleVelocitiesFieldGPU_T    = walberla::gpu::GPUField< real_t >;
+using particleForcesFieldGPU_T        = walberla::gpu::GPUField< real_t >;
+#endif
+
+// The ParticleAndVolumeFractionSoA encapsulates the data needed by the routines involved in the coupling
+template< int Weighting_T >
+struct ParticleAndVolumeFractionSoA_T
+{
+   BlockDataID nOverlappingParticlesFieldID;
+   BlockDataID BsFieldID;
+   BlockDataID idxFieldID;
+   BlockDataID BFieldID;
+   BlockDataID particleVelocitiesFieldID;
+   BlockDataID particleForcesFieldID;
+   // relaxation rate omega is used for Weighting_T != 1
+   real_t omega_;
+   // UIDs of the particles are stored during mapping, and it is checked that they are the same during the PSM kernel.
+   // This prevents running into troubles due to changed indices
+   std::vector< walberla::id_t > mappingUIDs;
+   // Store positions globally to avoid copying them from CPU to GPU in multiple sweeps
+   real_t* positions = nullptr;
+
+   // nrOfGhostLayers is also 1 for the fields that do not need a ghost layer since the generated sweeps can only handle
+   // fields with the same number of ghost layerserated kernels)
+   ParticleAndVolumeFractionSoA_T(const shared_ptr< StructuredBlockStorage >& bs, const real_t omega)
+   {
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+      nOverlappingParticlesFieldID = walberla::gpu::addGPUFieldToStorage< nOverlappingParticlesFieldGPU_T >(
+         bs, "number of overlapping particles field GPU", uint_t(1), field::fzyx, uint_t(1), true);
+      BsFieldID  = walberla::gpu::addGPUFieldToStorage< BsFieldGPU_T >(bs, "Bs field GPU", MaxParticlesPerCell,
+                                                                       field::fzyx, uint_t(1), true);
+      idxFieldID = walberla::gpu::addGPUFieldToStorage< idxFieldGPU_T >(bs, "idx field GPU", MaxParticlesPerCell,
+                                                                        field::fzyx, uint_t(1), true);
+      BFieldID = walberla::gpu::addGPUFieldToStorage< BFieldGPU_T >(bs, "B field GPU", 1, field::fzyx, uint_t(1), true);
+      particleVelocitiesFieldID = walberla::gpu::addGPUFieldToStorage< particleVelocitiesFieldGPU_T >(
+         bs, "particle velocities field GPU", uint_t(MaxParticlesPerCell * 3), field::fzyx, uint_t(1), true);
+      particleForcesFieldID = walberla::gpu::addGPUFieldToStorage< particleForcesFieldGPU_T >(
+         bs, "particle forces field GPU", uint_t(MaxParticlesPerCell * 3), field::fzyx, uint_t(1), true);
+#else
+      nOverlappingParticlesFieldID = field::addToStorage< nOverlappingParticlesField_T >(
+         bs, "number of overlapping particles field CPU", uint_t(0), field::fzyx, uint_t(1), true);
+      BsFieldID  = field::addToStorage< BsField_T >(bs, "Bs field CPU", real_t(0), field::fzyx, uint_t(1), true);
+      idxFieldID = field::addToStorage< idxField_T >(bs, "idx field CPU", uint_t(0), field::fzyx, uint_t(1), true);
+      BFieldID   = field::addToStorage< BField_T >(bs, "B field CPU", real_t(0), field::fzyx, uint_t(1), true);
+      particleVelocitiesFieldID = field::addToStorage< particleVelocitiesField_T >(
+         bs, "particle velocities field CPU", real_t(0), field::fzyx, uint_t(1), true);
+      particleForcesFieldID = field::addToStorage< particleForcesField_T >(bs, "particle forces field CPU", real_t(0),
+                                                                           field::fzyx, uint_t(1), true);
+#endif
+      omega_ = omega;
+   }
+
+   ~ParticleAndVolumeFractionSoA_T()
+   {
+      if (positions != nullptr)
+      {
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+         WALBERLA_GPU_CHECK(gpuFree(positions));
+#else
+         free(positions);
+#endif
+      }
+   }
+};
+
+} // namespace gpu
+} // namespace psm
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/overlapping/CMakeLists.txt b/src/lbm_mesapd_coupling/overlapping/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1dfa6dc12d91a77a01f15baa8200835fc78c2fbb
--- /dev/null
+++ b/src/lbm_mesapd_coupling/overlapping/CMakeLists.txt
@@ -0,0 +1,4 @@
+target_sources(lbm_mesapd_coupling
+        PRIVATE
+        OverlapFraction.h
+        )
diff --git a/src/lbm_mesapd_coupling/overlapping/OverlapFraction.h b/src/lbm_mesapd_coupling/overlapping/OverlapFraction.h
index 3dbf68e039af2494020a8ec045f60189a318fd9c..c1fa9d7c99583356f122595d8d8df4190f3e82ad 100644
--- a/src/lbm_mesapd_coupling/overlapping/OverlapFraction.h
+++ b/src/lbm_mesapd_coupling/overlapping/OverlapFraction.h
@@ -16,7 +16,7 @@
 //! \file OverlapFraction.h
 //! \ingroup lbm_mesapd_coupling
 //! \author Samuel Kemmler <samuel.kemmler@fau.de>
-//! \brief Functor that provides overlap fraction computations for different MESA-PD shapes (used for SingleCast)
+//! \brief Functor that provides overlap fraction computations for different MESA-PD shapes (used by SingleCast)
 //
 //======================================================================================================================
 
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/CMakeLists.txt b/src/lbm_mesapd_coupling/partially_saturated_cells_method/CMakeLists.txt
index 65b1c468c80828ea27ca30c9cc8f712e137803cb..4a87ce81e6dc56c3a5f3bbeb1b6d988ca2bed00c 100644
--- a/src/lbm_mesapd_coupling/partially_saturated_cells_method/CMakeLists.txt
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/CMakeLists.txt
@@ -4,3 +4,5 @@ target_sources( lbm_mesapd_coupling
     ParticleAndVolumeFractionMapping.h
     PSMUtility.h
     )
+
+add_subdirectory(codegen)
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/PSMSweep.h b/src/lbm_mesapd_coupling/partially_saturated_cells_method/PSMSweep.h
index 8ad996ba8c1a5b4ea61a4add9e29f1a9de67196b..d056e2e76ab010cd2732297778a0e4d6750560cd 100644
--- a/src/lbm_mesapd_coupling/partially_saturated_cells_method/PSMSweep.h
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/PSMSweep.h
@@ -221,7 +221,7 @@ void PSMSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut
                   // total coverage ratio in the cell
                   real_t Bn = real_t(0);
 
-                  // averaged solid collision operator for all intersecting bodies s
+                  // averaged solid collision operator for all intersecting particles s
                   // = \sum_s B_s * \Omega_s_i
                   std::vector< real_t > omega_n(Stencil_T::Size, real_t(0));
 
@@ -398,7 +398,7 @@ void PSMSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut
                   // total coverage ratio in the cell
                   real_t Bn = real_t(0);
 
-                  // averaged solid collision operator for all intersecting bodies s
+                  // averaged solid collision operator for all intersecting particles s
                   // = \sum_s B_s * \Omega_s_i
                   std::vector< real_t > omega_n(Stencil_T::Size, real_t(0));
 
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/PSMUtility.h b/src/lbm_mesapd_coupling/partially_saturated_cells_method/PSMUtility.h
index 854d27f99afd0b40c4eab0ed843ad951a2323cec..aaacd683ee7b50f88ae638fcabb3f547948b6561 100644
--- a/src/lbm_mesapd_coupling/partially_saturated_cells_method/PSMUtility.h
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/PSMUtility.h
@@ -51,10 +51,10 @@ namespace psm
  * Weighting_T is like in the PSMSweep.
  */
 template< typename LatticeModel_T, int Weighting_T, typename ParticleAccessor_T >
-Vector3< real_t > getPSMMacroscopicVelocity(
-   const IBlock& block, lbm::PdfField< LatticeModel_T >* pdfField,
-   GhostLayerField< std::vector< std::pair< walberla::size_t, real_t > >, 1 >* particleAndVolumeFractionField,
-   StructuredBlockStorage& blockStorage, const Cell& cell, const ParticleAccessor_T& ac)
+Vector3< real_t > getPSMMacroscopicVelocity(const IBlock& block, lbm::PdfField< LatticeModel_T >* pdfField,
+                                            ParticleAndVolumeFractionField_T* particleAndVolumeFractionField,
+                                            StructuredBlockStorage& blockStorage, const Cell& cell,
+                                            const ParticleAccessor_T& ac)
 {
    static_assert(LatticeModel_T::compressible == false, "Only works with incompressible models!");
    WALBERLA_ASSERT_NOT_NULLPTR(pdfField);
@@ -87,16 +87,16 @@ Vector3< real_t > getPSMMacroscopicVelocity(
    return velocity;
 }
 
-/*!\brief Initializes the PDF field inside the bodies according to the velocities of the bodies.
+/*!\brief Initializes the PDF field inside the particles according to the velocities of the particles.
  *
- * As the Partially Saturated Cells method relies on executing the LBM sweep also inside the bodies, it is good practice
- * (and for some PSM variants also required) to initialize the PDF field ( i.e. the velocity ) in agreement with
- * possible initial velocities of the bodies. This is also the case in the presence of external forces acting on the
- * fluid, as these will often shift the macroscopic velocities during the initialization of the PDF field.
+ * As the Partially Saturated Cells method relies on executing the LBM sweep also inside the particles, it is good
+ * practice (and for some PSM variants also required) to initialize the PDF field ( i.e. the velocity ) in agreement
+ * with possible initial velocities of the particles. This is also the case in the presence of external forces acting on
+ * the fluid, as these will often shift the macroscopic velocities during the initialization of the PDF field.
  *
  * Note, that the ParticleAndVolumeFractionMapping for PSM has to be called first to have a valid field.
  *
- * Only the velocity of cells intersecting with bodies is set, pure fluid cells remain unchanged.
+ * Only the velocity of cells intersecting with particles is set, pure fluid cells remain unchanged.
  */
 template< typename LatticeModel_T, int Weighting_T, typename ParticleAccessor_T >
 void initializeDomainForPSM(StructuredBlockStorage& blockStorage, const BlockDataID& pdfFieldID,
@@ -144,7 +144,7 @@ void initializeDomainForPSM(StructuredBlockStorage& blockStorage, const BlockDat
             Vector3< real_t > fluidVelocityInCell(real_t(0));
             const real_t rho = pdfField->getDensityAndVelocity(fluidVelocityInCell, cell);
 
-            // set the PDFs to equilibrium with the density rho and the average velocity of all intersecting bodies
+            // set the PDFs to equilibrium with the density rho and the average velocity of all intersecting particles
             pdfField->setToEquilibrium(cell,
                                        fluidVelocityInCell * (real_c(1) - totalSolidWeightingInCell) +
                                           weightedAverageParticleVelocityInCell,
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/CMakeLists.txt b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..82b23cd774f80475b781e4bbc092728757277276
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/CMakeLists.txt
@@ -0,0 +1,35 @@
+target_sources(lbm_mesapd_coupling
+        PRIVATE
+        ParticleAndVolumeFractionMappingSweepsCPU.h
+        PSMSweepCollection.h
+        PSMWrapperSweepsCPU.h
+)
+if (WALBERLA_BUILD_WITH_CODEGEN)
+    foreach (collision_setup srt trt mrt cumulant srt-smagorinsky trt-smagorinsky)
+        foreach (solid_collision 1 2 3)
+            set(config ${collision_setup}_sc${solid_collision})
+            walberla_generate_target_from_python(NAME PSMCodegenPython_${config}
+                    FILE PSMCodegen.py
+                    CODEGEN_CFG ${config}_${MAX_PARTICLES_PER_CELL}
+                    OUT_FILES LBMSweep.${CODEGEN_FILE_SUFFIX} LBMSweep.h LBMSplitSweep.${CODEGEN_FILE_SUFFIX} LBMSplitSweep.h
+                    PSMSweep.${CODEGEN_FILE_SUFFIX} PSMSweep.h PSMSweepSplit.${CODEGEN_FILE_SUFFIX} PSMSweepSplit.h
+                    PSMPackInfo.${CODEGEN_FILE_SUFFIX} PSMPackInfo.h InitializeDomainForPSM.${CODEGEN_FILE_SUFFIX} InitializeDomainForPSM.h
+                    PSM_NoSlip.${CODEGEN_FILE_SUFFIX} PSM_NoSlip.h PSM_UBB.${CODEGEN_FILE_SUFFIX} PSM_UBB.h PSM_Density.${CODEGEN_FILE_SUFFIX} PSM_Density.h
+                    PSM_FreeSlip.${CODEGEN_FILE_SUFFIX} PSM_FreeSlip.h PSM_InfoHeader.h PSM_MacroGetter.cpp PSM_MacroGetter.h
+                    PSM_MacroSetter.cpp PSM_MacroSetter.h)
+            add_dependencies(PSMCodegenPython_${config} MAX_PARTICLES_PER_CELL)
+        endforeach ()
+    endforeach ()
+endif ()
+if (WALBERLA_BUILD_WITH_GPU_SUPPORT AND (CMAKE_CUDA_ARCHITECTURES GREATER_EQUAL 60 OR WALBERLA_BUILD_WITH_HIP))
+    target_sources(lbm_mesapd_coupling
+            PRIVATE
+            ParticleAndVolumeFractionMappingSweepsGPU.h
+            ParticleAndVolumeFractionMappingKernels.${CODEGEN_FILE_SUFFIX}
+            ParticleAndVolumeFractionMappingKernels.h
+            PSMUtilityGPU.h
+            PSMWrapperKernels.${CODEGEN_FILE_SUFFIX}
+            PSMWrapperKernels.h
+            PSMWrapperSweepsGPU.h
+    )
+endif ()
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMCodegen.py b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMCodegen.py
new file mode 100644
index 0000000000000000000000000000000000000000..7564b54db0b76e863990849b0474e1a4f6abfd7d
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMCodegen.py
@@ -0,0 +1,272 @@
+import copy
+import sympy as sp
+import pystencils as ps
+from sympy.core.add import Add
+from sympy.codegen.ast import Assignment
+
+from lbmpy import LBMConfig, LBMOptimisation, LBStencil, Method, Stencil, ForceModel
+from lbmpy.partially_saturated_cells import PSMConfig
+
+from lbmpy.boundaries import NoSlip, UBB, FixedDensity, FreeSlip
+from lbmpy.creationfunctions import (
+    create_lb_update_rule,
+    create_lb_method,
+    create_psm_update_rule,
+)
+
+from lbmpy.macroscopic_value_kernels import (
+    macroscopic_values_getter,
+    macroscopic_values_setter,
+)
+
+from pystencils_walberla import (
+    CodeGeneration,
+    generate_info_header,
+    generate_sweep,
+    generate_pack_info_from_kernel,
+)
+
+from lbmpy_walberla import generate_boundary
+
+# Based on the following paper: https://doi.org/10.1016/j.compfluid.2017.05.033
+
+info_header = """
+const char * infoStencil = "{stencil}";
+const char * infoStreamingPattern = "{streaming_pattern}";
+const char * infoCollisionSetup = "{collision_setup}";
+const bool infoCseGlobal = {cse_global};
+const bool infoCsePdfs = {cse_pdfs};
+"""
+
+with CodeGeneration() as ctx:
+    data_type = "float64" if ctx.double_accuracy else "float32"
+    stencil = LBStencil(Stencil.D3Q27)
+    omega = sp.Symbol("omega")
+    init_density = sp.Symbol("init_density")
+    init_velocity = sp.symbols("init_velocity_:3")
+    pdfs_inter = sp.symbols("pdfs_inter:" + str(stencil.Q))
+    layout = "fzyx"
+    config_tokens = ctx.config.split("_")
+    MaxParticlesPerCell = int(config_tokens[2])
+    methods = {
+        "srt": Method.SRT,
+        "trt": Method.TRT,
+        "mrt": Method.MRT,
+        "cumulant": Method.MONOMIAL_CUMULANT,
+        "srt-smagorinsky": Method.SRT,
+        "trt-smagorinsky": Method.TRT,
+    }
+    # Solid collision variant
+    SC = int(config_tokens[1][2])
+
+    pdfs, pdfs_tmp, velocity_field, density_field = ps.fields(
+        f"pdfs({stencil.Q}), pdfs_tmp({stencil.Q}), velocity_field({stencil.D}), density_field({1}): {data_type}[3D]",
+        layout=layout,
+    )
+
+    particle_velocities, particle_forces, Bs = ps.fields(
+        f"particle_v({MaxParticlesPerCell * stencil.D}), particle_f({MaxParticlesPerCell * stencil.D}), Bs({MaxParticlesPerCell}): {data_type}[3D]",
+        layout=layout,
+    )
+
+    # Solid fraction field
+    B = ps.fields(f"b({1}): {data_type}[3D]", layout=layout)
+
+    psm_opt = LBMOptimisation(
+        cse_global=True,
+        symbolic_field=pdfs,
+        symbolic_temporary_field=pdfs_tmp,
+        field_layout=layout,
+    )
+
+    psm_config = PSMConfig(
+        fraction_field=B,
+        object_velocity_field=particle_velocities,
+        SC=SC,
+        MaxParticlesPerCell=MaxParticlesPerCell,
+        individual_fraction_field=Bs,
+        particle_force_field=particle_forces,
+    )
+
+    lbm_config = LBMConfig(
+        stencil=stencil,
+        method=methods[config_tokens[0]],
+        relaxation_rate=omega,
+        force=sp.symbols("F_:3"),
+        force_model=ForceModel.LUO,
+        compressible=True,
+        psm_config=psm_config,
+    )
+
+    if config_tokens[0] == "srt-smagorinsky" or config_tokens[0] == "trt-smagorinsky":
+        lbm_config.smagorinsky = True
+
+    # =====================
+    # Generate method
+    # =====================
+
+    method = create_lb_method(lbm_config=lbm_config)
+
+    node_collection = create_psm_update_rule(lbm_config, psm_opt)
+
+    pdfs_setter = macroscopic_values_setter(
+        method, init_density, init_velocity, pdfs.center_vector
+    )
+
+    # Use average velocity of all intersecting particles when setting PDFs (mandatory for SC=3)
+    for i, sub_exp in enumerate(pdfs_setter.subexpressions[-3:]):
+        rhs = []
+        for summand in sub_exp.rhs.args:
+            rhs.append(summand * (1.0 - B.center))
+        for p in range(MaxParticlesPerCell):
+            rhs.append(particle_velocities(p * stencil.D + i) * Bs.center(p))
+        pdfs_setter.subexpressions.remove(sub_exp)
+        pdfs_setter.subexpressions.append(Assignment(sub_exp.lhs, Add(*rhs)))
+
+    # =====================
+    # Write method to files
+    # =====================
+
+    if ctx.gpu:
+        target = ps.Target.GPU
+    else:
+        target = ps.Target.CPU
+
+    # Generate files
+    generate_sweep(
+        ctx,
+        "PSMSweep",
+        node_collection,
+        field_swaps=[(pdfs, pdfs_tmp)],
+        target=target,
+    )
+
+    generate_sweep(
+        ctx,
+        "PSMSweepSplit",
+        node_collection,
+        field_swaps=[(pdfs, pdfs_tmp)],
+        target=target,
+        inner_outer_split=True,
+    )
+
+    config_without_psm = LBMConfig(
+        stencil=stencil,
+        method=methods[config_tokens[0]],
+        relaxation_rate=omega,
+        force=sp.symbols("F_:3"),
+        force_model=ForceModel.LUO,
+        compressible=True,
+    )
+
+    if config_tokens[0] == "srt-smagorinsky" or config_tokens[0] == "trt-smagorinsky":
+        config_without_psm.smagorinsky = True
+
+    generate_sweep(
+        ctx,
+        "LBMSweep",
+        create_lb_update_rule(lbm_config=config_without_psm, lbm_optimisation=psm_opt),
+        field_swaps=[(pdfs, pdfs_tmp)],
+        target=target,
+    )
+
+    generate_sweep(
+        ctx,
+        "LBMSplitSweep",
+        create_lb_update_rule(lbm_config=config_without_psm, lbm_optimisation=psm_opt),
+        field_swaps=[(pdfs, pdfs_tmp)],
+        target=target,
+        inner_outer_split=True,
+    )
+
+    generate_pack_info_from_kernel(
+        ctx,
+        "PSMPackInfo",
+        create_lb_update_rule(lbm_config=lbm_config, lbm_optimisation=psm_opt),
+        target=target,
+    )
+
+    generate_sweep(ctx, "InitializeDomainForPSM", pdfs_setter, target=target)
+
+    # Boundary conditions
+    generate_boundary(
+        ctx,
+        "PSM_NoSlip",
+        NoSlip(),
+        method,
+        field_name=pdfs.name,
+        streaming_pattern="pull",
+        target=target,
+    )
+
+    bc_velocity = sp.symbols("bc_velocity_:3")
+    generate_boundary(
+        ctx,
+        "PSM_UBB",
+        UBB(bc_velocity),
+        method,
+        field_name=pdfs.name,
+        streaming_pattern="pull",
+        target=target,
+    )
+
+    bc_density = sp.Symbol("bc_density")
+    generate_boundary(
+        ctx,
+        "PSM_Density",
+        FixedDensity(bc_density),
+        method,
+        field_name=pdfs.name,
+        streaming_pattern="pull",
+        target=target,
+    )
+
+    generate_boundary(
+        ctx,
+        "PSM_FreeSlip",
+        FreeSlip(stencil),
+        method,
+        field_name=pdfs.name,
+        streaming_pattern="pull",
+        target=target,
+    )
+
+    # Info header containing correct template definitions for stencil and fields
+    infoHeaderParams = {
+        "stencil": stencil.name,
+        "streaming_pattern": lbm_config.streaming_pattern,
+        "collision_setup": config_tokens[0],
+        "cse_global": int(psm_opt.cse_global),
+        "cse_pdfs": int(psm_opt.cse_pdfs),
+    }
+
+    stencil_typedefs = {"Stencil_T": stencil, "CommunicationStencil_T": stencil}
+    field_typedefs = {
+        "PdfField_T": pdfs,
+        "DensityField_T": density_field,
+        "VelocityField_T": velocity_field,
+    }
+
+    generate_info_header(
+        ctx,
+        "PSM_InfoHeader",
+        stencil_typedefs=stencil_typedefs,
+        field_typedefs=field_typedefs,
+        additional_code=info_header.format(**infoHeaderParams),
+    )
+
+    # Getter & setter to compute moments from pdfs
+    setter_assignments = macroscopic_values_setter(
+        method,
+        velocity=velocity_field.center_vector,
+        pdfs=pdfs.center_vector,
+        density=1.0,
+    )
+    getter_assignments = macroscopic_values_getter(
+        method,
+        density=density_field,
+        velocity=velocity_field.center_vector,
+        pdfs=pdfs.center_vector,
+    )
+    generate_sweep(ctx, "PSM_MacroSetter", setter_assignments)
+    generate_sweep(ctx, "PSM_MacroGetter", getter_assignments)
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMSweepCollection.h b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMSweepCollection.h
new file mode 100644
index 0000000000000000000000000000000000000000..ec0ed85e2ab09b86eee1034d9f9ca8d9c2120b67
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMSweepCollection.h
@@ -0,0 +1,124 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file PSMSweepCollection.h
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+#   include "lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperSweepsGPU.h"
+#   include "lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingSweepsGPU.h"
+#else
+#   include "lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperSweepsCPU.h"
+#   include "lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingSweepsCPU.h"
+#endif
+
+namespace walberla
+{
+namespace lbm_mesapd_coupling
+{
+namespace psm
+{
+namespace gpu
+{
+
+// The deviceSyncWrapper can be used so that the timeloop measures the correct device runtime
+auto deviceSyncWrapper = [](std::function< void(IBlock*) > sweep) {
+   return [sweep](IBlock* b) {
+      sweep(b);
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+      WALBERLA_GPU_CHECK(gpuDeviceSynchronize());
+#endif
+   };
+};
+
+template< typename ParticleAccessor_T, typename ParticleSelector_T, int Weighting_T >
+class PSMSweepCollection
+{
+ public:
+   PSMSweepCollection(const shared_ptr< StructuredBlockStorage >& bs, const shared_ptr< ParticleAccessor_T >& ac,
+                      const ParticleSelector_T& ps,
+                      ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA,
+                      const Vector3< uint_t > particleSubBlockSize = Vector3< uint_t >(10))
+      : particleMappingSweep(SphereFractionMappingSweep< ParticleAccessor_T, ParticleSelector_T, Weighting_T >(
+           bs, ac, ps, particleAndVolumeFractionSoA, particleSubBlockSize)),
+        setParticleVelocitiesSweep(SetParticleVelocitiesSweep< ParticleAccessor_T, ParticleSelector_T, Weighting_T >(
+           bs, ac, ps, particleAndVolumeFractionSoA)),
+        reduceParticleForcesSweep(ReduceParticleForcesSweep< ParticleAccessor_T, ParticleSelector_T, Weighting_T >(
+           bs, ac, ps, particleAndVolumeFractionSoA))
+   {}
+   SphereFractionMappingSweep< ParticleAccessor_T, ParticleSelector_T, Weighting_T > particleMappingSweep;
+   SetParticleVelocitiesSweep< ParticleAccessor_T, ParticleSelector_T, Weighting_T > setParticleVelocitiesSweep;
+   ReduceParticleForcesSweep< ParticleAccessor_T, ParticleSelector_T, Weighting_T > reduceParticleForcesSweep;
+};
+
+template< typename SweepCollection, typename PSMSweep >
+void addPSMSweepsToTimeloop(SweepTimeloop& timeloop, SweepCollection& psmSweepCollection, PSMSweep& psmSweep,
+                            bool synchronize = true)
+{
+   if (synchronize)
+   {
+      timeloop.add() << Sweep(deviceSyncWrapper(psmSweepCollection.particleMappingSweep), "Particle mapping");
+      timeloop.add() << Sweep(deviceSyncWrapper(psmSweepCollection.setParticleVelocitiesSweep),
+                              "Set particle velocities");
+      timeloop.add() << Sweep(deviceSyncWrapper(psmSweep), "PSM sweep");
+      timeloop.add() << Sweep(deviceSyncWrapper(psmSweepCollection.reduceParticleForcesSweep),
+                              "Reduce particle forces");
+   }
+   else
+   {
+      timeloop.add() << Sweep(psmSweepCollection.particleMappingSweep, "Particle mapping");
+      timeloop.add() << Sweep(psmSweepCollection.setParticleVelocitiesSweep, "Set particle velocities");
+      timeloop.add() << Sweep(psmSweep, "PSM sweep");
+      timeloop.add() << Sweep(psmSweepCollection.reduceParticleForcesSweep, "Reduce particle forces");
+   };
+}
+
+template< typename SweepCollection, typename PSMSweep, typename Communication >
+void addPSMSweepsToTimeloops(SweepTimeloop& commTimeloop, SweepTimeloop& timeloop, Communication& comm,
+                             SweepCollection& psmSweepCollection, PSMSweep& psmSweep, bool synchronize = true)
+{
+   if (synchronize)
+   {
+      commTimeloop.add() << BeforeFunction([&]() { comm.startCommunication(); })
+                         << Sweep(deviceSyncWrapper(psmSweepCollection.particleMappingSweep), "Particle mapping");
+      commTimeloop.add() << Sweep(deviceSyncWrapper(psmSweepCollection.setParticleVelocitiesSweep),
+                                  "Set particle velocities");
+      commTimeloop.add() << Sweep(deviceSyncWrapper(psmSweep.getInnerSweep()), "PSM inner sweep")
+                         << AfterFunction([&]() { comm.wait(); }, "LBM Communication (wait)");
+      timeloop.add() << Sweep(deviceSyncWrapper(psmSweep.getOuterSweep()), "PSM outer sweep");
+      timeloop.add() << Sweep(deviceSyncWrapper(psmSweepCollection.reduceParticleForcesSweep),
+                              "Reduce particle forces");
+   }
+   else
+   {
+      commTimeloop.add() << BeforeFunction([&]() { comm.startCommunication(); })
+                         << Sweep(psmSweepCollection.particleMappingSweep, "Particle mapping");
+      commTimeloop.add() << Sweep(psmSweepCollection.setParticleVelocitiesSweep, "Set particle velocities");
+      commTimeloop.add() << Sweep(psmSweep.getInnerSweep(), "PSM inner sweep")
+                         << AfterFunction([&]() { comm.wait(); }, "LBM Communication (wait)");
+      timeloop.add() << Sweep(psmSweep.getOuterSweep(), "PSM outer sweep");
+      timeloop.add() << Sweep(psmSweepCollection.reduceParticleForcesSweep, "Reduce particle forces");
+   };
+}
+
+} // namespace gpu
+} // namespace psm
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMUtilityGPU.h b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMUtilityGPU.h
new file mode 100644
index 0000000000000000000000000000000000000000..6ac6d0a9a53321b18c964da6bdc5b72df501b788
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMUtilityGPU.h
@@ -0,0 +1,96 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file PSMUtilityGPU.cuh
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "gpu/FieldAccessor.h"
+
+namespace walberla
+{
+namespace lbm_mesapd_coupling
+{
+namespace psm
+{
+namespace gpu
+{
+
+__device__ void cross(real_t* __restrict__ const crossResult, const real_t* __restrict__ const lhs,
+                      const real_t* __restrict__ const rhs)
+{
+   crossResult[0] = lhs[1] * rhs[2] - lhs[2] * rhs[1];
+   crossResult[1] = lhs[2] * rhs[0] - lhs[0] * rhs[2];
+   crossResult[2] = lhs[0] * rhs[1] - lhs[1] * rhs[0];
+}
+
+__device__ void getVelocityAtWFPoint(real_t* __restrict__ const velocityAtWFPoint,
+                                     const real_t* __restrict__ const linearVelocity,
+                                     const real_t* __restrict__ const angularVelocity,
+                                     const real_t* __restrict__ const position, const real_t* __restrict__ const wf_pt)
+{
+   real_t crossResult[3];
+   real_t rhs[] = { wf_pt[0] - position[0], wf_pt[1] - position[1], wf_pt[2] - position[2] };
+   cross(crossResult, angularVelocity, rhs);
+   velocityAtWFPoint[0] = linearVelocity[0] + crossResult[0];
+   velocityAtWFPoint[1] = linearVelocity[1] + crossResult[1];
+   velocityAtWFPoint[2] = linearVelocity[2] + crossResult[2];
+}
+
+__device__ void addHydrodynamicForceTorqueAtWFPosAtomic(real_t* __restrict__ const particleForce,
+                                                        real_t* __restrict__ const particleTorque,
+                                                        const real_t* __restrict__ const f,
+                                                        const real_t* __restrict__ const pos,
+                                                        const real_t* __restrict__ const wf_pt)
+{
+#if defined(WALBERLA_BUILD_WITH_CUDA)
+   atomicAdd(&(particleForce[0]), f[0]);
+   atomicAdd(&(particleForce[1]), f[1]);
+   atomicAdd(&(particleForce[2]), f[2]);
+#endif
+
+   // Using unsafeAtomicAdd ensures that HW FP Atomics are used instead of CAS loops, see:
+   // https://fs.hlrs.de/projects/par/events/2023/GPU-AMD/day3/11.%20AMD_Node_Memory_Model.pdf
+#ifdef WALBERLA_BUILD_WITH_HIP
+   unsafeAtomicAdd(&(particleForce[0]), f[0]);
+   unsafeAtomicAdd(&(particleForce[1]), f[1]);
+   unsafeAtomicAdd(&(particleForce[2]), f[2]);
+#endif
+
+   real_t torque[] = { 0.0, 0.0, 0.0 };
+   real_t lhs[]    = { wf_pt[0] - pos[0], wf_pt[1] - pos[1], wf_pt[2] - pos[2] };
+   cross(torque, lhs, f);
+
+#if defined(WALBERLA_BUILD_WITH_CUDA)
+   atomicAdd(&(particleTorque[0]), torque[0]);
+   atomicAdd(&(particleTorque[1]), torque[1]);
+   atomicAdd(&(particleTorque[2]), torque[2]);
+#endif
+
+#ifdef WALBERLA_BUILD_WITH_HIP
+   unsafeAtomicAdd(&(particleTorque[0]), torque[0]);
+   unsafeAtomicAdd(&(particleTorque[1]), torque[1]);
+   unsafeAtomicAdd(&(particleTorque[2]), torque[2]);
+#endif
+}
+
+} // namespace gpu
+} // namespace psm
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperKernels.cpp b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperKernels.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4bbf4439f56f5e3acae17bef0cc81742437ef5be
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperKernels.cpp
@@ -0,0 +1,23 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file PSMWrapperKernels.cpp
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//! \brief Mirror PSMWrapperKernels.cu to provide a .cpp file for HIP
+//
+//======================================================================================================================
+
+#include "PSMWrapperKernels.cu"
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperKernels.cu b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperKernels.cu
new file mode 100644
index 0000000000000000000000000000000000000000..9aacf68d450bc8a6dbd2f8c5288f6116eaed2e0b
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperKernels.cu
@@ -0,0 +1,101 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file PSMWrapperKernels.cu
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//! \brief Provide two kernels that need to be called before and after the PSM kernel
+//
+//======================================================================================================================
+
+#include "PSMUtilityGPU.h"
+#include "PSMWrapperKernels.h"
+
+namespace walberla
+{
+namespace lbm_mesapd_coupling
+{
+namespace psm
+{
+namespace gpu
+{
+
+__global__ void SetParticleVelocities(walberla::gpu::FieldAccessor< uint_t > nOverlappingParticlesField,
+                                      walberla::gpu::FieldAccessor< uint_t > idxField,
+                                      walberla::gpu::FieldAccessor< real_t > particleVelocitiesField,
+                                      real_t* __restrict__ const linearVelocities,
+                                      real_t* __restrict__ const angularVelocities,
+                                      real_t* __restrict__ const positions, const double3 blockStart, const real_t dx)
+{
+   const uint3 blockIdx_uint3  = make_uint3(blockIdx.x, blockIdx.y, blockIdx.z);
+   const uint3 threadIdx_uint3 = make_uint3(threadIdx.x, threadIdx.y, threadIdx.z);
+
+   nOverlappingParticlesField.set(blockIdx_uint3, threadIdx_uint3);
+   idxField.set(blockIdx_uint3, threadIdx_uint3);
+   particleVelocitiesField.set(blockIdx_uint3, threadIdx_uint3);
+
+   // Cell center is needed in order to compute the particle velocity at this WF point
+   const real_t cellCenter[] = { (blockStart.x + (threadIdx.x + 0.5) * dx), (blockStart.y + (blockIdx.x + 0.5) * dx),
+                                 (blockStart.z + (blockIdx.y + 0.5) * dx) };
+
+   // Compute the particle velocity at the cell center for all overlapping particles
+   for (uint_t p = 0; p < nOverlappingParticlesField.get(); p++)
+   {
+      real_t particleVelocityAtWFPoint[] = { 0.0, 0.0, 0.0 };
+      getVelocityAtWFPoint(particleVelocityAtWFPoint, &linearVelocities[idxField.get(p) * 3],
+                           &angularVelocities[idxField.get(p) * 3], &positions[idxField.get(p) * 3], cellCenter);
+      particleVelocitiesField.get(p * 3 + 0) = particleVelocityAtWFPoint[0];
+      particleVelocitiesField.get(p * 3 + 1) = particleVelocityAtWFPoint[1];
+      particleVelocitiesField.get(p * 3 + 2) = particleVelocityAtWFPoint[2];
+   }
+}
+
+__global__ void ReduceParticleForces(walberla::gpu::FieldAccessor< uint_t > nOverlappingParticlesField,
+                                     walberla::gpu::FieldAccessor< id_t > idxField,
+                                     walberla::gpu::FieldAccessor< real_t > particleForcesField,
+                                     real_t* __restrict__ const hydrodynamicForces,
+                                     real_t* __restrict__ const hydrodynamicTorques,
+                                     real_t* __restrict__ const positions, const double3 blockStart, const real_t dx,
+                                     const real_t forceScalingFactor)
+{
+   const uint3 blockIdx_uint3  = make_uint3(blockIdx.x, blockIdx.y, blockIdx.z);
+   const uint3 threadIdx_uint3 = make_uint3(threadIdx.x, threadIdx.y, threadIdx.z);
+
+   nOverlappingParticlesField.set(blockIdx_uint3, threadIdx_uint3);
+   idxField.set(blockIdx_uint3, threadIdx_uint3);
+   particleForcesField.set(blockIdx_uint3, threadIdx_uint3);
+
+   // Cell center is needed in order to compute the particle velocity at this WF point
+   const real_t cellCenter[] = { (blockStart.x + (threadIdx.x + 0.5) * dx), (blockStart.y + (blockIdx.x + 0.5) * dx),
+                                 (blockStart.z + (blockIdx.y + 0.5) * dx) };
+
+   // Reduce the forces for all overlapping particles
+   for (uint_t p = 0; p < nOverlappingParticlesField.get(); p++)
+   {
+      real_t forceOnParticle[] = { particleForcesField.get(p * 3 + 0), particleForcesField.get(p * 3 + 1),
+                                   particleForcesField.get(p * 3 + 2) };
+      forceOnParticle[0] *= forceScalingFactor;
+      forceOnParticle[1] *= forceScalingFactor;
+      forceOnParticle[2] *= forceScalingFactor;
+      addHydrodynamicForceTorqueAtWFPosAtomic(&hydrodynamicForces[idxField.get(p) * 3],
+                                              &hydrodynamicTorques[idxField.get(p) * 3], forceOnParticle,
+                                              &positions[idxField.get(p) * 3], cellCenter);
+   }
+}
+
+} // namespace gpu
+} // namespace psm
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperKernels.h b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperKernels.h
new file mode 100644
index 0000000000000000000000000000000000000000..d3afc21c7c6d387883e25185ed7a67f7cb03f511
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperKernels.h
@@ -0,0 +1,54 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file PSMWrapperKernels.h
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "gpu/FieldAccessor.h"
+
+namespace walberla
+{
+namespace lbm_mesapd_coupling
+{
+namespace psm
+{
+namespace gpu
+{
+
+__global__ void SetParticleVelocities(walberla::gpu::FieldAccessor< uint_t > nOverlappingParticlesField,
+                                      walberla::gpu::FieldAccessor< uint_t > idxField,
+                                      walberla::gpu::FieldAccessor< real_t > particleVelocitiesField,
+                                      real_t* __restrict__ const linearVelocities,
+                                      real_t* __restrict__ const angularVelocities,
+                                      real_t* __restrict__ const positions, const double3 blockStart,
+                                      const real_t dx);
+
+__global__ void ReduceParticleForces(walberla::gpu::FieldAccessor< uint_t > nOverlappingParticlesField,
+                                     walberla::gpu::FieldAccessor< id_t > idxField,
+                                     walberla::gpu::FieldAccessor< real_t > particleForcesField,
+                                     real_t* __restrict__ const hydrodynamicForces,
+                                     real_t* __restrict__ const hydrodynamicTorques,
+                                     real_t* __restrict__ const positions, const double3 blockStart, const real_t dx,
+                                     const real_t forceScalingFactor);
+
+} // namespace gpu
+} // namespace psm
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperSweepsCPU.h b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperSweepsCPU.h
new file mode 100644
index 0000000000000000000000000000000000000000..d4ab73dd4533dd24d0aec203fa772fe734ce08e7
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperSweepsCPU.h
@@ -0,0 +1,250 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file PSMWrapperSweepsCPU.h
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+#include "lbm/sweeps/StreamPull.h"
+#include "lbm/sweeps/SweepBase.h"
+
+#include "lbm_mesapd_coupling/DataTypesCodegen.h"
+#include "lbm_mesapd_coupling/utility/ParticleFunctions.h"
+
+#include "mesa_pd/common/ParticleFunctions.h"
+
+#include "timeloop/SweepTimeloop.h"
+
+#include <cassert>
+
+namespace walberla
+{
+namespace lbm_mesapd_coupling
+{
+namespace psm
+{
+namespace gpu
+{
+
+template< typename ParticleAccessor_T, typename ParticleSelector_T, int Weighting_T >
+class SetParticleVelocitiesSweep
+{
+ public:
+   SetParticleVelocitiesSweep(const shared_ptr< StructuredBlockStorage >& bs,
+                              const shared_ptr< ParticleAccessor_T >& ac,
+                              const ParticleSelector_T& mappingParticleSelector,
+                              ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA)
+      : bs_(bs), ac_(ac), mappingParticleSelector_(mappingParticleSelector),
+        particleAndVolumeFractionSoA_(particleAndVolumeFractionSoA)
+   {}
+   void operator()(IBlock* block)
+   {
+      // Check that uids of the particles have not changed since the last mapping to avoid incorrect indices
+      std::vector< walberla::id_t > currentUIDs;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_)) { currentUIDs.push_back(ac_->getUid(idx)); }
+      }
+      WALBERLA_ASSERT(particleAndVolumeFractionSoA_.mappingUIDs == currentUIDs);
+
+      size_t numMappedParticles = 0;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_)) { numMappedParticles++; }
+      }
+
+      if (numMappedParticles == uint_t(0)) return;
+
+      size_t arraySizes = numMappedParticles * sizeof(real_t) * 3;
+
+      // Allocate unified memory for the particle information required for computing the velocity at a WF point (used in
+      // the solid collision operator)
+      real_t* linearVelocities = (real_t*) malloc(arraySizes);
+      memset(linearVelocities, 0, arraySizes);
+      real_t* angularVelocities = (real_t*) malloc(arraySizes);
+      memset(angularVelocities, 0, arraySizes);
+
+      // Store particle information inside memory
+      size_t idxMapped = 0;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_))
+         {
+            for (size_t d = 0; d < 3; ++d)
+            {
+               linearVelocities[idxMapped * 3 + d]  = ac_->getLinearVelocity(idx)[d];
+               angularVelocities[idxMapped * 3 + d] = ac_->getAngularVelocity(idx)[d];
+            }
+            idxMapped++;
+         }
+      }
+
+      auto nOverlappingParticlesField =
+         block->getData< nOverlappingParticlesField_T >(particleAndVolumeFractionSoA_.nOverlappingParticlesFieldID);
+      auto idxField = block->getData< idxField_T >(particleAndVolumeFractionSoA_.idxFieldID);
+      auto particleVelocitiesField =
+         block->getData< particleVelocitiesField_T >(particleAndVolumeFractionSoA_.particleVelocitiesFieldID);
+
+      // For every cell, compute the particle velocities of the overlapping particles evaluated at the cell center
+      const real_t dx = block->getAABB().xSize() / real_t(nOverlappingParticlesField->xSize());
+      WALBERLA_FOR_ALL_CELLS_XYZ(
+         particleVelocitiesField, const Vector3< real_t > cellCenter =
+                                     Vector3< real_t >(real_t(x) + real_t(0.5) * dx, real_t(y) + real_t(0.5) * dx,
+                                                       real_t(z) + real_t(0.5) * dx) +
+                                     block->getAABB().minCorner();
+         for (uint_t p = 0; p < nOverlappingParticlesField->get(x, y, z); p++) {
+            Vector3< real_t > particleVelocityAtWFPoint =
+               Vector3< real_t >(linearVelocities[idxField->get(x, y, z, p) * 3 + 0],
+                                 linearVelocities[idxField->get(x, y, z, p) * 3 + 1],
+                                 linearVelocities[idxField->get(x, y, z, p) * 3 + 2]) +
+               cross(Vector3< real_t >(angularVelocities[idxField->get(x, y, z, p) * 3 + 0],
+                                       angularVelocities[idxField->get(x, y, z, p) * 3 + 1],
+                                       angularVelocities[idxField->get(x, y, z, p) * 3 + 2]),
+                     Vector3< real_t >(
+                        cellCenter[0] - particleAndVolumeFractionSoA_.positions[idxField->get(x, y, z, p) * 3 + 0],
+                        cellCenter[1] - particleAndVolumeFractionSoA_.positions[idxField->get(x, y, z, p) * 3 + 1],
+                        cellCenter[2] - particleAndVolumeFractionSoA_.positions[idxField->get(x, y, z, p) * 3 + 2]));
+            particleVelocitiesField->get(x, y, z, p * 3 + 0) = particleVelocityAtWFPoint[0];
+            particleVelocitiesField->get(x, y, z, p * 3 + 1) = particleVelocityAtWFPoint[1];
+            particleVelocitiesField->get(x, y, z, p * 3 + 2) = particleVelocityAtWFPoint[2];
+         })
+
+      free(linearVelocities);
+      free(angularVelocities);
+   }
+
+ private:
+   shared_ptr< StructuredBlockStorage > bs_;
+   const shared_ptr< ParticleAccessor_T > ac_;
+   const ParticleSelector_T& mappingParticleSelector_;
+   ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA_;
+};
+
+template< typename ParticleAccessor_T, typename ParticleSelector_T, int Weighting_T >
+class ReduceParticleForcesSweep
+{
+ public:
+   ReduceParticleForcesSweep(const shared_ptr< StructuredBlockStorage >& bs, const shared_ptr< ParticleAccessor_T >& ac,
+                             const ParticleSelector_T& mappingParticleSelector,
+                             const ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA)
+      : bs_(bs), ac_(ac), mappingParticleSelector_(mappingParticleSelector),
+        particleAndVolumeFractionSoA_(particleAndVolumeFractionSoA)
+   {}
+   void operator()(IBlock* block)
+   {
+      // Check that uids of the particles have not changed since the last mapping to avoid incorrect indices
+      std::vector< walberla::id_t > currentUIDs;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_)) { currentUIDs.push_back(ac_->getUid(idx)); }
+      }
+      WALBERLA_ASSERT(particleAndVolumeFractionSoA_.mappingUIDs == currentUIDs);
+
+      const real_t dxCurrentLevel      = bs_->dx(bs_->getLevel(*block));
+      const real_t lengthScalingFactor = dxCurrentLevel;
+      const real_t forceScalingFactor  = lengthScalingFactor * lengthScalingFactor;
+
+      size_t numMappedParticles = 0;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_)) { numMappedParticles++; }
+      }
+
+      if (numMappedParticles == uint_t(0)) return;
+
+      size_t arraySizes = numMappedParticles * sizeof(real_t) * 3;
+
+      // Allocate memory for the reduction of the particle forces and torques
+      real_t* hydrodynamicForces = (real_t*) malloc(arraySizes);
+      memset(hydrodynamicForces, 0, arraySizes);
+      real_t* hydrodynamicTorques = (real_t*) malloc(arraySizes);
+      memset(hydrodynamicTorques, 0, arraySizes);
+
+      auto nOverlappingParticlesField =
+         block->getData< nOverlappingParticlesField_T >(particleAndVolumeFractionSoA_.nOverlappingParticlesFieldID);
+      auto idxField = block->getData< idxField_T >(particleAndVolumeFractionSoA_.idxFieldID);
+      auto particleForcesField =
+         block->getData< particleForcesField_T >(particleAndVolumeFractionSoA_.particleForcesFieldID);
+
+      // For every cell, reduce the hydrodynamic forces and torques of the overlapping particles
+      const real_t dx = block->getAABB().xSize() / real_t(nOverlappingParticlesField->xSize());
+      WALBERLA_FOR_ALL_CELLS_XYZ(
+         particleForcesField, const Vector3< real_t > cellCenter =
+                                 Vector3< real_t >(real_t(x) + real_t(0.5) * dx, real_t(y) + real_t(0.5) * dx,
+                                                   real_t(z) + real_t(0.5) * dx) +
+                                 block->getAABB().minCorner();
+         for (uint_t p = 0; p < nOverlappingParticlesField->get(x, y, z); p++) {
+            Vector3< real_t > forceOnParticle(particleForcesField->get(x, y, z, p * 3 + 0),
+                                              particleForcesField->get(x, y, z, p * 3 + 1),
+                                              particleForcesField->get(x, y, z, p * 3 + 2));
+            forceOnParticle[0] *= forceScalingFactor;
+            forceOnParticle[1] *= forceScalingFactor;
+            forceOnParticle[2] *= forceScalingFactor;
+
+            hydrodynamicForces[idxField->get(x, y, z, p) * 3 + 0] += forceOnParticle[0];
+            hydrodynamicForces[idxField->get(x, y, z, p) * 3 + 1] += forceOnParticle[1];
+            hydrodynamicForces[idxField->get(x, y, z, p) * 3 + 2] += forceOnParticle[2];
+            Vector3< real_t > torqueOnParticle =
+               cross(Vector3< real_t >(
+                        cellCenter[0] - particleAndVolumeFractionSoA_.positions[idxField->get(x, y, z, p) * 3 + 0],
+                        cellCenter[1] - particleAndVolumeFractionSoA_.positions[idxField->get(x, y, z, p) * 3 + 1],
+                        cellCenter[2] - particleAndVolumeFractionSoA_.positions[idxField->get(x, y, z, p) * 3 + 2]),
+                     forceOnParticle);
+
+            hydrodynamicTorques[idxField->get(x, y, z, p) * 3 + 0] += torqueOnParticle[0];
+            hydrodynamicTorques[idxField->get(x, y, z, p) * 3 + 1] += torqueOnParticle[1];
+            hydrodynamicTorques[idxField->get(x, y, z, p) * 3 + 2] += torqueOnParticle[2];
+         }
+
+      )
+
+      // Copy forces and torques of particles
+      size_t idxMapped = 0;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_))
+         {
+            for (size_t d = 0; d < 3; ++d)
+            {
+               ac_->getHydrodynamicForceRef(idx)[d] += hydrodynamicForces[idxMapped * 3 + d];
+               ac_->getHydrodynamicTorqueRef(idx)[d] += hydrodynamicTorques[idxMapped * 3 + d];
+            }
+            idxMapped++;
+         }
+      }
+
+      free(hydrodynamicForces);
+      free(hydrodynamicTorques);
+   }
+
+ private:
+   shared_ptr< StructuredBlockStorage > bs_;
+   const shared_ptr< ParticleAccessor_T > ac_;
+   const ParticleSelector_T& mappingParticleSelector_;
+   const ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA_;
+};
+
+} // namespace gpu
+} // namespace psm
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperSweepsGPU.h b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperSweepsGPU.h
new file mode 100644
index 0000000000000000000000000000000000000000..863d13fbfa2108668608c83bed2eb2f9ab463f84
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMWrapperSweepsGPU.h
@@ -0,0 +1,253 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file PSMWrapperSweepsGPU.h
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+#include "gpu/FieldIndexing.h"
+#include "gpu/GPUField.h"
+#include "gpu/Kernel.h"
+#include "gpu/sweeps/GPUSweepBase.h"
+
+#include "lbm/sweeps/StreamPull.h"
+#include "lbm/sweeps/SweepBase.h"
+
+#include "lbm_mesapd_coupling/DataTypesCodegen.h"
+#include "lbm_mesapd_coupling/utility/ParticleFunctions.h"
+
+#include "mesa_pd/common/ParticleFunctions.h"
+
+#include "timeloop/SweepTimeloop.h"
+
+#include <cassert>
+
+#include "PSMWrapperKernels.h"
+
+namespace walberla
+{
+namespace lbm_mesapd_coupling
+{
+namespace psm
+{
+namespace gpu
+{
+
+template< typename ParticleAccessor_T, typename ParticleSelector_T, int Weighting_T >
+class SetParticleVelocitiesSweep
+{
+ public:
+   SetParticleVelocitiesSweep(const shared_ptr< StructuredBlockStorage >& bs,
+                              const shared_ptr< ParticleAccessor_T >& ac,
+                              const ParticleSelector_T& mappingParticleSelector,
+                              ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA)
+      : bs_(bs), ac_(ac), mappingParticleSelector_(mappingParticleSelector),
+        particleAndVolumeFractionSoA_(particleAndVolumeFractionSoA)
+   {}
+   void operator()(IBlock* block)
+   {
+      // Check that uids of the particles have not changed since the last mapping to avoid incorrect indices
+      std::vector< walberla::id_t > currentUIDs;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_)) { currentUIDs.push_back(ac_->getUid(idx)); }
+      }
+      WALBERLA_ASSERT(particleAndVolumeFractionSoA_.mappingUIDs == currentUIDs);
+
+      size_t numMappedParticles = 0;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_)) { numMappedParticles++; }
+      }
+
+      if (numMappedParticles == uint_t(0)) return;
+
+      size_t arraySizes = numMappedParticles * sizeof(real_t) * 3;
+
+      // Allocate memory for the particle information required for computing the velocity at a WF point (used in
+      // the solid collision operator)
+      real_t* linearVelocities_h  = (real_t*) malloc(arraySizes);
+      real_t* angularVelocities_h = (real_t*) malloc(arraySizes);
+
+      // Store particle information inside memory to communicate information to the GPU
+      size_t idxMapped = 0;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_))
+         {
+            for (size_t d = 0; d < 3; ++d)
+            {
+               linearVelocities_h[idxMapped * 3 + d]  = ac_->getLinearVelocity(idx)[d];
+               angularVelocities_h[idxMapped * 3 + d] = ac_->getAngularVelocity(idx)[d];
+            }
+            idxMapped++;
+         }
+      }
+
+      real_t* linearVelocities;
+      WALBERLA_GPU_CHECK(gpuMalloc(&linearVelocities, arraySizes));
+      WALBERLA_GPU_CHECK(gpuMemcpy(linearVelocities, linearVelocities_h, arraySizes, gpuMemcpyHostToDevice));
+      real_t* angularVelocities;
+      WALBERLA_GPU_CHECK(gpuMalloc(&angularVelocities, arraySizes));
+      WALBERLA_GPU_CHECK(gpuMemcpy(angularVelocities, angularVelocities_h, arraySizes, gpuMemcpyHostToDevice));
+
+      auto nOverlappingParticlesField =
+         block->getData< nOverlappingParticlesFieldGPU_T >(particleAndVolumeFractionSoA_.nOverlappingParticlesFieldID);
+      auto idxField = block->getData< idxFieldGPU_T >(particleAndVolumeFractionSoA_.idxFieldID);
+      auto particleVelocitiesField =
+         block->getData< particleVelocitiesFieldGPU_T >(particleAndVolumeFractionSoA_.particleVelocitiesFieldID);
+
+      // For every cell, compute the particle velocities of the overlapping particles evaluated at the cell center
+      auto velocitiesKernel = walberla::gpu::make_kernel(&(SetParticleVelocities));
+      velocitiesKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< uint_t >::xyz(*nOverlappingParticlesField));
+      velocitiesKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< id_t >::xyz(*idxField));
+      velocitiesKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< real_t >::xyz(*particleVelocitiesField));
+      velocitiesKernel.addParam(linearVelocities);
+      velocitiesKernel.addParam(angularVelocities);
+      velocitiesKernel.addParam(particleAndVolumeFractionSoA_.positions);
+      const double3 blockStart = { block->getAABB().minCorner()[0], block->getAABB().minCorner()[1],
+                                   block->getAABB().minCorner()[2] };
+      velocitiesKernel.addParam(blockStart);
+      velocitiesKernel.addParam(block->getAABB().xSize() / real_t(nOverlappingParticlesField->xSize()));
+      velocitiesKernel();
+
+      WALBERLA_GPU_CHECK(gpuFree(linearVelocities));
+      free(linearVelocities_h);
+
+      WALBERLA_GPU_CHECK(gpuFree(angularVelocities));
+      free(angularVelocities_h);
+   }
+
+ private:
+   shared_ptr< StructuredBlockStorage > bs_;
+   const shared_ptr< ParticleAccessor_T > ac_;
+   const ParticleSelector_T& mappingParticleSelector_;
+   ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA_;
+};
+
+template< typename ParticleAccessor_T, typename ParticleSelector_T, int Weighting_T >
+class ReduceParticleForcesSweep
+{
+ public:
+   ReduceParticleForcesSweep(const shared_ptr< StructuredBlockStorage >& bs, const shared_ptr< ParticleAccessor_T >& ac,
+                             const ParticleSelector_T& mappingParticleSelector,
+                             const ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA)
+      : bs_(bs), ac_(ac), mappingParticleSelector_(mappingParticleSelector),
+        particleAndVolumeFractionSoA_(particleAndVolumeFractionSoA)
+   {}
+   void operator()(IBlock* block)
+   {
+      // Check that uids of the particles have not changed since the last mapping to avoid incorrect indices
+      std::vector< walberla::id_t > currentUIDs;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_)) { currentUIDs.push_back(ac_->getUid(idx)); }
+      }
+      WALBERLA_ASSERT(particleAndVolumeFractionSoA_.mappingUIDs == currentUIDs);
+
+      const real_t dxCurrentLevel      = bs_->dx(bs_->getLevel(*block));
+      const real_t lengthScalingFactor = dxCurrentLevel;
+      const real_t forceScalingFactor  = lengthScalingFactor * lengthScalingFactor;
+
+      size_t numMappedParticles = 0;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_)) { numMappedParticles++; }
+      }
+
+      if (numMappedParticles == uint_t(0)) return;
+
+      size_t arraySizes = numMappedParticles * sizeof(real_t) * 3;
+
+      // Allocate memory for the reduction of the particle forces and torques on the GPU
+      real_t* hydrodynamicForces;
+      WALBERLA_GPU_CHECK(gpuMalloc(&hydrodynamicForces, arraySizes));
+      WALBERLA_GPU_CHECK(gpuMemset(hydrodynamicForces, 0, arraySizes));
+
+      real_t* hydrodynamicTorques;
+      WALBERLA_GPU_CHECK(gpuMalloc(&hydrodynamicTorques, arraySizes));
+      WALBERLA_GPU_CHECK(gpuMemset(hydrodynamicTorques, 0, arraySizes));
+
+      auto nOverlappingParticlesField =
+         block->getData< nOverlappingParticlesFieldGPU_T >(particleAndVolumeFractionSoA_.nOverlappingParticlesFieldID);
+      auto idxField = block->getData< idxFieldGPU_T >(particleAndVolumeFractionSoA_.idxFieldID);
+      auto particleForcesField =
+         block->getData< particleForcesFieldGPU_T >(particleAndVolumeFractionSoA_.particleForcesFieldID);
+
+      const double3 blockStart = { block->getAABB().minCorner()[0], block->getAABB().minCorner()[1],
+                                   block->getAABB().minCorner()[2] };
+
+      // For every cell, reduce the hydrodynamic forces and torques of the overlapping particles
+      auto forcesKernel = walberla::gpu::make_kernel(&(ReduceParticleForces));
+      forcesKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< uint_t >::xyz(*nOverlappingParticlesField));
+      forcesKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< id_t >::xyz(*idxField));
+      forcesKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< real_t >::xyz(*particleForcesField));
+      forcesKernel.addParam(hydrodynamicForces);
+      forcesKernel.addParam(hydrodynamicTorques);
+      forcesKernel.addParam(particleAndVolumeFractionSoA_.positions);
+      forcesKernel.addParam(blockStart);
+      forcesKernel.addParam(block->getAABB().xSize() / real_t(nOverlappingParticlesField->xSize()));
+      forcesKernel.addParam(forceScalingFactor);
+      forcesKernel();
+
+      WALBERLA_GPU_CHECK(gpuDeviceSynchronize());
+
+      real_t* hydrodynamicForces_h = (real_t*) malloc(arraySizes);
+      WALBERLA_GPU_CHECK(gpuMemcpy(hydrodynamicForces_h, hydrodynamicForces, arraySizes, gpuMemcpyDeviceToHost));
+
+      real_t* hydrodynamicTorques_h = (real_t*) malloc(arraySizes);
+      WALBERLA_GPU_CHECK(gpuMemcpy(hydrodynamicTorques_h, hydrodynamicTorques, arraySizes, gpuMemcpyDeviceToHost));
+
+      // Copy forces and torques of particles from GPU to CPU
+      size_t idxMapped = 0;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_))
+         {
+            for (size_t d = 0; d < 3; ++d)
+            {
+               ac_->getHydrodynamicForceRef(idx)[d] += hydrodynamicForces_h[idxMapped * 3 + d];
+               ac_->getHydrodynamicTorqueRef(idx)[d] += hydrodynamicTorques_h[idxMapped * 3 + d];
+            }
+            idxMapped++;
+         }
+      }
+
+      WALBERLA_GPU_CHECK(gpuFree(hydrodynamicForces));
+      free(hydrodynamicForces_h);
+
+      WALBERLA_GPU_CHECK(gpuFree(hydrodynamicTorques));
+      free(hydrodynamicTorques_h);
+   }
+
+ private:
+   shared_ptr< StructuredBlockStorage > bs_;
+   const shared_ptr< ParticleAccessor_T > ac_;
+   const ParticleSelector_T& mappingParticleSelector_;
+   const ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA_;
+};
+
+} // namespace gpu
+} // namespace psm
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingKernels.cpp b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingKernels.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..43d8c9786c6153aad985528fc249a69897dd4451
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingKernels.cpp
@@ -0,0 +1,23 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file ParticleAndVolumeFractionMappingKernels.cpp
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//! \brief Mirror ParticleAndVolumeFractionMappingKernels.cu to provide a .cpp file for HIP
+//
+//======================================================================================================================
+
+#include "ParticleAndVolumeFractionMappingKernels.cu"
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingKernels.cu b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingKernels.cu
new file mode 100644
index 0000000000000000000000000000000000000000..fe22d4eb1dd4e1d75eeb7c8c5a7bd137a15a6b64
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingKernels.cu
@@ -0,0 +1,313 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file ParticleAndVolumeFractionMappingKernels.cu
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#include "lbm_mesapd_coupling/DataTypesCodegen.h"
+
+#include <assert.h>
+
+#include "ParticleAndVolumeFractionMappingKernels.h"
+
+namespace walberla
+{
+namespace lbm_mesapd_coupling
+{
+namespace psm
+{
+namespace gpu
+{
+
+// Functions to calculate Bs
+template< int Weighting_T >
+__device__ void calculateWeighting(real_t* __restrict__ const weighting, const real_t& /*epsilon*/,
+                                   const real_t& /*tau*/)
+{
+   WALBERLA_STATIC_ASSERT(Weighting_T == 1 || Weighting_T == 2);
+}
+template<>
+__device__ void calculateWeighting< 1 >(real_t* __restrict__ const weighting, const real_t& epsilon,
+                                        const real_t& /*tau*/)
+{
+   *weighting = epsilon;
+}
+template<>
+__device__ void calculateWeighting< 2 >(real_t* __restrict__ const weighting, const real_t& epsilon, const real_t& tau)
+{
+   *weighting = epsilon * (tau - real_t(0.5)) / ((real_t(1) - epsilon) + (tau - real_t(0.5)));
+}
+
+template< int Weighting_T >
+__global__ void superSampling(walberla::gpu::FieldAccessor< uint_t > nOverlappingParticlesField,
+                              walberla::gpu::FieldAccessor< real_t > BsField,
+                              walberla::gpu::FieldAccessor< id_t > idxField,
+                              walberla::gpu::FieldAccessor< real_t > BField, const real_t omega,
+                              const real_t* __restrict__ const spherePositions,
+                              const real_t* __restrict__ const sphereRadii, const double3 blockStart, const real_t dx,
+                              const int3 nSamples, const size_t* __restrict__ const numParticlesSubBlocks,
+                              const size_t* __restrict__ const particleIDsSubBlocks, const uint3 subBlocksPerDim)
+{
+   const uint3 blockIdx_uint3  = make_uint3(blockIdx.x, blockIdx.y, blockIdx.z);
+   const uint3 threadIdx_uint3 = make_uint3(threadIdx.x, threadIdx.y, threadIdx.z);
+
+   nOverlappingParticlesField.set(blockIdx_uint3, threadIdx_uint3);
+   BsField.set(blockIdx_uint3, threadIdx_uint3);
+   idxField.set(blockIdx_uint3, threadIdx_uint3);
+   BField.set(blockIdx_uint3, threadIdx_uint3);
+
+   // Clear the fields
+   for (uint i = 0; i < MaxParticlesPerCell; i++)
+   {
+      BsField.get(i)  = real_t(0.0);
+      idxField.get(i) = size_t(0);
+   }
+   nOverlappingParticlesField.get() = uint_t(0);
+   BField.get()                     = real_t(0.0);
+
+   double3 sampleDistance = { 1.0 / (nSamples.x + 1) * dx, 1.0 / (nSamples.y + 1) * dx, 1.0 / (nSamples.z + 1) * dx };
+   double3 startSamplingPoint = { (blockStart.x + threadIdx.x * dx + sampleDistance.x),
+                                  (blockStart.y + blockIdx.x * dx + sampleDistance.y),
+                                  (blockStart.z + blockIdx.y * dx + sampleDistance.z) };
+   const ulong3 subBlockIndex = { size_t(real_t(threadIdx.x) / blockDim.x * real_t(subBlocksPerDim.x)),
+                                  size_t(real_t(blockIdx.x) / gridDim.x * real_t(subBlocksPerDim.y)),
+                                  size_t(real_t(blockIdx.y) / gridDim.y * real_t(subBlocksPerDim.z)) };
+   size_t linearizedSubBlockIndex =
+      subBlockIndex.z * subBlocksPerDim.x * subBlocksPerDim.y + subBlockIndex.y * subBlocksPerDim.x + subBlockIndex.x;
+
+   for (uint i = 0; i < numParticlesSubBlocks[linearizedSubBlockIndex]; i++)
+   {
+      // SoA
+      size_t idxMapped =
+         particleIDsSubBlocks[linearizedSubBlockIndex + i * subBlocksPerDim.x * subBlocksPerDim.y * subBlocksPerDim.z];
+      double3 currentSamplingPoint = startSamplingPoint;
+
+      double3 minCornerSphere = { spherePositions[idxMapped * 3] - sphereRadii[idxMapped],
+                                  spherePositions[idxMapped * 3 + 1] - sphereRadii[idxMapped],
+                                  spherePositions[idxMapped * 3 + 2] - sphereRadii[idxMapped] };
+      double3 maxCornerSphere = { spherePositions[idxMapped * 3] + sphereRadii[idxMapped],
+                                  spherePositions[idxMapped * 3 + 1] + sphereRadii[idxMapped],
+                                  spherePositions[idxMapped * 3 + 2] + sphereRadii[idxMapped] };
+
+      double overlapFraction = 0.0;
+
+      if (startSamplingPoint.x + dx > minCornerSphere.x && startSamplingPoint.x < maxCornerSphere.x &&
+          startSamplingPoint.y + dx > minCornerSphere.y && startSamplingPoint.y < maxCornerSphere.y &&
+          startSamplingPoint.z + dx > minCornerSphere.z && startSamplingPoint.z < maxCornerSphere.z)
+      {
+         for (uint_t z = 0; z < nSamples.z; z++)
+         {
+            currentSamplingPoint.y = startSamplingPoint.y;
+            for (uint_t y = 0; y < nSamples.y; y++)
+            {
+               currentSamplingPoint.x = startSamplingPoint.x;
+               for (uint_t x = 0; x < nSamples.x; x++)
+               {
+                  if ((currentSamplingPoint.x - spherePositions[idxMapped * 3]) *
+                            (currentSamplingPoint.x - spherePositions[idxMapped * 3]) +
+                         (currentSamplingPoint.y - spherePositions[idxMapped * 3 + 1]) *
+                            (currentSamplingPoint.y - spherePositions[idxMapped * 3 + 1]) +
+                         (currentSamplingPoint.z - spherePositions[idxMapped * 3 + 2]) *
+                            (currentSamplingPoint.z - spherePositions[idxMapped * 3 + 2]) <=
+                      sphereRadii[idxMapped] * sphereRadii[idxMapped])
+                  {
+                     overlapFraction += 1.0;
+                  }
+                  currentSamplingPoint.x += sampleDistance.x;
+               }
+               currentSamplingPoint.y += sampleDistance.y;
+            }
+            currentSamplingPoint.z += sampleDistance.z;
+         }
+
+         // store overlap fraction only if there is an intersection
+         if (overlapFraction > 0.0)
+         {
+            assert(nOverlappingParticlesField.get() < MaxParticlesPerCell);
+            BsField.get(nOverlappingParticlesField.get()) = overlapFraction;
+            BsField.get(nOverlappingParticlesField.get()) *= 1.0 / (nSamples.x * nSamples.y * nSamples.z);
+            calculateWeighting< Weighting_T >(&BsField.get(nOverlappingParticlesField.get()),
+                                              BsField.get(nOverlappingParticlesField.get()), real_t(1.0) / omega);
+            idxField.get(nOverlappingParticlesField.get()) = idxMapped;
+            BField.get() += BsField.get(nOverlappingParticlesField.get());
+            nOverlappingParticlesField.get() += 1;
+         }
+      }
+   }
+
+   // Normalize fraction field (Bs) if sum over all fractions (B) > 1
+   if (BField.get() > 1)
+   {
+      for (uint i = 0; i < nOverlappingParticlesField.get(); i++)
+      {
+         BsField.get(i) /= BField.get();
+      }
+      BField.get() = 1.0;
+   }
+}
+
+// Based on the following paper: https://doi.org/10.1108/EC-02-2016-0052
+template< int Weighting_T >
+__global__ void
+   linearApproximation(walberla::gpu::FieldAccessor< uint_t > nOverlappingParticlesField,
+                       walberla::gpu::FieldAccessor< real_t > BsField, walberla::gpu::FieldAccessor< id_t > idxField,
+                       walberla::gpu::FieldAccessor< real_t > BField, const real_t omega,
+                       const real_t* __restrict__ const spherePositions, const real_t* __restrict__ const sphereRadii,
+                       const real_t* __restrict__ const f_rs, const double3 blockStart, const real_t dx,
+                       const size_t* __restrict__ const numParticlesSubBlocks,
+                       const size_t* __restrict__ const particleIDsSubBlocks, const uint3 subBlocksPerDim)
+{
+   const uint3 blockIdx_uint3  = make_uint3(blockIdx.x, blockIdx.y, blockIdx.z);
+   const uint3 threadIdx_uint3 = make_uint3(threadIdx.x, threadIdx.y, threadIdx.z);
+
+   nOverlappingParticlesField.set(blockIdx_uint3, threadIdx_uint3);
+   BsField.set(blockIdx_uint3, threadIdx_uint3);
+   idxField.set(blockIdx_uint3, threadIdx_uint3);
+   BField.set(blockIdx_uint3, threadIdx_uint3);
+
+   // Clear the fields
+   for (uint i = 0; i < MaxParticlesPerCell; i++)
+   {
+      BsField.get(i)  = real_t(0.0);
+      idxField.get(i) = size_t(0);
+   }
+   nOverlappingParticlesField.get() = uint_t(0);
+   BField.get()                     = real_t(0.0);
+
+   const double3 cellCenter   = { (blockStart.x + (threadIdx.x + 0.5) * dx), (blockStart.y + (blockIdx.x + 0.5) * dx),
+                                  (blockStart.z + (blockIdx.y + 0.5) * dx) };
+   const ulong3 subBlockIndex = { size_t(real_t(threadIdx.x) / blockDim.x * real_t(subBlocksPerDim.x)),
+                                  size_t(real_t(blockIdx.x) / gridDim.x * real_t(subBlocksPerDim.y)),
+                                  size_t(real_t(blockIdx.y) / gridDim.y * real_t(subBlocksPerDim.z)) };
+   size_t linearizedSubBlockIndex =
+      subBlockIndex.z * subBlocksPerDim.x * subBlocksPerDim.y + subBlockIndex.y * subBlocksPerDim.x + subBlockIndex.x;
+
+   for (uint i = 0; i < numParticlesSubBlocks[linearizedSubBlockIndex]; i++)
+   {
+      size_t idxMapped =
+         particleIDsSubBlocks[linearizedSubBlockIndex + i * subBlocksPerDim.x * subBlocksPerDim.y * subBlocksPerDim.z];
+      double3 minCornerSphere = { spherePositions[idxMapped * 3] - sphereRadii[idxMapped],
+                                  spherePositions[idxMapped * 3 + 1] - sphereRadii[idxMapped],
+                                  spherePositions[idxMapped * 3 + 2] - sphereRadii[idxMapped] };
+      double3 maxCornerSphere = { spherePositions[idxMapped * 3] + sphereRadii[idxMapped],
+                                  spherePositions[idxMapped * 3 + 1] + sphereRadii[idxMapped],
+                                  spherePositions[idxMapped * 3 + 2] + sphereRadii[idxMapped] };
+      if (cellCenter.x + dx > minCornerSphere.x && cellCenter.x - dx < maxCornerSphere.x &&
+          cellCenter.y + dx > minCornerSphere.y && cellCenter.y - dx < maxCornerSphere.y &&
+          cellCenter.z + dx > minCornerSphere.z && cellCenter.z - dx < maxCornerSphere.z)
+      {
+         const double3 cellSphereVector = { spherePositions[idxMapped * 3] - cellCenter.x,
+                                            spherePositions[idxMapped * 3 + 1] - cellCenter.y,
+                                            spherePositions[idxMapped * 3 + 2] - cellCenter.z };
+
+         const real_t D = sqrt(cellSphereVector.x * cellSphereVector.x + cellSphereVector.y * cellSphereVector.y +
+                               cellSphereVector.z * cellSphereVector.z) -
+                          sphereRadii[idxMapped];
+
+         real_t epsilon = -D + f_rs[idxMapped];
+         epsilon        = max(epsilon, 0.0);
+         epsilon        = min(epsilon, 1.0);
+
+         // Store overlap fraction only if there is an intersection
+         if (epsilon > 0.0)
+         {
+            // Check that the maximum number of overlapping particles has not yet been reached
+            assert(nOverlappingParticlesField.get() < MaxParticlesPerCell);
+            BsField.get(nOverlappingParticlesField.get()) = epsilon;
+            calculateWeighting< Weighting_T >(&BsField.get(nOverlappingParticlesField.get()),
+                                              BsField.get(nOverlappingParticlesField.get()), real_t(1.0) / omega);
+            idxField.get(nOverlappingParticlesField.get()) = idxMapped;
+            BField.get() += BsField.get(nOverlappingParticlesField.get());
+            nOverlappingParticlesField.get() += 1;
+         }
+      }
+   }
+
+   // Normalize fraction field (Bs) if sum over all fractions (B) > 1
+   if (BField.get() > 1)
+   {
+      for (uint i = 0; i < nOverlappingParticlesField.get(); i++)
+      {
+         BsField.get(i) /= BField.get();
+      }
+      BField.get() = 1.0;
+   }
+}
+
+template< int Weighting_T >
+__global__ void boxMapping(walberla::gpu::FieldAccessor< uint_t > nOverlappingParticlesField,
+                           walberla::gpu::FieldAccessor< real_t > BsField,
+                           walberla::gpu::FieldAccessor< id_t > idxField, walberla::gpu::FieldAccessor< real_t > BField,
+                           const real_t omega, const double3 boxPositionMin, const double3 boxPositionMax,
+                           const double3 blockStart, const real_t dx, const id_t idxMapped)
+{
+   const uint3 blockIdx_uint3  = make_uint3(blockIdx.x, blockIdx.y, blockIdx.z);
+   const uint3 threadIdx_uint3 = make_uint3(threadIdx.x, threadIdx.y, threadIdx.z);
+
+   nOverlappingParticlesField.set(blockIdx_uint3, threadIdx_uint3);
+   BsField.set(blockIdx_uint3, threadIdx_uint3);
+   idxField.set(blockIdx_uint3, threadIdx_uint3);
+   BField.set(blockIdx_uint3, threadIdx_uint3);
+
+   const double3 cellCenter = { (blockStart.x + (threadIdx.x + 0.5) * dx), (blockStart.y + (blockIdx.x + 0.5) * dx),
+                                (blockStart.z + (blockIdx.y + 0.5) * dx) };
+   const double3 cellMin    = { cellCenter.x - dx * real_t(0.5), cellCenter.y - dx * real_t(0.5),
+                                cellCenter.z - dx * real_t(0.5) };
+   const double3 cellMax    = { cellCenter.x + dx * real_t(0.5), cellCenter.y + dx * real_t(0.5),
+                                cellCenter.z + dx * real_t(0.5) };
+
+   const real_t xOverlap        = max(real_t(0), min(boxPositionMax.x, cellMax.x) - max(boxPositionMin.x, cellMin.x));
+   const real_t yOverlap        = max(real_t(0), min(boxPositionMax.y, cellMax.y) - max(boxPositionMin.y, cellMin.y));
+   const real_t zOverlap        = max(real_t(0), min(boxPositionMax.z, cellMax.z) - max(boxPositionMin.z, cellMin.z));
+   const real_t overlapFraction = xOverlap * yOverlap * zOverlap / (dx * dx * dx);
+
+   if (overlapFraction > real_t(0))
+   {
+      assert(nOverlappingParticlesField.get() < MaxParticlesPerCell);
+
+      BsField.get(nOverlappingParticlesField.get()) = overlapFraction;
+      calculateWeighting< Weighting_T >(&BsField.get(nOverlappingParticlesField.get()),
+                                        BsField.get(nOverlappingParticlesField.get()), real_t(1.0) / omega);
+      idxField.get(nOverlappingParticlesField.get()) = idxMapped;
+      BField.get() += BsField.get(nOverlappingParticlesField.get());
+      nOverlappingParticlesField.get() += 1;
+
+      // TODO: it can happen that the BsField for spheres is normalized twice, one here and in the sphere mapping
+      // Normalize fraction field (Bs) if sum over all fractions (B) > 1
+      if (BField.get() > 1)
+      {
+         for (uint i = 0; i < nOverlappingParticlesField.get(); i++)
+         {
+            BsField.get(i) /= BField.get();
+         }
+         BField.get() = 1.0;
+      }
+   }
+}
+
+auto instance0_with_weighting_1 = superSampling< 1 >;
+auto instance1_with_weighting_2 = superSampling< 2 >;
+auto instance2_with_weighting_1 = linearApproximation< 1 >;
+auto instance3_with_weighting_2 = linearApproximation< 2 >;
+auto instance4_with_weighting_1 = boxMapping< 1 >;
+auto instance5_with_weighting_2 = boxMapping< 2 >;
+
+} // namespace gpu
+} // namespace psm
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingKernels.h b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingKernels.h
new file mode 100644
index 0000000000000000000000000000000000000000..32ee62c3bcc87d401f7cc0962e7a25fdf888f22c
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingKernels.h
@@ -0,0 +1,68 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file ParticleAndVolumeFractionMappingKernels.h
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/DataTypes.h"
+#include "core/math/Vector3.h"
+
+#include "gpu/FieldAccessor.h"
+
+namespace walberla
+{
+namespace lbm_mesapd_coupling
+{
+namespace psm
+{
+namespace gpu
+{
+
+template< int Weighting_T >
+__global__ void superSampling(walberla::gpu::FieldAccessor< uint_t > nOverlappingParticlesField,
+                              walberla::gpu::FieldAccessor< real_t > BsField,
+                              walberla::gpu::FieldAccessor< id_t > idxField,
+                              walberla::gpu::FieldAccessor< real_t > BField, const real_t omega,
+                              const real_t* __restrict__ const spherePositions,
+                              const real_t* __restrict__ const sphereRadii, const double3 blockStart, const real_t dx,
+                              const int3 nSamples, const size_t* __restrict__ const numParticlesSubBlocks,
+                              const size_t* __restrict__ const particleIDsSubBlocks, const uint3 subBlocksPerDim);
+
+template< int Weighting_T >
+__global__ void
+   linearApproximation(walberla::gpu::FieldAccessor< uint_t > nOverlappingParticlesField,
+                       walberla::gpu::FieldAccessor< real_t > BsField, walberla::gpu::FieldAccessor< id_t > idxField,
+                       walberla::gpu::FieldAccessor< real_t > BField, const real_t omega,
+                       const real_t* __restrict__ const spherePositions, const real_t* __restrict__ const sphereRadii,
+                       const real_t* __restrict__ const f_rs, const double3 blockStart, const real_t dx,
+                       const size_t* __restrict__ const numParticlesSubBlocks,
+                       const size_t* __restrict__ const particleIDsSubBlocks, const uint3 subBlocksPerDim);
+
+template< int Weighting_T >
+__global__ void boxMapping(walberla::gpu::FieldAccessor< uint_t > nOverlappingParticlesField,
+                           walberla::gpu::FieldAccessor< real_t > BsField,
+                           walberla::gpu::FieldAccessor< id_t > idxField, walberla::gpu::FieldAccessor< real_t > BField,
+                           const real_t omega, const double3 boxPositionMin, const double3 boxPositionMax,
+                           const double3 blockStart, const real_t dx, const id_t idxMapped);
+
+} // namespace gpu
+} // namespace psm
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingSweepsCPU.h b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingSweepsCPU.h
new file mode 100644
index 0000000000000000000000000000000000000000..1ab47b344512d165b8e6c7d5718179f5725a3525
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingSweepsCPU.h
@@ -0,0 +1,318 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file ParticleAndVolumeFractionMappingSweepsCPU.h
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+#include "lbm_mesapd_coupling/DataTypesCodegen.h"
+#include "lbm_mesapd_coupling/mapping/ParticleBoundingBox.h"
+#include "lbm_mesapd_coupling/utility/ParticleSelector.h"
+
+#include "mesa_pd/common/AABBConversion.h"
+#include "mesa_pd/data/ParticleAccessorWithShape.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/kernel/SingleCast.h"
+
+#include <cassert>
+#include <functional>
+#include <mesa_pd/data/ParticleStorage.h>
+#include <mesa_pd/data/shape/Sphere.h>
+
+namespace walberla
+{
+namespace lbm_mesapd_coupling
+{
+namespace psm
+{
+// The CPU version is on purpose in the gpu namespace to avoid changes in the application codes
+namespace gpu
+{
+
+template< int Weighting_T >
+void calculateWeighting(real_t* const weighting, const real_t& /*epsilon*/, const real_t& /*tau*/)
+{
+   WALBERLA_STATIC_ASSERT(Weighting_T == 1 || Weighting_T == 2);
+}
+
+template<>
+void calculateWeighting< 1 >(real_t* const weighting, const real_t& epsilon, const real_t& /*tau*/)
+{
+   *weighting = epsilon;
+}
+template<>
+void calculateWeighting< 2 >(real_t* const weighting, const real_t& epsilon, const real_t& tau)
+{
+   *weighting = epsilon * (tau - real_t(0.5)) / ((real_t(1) - epsilon) + (tau - real_t(0.5)));
+}
+
+template< int Weighting_T >
+void mapParticles(IBlock& blockIt, const ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA,
+                  const real_t* const spherePositions, const real_t* const sphereRadii, const real_t* const f_rs,
+                  const size_t* const numParticlesSubBlocks, const size_t* const particleIDsSubBlocks,
+                  const Vector3< uint_t > subBlocksPerDim)
+{
+   auto nOverlappingParticlesField =
+      blockIt.getData< nOverlappingParticlesField_T >(particleAndVolumeFractionSoA.nOverlappingParticlesFieldID);
+   auto BsField  = blockIt.getData< BsField_T >(particleAndVolumeFractionSoA.BsFieldID);
+   auto idxField = blockIt.getData< idxField_T >(particleAndVolumeFractionSoA.idxFieldID);
+   auto BField   = blockIt.getData< BField_T >(particleAndVolumeFractionSoA.BFieldID);
+
+   real_t dx = blockIt.getAABB().xSize() / real_t(nOverlappingParticlesField->xSize());
+
+   WALBERLA_FOR_ALL_CELLS_XYZ(
+      BField,
+      for (size_t i = 0; i < MaxParticlesPerCell; i++) {
+         BsField->get(x, y, z, i)  = real_t(0.0);
+         idxField->get(x, y, z, i) = size_t(0);
+      } nOverlappingParticlesField->get(x, y, z) = uint_t(0);
+      BField->get(x, y, z)                       = real_t(0.0);
+      const Vector3< real_t > cellCenter =
+         Vector3< real_t >(real_t(x) + real_t(0.5) * dx, real_t(y) + real_t(0.5) * dx, real_t(z) + real_t(0.5) * dx) +
+         blockIt.getAABB().minCorner();
+      const Vector3< size_t > subBlockIndex(size_t(real_t(x) / blockIt.getAABB().xSize() * real_t(subBlocksPerDim[0])),
+                                            size_t(real_t(y) / blockIt.getAABB().ySize() * real_t(subBlocksPerDim[1])),
+                                            size_t(real_t(z) / blockIt.getAABB().zSize() * real_t(subBlocksPerDim[2])));
+      const size_t linearizedSubBlockIndex = subBlockIndex[2] * subBlocksPerDim[0] * subBlocksPerDim[1] +
+                                             subBlockIndex[1] * subBlocksPerDim[0] + subBlockIndex[0];
+
+      for (size_t i = 0; i < numParticlesSubBlocks[linearizedSubBlockIndex]; i++) {
+         size_t idxMapped = particleIDsSubBlocks[linearizedSubBlockIndex +
+                                                 i * subBlocksPerDim[0] * subBlocksPerDim[1] * subBlocksPerDim[2]];
+         const Vector3< real_t > minCornerSphere(spherePositions[idxMapped * 3] - sphereRadii[idxMapped],
+                                                 spherePositions[idxMapped * 3 + 1] - sphereRadii[idxMapped],
+                                                 spherePositions[idxMapped * 3 + 2] - sphereRadii[idxMapped]);
+         const Vector3< real_t > maxCornerSphere(spherePositions[idxMapped * 3] + sphereRadii[idxMapped],
+                                                 spherePositions[idxMapped * 3 + 1] + sphereRadii[idxMapped],
+                                                 spherePositions[idxMapped * 3 + 2] + sphereRadii[idxMapped]);
+         if (cellCenter[0] + dx > minCornerSphere[0] && cellCenter[0] - dx < maxCornerSphere[0] &&
+             cellCenter[1] + dx > minCornerSphere[1] && cellCenter[1] - dx < maxCornerSphere[1] &&
+             cellCenter[2] + dx > minCornerSphere[2] && cellCenter[2] - dx < maxCornerSphere[2])
+         {
+            const Vector3< real_t > cellSphereVector(spherePositions[idxMapped * 3] - cellCenter[0],
+                                                     spherePositions[idxMapped * 3 + 1] - cellCenter[1],
+                                                     spherePositions[idxMapped * 3 + 2] - cellCenter[2]);
+
+            const real_t D =
+               real_t(sqrt(cellSphereVector[0] * cellSphereVector[0] + cellSphereVector[1] * cellSphereVector[1] +
+                           cellSphereVector[2] * cellSphereVector[2])) -
+               sphereRadii[idxMapped];
+
+            real_t epsilon = -D + f_rs[idxMapped];
+            epsilon        = std::max(epsilon, real_t(0));
+            epsilon        = std::min(epsilon, real_t(1));
+
+            // Store overlap fraction only if there is an intersection
+            if (epsilon > 0.0)
+            {
+               // Check that the maximum number of overlapping particles has not yet been reached
+               assert(nOverlappingParticlesField->get(x, y, z) < MaxParticlesPerCell);
+               BsField->get(x, y, z, nOverlappingParticlesField->get(x, y, z)) = epsilon;
+               calculateWeighting< Weighting_T >(&BsField->get(x, y, z, nOverlappingParticlesField->get(x, y, z)),
+                                                 BsField->get(x, y, z, nOverlappingParticlesField->get(x, y, z)),
+                                                 real_t(1.0) / particleAndVolumeFractionSoA.omega_);
+               idxField->get(x, y, z, nOverlappingParticlesField->get(x, y, z)) = idxMapped;
+               BField->get(x, y, z) += BsField->get(x, y, z, nOverlappingParticlesField->get(x, y, z));
+               nOverlappingParticlesField->get(x, y, z) += 1;
+            }
+         }
+      }
+
+      // Normalize fraction field (Bs) if sum over all fractions (B) > 1
+      if (BField->get(x, y, z) > 1) {
+         for (size_t i = 0; i < nOverlappingParticlesField->get(x, y, z); i++)
+         {
+            BsField->get(x, y, z, i) /= BField->get(x, y, z);
+         }
+         BField->get(x, y, z) = 1.0;
+      })
+}
+
+template< typename ParticleAccessor_T, typename ParticleSelector_T, int Weighting_T >
+class SphereFractionMappingSweep
+{
+ public:
+   SphereFractionMappingSweep(const shared_ptr< StructuredBlockStorage >& blockStorage,
+                              const shared_ptr< ParticleAccessor_T >& ac,
+                              const ParticleSelector_T& mappingParticleSelector,
+                              ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA,
+                              const Vector3< uint_t > subBlockSize)
+      : blockStorage_(blockStorage), ac_(ac), mappingParticleSelector_(mappingParticleSelector),
+        particleAndVolumeFractionSoA_(particleAndVolumeFractionSoA), subBlockSize_(subBlockSize)
+   {
+      static_assert(std::is_base_of< mesa_pd::data::IAccessor, ParticleAccessor_T >::value,
+                    "Provide a valid accessor as template");
+      for (auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt)
+      {
+         auto aabb = blockIt->getAABB();
+         if (size_t(aabb.xSize()) % subBlockSize_[0] != 0 || size_t(aabb.ySize()) % subBlockSize_[1] != 0 ||
+             size_t(aabb.zSize()) % subBlockSize_[2] != 0)
+         {
+            WALBERLA_ABORT("Number of cells per block (" << aabb << ") is not divisible by subBlockSize ("
+                                                         << subBlockSize_ << ").")
+         }
+      }
+   }
+
+   void operator()(IBlock* block)
+   {
+      size_t numMappedParticles = 0;
+      particleAndVolumeFractionSoA_.mappingUIDs.clear();
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_))
+         {
+            numMappedParticles++;
+            // Store UIDs to make sure that the particles have not changed between the mapping and the PSM sweep
+            particleAndVolumeFractionSoA_.mappingUIDs.push_back(ac_->getUid(idx));
+         }
+      }
+
+      if (numMappedParticles == uint_t(0)) return;
+
+      // Allocate memory storing the particle information needed for the overlap fraction computations
+      const size_t scalarArraySize = numMappedParticles * sizeof(real_t);
+
+      if (particleAndVolumeFractionSoA_.positions != nullptr) { free(particleAndVolumeFractionSoA_.positions); }
+      particleAndVolumeFractionSoA_.positions = (real_t*) malloc(3 * scalarArraySize);
+      real_t* radii                           = (real_t*) malloc(scalarArraySize);
+      real_t* f_r = (real_t*) malloc(scalarArraySize); // f_r is described in https://doi.org/10.1108/EC-02-2016-0052
+
+      // Store particle information inside the memory
+      size_t idxMapped = 0;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_))
+         {
+            for (size_t d = 0; d < 3; ++d)
+            {
+               particleAndVolumeFractionSoA_.positions[idxMapped * 3 + d] = ac_->getPosition(idx)[d];
+            }
+            // If other shapes than spheres are mapped, ignore them here
+            if (ac_->getShape(idx)->getShapeType() == mesa_pd::data::Sphere::SHAPE_TYPE)
+            {
+               const real_t radius = static_cast< mesa_pd::data::Sphere* >(ac_->getShape(idx))->getRadius();
+               radii[idxMapped]    = radius;
+               real_t Va           = real_t(
+                  (1.0 / 12.0 - radius * radius) * atan((0.5 * sqrt(radius * radius - 0.5)) / (0.5 - radius * radius)) +
+                  1.0 / 3.0 * sqrt(radius * radius - 0.5) +
+                  (radius * radius - 1.0 / 12.0) * atan(0.5 / sqrt(radius * radius - 0.5)) -
+                  4.0 / 3.0 * radius * radius * radius * atan(0.25 / (radius * sqrt(radius * radius - 0.5))));
+               f_r[idxMapped] = Va - radius + real_t(0.5);
+            }
+            idxMapped++;
+         }
+      }
+
+      // Update fraction mapping
+      // Split the block into sub-blocks and sort the particle indices into each overlapping sub-block. This way, in
+      // the particle mapping, each iteration only has to check the potentially overlapping particles.
+      auto blockAABB = block->getAABB();
+      const Vector3< uint_t > subBlocksPerDim =
+         Vector3< uint_t >(uint_t(blockAABB.xSize()) / subBlockSize_[0], uint_t(blockAABB.ySize()) / subBlockSize_[1],
+                           uint_t(blockAABB.zSize()) / subBlockSize_[2]);
+      const size_t numSubBlocks = subBlocksPerDim[0] * subBlocksPerDim[1] * subBlocksPerDim[2];
+      std::vector< std::vector< size_t > > subBlocks(numSubBlocks);
+
+      idxMapped = 0;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_))
+         {
+            if (ac_->getShape(idx)->getShapeType() == mesa_pd::data::Sphere::SHAPE_TYPE)
+            {
+               auto sphereAABB = mesa_pd::getParticleAABB(idx, *ac_);
+               if (blockAABB.intersects(sphereAABB))
+               {
+                  auto intersectionAABB = blockAABB.getIntersection(sphereAABB);
+                  intersectionAABB.translate(-blockAABB.minCorner());
+                  mesa_pd::Vec3 blockScaling = mesa_pd::Vec3(real_t(subBlocksPerDim[0]) / blockAABB.sizes()[0],
+                                                             real_t(subBlocksPerDim[1]) / blockAABB.sizes()[1],
+                                                             real_t(subBlocksPerDim[2]) / blockAABB.sizes()[2]);
+
+                  for (size_t z = size_t(intersectionAABB.zMin() * blockScaling[2]);
+                       z < size_t(ceil(intersectionAABB.zMax() * blockScaling[2])); ++z)
+                  {
+                     for (size_t y = size_t(intersectionAABB.yMin() * blockScaling[1]);
+                          y < size_t(ceil(intersectionAABB.yMax() * blockScaling[1])); ++y)
+                     {
+                        for (size_t x = size_t(intersectionAABB.xMin() * blockScaling[0]);
+                             x < size_t(ceil(intersectionAABB.xMax() * blockScaling[0])); ++x)
+                        {
+                           size_t index = z * subBlocksPerDim[0] * subBlocksPerDim[1] + y * subBlocksPerDim[0] + x;
+                           subBlocks[index].push_back(idxMapped);
+                        }
+                     }
+                  }
+               }
+            }
+            idxMapped++;
+         }
+      }
+
+      size_t maxParticlesPerSubBlock = 0;
+      std::for_each(subBlocks.begin(), subBlocks.end(), [&maxParticlesPerSubBlock](std::vector< size_t >& subBlock) {
+         maxParticlesPerSubBlock = std::max(maxParticlesPerSubBlock, subBlock.size());
+      });
+
+      size_t* numParticlesPerSubBlock = (size_t*) malloc(numSubBlocks * sizeof(size_t));
+      size_t* particleIDsSubBlocks    = (size_t*) malloc(numSubBlocks * maxParticlesPerSubBlock * sizeof(size_t));
+
+      // Copy data from std::vector to memory
+      for (size_t z = 0; z < subBlocksPerDim[2]; ++z)
+      {
+         for (size_t y = 0; y < subBlocksPerDim[1]; ++y)
+         {
+            for (size_t x = 0; x < subBlocksPerDim[0]; ++x)
+            {
+               size_t index = z * subBlocksPerDim[0] * subBlocksPerDim[1] + y * subBlocksPerDim[0] + x;
+               numParticlesPerSubBlock[index] = subBlocks[index].size();
+               for (size_t k = 0; k < subBlocks[index].size(); k++)
+               {
+                  particleIDsSubBlocks[index + k * numSubBlocks] = subBlocks[index][k];
+               }
+            }
+         }
+      }
+
+      mapParticles(*block, particleAndVolumeFractionSoA_, particleAndVolumeFractionSoA_.positions, radii, f_r,
+                   numParticlesPerSubBlock, particleIDsSubBlocks, subBlocksPerDim);
+
+      free(numParticlesPerSubBlock);
+      free(particleIDsSubBlocks);
+
+      free(radii);
+      free(f_r);
+   }
+
+   shared_ptr< StructuredBlockStorage > blockStorage_;
+   const shared_ptr< ParticleAccessor_T > ac_;
+   const ParticleSelector_T& mappingParticleSelector_;
+   ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA_;
+   const Vector3< uint_t > subBlockSize_;
+};
+
+} // namespace gpu
+} // namespace psm
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingSweepsGPU.h b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingSweepsGPU.h
new file mode 100644
index 0000000000000000000000000000000000000000..7576d9476764d092a5c8c3defa4a85117f34b2d5
--- /dev/null
+++ b/src/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMappingSweepsGPU.h
@@ -0,0 +1,362 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file ParticleAndVolumeFractionMappingSweepsGPU.h
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "domain_decomposition/StructuredBlockStorage.h"
+
+#include "field/GhostLayerField.h"
+
+#include "gpu/AddGPUFieldToStorage.h"
+#include "gpu/FieldCopy.h"
+#include "gpu/FieldIndexing.h"
+#include "gpu/GPUField.h"
+#include "gpu/HostFieldAllocator.h"
+#include "gpu/Kernel.h"
+
+#include "lbm_mesapd_coupling/DataTypesCodegen.h"
+#include "lbm_mesapd_coupling/mapping/ParticleBoundingBox.h"
+#include "lbm_mesapd_coupling/utility/ParticleSelector.h"
+
+#include "mesa_pd/common/AABBConversion.h"
+#include "mesa_pd/data/ParticleAccessorWithShape.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/kernel/SingleCast.h"
+
+#include <functional>
+#include <mesa_pd/data/ParticleStorage.h>
+#include <mesa_pd/data/shape/Sphere.h>
+
+#include "ParticleAndVolumeFractionMappingKernels.h"
+
+namespace walberla
+{
+namespace lbm_mesapd_coupling
+{
+namespace psm
+{
+namespace gpu
+{
+
+template< int Weighting_T >
+void mapParticles(const IBlock& blockIt,
+                  const ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA,
+                  const real_t* const spherePositions, const real_t* const sphereRadii, const real_t* const f_rs,
+                  const size_t* const numParticlesSubBlocks, const size_t* const particleIDsSubBlocks,
+                  const Vector3< uint_t > subBlocksPerDim)
+{
+   auto nOverlappingParticlesField =
+      blockIt.getData< nOverlappingParticlesFieldGPU_T >(particleAndVolumeFractionSoA.nOverlappingParticlesFieldID);
+   auto BsField  = blockIt.getData< BsFieldGPU_T >(particleAndVolumeFractionSoA.BsFieldID);
+   auto idxField = blockIt.getData< idxFieldGPU_T >(particleAndVolumeFractionSoA.idxFieldID);
+   auto BField   = blockIt.getData< BFieldGPU_T >(particleAndVolumeFractionSoA.BFieldID);
+
+   auto myKernel = walberla::gpu::make_kernel(&(linearApproximation< Weighting_T >) );
+   myKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< uint_t >::xyz(*nOverlappingParticlesField));
+   myKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< real_t >::xyz(*BsField));
+   myKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< id_t >::xyz(*idxField));
+   myKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< real_t >::xyz(*BField));
+   myKernel.addParam(particleAndVolumeFractionSoA.omega_);
+   myKernel.addParam(spherePositions);
+   myKernel.addParam(sphereRadii);
+   myKernel.addParam(f_rs);
+   Vector3< real_t > blockStart = blockIt.getAABB().minCorner();
+   myKernel.addParam(double3{ blockStart[0], blockStart[1], blockStart[2] });
+   myKernel.addParam(blockIt.getAABB().xSize() / real_t(nOverlappingParticlesField->xSize()));
+   myKernel.addParam(numParticlesSubBlocks);
+   myKernel.addParam(particleIDsSubBlocks);
+   myKernel.addParam(uint3{ uint(subBlocksPerDim[0]), uint(subBlocksPerDim[1]), uint(subBlocksPerDim[2]) });
+   myKernel();
+}
+
+template< typename ParticleAccessor_T, typename ParticleSelector_T, int Weighting_T >
+class SphereFractionMappingSweep
+{
+ public:
+   SphereFractionMappingSweep(const shared_ptr< StructuredBlockStorage >& blockStorage,
+                              const shared_ptr< ParticleAccessor_T >& ac,
+                              const ParticleSelector_T& mappingParticleSelector,
+                              ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA,
+                              const Vector3< uint_t > subBlockSize)
+      : blockStorage_(blockStorage), ac_(ac), mappingParticleSelector_(mappingParticleSelector),
+        particleAndVolumeFractionSoA_(particleAndVolumeFractionSoA), subBlockSize_(subBlockSize)
+   {
+      static_assert(std::is_base_of< mesa_pd::data::IAccessor, ParticleAccessor_T >::value,
+                    "Provide a valid accessor as template");
+      for (auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt)
+      {
+         auto aabb = blockIt->getAABB();
+         if (size_t(aabb.xSize()) % subBlockSize_[0] != 0 || size_t(aabb.ySize()) % subBlockSize_[1] != 0 ||
+             size_t(aabb.zSize()) % subBlockSize_[2] != 0)
+         {
+            WALBERLA_ABORT("Number of cells per block (" << aabb << ") is not divisible by subBlockSize ("
+                                                         << subBlockSize_ << ").")
+         }
+      }
+   }
+
+   void operator()(IBlock* block)
+   {
+      size_t numMappedParticles = 0;
+      particleAndVolumeFractionSoA_.mappingUIDs.clear();
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_))
+         {
+            numMappedParticles++;
+            // Store UIDs to make sure that the particles have not changed between the mapping and the PSM sweep
+            particleAndVolumeFractionSoA_.mappingUIDs.push_back(ac_->getUid(idx));
+         }
+      }
+
+      if (numMappedParticles == uint_t(0)) return;
+
+      // Allocate memory storing the particle information needed for the overlap fraction computations
+      const size_t scalarArraySize = numMappedParticles * sizeof(real_t);
+
+      if (particleAndVolumeFractionSoA_.positions != nullptr)
+      {
+         WALBERLA_GPU_CHECK(gpuFree(particleAndVolumeFractionSoA_.positions));
+      }
+      real_t* positions_h = (real_t*) malloc(3 * scalarArraySize);
+      real_t* radii_h     = (real_t*) malloc(scalarArraySize);
+      real_t* f_r_h = (real_t*) malloc(scalarArraySize); // f_r is described in https://doi.org/10.1108/EC-02-2016-0052
+
+      // Store particle information inside the memory
+      size_t idxMapped = 0;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_))
+         {
+            for (size_t d = 0; d < 3; ++d)
+            {
+               positions_h[idxMapped * 3 + d] = ac_->getPosition(idx)[d];
+            }
+            // If other shapes than spheres are mapped, ignore them here
+            if (ac_->getShape(idx)->getShapeType() == mesa_pd::data::Sphere::SHAPE_TYPE)
+            {
+               const real_t radius = static_cast< mesa_pd::data::Sphere* >(ac_->getShape(idx))->getRadius();
+               radii_h[idxMapped]  = radius;
+               real_t Va           = real_t(
+                  (1.0 / 12.0 - radius * radius) * atan((0.5 * sqrt(radius * radius - 0.5)) / (0.5 - radius * radius)) +
+                  1.0 / 3.0 * sqrt(radius * radius - 0.5) +
+                  (radius * radius - 1.0 / 12.0) * atan(0.5 / sqrt(radius * radius - 0.5)) -
+                  4.0 / 3.0 * radius * radius * radius * atan(0.25 / (radius * sqrt(radius * radius - 0.5))));
+               f_r_h[idxMapped] = Va - radius + real_t(0.5);
+            }
+            idxMapped++;
+         }
+      }
+
+      // Update fraction mapping
+      // Split the block into sub-blocks and sort the particle indices into each overlapping sub-block. This way, in
+      // the particle mapping, each gpu thread only has to check the potentially overlapping particles.
+      auto blockAABB = block->getAABB();
+      const Vector3< uint_t > subBlocksPerDim =
+         Vector3< uint_t >(uint_t(blockAABB.xSize()) / subBlockSize_[0], uint_t(blockAABB.ySize()) / subBlockSize_[1],
+                           uint_t(blockAABB.zSize()) / subBlockSize_[2]);
+      const size_t numSubBlocks = subBlocksPerDim[0] * subBlocksPerDim[1] * subBlocksPerDim[2];
+      std::vector< std::vector< size_t > > subBlocks(numSubBlocks);
+
+      idxMapped = 0;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_))
+         {
+            if (ac_->getShape(idx)->getShapeType() == mesa_pd::data::Sphere::SHAPE_TYPE)
+            {
+               auto sphereAABB = mesa_pd::getParticleAABB(idx, *ac_);
+               if (blockAABB.intersects(sphereAABB))
+               {
+                  auto intersectionAABB = blockAABB.getIntersection(sphereAABB);
+                  intersectionAABB.translate(-blockAABB.minCorner());
+                  mesa_pd::Vec3 blockScaling = mesa_pd::Vec3(real_t(subBlocksPerDim[0]) / blockAABB.sizes()[0],
+                                                             real_t(subBlocksPerDim[1]) / blockAABB.sizes()[1],
+                                                             real_t(subBlocksPerDim[2]) / blockAABB.sizes()[2]);
+
+                  for (size_t z = size_t(intersectionAABB.zMin() * blockScaling[2]);
+                       z < size_t(ceil(intersectionAABB.zMax() * blockScaling[2])); ++z)
+                  {
+                     for (size_t y = size_t(intersectionAABB.yMin() * blockScaling[1]);
+                          y < size_t(ceil(intersectionAABB.yMax() * blockScaling[1])); ++y)
+                     {
+                        for (size_t x = size_t(intersectionAABB.xMin() * blockScaling[0]);
+                             x < size_t(ceil(intersectionAABB.xMax() * blockScaling[0])); ++x)
+                        {
+                           size_t index = z * subBlocksPerDim[0] * subBlocksPerDim[1] + y * subBlocksPerDim[0] + x;
+                           subBlocks[index].push_back(idxMapped);
+                        }
+                     }
+                  }
+               }
+            }
+            idxMapped++;
+         }
+      }
+
+      size_t maxParticlesPerSubBlock = 0;
+      std::for_each(subBlocks.begin(), subBlocks.end(), [&maxParticlesPerSubBlock](std::vector< size_t >& subBlock) {
+         maxParticlesPerSubBlock = std::max(maxParticlesPerSubBlock, subBlock.size());
+      });
+
+      size_t* numParticlesPerSubBlock_h = (size_t*) malloc(numSubBlocks * sizeof(size_t));
+      size_t* particleIDsSubBlocks_h    = nullptr;
+      if (maxParticlesPerSubBlock > uint_t(0))
+      {
+         particleIDsSubBlocks_h = (size_t*) malloc(numSubBlocks * maxParticlesPerSubBlock * sizeof(size_t));
+      }
+      // Copy data from std::vector to memory
+      for (size_t z = 0; z < subBlocksPerDim[2]; ++z)
+      {
+         for (size_t y = 0; y < subBlocksPerDim[1]; ++y)
+         {
+            for (size_t x = 0; x < subBlocksPerDim[0]; ++x)
+            {
+               size_t index = z * subBlocksPerDim[0] * subBlocksPerDim[1] + y * subBlocksPerDim[0] + x;
+               numParticlesPerSubBlock_h[index] = subBlocks[index].size();
+               for (size_t k = 0; k < subBlocks[index].size(); k++)
+               {
+                  particleIDsSubBlocks_h[index + k * numSubBlocks] = subBlocks[index][k];
+               }
+            }
+         }
+      }
+
+      WALBERLA_GPU_CHECK(gpuMalloc(&(particleAndVolumeFractionSoA_.positions), 3 * scalarArraySize));
+      WALBERLA_GPU_CHECK(
+         gpuMemcpy(particleAndVolumeFractionSoA_.positions, positions_h, 3 * scalarArraySize, gpuMemcpyHostToDevice));
+
+      real_t* radii;
+      WALBERLA_GPU_CHECK(gpuMalloc(&radii, scalarArraySize));
+      WALBERLA_GPU_CHECK(gpuMemcpy(radii, radii_h, scalarArraySize, gpuMemcpyHostToDevice));
+
+      real_t* f_r;
+      WALBERLA_GPU_CHECK(gpuMalloc(&f_r, scalarArraySize));
+      WALBERLA_GPU_CHECK(gpuMemcpy(f_r, f_r_h, scalarArraySize, gpuMemcpyHostToDevice));
+
+      size_t* numParticlesPerSubBlock;
+      WALBERLA_GPU_CHECK(gpuMalloc(&numParticlesPerSubBlock, numSubBlocks * sizeof(size_t)));
+      WALBERLA_GPU_CHECK(gpuMemcpy(numParticlesPerSubBlock, numParticlesPerSubBlock_h, numSubBlocks * sizeof(size_t),
+                                   gpuMemcpyHostToDevice));
+
+      size_t* particleIDsSubBlocks;
+      if (maxParticlesPerSubBlock > uint_t(0))
+      {
+         WALBERLA_GPU_CHECK(gpuMalloc(&particleIDsSubBlocks, numSubBlocks * maxParticlesPerSubBlock * sizeof(size_t)));
+         WALBERLA_GPU_CHECK(gpuMemcpy(particleIDsSubBlocks, particleIDsSubBlocks_h,
+                                      numSubBlocks * maxParticlesPerSubBlock * sizeof(size_t), gpuMemcpyHostToDevice));
+      }
+
+      mapParticles(*block, particleAndVolumeFractionSoA_, particleAndVolumeFractionSoA_.positions, radii, f_r,
+                   numParticlesPerSubBlock, particleIDsSubBlocks, subBlocksPerDim);
+
+      WALBERLA_GPU_CHECK(gpuFree(numParticlesPerSubBlock));
+      free(numParticlesPerSubBlock_h);
+
+      if (maxParticlesPerSubBlock > uint_t(0))
+      {
+         WALBERLA_GPU_CHECK(gpuFree(particleIDsSubBlocks));
+         free(particleIDsSubBlocks_h);
+      }
+
+      WALBERLA_GPU_CHECK(gpuFree(radii));
+      free(radii_h);
+
+      WALBERLA_GPU_CHECK(gpuFree(f_r));
+      free(f_r_h);
+
+      free(positions_h);
+   }
+
+   shared_ptr< StructuredBlockStorage > blockStorage_;
+   const shared_ptr< ParticleAccessor_T > ac_;
+   const ParticleSelector_T& mappingParticleSelector_;
+   ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA_;
+   const Vector3< uint_t > subBlockSize_;
+};
+
+template< typename ParticleAccessor_T, typename ParticleSelector_T, int Weighting_T >
+class BoxFractionMappingSweep
+{
+ public:
+   BoxFractionMappingSweep(const shared_ptr< StructuredBlockStorage >& blockStorage,
+                           const shared_ptr< ParticleAccessor_T >& ac, const uint_t boxUid,
+                           const Vector3< real_t > boxEdgeLength,
+                           ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA,
+                           const ParticleSelector_T& mappingParticleSelector)
+      : blockStorage_(blockStorage), ac_(ac), boxUid_(boxUid), boxEdgeLength_(boxEdgeLength),
+        particleAndVolumeFractionSoA_(particleAndVolumeFractionSoA), mappingParticleSelector_(mappingParticleSelector)
+   {
+      static_assert(std::is_base_of< mesa_pd::data::IAccessor, ParticleAccessor_T >::value,
+                    "Provide a valid accessor as template");
+   }
+
+   void operator()(IBlock* block)
+   {
+      auto nOverlappingParticlesField =
+         block->getData< nOverlappingParticlesFieldGPU_T >(particleAndVolumeFractionSoA_.nOverlappingParticlesFieldID);
+      auto BsField  = block->getData< BsFieldGPU_T >(particleAndVolumeFractionSoA_.BsFieldID);
+      auto idxField = block->getData< idxFieldGPU_T >(particleAndVolumeFractionSoA_.idxFieldID);
+      auto BField   = block->getData< BFieldGPU_T >(particleAndVolumeFractionSoA_.BFieldID);
+
+      auto myKernel = walberla::gpu::make_kernel(&(boxMapping< Weighting_T >) );
+      myKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< uint_t >::xyz(*nOverlappingParticlesField));
+      myKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< real_t >::xyz(*BsField));
+      myKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< id_t >::xyz(*idxField));
+      myKernel.addFieldIndexingParam(walberla::gpu::FieldIndexing< real_t >::xyz(*BField));
+      myKernel.addParam(particleAndVolumeFractionSoA_.omega_);
+      const Vector3< real_t > boxPosition = ac_->getPosition(ac_->uidToIdx(boxUid_));
+      myKernel.addParam(double3{ boxPosition[0] - boxEdgeLength_[0] / real_t(2),
+                                 boxPosition[1] - boxEdgeLength_[1] / real_t(2),
+                                 boxPosition[2] - boxEdgeLength_[2] / real_t(2) });
+      myKernel.addParam(double3{ boxPosition[0] + boxEdgeLength_[0] / real_t(2),
+                                 boxPosition[1] + boxEdgeLength_[1] / real_t(2),
+                                 boxPosition[2] + boxEdgeLength_[2] / real_t(2) });
+      Vector3< real_t > blockStart = block->getAABB().minCorner();
+      myKernel.addParam(double3{ blockStart[0], blockStart[1], blockStart[2] });
+      myKernel.addParam(block->getAABB().xSize() / real_t(nOverlappingParticlesField->xSize()));
+
+      // Determine the index of the box among the mapped particles
+      size_t idxMapped = 0;
+      for (size_t idx = 0; idx < ac_->size(); ++idx)
+      {
+         if (mappingParticleSelector_(idx, *ac_))
+         {
+            if (ac_->getUid(idx) == boxUid_) { break; }
+            idxMapped++;
+         }
+      }
+      myKernel.addParam(idxMapped);
+      myKernel();
+   }
+
+   shared_ptr< StructuredBlockStorage > blockStorage_;
+   const shared_ptr< ParticleAccessor_T > ac_;
+   const uint_t boxUid_;
+   const Vector3< real_t > boxEdgeLength_;
+   ParticleAndVolumeFractionSoA_T< Weighting_T >& particleAndVolumeFractionSoA_;
+   const ParticleSelector_T& mappingParticleSelector_;
+};
+
+} // namespace gpu
+} // namespace psm
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/mesa_pd/common/ParticleFunctions.h b/src/mesa_pd/common/ParticleFunctions.h
index 155ca3af1a8b2fd9627f5005f6def49c8b98092d..14397c39c6c3e8ff89a041bd01bcd556b2374215 100644
--- a/src/mesa_pd/common/ParticleFunctions.h
+++ b/src/mesa_pd/common/ParticleFunctions.h
@@ -88,23 +88,23 @@ inline Mat3 getInertia(const size_t p_idx, Accessor& ac)
 template <typename Accessor>
 inline void addForceAtomic(const size_t p_idx, Accessor& ac, const Vec3& f)
 {
-   // Increasing the force and torque on this particle;
-   ac.getForceRef(p_idx)[0]  += f[0];;
-   ac.getForceRef(p_idx)[1]  += f[1];;
+   // Increasing the force and torque on this particle
+   ac.getForceRef(p_idx)[0]  += f[0];
+   ac.getForceRef(p_idx)[1]  += f[1];
    ac.getForceRef(p_idx)[2]  += f[2];
 }
 
 template <typename Accessor>
 inline void addForceAtWFPosAtomic(const size_t p_idx, Accessor& ac, const Vec3& f, const Vec3& wf_pt)
 {
-   // Increasing the force and torque on this particle;
-   ac.getForceRef(p_idx)[0]  += f[0];;
-   ac.getForceRef(p_idx)[1]  += f[1];;
+   // Increasing the force and torque on this particle
+   ac.getForceRef(p_idx)[0]  += f[0];
+   ac.getForceRef(p_idx)[1]  += f[1];
    ac.getForceRef(p_idx)[2]  += f[2];
 
-   const auto t = cross(( wf_pt - ac.getPosition(p_idx) ), f);;
-   ac.getTorqueRef(p_idx)[0] += t[0];;
-   ac.getTorqueRef(p_idx)[1] += t[1];;
+   const auto t = cross(( wf_pt - ac.getPosition(p_idx) ), f);
+   ac.getTorqueRef(p_idx)[0] += t[0];
+   ac.getTorqueRef(p_idx)[1] += t[1];
    ac.getTorqueRef(p_idx)[2] += t[2];
 }
 
@@ -114,9 +114,9 @@ inline void addForceAtWFPosAtomic(const size_t p_idx, Accessor& ac, const Vec3&
 template <typename Accessor>
 inline void addTorqueAtomic(const size_t p_idx, Accessor& ac, const Vec3& t)
 {
-   // Increasing the torque on this particle;
-   ac.getTorqueRef(p_idx)[0]  += t[0];;
-   ac.getTorqueRef(p_idx)[1]  += t[1];;
+   // Increasing the torque on this particle
+   ac.getTorqueRef(p_idx)[0]  += t[0];
+   ac.getTorqueRef(p_idx)[1]  += t[1];
    ac.getTorqueRef(p_idx)[2]  += t[2];
 }
 
diff --git a/src/mesa_pd/data/ParticleAccessor.h b/src/mesa_pd/data/ParticleAccessor.h
index f18c0105a00ea1b45a2a97c4f0c2eb2803b74a93..ea82d84f93fc49a4400720aebfcde5bbcac93b8b 100644
--- a/src/mesa_pd/data/ParticleAccessor.h
+++ b/src/mesa_pd/data/ParticleAccessor.h
@@ -88,6 +88,10 @@ public:
    walberla::mesa_pd::Vec3& getOldForceRef(const size_t p_idx) {return ps_->getOldForceRef(p_idx);}
    void setOldForce(const size_t p_idx, walberla::mesa_pd::Vec3 const & v) { ps_->setOldForce(p_idx, v);}
    
+   walberla::real_t const & getCharge(const size_t p_idx) const {return ps_->getCharge(p_idx);}
+   walberla::real_t& getChargeRef(const size_t p_idx) {return ps_->getChargeRef(p_idx);}
+   void setCharge(const size_t p_idx, walberla::real_t const & v) { ps_->setCharge(p_idx, v);}
+   
    size_t const & getShapeID(const size_t p_idx) const {return ps_->getShapeID(p_idx);}
    size_t& getShapeIDRef(const size_t p_idx) {return ps_->getShapeIDRef(p_idx);}
    void setShapeID(const size_t p_idx, size_t const & v) { ps_->setShapeID(p_idx, v);}
@@ -172,6 +176,18 @@ public:
    walberla::mesa_pd::Vec3& getOldHydrodynamicTorqueRef(const size_t p_idx) {return ps_->getOldHydrodynamicTorqueRef(p_idx);}
    void setOldHydrodynamicTorque(const size_t p_idx, walberla::mesa_pd::Vec3 const & v) { ps_->setOldHydrodynamicTorque(p_idx, v);}
    
+   walberla::mesa_pd::Vec3 const & getElectrostaticForce(const size_t p_idx) const {return ps_->getElectrostaticForce(p_idx);}
+   walberla::mesa_pd::Vec3& getElectrostaticForceRef(const size_t p_idx) {return ps_->getElectrostaticForceRef(p_idx);}
+   void setElectrostaticForce(const size_t p_idx, walberla::mesa_pd::Vec3 const & v) { ps_->setElectrostaticForce(p_idx, v);}
+   
+   walberla::real_t const & getTotalDisplacement(const size_t p_idx) const {return ps_->getTotalDisplacement(p_idx);}
+   walberla::real_t& getTotalDisplacementRef(const size_t p_idx) {return ps_->getTotalDisplacementRef(p_idx);}
+   void setTotalDisplacement(const size_t p_idx, walberla::real_t const & v) { ps_->setTotalDisplacement(p_idx, v);}
+   
+   walberla::real_t const & getCollisionForceNorm(const size_t p_idx) const {return ps_->getCollisionForceNorm(p_idx);}
+   walberla::real_t& getCollisionForceNormRef(const size_t p_idx) {return ps_->getCollisionForceNormRef(p_idx);}
+   void setCollisionForceNorm(const size_t p_idx, walberla::real_t const & v) { ps_->setCollisionForceNorm(p_idx, v);}
+   
    walberla::real_t const & getVirtualMass(const size_t p_idx) const {return ps_->getVirtualMass(p_idx);}
    walberla::real_t& getVirtualMassRef(const size_t p_idx) {return ps_->getVirtualMassRef(p_idx);}
    void setVirtualMass(const size_t p_idx, walberla::real_t const & v) { ps_->setVirtualMass(p_idx, v);}
@@ -297,6 +313,10 @@ public:
    void setOldForce(const size_t /*p_idx*/, walberla::mesa_pd::Vec3 const & v) { oldForce_ = v;}
    walberla::mesa_pd::Vec3& getOldForceRef(const size_t /*p_idx*/) {return oldForce_;}
    
+   walberla::real_t const & getCharge(const size_t /*p_idx*/) const {return charge_;}
+   void setCharge(const size_t /*p_idx*/, walberla::real_t const & v) { charge_ = v;}
+   walberla::real_t& getChargeRef(const size_t /*p_idx*/) {return charge_;}
+   
    size_t const & getShapeID(const size_t /*p_idx*/) const {return shapeID_;}
    void setShapeID(const size_t /*p_idx*/, size_t const & v) { shapeID_ = v;}
    size_t& getShapeIDRef(const size_t /*p_idx*/) {return shapeID_;}
@@ -381,6 +401,18 @@ public:
    void setOldHydrodynamicTorque(const size_t /*p_idx*/, walberla::mesa_pd::Vec3 const & v) { oldHydrodynamicTorque_ = v;}
    walberla::mesa_pd::Vec3& getOldHydrodynamicTorqueRef(const size_t /*p_idx*/) {return oldHydrodynamicTorque_;}
    
+   walberla::mesa_pd::Vec3 const & getElectrostaticForce(const size_t /*p_idx*/) const {return electrostaticForce_;}
+   void setElectrostaticForce(const size_t /*p_idx*/, walberla::mesa_pd::Vec3 const & v) { electrostaticForce_ = v;}
+   walberla::mesa_pd::Vec3& getElectrostaticForceRef(const size_t /*p_idx*/) {return electrostaticForce_;}
+   
+   walberla::real_t const & getTotalDisplacement(const size_t /*p_idx*/) const {return totalDisplacement_;}
+   void setTotalDisplacement(const size_t /*p_idx*/, walberla::real_t const & v) { totalDisplacement_ = v;}
+   walberla::real_t& getTotalDisplacementRef(const size_t /*p_idx*/) {return totalDisplacement_;}
+   
+   walberla::real_t const & getCollisionForceNorm(const size_t /*p_idx*/) const {return collisionForceNorm_;}
+   void setCollisionForceNorm(const size_t /*p_idx*/, walberla::real_t const & v) { collisionForceNorm_ = v;}
+   walberla::real_t& getCollisionForceNormRef(const size_t /*p_idx*/) {return collisionForceNorm_;}
+   
    walberla::real_t const & getVirtualMass(const size_t /*p_idx*/) const {return virtualMass_;}
    void setVirtualMass(const size_t /*p_idx*/, walberla::real_t const & v) { virtualMass_ = v;}
    walberla::real_t& getVirtualMassRef(const size_t /*p_idx*/) {return virtualMass_;}
@@ -442,6 +474,7 @@ private:
    walberla::real_t invMass_;
    walberla::mesa_pd::Vec3 force_;
    walberla::mesa_pd::Vec3 oldForce_;
+   walberla::real_t charge_;
    size_t shapeID_;
    std::shared_ptr<walberla::mesa_pd::data::BaseShape> baseShape_;
    walberla::mesa_pd::Rot3 rotation_;
@@ -463,6 +496,9 @@ private:
    walberla::mesa_pd::Vec3 hydrodynamicTorque_;
    walberla::mesa_pd::Vec3 oldHydrodynamicForce_;
    walberla::mesa_pd::Vec3 oldHydrodynamicTorque_;
+   walberla::mesa_pd::Vec3 electrostaticForce_;
+   walberla::real_t totalDisplacement_;
+   walberla::real_t collisionForceNorm_;
    walberla::real_t virtualMass_;
    walberla::real_t invMassIncludingVirtual_;
    walberla::mesa_pd::Vec3 oldLinearAcceleration_;
diff --git a/src/mesa_pd/data/ParticleStorage.h b/src/mesa_pd/data/ParticleStorage.h
index a25c9d29e0ccf2c5e4eb53e554a004116b7428c0..ef37bbe0303d22f5069dc54558d4c3c7acd2d37c 100644
--- a/src/mesa_pd/data/ParticleStorage.h
+++ b/src/mesa_pd/data/ParticleStorage.h
@@ -82,6 +82,7 @@ public:
       using invMass_type = walberla::real_t;
       using force_type = walberla::mesa_pd::Vec3;
       using oldForce_type = walberla::mesa_pd::Vec3;
+      using charge_type = walberla::real_t;
       using shapeID_type = size_t;
       using baseShape_type = std::shared_ptr<walberla::mesa_pd::data::BaseShape>;
       using rotation_type = walberla::mesa_pd::Rot3;
@@ -103,6 +104,9 @@ public:
       using hydrodynamicTorque_type = walberla::mesa_pd::Vec3;
       using oldHydrodynamicForce_type = walberla::mesa_pd::Vec3;
       using oldHydrodynamicTorque_type = walberla::mesa_pd::Vec3;
+      using electrostaticForce_type = walberla::mesa_pd::Vec3;
+      using totalDisplacement_type = walberla::real_t;
+      using collisionForceNorm_type = walberla::real_t;
       using virtualMass_type = walberla::real_t;
       using invMassIncludingVirtual_type = walberla::real_t;
       using oldLinearAcceleration_type = walberla::mesa_pd::Vec3;
@@ -155,6 +159,10 @@ public:
       oldForce_type& getOldForceRef() {return storage_.getOldForceRef(i_);}
       void setOldForce(oldForce_type const & v) { storage_.setOldForce(i_, v);}
       
+      charge_type const & getCharge() const {return storage_.getCharge(i_);}
+      charge_type& getChargeRef() {return storage_.getChargeRef(i_);}
+      void setCharge(charge_type const & v) { storage_.setCharge(i_, v);}
+      
       shapeID_type const & getShapeID() const {return storage_.getShapeID(i_);}
       shapeID_type& getShapeIDRef() {return storage_.getShapeIDRef(i_);}
       void setShapeID(shapeID_type const & v) { storage_.setShapeID(i_, v);}
@@ -239,6 +247,18 @@ public:
       oldHydrodynamicTorque_type& getOldHydrodynamicTorqueRef() {return storage_.getOldHydrodynamicTorqueRef(i_);}
       void setOldHydrodynamicTorque(oldHydrodynamicTorque_type const & v) { storage_.setOldHydrodynamicTorque(i_, v);}
       
+      electrostaticForce_type const & getElectrostaticForce() const {return storage_.getElectrostaticForce(i_);}
+      electrostaticForce_type& getElectrostaticForceRef() {return storage_.getElectrostaticForceRef(i_);}
+      void setElectrostaticForce(electrostaticForce_type const & v) { storage_.setElectrostaticForce(i_, v);}
+      
+      totalDisplacement_type const & getTotalDisplacement() const {return storage_.getTotalDisplacement(i_);}
+      totalDisplacement_type& getTotalDisplacementRef() {return storage_.getTotalDisplacementRef(i_);}
+      void setTotalDisplacement(totalDisplacement_type const & v) { storage_.setTotalDisplacement(i_, v);}
+      
+      collisionForceNorm_type const & getCollisionForceNorm() const {return storage_.getCollisionForceNorm(i_);}
+      collisionForceNorm_type& getCollisionForceNormRef() {return storage_.getCollisionForceNormRef(i_);}
+      void setCollisionForceNorm(collisionForceNorm_type const & v) { storage_.setCollisionForceNorm(i_, v);}
+      
       virtualMass_type const & getVirtualMass() const {return storage_.getVirtualMass(i_);}
       virtualMass_type& getVirtualMassRef() {return storage_.getVirtualMassRef(i_);}
       void setVirtualMass(virtualMass_type const & v) { storage_.setVirtualMass(i_, v);}
@@ -349,6 +369,7 @@ public:
    using invMass_type = walberla::real_t;
    using force_type = walberla::mesa_pd::Vec3;
    using oldForce_type = walberla::mesa_pd::Vec3;
+   using charge_type = walberla::real_t;
    using shapeID_type = size_t;
    using baseShape_type = std::shared_ptr<walberla::mesa_pd::data::BaseShape>;
    using rotation_type = walberla::mesa_pd::Rot3;
@@ -370,6 +391,9 @@ public:
    using hydrodynamicTorque_type = walberla::mesa_pd::Vec3;
    using oldHydrodynamicForce_type = walberla::mesa_pd::Vec3;
    using oldHydrodynamicTorque_type = walberla::mesa_pd::Vec3;
+   using electrostaticForce_type = walberla::mesa_pd::Vec3;
+   using totalDisplacement_type = walberla::real_t;
+   using collisionForceNorm_type = walberla::real_t;
    using virtualMass_type = walberla::real_t;
    using invMassIncludingVirtual_type = walberla::real_t;
    using oldLinearAcceleration_type = walberla::mesa_pd::Vec3;
@@ -422,6 +446,10 @@ public:
    oldForce_type& getOldForceRef(const size_t idx) {return oldForce_[idx];}
    void setOldForce(const size_t idx, oldForce_type const & v) { oldForce_[idx] = v; }
    
+   charge_type const & getCharge(const size_t idx) const {return charge_[idx];}
+   charge_type& getChargeRef(const size_t idx) {return charge_[idx];}
+   void setCharge(const size_t idx, charge_type const & v) { charge_[idx] = v; }
+   
    shapeID_type const & getShapeID(const size_t idx) const {return shapeID_[idx];}
    shapeID_type& getShapeIDRef(const size_t idx) {return shapeID_[idx];}
    void setShapeID(const size_t idx, shapeID_type const & v) { shapeID_[idx] = v; }
@@ -506,6 +534,18 @@ public:
    oldHydrodynamicTorque_type& getOldHydrodynamicTorqueRef(const size_t idx) {return oldHydrodynamicTorque_[idx];}
    void setOldHydrodynamicTorque(const size_t idx, oldHydrodynamicTorque_type const & v) { oldHydrodynamicTorque_[idx] = v; }
    
+   electrostaticForce_type const & getElectrostaticForce(const size_t idx) const {return electrostaticForce_[idx];}
+   electrostaticForce_type& getElectrostaticForceRef(const size_t idx) {return electrostaticForce_[idx];}
+   void setElectrostaticForce(const size_t idx, electrostaticForce_type const & v) { electrostaticForce_[idx] = v; }
+   
+   totalDisplacement_type const & getTotalDisplacement(const size_t idx) const {return totalDisplacement_[idx];}
+   totalDisplacement_type& getTotalDisplacementRef(const size_t idx) {return totalDisplacement_[idx];}
+   void setTotalDisplacement(const size_t idx, totalDisplacement_type const & v) { totalDisplacement_[idx] = v; }
+   
+   collisionForceNorm_type const & getCollisionForceNorm(const size_t idx) const {return collisionForceNorm_[idx];}
+   collisionForceNorm_type& getCollisionForceNormRef(const size_t idx) {return collisionForceNorm_[idx];}
+   void setCollisionForceNorm(const size_t idx, collisionForceNorm_type const & v) { collisionForceNorm_[idx] = v; }
+   
    virtualMass_type const & getVirtualMass(const size_t idx) const {return virtualMass_[idx];}
    virtualMass_type& getVirtualMassRef(const size_t idx) {return virtualMass_[idx];}
    void setVirtualMass(const size_t idx, virtualMass_type const & v) { virtualMass_[idx] = v; }
@@ -647,6 +687,7 @@ public:
    std::vector<invMass_type> invMass_ {};
    std::vector<force_type> force_ {};
    std::vector<oldForce_type> oldForce_ {};
+   std::vector<charge_type> charge_ {};
    std::vector<shapeID_type> shapeID_ {};
    std::vector<baseShape_type> baseShape_ {};
    std::vector<rotation_type> rotation_ {};
@@ -668,6 +709,9 @@ public:
    std::vector<hydrodynamicTorque_type> hydrodynamicTorque_ {};
    std::vector<oldHydrodynamicForce_type> oldHydrodynamicForce_ {};
    std::vector<oldHydrodynamicTorque_type> oldHydrodynamicTorque_ {};
+   std::vector<electrostaticForce_type> electrostaticForce_ {};
+   std::vector<totalDisplacement_type> totalDisplacement_ {};
+   std::vector<collisionForceNorm_type> collisionForceNorm_ {};
    std::vector<virtualMass_type> virtualMass_ {};
    std::vector<invMassIncludingVirtual_type> invMassIncludingVirtual_ {};
    std::vector<oldLinearAcceleration_type> oldLinearAcceleration_ {};
@@ -697,6 +741,7 @@ ParticleStorage::Particle& ParticleStorage::Particle::operator=(const ParticleSt
    getInvMassRef() = rhs.getInvMass();
    getForceRef() = rhs.getForce();
    getOldForceRef() = rhs.getOldForce();
+   getChargeRef() = rhs.getCharge();
    getShapeIDRef() = rhs.getShapeID();
    getBaseShapeRef() = rhs.getBaseShape();
    getRotationRef() = rhs.getRotation();
@@ -718,6 +763,9 @@ ParticleStorage::Particle& ParticleStorage::Particle::operator=(const ParticleSt
    getHydrodynamicTorqueRef() = rhs.getHydrodynamicTorque();
    getOldHydrodynamicForceRef() = rhs.getOldHydrodynamicForce();
    getOldHydrodynamicTorqueRef() = rhs.getOldHydrodynamicTorque();
+   getElectrostaticForceRef() = rhs.getElectrostaticForce();
+   getTotalDisplacementRef() = rhs.getTotalDisplacement();
+   getCollisionForceNormRef() = rhs.getCollisionForceNorm();
    getVirtualMassRef() = rhs.getVirtualMass();
    getInvMassIncludingVirtualRef() = rhs.getInvMassIncludingVirtual();
    getOldLinearAccelerationRef() = rhs.getOldLinearAcceleration();
@@ -744,6 +792,7 @@ ParticleStorage::Particle& ParticleStorage::Particle::operator=(ParticleStorage:
    getInvMassRef() = std::move(rhs.getInvMassRef());
    getForceRef() = std::move(rhs.getForceRef());
    getOldForceRef() = std::move(rhs.getOldForceRef());
+   getChargeRef() = std::move(rhs.getChargeRef());
    getShapeIDRef() = std::move(rhs.getShapeIDRef());
    getBaseShapeRef() = std::move(rhs.getBaseShapeRef());
    getRotationRef() = std::move(rhs.getRotationRef());
@@ -765,6 +814,9 @@ ParticleStorage::Particle& ParticleStorage::Particle::operator=(ParticleStorage:
    getHydrodynamicTorqueRef() = std::move(rhs.getHydrodynamicTorqueRef());
    getOldHydrodynamicForceRef() = std::move(rhs.getOldHydrodynamicForceRef());
    getOldHydrodynamicTorqueRef() = std::move(rhs.getOldHydrodynamicTorqueRef());
+   getElectrostaticForceRef() = std::move(rhs.getElectrostaticForceRef());
+   getTotalDisplacementRef() = std::move(rhs.getTotalDisplacementRef());
+   getCollisionForceNormRef() = std::move(rhs.getCollisionForceNormRef());
    getVirtualMassRef() = std::move(rhs.getVirtualMassRef());
    getInvMassIncludingVirtualRef() = std::move(rhs.getInvMassIncludingVirtualRef());
    getOldLinearAccelerationRef() = std::move(rhs.getOldLinearAccelerationRef());
@@ -792,6 +844,7 @@ void swap(ParticleStorage::Particle lhs, ParticleStorage::Particle rhs)
    std::swap(lhs.getInvMassRef(), rhs.getInvMassRef());
    std::swap(lhs.getForceRef(), rhs.getForceRef());
    std::swap(lhs.getOldForceRef(), rhs.getOldForceRef());
+   std::swap(lhs.getChargeRef(), rhs.getChargeRef());
    std::swap(lhs.getShapeIDRef(), rhs.getShapeIDRef());
    std::swap(lhs.getBaseShapeRef(), rhs.getBaseShapeRef());
    std::swap(lhs.getRotationRef(), rhs.getRotationRef());
@@ -813,6 +866,9 @@ void swap(ParticleStorage::Particle lhs, ParticleStorage::Particle rhs)
    std::swap(lhs.getHydrodynamicTorqueRef(), rhs.getHydrodynamicTorqueRef());
    std::swap(lhs.getOldHydrodynamicForceRef(), rhs.getOldHydrodynamicForceRef());
    std::swap(lhs.getOldHydrodynamicTorqueRef(), rhs.getOldHydrodynamicTorqueRef());
+   std::swap(lhs.getElectrostaticForceRef(), rhs.getElectrostaticForceRef());
+   std::swap(lhs.getTotalDisplacementRef(), rhs.getTotalDisplacementRef());
+   std::swap(lhs.getCollisionForceNormRef(), rhs.getCollisionForceNormRef());
    std::swap(lhs.getVirtualMassRef(), rhs.getVirtualMassRef());
    std::swap(lhs.getInvMassIncludingVirtualRef(), rhs.getInvMassIncludingVirtualRef());
    std::swap(lhs.getOldLinearAccelerationRef(), rhs.getOldLinearAccelerationRef());
@@ -840,6 +896,7 @@ std::ostream& operator<<( std::ostream& os, const ParticleStorage::Particle& p )
          "invMass             : " << p.getInvMass() << "\n" <<
          "force               : " << p.getForce() << "\n" <<
          "oldForce            : " << p.getOldForce() << "\n" <<
+         "charge              : " << p.getCharge() << "\n" <<
          "shapeID             : " << p.getShapeID() << "\n" <<
          "baseShape           : " << p.getBaseShape() << "\n" <<
          "rotation            : " << p.getRotation() << "\n" <<
@@ -861,6 +918,9 @@ std::ostream& operator<<( std::ostream& os, const ParticleStorage::Particle& p )
          "hydrodynamicTorque  : " << p.getHydrodynamicTorque() << "\n" <<
          "oldHydrodynamicForce: " << p.getOldHydrodynamicForce() << "\n" <<
          "oldHydrodynamicTorque: " << p.getOldHydrodynamicTorque() << "\n" <<
+         "electrostaticForce  : " << p.getElectrostaticForce() << "\n" <<
+         "totalDisplacement   : " << p.getTotalDisplacement() << "\n" <<
+         "collisionForceNorm  : " << p.getCollisionForceNorm() << "\n" <<
          "virtualMass         : " << p.getVirtualMass() << "\n" <<
          "invMassIncludingVirtual: " << p.getInvMassIncludingVirtual() << "\n" <<
          "oldLinearAcceleration: " << p.getOldLinearAcceleration() << "\n" <<
@@ -958,6 +1018,7 @@ inline ParticleStorage::iterator ParticleStorage::create(const id_t& uid)
    invMass_.emplace_back(real_t(1));
    force_.emplace_back(real_t(0));
    oldForce_.emplace_back(real_t(0));
+   charge_.emplace_back(real_t(0));
    shapeID_.emplace_back();
    baseShape_.emplace_back(make_shared<walberla::mesa_pd::data::BaseShape>());
    rotation_.emplace_back();
@@ -979,6 +1040,9 @@ inline ParticleStorage::iterator ParticleStorage::create(const id_t& uid)
    hydrodynamicTorque_.emplace_back(real_t(0));
    oldHydrodynamicForce_.emplace_back(real_t(0));
    oldHydrodynamicTorque_.emplace_back(real_t(0));
+   electrostaticForce_.emplace_back(real_t(0));
+   totalDisplacement_.emplace_back(real_t(0));
+   collisionForceNorm_.emplace_back(real_t(0));
    virtualMass_.emplace_back(real_t(0));
    invMassIncludingVirtual_.emplace_back(real_t(0));
    oldLinearAcceleration_.emplace_back(real_t(0));
@@ -1031,6 +1095,7 @@ inline ParticleStorage::iterator ParticleStorage::erase(iterator& it)
    invMass_.pop_back();
    force_.pop_back();
    oldForce_.pop_back();
+   charge_.pop_back();
    shapeID_.pop_back();
    baseShape_.pop_back();
    rotation_.pop_back();
@@ -1052,6 +1117,9 @@ inline ParticleStorage::iterator ParticleStorage::erase(iterator& it)
    hydrodynamicTorque_.pop_back();
    oldHydrodynamicForce_.pop_back();
    oldHydrodynamicTorque_.pop_back();
+   electrostaticForce_.pop_back();
+   totalDisplacement_.pop_back();
+   collisionForceNorm_.pop_back();
    virtualMass_.pop_back();
    invMassIncludingVirtual_.pop_back();
    oldLinearAcceleration_.pop_back();
@@ -1091,6 +1159,7 @@ inline void ParticleStorage::reserve(const size_t size)
    invMass_.reserve(size);
    force_.reserve(size);
    oldForce_.reserve(size);
+   charge_.reserve(size);
    shapeID_.reserve(size);
    baseShape_.reserve(size);
    rotation_.reserve(size);
@@ -1112,6 +1181,9 @@ inline void ParticleStorage::reserve(const size_t size)
    hydrodynamicTorque_.reserve(size);
    oldHydrodynamicForce_.reserve(size);
    oldHydrodynamicTorque_.reserve(size);
+   electrostaticForce_.reserve(size);
+   totalDisplacement_.reserve(size);
+   collisionForceNorm_.reserve(size);
    virtualMass_.reserve(size);
    invMassIncludingVirtual_.reserve(size);
    oldLinearAcceleration_.reserve(size);
@@ -1136,6 +1208,7 @@ inline void ParticleStorage::clear()
    invMass_.clear();
    force_.clear();
    oldForce_.clear();
+   charge_.clear();
    shapeID_.clear();
    baseShape_.clear();
    rotation_.clear();
@@ -1157,6 +1230,9 @@ inline void ParticleStorage::clear()
    hydrodynamicTorque_.clear();
    oldHydrodynamicForce_.clear();
    oldHydrodynamicTorque_.clear();
+   electrostaticForce_.clear();
+   totalDisplacement_.clear();
+   collisionForceNorm_.clear();
    virtualMass_.clear();
    invMassIncludingVirtual_.clear();
    oldLinearAcceleration_.clear();
@@ -1182,6 +1258,7 @@ inline size_t ParticleStorage::size() const
    //WALBERLA_ASSERT_EQUAL( uid_.size(), invMass.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), force.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), oldForce.size() );
+   //WALBERLA_ASSERT_EQUAL( uid_.size(), charge.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), shapeID.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), baseShape.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), rotation.size() );
@@ -1203,6 +1280,9 @@ inline size_t ParticleStorage::size() const
    //WALBERLA_ASSERT_EQUAL( uid_.size(), hydrodynamicTorque.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), oldHydrodynamicForce.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), oldHydrodynamicTorque.size() );
+   //WALBERLA_ASSERT_EQUAL( uid_.size(), electrostaticForce.size() );
+   //WALBERLA_ASSERT_EQUAL( uid_.size(), totalDisplacement.size() );
+   //WALBERLA_ASSERT_EQUAL( uid_.size(), collisionForceNorm.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), virtualMass.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), invMassIncludingVirtual.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), oldLinearAcceleration.size() );
@@ -1469,6 +1549,15 @@ public:
    walberla::mesa_pd::Vec3 const & operator()(const data::Particle& p) const {return p.getOldForce();}
 };
 ///Predicate that selects a certain property from a Particle
+class SelectParticleCharge
+{
+public:
+   using return_type = walberla::real_t;
+   walberla::real_t& operator()(data::Particle& p) const {return p.getChargeRef();}
+   walberla::real_t& operator()(data::Particle&& p) const {return p.getChargeRef();}
+   walberla::real_t const & operator()(const data::Particle& p) const {return p.getCharge();}
+};
+///Predicate that selects a certain property from a Particle
 class SelectParticleShapeID
 {
 public:
@@ -1658,6 +1747,33 @@ public:
    walberla::mesa_pd::Vec3 const & operator()(const data::Particle& p) const {return p.getOldHydrodynamicTorque();}
 };
 ///Predicate that selects a certain property from a Particle
+class SelectParticleElectrostaticForce
+{
+public:
+   using return_type = walberla::mesa_pd::Vec3;
+   walberla::mesa_pd::Vec3& operator()(data::Particle& p) const {return p.getElectrostaticForceRef();}
+   walberla::mesa_pd::Vec3& operator()(data::Particle&& p) const {return p.getElectrostaticForceRef();}
+   walberla::mesa_pd::Vec3 const & operator()(const data::Particle& p) const {return p.getElectrostaticForce();}
+};
+///Predicate that selects a certain property from a Particle
+class SelectParticleTotalDisplacement
+{
+public:
+   using return_type = walberla::real_t;
+   walberla::real_t& operator()(data::Particle& p) const {return p.getTotalDisplacementRef();}
+   walberla::real_t& operator()(data::Particle&& p) const {return p.getTotalDisplacementRef();}
+   walberla::real_t const & operator()(const data::Particle& p) const {return p.getTotalDisplacement();}
+};
+///Predicate that selects a certain property from a Particle
+class SelectParticleCollisionForceNorm
+{
+public:
+   using return_type = walberla::real_t;
+   walberla::real_t& operator()(data::Particle& p) const {return p.getCollisionForceNormRef();}
+   walberla::real_t& operator()(data::Particle&& p) const {return p.getCollisionForceNormRef();}
+   walberla::real_t const & operator()(const data::Particle& p) const {return p.getCollisionForceNorm();}
+};
+///Predicate that selects a certain property from a Particle
 class SelectParticleVirtualMass
 {
 public:
diff --git a/src/mesa_pd/kernel/LinearSpringDashpot.h b/src/mesa_pd/kernel/LinearSpringDashpot.h
index db9ca2e902bbf53ebba37a850875a5cf04bde4a8..5907132172d45aa0a9f58685fde5c1ba9aebd124 100644
--- a/src/mesa_pd/kernel/LinearSpringDashpot.h
+++ b/src/mesa_pd/kernel/LinearSpringDashpot.h
@@ -344,7 +344,8 @@ inline void LinearSpringDashpot::operator()(const size_t p_idx1,
       const real_t fTabs( std::min( fTLS.length(), fFrictionAbs) );
       const Vec3   fT   ( fTabs * t );
 
-      //TODO check if tangential spring displacement is same for symmetric case
+      // TODO check if tangential spring displacement is same for symmetric case
+      // TODO: check why exactly this critical section is needed
       auto& ch1 = ac.getNewContactHistoryRef(p_idx1)[ac.getUid(p_idx2)];
       ch1.setTangentialSpringDisplacement(newTangentialSpringDisplacement);
       ch1.setIsSticking(isSticking);
diff --git a/src/mesa_pd/mpi/notifications/CMakeLists.txt b/src/mesa_pd/mpi/notifications/CMakeLists.txt
index c50238b775c0bc00aa6986c8c471ab237ba296f0..16e1bac3779869d71835a318d0ba9b758bd47a35 100644
--- a/src/mesa_pd/mpi/notifications/CMakeLists.txt
+++ b/src/mesa_pd/mpi/notifications/CMakeLists.txt
@@ -17,5 +17,6 @@ target_sources( mesa_pd
     HydrodynamicForceTorqueNotification.h
     reset.h
     ForceTorqueNotification.h
-    NotificationType.h     
+    NotificationType.h
+    ElectrostaticForceNotification.h
     )
diff --git a/src/mesa_pd/mpi/notifications/ElectrostaticForceNotification.h b/src/mesa_pd/mpi/notifications/ElectrostaticForceNotification.h
new file mode 100644
index 0000000000000000000000000000000000000000..91eb0d71c7f4afbe9978fd14910cab83a06516bc
--- /dev/null
+++ b/src/mesa_pd/mpi/notifications/ElectrostaticForceNotification.h
@@ -0,0 +1,114 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/ParticleStorage.h>
+#include <mesa_pd/mpi/notifications/NotificationType.h>
+#include <mesa_pd/mpi/notifications/reset.h>
+
+#include <core/mpi/Datatype.h>
+#include <core/mpi/RecvBuffer.h>
+#include <core/mpi/SendBuffer.h>
+
+namespace walberla {
+namespace mesa_pd {
+
+/**
+ * Transmits force and torque information.
+ */
+class ElectrostaticForceNotification
+{
+public:
+   struct Parameters
+   {
+      id_t uid_;
+      mesa_pd::Vec3 electrostaticForce_;
+   };
+
+   inline explicit ElectrostaticForceNotification( const data::Particle& p ) : p_(p) {}
+
+   const data::Particle& p_;
+};
+
+template <>
+void reset<ElectrostaticForceNotification>(data::Particle& p)
+{
+   p.setElectrostaticForce( Vec3(real_t(0)) );
+}
+
+void reduce(data::Particle&& p, const ElectrostaticForceNotification::Parameters& objparam)
+{
+   p.getElectrostaticForceRef() += objparam.electrostaticForce_;
+}
+
+void update(data::Particle&& p, const ElectrostaticForceNotification::Parameters& objparam)
+{
+   p.setElectrostaticForce( objparam.electrostaticForce_ );
+}
+
+}  // namespace mesa_pd
+}  // namespace walberla
+
+//======================================================================================================================
+//
+//  Send/Recv Buffer Serialization Specialization
+//
+//======================================================================================================================
+
+namespace walberla {
+namespace mpi {
+
+template< typename T,    // Element type of SendBuffer
+          typename G>    // Growth policy of SendBuffer
+mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, const mesa_pd::ElectrostaticForceNotification& obj )
+{
+   buf.addDebugMarker( "pn" );
+   buf << obj.p_.getUid();
+   buf << obj.p_.getElectrostaticForce();
+   return buf;
+}
+
+template< typename T>    // Element type  of RecvBuffer
+mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd::ElectrostaticForceNotification::Parameters& objparam )
+{
+   buf.readDebugMarker( "pn" );
+   buf >> objparam.uid_;
+   buf >> objparam.electrostaticForce_;
+   return buf;
+}
+
+template< >
+struct BufferSizeTrait< mesa_pd::ElectrostaticForceNotification > {
+   static const bool constantSize = true;
+   static const uint_t size = BufferSizeTrait<id_t>::size +
+                              BufferSizeTrait<mesa_pd::Vec3>::size +
+                              mpi::BUFFER_DEBUG_OVERHEAD;
+};
+
+} // mpi
+} // walberla
\ No newline at end of file
diff --git a/src/mesa_pd/mpi/notifications/ParseMessage.h b/src/mesa_pd/mpi/notifications/ParseMessage.h
index ed3124bf252166d98114bc90d640531ebc002d91..082b02909d7068c79846b0a1a0776c99b4fc68ae 100644
--- a/src/mesa_pd/mpi/notifications/ParseMessage.h
+++ b/src/mesa_pd/mpi/notifications/ParseMessage.h
@@ -115,6 +115,7 @@ void ParseMessage::operator()(int sender,
       pIt->setUid(objparam.uid);
       pIt->setPosition(objparam.position);
       pIt->setLinearVelocity(objparam.linearVelocity);
+      pIt->setCharge(objparam.charge);
       pIt->setRotation(objparam.rotation);
       pIt->setAngularVelocity(objparam.angularVelocity);
       pIt->setRadiusAtTemperature(objparam.radiusAtTemperature);
@@ -153,6 +154,9 @@ void ParseMessage::operator()(int sender,
       pIt->setHydrodynamicTorque(objparam.hydrodynamicTorque_);
       pIt->setOldHydrodynamicForce(objparam.oldHydrodynamicForce_);
       pIt->setOldHydrodynamicTorque(objparam.oldHydrodynamicTorque_);
+      pIt->setElectrostaticForce(objparam.electrostaticForce_);
+      pIt->setTotalDisplacement(objparam.totalDisplacement_);
+      pIt->setCollisionForceNorm(objparam.collisionForceNorm_);
       pIt->setVirtualMass(objparam.virtualMass_);
       pIt->setInvMassIncludingVirtual(objparam.invMassIncludingVirtual_);
       pIt->setOldLinearAcceleration(objparam.oldLinearAcceleration_);
diff --git a/src/mesa_pd/mpi/notifications/ParticleCopyNotification.h b/src/mesa_pd/mpi/notifications/ParticleCopyNotification.h
index 78de9c6eaee587fe1a1fffca9a193dfcba4a6354..23d085b51440a2db1da7ca731530072378816372 100644
--- a/src/mesa_pd/mpi/notifications/ParticleCopyNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleCopyNotification.h
@@ -57,6 +57,7 @@ public:
       walberla::mesa_pd::Vec3 linearVelocity {real_t(0)};
       walberla::real_t invMass {real_t(1)};
       walberla::mesa_pd::Vec3 oldForce {real_t(0)};
+      walberla::real_t charge {real_t(0)};
       size_t shapeID {};
       std::shared_ptr<walberla::mesa_pd::data::BaseShape> baseShape {make_shared<walberla::mesa_pd::data::BaseShape>()};
       walberla::mesa_pd::Rot3 rotation {};
@@ -70,6 +71,9 @@ public:
       walberla::mesa_pd::Vec3 hydrodynamicTorque {real_t(0)};
       walberla::mesa_pd::Vec3 oldHydrodynamicForce {real_t(0)};
       walberla::mesa_pd::Vec3 oldHydrodynamicTorque {real_t(0)};
+      walberla::mesa_pd::Vec3 electrostaticForce {real_t(0)};
+      walberla::real_t totalDisplacement {real_t(0)};
+      walberla::real_t collisionForceNorm {real_t(0)};
       walberla::real_t virtualMass {real_t(0)};
       walberla::real_t invMassIncludingVirtual {real_t(0)};
       walberla::mesa_pd::Vec3 oldLinearAcceleration {real_t(0)};
@@ -99,6 +103,7 @@ inline data::ParticleStorage::iterator createNewParticle(data::ParticleStorage&
    pIt->setLinearVelocity(data.linearVelocity);
    pIt->setInvMass(data.invMass);
    pIt->setOldForce(data.oldForce);
+   pIt->setCharge(data.charge);
    pIt->setShapeID(data.shapeID);
    pIt->setBaseShape(data.baseShape);
    pIt->setRotation(data.rotation);
@@ -112,6 +117,9 @@ inline data::ParticleStorage::iterator createNewParticle(data::ParticleStorage&
    pIt->setHydrodynamicTorque(data.hydrodynamicTorque);
    pIt->setOldHydrodynamicForce(data.oldHydrodynamicForce);
    pIt->setOldHydrodynamicTorque(data.oldHydrodynamicTorque);
+   pIt->setElectrostaticForce(data.electrostaticForce);
+   pIt->setTotalDisplacement(data.totalDisplacement);
+   pIt->setCollisionForceNorm(data.collisionForceNorm);
    pIt->setVirtualMass(data.virtualMass);
    pIt->setInvMassIncludingVirtual(data.invMassIncludingVirtual);
    pIt->setOldLinearAcceleration(data.oldLinearAcceleration);
@@ -156,6 +164,7 @@ mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, cons
    buf << obj.particle_.getLinearVelocity();
    buf << obj.particle_.getInvMass();
    buf << obj.particle_.getOldForce();
+   buf << obj.particle_.getCharge();
    buf << obj.particle_.getShapeID();
    buf << obj.particle_.getBaseShape();
    buf << obj.particle_.getRotation();
@@ -169,6 +178,9 @@ mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, cons
    buf << obj.particle_.getHydrodynamicTorque();
    buf << obj.particle_.getOldHydrodynamicForce();
    buf << obj.particle_.getOldHydrodynamicTorque();
+   buf << obj.particle_.getElectrostaticForce();
+   buf << obj.particle_.getTotalDisplacement();
+   buf << obj.particle_.getCollisionForceNorm();
    buf << obj.particle_.getVirtualMass();
    buf << obj.particle_.getInvMassIncludingVirtual();
    buf << obj.particle_.getOldLinearAcceleration();
@@ -194,6 +206,7 @@ mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd:
    buf >> objparam.linearVelocity;
    buf >> objparam.invMass;
    buf >> objparam.oldForce;
+   buf >> objparam.charge;
    buf >> objparam.shapeID;
    buf >> objparam.baseShape;
    buf >> objparam.rotation;
@@ -207,6 +220,9 @@ mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd:
    buf >> objparam.hydrodynamicTorque;
    buf >> objparam.oldHydrodynamicForce;
    buf >> objparam.oldHydrodynamicTorque;
+   buf >> objparam.electrostaticForce;
+   buf >> objparam.totalDisplacement;
+   buf >> objparam.collisionForceNorm;
    buf >> objparam.virtualMass;
    buf >> objparam.invMassIncludingVirtual;
    buf >> objparam.oldLinearAcceleration;
diff --git a/src/mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h b/src/mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h
index fa87d8571fd11db5aa83b881a9461d9befb7bc3b..12ee33efad8e64abfeee2250226525b848381d4d 100644
--- a/src/mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h
@@ -55,6 +55,7 @@ public:
       int owner {-1};
       walberla::mesa_pd::Vec3 linearVelocity {real_t(0)};
       walberla::real_t invMass {real_t(1)};
+      walberla::real_t charge {real_t(0)};
       size_t shapeID {};
       std::shared_ptr<walberla::mesa_pd::data::BaseShape> baseShape {make_shared<walberla::mesa_pd::data::BaseShape>()};
       walberla::mesa_pd::Rot3 rotation {};
@@ -83,6 +84,7 @@ inline data::ParticleStorage::iterator createNewParticle(data::ParticleStorage&
    pIt->setOwner(data.owner);
    pIt->setLinearVelocity(data.linearVelocity);
    pIt->setInvMass(data.invMass);
+   pIt->setCharge(data.charge);
    pIt->setShapeID(data.shapeID);
    pIt->setBaseShape(data.baseShape);
    pIt->setRotation(data.rotation);
@@ -126,6 +128,7 @@ mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, cons
    buf << obj.particle_.getOwner();
    buf << obj.particle_.getLinearVelocity();
    buf << obj.particle_.getInvMass();
+   buf << obj.particle_.getCharge();
    buf << obj.particle_.getShapeID();
    buf << obj.particle_.getBaseShape();
    buf << obj.particle_.getRotation();
@@ -150,6 +153,7 @@ mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd:
    buf >> objparam.owner;
    buf >> objparam.linearVelocity;
    buf >> objparam.invMass;
+   buf >> objparam.charge;
    buf >> objparam.shapeID;
    buf >> objparam.baseShape;
    buf >> objparam.rotation;
diff --git a/src/mesa_pd/mpi/notifications/ParticleMigrationNotification.h b/src/mesa_pd/mpi/notifications/ParticleMigrationNotification.h
index d7cf2b54d86bc57b5875130e5f7edf3c0f8d98b0..efaf5ff61897b6548fe2b0e47abfcc159ca9cbd6 100644
--- a/src/mesa_pd/mpi/notifications/ParticleMigrationNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleMigrationNotification.h
@@ -52,6 +52,9 @@ public:
       walberla::mesa_pd::Vec3 hydrodynamicTorque_ {real_t(0)};
       walberla::mesa_pd::Vec3 oldHydrodynamicForce_ {real_t(0)};
       walberla::mesa_pd::Vec3 oldHydrodynamicTorque_ {real_t(0)};
+      walberla::mesa_pd::Vec3 electrostaticForce_ {real_t(0)};
+      walberla::real_t totalDisplacement_ {real_t(0)};
+      walberla::real_t collisionForceNorm_ {real_t(0)};
       walberla::real_t virtualMass_ {real_t(0)};
       walberla::real_t invMassIncludingVirtual_ {real_t(0)};
       walberla::mesa_pd::Vec3 oldLinearAcceleration_ {real_t(0)};
@@ -96,6 +99,9 @@ mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, cons
    buf << obj.particle_.getHydrodynamicTorque();
    buf << obj.particle_.getOldHydrodynamicForce();
    buf << obj.particle_.getOldHydrodynamicTorque();
+   buf << obj.particle_.getElectrostaticForce();
+   buf << obj.particle_.getTotalDisplacement();
+   buf << obj.particle_.getCollisionForceNorm();
    buf << obj.particle_.getVirtualMass();
    buf << obj.particle_.getInvMassIncludingVirtual();
    buf << obj.particle_.getOldLinearAcceleration();
@@ -118,6 +124,9 @@ mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd:
    buf >> objparam.hydrodynamicTorque_;
    buf >> objparam.oldHydrodynamicForce_;
    buf >> objparam.oldHydrodynamicTorque_;
+   buf >> objparam.electrostaticForce_;
+   buf >> objparam.totalDisplacement_;
+   buf >> objparam.collisionForceNorm_;
    buf >> objparam.virtualMass_;
    buf >> objparam.invMassIncludingVirtual_;
    buf >> objparam.oldLinearAcceleration_;
diff --git a/src/mesa_pd/mpi/notifications/ParticleUpdateNotification.h b/src/mesa_pd/mpi/notifications/ParticleUpdateNotification.h
index 95d0d7e37acc742b4cff1fce9442398f9a3a3042..73bc47acfc62602e7d3130c9ad09362efd18500f 100644
--- a/src/mesa_pd/mpi/notifications/ParticleUpdateNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleUpdateNotification.h
@@ -48,6 +48,7 @@ public:
    walberla::id_t uid {UniqueID<data::Particle>::invalidID()};
    walberla::mesa_pd::Vec3 position {real_t(0)};
    walberla::mesa_pd::Vec3 linearVelocity {real_t(0)};
+   walberla::real_t charge {real_t(0)};
    walberla::mesa_pd::Rot3 rotation {};
    walberla::mesa_pd::Vec3 angularVelocity {real_t(0)};
    walberla::real_t radiusAtTemperature {real_t(0)};
@@ -85,6 +86,7 @@ mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, cons
    buf << obj.particle_.getUid();
    buf << obj.particle_.getPosition();
    buf << obj.particle_.getLinearVelocity();
+   buf << obj.particle_.getCharge();
    buf << obj.particle_.getRotation();
    buf << obj.particle_.getAngularVelocity();
    buf << obj.particle_.getRadiusAtTemperature();
@@ -100,6 +102,7 @@ mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd:
    buf >> objparam.uid;
    buf >> objparam.position;
    buf >> objparam.linearVelocity;
+   buf >> objparam.charge;
    buf >> objparam.rotation;
    buf >> objparam.angularVelocity;
    buf >> objparam.radiusAtTemperature;
diff --git a/tests/lbm_mesapd_coupling/CMakeLists.txt b/tests/lbm_mesapd_coupling/CMakeLists.txt
index 94438a2d9863101d05ea984eb59ff3f58f6aad2c..b6c8f15397a1317a5643d488b8b685f0fe37e43a 100644
--- a/tests/lbm_mesapd_coupling/CMakeLists.txt
+++ b/tests/lbm_mesapd_coupling/CMakeLists.txt
@@ -7,13 +7,13 @@
 waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_Mapping FILES mapping/ParticleMapping.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_Mapping PROCESSES 3)
 
-waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_MovingMapping FILES momentum_exchange_method/MovingParticleMapping.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk)
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_MovingMapping FILES momentum_exchange_method/MovingParticleMappingMEM.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk)
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_MovingMapping PROCESSES 3)
 
-waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_DragForceSphere FILES momentum_exchange_method/DragForceSphere.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk )
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_DragForceSphere FILES momentum_exchange_method/DragForceSphereMEM.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_DragForceSphere COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_DragForceSphere> --funcTest PROCESSES 2)
 
-waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs FILES momentum_exchange_method/ForceBetweenTwoStationaryObjects.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk )
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs FILES momentum_exchange_method/ForceBetweenTwoStationaryObjectsMEM.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSS1 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs> PROCESSES 1)
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSS2 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs> --useSBB PROCESSES 1)
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSS3 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs> --useCompressible PROCESSES 1)
@@ -23,13 +23,13 @@ waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSW2 COMMAND
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSW3 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs> --useSphereWallSetup --useCompressible PROCESSES 1)
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSW4 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs> --useSphereWallSetup --systemVelocity 0.1 PROCESSES 1)
 
-waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_SettlingSphere FILES momentum_exchange_method/SettlingSphere.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk )
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_SettlingSphere FILES momentum_exchange_method/SettlingSphereMEM.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_SettlingSphere COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_SettlingSphere> --funcTest PROCESSES 4)
 
-waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_PdfReconstruction FILES momentum_exchange_method/PdfReconstruction.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk)
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_PdfReconstruction FILES momentum_exchange_method/PdfReconstructionMEM.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk)
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_PdfReconstruction PROCESSES 3)
 
-waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_UpdateParticleMapping FILES momentum_exchange_method/UpdateParticleMapping.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk)
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_UpdateParticleMapping FILES momentum_exchange_method/UpdateParticleMappingMEM.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk)
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_UpdateParticleMapping PROCESSES 1)
 
 waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_UTIL_LubricationCorrection FILES utility/LubricationCorrection.cpp DEPENDS mesa_pd lbm_mesapd_coupling )
@@ -47,18 +47,21 @@ waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks_VVAvg CO
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks_EulerNoAvg COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks> --noForceAveraging PROCESSES 4 )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks_VVNoAvg COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks> --noForceAveraging --useVV PROCESSES 4 )
 
-waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_PSM_BodyAndVolumeFractionMapping FILES partially_saturated_cells_method/ParticleAndVolumeFractionMapping.cpp DEPENDS blockforest boundary core field lbm_mesapd_coupling stencil mesa_pd )
-waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_BodyAndVolumeFractionMapping PROCESSES 27 )
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_PSM_ParticleAndVolumeFractionMapping FILES partially_saturated_cells_method/ParticleAndVolumeFractionMappingPSM.cpp DEPENDS blockforest boundary core field lbm_mesapd_coupling stencil mesa_pd )
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_ParticleAndVolumeFractionMapping COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_ParticleAndVolumeFractionMapping> PROCESSES 27 )
 
-waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_PSM_DragForceSphere FILES partially_saturated_cells_method/DragForceSphere.cpp DEPENDS blockforest boundary core field lbm lbm_mesapd_coupling timeloop mesa_pd )
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_PSM_ParticleAndVolumeFractionMapping_CPU_GPU FILES partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMapping.cpp DEPENDS blockforest boundary core gpu field lbm_mesapd_coupling stencil mesa_pd )
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_ParticleAndVolumeFractionMapping_CPU_GPU COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_ParticleAndVolumeFractionMapping_CPU_GPU> PROCESSES 27 )
+
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_PSM_DragForceSphere FILES partially_saturated_cells_method/DragForceSpherePSM.cpp DEPENDS blockforest boundary core field lbm lbm_mesapd_coupling timeloop mesa_pd )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_DragForceSphereFuncTest COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_DragForceSphere> --funcTest PROCESSES 8 )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_DragForceSphere COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_DragForceSphere> PROCESSES 8 LABELS longrun CONFIGURATIONS Release RelWithDbgInfo)
 
-waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_PSM_SettlingSphere FILES partially_saturated_cells_method/SettlingSphere.cpp DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling timeloop mesa_pd )
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_PSM_SettlingSphere FILES partially_saturated_cells_method/SettlingSpherePSM.cpp DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling timeloop mesa_pd )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_SettlingSphereFuncTest COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_SettlingSphere> --funcTest PROCESSES 4 )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_SettlingSphere COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_SettlingSphere> --resolution 70 PROCESSES 4 LABELS longrun CONFIGURATIONS Release RelWithDbgInfo )
 
-waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_PSM_TorqueSphere FILES partially_saturated_cells_method/TorqueSphere.cpp DEPENDS blockforest boundary core domain_decomposition field lbm stencil timeloop )
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_PSM_TorqueSphere FILES partially_saturated_cells_method/TorqueSpherePSM.cpp DEPENDS blockforest boundary core domain_decomposition field lbm stencil timeloop )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_TorqueSphereSC1W1FuncTest     COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_TorqueSphere> --funcTest --SC1W1   PROCESSES 1 )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_TorqueSphereSC1W1SingleTest   COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_TorqueSphere> --SC1W1              PROCESSES 1 LABELS longrun     CONFIGURATIONS Release RelWithDbgInfo )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_TorqueSphereSC1W1ParallelTest COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_TorqueSphere> --SC1W1              PROCESSES 8 LABELS verylongrun CONFIGURATIONS Release RelWithDbgInfo )
@@ -72,3 +75,28 @@ waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_TorqueSphereSC2W2FuncTest
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_TorqueSphereSC2W2SingleTest   COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_TorqueSphere> --SC2W2              PROCESSES 1 LABELS longrun     CONFIGURATIONS Release RelWithDbgInfo )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_TorqueSphereSC3W2FuncTest     COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_TorqueSphere> --funcTest --SC3W2   PROCESSES 1 )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_PSM_TorqueSphereSC3W2SingleTest   COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_TorqueSphere> --SC3W2              PROCESSES 1 LABELS longrun     CONFIGURATIONS Release RelWithDbgInfo )
+
+if (WALBERLA_BUILD_WITH_CODEGEN)
+    if (NOT WALBERLA_BUILD_WITH_GPU_SUPPORT OR (WALBERLA_BUILD_WITH_GPU_SUPPORT AND (CMAKE_CUDA_ARCHITECTURES GREATER_EQUAL 60 OR WALBERLA_BUILD_WITH_HIP)))
+        waLBerla_compile_test(NAME LBM_MESAPD_COUPLING_PSM_DragForceSphere_CPU_GPU FILES partially_saturated_cells_method/codegen/DragForceSpherePSM.cpp DEPENDS blockforest core gpu field lbm_mesapd_coupling mesa_pd PSMCodegenPython_srt_sc1)
+        waLBerla_execute_test(NAME LBM_MESAPD_COUPLING_PSM_DragForceSphere_CPU_GPU_FuncTest COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_DragForceSphere_CPU_GPU> --funcTest PROCESSES 1)
+        waLBerla_execute_test(NAME LBM_MESAPD_COUPLING_PSM_DragForceSphere_CPU_GPU COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_DragForceSphere_CPU_GPU> PROCESSES 8 LABELS verylongrun CONFIGURATIONS Release RelWithDbgInfo)
+
+        foreach (collision_setup srt trt-smagorinsky)
+            foreach (solid_collision 1 2 3)
+                foreach (weighting 1 2)
+                    set(config ${collision_setup}_sc${solid_collision}_w${weighting})
+                    waLBerla_compile_test(NAME LBM_MESAPD_COUPLING_PSM_TorqueSphere_CPU_GPU_${config} FILES partially_saturated_cells_method/codegen/TorqueSpherePSM.cpp DEPENDS blockforest core gpu field lbm_mesapd_coupling mesa_pd PSMCodegenPython_${collision_setup}_sc${solid_collision})
+                    target_compile_definitions(LBM_MESAPD_COUPLING_PSM_TorqueSphere_CPU_GPU_${config} PRIVATE SC=${solid_collision})
+                    target_compile_definitions(LBM_MESAPD_COUPLING_PSM_TorqueSphere_CPU_GPU_${config} PRIVATE Weighting=${weighting})
+                    waLBerla_execute_test(NAME LBM_MESAPD_COUPLING_PSM_TorqueSphere_CPU_GPU_${config}_FuncTest COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_TorqueSphere_CPU_GPU_${config}> --funcTest PROCESSES 1)
+                    waLBerla_execute_test(NAME LBM_MESAPD_COUPLING_PSM_TorqueSphere_CPU_GPU_${config} COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_TorqueSphere_CPU_GPU_${config}> PROCESSES 8 LABELS longrun CONFIGURATIONS Release RelWithDbgInfo)
+                endforeach ()
+            endforeach ()
+        endforeach ()
+
+        waLBerla_compile_test(NAME LBM_MESAPD_COUPLING_PSM_SettlingSphere_CPU_GPU FILES partially_saturated_cells_method/codegen/SettlingSpherePSM.cpp DEPENDS blockforest core gpu field lbm lbm_mesapd_coupling mesa_pd timeloop vtk PSMCodegenPython_trt-smagorinsky_sc2)
+        waLBerla_execute_test(NAME LBM_MESAPD_COUPLING_PSM_SettlingSphere_CPU_GPU_FuncTest COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_SettlingSphere_CPU_GPU> --funcTest PROCESSES 1)
+        waLBerla_execute_test(NAME LBM_MESAPD_COUPLING_PSM_SettlingSphere_CPU_GPU COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_PSM_SettlingSphere_CPU_GPU> PROCESSES 8 LABELS verylongrun CONFIGURATIONS Release RelWithDbgInfo)
+    endif ()
+endif ()
diff --git a/tests/lbm_mesapd_coupling/momentum_exchange_method/DragForceSphere.cpp b/tests/lbm_mesapd_coupling/momentum_exchange_method/DragForceSphereMEM.cpp
similarity index 100%
rename from tests/lbm_mesapd_coupling/momentum_exchange_method/DragForceSphere.cpp
rename to tests/lbm_mesapd_coupling/momentum_exchange_method/DragForceSphereMEM.cpp
diff --git a/tests/lbm_mesapd_coupling/momentum_exchange_method/ForceBetweenTwoStationaryObjects.cpp b/tests/lbm_mesapd_coupling/momentum_exchange_method/ForceBetweenTwoStationaryObjectsMEM.cpp
similarity index 100%
rename from tests/lbm_mesapd_coupling/momentum_exchange_method/ForceBetweenTwoStationaryObjects.cpp
rename to tests/lbm_mesapd_coupling/momentum_exchange_method/ForceBetweenTwoStationaryObjectsMEM.cpp
diff --git a/tests/lbm_mesapd_coupling/momentum_exchange_method/MovingParticleMapping.cpp b/tests/lbm_mesapd_coupling/momentum_exchange_method/MovingParticleMappingMEM.cpp
similarity index 100%
rename from tests/lbm_mesapd_coupling/momentum_exchange_method/MovingParticleMapping.cpp
rename to tests/lbm_mesapd_coupling/momentum_exchange_method/MovingParticleMappingMEM.cpp
diff --git a/tests/lbm_mesapd_coupling/momentum_exchange_method/PdfReconstruction.cpp b/tests/lbm_mesapd_coupling/momentum_exchange_method/PdfReconstructionMEM.cpp
similarity index 100%
rename from tests/lbm_mesapd_coupling/momentum_exchange_method/PdfReconstruction.cpp
rename to tests/lbm_mesapd_coupling/momentum_exchange_method/PdfReconstructionMEM.cpp
diff --git a/tests/lbm_mesapd_coupling/momentum_exchange_method/SettlingSphere.cpp b/tests/lbm_mesapd_coupling/momentum_exchange_method/SettlingSphereMEM.cpp
similarity index 100%
rename from tests/lbm_mesapd_coupling/momentum_exchange_method/SettlingSphere.cpp
rename to tests/lbm_mesapd_coupling/momentum_exchange_method/SettlingSphereMEM.cpp
diff --git a/tests/lbm_mesapd_coupling/momentum_exchange_method/UpdateParticleMapping.cpp b/tests/lbm_mesapd_coupling/momentum_exchange_method/UpdateParticleMappingMEM.cpp
similarity index 100%
rename from tests/lbm_mesapd_coupling/momentum_exchange_method/UpdateParticleMapping.cpp
rename to tests/lbm_mesapd_coupling/momentum_exchange_method/UpdateParticleMappingMEM.cpp
diff --git a/tests/lbm_mesapd_coupling/partially_saturated_cells_method/DragForceSphere.cpp b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/DragForceSpherePSM.cpp
similarity index 90%
rename from tests/lbm_mesapd_coupling/partially_saturated_cells_method/DragForceSphere.cpp
rename to tests/lbm_mesapd_coupling/partially_saturated_cells_method/DragForceSpherePSM.cpp
index 151b109c07e083b7d5adc55cb28a1dd0fce0e4eb..12bc2a240dd37765d161ebc3556c78d370546fa1 100644
--- a/tests/lbm_mesapd_coupling/partially_saturated_cells_method/DragForceSphere.cpp
+++ b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/DragForceSpherePSM.cpp
@@ -36,6 +36,7 @@
 #include "core/timing/RemainingTimeLogger.h"
 
 #include "field/AddToStorage.h"
+#include "field/vtk/VTKWriter.h"
 
 #include "lbm/communication/PdfFieldPackInfo.h"
 #include "lbm/field/AddToStorage.h"
@@ -43,10 +44,9 @@
 #include "lbm/lattice_model/D3Q19.h"
 #include "lbm/lattice_model/ForceModel.h"
 #include "lbm/sweeps/SweepWrappers.h"
+#include "lbm/vtk/Velocity.h"
 
 #include "lbm_mesapd_coupling/DataTypes.h"
-#include "lbm_mesapd_coupling/momentum_exchange_method/MovingParticleMapping.h"
-#include "lbm_mesapd_coupling/momentum_exchange_method/boundary/SimpleBB.h"
 #include "lbm_mesapd_coupling/partially_saturated_cells_method/PSMSweep.h"
 #include "lbm_mesapd_coupling/partially_saturated_cells_method/PSMUtility.h"
 #include "lbm_mesapd_coupling/partially_saturated_cells_method/ParticleAndVolumeFractionMapping.h"
@@ -103,9 +103,10 @@ class DragForceEvaluator
 {
  public:
    DragForceEvaluator(SweepTimeloop* timeloop, Setup* setup, const shared_ptr< StructuredBlockStorage >& blocks,
-                      const BlockDataID& pdfFieldID, const shared_ptr< ParticleAccessor_T >& ac,
-                      walberla::id_t sphereID)
-      : timeloop_(timeloop), setup_(setup), blocks_(blocks), pdfFieldID_(pdfFieldID), ac_(ac), sphereID_(sphereID),
+                      const BlockDataID& pdfFieldID, const BlockDataID& particleAndVolumeFractionFieldID,
+                      const shared_ptr< ParticleAccessor_T >& ac, walberla::id_t sphereID)
+      : timeloop_(timeloop), setup_(setup), blocks_(blocks), pdfFieldID_(pdfFieldID),
+        particleAndVolumeFractionFieldID_(particleAndVolumeFractionFieldID), ac_(ac), sphereID_(sphereID),
         normalizedDragOld_(0.0), normalizedDragNew_(0.0)
    {
       // calculate the analytical drag force value based on the series expansion of chi
@@ -198,13 +199,18 @@ class DragForceEvaluator
       {
          // retrieve the pdf field and the flag field from the block
          PdfField_T* pdfField = blockIt->getData< PdfField_T >(pdfFieldID_);
+         lbm_mesapd_coupling::psm::ParticleAndVolumeFractionField_T* particleAndVolumeFractionField =
+            blockIt->getData< lbm_mesapd_coupling::psm::ParticleAndVolumeFractionField_T >(
+               particleAndVolumeFractionFieldID_);
 
          // get the flag that marks a cell as being fluid
 
          auto xyzField = pdfField->xyzSize();
          for (auto cell : xyzField)
          {
-            velocity_sum += pdfField->getVelocity(cell)[0];
+            // TODO: weighting is fixed to 1
+            velocity_sum += lbm_mesapd_coupling::psm::getPSMMacroscopicVelocity< LatticeModel_T, 1 >(
+               *blockIt, pdfField, particleAndVolumeFractionField, *blocks_, cell, *ac_)[0];
          }
       }
 
@@ -219,6 +225,7 @@ class DragForceEvaluator
 
    shared_ptr< StructuredBlockStorage > blocks_;
    const BlockDataID pdfFieldID_;
+   const BlockDataID particleAndVolumeFractionFieldID_;
 
    shared_ptr< ParticleAccessor_T > ac_;
    const walberla::id_t sphereID_;
@@ -278,11 +285,12 @@ int main(int argc, char** argv)
    // Customization //
    ///////////////////
 
-   bool shortrun = false;
-   bool funcTest = false;
-   bool logging  = false;
-   real_t tau    = real_c(1.5);
-   uint_t length = uint_c(32);
+   bool shortrun       = false;
+   bool funcTest       = false;
+   bool logging        = false;
+   uint_t vtkFrequency = uint_c(0);
+   real_t tau          = real_c(1.5);
+   uint_t length       = uint_c(32);
 
    for (int i = 1; i < argc; ++i)
    {
@@ -311,6 +319,11 @@ int main(int argc, char** argv)
          length = uint_c(std::atof(argv[++i]));
          continue;
       }
+      if (std::strcmp(argv[i], "--vtkFrequency") == 0)
+      {
+         vtkFrequency = uint_c(std::atof(argv[++i]));
+         continue;
+      }
       WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
    }
 
@@ -440,7 +453,8 @@ int main(int argc, char** argv)
 
    // add LBM communication function and streaming & force evaluation
    using DragForceEval_T = DragForceEvaluator< ParticleAccessor_T >;
-   auto forceEval        = make_shared< DragForceEval_T >(&timeloop, &setup, blocks, pdfFieldID, accessor, sphereID);
+   auto forceEval        = make_shared< DragForceEval_T >(&timeloop, &setup, blocks, pdfFieldID,
+                                                   particleAndVolumeFractionFieldID, accessor, sphereID);
    timeloop.add() << BeforeFunction(optimizedPDFCommunicationScheme, "LBM Communication")
                   << Sweep(lbm::makeStreamSweep(sweep), "cell-wise LB sweep (stream)")
                   << AfterFunction(SharedFunctor< DragForceEval_T >(forceEval), "drag force evaluation");
@@ -454,7 +468,17 @@ int main(int argc, char** argv)
       "reset force on sphere");
 
    timeloop.addFuncAfterTimeStep(RemainingTimeLogger(timeloop.getNrOfTimeSteps()), "Remaining Time Logger");
+   if (vtkFrequency > 0)
+   {
+      const std::string path = "vtk_out/dragForceSphere";
+      auto vtkOutput = vtk::createVTKOutput_BlockData(*blocks, "psm_velocity_field", vtkFrequency, 0, false, path,
+                                                      "simulation_step", false, true, true, false, 0);
+
+      auto velWriter = make_shared< walberla::lbm::VelocityVTKWriter< LatticeModel_T > >(pdfFieldID, "Velocity");
+      vtkOutput->addCellDataWriter(velWriter);
 
+      timeloop.addFuncBeforeTimeStep(vtk::writeFiles(vtkOutput), "VTK Output");
+   }
    ////////////////////////
    // EXECUTE SIMULATION //
    ////////////////////////
diff --git a/tests/lbm_mesapd_coupling/partially_saturated_cells_method/ParticleAndVolumeFractionMapping.cpp b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/ParticleAndVolumeFractionMappingPSM.cpp
similarity index 98%
rename from tests/lbm_mesapd_coupling/partially_saturated_cells_method/ParticleAndVolumeFractionMapping.cpp
rename to tests/lbm_mesapd_coupling/partially_saturated_cells_method/ParticleAndVolumeFractionMappingPSM.cpp
index d1bd029a7fa4109d2390b14f93744d2b8d6b4d75..b49590aedd345de8fd8efa5bfbaaa54a0f75d93c 100644
--- a/tests/lbm_mesapd_coupling/partially_saturated_cells_method/ParticleAndVolumeFractionMapping.cpp
+++ b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/ParticleAndVolumeFractionMappingPSM.cpp
@@ -146,9 +146,8 @@ int main(int argc, char** argv)
 
    // set up synchronization
    std::function< void(void) > syncCall = [&]() {
-      const real_t overlap = real_t(1.5) * dx;
       mesa_pd::mpi::SyncNextNeighbors syncNextNeighborFunc;
-      syncNextNeighborFunc(*ps, *mesapdDomain, overlap);
+      syncNextNeighborFunc(*ps, *mesapdDomain);
    };
 
    // add the sphere in the center of the domain
diff --git a/tests/lbm_mesapd_coupling/partially_saturated_cells_method/SettlingSphere.cpp b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/SettlingSpherePSM.cpp
similarity index 99%
rename from tests/lbm_mesapd_coupling/partially_saturated_cells_method/SettlingSphere.cpp
rename to tests/lbm_mesapd_coupling/partially_saturated_cells_method/SettlingSpherePSM.cpp
index 8144d27523f2d72c57bc08e735beb2f8f8d6adf7..f0dd609111994eecf7b0d5519ccd9e2da5376c51 100644
--- a/tests/lbm_mesapd_coupling/partially_saturated_cells_method/SettlingSphere.cpp
+++ b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/SettlingSpherePSM.cpp
@@ -592,9 +592,8 @@ int main(int argc, char** argv)
 
    // set up RPD functionality
    std::function< void(void) > syncCall = [ps, rpdDomain]() {
-      const real_t overlap = real_t(1.5);
       mesa_pd::mpi::SyncNextNeighbors syncNextNeighborFunc;
-      syncNextNeighborFunc(*ps, *rpdDomain, overlap);
+      syncNextNeighborFunc(*ps, *rpdDomain);
    };
 
    syncCall();
diff --git a/tests/lbm_mesapd_coupling/partially_saturated_cells_method/TorqueSphere.cpp b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/TorqueSpherePSM.cpp
similarity index 98%
rename from tests/lbm_mesapd_coupling/partially_saturated_cells_method/TorqueSphere.cpp
rename to tests/lbm_mesapd_coupling/partially_saturated_cells_method/TorqueSpherePSM.cpp
index f0db14fe5d902f80b1e5f293ef6dc0b5a50f64ca..419897653e391094f62a668e2ec3602395fd4165 100644
--- a/tests/lbm_mesapd_coupling/partially_saturated_cells_method/TorqueSphere.cpp
+++ b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/TorqueSpherePSM.cpp
@@ -57,8 +57,6 @@
 #include "mesa_pd/domain/BlockForestDomain.h"
 #include "mesa_pd/mpi/SyncNextNeighbors.h"
 
-#include "stencil/D3Q27.h"
-
 #include "timeloop/SweepTimeloop.h"
 
 #include <iostream>
@@ -392,11 +390,10 @@ int main(int argc, char** argv)
       sphereParticle->setInteractionRadius(setup.radius);
    }
 
-   // synchronize often enough for large bodies
+   // synchronize often enough for large particles
    std::function< void(void) > syncCall = [&]() {
-      // const real_t overlap = real_t(1.5) * dx;
       mesa_pd::mpi::SyncNextNeighbors syncNextNeighborFunc;
-      syncNextNeighborFunc(*ps, *mesapdDomain, overlap);
+      syncNextNeighborFunc(*ps, *mesapdDomain);
    };
 
    syncCall();
@@ -418,7 +415,7 @@ int main(int argc, char** argv)
       field::addToStorage< lbm_mesapd_coupling::psm::ParticleAndVolumeFractionField_T >(
          blocks, "particle and volume fraction field",
          std::vector< lbm_mesapd_coupling::psm::ParticleAndVolumeFraction_T >(), field::fzyx, 0);
-   // map bodies and calculate solid volume fraction initially
+   // map particles and calculate solid volume fraction initially
    lbm_mesapd_coupling::psm::ParticleAndVolumeFractionMapping particleMapping(
       blocks, accessor, lbm_mesapd_coupling::RegularParticlesSelector(), particleAndVolumeFractionFieldID, 4);
    particleMapping();
@@ -445,7 +442,7 @@ int main(int argc, char** argv)
    // setup of the LBM communication for synchronizing the pdf field between neighboring blocks
    std::function< void() > commFunction;
 
-   blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > scheme(blocks);
+   blockforest::communication::UniformBufferedScheme< Stencil_T > scheme(blocks);
    scheme.addPackInfo(make_shared< field::communication::PackInfo< PdfField_T > >(pdfFieldID));
    commFunction = scheme;
 
diff --git a/tests/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/DragForceSpherePSM.cpp b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/DragForceSpherePSM.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..84edc556741f33c130d5160f5e2dac06eb24cf2d
--- /dev/null
+++ b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/DragForceSpherePSM.cpp
@@ -0,0 +1,552 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file DragForceSpherePSMCPUGPU.cpp
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//! \brief Modification of partially_saturated_cells_method/DragForceSphere.cpp
+//
+//======================================================================================================================
+
+#include "blockforest/Initialization.h"
+#include "blockforest/communication/UniformBufferedScheme.h"
+
+#include "core/Environment.h"
+#include "core/SharedFunctor.h"
+#include "core/debug/TestSubsystem.h"
+#include "core/logging/Logging.h"
+#include "core/mpi/MPIManager.h"
+#include "core/mpi/Reduce.h"
+#include "core/timing/RemainingTimeLogger.h"
+
+#include "field/vtk/VTKWriter.h"
+
+#include "gpu/AddGPUFieldToStorage.h"
+#include "gpu/DeviceSelectMPI.h"
+#include "gpu/communication/UniformGPUScheme.h"
+
+#include "lbm_mesapd_coupling/DataTypesCodegen.h"
+#include "lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMSweepCollection.h"
+#include "lbm_mesapd_coupling/utility/ResetHydrodynamicForceTorqueKernel.h"
+
+#include "mesa_pd/data/ParticleAccessorWithShape.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/ShapeStorage.h"
+#include "mesa_pd/domain/BlockForestDomain.h"
+#include "mesa_pd/kernel/ParticleSelector.h"
+
+#include <iostream>
+
+// codegen
+#include "InitializeDomainForPSM.h"
+#include "PSMPackInfo.h"
+#include "PSMSweep.h"
+#include "PSM_InfoHeader.h"
+#include "PSM_MacroGetter.h"
+
+namespace drag_force_sphere_psm
+{
+
+///////////
+// USING //
+///////////
+
+using namespace walberla;
+using walberla::uint_t;
+using namespace lbm_mesapd_coupling::psm::gpu;
+
+typedef pystencils::PSMPackInfo PackInfo_T;
+
+////////////////
+// PARAMETERS //
+////////////////
+
+struct Setup
+{
+   uint_t checkFrequency;
+   real_t visc;
+   real_t tau;
+   real_t radius;
+   uint_t length;
+   real_t chi;
+   real_t extForce;
+   real_t analyticalDrag;
+};
+
+template< typename ParticleAccessor_T >
+class DragForceEvaluator
+{
+ public:
+   DragForceEvaluator(SweepTimeloop* timeloop, Setup* setup, const shared_ptr< StructuredBlockStorage >& blocks,
+                      const BlockDataID& velocityFieldID, const shared_ptr< ParticleAccessor_T >& ac,
+                      walberla::id_t sphereID)
+      : timeloop_(timeloop), setup_(setup), blocks_(blocks), velocityFieldID_(velocityFieldID), ac_(ac),
+        sphereID_(sphereID), normalizedDragOld_(0.0), normalizedDragNew_(0.0)
+   {
+      // calculate the analytical drag force value based on the series expansion of chi
+      // see also Sangani - Slow flow through a periodic array of spheres, IJMF 1982. Eq. 60 and Table 1
+      real_t analyticalDrag = real_c(0);
+      real_t tempChiPowS    = real_c(1);
+
+      // coefficients to calculate the drag in a series expansion
+      real_t dragCoefficients[31] = { real_c(1.000000),  real_c(1.418649),  real_c(2.012564),   real_c(2.331523),
+                                      real_c(2.564809),  real_c(2.584787),  real_c(2.873609),   real_c(3.340163),
+                                      real_c(3.536763),  real_c(3.504092),  real_c(3.253622),   real_c(2.689757),
+                                      real_c(2.037769),  real_c(1.809341),  real_c(1.877347),   real_c(1.534685),
+                                      real_c(0.9034708), real_c(0.2857896), real_c(-0.5512626), real_c(-1.278724),
+                                      real_c(1.013350),  real_c(5.492491),  real_c(4.615388),   real_c(-0.5736023),
+                                      real_c(-2.865924), real_c(-4.709215), real_c(-6.870076),  real_c(0.1455304),
+                                      real_c(12.51891),  real_c(9.742811),  real_c(-5.566269) };
+
+      for (uint_t s = 0; s <= uint_t(30); ++s)
+      {
+         analyticalDrag += dragCoefficients[s] * tempChiPowS;
+         tempChiPowS *= setup->chi;
+      }
+      setup_->analyticalDrag = analyticalDrag;
+   }
+
+   // evaluate the acting drag force
+   void operator()()
+   {
+      const uint_t timestep(timeloop_->getCurrentTimeStep() + 1);
+
+      if (timestep % setup_->checkFrequency != 0) return;
+
+      // get force in x-direction acting on the sphere
+      real_t forceX = computeDragForce();
+      // get average volumetric flowrate in the domain
+      real_t uBar = computeAverageVel();
+
+      // f_total = f_drag + f_buoyancy
+      real_t totalForce =
+         forceX + real_c(4.0 / 3.0) * math::pi * setup_->radius * setup_->radius * setup_->radius * setup_->extForce;
+
+      real_t normalizedDragForce = totalForce / real_c(6.0 * math::pi * setup_->visc * setup_->radius * uBar);
+
+      // update drag force values
+      normalizedDragOld_ = normalizedDragNew_;
+      normalizedDragNew_ = normalizedDragForce;
+   }
+
+   // return the relative temporal change in the normalized drag
+   real_t getDragForceDiff() const { return std::fabs((normalizedDragNew_ - normalizedDragOld_) / normalizedDragNew_); }
+
+   // return the drag force
+   real_t getDragForce() const { return normalizedDragNew_; }
+
+   void logResultToFile(const std::string& filename) const
+   {
+      // write to file if desired
+      // format: length tau viscosity simulatedDrag analyticalDrag\n
+      WALBERLA_ROOT_SECTION()
+      {
+         std::ofstream file;
+         file.open(filename.c_str(), std::ofstream::app);
+         file.precision(8);
+         file << setup_->length << " " << setup_->tau << " " << setup_->visc << " " << normalizedDragNew_ << " "
+              << setup_->analyticalDrag << "\n";
+         file.close();
+      }
+   }
+
+ private:
+   // obtain the drag force acting on the sphere by summing up all the process local parts of fX
+   real_t computeDragForce()
+   {
+      size_t idx = ac_->uidToIdx(sphereID_);
+      WALBERLA_ASSERT_UNEQUAL(idx, ac_->getInvalidIdx(), "Index of particle is invalid!");
+      real_t force = real_t(0);
+      if (idx != ac_->getInvalidIdx()) { force = ac_->getHydrodynamicForce(idx)[0]; }
+
+      WALBERLA_MPI_SECTION() { mpi::allReduceInplace(force, mpi::SUM); }
+
+      return force;
+   }
+
+   // calculate the average velocity in forcing direction (here: x) inside the domain (assuming dx=1)
+   real_t computeAverageVel()
+   {
+      auto velocity_sum = real_t(0);
+      // iterate all blocks stored locally on this process
+      for (auto blockIt = blocks_->begin(); blockIt != blocks_->end(); ++blockIt)
+      {
+         // retrieve the pdf field and the flag field from the block
+         VelocityField_T* velocityField = blockIt->getData< VelocityField_T >(velocityFieldID_);
+
+         // get the flag that marks a cell as being fluid
+
+         auto xyzField = velocityField->xyzSize();
+         for (auto cell : xyzField)
+         {
+            // TODO: fix velocity computation by using getPSMMacroscopicVelocity
+            velocity_sum += velocityField->get(cell, 0);
+         }
+      }
+
+      WALBERLA_MPI_SECTION() { mpi::allReduceInplace(velocity_sum, mpi::SUM); }
+
+      return velocity_sum / real_c(setup_->length * setup_->length * setup_->length);
+   }
+
+   SweepTimeloop* timeloop_;
+
+   Setup* setup_;
+
+   shared_ptr< StructuredBlockStorage > blocks_;
+   const BlockDataID velocityFieldID_;
+
+   shared_ptr< ParticleAccessor_T > ac_;
+   const walberla::id_t sphereID_;
+
+   // drag coefficient
+   real_t normalizedDragOld_;
+   real_t normalizedDragNew_;
+};
+
+//////////
+// MAIN //
+//////////
+
+//*******************************************************************************************************************
+/*!\brief Testcase that checks the drag force acting on a fixed sphere in the center of a cubic domain in Stokes flow
+ *
+ * The drag force for this problem (often denoted as Simple Cubic setup) is given by a semi-analytical series expansion.
+ * The cubic domain is periodic in all directions, making it a physically infinite periodic array of spheres.
+   \verbatim
+           _______________
+        ->|               |->
+        ->|      ___      |->
+      W ->|     /   \     |-> E
+      E ->|    |  x  |    |-> A
+      S ->|     \___/     |-> S
+      T ->|               |-> T
+        ->|_______________|->
+
+   \endverbatim
+ *
+ * The collision model used for the LBM is TRT with a relaxation parameter tau=1.5 and the magic parameter 3/16.
+ * The Stokes approximation of the equilibrium PDFs is used.
+ * The flow is driven by a constant particle force of 1e-5.
+ * The domain is 32x32x32, and the sphere has a diameter of 16 cells ( chi * domainlength )
+ * The simulation is run until the relative change in the dragforce between 100 time steps is less than 1e-5.
+ * The RPD is not used since the sphere is kept fixed and the force is explicitly reset after each time step.
+ * To avoid periodicity constrain problems, the sphere is set as global.
+ *
+ */
+//*******************************************************************************************************************
+
+int main(int argc, char** argv)
+{
+   debug::enterTestMode();
+
+   mpi::Environment env(argc, argv);
+
+   logging::Logging::instance()->setLogLevel(logging::Logging::INFO);
+
+   auto processes = MPIManager::instance()->numProcesses();
+
+   if (processes != 1 && processes != 2 && processes != 4 && processes != 8)
+   {
+      std::cerr << "Number of processes must be equal to either 1, 2, 4, or 8!" << std::endl;
+      return EXIT_FAILURE;
+   }
+
+   ///////////////////
+   // Customization //
+   ///////////////////
+
+   bool shortrun       = false;
+   bool funcTest       = false;
+   bool logging        = false;
+   uint_t vtkFrequency = uint_c(0);
+   real_t tau          = real_c(1.5);
+   uint_t length       = uint_c(32);
+
+   for (int i = 1; i < argc; ++i)
+   {
+      if (std::strcmp(argv[i], "--shortrun") == 0)
+      {
+         shortrun = true;
+         continue;
+      }
+      if (std::strcmp(argv[i], "--funcTest") == 0)
+      {
+         funcTest = true;
+         continue;
+      }
+      if (std::strcmp(argv[i], "--logging") == 0)
+      {
+         logging = true;
+         continue;
+      }
+      if (std::strcmp(argv[i], "--tau") == 0)
+      {
+         tau = real_c(std::atof(argv[++i]));
+         continue;
+      }
+      if (std::strcmp(argv[i], "--length") == 0)
+      {
+         length = uint_c(std::atof(argv[++i]));
+         continue;
+      }
+      if (std::strcmp(argv[i], "--vtkFrequency") == 0)
+      {
+         vtkFrequency = uint_c(std::atof(argv[++i]));
+         continue;
+      }
+      WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
+   }
+
+   ///////////////////////////
+   // SIMULATION PROPERTIES //
+   ///////////////////////////
+
+   Setup setup;
+
+   setup.length                  = length;       // length of the cubic domain in lattice cells
+   setup.chi                     = real_c(0.5);  // porosity parameter: diameter / length
+   setup.tau                     = tau;          // relaxation time
+   setup.extForce                = real_c(1e-7); // constant particle force in lattice units
+   setup.checkFrequency          = uint_t(100);  // evaluate the drag force only every checkFrequency time steps
+   setup.radius                  = real_c(0.5) * setup.chi * real_c(setup.length); // sphere radius
+   setup.visc                    = (setup.tau - real_c(0.5)) / real_c(3);          // viscosity in lattice units
+   const real_t omega            = real_c(1) / setup.tau;                          // relaxation rate
+   const real_t dx               = real_c(1);                                      // lattice dx
+   const real_t convergenceLimit = real_c(1e-7); // tolerance for relative change in drag force
+   const uint_t timesteps =
+      funcTest ? 1 : (shortrun ? uint_c(150) : uint_c(50000)); // maximum number of time steps for the whole simulation
+
+   ///////////////////////////
+   // BLOCK STRUCTURE SETUP //
+   ///////////////////////////
+
+   const uint_t XBlocks = (processes >= 2) ? uint_t(2) : uint_t(1);
+   const uint_t YBlocks = (processes >= 4) ? uint_t(2) : uint_t(1);
+   const uint_t ZBlocks = (processes == 8) ? uint_t(2) : uint_t(1);
+   const uint_t XCells  = setup.length / XBlocks;
+   const uint_t YCells  = setup.length / YBlocks;
+   const uint_t ZCells  = setup.length / ZBlocks;
+
+   // create fully periodic domain
+   auto blocks = blockforest::createUniformBlockGrid(XBlocks, YBlocks, ZBlocks, XCells, YCells, ZCells, dx, true, true,
+                                                     true, true);
+
+   /////////
+   // RPD //
+   /////////
+
+   mesa_pd::domain::BlockForestDomain domain(blocks->getBlockForestPointer());
+
+   // init data structures
+   auto ps                  = std::make_shared< mesa_pd::data::ParticleStorage >(1);
+   auto ss                  = std::make_shared< mesa_pd::data::ShapeStorage >();
+   using ParticleAccessor_T = mesa_pd::data::ParticleAccessorWithShape;
+   auto accessor            = make_shared< ParticleAccessor_T >(ps, ss);
+   auto sphereShape         = ss->create< mesa_pd::data::Sphere >(setup.radius);
+
+   //////////////////
+   // RPD COUPLING //
+   //////////////////
+
+   // connect to pe
+   const real_t overlap = real_t(1.5) * dx;
+
+   if (setup.radius > real_c(setup.length) * real_t(0.5) - overlap)
+   {
+      std::cerr << "Periodic sphere is too large and would lead to incorrect mapping!" << std::endl;
+      // solution: create the periodic copies explicitly
+      return EXIT_FAILURE;
+   }
+
+   // create the sphere in the middle of the domain
+   // it is global and thus present on all processes
+   Vector3< real_t > position(real_c(setup.length) * real_c(0.5));
+   walberla::id_t sphereID;
+   {
+      mesa_pd::data::Particle&& p = *ps->create(true);
+      p.setPosition(position);
+      p.setInteractionRadius(setup.radius);
+      p.setOwner(mpi::MPIManager::instance()->rank());
+      p.setShapeID(sphereShape);
+      sphereID = p.getUid();
+   }
+
+   ///////////////////////
+   // ADD DATA TO BLOCKS //
+   ////////////////////////
+
+   // add fields
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   BlockDataID pdfFieldID =
+      field::addToStorage< PdfField_T >(blocks, "pdf field (fzyx)", real_c(std::nan("")), field::fzyx);
+   BlockDataID BFieldID         = field::addToStorage< BField_T >(blocks, "B", real_t(0), field::fzyx);
+   BlockDataID pdfFieldCPUGPUID = gpu::addGPUFieldToStorage< PdfField_T >(blocks, pdfFieldID, "pdf field GPU");
+#else
+   BlockDataID pdfFieldCPUGPUID =
+      field::addToStorage< PdfField_T >(blocks, "pdf field CPU", real_c(std::nan("")), field::fzyx);
+#endif
+
+   BlockDataID densityFieldID = field::addToStorage< DensityField_T >(blocks, "Density", real_t(0), field::fzyx);
+   BlockDataID velFieldID     = field::addToStorage< VelocityField_T >(blocks, "Velocity", real_t(0), field::fzyx);
+
+   ///////////////
+   // TIME LOOP //
+   ///////////////
+
+   // create the timeloop
+   SweepTimeloop timeloop(blocks->getBlockStorage(), timesteps);
+
+   // setup of the LBM communication for synchronizing the pdf field between neighboring blocks
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   gpu::communication::UniformGPUScheme< Stencil_T > com(blocks, 0, false);
+#else
+   walberla::blockforest::communication::UniformBufferedScheme< Stencil_T > com(blocks);
+#endif
+   com.addPackInfo(make_shared< PackInfo_T >(pdfFieldCPUGPUID));
+   auto communication = std::function< void() >([&]() { com.communicate(); });
+
+   // add particle and volume fraction data structures
+   ParticleAndVolumeFractionSoA_T< 1 > particleAndVolumeFractionSoA(blocks, omega);
+
+   // map particles and calculate solid volume fraction initially
+   PSMSweepCollection psmSweepCollection(blocks, accessor, lbm_mesapd_coupling::GlobalParticlesSelector(),
+                                         particleAndVolumeFractionSoA, Vector3(8));
+   for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+   {
+      psmSweepCollection.particleMappingSweep(&(*blockIt));
+   }
+
+   pystencils::InitializeDomainForPSM pdfSetter(
+      particleAndVolumeFractionSoA.BsFieldID, particleAndVolumeFractionSoA.BFieldID,
+      particleAndVolumeFractionSoA.particleVelocitiesFieldID, pdfFieldCPUGPUID, real_t(0), real_t(0), real_t(0),
+      real_t(1.0), real_t(0), real_t(0), real_t(0));
+
+   for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+   {
+      // pdfSetter requires particle velocities at cell centers
+      psmSweepCollection.setParticleVelocitiesSweep(&(*blockIt));
+      pdfSetter(&(*blockIt));
+   }
+
+   // since external forcing is applied, the evaluation of the velocity has to be carried out directly after the
+   // streaming step however, the default sweep is a  stream - collide step, i.e. after the sweep, the velocity
+   // evaluation is not correct solution: split the sweep explicitly into collide and stream
+   pystencils::PSMSweep PSMSweep(particleAndVolumeFractionSoA.BsFieldID, particleAndVolumeFractionSoA.BFieldID,
+                                 particleAndVolumeFractionSoA.particleForcesFieldID,
+                                 particleAndVolumeFractionSoA.particleVelocitiesFieldID, pdfFieldCPUGPUID,
+                                 setup.extForce, real_t(0.0), real_t(0.0), omega);
+
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   pystencils::PSM_MacroGetter getterSweep(BFieldID, densityFieldID, pdfFieldID, velFieldID, setup.extForce,
+                                           real_t(0.0), real_t(0.0));
+#else
+   pystencils::PSM_MacroGetter getterSweep(particleAndVolumeFractionSoA.BFieldID, densityFieldID, pdfFieldCPUGPUID,
+                                           velFieldID, setup.extForce, real_t(0.0), real_t(0.0));
+#endif
+
+   // add LBM communication function and streaming & force evaluation
+   using DragForceEval_T = DragForceEvaluator< ParticleAccessor_T >;
+   auto forceEval        = make_shared< DragForceEval_T >(&timeloop, &setup, blocks, velFieldID, accessor, sphereID);
+   timeloop.add() << BeforeFunction(communication, "LBM Communication")
+                  << Sweep(deviceSyncWrapper(psmSweepCollection.setParticleVelocitiesSweep), "Set particle velocities");
+   timeloop.add() << Sweep(deviceSyncWrapper(PSMSweep), "cell-wise PSM sweep");
+   timeloop.add() << Sweep(deviceSyncWrapper(psmSweepCollection.reduceParticleForcesSweep), "Reduce particle forces");
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   timeloop.add() << Sweep(gpu::fieldCpyFunctor< PdfField_T, gpu::GPUField< real_t > >(pdfFieldID, pdfFieldCPUGPUID),
+                           "copy pdf from GPU to CPU");
+   timeloop.add() << Sweep(
+      gpu::fieldCpyFunctor< BField_T, gpu::GPUField< real_t > >(BFieldID, particleAndVolumeFractionSoA.BFieldID),
+      "copy B field from GPU to CPU");
+#endif
+   timeloop.add() << Sweep(getterSweep, "compute velocity")
+                  << AfterFunction(SharedFunctor< DragForceEval_T >(forceEval), "drag force evaluation");
+
+   // resetting force
+   timeloop.addFuncAfterTimeStep(
+      [ps, accessor]() {
+         ps->forEachParticle(false, mesa_pd::kernel::SelectAll(), *accessor,
+                             lbm_mesapd_coupling::ResetHydrodynamicForceTorqueKernel(), *accessor);
+      },
+      "reset force on sphere");
+
+   timeloop.addFuncAfterTimeStep(RemainingTimeLogger(timeloop.getNrOfTimeSteps()), "Remaining Time Logger");
+
+   if (vtkFrequency > 0)
+   {
+      const std::string path = "vtk_out/dragForceSphereCPUGPU";
+      auto vtkOutput = vtk::createVTKOutput_BlockData(*blocks, "psm_velocity_fieldCPUGPU", vtkFrequency, 0, false, path,
+                                                      "simulation_step", false, true, true, false, 0);
+
+      vtkOutput->addBeforeFunction([&]() {
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+         gpu::fieldCpy< PdfField_T, gpu::GPUField< real_t > >(blocks, pdfFieldID, pdfFieldCPUGPUID);
+         gpu::fieldCpy< BField_T, gpu::GPUField< real_t > >(blocks, BFieldID, particleAndVolumeFractionSoA.BFieldID);
+#endif
+         for (auto& block : *blocks)
+            getterSweep(&block);
+      });
+      vtkOutput->addCellDataWriter(make_shared< field::VTKWriter< VelocityField_T > >(velFieldID, "Velocity"));
+
+      timeloop.addFuncBeforeTimeStep(vtk::writeFiles(vtkOutput), "VTK Output");
+   }
+
+   ////////////////////////
+   // EXECUTE SIMULATION //
+   ////////////////////////
+
+   WcTimingPool timeloopTiming;
+
+   // time loop
+   for (uint_t i = 0; i < timesteps; ++i)
+   {
+      // perform a single simulation step
+      timeloop.singleStep(timeloopTiming);
+
+      // check if the relative change in the normalized drag force is below the specified convergence criterion
+      if (i > setup.checkFrequency && forceEval->getDragForceDiff() < convergenceLimit)
+      {
+         // if simulation has converged, terminate simulation
+         break;
+      }
+   }
+
+   timeloopTiming.logResultOnRoot();
+
+   if (!funcTest && !shortrun)
+   {
+      // check the result
+      real_t relErr = std::fabs((setup.analyticalDrag - forceEval->getDragForce()) / setup.analyticalDrag);
+      if (logging)
+      {
+         WALBERLA_ROOT_SECTION()
+         {
+            std::cout << "Analytical drag: " << setup.analyticalDrag << "\n"
+                      << "Simulated drag: " << forceEval->getDragForce() << "\n"
+                      << "Relative error: " << relErr << "\n";
+         }
+         forceEval->logResultToFile("log_DragForceSphere.txt");
+      }
+      // the relative error has to be below 10%
+      WALBERLA_CHECK_LESS(relErr, real_c(0.1));
+   }
+
+   return 0;
+}
+
+} // namespace drag_force_sphere_psm
+
+int main(int argc, char** argv) { drag_force_sphere_psm::main(argc, argv); }
diff --git a/tests/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMapping.cpp b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMapping.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d71062655a4e4b4bffaa325bb040654d9e603900
--- /dev/null
+++ b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/ParticleAndVolumeFractionMapping.cpp
@@ -0,0 +1,311 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file ParticleAndVolumeFractionMappingPSMCPUGPU.cpp
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#include "blockforest/Initialization.h"
+
+#include "core/DataTypes.h"
+#include "core/Environment.h"
+#include "core/debug/TestSubsystem.h"
+
+#include "field/AddToStorage.h"
+
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+#   include "gpu/FieldCopy.h"
+#   include "gpu/GPUField.h"
+#endif
+
+#include "lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMSweepCollection.h"
+#include "lbm_mesapd_coupling/utility/ParticleSelector.h"
+
+#include "mesa_pd/data/ParticleAccessorWithShape.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/ShapeStorage.h"
+#include "mesa_pd/domain/BlockForestDomain.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
+#include "mesa_pd/mpi/SyncNextNeighbors.h"
+
+#include <memory>
+
+namespace particle_volume_fraction_check
+{
+
+///////////
+// USING //
+///////////
+
+using namespace walberla;
+using namespace walberla::lbm_mesapd_coupling::psm::gpu;
+
+//*******************************************************************************************************************
+/*!\brief Calculating the sum over all fraction values. This can be used as a sanity check since it has to be roughly
+ * equal to the volume of all particles.
+ *
+ */
+//*******************************************************************************************************************
+class FractionFieldSum
+{
+ public:
+   FractionFieldSum(const shared_ptr< StructuredBlockStorage >& blockStorage,
+                    const BlockDataID& nOverlappingParticlesFieldID, const BlockDataID& BsFieldID)
+      : blockStorage_(blockStorage), nOverlappingParticlesFieldID_(nOverlappingParticlesFieldID), BsFieldID_(BsFieldID)
+   {}
+
+   real_t operator()()
+   {
+      real_t sum = 0.0;
+
+      for (auto blockIt = blockStorage_->begin(); blockIt != blockStorage_->end(); ++blockIt)
+      {
+         auto nOverlappingParticlesField =
+            blockIt->getData< nOverlappingParticlesField_T >(nOverlappingParticlesFieldID_);
+         auto BsField = blockIt->getData< BsField_T >(BsFieldID_);
+
+         const cell_idx_t xSize = cell_idx_c(BsField->xSize());
+         const cell_idx_t ySize = cell_idx_c(BsField->ySize());
+         const cell_idx_t zSize = cell_idx_c(BsField->zSize());
+
+         for (cell_idx_t z = 0; z < zSize; ++z)
+         {
+            for (cell_idx_t y = 0; y < ySize; ++y)
+            {
+               for (cell_idx_t x = 0; x < xSize; ++x)
+               {
+                  for (uint_t n = 0; n < nOverlappingParticlesField->get(x, y, z); ++n)
+                  {
+                     sum += BsField->get(x, y, z, n);
+                  }
+               }
+            }
+         }
+      }
+
+      WALBERLA_MPI_SECTION() { mpi::allReduceInplace(sum, mpi::SUM); }
+
+      return sum;
+   }
+
+ private:
+   shared_ptr< StructuredBlockStorage > blockStorage_;
+   BlockDataID nOverlappingParticlesFieldID_;
+   BlockDataID BsFieldID_;
+};
+
+////////////////
+// Parameters //
+////////////////
+
+struct Setup
+{
+   // domain size (in lattice cells) in x, y and z direction
+   uint_t xlength;
+   uint_t ylength;
+   uint_t zlength;
+
+   // number of block in x, y and z, direction
+   Vector3< uint_t > nBlocks;
+
+   // cells per block in x, y and z direction
+   Vector3< uint_t > cellsPerBlock;
+
+   real_t sphereDiam;
+
+   uint_t timesteps;
+};
+
+//////////
+// MAIN //
+//////////
+
+//*******************************************************************************************************************
+/*!\brief Testcase that checks if ParticleAndVolumeFractionMapping.h works as intended
+ *
+ * A sphere particle is placed inside the domain and is moving with a constant velocity. The overlap fraction is
+ * computed for all cells in each time step. If the mapping is correct, the sum over all fractions should be roughly
+ * equivalent to the volume of the sphere.
+ *
+ */
+//*******************************************************************************************************************
+
+int main(int argc, char** argv)
+{
+   debug::enterTestMode();
+
+   mpi::Environment env(argc, argv);
+
+   logging::Logging::instance()->setLogLevel(logging::Logging::INFO);
+
+   auto processes = MPIManager::instance()->numProcesses();
+
+   if (processes != 27)
+   {
+      std::cerr << "Number of processes must be 27!" << std::endl;
+      return EXIT_FAILURE;
+   }
+
+   ///////////////////////////
+   // SIMULATION PROPERTIES //
+   ///////////////////////////
+
+   Setup setup;
+
+   setup.sphereDiam = real_c(12);
+   setup.zlength    = uint_c(4 * setup.sphereDiam);
+   setup.xlength    = setup.zlength;
+   setup.ylength    = setup.zlength;
+
+   const real_t sphereRadius = real_c(0.5) * setup.sphereDiam;
+   const real_t dx           = real_c(1);
+
+   setup.timesteps = 100;
+
+   ///////////////////////////
+   // BLOCK STRUCTURE SETUP //
+   ///////////////////////////
+
+   setup.nBlocks[0]       = uint_c(3);
+   setup.nBlocks[1]       = uint_c(3);
+   setup.nBlocks[2]       = uint_c(3);
+   setup.cellsPerBlock[0] = setup.xlength / setup.nBlocks[0];
+   setup.cellsPerBlock[1] = setup.ylength / setup.nBlocks[1];
+   setup.cellsPerBlock[2] = setup.zlength / setup.nBlocks[2];
+
+   auto blocks =
+      blockforest::createUniformBlockGrid(setup.nBlocks[0], setup.nBlocks[1], setup.nBlocks[2], setup.cellsPerBlock[0],
+                                          setup.cellsPerBlock[1], setup.cellsPerBlock[2], dx, true, true, true, true);
+
+   ////////////
+   // MesaPD //
+   ////////////
+
+   auto mesapdDomain        = std::make_shared< mesa_pd::domain::BlockForestDomain >(blocks->getBlockForestPointer());
+   auto ps                  = std::make_shared< mesa_pd::data::ParticleStorage >(1);
+   auto ss                  = std::make_shared< mesa_pd::data::ShapeStorage >();
+   using ParticleAccessor_T = mesa_pd::data::ParticleAccessorWithShape;
+   auto accessor            = walberla::make_shared< ParticleAccessor_T >(ps, ss);
+
+   // set up synchronization
+   std::function< void(void) > syncCall = [&]() {
+      mesa_pd::mpi::SyncNextNeighbors syncNextNeighborFunc;
+      syncNextNeighborFunc(*ps, *mesapdDomain);
+   };
+
+   // add the sphere in the center of the domain
+   Vector3< real_t > position(real_c(setup.xlength) * real_c(0.5), real_c(setup.ylength) * real_c(0.5),
+                              real_c(setup.zlength) * real_c(0.5));
+   Vector3< real_t > velocity(real_c(0.1), real_c(0.1), real_c(0.1));
+   auto sphereShape = ss->create< mesa_pd::data::Sphere >(sphereRadius);
+
+   if (mesapdDomain->isContainedInProcessSubdomain(uint_c(walberla::mpi::MPIManager::instance()->rank()), position))
+   {
+      auto sphereParticle = ps->create();
+
+      sphereParticle->setShapeID(sphereShape);
+      sphereParticle->setType(0);
+      sphereParticle->setPosition(position);
+      sphereParticle->setLinearVelocity(velocity);
+      sphereParticle->setOwner(walberla::MPIManager::instance()->rank());
+      sphereParticle->setInteractionRadius(sphereRadius);
+   }
+
+   Vector3< real_t > position2(real_c(0.0), real_c(0.0), real_c(0.0));
+   Vector3< real_t > velocity2(real_c(0.1), real_c(0.1), real_c(0.1));
+
+   if (mesapdDomain->isContainedInProcessSubdomain(uint_c(walberla::mpi::MPIManager::instance()->rank()), position2))
+   {
+      auto sphereParticle = ps->create();
+
+      sphereParticle->setShapeID(sphereShape);
+      sphereParticle->setType(0);
+      sphereParticle->setPosition(position2);
+      sphereParticle->setLinearVelocity(velocity2);
+      sphereParticle->setOwner(walberla::MPIManager::instance()->rank());
+      sphereParticle->setInteractionRadius(sphereRadius);
+   }
+
+   syncCall();
+
+   ///////////////////////
+   // ADD DATA TO BLOCKS //
+   ////////////////////////
+
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   // add particle and volume fraction fields (needed for the PSM)
+   BlockDataID nOverlappingParticlesFieldID = field::addToStorage< nOverlappingParticlesField_T >(
+      blocks, "number of overlapping particles field CPU", 0, field::fzyx, 1);
+   BlockDataID BsFieldID = field::addToStorage< BsField_T >(blocks, "Bs field CPU", 0, field::fzyx, 1);
+#endif
+
+   // dummy value for omega since it is not use because Weighting_T == 1
+   real_t omega = real_t(42.0);
+   ParticleAndVolumeFractionSoA_T< 1 > particleAndVolumeFractionSoA(blocks, omega);
+
+   // calculate fraction
+   PSMSweepCollection psmSweepCollection(blocks, accessor, lbm_mesapd_coupling::RegularParticlesSelector(),
+                                         particleAndVolumeFractionSoA, Vector3(16));
+   for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+   {
+      psmSweepCollection.particleMappingSweep(&(*blockIt));
+   }
+
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   FractionFieldSum fractionFieldSum(blocks, nOverlappingParticlesFieldID, BsFieldID);
+#else
+   FractionFieldSum fractionFieldSum(blocks, particleAndVolumeFractionSoA.nOverlappingParticlesFieldID,
+                                     particleAndVolumeFractionSoA.BsFieldID);
+#endif
+   auto selector = mesa_pd::kernel::SelectMaster();
+   mesa_pd::kernel::SemiImplicitEuler particleIntegration(1.0);
+
+   for (uint_t i = 0; i < setup.timesteps; ++i)
+   {
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+      // copy data back to perform the check on CPU
+      for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+      {
+         gpu::fieldCpySweepFunction< nOverlappingParticlesField_T, nOverlappingParticlesFieldGPU_T >(
+            nOverlappingParticlesFieldID, particleAndVolumeFractionSoA.nOverlappingParticlesFieldID, &(*blockIt));
+         gpu::fieldCpySweepFunction< BsField_T, BsFieldGPU_T >(BsFieldID, particleAndVolumeFractionSoA.BsFieldID,
+                                                               &(*blockIt));
+      }
+#endif
+
+      // check that the sum over all fractions is roughly the volume of the sphere
+      real_t sum = fractionFieldSum();
+      WALBERLA_CHECK_LESS(std::fabs(4.0 / 3.0 * math::pi * sphereRadius * sphereRadius * sphereRadius * 2 - sum),
+                          real_c(5));
+
+      // update position
+      ps->forEachParticle(false, selector, *accessor, particleIntegration, *accessor);
+      syncCall();
+
+      // map particles into field
+      for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+      {
+         psmSweepCollection.particleMappingSweep(&(*blockIt));
+      }
+   }
+
+   return EXIT_SUCCESS;
+}
+
+} // namespace particle_volume_fraction_check
+
+int main(int argc, char** argv) { particle_volume_fraction_check::main(argc, argv); }
diff --git a/tests/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/SettlingSpherePSM.cpp b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/SettlingSpherePSM.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bbcf1a2392322e9e4f45ffeb316240ba3bc0abe4
--- /dev/null
+++ b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/SettlingSpherePSM.cpp
@@ -0,0 +1,871 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file SettlingSpherePSMGPU.cpp
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//! \brief Modification of momentum_exchange_method/SettlingSphere.cpp
+//
+//======================================================================================================================
+
+#include "blockforest/Initialization.h"
+#include "blockforest/communication/UniformBufferedScheme.h"
+
+#include "core/Environment.h"
+#include "core/debug/TestSubsystem.h"
+#include "core/logging/all.h"
+#include "core/math/all.h"
+#include "core/timing/RemainingTimeLogger.h"
+
+#include "field/AddToStorage.h"
+#include "field/vtk/all.h"
+
+#include "geometry/InitBoundaryHandling.h"
+
+#include "gpu/AddGPUFieldToStorage.h"
+#include "gpu/DeviceSelectMPI.h"
+#include "gpu/communication/UniformGPUScheme.h"
+
+#include "lbm/field/AddToStorage.h"
+#include "lbm/vtk/all.h"
+
+#include "lbm_mesapd_coupling/DataTypesCodegen.h"
+#include "lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMSweepCollection.h"
+#include "lbm_mesapd_coupling/utility/AddForceOnParticlesKernel.h"
+#include "lbm_mesapd_coupling/utility/AddHydrodynamicInteractionKernel.h"
+#include "lbm_mesapd_coupling/utility/AverageHydrodynamicForceTorqueKernel.h"
+#include "lbm_mesapd_coupling/utility/InitializeHydrodynamicForceTorqueForAveragingKernel.h"
+#include "lbm_mesapd_coupling/utility/LubricationCorrectionKernel.h"
+#include "lbm_mesapd_coupling/utility/ParticleSelector.h"
+#include "lbm_mesapd_coupling/utility/ResetHydrodynamicForceTorqueKernel.h"
+
+#include "mesa_pd/collision_detection/AnalyticContactDetection.h"
+#include "mesa_pd/data/DataTypes.h"
+#include "mesa_pd/data/ParticleAccessorWithShape.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/ShapeStorage.h"
+#include "mesa_pd/data/shape/HalfSpace.h"
+#include "mesa_pd/data/shape/Sphere.h"
+#include "mesa_pd/domain/BlockForestDomain.h"
+#include "mesa_pd/kernel/DoubleCast.h"
+#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/ParticleSelector.h"
+#include "mesa_pd/kernel/SpringDashpot.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
+#include "mesa_pd/mpi/ContactFilter.h"
+#include "mesa_pd/mpi/ReduceProperty.h"
+#include "mesa_pd/mpi/SyncNextNeighbors.h"
+#include "mesa_pd/mpi/notifications/ForceTorqueNotification.h"
+#include "mesa_pd/mpi/notifications/HydrodynamicForceTorqueNotification.h"
+#include "mesa_pd/vtk/ParticleVtkOutput.h"
+
+#include "vtk/all.h"
+
+#include <functional>
+
+#include "InitializeDomainForPSM.h"
+#include "PSMPackInfo.h"
+#include "PSMSweep.h"
+#include "PSM_InfoHeader.h"
+#include "PSM_MacroGetter.h"
+#include "PSM_NoSlip.h"
+
+namespace settling_sphere
+{
+
+///////////
+// USING //
+///////////
+
+using namespace walberla;
+using walberla::uint_t;
+using namespace lbm_mesapd_coupling::psm::gpu;
+
+using flag_t      = walberla::uint8_t;
+using FlagField_T = FlagField< flag_t >;
+
+typedef pystencils::PSMPackInfo PackInfo_T;
+
+///////////
+// FLAGS //
+///////////
+
+const FlagUID Fluid_Flag("Fluid");
+const FlagUID NoSlip_Flag("NoSlip");
+
+//*******************************************************************************************************************
+/*!\brief Evaluating the position and velocity of the sphere
+ *
+ */
+//*******************************************************************************************************************
+template< typename ParticleAccessor_T >
+class SpherePropertyLogger
+{
+ public:
+   SpherePropertyLogger(const shared_ptr< ParticleAccessor_T >& ac, walberla::id_t sphereUid,
+                        const std::string& fileName, bool fileIO, real_t dx_SI, real_t dt_SI, real_t diameter,
+                        real_t gravitationalForceMag)
+      : ac_(ac), sphereUid_(sphereUid), fileName_(fileName), fileIO_(fileIO), dx_SI_(dx_SI), dt_SI_(dt_SI),
+        diameter_(diameter), gravitationalForceMag_(gravitationalForceMag), position_(real_t(0)),
+        maxVelocity_(real_t(0))
+   {
+      if (fileIO_)
+      {
+         WALBERLA_ROOT_SECTION()
+         {
+            std::ofstream file;
+            file.open(fileName_.c_str());
+            file << "#\t t\t posX\t posY\t gapZ\t velX\t velY\t velZ\n";
+            file.close();
+         }
+      }
+   }
+
+   void operator()(const uint_t timestep)
+   {
+      Vector3< real_t > pos(real_t(0));
+      Vector3< real_t > transVel(real_t(0));
+      Vector3< real_t > hydForce(real_t(0));
+
+      size_t idx = ac_->uidToIdx(sphereUid_);
+      if (idx != ac_->getInvalidIdx())
+      {
+         if (!mesa_pd::data::particle_flags::isSet(ac_->getFlags(idx), mesa_pd::data::particle_flags::GHOST))
+         {
+            pos      = ac_->getPosition(idx);
+            transVel = ac_->getLinearVelocity(idx);
+            hydForce = ac_->getHydrodynamicForce(idx);
+         }
+      }
+
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace(pos[0], mpi::SUM);
+         mpi::allReduceInplace(pos[1], mpi::SUM);
+         mpi::allReduceInplace(pos[2], mpi::SUM);
+
+         mpi::allReduceInplace(transVel[0], mpi::SUM);
+         mpi::allReduceInplace(transVel[1], mpi::SUM);
+         mpi::allReduceInplace(transVel[2], mpi::SUM);
+
+         mpi::allReduceInplace(hydForce[0], mpi::SUM);
+         mpi::allReduceInplace(hydForce[1], mpi::SUM);
+         mpi::allReduceInplace(hydForce[2], mpi::SUM);
+      }
+
+      position_    = pos[2];
+      maxVelocity_ = std::max(maxVelocity_, -transVel[2]);
+
+      if (fileIO_) writeToFile(timestep, pos, transVel, hydForce);
+   }
+
+   real_t getPosition() const { return position_; }
+
+   real_t getMaxVelocity() const { return maxVelocity_; }
+
+ private:
+   void writeToFile(const uint_t timestep, const Vector3< real_t >& position, const Vector3< real_t >& velocity,
+                    const Vector3< real_t >& hydForce)
+   {
+      WALBERLA_ROOT_SECTION()
+      {
+         std::ofstream file;
+         file.open(fileName_.c_str(), std::ofstream::app);
+
+         auto scaledPosition     = position / diameter_;
+         auto velocity_SI        = velocity * dx_SI_ / dt_SI_;
+         auto normalizedHydForce = hydForce / gravitationalForceMag_;
+
+         file << timestep << "\t" << real_c(timestep) * dt_SI_ << "\t" << "\t" << scaledPosition[0] << "\t"
+              << scaledPosition[1] << "\t" << scaledPosition[2] - real_t(0.5) << "\t" << velocity_SI[0] << "\t"
+              << velocity_SI[1] << "\t" << velocity_SI[2] << "\t" << normalizedHydForce[0] << "\t"
+              << normalizedHydForce[1] << "\t" << normalizedHydForce[2] << "\n";
+         file.close();
+      }
+   }
+
+   shared_ptr< ParticleAccessor_T > ac_;
+   const walberla::id_t sphereUid_;
+   std::string fileName_;
+   bool fileIO_;
+   real_t dx_SI_, dt_SI_, diameter_, gravitationalForceMag_;
+
+   real_t position_;
+   real_t maxVelocity_;
+};
+
+void createPlaneSetup(const shared_ptr< mesa_pd::data::ParticleStorage >& ps,
+                      const shared_ptr< mesa_pd::data::ShapeStorage >& ss, const math::AABB& simulationDomain)
+{
+   // create bounding planes
+   mesa_pd::data::Particle p0 = *ps->create(true);
+   p0.setPosition(simulationDomain.minCorner());
+   p0.setInteractionRadius(std::numeric_limits< real_t >::infinity());
+   p0.setShapeID(ss->create< mesa_pd::data::HalfSpace >(Vector3< real_t >(0, 0, 1)));
+   p0.setOwner(mpi::MPIManager::instance()->rank());
+   p0.setType(0);
+   mesa_pd::data::particle_flags::set(p0.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
+   mesa_pd::data::particle_flags::set(p0.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
+
+   mesa_pd::data::Particle p1 = *ps->create(true);
+   p1.setPosition(simulationDomain.maxCorner());
+   p1.setInteractionRadius(std::numeric_limits< real_t >::infinity());
+   p1.setShapeID(ss->create< mesa_pd::data::HalfSpace >(Vector3< real_t >(0, 0, -1)));
+   p1.setOwner(mpi::MPIManager::instance()->rank());
+   p1.setType(0);
+   mesa_pd::data::particle_flags::set(p1.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
+   mesa_pd::data::particle_flags::set(p1.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
+
+   mesa_pd::data::Particle p2 = *ps->create(true);
+   p2.setPosition(simulationDomain.minCorner());
+   p2.setInteractionRadius(std::numeric_limits< real_t >::infinity());
+   p2.setShapeID(ss->create< mesa_pd::data::HalfSpace >(Vector3< real_t >(1, 0, 0)));
+   p2.setOwner(mpi::MPIManager::instance()->rank());
+   p2.setType(0);
+   mesa_pd::data::particle_flags::set(p2.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
+   mesa_pd::data::particle_flags::set(p2.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
+
+   mesa_pd::data::Particle p3 = *ps->create(true);
+   p3.setPosition(simulationDomain.maxCorner());
+   p3.setInteractionRadius(std::numeric_limits< real_t >::infinity());
+   p3.setShapeID(ss->create< mesa_pd::data::HalfSpace >(Vector3< real_t >(-1, 0, 0)));
+   p3.setOwner(mpi::MPIManager::instance()->rank());
+   p3.setType(0);
+   mesa_pd::data::particle_flags::set(p3.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
+   mesa_pd::data::particle_flags::set(p3.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
+
+   mesa_pd::data::Particle p4 = *ps->create(true);
+   p4.setPosition(simulationDomain.minCorner());
+   p4.setInteractionRadius(std::numeric_limits< real_t >::infinity());
+   p4.setShapeID(ss->create< mesa_pd::data::HalfSpace >(Vector3< real_t >(0, 1, 0)));
+   p4.setOwner(mpi::MPIManager::instance()->rank());
+   p4.setType(0);
+   mesa_pd::data::particle_flags::set(p4.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
+   mesa_pd::data::particle_flags::set(p4.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
+
+   mesa_pd::data::Particle p5 = *ps->create(true);
+   p5.setPosition(simulationDomain.maxCorner());
+   p5.setInteractionRadius(std::numeric_limits< real_t >::infinity());
+   p5.setShapeID(ss->create< mesa_pd::data::HalfSpace >(Vector3< real_t >(0, -1, 0)));
+   p5.setOwner(mpi::MPIManager::instance()->rank());
+   p5.setType(0);
+   mesa_pd::data::particle_flags::set(p5.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
+   mesa_pd::data::particle_flags::set(p5.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
+}
+
+//////////
+// MAIN //
+//////////
+
+//*******************************************************************************************************************
+/*!\brief Testcase that simulates the settling of a sphere inside a rectangular column filled with viscous fluid
+ *
+ * see: ten Cate, Nieuwstad, Derksen, Van den Akker - "Particle imaging velocimetry experiments and lattice-Boltzmann
+ * simulations on a single sphere settling under gravity" (2002), Physics of Fluids, doi: 10.1063/1.1512918
+ */
+//*******************************************************************************************************************
+
+int main(int argc, char** argv)
+{
+   debug::enterTestMode();
+
+   mpi::Environment env(argc, argv);
+
+   logging::Logging::instance()->setLogLevel(logging::Logging::INFO);
+
+   ///////////////////
+   // Customization //
+   ///////////////////
+
+   // simulation control
+   bool shortrun          = false;
+   bool funcTest          = false;
+   bool fileIO            = false;
+   uint_t vtkIOFreq       = 0;
+   std::string baseFolder = "vtk_out_SettlingSphere_CPU_GPU";
+
+   // physical setup
+   uint_t fluidType = 1;
+
+   // numerical parameters
+   uint_t numberOfCellsInHorizontalDirection = uint_t(135);
+   bool averageForceTorqueOverTwoTimeSteps   = true;
+   uint_t numRPDSubCycles                    = uint_t(1);
+   bool useVelocityVerlet                    = false;
+
+   for (int i = 1; i < argc; ++i)
+   {
+      if (std::strcmp(argv[i], "--shortrun") == 0)
+      {
+         shortrun = true;
+         continue;
+      }
+      if (std::strcmp(argv[i], "--funcTest") == 0)
+      {
+         funcTest = true;
+         continue;
+      }
+      if (std::strcmp(argv[i], "--fileIO") == 0)
+      {
+         fileIO = true;
+         continue;
+      }
+      if (std::strcmp(argv[i], "--vtkIOFreq") == 0)
+      {
+         vtkIOFreq = uint_c(std::atof(argv[++i]));
+         continue;
+      }
+      if (std::strcmp(argv[i], "--fluidType") == 0)
+      {
+         fluidType = uint_c(std::atof(argv[++i]));
+         continue;
+      }
+      if (std::strcmp(argv[i], "--numRPDSubCycles") == 0)
+      {
+         numRPDSubCycles = uint_c(std::atof(argv[++i]));
+         continue;
+      }
+      if (std::strcmp(argv[i], "--resolution") == 0)
+      {
+         numberOfCellsInHorizontalDirection = uint_c(std::atof(argv[++i]));
+         continue;
+      }
+      if (std::strcmp(argv[i], "--noForceAveraging") == 0)
+      {
+         averageForceTorqueOverTwoTimeSteps = false;
+         continue;
+      }
+      if (std::strcmp(argv[i], "--baseFolder") == 0)
+      {
+         baseFolder = argv[++i];
+         continue;
+      }
+      if (std::strcmp(argv[i], "--useVV") == 0)
+      {
+         useVelocityVerlet = true;
+         continue;
+      }
+      WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
+   }
+
+   if (funcTest) { walberla::logging::Logging::instance()->setLogLevel(logging::Logging::LogLevel::WARNING); }
+
+   if (fileIO)
+   {
+      // create base directory if it does not yet exist
+      filesystem::path tpath(baseFolder);
+      if (!filesystem::exists(tpath)) filesystem::create_directory(tpath);
+   }
+
+   //////////////////////////////////////
+   // SIMULATION PROPERTIES in SI units//
+   //////////////////////////////////////
+
+   // values are mainly taken from the reference paper
+   const real_t diameter_SI      = real_t(15e-3);
+   const real_t densitySphere_SI = real_t(1120);
+
+   real_t densityFluid_SI, dynamicViscosityFluid_SI;
+   real_t expectedSettlingVelocity_SI;
+   switch (fluidType)
+   {
+   case 1:
+      // Re_p around 1.5
+      densityFluid_SI             = real_t(970);
+      dynamicViscosityFluid_SI    = real_t(373e-3);
+      expectedSettlingVelocity_SI = real_t(0.035986);
+      break;
+   case 2:
+      // Re_p around 4.1
+      densityFluid_SI             = real_t(965);
+      dynamicViscosityFluid_SI    = real_t(212e-3);
+      expectedSettlingVelocity_SI = real_t(0.05718);
+      break;
+   case 3:
+      // Re_p around 11.6
+      densityFluid_SI             = real_t(962);
+      dynamicViscosityFluid_SI    = real_t(113e-3);
+      expectedSettlingVelocity_SI = real_t(0.087269);
+      break;
+   case 4:
+      // Re_p around 31.9
+      densityFluid_SI             = real_t(960);
+      dynamicViscosityFluid_SI    = real_t(58e-3);
+      expectedSettlingVelocity_SI = real_t(0.12224);
+      break;
+   default:
+      WALBERLA_ABORT("Only four different fluids are supported! Choose type between 1 and 4.");
+   }
+   const real_t kinematicViscosityFluid_SI = dynamicViscosityFluid_SI / densityFluid_SI;
+
+   const real_t gravitationalAcceleration_SI = real_t(9.81);
+   Vector3< real_t > domainSize_SI(real_t(100e-3), real_t(100e-3), real_t(160e-3));
+   // shift starting gap a bit upwards to match the reported (plotted) values
+   const real_t startingGapSize_SI = real_t(120e-3) + real_t(0.25) * diameter_SI;
+
+   WALBERLA_LOG_INFO_ON_ROOT("Setup (in SI units):");
+   WALBERLA_LOG_INFO_ON_ROOT(" - fluid type = " << fluidType);
+   WALBERLA_LOG_INFO_ON_ROOT(" - domain size = " << domainSize_SI);
+   WALBERLA_LOG_INFO_ON_ROOT(" - sphere: diameter = " << diameter_SI << ", density = " << densitySphere_SI
+                                                      << ", starting gap size = " << startingGapSize_SI);
+   WALBERLA_LOG_INFO_ON_ROOT(" - fluid: density = " << densityFluid_SI << ", dyn. visc = " << dynamicViscosityFluid_SI
+                                                    << ", kin. visc = " << kinematicViscosityFluid_SI);
+   WALBERLA_LOG_INFO_ON_ROOT(" - expected settling velocity = "
+                             << expectedSettlingVelocity_SI << " --> Re_p = "
+                             << expectedSettlingVelocity_SI * diameter_SI / kinematicViscosityFluid_SI);
+
+   //////////////////////////
+   // NUMERICAL PARAMETERS //
+   //////////////////////////
+
+   const real_t dx_SI = domainSize_SI[0] / real_c(numberOfCellsInHorizontalDirection);
+   const Vector3< uint_t > domainSize(uint_c(floor(domainSize_SI[0] / dx_SI + real_t(0.5))),
+                                      uint_c(floor(domainSize_SI[1] / dx_SI + real_t(0.5))),
+                                      uint_c(floor(domainSize_SI[2] / dx_SI + real_t(0.5))));
+   const real_t diameter     = diameter_SI / dx_SI;
+   const real_t sphereVolume = math::pi / real_t(6) * diameter * diameter * diameter;
+
+   const real_t expectedSettlingVelocity = real_t(0.01);
+   const real_t dt_SI                    = expectedSettlingVelocity / expectedSettlingVelocity_SI * dx_SI;
+
+   const real_t viscosity      = kinematicViscosityFluid_SI * dt_SI / (dx_SI * dx_SI);
+   const real_t relaxationTime = real_t(1) / lbm::collision_model::omegaFromViscosity(viscosity);
+
+   const real_t gravitationalAcceleration = gravitationalAcceleration_SI * dt_SI * dt_SI / dx_SI;
+
+   const real_t densityFluid  = real_t(1);
+   const real_t densitySphere = densityFluid * densitySphere_SI / densityFluid_SI;
+
+   const real_t dx = real_t(1);
+
+   const uint_t timesteps = funcTest ? 1 : (shortrun ? uint_t(200) : uint_t(250000));
+
+   WALBERLA_LOG_INFO_ON_ROOT(" - dx_SI = " << dx_SI << ", dt_SI = " << dt_SI);
+   WALBERLA_LOG_INFO_ON_ROOT("Setup (in simulation, i.e. lattice, units):");
+   WALBERLA_LOG_INFO_ON_ROOT(" - domain size = " << domainSize);
+   WALBERLA_LOG_INFO_ON_ROOT(" - sphere: diameter = " << diameter << ", density = " << densitySphere);
+   WALBERLA_LOG_INFO_ON_ROOT(" - fluid: density = " << densityFluid << ", relaxation time (tau) = " << relaxationTime
+                                                    << ", kin. visc = " << viscosity);
+   WALBERLA_LOG_INFO_ON_ROOT(" - gravitational acceleration = " << gravitationalAcceleration);
+   WALBERLA_LOG_INFO_ON_ROOT(" - expected settling velocity = " << expectedSettlingVelocity << " --> Re_p = "
+                                                                << expectedSettlingVelocity * diameter / viscosity);
+   WALBERLA_LOG_INFO_ON_ROOT(" - integrator = " << (useVelocityVerlet ? "Velocity Verlet" : "Explicit Euler"));
+
+   if (vtkIOFreq > 0)
+   {
+      WALBERLA_LOG_INFO_ON_ROOT(" - writing vtk files to folder \"" << baseFolder << "\" with frequency " << vtkIOFreq);
+   }
+
+   ///////////////////////////
+   // BLOCK STRUCTURE SETUP //
+   ///////////////////////////
+
+   Vector3< uint_t > numberOfBlocksPerDirection(uint_t(1), uint_t(1), uint_t(MPIManager::instance()->numProcesses()));
+   Vector3< uint_t > cellsPerBlockPerDirection(domainSize[0] / numberOfBlocksPerDirection[0],
+                                               domainSize[1] / numberOfBlocksPerDirection[1],
+                                               domainSize[2] / numberOfBlocksPerDirection[2]);
+   WALBERLA_CHECK_EQUAL(
+      numberOfBlocksPerDirection[0] * numberOfBlocksPerDirection[1] * numberOfBlocksPerDirection[2],
+      uint_t(MPIManager::instance()->numProcesses()),
+      "When using GPUs, the number of blocks ("
+         << numberOfBlocksPerDirection[0] * numberOfBlocksPerDirection[1] * numberOfBlocksPerDirection[2]
+         << ") has to match the number of MPI processes (" << uint_t(MPIManager::instance()->numProcesses()) << ")");
+
+   for (uint_t i = 0; i < 3; ++i)
+   {
+      WALBERLA_CHECK_EQUAL(cellsPerBlockPerDirection[i] * numberOfBlocksPerDirection[i], domainSize[i],
+                           "Unmatching domain decomposition in direction " << i << "!");
+   }
+
+   auto blocks = blockforest::createUniformBlockGrid(numberOfBlocksPerDirection[0], numberOfBlocksPerDirection[1],
+                                                     numberOfBlocksPerDirection[2], cellsPerBlockPerDirection[0],
+                                                     cellsPerBlockPerDirection[1], cellsPerBlockPerDirection[2], dx, 0,
+                                                     false, false, false, false, false, // periodicity
+                                                     false);
+
+   WALBERLA_LOG_INFO_ON_ROOT("Domain decomposition:");
+   WALBERLA_LOG_INFO_ON_ROOT(" - blocks per direction = " << numberOfBlocksPerDirection);
+   WALBERLA_LOG_INFO_ON_ROOT(" - cells per block = " << cellsPerBlockPerDirection);
+
+   // write domain decomposition to file
+   if (vtkIOFreq > 0) { vtk::writeDomainDecomposition(blocks, "initial_domain_decomposition", baseFolder); }
+
+   //////////////////
+   // RPD COUPLING //
+   //////////////////
+
+   auto rpdDomain = std::make_shared< mesa_pd::domain::BlockForestDomain >(blocks->getBlockForestPointer());
+
+   // init data structures
+   auto ps                  = walberla::make_shared< mesa_pd::data::ParticleStorage >(1);
+   auto ss                  = walberla::make_shared< mesa_pd::data::ShapeStorage >();
+   using ParticleAccessor_T = mesa_pd::data::ParticleAccessorWithShape;
+   auto accessor            = walberla::make_shared< ParticleAccessor_T >(ps, ss);
+
+   // bounding planes
+   createPlaneSetup(ps, ss, blocks->getDomain());
+
+   // create sphere and store Uid
+   Vector3< real_t > initialPosition(real_t(0.5) * real_c(domainSize[0]), real_t(0.5) * real_c(domainSize[1]),
+                                     startingGapSize_SI / dx_SI + real_t(0.5) * diameter);
+   auto sphereShape = ss->create< mesa_pd::data::Sphere >(diameter * real_t(0.5));
+   ss->shapes[sphereShape]->updateMassAndInertia(densitySphere);
+
+   walberla::id_t sphereUid = 0;
+   if (rpdDomain->isContainedInProcessSubdomain(uint_c(mpi::MPIManager::instance()->rank()), initialPosition))
+   {
+      mesa_pd::data::Particle&& p = *ps->create();
+      p.setPosition(initialPosition);
+      p.setInteractionRadius(diameter * real_t(0.5));
+      p.setOwner(mpi::MPIManager::instance()->rank());
+      p.setShapeID(sphereShape);
+      sphereUid = p.getUid();
+   }
+   mpi::allReduceInplace(sphereUid, mpi::SUM);
+
+   ///////////////////////
+   // ADD DATA TO BLOCKS //
+   ////////////////////////
+
+   // add PDF field
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   BlockDataID pdfFieldID =
+      field::addToStorage< PdfField_T >(blocks, "pdf field (fzyx)", real_c(std::nan("")), field::fzyx);
+   BlockDataID BFieldID         = field::addToStorage< BField_T >(blocks, "B field CPU", 0, field::fzyx, 1);
+   BlockDataID BsFieldID        = field::addToStorage< BsField_T >(blocks, "Bs field CPU", 0, field::fzyx, 1);
+   BlockDataID pdfFieldCPUGPUID = gpu::addGPUFieldToStorage< PdfField_T >(blocks, pdfFieldID, "pdf field GPU");
+#else
+   BlockDataID pdfFieldCPUGPUID =
+      field::addToStorage< PdfField_T >(blocks, "pdf field CPU", real_c(std::nan("")), field::fzyx);
+#endif
+
+   BlockDataID densityFieldID = field::addToStorage< DensityField_T >(blocks, "Density", real_t(0), field::fzyx);
+   BlockDataID velFieldID     = field::addToStorage< VelocityField_T >(blocks, "Velocity", real_t(0), field::fzyx);
+
+   // add flag field
+   BlockDataID flagFieldID = field::addFlagFieldToStorage< FlagField_T >(blocks, "flag field");
+
+   // set up RPD functionality
+   std::function< void(void) > syncCall = [ps, rpdDomain]() {
+      mesa_pd::mpi::SyncNextNeighbors syncNextNeighborFunc;
+      syncNextNeighborFunc(*ps, *rpdDomain);
+   };
+
+   syncCall();
+
+   mesa_pd::kernel::ExplicitEuler explEulerIntegrator(real_t(1) / real_t(numRPDSubCycles));
+   mesa_pd::kernel::VelocityVerletPreForceUpdate vvIntegratorPreForce(real_t(1) / real_t(numRPDSubCycles));
+   mesa_pd::kernel::VelocityVerletPostForceUpdate vvIntegratorPostForce(real_t(1) / real_t(numRPDSubCycles));
+
+   mesa_pd::kernel::SpringDashpot collisionResponse(1);
+   mesa_pd::mpi::ReduceProperty reduceProperty;
+
+   // set up coupling functionality
+   lbm_mesapd_coupling::RegularParticlesSelector sphereSelector;
+   Vector3< real_t > gravitationalForce(real_t(0), real_t(0),
+                                        -(densitySphere - densityFluid) * gravitationalAcceleration * sphereVolume);
+   lbm_mesapd_coupling::AddForceOnParticlesKernel addGravitationalForce(gravitationalForce);
+   lbm_mesapd_coupling::AddHydrodynamicInteractionKernel addHydrodynamicInteraction;
+   lbm_mesapd_coupling::ResetHydrodynamicForceTorqueKernel resetHydrodynamicForceTorque;
+   lbm_mesapd_coupling::AverageHydrodynamicForceTorqueKernel averageHydrodynamicForceTorque;
+   lbm_mesapd_coupling::LubricationCorrectionKernel lubricationCorrectionKernel(
+      viscosity, [](real_t r) { return real_t(0.0016) * r; });
+   lbm::PSM_NoSlip noSlip(blocks, pdfFieldCPUGPUID);
+
+   ///////////////
+   // TIME LOOP //
+   ///////////////
+
+   // map no-slip boundaries into the LBM simulation
+   std::string boundariesBlockString = " Boundaries"
+                                       "{"
+                                       "Border { direction T;    walldistance -1;  flag NoSlip; }"
+                                       "Border { direction B;    walldistance -1;  flag NoSlip; }"
+                                       "Border { direction N;    walldistance -1;  flag NoSlip; }"
+                                       "Border { direction S;    walldistance -1;  flag NoSlip; }"
+                                       "Border { direction W;    walldistance -1;  flag NoSlip; }"
+                                       "Border { direction E;    walldistance -1;  flag NoSlip; }"
+                                       "}";
+
+   WALBERLA_ROOT_SECTION()
+   {
+      std::ofstream boundariesFile("boundaries.prm");
+      boundariesFile << boundariesBlockString;
+      boundariesFile.close();
+   }
+   WALBERLA_MPI_BARRIER()
+
+   auto boundariesCfgFile = Config();
+   boundariesCfgFile.readParameterFile("boundaries.prm");
+   auto boundariesConfig = boundariesCfgFile.getBlock("Boundaries");
+   geometry::initBoundaryHandling< FlagField_T >(*blocks, flagFieldID, boundariesConfig);
+   geometry::setNonBoundaryCellsToDomain< FlagField_T >(*blocks, flagFieldID, Fluid_Flag);
+   noSlip.fillFromFlagField< FlagField_T >(blocks, flagFieldID, NoSlip_Flag, Fluid_Flag);
+
+   // add particle and volume fraction data structures
+   ParticleAndVolumeFractionSoA_T< 1 > particleAndVolumeFractionSoA(
+      blocks, lbm::collision_model::omegaFromViscosity(viscosity));
+   // map particles and calculate solid volume fraction initially
+   PSMSweepCollection psmSweepCollection(blocks, accessor, sphereSelector, particleAndVolumeFractionSoA, Vector3(27));
+   for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+   {
+      psmSweepCollection.particleMappingSweep(&(*blockIt));
+   }
+
+   pystencils::InitializeDomainForPSM pdfSetter(
+      particleAndVolumeFractionSoA.BsFieldID, particleAndVolumeFractionSoA.BFieldID,
+      particleAndVolumeFractionSoA.particleVelocitiesFieldID, pdfFieldCPUGPUID, real_t(0), real_t(0), real_t(0),
+      real_t(1.0), real_t(0), real_t(0), real_t(0));
+
+   for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+   {
+      // pdfSetter requires particle velocities at cell centers
+      psmSweepCollection.setParticleVelocitiesSweep(&(*blockIt));
+      pdfSetter(&(*blockIt));
+   }
+
+   // setup of the LBM communication for synchronizing the pdf field between neighboring blocks
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   gpu::communication::UniformGPUScheme< Stencil_T > com(blocks, 0, false);
+#else
+   walberla::blockforest::communication::UniformBufferedScheme< Stencil_T > com(blocks);
+#endif
+
+   com.addPackInfo(make_shared< PackInfo_T >(pdfFieldCPUGPUID));
+   auto communication = std::function< void() >([&]() { com.communicate(); });
+
+   // create the timeloop
+   SweepTimeloop timeloop(blocks->getBlockStorage(), timesteps);
+
+   pystencils::PSMSweep PSMSweep(particleAndVolumeFractionSoA.BsFieldID, particleAndVolumeFractionSoA.BFieldID,
+                                 particleAndVolumeFractionSoA.particleForcesFieldID,
+                                 particleAndVolumeFractionSoA.particleVelocitiesFieldID, pdfFieldCPUGPUID, real_t(0.0),
+                                 real_t(0.0), real_t(0.0), lbm::collision_model::omegaFromViscosity(viscosity));
+
+   timeloop.addFuncBeforeTimeStep(RemainingTimeLogger(timeloop.getNrOfTimeSteps()), "Remaining Time Logger");
+
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   pystencils::PSM_MacroGetter getterSweep(BFieldID, densityFieldID, pdfFieldID, velFieldID, real_t(0.0), real_t(0.0),
+                                           real_t(0.0));
+#else
+   pystencils::PSM_MacroGetter getterSweep(particleAndVolumeFractionSoA.BFieldID, densityFieldID, pdfFieldCPUGPUID,
+                                           velFieldID, real_t(0.0), real_t(0.0), real_t(0.0));
+#endif
+   // vtk output
+   if (vtkIOFreq != uint_t(0))
+   {
+      // spheres
+      auto particleVtkOutput = make_shared< mesa_pd::vtk::ParticleVtkOutput >(ps);
+      particleVtkOutput->setParticleSelector([sphereShape](const mesa_pd::data::ParticleStorage::iterator& pIt) {
+         return pIt->getShapeID() == sphereShape;
+      });
+      particleVtkOutput->addOutput< mesa_pd::data::SelectParticleOwner >("owner");
+      particleVtkOutput->addOutput< mesa_pd::data::SelectParticleLinearVelocity >("velocity");
+      auto particleVtkWriter =
+         vtk::createVTKOutput_PointData(particleVtkOutput, "Particles", vtkIOFreq, baseFolder, "simulation_step");
+      timeloop.addFuncBeforeTimeStep(vtk::writeFiles(particleVtkWriter), "VTK (sphere data)");
+
+      // flag field (written only once in the first time step, ghost layers are also written)
+      // auto flagFieldVTK = vtk::createVTKOutput_BlockData( blocks, "flag_field", timesteps, FieldGhostLayers, false,
+      // baseFolder ); flagFieldVTK->addCellDataWriter( make_shared< field::VTKWriter< FlagField_T > >( flagFieldID,
+      // "FlagField" ) ); timeloop.addFuncBeforeTimeStep( vtk::writeFiles( flagFieldVTK ), "VTK (flag field data)" );
+
+      // pdf field
+      auto pdfFieldVTK = vtk::createVTKOutput_BlockData(blocks, "fluid_field", vtkIOFreq, 0, false, baseFolder);
+
+      pdfFieldVTK->addBeforeFunction(communication);
+
+      pdfFieldVTK->addBeforeFunction([&]() {
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+         gpu::fieldCpy< PdfField_T, gpu::GPUField< real_t > >(blocks, pdfFieldID, pdfFieldCPUGPUID);
+         gpu::fieldCpy< BField_T, BFieldGPU_T >(blocks, BFieldID, particleAndVolumeFractionSoA.BFieldID);
+         gpu::fieldCpy< BsField_T, BsFieldGPU_T >(blocks, BsFieldID, particleAndVolumeFractionSoA.BsFieldID);
+#endif
+         for (auto& block : *blocks)
+            getterSweep(&block);
+      });
+
+      field::FlagFieldCellFilter< FlagField_T > fluidFilter(flagFieldID);
+      fluidFilter.addFlag(Fluid_Flag);
+      pdfFieldVTK->addCellInclusionFilter(fluidFilter);
+
+      pdfFieldVTK->addCellDataWriter(make_shared< field::VTKWriter< VelocityField_T > >(velFieldID, "Velocity"));
+      pdfFieldVTK->addCellDataWriter(make_shared< field::VTKWriter< DensityField_T > >(densityFieldID, "Density"));
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+      pdfFieldVTK->addCellDataWriter(make_shared< field::VTKWriter< BField_T > >(BFieldID, "Fraction mapping field B"));
+      pdfFieldVTK->addCellDataWriter(
+         make_shared< field::VTKWriter< BsField_T > >(BsFieldID, "Fraction mapping field Bs"));
+#else
+      pdfFieldVTK->addCellDataWriter(make_shared< field::VTKWriter< BField_T > >(particleAndVolumeFractionSoA.BFieldID,
+                                                                                 "Fraction mapping field B"));
+      pdfFieldVTK->addCellDataWriter(make_shared< field::VTKWriter< BsField_T > >(
+         particleAndVolumeFractionSoA.BsFieldID, "Fraction mapping field Bs"));
+#endif
+
+      timeloop.addFuncBeforeTimeStep(vtk::writeFiles(pdfFieldVTK), "VTK (fluid field data)");
+   }
+
+   // add LBM communication function and boundary handling sweep (does the hydro force calculations and the no-slip
+   // treatment)
+   timeloop.add() << BeforeFunction(communication, "LBM Communication")
+                  << Sweep(noSlip.getSweep(), "Boundary Handling");
+
+   // stream + collide LBM step
+   addPSMSweepsToTimeloop(timeloop, psmSweepCollection, PSMSweep);
+
+   // evaluation functionality
+   std::string loggingFileName(baseFolder + "/LoggingSettlingSphereGPU_");
+   loggingFileName += std::to_string(fluidType);
+   loggingFileName += ".txt";
+   if (fileIO) { WALBERLA_LOG_INFO_ON_ROOT(" - writing logging output to file \"" << loggingFileName << "\""); }
+   SpherePropertyLogger< ParticleAccessor_T > logger(accessor, sphereUid, loggingFileName, fileIO, dx_SI, dt_SI,
+                                                     diameter, -gravitationalForce[2]);
+
+   ////////////////////////
+   // EXECUTE SIMULATION //
+   ////////////////////////
+
+   WcTimingPool timeloopTiming;
+
+   real_t terminationPosition = real_t(0.51) * diameter; // right before sphere touches the bottom wall
+
+   const bool useOpenMP = false;
+
+   // time loop
+   for (uint_t i = 0; i < timesteps; ++i)
+   {
+      // perform a single simulation step -> this contains LBM and setting of the hydrodynamic interactions
+      timeloop.singleStep(timeloopTiming);
+
+      timeloopTiming["RPD"].start();
+
+      // -> full hydrodynamic force/torque info is available on local particle
+      reduceProperty.operator()< mesa_pd::HydrodynamicForceTorqueNotification >(*ps);
+
+      if (averageForceTorqueOverTwoTimeSteps)
+      {
+         if (i == 0)
+         {
+            lbm_mesapd_coupling::InitializeHydrodynamicForceTorqueForAveragingKernel
+               initializeHydrodynamicForceTorqueForAveragingKernel;
+            ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor,
+                                initializeHydrodynamicForceTorqueForAveragingKernel, *accessor);
+         }
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, averageHydrodynamicForceTorque,
+                             *accessor);
+      }
+
+      for (auto subCycle = uint_t(0); subCycle < numRPDSubCycles; ++subCycle)
+      {
+         if (useVelocityVerlet)
+         {
+            ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPreForce, *accessor);
+            syncCall();
+         }
+
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addHydrodynamicInteraction,
+                             *accessor);
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addGravitationalForce, *accessor);
+
+         // lubrication correction
+         ps->forEachParticlePairHalf(
+            useOpenMP, mesa_pd::kernel::ExcludeInfiniteInfinite(), *accessor,
+            [&lubricationCorrectionKernel, rpdDomain](const size_t idx1, const size_t idx2, auto& ac) {
+               // TODO change this to storing copy, not reference
+               mesa_pd::collision_detection::AnalyticContactDetection acd;
+               acd.getContactThreshold() = lubricationCorrectionKernel.getNormalCutOffDistance();
+               mesa_pd::kernel::DoubleCast double_cast;
+               mesa_pd::mpi::ContactFilter contact_filter;
+               if (double_cast(idx1, idx2, ac, acd, ac))
+               {
+                  if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), *rpdDomain))
+                  {
+                     double_cast(idx1, idx2, ac, lubricationCorrectionKernel, ac, acd.getContactNormal(),
+                                 acd.getPenetrationDepth());
+                  }
+               }
+            },
+            *accessor);
+
+         // one could add linked cells here
+
+         // collision response
+         ps->forEachParticlePairHalf(
+            useOpenMP, mesa_pd::kernel::ExcludeInfiniteInfinite(), *accessor,
+            [collisionResponse, rpdDomain](const size_t idx1, const size_t idx2, auto& ac) {
+               mesa_pd::collision_detection::AnalyticContactDetection acd;
+               mesa_pd::kernel::DoubleCast double_cast;
+               mesa_pd::mpi::ContactFilter contact_filter;
+               if (double_cast(idx1, idx2, ac, acd, ac))
+               {
+                  if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), *rpdDomain))
+                  {
+                     collisionResponse(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), acd.getContactNormal(),
+                                       acd.getPenetrationDepth());
+                  }
+               }
+            },
+            *accessor);
+
+         reduceProperty.operator()< mesa_pd::ForceTorqueNotification >(*ps);
+
+         if (useVelocityVerlet)
+            ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPostForce, *accessor);
+         else
+            ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, explEulerIntegrator, *accessor);
+
+         syncCall();
+      }
+
+      timeloopTiming["RPD"].end();
+
+      // evaluation
+      timeloopTiming["Logging"].start();
+      logger(i);
+      timeloopTiming["Logging"].end();
+
+      // reset after logging here
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectAll(), *accessor, resetHydrodynamicForceTorque, *accessor);
+
+      // check for termination
+      if (logger.getPosition() < terminationPosition)
+      {
+         WALBERLA_LOG_INFO_ON_ROOT("Sphere reached terminal position " << logger.getPosition() << " after " << i
+                                                                       << " timesteps!");
+         break;
+      }
+   }
+
+   timeloopTiming.logResultOnRoot();
+
+   // check the result
+   if (!funcTest && !shortrun)
+   {
+      real_t relErr = std::fabs(expectedSettlingVelocity - logger.getMaxVelocity()) / expectedSettlingVelocity;
+      WALBERLA_LOG_INFO_ON_ROOT("Expected maximum settling velocity: " << expectedSettlingVelocity);
+      WALBERLA_LOG_INFO_ON_ROOT("Simulated maximum settling velocity: " << logger.getMaxVelocity());
+      WALBERLA_LOG_INFO_ON_ROOT("Relative error: " << relErr);
+
+      // the relative error has to be below 10%
+      WALBERLA_CHECK_LESS(relErr, real_t(0.1));
+   }
+
+   return EXIT_SUCCESS;
+}
+
+} // namespace settling_sphere
+
+int main(int argc, char** argv) { settling_sphere::main(argc, argv); }
diff --git a/tests/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/TorqueSpherePSM.cpp b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/TorqueSpherePSM.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..194b3eb3a7dfbfc8f356aa9108c4b70694c1241a
--- /dev/null
+++ b/tests/lbm_mesapd_coupling/partially_saturated_cells_method/codegen/TorqueSpherePSM.cpp
@@ -0,0 +1,480 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file TorqueSpherePSMGPU.cpp
+//! \ingroup lbm_mesapd_coupling
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//! \brief Modification of pe_coupling/partially_saturated_cells_method/TorqueSpherePSM.cpp
+//
+//======================================================================================================================
+
+#include "blockforest/Initialization.h"
+#include "blockforest/communication/UniformBufferedScheme.h"
+
+#include "core/Environment.h"
+#include "core/SharedFunctor.h"
+#include "core/debug/TestSubsystem.h"
+#include "core/logging/Logging.h"
+#include "core/mpi/MPIManager.h"
+#include "core/mpi/Reduce.h"
+#include "core/timing/RemainingTimeLogger.h"
+
+#include "field/AddToStorage.h"
+
+#include "gpu/AddGPUFieldToStorage.h"
+#include "gpu/DeviceSelectMPI.h"
+#include "gpu/communication/UniformGPUScheme.h"
+
+#include "lbm_mesapd_coupling/DataTypesCodegen.h"
+#include "lbm_mesapd_coupling/partially_saturated_cells_method/codegen/PSMSweepCollection.h"
+#include "lbm_mesapd_coupling/utility/ResetHydrodynamicForceTorqueKernel.h"
+
+#include "mesa_pd/data/ParticleAccessorWithShape.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/ShapeStorage.h"
+#include "mesa_pd/domain/BlockForestDomain.h"
+#include "mesa_pd/mpi/SyncNextNeighbors.h"
+
+#include <iostream>
+// codegen
+#include "InitializeDomainForPSM.h"
+#include "PSMPackInfo.h"
+#include "PSMSweep.h"
+#include "PSM_InfoHeader.h"
+
+namespace torque_sphere_psm
+{
+
+///////////
+// USING //
+///////////
+
+using namespace walberla;
+using walberla::uint_t;
+using namespace lbm_mesapd_coupling::psm::gpu;
+
+using flag_t      = walberla::uint8_t;
+using FlagField_T = FlagField< flag_t >;
+
+typedef pystencils::PSMPackInfo PackInfo_T;
+
+///////////
+// FLAGS //
+///////////
+
+const FlagUID Fluid_Flag("fluid");
+
+////////////////
+// PARAMETERS //
+////////////////
+
+struct Setup
+{
+   uint_t checkFrequency;
+   real_t visc;
+   real_t tau;
+   real_t radius;
+   uint_t length;
+   real_t phi;
+   real_t angularVel;
+   real_t analyticalTorque;
+};
+
+//*******************************************************************************************************************
+/*!\brief Evaluating the torque on a sphere, rotating with a constant angular velocity
+ */
+//*******************************************************************************************************************
+template< typename ParticleAccessor_T >
+class TorqueEval
+{
+ public:
+   TorqueEval(SweepTimeloop* timeloop, Setup* setup, const shared_ptr< StructuredBlockStorage >& blocks,
+              const shared_ptr< ParticleAccessor_T >& ac, bool fileIO)
+      : timeloop_(timeloop), setup_(setup), blocks_(blocks), ac_(ac), fileIO_(fileIO), torqueOld_(0.0), torqueNew_(0.0)
+   {
+      // calculate the (semi)analytical torque value
+      // see also Hofmann et al. - Hydrodynamic interactions in colloidal crystals:(II). Application to dense cubic and
+      // tetragonal arrays (1999), Eqs. 5.1 and 5.5
+      const real_t S = real_c(1.95708);
+      setup_->analyticalTorque =
+         -setup_->visc *
+         (real_c(6) * setup_->phi / (real_c(1) - setup_->phi - S * std::pow(setup_->phi, real_c(10. / 3.)))) *
+         setup_->angularVel * real_c(setup_->length * setup_->length * setup_->length);
+
+      if (fileIO_)
+      {
+         std::ofstream file;
+         filename_ = "TorqueSpherePSMGPU.txt";
+         WALBERLA_ROOT_SECTION()
+         {
+            file.open(filename_.c_str());
+            file << "#\t torqueSim\t torqueAnaly\n";
+            file.close();
+         }
+      }
+   }
+
+   // evaluate the acting torque
+   void operator()()
+   {
+      const uint_t timestep(timeloop_->getCurrentTimeStep() + 1);
+
+      if (timestep % setup_->checkFrequency != 0) return;
+
+      // update torque values
+      torqueOld_ = torqueNew_;
+      torqueNew_ = calculateTorque();
+
+      // write to file if desired
+      WALBERLA_ROOT_SECTION()
+      {
+         if (fileIO_)
+         {
+            std::ofstream file;
+            file.open(filename_.c_str(), std::ofstream::app);
+            file.setf(std::ios::unitbuf);
+            file.precision(15);
+            file << timestep << " " << torqueNew_ << " " << setup_->analyticalTorque << "\n";
+            file.close();
+         }
+      }
+   }
+
+   // obtain the torque acting on the sphere by summing up all the process local parts
+   real_t calculateTorque()
+   {
+      real_t torque = real_c(0);
+      for (auto blockIt = blocks_->begin(); blockIt != blocks_->end(); ++blockIt)
+      {
+         for (size_t idx = 0; idx < ac_->size(); ++idx)
+         {
+            torque += ac_->getHydrodynamicTorque(idx)[1];
+         }
+      }
+
+      WALBERLA_MPI_SECTION() { mpi::allReduceInplace(torque, mpi::SUM); }
+      return torque;
+   }
+
+   // return the relative temporal change in the torque
+   real_t getTorqueDiff() const { return std::fabs((torqueNew_ - torqueOld_) / torqueNew_); }
+
+   // return the torque
+   real_t getTorque() const { return torqueNew_; }
+
+ private:
+   SweepTimeloop* timeloop_;
+
+   Setup* setup_;
+
+   shared_ptr< StructuredBlockStorage > blocks_;
+   shared_ptr< ParticleAccessor_T > ac_;
+
+   bool fileIO_;
+   std::string filename_;
+
+   real_t torqueOld_;
+   real_t torqueNew_;
+};
+
+//////////
+// MAIN //
+//////////
+
+//*******************************************************************************************************************
+/*!\brief Testcase that checks the torque acting on a constantly rotating sphere in the center of a cubic domain
+ *
+ * The torque for this problem (often denoted as Simple Cubic setup) is given by a semi-analytical formula.
+ * The cubic domain is periodic in all directions, making it a physically infinite periodic array of spheres.
+   \verbatim
+         _______________
+        |       <-      |
+        |      ___      |
+        |     /   \     |
+        |    |  x  |    |
+        |     \___/     |
+        |      ->       |
+        |_______________|
+
+   \endverbatim
+ *
+ * The collision model used for the LBM is TRT with a relaxation parameter tau=1.5 and the magic parameter 3/16.
+ * The Stokes approximation of the equilibrium PDFs is used.
+ * The sphere rotates with a angular velocity of 1e-5.
+ * The domain is 32x32x32, and the sphere has a diameter of around 27 cells ( chi * domainlength )
+ * The simulation is run until the relative change in the torque between 100 time steps is less than 1e-5.
+ * The pe is not used since the angular velocity is kept constant and the force is explicitly reset after each time
+ step.
+ *
+ */
+//*******************************************************************************************************************
+
+int main(int argc, char** argv)
+{
+   debug::enterTestMode();
+
+   mpi::Environment env(argc, argv);
+
+   logging::Logging::instance()->setLogLevel(logging::Logging::INFO);
+
+   auto processes = MPIManager::instance()->numProcesses();
+
+   if (processes != 1 && processes != 2 && processes != 4 && processes != 8)
+   {
+      std::cerr << "Number of processes must be equal to either 1, 2, 4, or 8!" << std::endl;
+      return EXIT_FAILURE;
+   }
+
+   ///////////////////
+   // Customization //
+   ///////////////////
+
+   bool shortrun = false;
+   bool funcTest = false;
+   bool fileIO   = false;
+
+   for (int i = 1; i < argc; ++i)
+   {
+      if (std::strcmp(argv[i], "--shortrun") == 0)
+      {
+         shortrun = true;
+         continue;
+      }
+      if (std::strcmp(argv[i], "--funcTest") == 0)
+      {
+         funcTest = true;
+         continue;
+      }
+      if (std::strcmp(argv[i], "--fileIO") == 0)
+      {
+         fileIO = true;
+         continue;
+      }
+      WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
+   }
+
+   ///////////////////////////
+   // SIMULATION PROPERTIES //
+   ///////////////////////////
+
+   Setup setup;
+
+   setup.length         = uint_t(32);   // length of the cubic domain in lattice cells
+   const real_t chi     = real_c(0.85); // porosity parameter: diameter / length
+   setup.tau            = real_c(1.5);  // relaxation time
+   setup.angularVel     = real_c(1e-5); // angular velocity of the sphere
+   setup.checkFrequency = uint_t(100);  // evaluate the torque only every checkFrequency time steps
+   setup.radius         = real_c(0.5) * chi * real_c(setup.length); // sphere radius
+   setup.visc           = (setup.tau - real_c(0.5)) / real_c(3);    // viscosity in lattice units
+   setup.phi            = real_c(4.0 / 3.0) * math::pi * setup.radius * setup.radius * setup.radius /
+               (real_c(setup.length * setup.length * setup.length)); // solid volume fraction
+   const real_t omega            = real_c(1) / setup.tau;            // relaxation rate
+   const real_t dx               = real_c(1);                        // lattice dx
+   const real_t convergenceLimit = real_c(1e-5);                     // tolerance for relative change in torque
+   const uint_t timesteps =
+      funcTest ? 1 : (shortrun ? uint_c(150) : uint_c(5000)); // maximum number of time steps for the whole simulation
+
+   ///////////////////////////
+   // BLOCK STRUCTURE SETUP //
+   ///////////////////////////
+
+   const uint_t XBlocks = (processes >= 2) ? uint_t(2) : uint_t(1);
+   const uint_t YBlocks = (processes >= 4) ? uint_t(2) : uint_t(1);
+   const uint_t ZBlocks = (processes == 8) ? uint_t(2) : uint_t(1);
+   const uint_t XCells  = setup.length / XBlocks;
+   const uint_t YCells  = setup.length / YBlocks;
+   const uint_t ZCells  = setup.length / ZBlocks;
+
+   // create fully periodic domain
+   auto blocks = blockforest::createUniformBlockGrid(XBlocks, YBlocks, ZBlocks, XCells, YCells, ZCells, dx, true, true,
+                                                     true, true);
+
+   ////////
+   // PE //
+   ////////
+
+   auto mesapdDomain        = std::make_shared< mesa_pd::domain::BlockForestDomain >(blocks->getBlockForestPointer());
+   auto ps                  = std::make_shared< mesa_pd::data::ParticleStorage >(1);
+   auto ss                  = std::make_shared< mesa_pd::data::ShapeStorage >();
+   using ParticleAccessor_T = mesa_pd::data::ParticleAccessorWithShape;
+   auto accessor            = walberla::make_shared< ParticleAccessor_T >(ps, ss);
+
+   /////////////////
+   // PE COUPLING //
+   /////////////////
+
+   // connect to pe
+   const real_t overlap = real_c(1.5) * dx;
+
+   if (setup.radius > real_c(setup.length) * real_c(0.5) - overlap)
+   {
+      std::cerr << "Periodic sphere is too large!" << std::endl;
+      return EXIT_FAILURE;
+   }
+
+   // create the sphere in the middle of the domain
+   Vector3< real_t > position(real_c(setup.length) * real_c(0.5));
+   auto sphereShape = ss->create< mesa_pd::data::Sphere >(setup.radius);
+
+   if (mesapdDomain->isContainedInProcessSubdomain(uint_c(walberla::mpi::MPIManager::instance()->rank()), position))
+   {
+      auto sphereParticle = ps->create();
+      sphereParticle->setShapeID(sphereShape);
+      sphereParticle->setType(0);
+      sphereParticle->setPosition(position);
+      sphereParticle->setAngularVelocity(Vector3(real_c(0), setup.angularVel, real_c(0)));
+      sphereParticle->setOwner(walberla::MPIManager::instance()->rank());
+      sphereParticle->setInteractionRadius(setup.radius);
+   }
+
+   // synchronize often enough for large particles
+   std::function< void(void) > syncCall = [&]() {
+      mesa_pd::mpi::SyncNextNeighbors syncNextNeighborFunc;
+      syncNextNeighborFunc(*ps, *mesapdDomain);
+   };
+
+   syncCall();
+
+   ///////////////////////
+   // ADD DATA TO BLOCKS //
+   ////////////////////////
+
+   // add fields ( uInit = <0,0,0>, rhoInit = 1 )
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   BlockDataID pdfFieldID =
+      field::addToStorage< PdfField_T >(blocks, "pdf field (fzyx)", real_c(std::nan("")), field::fzyx);
+   BlockDataID pdfFieldCPUGPUID = gpu::addGPUFieldToStorage< PdfField_T >(blocks, pdfFieldID, "pdf field GPU", true);
+#else
+   BlockDataID pdfFieldCPUGPUID =
+      field::addToStorage< PdfField_T >(blocks, "pdf field CPU", real_c(std::nan("")), field::fzyx);
+#endif
+
+   // add particle and volume fraction data structures
+   ParticleAndVolumeFractionSoA_T< Weighting > particleAndVolumeFractionSoA(blocks, omega);
+   // map particles and calculate solid volume fraction initially
+   PSMSweepCollection psmSweepCollection(blocks, accessor, lbm_mesapd_coupling::RegularParticlesSelector(),
+                                         particleAndVolumeFractionSoA, Vector3(8));
+   for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+   {
+      psmSweepCollection.particleMappingSweep(&(*blockIt));
+   }
+
+   pystencils::InitializeDomainForPSM pdfSetter(
+      particleAndVolumeFractionSoA.BsFieldID, particleAndVolumeFractionSoA.BFieldID,
+      particleAndVolumeFractionSoA.particleVelocitiesFieldID, pdfFieldCPUGPUID, real_t(0), real_t(0), real_t(0),
+      real_t(1.0), real_t(0), real_t(0), real_t(0));
+
+   for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+   {
+      // pdfSetter requires particle velocities at cell centers
+      psmSweepCollection.setParticleVelocitiesSweep(&(*blockIt));
+      pdfSetter(&(*blockIt));
+   }
+
+   ///////////////
+   // TIME LOOP //
+   ///////////////
+
+   // create the timeloop
+   SweepTimeloop timeloop(blocks->getBlockStorage(), timesteps);
+
+   // setup of the LBM communication for synchronizing the pdf field between neighboring blocks
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   gpu::communication::UniformGPUScheme< Stencil_T > com(blocks, 0, false);
+#else
+   walberla::blockforest::communication::UniformBufferedScheme< Stencil_T > com(blocks);
+#endif
+   com.addPackInfo(make_shared< PackInfo_T >(pdfFieldCPUGPUID));
+   auto communication = std::function< void() >([&]() { com.communicate(); });
+
+   using TorqueEval_T                    = TorqueEval< ParticleAccessor_T >;
+   shared_ptr< TorqueEval_T > torqueEval = make_shared< TorqueEval_T >(&timeloop, &setup, blocks, accessor, fileIO);
+
+   pystencils::PSMSweep PSMSweep(particleAndVolumeFractionSoA.BsFieldID, particleAndVolumeFractionSoA.BFieldID,
+                                 particleAndVolumeFractionSoA.particleForcesFieldID,
+                                 particleAndVolumeFractionSoA.particleVelocitiesFieldID, pdfFieldCPUGPUID, real_t(0.0),
+                                 real_t(0.0), real_t(0.0), omega);
+
+   // communication, streaming and force evaluation
+   timeloop.add() << BeforeFunction(communication, "LBM Communication")
+                  << Sweep(deviceSyncWrapper(psmSweepCollection.setParticleVelocitiesSweep),
+                           "setParticleVelocitiesSweep");
+   timeloop.add() << Sweep(deviceSyncWrapper(PSMSweep), "cell-wise LB sweep");
+   timeloop.add() << Sweep(deviceSyncWrapper(psmSweepCollection.reduceParticleForcesSweep), "Reduce particle forces");
+#ifdef WALBERLA_BUILD_WITH_GPU_SUPPORT
+   timeloop.add() << Sweep(gpu::fieldCpyFunctor< PdfField_T, gpu::GPUField< real_t > >(pdfFieldID, pdfFieldCPUGPUID),
+                           "copy pdf from GPU to CPU")
+#else
+   struct emptySweep
+   {
+      void operator()(IBlock*) {}
+   };
+   timeloop.add() << Sweep(emptySweep(), "emptySweep")
+#endif
+                  << AfterFunction(SharedFunctor< TorqueEval_T >(torqueEval), "torque evaluation");
+
+   lbm_mesapd_coupling::ResetHydrodynamicForceTorqueKernel resetHydrodynamicForceTorque;
+
+   timeloop.addFuncAfterTimeStep(RemainingTimeLogger(timeloop.getNrOfTimeSteps()), "Remaining Time Logger");
+
+   ////////////////////////
+   // EXECUTE SIMULATION //
+   ////////////////////////
+
+   WcTimingPool timeloopTiming;
+
+   // time loop
+   for (uint_t i = 0; i < timesteps; ++i)
+   {
+      // perform a single simulation step
+      timeloop.singleStep(timeloopTiming);
+
+      // resetting force
+      ps->forEachParticle(false, mesa_pd::kernel::SelectAll(), *accessor, resetHydrodynamicForceTorque, *accessor);
+
+      // check if the relative change in the torque is below the specified convergence criterion
+      if (i > setup.checkFrequency && torqueEval->getTorqueDiff() < convergenceLimit)
+      {
+         // if simulation has converged, terminate simulation
+         break;
+      }
+   }
+
+   timeloopTiming.logResultOnRoot();
+
+   // check the result
+   if (!funcTest && !shortrun)
+   {
+      real_t relErr = std::fabs((setup.analyticalTorque - torqueEval->getTorque()) / setup.analyticalTorque);
+      if (fileIO)
+      {
+         WALBERLA_ROOT_SECTION()
+         {
+            std::cout << "Analytical torque: " << setup.analyticalTorque << "\n"
+                      << "Simulated torque: " << torqueEval->getTorque() << "\n"
+                      << "Relative error: " << relErr << "\n";
+         }
+      }
+      // the relative error has to be below 10% (25% for SC2)
+      WALBERLA_CHECK_LESS(relErr, (SC == 2) ? real_c(0.25) : real_c(0.1));
+   }
+
+   return 0;
+}
+
+} // namespace torque_sphere_psm
+
+int main(int argc, char** argv) { torque_sphere_psm::main(argc, argv); }
diff --git a/tests/mesa_pd/CMakeLists.txt b/tests/mesa_pd/CMakeLists.txt
index 423dda6ef8a2811cd5225d8e34ed12752283c07a..7b217a13677f9c56c79a81cccd0ff007e24d99dd 100644
--- a/tests/mesa_pd/CMakeLists.txt
+++ b/tests/mesa_pd/CMakeLists.txt
@@ -4,6 +4,8 @@
 #
 ###################################################################################################
 
+waLBerla_link_files_to_builddir(*.prm)
+
 waLBerla_compile_test( NAME   MESA_PD_COLLISIONDETECTION_AnalyticCollisionFunctions FILES collision_detection/AnalyticCollisionFunctions.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_COLLISIONDETECTION_AnalyticCollisionFunctions )
 
@@ -224,6 +226,8 @@ waLBerla_execute_test( NAME   MESA_PD_MPI_VelocityCorrectionNotification PROCESS
 waLBerla_compile_test( NAME   MESA_PD_Sorting FILES Sorting.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_Sorting )
 
+waLBerla_compile_test( NAME   MESA_PD_Stiffness FILES Stiffness.cpp DEPENDS blockforest core mesa_pd )
+
 waLBerla_compile_test( NAME   MESA_PD_VTK_Outputs FILES vtk/VTKOutputs.cpp DEPENDS blockforest core vtk )
 waLBerla_execute_test( NAME   MESA_PD_VTK_Outputs PROCESSES 8 )
 
diff --git a/tests/mesa_pd/Stiffness.cpp b/tests/mesa_pd/Stiffness.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0ebf03ba6d74079043f4b70b0d0e4312b52fc52f
--- /dev/null
+++ b/tests/mesa_pd/Stiffness.cpp
@@ -0,0 +1,142 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file   Stiffness.cpp
+//! \author Samuel Kemmler <samuel.kemmler@fau.de>
+//
+//======================================================================================================================
+
+#include "blockforest/Initialization.h"
+
+#include "core/Environment.h"
+
+#include "mesa_pd/collision_detection/AnalyticContactDetection.h"
+#include "mesa_pd/data/DataTypes.h"
+#include "mesa_pd/data/ParticleAccessorWithBaseShape.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/shape/Sphere.h"
+#include "mesa_pd/domain/BlockForestDomain.h"
+#include "mesa_pd/kernel/DoubleCast.h"
+#include "mesa_pd/kernel/LinearSpringDashpot.h"
+#include "mesa_pd/kernel/ParticleSelector.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
+#include "mesa_pd/mpi/ContactFilter.h"
+
+namespace walberla
+{
+
+using namespace mesa_pd;
+
+int main(int argc, char** argv)
+{
+   Environment env(argc, argv);
+   walberla::mpi::MPIManager::instance()->useWorldComm();
+
+   WALBERLA_CHECK(MPIManager::instance()->numProcesses() == 1)
+
+   // Config
+   auto cfg = env.config();
+   if (cfg == nullptr) WALBERLA_ABORT("No config specified!");
+   WALBERLA_LOG_INFO_ON_ROOT(*cfg);
+   const Config::BlockHandle config = cfg->getBlock("Stiffness");
+
+   const Vec3 domainSize_SI             = config.getParameter< Vec3 >("domainSize_SI");
+   const real_t diameter_SI             = config.getParameter< real_t >("diameter_SI");
+   const real_t densityParticle_SI      = config.getParameter< real_t >("densityParticle_SI");
+   const real_t dt_SI                   = config.getParameter< real_t >("dt_SI");
+   const uint_t timeSteps               = config.getParameter< uint_t >("timeSteps");
+   const real_t force_SI                = config.getParameter< real_t >("force_SI");
+   const real_t normalSpringConstant_SI = config.getParameter< real_t >("normalSpringConstant_SI");
+
+   // BlockForest
+   const math::AABB simulationDomain_SI(real_t(0.0), real_t(0.0), real_t(0.0), domainSize_SI[0], domainSize_SI[1],
+                                        domainSize_SI[2]);
+
+   shared_ptr< BlockForest > forest =
+      blockforest::createBlockForest(simulationDomain_SI, Vec3(uint(1)), Vector3< bool >(false));
+   auto domain = std::make_shared< mesa_pd::domain::BlockForestDomain >(forest);
+
+   // MesaPD data structures
+   auto ps = std::make_shared< data::ParticleStorage >(1);
+   data::ParticleAccessorWithBaseShape accessor(ps);
+
+   // Init sphere 0
+   auto p0                       = ps->create();
+   p0->getPositionRef()          = simulationDomain_SI.center() - Vec3(diameter_SI / 2, real_t(0), real_t(0));
+   p0->getInteractionRadiusRef() = diameter_SI * real_t(0.5);
+   p0->getBaseShapeRef()         = std::make_shared< data::Sphere >(p0->getInteractionRadius());
+   p0->getBaseShapeRef()->updateMassAndInertia(densityParticle_SI);
+   p0->getOwnerRef() = walberla::mpi::MPIManager::instance()->rank();
+   p0->getTypeRef()  = 0;
+   auto idxp0        = p0->getIdx();
+
+   // Init sphere 1
+   auto p1                       = ps->create();
+   p1->getPositionRef()          = simulationDomain_SI.center() + Vec3(diameter_SI / 2, real_t(0), real_t(0));
+   p1->getInteractionRadiusRef() = diameter_SI * real_t(0.5);
+   p1->getBaseShapeRef()         = std::make_shared< data::Sphere >(p1->getInteractionRadius());
+   p1->getBaseShapeRef()->updateMassAndInertia(densityParticle_SI);
+   p1->getOwnerRef() = walberla::mpi::MPIManager::instance()->rank();
+   p1->getTypeRef()  = 0;
+   auto idxp1        = p1->getIdx();
+
+   auto overlap = diameter_SI - (accessor.getPosition(idxp1)[0] - accessor.getPosition(idxp0)[0]);
+
+   // Init kernels
+   mesa_pd::kernel::VelocityVerletPreForceUpdate vvIntegratorPreForce(dt_SI);
+   mesa_pd::kernel::VelocityVerletPostForceUpdate vvIntegratorPostForce(dt_SI);
+   kernel::LinearSpringDashpot dem(1);
+   dem.setStiffnessN(0, 0, normalSpringConstant_SI);
+
+   for (uint_t i = 0; i < timeSteps; ++i)
+   {
+      ps->forEachParticle(false, kernel::SelectLocal(), accessor, vvIntegratorPreForce, accessor);
+
+      p0->setForce(Vec3(force_SI * real_t(i) / real_t(timeSteps), real_t(0), real_t(0)));
+      p1->setForce(Vec3(-force_SI * real_t(i) / real_t(timeSteps), real_t(0), real_t(0)));
+
+      ps->forEachParticlePairHalf(
+         false, kernel::ExcludeInfiniteInfinite(), accessor,
+         [domain, &dem, dt_SI](const size_t idx1, const size_t idx2, auto& ac) {
+            kernel::DoubleCast double_cast;
+            mesa_pd::mpi::ContactFilter contact_filter;
+            collision_detection::AnalyticContactDetection acd;
+
+            if (double_cast(idx1, idx2, ac, acd, ac))
+            {
+               if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), *domain))
+               {
+                  dem(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), acd.getContactNormal(),
+                      acd.getPenetrationDepth(), dt_SI);
+               }
+            }
+         },
+         accessor);
+
+      overlap = diameter_SI - (accessor.getPosition(idxp1)[0] - accessor.getPosition(idxp0)[0]);
+
+      ps->forEachParticle(false, kernel::SelectLocal(), accessor, vvIntegratorPostForce, accessor);
+   }
+
+   WALBERLA_LOG_DEVEL_VAR(overlap)
+   const real_t expectedOverlap = force_SI / normalSpringConstant_SI;
+   WALBERLA_LOG_DEVEL_VAR(expectedOverlap)
+   WALBERLA_CHECK_FLOAT_EQUAL(overlap, expectedOverlap)
+
+   return EXIT_SUCCESS;
+}
+} // namespace walberla
+
+int main(int argc, char** argv) { return walberla::main(argc, argv); }
diff --git a/tests/mesa_pd/Stiffness.prm b/tests/mesa_pd/Stiffness.prm
new file mode 100644
index 0000000000000000000000000000000000000000..e943ae84e8b6395b9f2eb3040131def2d4884368
--- /dev/null
+++ b/tests/mesa_pd/Stiffness.prm
@@ -0,0 +1,9 @@
+Stiffness{
+    domainSize_SI < 1, 1, 1 >;
+    diameter_SI 0.01;
+    densityParticle_SI 2500;
+    dt_SI 5e-5;
+    timeSteps 1000000;
+    force_SI 1;
+    normalSpringConstant_SI 1000;
+}