Implement MathSignal
authorSoeren Apel <soeren@apelpie.net>
Tue, 11 Aug 2020 17:51:15 +0000 (19:51 +0200)
committerSoeren Apel <soeren@apelpie.net>
Sat, 22 Aug 2020 22:16:19 +0000 (00:16 +0200)
19 files changed:
CMakeLists.txt
README
pv/data/decodesignal.cpp
pv/data/mathsignal.cpp
pv/data/mathsignal.hpp
pv/data/signalbase.cpp
pv/data/signalbase.hpp
pv/exprtk.hpp [new file with mode: 0644]
pv/session.cpp
pv/session.hpp
pv/views/trace/analogsignal.cpp
pv/views/trace/mathsignal.cpp [new file with mode: 0644]
pv/views/trace/mathsignal.hpp [new file with mode: 0644]
pv/views/trace/signal.cpp
pv/views/trace/signal.hpp
pv/views/trace/trace.cpp
pv/views/trace/trace.hpp
pv/views/trace/view.cpp
test/CMakeLists.txt

index aead6ee7e8e19446f5c961d8620b6a08d3de075f..aa5175cc6ae1559fa8970922a22abf1a9492f160 100644 (file)
@@ -286,6 +286,7 @@ set(pulseview_SOURCES
        pv/views/trace/cursorpair.cpp
        pv/views/trace/flag.cpp
        pv/views/trace/header.cpp
+       pv/views/trace/mathsignal.cpp
        pv/views/trace/marginwidget.cpp
        pv/views/trace/logicsignal.cpp
        pv/views/trace/ruler.cpp
@@ -321,6 +322,7 @@ set(pulseview_SOURCES
 
 # This list includes only QObject derived class headers.
 set(pulseview_HEADERS
+       pv/exprtk.hpp
        pv/logging.hpp
        pv/globalsettings.hpp
        pv/mainwindow.hpp
@@ -353,6 +355,7 @@ set(pulseview_HEADERS
        pv/views/trace/flag.hpp
        pv/views/trace/header.hpp
        pv/views/trace/logicsignal.hpp
+       pv/views/trace/mathsignal.hpp
        pv/views/trace/marginwidget.hpp
        pv/views/trace/ruler.hpp
        pv/views/trace/signal.hpp
diff --git a/README b/README
index 96f17ab6fa5ad933b0ec4200a9ed0381f512a266..d4056b3cd33c0d03b1045bfe05e5d6f76a480d68 100644 (file)
--- a/README
+++ b/README
@@ -88,12 +88,17 @@ DarkStyle: Juergen Skrotzky
     MIT license
     https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle#licence
 
-QHexView:
+QHexView: Victor Anjin
   https://github.com/virinext/QHexView
   License:
     MIT license
     https://github.com/virinext/QHexView/blob/master/LICENSE
 
+ExprTk: Arash Partow
+  https://www.partow.net/programming/exprtk/index.html
+  License:
+    MIT license
+
 
 Mailing list
 ------------
index 2c2c58e202f724ec58b62de207e8325c7d508972..a17ea693aaa89d67ca3859751529e84b75b42eb2 100644 (file)
@@ -56,7 +56,8 @@ DecodeSignal::DecodeSignal(pv::Session &session) :
        srd_session_(nullptr),
        logic_mux_data_invalid_(false),
        stack_config_changed_(true),
-       current_segment_id_(0)
+       current_segment_id_(0),
+       error_message_("")
 {
        connect(&session_, SIGNAL(capture_state_changed(int)),
                this, SLOT(on_capture_state_changed(int)));
index 18761682dd2a7768e2ea93be6b3c8e8a1809f0bb..25b8110e07a78c2a9e5cbbea5957ed1ee29a0b6b 100644 (file)
 #include <pv/data/analogsegment.hpp>
 #include <pv/data/signalbase.hpp>
 
+using std::dynamic_pointer_cast;
 using std::make_shared;
+using std::min;
+using std::unique_lock;
 
 namespace pv {
 namespace data {
@@ -38,29 +41,275 @@ const int64_t MathSignal::ChunkLength = 256 * 1024;
 
 MathSignal::MathSignal(pv::Session &session) :
        SignalBase(nullptr, SignalBase::MathChannel),
-       session_(session)
+       session_(session),
+       use_custom_sample_rate_(false),
+       use_custom_sample_count_(false),
+       expression_(""),
+       error_message_(""),
+       exprtk_symbol_table_(nullptr),
+       exprtk_expression_(nullptr),
+       exprtk_parser_(nullptr)
 {
-       shared_ptr<data::Analog> data(new data::Analog());
+       set_name(QString(tr("Math%1")).arg(session_.get_next_signal_index(MathChannel)));
+
+       shared_ptr<Analog> data(new data::Analog());
        set_data(data);
 
-       shared_ptr<data::AnalogSegment> segment = make_shared<data::AnalogSegment>(
-               *data, data->get_segment_count(), session.get_samplerate());
+       connect(&session_, SIGNAL(capture_state_changed(int)),
+               this, SLOT(on_capture_state_changed(int)));
+       connect(&session_, SIGNAL(data_received()),
+               this, SLOT(on_data_received()));
 
-       data->push_segment(segment);
+       expression_ = "sin(2 * pi * t) + cos(t / 2 * pi)";
 }
 
 MathSignal::~MathSignal()
 {
+       reset_generation();
 }
 
 void MathSignal::save_settings(QSettings &settings) const
 {
-       (void)settings;
+       settings.setValue("expression", expression_);
+
+       settings.setValue("custom_sample_rate", (qulonglong)custom_sample_rate_);
+       settings.setValue("custom_sample_count", (qulonglong)custom_sample_count_);
+       settings.setValue("use_custom_sample_rate", use_custom_sample_rate_);
+       settings.setValue("use_custom_sample_count", use_custom_sample_count_);
 }
 
 void MathSignal::restore_settings(QSettings &settings)
 {
-       (void)settings;
+       if (settings.contains("expression"))
+               expression_ = settings.value("expression").toString();
+
+       if (settings.contains("custom_sample_rate"))
+               custom_sample_rate_ = settings.value("custom_sample_rate").toULongLong();
+
+       if (settings.contains("custom_sample_count"))
+               custom_sample_count_ = settings.value("custom_sample_count").toULongLong();
+
+       if (settings.contains("use_custom_sample_rate"))
+               use_custom_sample_rate_ = settings.value("use_custom_sample_rate").toBool();
+
+       if (settings.contains("use_custom_sample_count"))
+               use_custom_sample_count_ = settings.value("use_custom_sample_count").toBool();
+}
+
+QString MathSignal::error_message() const
+{
+       return error_message_;
+}
+
+QString MathSignal::get_expression() const
+{
+       return expression_;
+}
+
+void MathSignal::set_expression(QString expression)
+{
+       expression_ = expression;
+
+       begin_generation();
+}
+
+void MathSignal::set_error_message(QString msg)
+{
+       error_message_ = msg;
+       // TODO Emulate noquote()
+       qDebug().nospace() << name() << ": " << msg;
+}
+
+uint64_t MathSignal::get_working_sample_count(uint32_t segment_id) const
+{
+       // The working sample count is the highest sample number for
+       // which all used signals have data available, so go through all
+       // channels and use the lowest overall sample count of the segment
+
+       int64_t result = std::numeric_limits<int64_t>::max();
+
+       if (use_custom_sample_count_)
+               // A custom sample count implies that only one segment will be created
+               result = (segment_id == 0) ? custom_sample_count_ : 0;
+       else {
+               if (input_signals_.size() > 0) {
+                       for (const shared_ptr<SignalBase> &sb : input_signals_) {
+                               shared_ptr<Analog> a = sb->analog_data();
+                               const uint32_t last_segment = (a->analog_segments().size() - 1);
+                               if (segment_id > last_segment)
+                                       continue;
+                               const shared_ptr<AnalogSegment> segment = a->analog_segments()[segment_id];
+                               result = min(result, (int64_t)segment->get_sample_count());
+                       }
+               } else
+                       result = session_.get_segment_sample_count(segment_id);
+       }
+
+       return result;
+}
+
+void MathSignal::reset_generation()
+{
+       if (gen_thread_.joinable()) {
+               gen_interrupt_ = true;
+               gen_input_cond_.notify_one();
+               gen_thread_.join();
+       }
+
+       data_->clear();
+
+       if (exprtk_symbol_table_) {
+               delete exprtk_symbol_table_;
+               exprtk_symbol_table_ = nullptr;
+       }
+
+       if (exprtk_expression_) {
+               delete exprtk_expression_;
+               exprtk_expression_ = nullptr;
+       }
+
+       if (exprtk_parser_) {
+               delete exprtk_parser_;
+               exprtk_parser_ = nullptr;
+       }
+
+       if (!error_message_.isEmpty()) {
+               error_message_ = QString();
+               // TODO Emulate noquote()
+               qDebug().nospace() << name() << ": Error cleared";
+       }
+}
+
+void MathSignal::begin_generation()
+{
+       reset_generation();
+
+       if (expression_.isEmpty()) {
+               set_error_message(tr("No expression defined, nothing to do"));
+               return;
+       }
+
+       exprtk_symbol_table_ = new exprtk::symbol_table<double>();
+       exprtk_symbol_table_->add_variable("t", exprtk_current_time_);
+       exprtk_symbol_table_->add_variable("s", exprtk_current_sample_);
+       exprtk_symbol_table_->add_constants();
+
+       exprtk_expression_ = new exprtk::expression<double>();
+       exprtk_expression_->register_symbol_table(*exprtk_symbol_table_);
+
+       exprtk_parser_ = new exprtk::parser<double>();
+       exprtk_parser_->compile(expression_.toStdString(), *exprtk_expression_);
+
+       gen_interrupt_ = false;
+       gen_thread_ = std::thread(&MathSignal::generation_proc, this);
+}
+
+void MathSignal::generate_samples(uint32_t segment_id, const uint64_t start_sample,
+       const int64_t sample_count)
+{
+       shared_ptr<Analog> analog = dynamic_pointer_cast<Analog>(data_);
+       shared_ptr<AnalogSegment> segment = analog->analog_segments().at(segment_id);
+
+       const double sample_rate = data_->get_samplerate();
+
+       exprtk_current_sample_ = start_sample;
+
+       float *sample_data = new float[sample_count];
+
+       for (int64_t i = 0; i < sample_count; i++) {
+               exprtk_current_sample_ += 1;
+               exprtk_current_time_ = exprtk_current_sample_ / sample_rate;
+               double value = exprtk_expression_->value();
+               sample_data[i] = value;
+       }
+
+       segment->append_interleaved_samples(sample_data, sample_count, 1);
+
+       delete[] sample_data;
+}
+
+void MathSignal::generation_proc()
+{
+       uint32_t segment_id = 0;
+
+       // Don't do anything until we have a valid sample rate
+       do {
+               if (use_custom_sample_rate_)
+                       data_->set_samplerate(custom_sample_rate_);
+               else
+                       data_->set_samplerate(session_.get_samplerate());
+
+               if (data_->get_samplerate() == 1) {
+                       unique_lock<mutex> gen_input_lock(input_mutex_);
+                       gen_input_cond_.wait(gen_input_lock);
+               }
+       } while ((!gen_interrupt_) && (data_->get_samplerate() == 1));
+
+       if (gen_interrupt_)
+               return;
+
+       shared_ptr<Analog> analog = dynamic_pointer_cast<Analog>(data_);
+
+       // Create initial analog segment
+       shared_ptr<AnalogSegment> output_segment =
+               make_shared<AnalogSegment>(*analog.get(), segment_id, analog->get_samplerate());
+       analog->push_segment(output_segment);
+
+       // Create analog samples
+       do {
+               const uint64_t input_sample_count = get_working_sample_count(segment_id);
+               const uint64_t output_sample_count = output_segment->get_sample_count();
+
+               const uint64_t samples_to_process =
+                       (input_sample_count > output_sample_count) ?
+                       (input_sample_count - output_sample_count) : 0;
+
+               // Process the samples if necessary...
+               if (samples_to_process > 0) {
+                       const uint64_t chunk_sample_count = ChunkLength;
+
+                       uint64_t processed_samples = 0;
+                       do {
+                               const uint64_t start_sample = output_sample_count + processed_samples;
+                               const uint64_t sample_count =
+                                       min(samples_to_process - processed_samples,     chunk_sample_count);
+
+                               generate_samples(segment_id, start_sample, sample_count);
+                               processed_samples += sample_count;
+
+                               // Notify consumers of this signal's data
+                               // TODO Does this work when a conversion is active?
+                               samples_added(segment_id, start_sample, start_sample + processed_samples);
+                       } while (!gen_interrupt_ && (processed_samples < samples_to_process));
+               }
+
+               if (samples_to_process == 0) {
+                       if (segment_id < session_.get_highest_segment_id()) {
+                               // Process next segment
+                               segment_id++;
+
+                               output_segment =
+                                       make_shared<AnalogSegment>(*analog.get(), segment_id, analog->get_samplerate());
+                               analog->push_segment(output_segment);
+                       } else {
+                               // All segments have been processed, wait for more input
+                               unique_lock<mutex> gen_input_lock(input_mutex_);
+                               gen_input_cond_.wait(gen_input_lock);
+                       }
+               }
+
+       } while (!gen_interrupt_);
+}
+
+void MathSignal::on_capture_state_changed(int state)
+{
+       if (state == Session::Running)
+               begin_generation();
+}
+
+void MathSignal::on_data_received()
+{
+       gen_input_cond_.notify_one();
 }
 
 } // namespace data
index 04380cdc813f5d734f9dcaf7c356c0de5cb41747..f8e15d16261ba9ecbe74efe4015ba170241d00f4 100644 (file)
 #ifndef PULSEVIEW_PV_DATA_MATHSIGNAL_HPP
 #define PULSEVIEW_PV_DATA_MATHSIGNAL_HPP
 
-#include <QSettings>
 #include <QString>
 
+#include <pv/exprtk.hpp>
+#include <pv/util.hpp>
 #include <pv/data/analog.hpp>
 #include <pv/data/signalbase.hpp>
-#include <pv/util.hpp>
 
+using std::atomic;
+using std::condition_variable;
+using std::mutex;
 using std::shared_ptr;
 
 namespace pv {
@@ -35,11 +38,12 @@ class Session;
 namespace data {
 
 class SignalBase;
-class SignalData;
 
 class MathSignal : public SignalBase
 {
        Q_OBJECT
+       Q_PROPERTY(QString expression READ get_expression WRITE set_expression NOTIFY expression_changed)
+       Q_PROPERTY(QString error_message READ error_message)
 
 private:
        static const int64_t ChunkLength;
@@ -51,6 +55,30 @@ public:
        virtual void save_settings(QSettings &settings) const;
        virtual void restore_settings(QSettings &settings);
 
+       QString error_message() const;
+
+       QString get_expression() const;
+       void set_expression(QString expression);
+
+private:
+       void set_error_message(QString msg);
+
+       /**
+        * Returns the number of samples that can be worked on,
+        * i.e. the number of samples where samples are available
+        * for all connected channels.
+        * If the math signal uses no input channels, this is the
+        * number of samples in the session.
+        */
+       uint64_t get_working_sample_count(uint32_t segment_id) const;
+
+       void reset_generation();
+       void begin_generation();
+
+       void generate_samples(uint32_t segment_id, const uint64_t start_sample,
+               const int64_t sample_count);
+       void generation_proc();
+
 Q_SIGNALS:
        void samples_cleared();
 
@@ -59,19 +87,34 @@ Q_SIGNALS:
 
        void min_max_changed(float min, float max);
 
-//private Q_SLOTS:
-//     void on_data_cleared();
-//     void on_data_received();
-
-//     void on_samples_added(SharedPtrToSegment segment, uint64_t start_sample,
-//             uint64_t end_sample);
+       void expression_changed(QString expression);
 
-//     void on_min_max_changed(float min, float max);
+private Q_SLOTS:
+       void on_capture_state_changed(int state);
+       void on_data_received();
 
 private:
        pv::Session &session_;
 
+       uint64_t custom_sample_rate_;
+       uint64_t custom_sample_count_;
+       bool use_custom_sample_rate_, use_custom_sample_count_;
+       vector< shared_ptr<SignalBase>> input_signals_;
+
+       QString expression_;
+
        QString error_message_;
+
+       mutable mutex input_mutex_;
+       mutable condition_variable gen_input_cond_;
+
+       std::thread gen_thread_;
+       atomic<bool> gen_interrupt_;
+
+       exprtk::symbol_table<double> *exprtk_symbol_table_;
+       exprtk::expression<double> *exprtk_expression_;
+       exprtk::parser<double> *exprtk_parser_;
+       double exprtk_current_time_, exprtk_current_sample_;
 };
 
 } // namespace data
index f49d95ebe8b0206f9f020dc0cba4bc7dd7ae1493..7d420ac7017902e8cffa80aee9be976f1fa75e67 100644 (file)
@@ -118,6 +118,12 @@ shared_ptr<sigrok::Channel> SignalBase::channel() const
        return channel_;
 }
 
+bool SignalBase::is_generated() const
+{
+       // Only signals associated with a device have a corresponding sigrok channel
+       return channel_ == nullptr;
+}
+
 bool SignalBase::enabled() const
 {
        return (channel_) ? channel_->enabled() : true;
@@ -571,7 +577,8 @@ void SignalBase::convert_single_segment_range(AnalogSegment *asegment,
                uint8_t *lsamples = new uint8_t[ConversionBlockSize];
 
                vector<shared_ptr<sigrok::Channel> > channels;
-               channels.push_back(channel_);
+               if (channel_)
+                       channels.push_back(channel_);
 
                vector<const sigrok::QuantityFlag*> mq_flags;
                const sigrok::Quantity * const mq = sigrok::Quantity::VOLTAGE;
index 7d7a795e5f8648bf649fa46345d5a679a930c510..5a35218f3a2501e87d5e27ef8656532f27191e74 100644 (file)
@@ -128,6 +128,11 @@ public:
         */
        shared_ptr<sigrok::Channel> channel() const;
 
+       /**
+        * Returns whether this channel is generated or a channel associated with the device.
+        */
+       bool is_generated() const;
+
        /**
         * Returns enabled status of this channel.
         */
diff --git a/pv/exprtk.hpp b/pv/exprtk.hpp
new file mode 100644 (file)
index 0000000..ca38375
--- /dev/null
@@ -0,0 +1,39050 @@
+/*
+ ******************************************************************
+ *           C++ Mathematical Expression Toolkit Library          *
+ *                                                                *
+ * Author: Arash Partow (1999-2020)                               *
+ * URL: http://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                         *
+ *                                                                *
+ * Example expressions:                                           *
+ * (00) (y + x / y) * (x - y / x)                                 *
+ * (01) (x^2 / sin(2 * pi / y)) - x / 2                           *
+ * (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)        *
+ * (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)     *
+ * (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)]                  *
+ *                                                                *
+ ******************************************************************
+*/
+
+
+#ifndef INCLUDE_EXPRTK_HPP
+#define INCLUDE_EXPRTK_HPP
+
+
+#include <algorithm>
+#include <cctype>
+#include <cmath>
+#include <complex>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <deque>
+#include <exception>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <list>
+#include <map>
+#include <set>
+#include <stack>
+#include <stdexcept>
+#include <string>
+#include <utility>
+#include <vector>
+
+
+namespace exprtk
+{
+   #ifdef exprtk_enable_debugging
+     #define exprtk_debug(params) printf params
+   #else
+     #define exprtk_debug(params) (void)0
+   #endif
+
+   #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")                                \
+
+   #else
+      #define exprtk_disable_fallthrough_begin (void)0;
+      #define exprtk_disable_fallthrough_end   (void)0;
+   #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_t const*          char_cptr;
+      typedef unsigned long long int _uint64_t;
+      typedef long long int           _int64_t;
+
+      inline bool is_whitespace(const char_t c)
+      {
+         return (' '  == c) || ('\n' == c) ||
+                ('\r' == c) || ('\t' == c) ||
+                ('\b' == c) || ('\v' == c) ||
+                ('\f' == c) ;
+      }
+
+      inline bool is_operator_char(const char_t c)
+      {
+         return ('+' == c) || ('-' == c) ||
+                ('*' == c) || ('/' == c) ||
+                ('^' == c) || ('<' == c) ||
+                ('>' == c) || ('=' == c) ||
+                (',' == c) || ('!' == c) ||
+                ('(' == c) || (')' == c) ||
+                ('[' == c) || (']' == c) ||
+                ('{' == c) || ('}' == c) ||
+                ('%' == c) || (':' == c) ||
+                ('?' == c) || ('&' == c) ||
+                ('|' == c) || (';' == c) ;
+      }
+
+      inline bool is_letter(const char_t c)
+      {
+         return (('a' <= c) && (c <= 'z')) ||
+                (('A' <= c) && (c <= 'Z')) ;
+      }
+
+      inline bool is_digit(const char_t c)
+      {
+         return ('0' <= c) && (c <= '9');
+      }
+
+      inline bool is_letter_or_digit(const char_t c)
+      {
+         return is_letter(c) || is_digit(c);
+      }
+
+      inline bool is_left_bracket(const char_t c)
+      {
+         return ('(' == c) || ('[' == c) || ('{' == c);
+      }
+
+      inline bool is_right_bracket(const char_t c)
+      {
+         return (')' == c) || (']' == c) || ('}' == c);
+      }
+
+      inline bool is_bracket(const char_t c)
+      {
+         return is_left_bracket(c) || is_right_bracket(c);
+      }
+
+      inline bool is_sign(const char_t c)
+      {
+         return ('+' == c) || ('-' == c);
+      }
+
+      inline bool is_invalid(const char_t c)
+      {
+         return !is_whitespace   (c) &&
+                !is_operator_char(c) &&
+                !is_letter       (c) &&
+                !is_digit        (c) &&
+                ('.'  != c)          &&
+                ('_'  != c)          &&
+                ('$'  != c)          &&
+                ('~'  != c)          &&
+                ('\'' != c);
+      }
+
+      #ifndef exprtk_disable_caseinsensitivity
+      inline void case_normalise(std::string& s)
+      {
+         for (std::size_t i = 0; i < s.size(); ++i)
+         {
+            s[i] = static_cast<std::string::value_type>(std::tolower(s[i]));
+         }
+      }
+
+      inline bool imatch(const char_t c1, const char_t c2)
+      {
+         return std::tolower(c1) == std::tolower(c2);
+      }
+
+      inline bool imatch(const std::string& s1, const std::string& s2)
+      {
+         if (s1.size() == s2.size())
+         {
+            for (std::size_t i = 0; i < s1.size(); ++i)
+            {
+               if (std::tolower(s1[i]) != std::tolower(s2[i]))
+               {
+                  return false;
+               }
+            }
+
+            return true;
+         }
+
+         return false;
+      }
+
+      struct ilesscompare
+      {
+         inline bool operator() (const std::string& s1, const std::string& s2) const
+         {
+            const std::size_t length = std::min(s1.size(),s2.size());
+
+            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]));
+
+               if (c1 > c2)
+                  return false;
+               else if (c1 < c2)
+                  return true;
+            }
+
+            return s1.size() < s2.size();
+         }
+      };
+
+      #else
+      inline void case_normalise(std::string&)
+      {}
+
+      inline bool imatch(const char_t c1, const char_t c2)
+      {
+         return c1 == c2;
+      }
+
+      inline bool imatch(const std::string& s1, const std::string& s2)
+      {
+         return s1 == s2;
+      }
+
+      struct ilesscompare
+      {
+         inline bool operator() (const std::string& s1, const std::string& s2) const
+         {
+            return s1 < s2;
+         }
+      };
+      #endif
+
+      inline bool is_valid_sf_symbol(const std::string& symbol)
+      {
+         // Special function: $f12 or $F34
+         return (4 == symbol.size())  &&
+                ('$' == symbol[0])    &&
+                imatch('f',symbol[1]) &&
+                is_digit(symbol[2])   &&
+                is_digit(symbol[3]);
+      }
+
+      inline const char_t& front(const std::string& s)
+      {
+         return s[0];
+      }
+
+      inline const char_t& back(const std::string& s)
+      {
+         return s[s.size() - 1];
+      }
+
+      inline std::string to_str(int i)
+      {
+         if (0 == i)
+            return std::string("0");
+
+         std::string result;
+
+         if (i < 0)
+         {
+            for ( ; i; i /= 10)
+            {
+               result += '0' + char(-(i % 10));
+            }
+
+            result += '-';
+         }
+         else
+         {
+            for ( ; i; i /= 10)
+            {
+               result += '0' + char(i % 10);
+            }
+         }
+
+         std::reverse(result.begin(), result.end());
+
+         return result;
+      }
+
+      inline std::string to_str(std::size_t i)
+      {
+         return to_str(static_cast<int>(i));
+      }
+
+      inline bool is_hex_digit(const std::string::value_type digit)
+      {
+         return (('0' <= digit) && (digit <= '9')) ||
+                (('A' <= digit) && (digit <= 'F')) ||
+                (('a' <= digit) && (digit <= 'f')) ;
+      }
+
+      inline uchar_t hex_to_bin(uchar_t h)
+      {
+         if (('0' <= h) && (h <= '9'))
+            return (h - '0');
+         else
+            return static_cast<unsigned char>(std::toupper(h) - 'A');
+      }
+
+      template <typename Iterator>
+      inline void parse_hex(Iterator& itr, Iterator end, std::string::value_type& result)
+      {
+         if (
+              (end !=  (itr    )) &&
+              (end !=  (itr + 1)) &&
+              (end !=  (itr + 2)) &&
+              (end !=  (itr + 3)) &&
+              ('0' == *(itr    )) &&
+              (
+                ('x' == *(itr + 1)) ||
+                ('X' == *(itr + 1))
+              ) &&
+              (is_hex_digit(*(itr + 2))) &&
+              (is_hex_digit(*(itr + 3)))
+            )
+         {
+            result = hex_to_bin(static_cast<uchar_t>(*(itr + 2))) << 4 |
+                     hex_to_bin(static_cast<uchar_t>(*(itr + 3))) ;
+            itr += 3;
+         }
+         else
+            result = '\0';
+      }
+
+      inline void cleanup_escapes(std::string& s)
+      {
+         typedef std::string::iterator str_itr_t;
+
+         str_itr_t itr1 = s.begin();
+         str_itr_t itr2 = s.begin();
+         str_itr_t end  = s.end  ();
+
+         std::size_t removal_count  = 0;
+
+         while (end != itr1)
+         {
+            if ('\\' == (*itr1))
+            {
+               ++removal_count;
+
+               if (end == ++itr1)
+                  break;
+               else if ('\\' != (*itr1))
+               {
+                  switch (*itr1)
+                  {
+                     case 'n' : (*itr1) = '\n'; break;
+                     case 'r' : (*itr1) = '\r'; break;
+                     case 't' : (*itr1) = '\t'; break;
+                     case '0' : parse_hex(itr1, end, (*itr1));
+                                removal_count += 3;
+                                break;
+                  }
+
+                  continue;
+               }
+            }
+
+            if (itr1 != itr2)
+            {
+               (*itr2) = (*itr1);
+            }
+
+            ++itr1;
+            ++itr2;
+         }
+
+         s.resize(s.size() - removal_count);
+      }
+
+      class build_string
+      {
+      public:
+
+         build_string(const std::size_t& initial_size = 64)
+         {
+            data_.reserve(initial_size);
+         }
+
+         inline build_string& operator << (const std::string& s)
+         {
+            data_ += s;
+            return (*this);
+         }
+
+         inline build_string& operator << (char_cptr s)
+         {
+            data_ += std::string(s);
+            return (*this);
+         }
+
+         inline operator std::string () const
+         {
+            return data_;
+         }
+
+         inline std::string as_string() const
+         {
+            return data_;
+         }
+
+      private:
+
+         std::string data_;
+      };
+
+      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", "&", "|"
+                                  };
+
+      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", "&", "|"
+                                  };
+
+      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"
+                                  };
+
+      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", "&", "|"
+                                  };
+
+      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"
+                                  };
+
+      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);
+
+      inline bool is_reserved_word(const std::string& symbol)
+      {
+         for (std::size_t i = 0; i < reserved_words_size; ++i)
+         {
+            if (imatch(symbol, reserved_words[i]))
+            {
+               return true;
+            }
+         }
+
+         return false;
+      }
+
+      inline bool is_reserved_symbol(const std::string& symbol)
+      {
+         for (std::size_t i = 0; i < reserved_symbols_size; ++i)
+         {
+            if (imatch(symbol, reserved_symbols[i]))
+            {
+               return true;
+            }
+         }
+
+         return false;
+      }
+
+      inline bool is_base_function(const std::string& function_name)
+      {
+         for (std::size_t i = 0; i < base_function_list_size; ++i)
+         {
+            if (imatch(function_name, base_function_list[i]))
+            {
+               return true;
+            }
+         }
+
+         return false;
+      }
+
+      inline bool is_control_struct(const std::string& cntrl_strct)
+      {
+         for (std::size_t i = 0; i < cntrl_struct_list_size; ++i)
+         {
+            if (imatch(cntrl_strct, cntrl_struct_list[i]))
+            {
+               return true;
+            }
+         }
+
+         return false;
+      }
+
+      inline bool is_logic_opr(const std::string& lgc_opr)
+      {
+         for (std::size_t i = 0; i < logic_ops_list_size; ++i)
+         {
+            if (imatch(lgc_opr, logic_ops_list[i]))
+            {
+               return true;
+            }
+         }
+
+         return false;
+      }
+
+      struct cs_match
+      {
+         static inline bool cmp(const char_t c0, const char_t c1)
+         {
+            return (c0 == c1);
+         }
+      };
+
+      struct cis_match
+      {
+         static inline bool cmp(const char_t c0, const char_t c1)
+         {
+            return (std::tolower(c0) == std::tolower(c1));
+         }
+      };
+
+      template <typename Iterator, typename Compare>
+      inline bool match_impl(const Iterator pattern_begin,
+                             const Iterator pattern_end  ,
+                             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 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;
+
+         while (d_itr != data_end)
+         {
+            if (zero_or_more == *p_itr)
+            {
+               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);
+
+               while ((data_end != d_itr) && !Compare::cmp(c,*d_itr))
+               {
+                  ++d_itr;
+               }
+
+               tb_p_itr = p_itr;
+               tb_d_itr = d_itr;
+
+               continue;
+            }
+            else if (!Compare::cmp(*p_itr, *d_itr) && (zero_or_one != *p_itr))
+            {
+               if (null_itr == tb_d_itr)
+                  return false;
+
+               d_itr = tb_d_itr++;
+               p_itr = tb_p_itr;
+
+               continue;
+            }
+
+            ++p_itr;
+            ++d_itr;
+         }
+
+         while ((pattern_end != p_itr) && ((zero_or_more == *p_itr) || (zero_or_one == *p_itr)))
+         {
+            ++p_itr;
+         }
+
+         return (pattern_end == p_itr);
+      }
+
+      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(),
+                                               '*',
+                                               '?');
+      }
+
+      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(),
+                                                '*',
+                                                '?');
+      }
+
+      inline bool sequence_match(const std::string& pattern,
+                                 const std::string& str,
+                                 std::size_t&       diff_index,
+                                 char_t&            diff_value)
+      {
+         if (str.empty())
+         {
+            return ("Z" == pattern);
+         }
+         else if ('*' == pattern[0])
+            return false;
+
+         typedef std::string::const_iterator itr_t;
+
+         itr_t p_itr = pattern.begin();
+         itr_t s_itr = str    .begin();
+
+         itr_t p_end = pattern.end();
+         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)));
+
+               if ('*' == target)
+               {
+                  diff_index = static_cast<std::size_t>(std::distance(str.begin(),s_itr));
+                  diff_value = static_cast<char>(std::toupper(*p_itr));
+
+                  return false;
+               }
+               else
+                  ++p_itr;
+
+               while (s_itr != s_end)
+               {
+                  if (target != std::toupper(*s_itr))
+                     break;
+                  else
+                     ++s_itr;
+               }
+
+               continue;
+            }
+            else if (
+                      ('?' != *p_itr) &&
+                      std::toupper(*p_itr) != std::toupper(*s_itr)
+                    )
+            {
+               diff_index = static_cast<std::size_t>(std::distance(str.begin(),s_itr));
+               diff_value = static_cast<char>(std::toupper(*p_itr));
+
+               return false;
+            }
+
+            ++p_itr;
+            ++s_itr;
+         }
+
+         return (
+                  (s_end == s_itr) &&
+                  (
+                    (p_end ==  p_itr) ||
+                    ('*'   == *p_itr)
+                  )
+                );
+      }
+
+      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);
+
+      namespace numeric
+      {
+         namespace constant
+         {
+            static const double e       =  2.71828182845904523536028747135266249775724709369996;
+            static const double pi      =  3.14159265358979323846264338327950288419716939937510;
+            static const double pi_2    =  1.57079632679489661923132169163975144209858469968755;
+            static const double pi_4    =  0.78539816339744830961566084581987572104929234984378;
+            static const double pi_180  =  0.01745329251994329576923690768488612713442871888542;
+            static const double _1_pi   =  0.31830988618379067153776752674502872406891929148091;
+            static const double _2_pi   =  0.63661977236758134307553505349005744813783858296183;
+            static const double _180_pi = 57.29577951308232087679815481410517033240547246656443;
+            static const double log2    =  0.69314718055994530941723212145817656807550013436026;
+            static const double sqrt2   =  1.41421356237309504880168872420969807856967187537695;
+         }
+
+         namespace details
+         {
+            struct unknown_type_tag { unknown_type_tag() {} };
+            struct real_type_tag    { real_type_tag   () {} };
+            struct complex_type_tag { complex_type_tag() {} };
+            struct int_type_tag     { int_type_tag    () {} };
+
+            template <typename T>
+            struct number_type
+            {
+               typedef unknown_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_complex_type_tag(T)          \
+            template<> struct number_type<std::complex<T> >      \
+            { typedef complex_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(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           )
+            exprtk_register_int_type_tag(_int64_t      )
+            exprtk_register_int_type_tag(unsigned short)
+            exprtk_register_int_type_tag(unsigned int  )
+            exprtk_register_int_type_tag(_uint64_t     )
+
+            #undef exprtk_register_real_type_tag
+            #undef exprtk_register_int_type_tag
+
+            template <typename T>
+            struct epsilon_type
+            {
+               static inline T value()
+               {
+                  const T epsilon = T(0.0000000001);
+                  return epsilon;
+               }
+            };
+
+            template <>
+            struct epsilon_type <float>
+            {
+               static inline float value()
+               {
+                  const float epsilon = float(0.000001f);
+                  return epsilon;
+               }
+            };
+
+            template <>
+            struct epsilon_type <long double>
+            {
+               static inline long double value()
+               {
+                  const long double epsilon = (long double)(0.000000000001);
+                  return epsilon;
+               }
+            };
+
+            template <typename T>
+            inline bool is_nan_impl(const T v, real_type_tag)
+            {
+               return std::not_equal_to<T>()(v,v);
+            }
+
+            template <typename T>
+            inline int to_int32_impl(const T v, real_type_tag)
+            {
+               return static_cast<int>(v);
+            }
+
+            template <typename T>
+            inline _int64_t to_int64_impl(const T v, real_type_tag)
+            {
+               return static_cast<_int64_t>(v);
+            }
+
+            template <typename T>
+            inline bool is_true_impl(const T v)
+            {
+               return std::not_equal_to<T>()(T(0),v);
+            }
+
+            template <typename T>
+            inline bool is_false_impl(const T v)
+            {
+               return std::equal_to<T>()(T(0),v);
+            }
+
+            template <typename T>
+            inline T abs_impl(const T v, real_type_tag)
+            {
+               return ((v < T(0)) ? -v : v);
+            }
+
+            template <typename T>
+            inline T min_impl(const T v0, const T v1, real_type_tag)
+            {
+               return std::min<T>(v0,v1);
+            }
+
+            template <typename T>
+            inline T max_impl(const T v0, const T v1, real_type_tag)
+            {
+               return std::max<T>(v0,v1);
+            }
+
+            template <typename T>
+            inline T equal_impl(const T v0, const T v1, real_type_tag)
+            {
+               const T epsilon = epsilon_type<T>::value();
+               return (abs_impl(v0 - v1,real_type_tag()) <= (std::max(T(1),std::max(abs_impl(v0,real_type_tag()),abs_impl(v1,real_type_tag()))) * epsilon)) ? T(1) : T(0);
+            }
+
+            inline float equal_impl(const float v0, const float v1, real_type_tag)
+            {
+               const float epsilon = epsilon_type<float>::value();
+               return (abs_impl(v0 - v1,real_type_tag()) <= (std::max(1.0f,std::max(abs_impl(v0,real_type_tag()),abs_impl(v1,real_type_tag()))) * epsilon)) ? 1.0f : 0.0f;
+            }
+
+            template <typename T>
+            inline T equal_impl(const T v0, const T v1, int_type_tag)
+            {
+               return (v0 == v1) ? 1 : 0;
+            }
+
+            template <typename T>
+            inline T expm1_impl(const T v, real_type_tag)
+            {
+               // return std::expm1<T>(v);
+               if (abs_impl(v,real_type_tag()) < T(0.00001))
+                  return v + (T(0.5) * v * v);
+               else
+                  return std::exp(v) - T(1);
+            }
+
+            template <typename T>
+            inline T expm1_impl(const T v, int_type_tag)
+            {
+               return T(std::exp<double>(v)) - T(1);
+            }
+
+            template <typename T>
+            inline T nequal_impl(const T v0, const T v1, real_type_tag)
+            {
+               typedef real_type_tag rtg;
+               const T epsilon = epsilon_type<T>::value();
+               return (abs_impl(v0 - v1,rtg()) > (std::max(T(1),std::max(abs_impl(v0,rtg()),abs_impl(v1,rtg()))) * epsilon)) ? T(1) : T(0);
+            }
+
+            inline float nequal_impl(const float v0, const float v1, real_type_tag)
+            {
+               typedef real_type_tag rtg;
+               const float epsilon = epsilon_type<float>::value();
+               return (abs_impl(v0 - v1,rtg()) > (std::max(1.0f,std::max(abs_impl(v0,rtg()),abs_impl(v1,rtg()))) * epsilon)) ? 1.0f : 0.0f;
+            }
+
+            template <typename T>
+            inline T nequal_impl(const T v0, const T v1, int_type_tag)
+            {
+               return (v0 != v1) ? 1 : 0;
+            }
+
+            template <typename T>
+            inline T modulus_impl(const T v0, const T v1, real_type_tag)
+            {
+               return std::fmod(v0,v1);
+            }
+
+            template <typename T>
+            inline T modulus_impl(const T v0, const T v1, int_type_tag)
+            {
+               return v0 % v1;
+            }
+
+            template <typename T>
+            inline T pow_impl(const T v0, const T v1, real_type_tag)
+            {
+               return std::pow(v0,v1);
+            }
+
+            template <typename T>
+            inline T pow_impl(const T v0, const T v1, int_type_tag)
+            {
+               return std::pow(static_cast<double>(v0),static_cast<double>(v1));
+            }
+
+            template <typename T>
+            inline T logn_impl(const T v0, const T v1, real_type_tag)
+            {
+               return std::log(v0) / std::log(v1);
+            }
+
+            template <typename T>
+            inline T logn_impl(const T v0, const T v1, int_type_tag)
+            {
+               return static_cast<T>(logn_impl<double>(static_cast<double>(v0),static_cast<double>(v1),real_type_tag()));
+            }
+
+            template <typename T>
+            inline T log1p_impl(const T v, real_type_tag)
+            {
+               if (v > T(-1))
+               {
+                  if (abs_impl(v,real_type_tag()) > T(0.0001))
+                  {
+                     return std::log(T(1) + v);
+                  }
+                  else
+                     return (T(-0.5) * v + T(1)) * v;
+               }
+               else
+                  return std::numeric_limits<T>::quiet_NaN();
+            }
+
+            template <typename T>
+            inline T log1p_impl(const T v, int_type_tag)
+            {
+               if (v > T(-1))
+               {
+                  return std::log(T(1) + v);
+               }
+               else
+                  return std::numeric_limits<T>::quiet_NaN();
+            }
+
+            template <typename T>
+            inline T root_impl(const T v0, const T v1, real_type_tag)
+            {
+               if (v1 < T(0))
+                  return std::numeric_limits<T>::quiet_NaN();
+
+               const std::size_t n = static_cast<std::size_t>(v1);
+
+               if ((v0 < T(0)) && (0 == (n % 2)))
+                  return std::numeric_limits<T>::quiet_NaN();
+
+               return std::pow(v0, T(1) / n);
+            }
+
+            template <typename T>
+            inline T root_impl(const T v0, const T v1, int_type_tag)
+            {
+               return root_impl<double>(static_cast<double>(v0),static_cast<double>(v1),real_type_tag());
+            }
+
+            template <typename T>
+            inline T round_impl(const T v, real_type_tag)
+            {
+               return ((v < T(0)) ? std::ceil(v - T(0.5)) : std::floor(v + T(0.5)));
+            }
+
+            template <typename T>
+            inline T roundn_impl(const T v0, const T v1, real_type_tag)
+            {
+               const int index = std::max<int>(0, std::min<int>(pow10_size - 1, (int)std::floor(v1)));
+               const T p10 = T(pow10[index]);
+
+               if (v0 < T(0))
+                  return T(std::ceil ((v0 * p10) - T(0.5)) / p10);
+               else
+                  return T(std::floor((v0 * p10) + T(0.5)) / p10);
+            }
+
+            template <typename T>
+            inline T roundn_impl(const T v0, const T, int_type_tag)
+            {
+               return v0;
+            }
+
+            template <typename T>
+            inline T hypot_impl(const T v0, const T v1, real_type_tag)
+            {
+               return std::sqrt((v0 * v0) + (v1 * v1));
+            }
+
+            template <typename T>
+            inline T hypot_impl(const T v0, const T v1, int_type_tag)
+            {
+               return static_cast<T>(std::sqrt(static_cast<double>((v0 * v0) + (v1 * v1))));
+            }
+
+            template <typename T>
+            inline T atan2_impl(const T v0, const T v1, real_type_tag)
+            {
+               return std::atan2(v0,v1);
+            }
+
+            template <typename T>
+            inline T atan2_impl(const T, const T, int_type_tag)
+            {
+               return 0;
+            }
+
+            template <typename T>
+            inline T shr_impl(const T v0, const T v1, real_type_tag)
+            {
+               return v0 * (T(1) / std::pow(T(2),static_cast<T>(static_cast<int>(v1))));
+            }
+
+            template <typename T>
+            inline T shr_impl(const T v0, const T v1, int_type_tag)
+            {
+               return v0 >> v1;
+            }
+
+            template <typename T>
+            inline T shl_impl(const T v0, const T v1, real_type_tag)
+            {
+               return v0 * std::pow(T(2),static_cast<T>(static_cast<int>(v1)));
+            }
+
+            template <typename T>
+            inline T shl_impl(const T v0, const T v1, int_type_tag)
+            {
+               return v0 << v1;
+            }
+
+            template <typename T>
+            inline T sgn_impl(const T v, real_type_tag)
+            {
+                    if (v > T(0)) return T(+1);
+               else if (v < T(0)) return T(-1);
+               else               return T( 0);
+            }
+
+            template <typename T>
+            inline T sgn_impl(const T v, int_type_tag)
+            {
+                    if (v > T(0)) return T(+1);
+               else if (v < T(0)) return T(-1);
+               else               return T( 0);
+            }
+
+            template <typename T>
+            inline T and_impl(const T v0, const T v1, real_type_tag)
+            {
+               return (is_true_impl(v0) && is_true_impl(v1)) ? T(1) : T(0);
+            }
+
+            template <typename T>
+            inline T and_impl(const T v0, const T v1, int_type_tag)
+            {
+               return v0 && v1;
+            }
+
+            template <typename T>
+            inline T nand_impl(const T v0, const T v1, real_type_tag)
+            {
+               return (is_false_impl(v0) || is_false_impl(v1)) ? T(1) : T(0);
+            }
+
+            template <typename T>
+            inline T nand_impl(const T v0, const T v1, int_type_tag)
+            {
+               return !(v0 && v1);
+            }
+
+            template <typename T>
+            inline T or_impl(const T v0, const T v1, real_type_tag)
+            {
+               return (is_true_impl(v0) || is_true_impl(v1)) ? T(1) : T(0);
+            }
+
+            template <typename T>
+            inline T or_impl(const T v0, const T v1, int_type_tag)
+            {
+               return (v0 || v1);
+            }
+
+            template <typename T>
+            inline T nor_impl(const T v0, const T v1, real_type_tag)
+            {
+               return (is_false_impl(v0) && is_false_impl(v1)) ? T(1) : T(0);
+            }
+
+            template <typename T>
+            inline T nor_impl(const T v0, const T v1, int_type_tag)
+            {
+               return !(v0 || v1);
+            }
+
+            template <typename T>
+            inline T xor_impl(const T v0, const T v1, real_type_tag)
+            {
+               return (is_false_impl(v0) != is_false_impl(v1)) ? T(1) : T(0);
+            }
+
+            template <typename T>
+            inline T xor_impl(const T v0, const T v1, int_type_tag)
+            {
+               return v0 ^ v1;
+            }
+
+            template <typename T>
+            inline T xnor_impl(const T v0, const T v1, real_type_tag)
+            {
+               const bool v0_true = is_true_impl(v0);
+               const bool v1_true = is_true_impl(v1);
+
+               if ((v0_true &&  v1_true) || (!v0_true && !v1_true))
+                  return T(1);
+               else
+                  return T(0);
+            }
+
+            template <typename T>
+            inline T xnor_impl(const T v0, const T v1, int_type_tag)
+            {
+               const bool v0_true = is_true_impl(v0);
+               const bool v1_true = is_true_impl(v1);
+
+               if ((v0_true &&  v1_true) || (!v0_true && !v1_true))
+                  return T(1);
+               else
+                  return T(0);
+            }
+
+            #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); } \
+
+            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)
+            {
+               #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)
+                                    };
+
+               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]))))))))));
+
+               return (v >= T(0)) ? result : -result;
+               #else
+               return erf_impl(v);
+               #endif
+            }
+
+            template <typename T>
+            inline T erf_impl(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); } \
+
+            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)
+            {
+               #if defined(_MSC_VER) && (_MSC_VER < 1900)
+               return T(1) - erf_impl(v,real_type_tag());
+               #else
+               return erfc_impl(v);
+               #endif
+            }
+
+            template <typename T>
+            inline T erfc_impl(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)
+            {
+               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;
+            }
+
+            template <typename T>
+            inline T ncdf_impl(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)
+            {
+               if (std::abs(v) >= std::numeric_limits<T>::epsilon())
+                   return(std::sin(v) / v);
+               else
+                  return T(1);
+            }
+
+            template <typename T>
+            inline T sinc_impl(T v, int_type_tag)
+            {
+               return sinc_impl(static_cast<double>(v),real_type_tag());
+            }
+
+            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); }
+            template <typename T> inline T   exp_impl(const T v, real_type_tag) { return std::exp  (v); }
+            template <typename T> inline T floor_impl(const T v, real_type_tag) { return std::floor(v); }
+            template <typename T> inline T   log_impl(const T v, real_type_tag) { return std::log  (v); }
+            template <typename T> inline T log10_impl(const T v, real_type_tag) { return std::log10(v); }
+            template <typename T> inline T  log2_impl(const T v, real_type_tag) { return std::log(v)/T(numeric::constant::log2); }
+            template <typename T> inline T   neg_impl(const T v, real_type_tag) { return -v;            }
+            template <typename T> inline T   pos_impl(const T v, real_type_tag) { return +v;            }
+            template <typename T> inline T   sin_impl(const T v, real_type_tag) { return std::sin  (v); }
+            template <typename T> inline T  sinh_impl(const T v, real_type_tag) { return std::sinh (v); }
+            template <typename T> inline T  sqrt_impl(const T v, real_type_tag) { return std::sqrt (v); }
+            template <typename T> inline T   tan_impl(const T v, real_type_tag) { return std::tan  (v); }
+            template <typename T> inline T  tanh_impl(const T v, real_type_tag) { return std::tanh (v); }
+            template <typename T> inline T   cot_impl(const T v, real_type_tag) { return T(1) / std::tan(v); }
+            template <typename T> inline T   sec_impl(const T v, real_type_tag) { return T(1) / std::cos(v); }
+            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  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   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); }
+            template <typename T> inline T   log_impl(const T v, int_type_tag) { return std::log  (v); }
+            template <typename T> inline T log10_impl(const T v, int_type_tag) { return std::log10(v); }
+            template <typename T> inline T  log2_impl(const T v, int_type_tag) { return std::log(v)/T(numeric::constant::log2); }
+            template <typename T> inline T   neg_impl(const T v, int_type_tag) { return -v;            }
+            template <typename T> inline T   pos_impl(const T v, int_type_tag) { return +v;            }
+            template <typename T> inline T  ceil_impl(const T v, int_type_tag) { return v;             }
+            template <typename T> inline T floor_impl(const T v, int_type_tag) { return v;             }
+            template <typename T> inline T round_impl(const T v, int_type_tag) { return v;             }
+            template <typename T> inline T  notl_impl(const T v, int_type_tag) { return !v;            }
+            template <typename T> inline T  sqrt_impl(const T v, int_type_tag) { return std::sqrt (v); }
+            template <typename T> inline T  frac_impl(const T  , int_type_tag) { return T(0);          }
+            template <typename T> inline T trunc_impl(const T v, int_type_tag) { return v;             }
+            template <typename T> inline T  acos_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T acosh_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T  asin_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T asinh_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T  atan_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T atanh_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T   cos_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T  cosh_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T   sin_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T  sinh_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T   tan_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T  tanh_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T   cot_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T   sec_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+            template <typename T> inline T   csc_impl(const T  , int_type_tag) { return std::numeric_limits<T>::quiet_NaN(); }
+
+            template <typename T>
+            inline bool is_integer_impl(const T& v, real_type_tag)
+            {
+               return std::equal_to<T>()(T(0),std::fmod(v,T(1)));
+            }
+
+            template <typename T>
+            inline bool is_integer_impl(const T&, int_type_tag)
+            {
+               return true;
+            }
+         }
+
+         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 <typename T>
+         inline int to_int32(const T v)
+         {
+            const typename details::number_type<T>::type num_type;
+            return to_int32_impl(v, num_type);
+         }
+
+         template <typename T>
+         inline _int64_t to_int64(const T v)
+         {
+            const typename details::number_type<T>::type num_type;
+            return to_int64_impl(v, num_type);
+         }
+
+         template <typename T>
+         inline bool is_nan(const T v)
+         {
+            const typename details::number_type<T>::type num_type;
+            return is_nan_impl(v, num_type);
+         }
+
+         template <typename T>
+         inline T min(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return min_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T max(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return max_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T equal(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return equal_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T nequal(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return nequal_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T modulus(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return modulus_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T pow(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return pow_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T logn(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return logn_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T root(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return root_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T roundn(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return roundn_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T hypot(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return hypot_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T atan2(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return atan2_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T shr(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return shr_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T shl(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return shl_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T and_opr(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return and_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T nand_opr(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return nand_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T or_opr(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return or_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T nor_opr(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return nor_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T xor_opr(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return xor_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline T xnor_opr(const T v0, const T v1)
+         {
+            const typename details::number_type<T>::type num_type;
+            return xnor_impl(v0, v1, num_type);
+         }
+
+         template <typename T>
+         inline bool is_integer(const T v)
+         {
+            const typename details::number_type<T>::type num_type;
+            return is_integer_impl(v, num_type);
+         }
+
+         template <typename T, unsigned int N>
+         struct fast_exp
+         {
+            static inline T result(T v)
+            {
+               unsigned int k = N;
+               T l = T(1);
+
+               while (k)
+               {
+                  if (k & 1)
+                  {
+                     l *= v;
+                     --k;
+                  }
+
+                  v *= v;
+                  k >>= 1;
+               }
+
+               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);      } };
+
+         #define exprtk_define_unary_function(FunctionName)        \
+         template <typename T>                                     \
+         inline T FunctionName (const T v)                         \
+         {                                                         \
+            const typename details::number_type<T>::type num_type; \
+            return  FunctionName##_impl(v,num_type);               \
+         }                                                         \
+
+         exprtk_define_unary_function(abs  )
+         exprtk_define_unary_function(acos )
+         exprtk_define_unary_function(acosh)
+         exprtk_define_unary_function(asin )
+         exprtk_define_unary_function(asinh)
+         exprtk_define_unary_function(atan )
+         exprtk_define_unary_function(atanh)
+         exprtk_define_unary_function(ceil )
+         exprtk_define_unary_function(cos  )
+         exprtk_define_unary_function(cosh )
+         exprtk_define_unary_function(exp  )
+         exprtk_define_unary_function(expm1)
+         exprtk_define_unary_function(floor)
+         exprtk_define_unary_function(log  )
+         exprtk_define_unary_function(log10)
+         exprtk_define_unary_function(log2 )
+         exprtk_define_unary_function(log1p)
+         exprtk_define_unary_function(neg  )
+         exprtk_define_unary_function(pos  )
+         exprtk_define_unary_function(round)
+         exprtk_define_unary_function(sin  )
+         exprtk_define_unary_function(sinc )
+         exprtk_define_unary_function(sinh )
+         exprtk_define_unary_function(sqrt )
+         exprtk_define_unary_function(tan  )
+         exprtk_define_unary_function(tanh )
+         exprtk_define_unary_function(cot  )
+         exprtk_define_unary_function(sec  )
+         exprtk_define_unary_function(csc  )
+         exprtk_define_unary_function(r2d  )
+         exprtk_define_unary_function(d2r  )
+         exprtk_define_unary_function(d2g  )
+         exprtk_define_unary_function(g2d  )
+         exprtk_define_unary_function(notl )
+         exprtk_define_unary_function(sgn  )
+         exprtk_define_unary_function(erf  )
+         exprtk_define_unary_function(erfc )
+         exprtk_define_unary_function(ncdf )
+         exprtk_define_unary_function(frac )
+         exprtk_define_unary_function(trunc)
+         #undef exprtk_define_unary_function
+      }
+
+      template <typename T>
+      inline T compute_pow10(T d, const int exponent)
+      {
+         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
+         };
+
+         static const int fract10_size = static_cast<int>(sizeof(fract10) / sizeof(double));
+
+         const int e = std::abs(exponent);
+
+         if (exponent >= std::numeric_limits<T>::min_exponent10)
+         {
+            if (e < fract10_size)
+            {
+               if (exponent > 0)
+                  return T(d * fract10[e]);
+               else
+                  return T(d / fract10[e]);
+            }
+            else
+               return T(d * std::pow(10.0, 10.0 * exponent));
+         }
+         else
+         {
+                     d /= T(fract10[           -std::numeric_limits<T>::min_exponent10]);
+            return T(d /    fract10[-exponent + std::numeric_limits<T>::min_exponent10]);
+         }
+      }
+
+      template <typename Iterator, typename T>
+      inline bool string_to_type_converter_impl_ref(Iterator& itr, const Iterator end, T& result)
+      {
+         if (itr == end)
+            return false;
+
+         const bool negative = ('-' == (*itr));
+
+         if (negative || ('+' == (*itr)))
+         {
+            if (end == ++itr)
+               return false;
+         }
+
+         static const uchar_t zero = static_cast<uchar_t>('0');
+
+         while ((end != itr) && (zero == (*itr))) ++itr;
+
+         bool return_result = true;
+         unsigned int digit = 0;
+         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
+
+               #define exprtk_process_digit                          \
+               if ((digit = details::digit_table[(int)*itr++]) < 10) \
+                  result = result * 10 + (digit);                    \
+               else                                                  \
+               {                                                     \
+                  return_result = false;                             \
+                  break;                                             \
+               }                                                     \
+
+               #else
+
+               #define exprtk_process_digit         \
+               if ((digit = (*itr++ - zero)) < 10)  \
+                  result = result * T(10) + digit;  \
+               else                                 \
+               {                                    \
+                  return_result = false;            \
+                  break;                            \
+               }                                    \
+
+               #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; }
+
+               #undef exprtk_process_digit
+            }
+            exprtk_disable_fallthrough_end
+         }
+         else
+            return_result = false;
+
+         if (length && return_result)
+         {
+            result = result * 10 + static_cast<T>(digit);
+            ++itr;
+         }
+
+         result = negative ? -result : result;
+         return return_result;
+      }
+
+      template <typename Iterator, typename T>
+      static inline bool parse_nan(Iterator& itr, const Iterator end, T& t)
+      {
+         typedef typename std::iterator_traits<Iterator>::value_type type;
+
+         static const std::size_t nan_length = 3;
+
+         if (std::distance(itr,end) != static_cast<int>(nan_length))
+            return false;
+
+         if (static_cast<type>('n') == (*itr))
+         {
+            if (
+                 (static_cast<type>('a') != *(itr + 1)) ||
+                 (static_cast<type>('n') != *(itr + 2))
+               )
+            {
+               return false;
+            }
+         }
+         else if (
+                   (static_cast<type>('A') != *(itr + 1)) ||
+                   (static_cast<type>('N') != *(itr + 2))
+                 )
+         {
+            return false;
+         }
+
+         t = std::numeric_limits<T>::quiet_NaN();
+
+         return true;
+      }
+
+      template <typename Iterator, typename T>
+      static inline bool parse_inf(Iterator& itr, const Iterator end, T& t, bool negative)
+      {
+         static const char_t inf_uc[] = "INFINITY";
+         static const char_t inf_lc[] = "infinity";
+         static const std::size_t inf_length = 8;
+
+         const std::size_t length = static_cast<std::size_t>(std::distance(itr,end));
+
+         if ((3 != length) && (inf_length != length))
+            return false;
+
+         char_cptr inf_itr = ('i' == (*itr)) ? inf_lc : inf_uc;
+
+         while (end != itr)
+         {
+            if (*inf_itr == static_cast<char>(*itr))
+            {
+               ++itr;
+               ++inf_itr;
+               continue;
+            }
+            else
+               return false;
+         }
+
+         if (negative)
+            t = -std::numeric_limits<T>::infinity();
+         else
+            t =  std::numeric_limits<T>::infinity();
+
+         return true;
+      }
+
+      template <typename Iterator, typename T>
+      inline bool string_to_real(Iterator& itr_external, const Iterator end, T& t, numeric::details::real_type_tag)
+      {
+         if (end == itr_external) return false;
+
+         Iterator itr = itr_external;
+
+         T d = T(0);
+
+         const bool negative = ('-' == (*itr));
+
+         if (negative || '+' == (*itr))
+         {
+            if (end == ++itr)
+               return false;
+         }
+
+         bool instate = false;
+
+         static const char_t zero = static_cast<uchar_t>('0');
+
+         #define parse_digit_1(d)          \
+         if ((digit = (*itr - zero)) < 10) \
+            { d = d * T(10) + digit; }     \
+         else                              \
+            { break; }                     \
+         if (end == ++itr) break;          \
+
+         #define parse_digit_2(d)          \
+         if ((digit = (*itr - zero)) < 10) \
+            { d = d * T(10) + digit; }     \
+         else { break; }                   \
+            ++itr;                         \
+
+         if ('.' != (*itr))
+         {
+            const Iterator curr = itr;
+
+            while ((end != itr) && (zero == (*itr))) ++itr;
+
+            unsigned int digit;
+
+            while (end != itr)
+            {
+               // Note: For 'physical' superscalar architectures it
+               // is advised that the following loop be: 4xPD1 and 1xPD2
+               #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)
+            }
+
+            if (curr != itr) instate = true;
+         }
+
+         int exponent = 0;
+
+         if (end != itr)
+         {
+            if ('.' == (*itr))
+            {
+               const Iterator curr = ++itr;
+               unsigned int digit;
+               T tmp_d = T(0);
+
+               while (end != itr)
+               {
+                  #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)
+               }
+
+               if (curr != itr)
+               {
+                  instate = true;
+                  d += compute_pow10(tmp_d,static_cast<int>(-std::distance(curr,itr)));
+               }
+
+               #undef parse_digit_1
+               #undef parse_digit_2
+            }
+
+            if (end != itr)
+            {
+               typename std::iterator_traits<Iterator>::value_type c = (*itr);
+
+               if (('e' == c) || ('E' == c))
+               {
+                  int exp = 0;
+
+                  if (!details::string_to_type_converter_impl_ref(++itr, end, exp))
+                  {
+                     if (end == itr)
+                        return false;
+                     else
+                        c = (*itr);
+                  }
+
+                  exponent += exp;
+               }
+
+               if (end != itr)
+               {
+                  if (('f' == c) || ('F' == c) || ('l' == c) || ('L' == c))
+                     ++itr;
+                  else if ('#' == c)
+                  {
+                     if (end == ++itr)
+                        return false;
+                     else if (('I' <= (*itr)) && ((*itr) <= 'n'))
+                     {
+                        if (('i' == (*itr)) || ('I' == (*itr)))
+                        {
+                           return parse_inf(itr, end, t, negative);
+                        }
+                        else if (('n' == (*itr)) || ('N' == (*itr)))
+                        {
+                           return parse_nan(itr, end, t);
+                        }
+                        else
+                           return false;
+                     }
+                     else
+                        return false;
+                  }
+                  else if (('I' <= (*itr)) && ((*itr) <= 'n'))
+                  {
+                     if (('i' == (*itr)) || ('I' == (*itr)))
+                     {
+                        return parse_inf(itr, end, t, negative);
+                     }
+                     else if (('n' == (*itr)) || ('N' == (*itr)))
+                     {
+                        return parse_nan(itr, end, t);
+                     }
+                     else
+                        return false;
+                  }
+                  else
+                     return false;
+               }
+            }
+         }
+
+         if ((end != itr) || (!instate))
+            return false;
+         else if (exponent)
+            d = compute_pow10(d,exponent);
+
+         t = static_cast<T>((negative) ? -d : d);
+         return true;
+      }
+
+      template <typename T>
+      inline bool string_to_real(const std::string& s, T& t)
+      {
+         const typename numeric::details::number_type<T>::type num_type;
+
+         char_cptr begin = s.data();
+         char_cptr end   = s.data() + s.size();
+
+         return string_to_real(begin, end, t, num_type);
+      }
+
+      template <typename T>
+      struct functor_t
+      {
+         /*
+            Note: The following definitions for Type, may require tweaking
+                  based on the compiler and target architecture. The benchmark
+                  should provide enough information to make the right choice.
+         */
+         //typedef T Type;
+         //typedef const T Type;
+         typedef const T& Type;
+         typedef       T& RefType;
+         typedef T (*qfunc_t)(Type t0, Type t1, Type t2, Type t3);
+         typedef T (*tfunc_t)(Type t0, Type t1, Type t2);
+         typedef T (*bfunc_t)(Type t0, Type t1);
+         typedef T (*ufunc_t)(Type t0);
+      };
+
+   } // namespace details
+
+   namespace lexer
+   {
+      struct token
+      {
+         enum token_type
+         {
+            e_none        =   0, e_error       =   1, e_err_symbol  =   2,
+            e_err_number  =   3, e_err_string  =   4, e_err_sfunc   =   5,
+            e_eof         =   6, e_number      =   7, e_symbol      =   8,
+            e_string      =   9, e_assign      =  10, e_addass      =  11,
+            e_subass      =  12, e_mulass      =  13, e_divass      =  14,
+            e_modass      =  15, e_shr         =  16, e_shl         =  17,
+            e_lte         =  18, e_ne          =  19, e_gte         =  20,
+            e_swap        =  21, e_lt          = '<', e_gt          = '>',
+            e_eq          = '=', e_rbracket    = ')', e_lbracket    = '(',
+            e_rsqrbracket = ']', e_lsqrbracket = '[', e_rcrlbracket = '}',
+            e_lcrlbracket = '{', e_comma       = ',', e_add         = '+',
+            e_sub         = '-', e_div         = '/', e_mul         = '*',
+            e_mod         = '%', e_pow         = '^', e_colon       = ':',
+            e_ternary     = '?'
+         };
+
+         token()
+         : type(e_none),
+           value(""),
+           position(std::numeric_limits<std::size_t>::max())
+         {}
+
+         void clear()
+         {
+            type     = e_none;
+            value    = "";
+            position = std::numeric_limits<std::size_t>::max();
+         }
+
+         template <typename Iterator>
+         inline token& set_operator(const token_type tt,
+                                    const Iterator begin, const Iterator end,
+                                    const Iterator base_begin = Iterator(0))
+         {
+            type = tt;
+            value.assign(begin,end);
+            if (base_begin)
+               position = static_cast<std::size_t>(std::distance(base_begin,begin));
+            return (*this);
+         }
+
+         template <typename Iterator>
+         inline token& set_symbol(const Iterator begin, const Iterator end, const Iterator base_begin = Iterator(0))
+         {
+            type = e_symbol;
+            value.assign(begin,end);
+            if (base_begin)
+               position = static_cast<std::size_t>(std::distance(base_begin,begin));
+            return (*this);
+         }
+
+         template <typename Iterator>
+         inline token& set_numeric(const Iterator begin, const Iterator end, const Iterator base_begin = Iterator(0))
+         {
+            type = e_number;
+            value.assign(begin,end);
+            if (base_begin)
+               position = static_cast<std::size_t>(std::distance(base_begin,begin));
+            return (*this);
+         }
+
+         template <typename Iterator>
+         inline token& set_string(const Iterator begin, const Iterator end, const Iterator base_begin = Iterator(0))
+         {
+            type = e_string;
+            value.assign(begin,end);
+            if (base_begin)
+               position = static_cast<std::size_t>(std::distance(base_begin,begin));
+            return (*this);
+         }
+
+         inline token& set_string(const std::string& s, const std::size_t p)
+         {
+            type     = e_string;
+            value    = s;
+            position = p;
+            return (*this);
+         }
+
+         template <typename Iterator>
+         inline token& set_error(const token_type et,
+                                 const Iterator begin, const Iterator end,
+                                 const Iterator base_begin = Iterator(0))
+         {
+            if (
+                 (e_error      == et) ||
+                 (e_err_symbol == et) ||
+                 (e_err_number == et) ||
+                 (e_err_string == et) ||
+                 (e_err_sfunc  == et)
+               )
+            {
+               type = et;
+            }
+            else
+               type = e_error;
+
+            value.assign(begin,end);
+
+            if (base_begin)
+               position = static_cast<std::size_t>(std::distance(base_begin,begin));
+
+            return (*this);
+         }
+
+         static inline std::string to_str(token_type t)
+         {
+            switch (t)
+            {
+               case e_none        : return "NONE";
+               case e_error       : return "ERROR";
+               case e_err_symbol  : return "ERROR_SYMBOL";
+               case e_err_number  : return "ERROR_NUMBER";
+               case e_err_string  : return "ERROR_STRING";
+               case e_eof         : return "EOF";
+               case e_number      : return "NUMBER";
+               case e_symbol      : return "SYMBOL";
+               case e_string      : return "STRING";
+               case e_assign      : return ":=";
+               case e_addass      : return "+=";
+               case e_subass      : return "-=";
+               case e_mulass      : return "*=";
+               case e_divass      : return "/=";
+               case e_modass      : return "%=";
+               case e_shr         : return ">>";
+               case e_shl         : return "<<";
+               case e_lte         : return "<=";
+               case e_ne          : return "!=";
+               case e_gte         : return ">=";
+               case e_lt          : return "<";
+               case e_gt          : return ">";
+               case e_eq          : return "=";
+               case e_rbracket    : return ")";
+               case e_lbracket    : return "(";
+               case e_rsqrbracket : return "]";
+               case e_lsqrbracket : return "[";
+               case e_rcrlbracket : return "}";
+               case e_lcrlbracket : return "{";
+               case e_comma       : return ",";
+               case e_add         : return "+";
+               case e_sub         : return "-";
+               case e_div         : return "/";
+               case e_mul         : return "*";
+               case e_mod         : return "%";
+               case e_pow         : return "^";
+               case e_colon       : return ":";
+               case e_ternary     : return "?";
+               case e_swap        : return "<=>";
+               default            : return "UNKNOWN";
+            }
+         }
+
+         inline bool is_error() const
+         {
+            return (
+                     (e_error      == type) ||
+                     (e_err_symbol == type) ||
+                     (e_err_number == type) ||
+                     (e_err_string == type) ||
+                     (e_err_sfunc  == type)
+                   );
+         }
+
+         token_type type;
+         std::string value;
+         std::size_t position;
+      };
+
+      class generator
+      {
+      public:
+
+         typedef token token_t;
+         typedef std::vector<token_t> token_list_t;
+         typedef std::vector<token_t>::iterator token_list_itr_t;
+         typedef details::char_t char_t;
+
+         generator()
+         : base_itr_(0),
+           s_itr_   (0),
+           s_end_   (0)
+         {
+            clear();
+         }
+
+         inline void clear()
+         {
+            base_itr_ = 0;
+            s_itr_    = 0;
+            s_end_    = 0;
+            token_list_.clear();
+            token_itr_ = token_list_.end();
+            store_token_itr_ = token_list_.end();
+         }
+
+         inline bool process(const std::string& str)
+         {
+            base_itr_ = str.data();
+            s_itr_    = str.data();
+            s_end_    = str.data() + str.size();
+
+            eof_token_.set_operator(token_t::e_eof,s_end_,s_end_,base_itr_);
+            token_list_.clear();
+
+            while (!is_end(s_itr_))
+            {
+               scan_token();
+
+               if (!token_list_.empty() && token_list_.back().is_error())
+                  return false;
+            }
+
+            return true;
+         }
+
+         inline bool empty() const
+         {
+            return token_list_.empty();
+         }
+
+         inline std::size_t size() const
+         {
+            return token_list_.size();
+         }
+
+         inline void begin()
+         {
+            token_itr_ = token_list_.begin();
+            store_token_itr_ = token_list_.begin();
+         }
+
+         inline void store()
+         {
+            store_token_itr_ = token_itr_;
+         }
+
+         inline void restore()
+         {
+            token_itr_ = store_token_itr_;
+         }
+
+         inline token_t& next_token()
+         {
+            if (token_list_.end() != token_itr_)
+            {
+               return *token_itr_++;
+            }
+            else
+               return eof_token_;
+         }
+
+         inline token_t& peek_next_token()
+         {
+            if (token_list_.end() != token_itr_)
+            {
+               return *token_itr_;
+            }
+            else
+               return eof_token_;
+         }
+
+         inline token_t& operator[](const std::size_t& index)
+         {
+            if (index < token_list_.size())
+               return token_list_[index];
+            else
+               return eof_token_;
+         }
+
+         inline token_t operator[](const std::size_t& index) const
+         {
+            if (index < token_list_.size())
+               return token_list_[index];
+            else
+               return eof_token_;
+         }
+
+         inline bool finished() const
+         {
+            return (token_list_.end() == token_itr_);
+         }
+
+         inline void insert_front(token_t::token_type tk_type)
+         {
+            if (
+                 !token_list_.empty() &&
+                 (token_list_.end() != token_itr_)
+               )
+            {
+               token_t t = *token_itr_;
+
+               t.type     = tk_type;
+               token_itr_ = token_list_.insert(token_itr_,t);
+            }
+         }
+
+         inline std::string substr(const std::size_t& begin, const std::size_t& end)
+         {
+            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_;
+
+            return std::string(begin_itr,end_itr);
+         }
+
+         inline std::string remaining() const
+         {
+            if (finished())
+               return "";
+            else if (token_list_.begin() != token_itr_)
+               return std::string(base_itr_ + (token_itr_ - 1)->position, s_end_);
+            else
+               return std::string(base_itr_ + token_itr_->position, s_end_);
+         }
+
+      private:
+
+         inline bool is_end(details::char_cptr itr)
+         {
+            return (s_end_ == itr);
+         }
+
+         inline bool is_comment_start(details::char_cptr itr)
+         {
+            #ifndef exprtk_disable_comments
+            const char_t c0 = *(itr + 0);
+            const char_t c1 = *(itr + 1);
+
+            if ('#' == c0)
+               return true;
+            else if (!is_end(itr + 1))
+            {
+               if (('/' == c0) && ('/' == c1)) return true;
+               if (('/' == c0) && ('*' == c1)) return true;
+            }
+            #endif
+            return false;
+         }
+
+         inline void skip_whitespace()
+         {
+            while (!is_end(s_itr_) && details::is_whitespace(*s_itr_))
+            {
+               ++s_itr_;
+            }
+         }
+
+         inline void skip_comments()
+         {
+            #ifndef exprtk_disable_comments
+            // The following comment styles are supported:
+            // 1. // .... \n
+            // 2. #  .... \n
+            // 3. /* .... */
+            struct test
+            {
+               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; }
+                  else if ('/' == c0)
+                  {
+                          if ('/' == c1) { mode = 1; incr = 2; }
+                     else if ('*' == c1) { mode = 2; incr = 2; }
+                  }
+                  return (0 != mode);
+               }
+
+               static inline bool comment_end(const char_t c0, const char_t c1, int& mode)
+               {
+                  if (
+                       ((1 == mode) && ('\n' == c0)) ||
+                       ((2 == mode) && ( '*' == c0) && ('/' == c1))
+                     )
+                  {
+                     mode = 0;
+                     return true;
+                  }
+                  else
+                     return false;
+               }
+            };
+
+            int mode      = 0;
+            int increment = 0;
+
+            if (is_end(s_itr_))
+               return;
+            else if (!test::comment_start(*s_itr_, *(s_itr_ + 1), mode, increment))
+               return;
+
+            details::char_cptr cmt_start = s_itr_;
+
+            s_itr_ += increment;
+
+            while (!is_end(s_itr_))
+            {
+               if ((1 == mode) && test::comment_end(*s_itr_, 0, mode))
+               {
+                  ++s_itr_;
+                  return;
+               }
+
+               if ((2 == mode))
+               {
+                  if (!is_end((s_itr_ + 1)) && test::comment_end(*s_itr_, *(s_itr_ + 1), mode))
+                  {
+                     s_itr_ += 2;
+                     return;
+                  }
+               }
+
+                ++s_itr_;
+            }
+
+            if (2 == mode)
+            {
+               token_t t;
+               t.set_error(token::e_error, cmt_start, cmt_start + mode, base_itr_);
+               token_list_.push_back(t);
+            }
+            #endif
+         }
+
+         inline void scan_token()
+         {
+            if (details::is_whitespace(*s_itr_))
+            {
+               skip_whitespace();
+               return;
+            }
+            else if (is_comment_start(s_itr_))
+            {
+               skip_comments();
+               return;
+            }
+            else if (details::is_operator_char(*s_itr_))
+            {
+               scan_operator();
+               return;
+            }
+            else if (details::is_letter(*s_itr_))
+            {
+               scan_symbol();
+               return;
+            }
+            else if (details::is_digit((*s_itr_)) || ('.' == (*s_itr_)))
+            {
+               scan_number();
+               return;
+            }
+            else if ('$' == (*s_itr_))
+            {
+               scan_special_function();
+               return;
+            }
+            #ifndef exprtk_disable_string_capabilities
+            else if ('\'' == (*s_itr_))
+            {
+               scan_string();
+               return;
+            }
+            #endif
+            else if ('~' == (*s_itr_))
+            {
+               token_t t;
+               t.set_symbol(s_itr_, s_itr_ + 1, base_itr_);
+               token_list_.push_back(t);
+               ++s_itr_;
+               return;
+            }
+            else
+            {
+               token_t t;
+               t.set_error(token::e_error, s_itr_, s_itr_ + 2, base_itr_);
+               token_list_.push_back(t);
+               ++s_itr_;
+            }
+         }
+
+         inline void scan_operator()
+         {
+            token_t t;
+
+            const char_t c0 = s_itr_[0];
+
+            if (!is_end(s_itr_ + 1))
+            {
+               const char_t c1 = s_itr_[1];
+
+               if (!is_end(s_itr_ + 2))
+               {
+                  const char_t c2 = s_itr_[2];
+
+                  if ((c0 == '<') && (c1 == '=') && (c2 == '>'))
+                  {
+                     t.set_operator(token_t::e_swap, s_itr_, s_itr_ + 3, base_itr_);
+                     token_list_.push_back(t);
+                     s_itr_ += 3;
+                     return;
+                  }
+               }
+
+               token_t::token_type ttype = token_t::e_none;
+
+                    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;
+               else if ((c0 == '=') && (c1 == '=')) ttype = token_t::e_eq;
+               else if ((c0 == ':') && (c1 == '=')) ttype = token_t::e_assign;
+               else if ((c0 == '<') && (c1 == '<')) ttype = token_t::e_shl;
+               else if ((c0 == '>') && (c1 == '>')) ttype = token_t::e_shr;
+               else if ((c0 == '+') && (c1 == '=')) ttype = token_t::e_addass;
+               else if ((c0 == '-') && (c1 == '=')) ttype = token_t::e_subass;
+               else if ((c0 == '*') && (c1 == '=')) ttype = token_t::e_mulass;
+               else if ((c0 == '/') && (c1 == '=')) ttype = token_t::e_divass;
+               else if ((c0 == '%') && (c1 == '=')) ttype = token_t::e_modass;
+
+               if (token_t::e_none != ttype)
+               {
+                  t.set_operator(ttype, s_itr_, s_itr_ + 2, base_itr_);
+                  token_list_.push_back(t);
+                  s_itr_ += 2;
+                  return;
+               }
+            }
+
+            if ('<' == c0)
+               t.set_operator(token_t::e_lt , s_itr_, s_itr_ + 1, base_itr_);
+            else if ('>' == c0)
+               t.set_operator(token_t::e_gt , s_itr_, s_itr_ + 1, base_itr_);
+            else if (';' == c0)
+               t.set_operator(token_t::e_eof, s_itr_, s_itr_ + 1, base_itr_);
+            else if ('&' == c0)
+               t.set_symbol(s_itr_, s_itr_ + 1, base_itr_);
+            else if ('|' == c0)
+               t.set_symbol(s_itr_, s_itr_ + 1, base_itr_);
+            else
+               t.set_operator(token_t::token_type(c0), s_itr_, s_itr_ + 1, base_itr_);
+
+            token_list_.push_back(t);
+            ++s_itr_;
+         }
+
+         inline void scan_symbol()
+         {
+            details::char_cptr initial_itr = s_itr_;
+
+            while (!is_end(s_itr_))
+            {
+               if (!details::is_letter_or_digit(*s_itr_) && ('_' != (*s_itr_)))
+               {
+                  if ('.' != (*s_itr_))
+                     break;
+                  /*
+                     Permit symbols that contain a 'dot'
+                     Allowed   : abc.xyz, a123.xyz, abc.123, abc_.xyz a123_.xyz abc._123
+                     Disallowed: .abc, abc.<white-space>, abc.<eof>, abc.<operator +,-,*,/...>
+                  */
+                  if (
+                       (s_itr_ != initial_itr)                     &&
+                       !is_end(s_itr_ + 1)                         &&
+                       !details::is_letter_or_digit(*(s_itr_ + 1)) &&
+                       ('_' != (*(s_itr_ + 1)))
+                     )
+                     break;
+               }
+
+               ++s_itr_;
+            }
+
+            token_t t;
+            t.set_symbol(initial_itr,s_itr_,base_itr_);
+            token_list_.push_back(t);
+         }
+
+         inline void scan_number()
+         {
+            /*
+               Attempt to match a valid numeric value in one of the following formats:
+               (01) 123456
+               (02) 123456.
+               (03) 123.456
+               (04) 123.456e3
+               (05) 123.456E3
+               (06) 123.456e+3
+               (07) 123.456E+3
+               (08) 123.456e-3
+               (09) 123.456E-3
+               (00) .1234
+               (11) .1234e3
+               (12) .1234E+3
+               (13) .1234e+3
+               (14) .1234E-3
+               (15) .1234e-3
+            */
+
+            details::char_cptr initial_itr = s_itr_;
+            bool dot_found                 = false;
+            bool e_found                   = false;
+            bool post_e_sign_found         = false;
+            bool post_e_digit_found        = false;
+            token_t t;
+
+            while (!is_end(s_itr_))
+            {
+               if ('.' == (*s_itr_))
+               {
+                  if (dot_found)
+                  {
+                     t.set_error(token::e_err_number, initial_itr, s_itr_, base_itr_);
+                     token_list_.push_back(t);
+                     return;
+                  }
+
+                  dot_found = true;
+                  ++s_itr_;
+
+                  continue;
+               }
+               else if ('e' == std::tolower(*s_itr_))
+               {
+                  const char_t& c = *(s_itr_ + 1);
+
+                  if (is_end(s_itr_ + 1))
+                  {
+                     t.set_error(token::e_err_number, initial_itr, s_itr_, base_itr_);
+                     token_list_.push_back(t);
+
+                     return;
+                  }
+                  else if (
+                            ('+' != c) &&
+                            ('-' != c) &&
+                            !details::is_digit(c)
+                          )
+                  {
+                     t.set_error(token::e_err_number, initial_itr, s_itr_, base_itr_);
+                     token_list_.push_back(t);
+
+                     return;
+                  }
+
+                  e_found = true;
+                  ++s_itr_;
+
+                  continue;
+               }
+               else if (e_found && details::is_sign(*s_itr_) && !post_e_digit_found)
+               {
+                  if (post_e_sign_found)
+                  {
+                     t.set_error(token::e_err_number, initial_itr, s_itr_, base_itr_);
+                     token_list_.push_back(t);
+
+                     return;
+                  }
+
+                  post_e_sign_found = true;
+                  ++s_itr_;
+
+                  continue;
+               }
+               else if (e_found && details::is_digit(*s_itr_))
+               {
+                  post_e_digit_found = true;
+                  ++s_itr_;
+
+                  continue;
+               }
+               else if (('.' != (*s_itr_)) && !details::is_digit(*s_itr_))
+                  break;
+               else
+                  ++s_itr_;
+            }
+
+            t.set_numeric(initial_itr, s_itr_, base_itr_);
+            token_list_.push_back(t);
+
+            return;
+         }
+
+         inline void scan_special_function()
+         {
+            details::char_cptr initial_itr = s_itr_;
+            token_t t;
+
+            // $fdd(x,x,x) = at least 11 chars
+            if (std::distance(s_itr_,s_end_) < 11)
+            {
+               t.set_error(token::e_err_sfunc, initial_itr, s_itr_, base_itr_);
+               token_list_.push_back(t);
+
+               return;
+            }
+
+            if (
+                 !(('$' == *s_itr_)                       &&
+                   (details::imatch  ('f',*(s_itr_ + 1))) &&
+                   (details::is_digit(*(s_itr_ + 2)))     &&
+                   (details::is_digit(*(s_itr_ + 3))))
+               )
+            {
+               t.set_error(token::e_err_sfunc, initial_itr, s_itr_, base_itr_);
+               token_list_.push_back(t);
+
+               return;
+            }
+
+            s_itr_ += 4; // $fdd = 4chars
+
+            t.set_symbol(initial_itr, s_itr_, base_itr_);
+            token_list_.push_back(t);
+
+            return;
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         inline void scan_string()
+         {
+            details::char_cptr initial_itr = s_itr_ + 1;
+            token_t t;
+
+            if (std::distance(s_itr_,s_end_) < 2)
+            {
+               t.set_error(token::e_err_string, s_itr_, s_end_, base_itr_);
+               token_list_.push_back(t);
+               return;
+            }
+
+            ++s_itr_;
+
+            bool escaped_found = false;
+            bool escaped = false;
+
+            while (!is_end(s_itr_))
+            {
+               if (!escaped && ('\\' == *s_itr_))
+               {
+                  escaped_found = true;
+                  escaped = true;
+                  ++s_itr_;
+
+                  continue;
+               }
+               else if (!escaped)
+               {
+                  if ('\'' == *s_itr_)
+                     break;
+               }
+               else if (escaped)
+               {
+                  if (!is_end(s_itr_) && ('0' == *(s_itr_)))
+                  {
+                     /*
+                        Note: The following 'awkward' conditional is
+                              due to various broken msvc compilers.
+                     */
+                     #if defined(_MSC_VER) && (_MSC_VER == 1600)
+                     const bool within_range = !is_end(s_itr_ + 2) &&
+                                               !is_end(s_itr_ + 3) ;
+                     #else
+                     const bool within_range = !is_end(s_itr_ + 1) &&
+                                               !is_end(s_itr_ + 2) &&
+                                               !is_end(s_itr_ + 3) ;
+                     #endif
+
+                     const bool x_seperator  = ('x' == *(s_itr_ + 1)) ||
+                                               ('X' == *(s_itr_ + 1)) ;
+
+                     const bool both_digits  = details::is_hex_digit(*(s_itr_ + 2)) &&
+                                               details::is_hex_digit(*(s_itr_ + 3)) ;
+
+                     if (!within_range || !x_seperator || !both_digits)
+                     {
+                        t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_);
+                        token_list_.push_back(t);
+
+                        return;
+                     }
+                     else
+                        s_itr_ += 3;
+                  }
+
+                  escaped = false;
+               }
+
+               ++s_itr_;
+            }
+
+            if (is_end(s_itr_))
+            {
+               t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_);
+               token_list_.push_back(t);
+
+               return;
+            }
+
+            if (!escaped_found)
+               t.set_string(initial_itr, s_itr_, base_itr_);
+            else
+            {
+               std::string parsed_string(initial_itr,s_itr_);
+
+               details::cleanup_escapes(parsed_string);
+
+               t.set_string(
+                    parsed_string,
+                    static_cast<std::size_t>(std::distance(base_itr_,initial_itr)));
+            }
+
+            token_list_.push_back(t);
+            ++s_itr_;
+
+            return;
+         }
+         #endif
+
+      private:
+
+         token_list_t     token_list_;
+         token_list_itr_t token_itr_;
+         token_list_itr_t store_token_itr_;
+         token_t eof_token_;
+         details::char_cptr base_itr_;
+         details::char_cptr s_itr_;
+         details::char_cptr s_end_;
+
+         friend class token_scanner;
+         friend class token_modifier;
+         friend class token_inserter;
+         friend class token_joiner;
+      };
+
+      class helper_interface
+      {
+      public:
+
+         virtual void init()                     {              }
+         virtual void reset()                    {              }
+         virtual bool result()                   { return true; }
+         virtual std::size_t process(generator&) { return 0;    }
+         virtual ~helper_interface()             {              }
+      };
+
+      class token_scanner : public helper_interface
+      {
+      public:
+
+         virtual ~token_scanner()
+         {}
+
+         explicit token_scanner(const std::size_t& stride)
+         : stride_(stride)
+         {
+            if (stride > 4)
+            {
+               throw std::invalid_argument("token_scanner() - Invalid stride value");
+            }
+         }
+
+         inline std::size_t process(generator& g)
+         {
+            if (g.token_list_.size() >= stride_)
+            {
+               for (std::size_t i = 0; i < (g.token_list_.size() - stride_ + 1); ++i)
+               {
+                  token t;
+
+                  switch (stride_)
+                  {
+                     case 1 :
+                              {
+                                 const token& t0 = g.token_list_[i];
+
+                                 if (!operator()(t0))
+                                 {
+                                    return i;
+                                 }
+                              }
+                              break;
+
+                     case 2 :
+                              {
+                                 const token& t0 = g.token_list_[i    ];
+                                 const token& t1 = g.token_list_[i + 1];
+
+                                 if (!operator()(t0, t1))
+                                 {
+                                    return i;
+                                 }
+                              }
+                              break;
+
+                     case 3 :
+                              {
+                                 const token& t0 = g.token_list_[i    ];
+                                 const token& t1 = g.token_list_[i + 1];
+                                 const token& t2 = g.token_list_[i + 2];
+
+                                 if (!operator()(t0, t1, t2))
+                                 {
+                                    return i;
+                                 }
+                              }
+                              break;
+
+                     case 4 :
+                              {
+                                 const token& t0 = g.token_list_[i    ];
+                                 const token& t1 = g.token_list_[i + 1];
+                                 const token& t2 = g.token_list_[i + 2];
+                                 const token& t3 = g.token_list_[i + 3];
+
+                                 if (!operator()(t0, t1, t2, t3))
+                                 {
+                                    return i;
+                                 }
+                              }
+                              break;
+                  }
+               }
+            }
+
+            return (g.token_list_.size() - stride_ + 1);
+         }
+
+         virtual bool operator() (const token&)
+         {
+            return false;
+         }
+
+         virtual bool operator() (const token&, const token&)
+         {
+            return false;
+         }
+
+         virtual bool operator() (const token&, const token&, const token&)
+         {
+            return false;
+         }
+
+         virtual bool operator() (const token&, const token&, const token&, const token&)
+         {
+            return false;
+         }
+
+      private:
+
+         const std::size_t stride_;
+      };
+
+      class token_modifier : public helper_interface
+      {
+      public:
+
+         inline std::size_t process(generator& g)
+         {
+            std::size_t changes = 0;
+
+            for (std::size_t i = 0; i < g.token_list_.size(); ++i)
+            {
+               if (modify(g.token_list_[i])) changes++;
+            }
+
+            return changes;
+         }
+
+         virtual bool modify(token& t) = 0;
+      };
+
+      class token_inserter : public helper_interface
+      {
+      public:
+
+         explicit token_inserter(const std::size_t& stride)
+         : stride_(stride)
+         {
+            if (stride > 5)
+            {
+               throw std::invalid_argument("token_inserter() - Invalid stride value");
+            }
+         }
+
+         inline std::size_t process(generator& g)
+         {
+            if (g.token_list_.empty())
+               return 0;
+            else if (g.token_list_.size() < stride_)
+               return 0;
+
+            std::size_t changes = 0;
+
+            for (std::size_t i = 0; i < (g.token_list_.size() - stride_ + 1); ++i)
+            {
+               int insert_index = -1;
+               token t;
+
+               switch (stride_)
+               {
+                  case 1 : insert_index = insert(g.token_list_[i],t);
+                           break;
+
+                  case 2 : insert_index = insert(g.token_list_[i], g.token_list_[i + 1], t);
+                           break;
+
+                  case 3 : insert_index = insert(g.token_list_[i], g.token_list_[i + 1], g.token_list_[i + 2], t);
+                           break;
+
+                  case 4 : insert_index = insert(g.token_list_[i], g.token_list_[i + 1], g.token_list_[i + 2], g.token_list_[i + 3], t);
+                           break;
+
+                  case 5 : insert_index = insert(g.token_list_[i], g.token_list_[i + 1], g.token_list_[i + 2], g.token_list_[i + 3], g.token_list_[i + 4], t);
+                           break;
+               }
+
+               typedef std::iterator_traits<generator::token_list_t::iterator>::difference_type diff_t;
+
+               if ((insert_index >= 0) && (insert_index <= (static_cast<int>(stride_) + 1)))
+               {
+                  g.token_list_.insert(
+                     g.token_list_.begin() + static_cast<diff_t>(i + static_cast<std::size_t>(insert_index)), t);
+
+                  changes++;
+               }
+            }
+
+            return changes;
+         }
+
+         #define token_inserter_empty_body \
+         {                                 \
+            return -1;                     \
+         }                                 \
+
+         inline virtual int insert(const token&, token&)
+         token_inserter_empty_body
+
+         inline virtual int insert(const token&, const token&, token&)
+         token_inserter_empty_body
+
+         inline virtual int insert(const token&, const token&, const token&, token&)
+         token_inserter_empty_body
+
+         inline virtual int insert(const token&, const token&, const token&, const token&, token&)
+         token_inserter_empty_body
+
+         inline virtual int insert(const token&, const token&, const token&, const token&, const token&, token&)
+         token_inserter_empty_body
+
+         #undef token_inserter_empty_body
+
+      private:
+
+         const std::size_t stride_;
+      };
+
+      class token_joiner : public helper_interface
+      {
+      public:
+
+         explicit token_joiner(const std::size_t& stride)
+         : stride_(stride)
+         {}
+
+         inline std::size_t process(generator& g)
+         {
+            if (g.token_list_.empty())
+               return 0;
+
+            switch (stride_)
+            {
+               case 2  : return process_stride_2(g);
+               case 3  : return process_stride_3(g);
+               default : return 0;
+            }
+         }
+
+         virtual bool join(const token&, const token&, token&)               { return false; }
+         virtual bool join(const token&, const token&, const token&, token&) { return false; }
+
+      private:
+
+         inline std::size_t process_stride_2(generator& g)
+         {
+            typedef std::iterator_traits<generator::token_list_t::iterator>::difference_type diff_t;
+
+            if (g.token_list_.size() < 2)
+               return 0;
+
+            std::size_t changes = 0;
+
+            for (int i = 0;  i < static_cast<int>(g.token_list_.size() - 1); ++i)
+            {
+               token t;
+
+               while (join(g[i], g[i + 1], t))
+               {
+                  g.token_list_[i] = t;
+
+                  g.token_list_.erase(g.token_list_.begin() + static_cast<diff_t>(i + 1));
+
+                  ++changes;
+
+                  if (static_cast<std::size_t>(i + 1) >= g.token_list_.size())
+                     break;
+               }
+            }
+
+            return changes;
+         }
+
+         inline std::size_t process_stride_3(generator& g)
+         {
+            typedef std::iterator_traits<generator::token_list_t::iterator>::difference_type diff_t;
+
+            if (g.token_list_.size() < 3)
+               return 0;
+
+            std::size_t changes = 0;
+
+            for (int i = 0;  i < static_cast<int>(g.token_list_.size() - 2); ++i)
+            {
+               token t;
+
+               while (join(g[i], g[i + 1], g[i + 2], t))
+               {
+                  g.token_list_[i] = t;
+
+                  g.token_list_.erase(g.token_list_.begin() + static_cast<diff_t>(i + 1),
+                                      g.token_list_.begin() + static_cast<diff_t>(i + 3));
+                  ++changes;
+
+                  if (static_cast<std::size_t>(i + 2) >= g.token_list_.size())
+                     break;
+               }
+            }
+
+            return changes;
+         }
+
+         const std::size_t stride_;
+      };
+
+      namespace helper
+      {
+
+         inline void dump(lexer::generator& generator)
+         {
+            for (std::size_t i = 0; i < generator.size(); ++i)
+            {
+               lexer::token t = generator[i];
+               printf("Token[%02d] @ %03d  %6s  -->  '%s'\n",
+                      static_cast<int>(i),
+                      static_cast<int>(t.position),
+                      t.to_str(t.type).c_str(),
+                      t.value.c_str());
+            }
+         }
+
+         class commutative_inserter : public lexer::token_inserter
+         {
+         public:
+
+            using lexer::token_inserter::insert;
+
+            commutative_inserter()
+            : lexer::token_inserter(2)
+            {}
+
+            inline void ignore_symbol(const std::string& symbol)
+            {
+               ignore_set_.insert(symbol);
+            }
+
+            inline int insert(const lexer::token& t0, const lexer::token& t1, lexer::token& new_token)
+            {
+               bool match         = false;
+               new_token.type     = lexer::token::e_mul;
+               new_token.value    = "*";
+               new_token.position = t1.position;
+
+               if (t0.type == lexer::token::e_symbol)
+               {
+                  if (ignore_set_.end() != ignore_set_.find(t0.value))
+                  {
+                     return -1;
+                  }
+                  else if (!t0.value.empty() && ('$' == t0.value[0]))
+                  {
+                     return -1;
+                  }
+               }
+
+               if (t1.type == lexer::token::e_symbol)
+               {
+                  if (ignore_set_.end() != ignore_set_.find(t1.value))
+                  {
+                     return -1;
+                  }
+               }
+                    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;
+               else if ((t0.type == lexer::token::e_symbol     ) && (t1.type == lexer::token::e_number     )) match = true;
+               else if ((t0.type == lexer::token::e_rbracket   ) && (t1.type == lexer::token::e_number     )) match = true;
+               else if ((t0.type == lexer::token::e_rcrlbracket) && (t1.type == lexer::token::e_number     )) match = true;
+               else if ((t0.type == lexer::token::e_rsqrbracket) && (t1.type == lexer::token::e_number     )) match = true;
+               else if ((t0.type == lexer::token::e_rbracket   ) && (t1.type == lexer::token::e_symbol     )) match = true;
+               else if ((t0.type == lexer::token::e_rcrlbracket) && (t1.type == lexer::token::e_symbol     )) match = true;
+               else if ((t0.type == lexer::token::e_rsqrbracket) && (t1.type == lexer::token::e_symbol     )) match = true;
+               else if ((t0.type == lexer::token::e_symbol     ) && (t1.type == lexer::token::e_symbol     )) match = true;
+
+               return (match) ? 1 : -1;
+            }
+
+         private:
+
+            std::set<std::string,details::ilesscompare> ignore_set_;
+         };
+
+         class operator_joiner : public token_joiner
+         {
+         public:
+
+            explicit operator_joiner(const std::size_t& stride)
+            : token_joiner(stride)
+            {}
+
+            inline bool join(const lexer::token& t0, const lexer::token& t1, lexer::token& t)
+            {
+               // ': =' --> ':='
+               if ((t0.type == lexer::token::e_colon) && (t1.type == lexer::token::e_eq))
+               {
+                  t.type     = lexer::token::e_assign;
+                  t.value    = ":=";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '+ =' --> '+='
+               else if ((t0.type == lexer::token::e_add) && (t1.type == lexer::token::e_eq))
+               {
+                  t.type     = lexer::token::e_addass;
+                  t.value    = "+=";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '- =' --> '-='
+               else if ((t0.type == lexer::token::e_sub) && (t1.type == lexer::token::e_eq))
+               {
+                  t.type     = lexer::token::e_subass;
+                  t.value    = "-=";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '* =' --> '*='
+               else if ((t0.type == lexer::token::e_mul) && (t1.type == lexer::token::e_eq))
+               {
+                  t.type     = lexer::token::e_mulass;
+                  t.value    = "*=";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '/ =' --> '/='
+               else if ((t0.type == lexer::token::e_div) && (t1.type == lexer::token::e_eq))
+               {
+                  t.type     = lexer::token::e_divass;
+                  t.value    = "/=";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '% =' --> '%='
+               else if ((t0.type == lexer::token::e_mod) && (t1.type == lexer::token::e_eq))
+               {
+                  t.type     = lexer::token::e_modass;
+                  t.value    = "%=";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '> =' --> '>='
+               else if ((t0.type == lexer::token::e_gt) && (t1.type == lexer::token::e_eq))
+               {
+                  t.type     = lexer::token::e_gte;
+                  t.value    = ">=";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '< =' --> '<='
+               else if ((t0.type == lexer::token::e_lt) && (t1.type == lexer::token::e_eq))
+               {
+                  t.type     = lexer::token::e_lte;
+                  t.value    = "<=";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '= =' --> '=='
+               else if ((t0.type == lexer::token::e_eq) && (t1.type == lexer::token::e_eq))
+               {
+                  t.type     = lexer::token::e_eq;
+                  t.value    = "==";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '! =' --> '!='
+               else if ((static_cast<char>(t0.type) == '!') && (t1.type == lexer::token::e_eq))
+               {
+                  t.type     = lexer::token::e_ne;
+                  t.value    = "!=";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '< >' --> '<>'
+               else if ((t0.type == lexer::token::e_lt) && (t1.type == lexer::token::e_gt))
+               {
+                  t.type     = lexer::token::e_ne;
+                  t.value    = "<>";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '<= >' --> '<=>'
+               else if ((t0.type == lexer::token::e_lte) && (t1.type == lexer::token::e_gt))
+               {
+                  t.type     = lexer::token::e_swap;
+                  t.value    = "<=>";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '+ -' --> '-'
+               else if ((t0.type == lexer::token::e_add) && (t1.type == lexer::token::e_sub))
+               {
+                  t.type     = lexer::token::e_sub;
+                  t.value    = "-";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '- +' --> '-'
+               else if ((t0.type == lexer::token::e_sub) && (t1.type == lexer::token::e_add))
+               {
+                  t.type     = lexer::token::e_sub;
+                  t.value    = "-";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               // '- -' --> '+'
+               else if ((t0.type == lexer::token::e_sub) && (t1.type == lexer::token::e_sub))
+               {
+                  /*
+                     Note: May need to reconsider this when wanting to implement
+                     pre/postfix decrement operator
+                  */
+                  t.type     = lexer::token::e_add;
+                  t.value    = "+";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               else
+                  return false;
+            }
+
+            inline bool join(const lexer::token& t0, const lexer::token& t1, const lexer::token& t2, lexer::token& t)
+            {
+               // '[ * ]' --> '[*]'
+               if (
+                    (t0.type == lexer::token::e_lsqrbracket) &&
+                    (t1.type == lexer::token::e_mul        ) &&
+                    (t2.type == lexer::token::e_rsqrbracket)
+                  )
+               {
+                  t.type     = lexer::token::e_symbol;
+                  t.value    = "[*]";
+                  t.position = t0.position;
+
+                  return true;
+               }
+               else
+                  return false;
+            }
+         };
+
+         class bracket_checker : public lexer::token_scanner
+         {
+         public:
+
+            using lexer::token_scanner::operator();
+
+            bracket_checker()
+            : token_scanner(1),
+              state_(true)
+            {}
+
+            bool result()
+            {
+               if (!stack_.empty())
+               {
+                  lexer::token t;
+                  t.value      = stack_.top().first;
+                  t.position   = stack_.top().second;
+                  error_token_ = t;
+                  state_       = false;
+
+                  return false;
+               }
+               else
+                  return state_;
+            }
+
+            lexer::token error_token()
+            {
+               return error_token_;
+            }
+
+            void reset()
+            {
+               // Why? because msvc doesn't support swap properly.
+               stack_ = std::stack<std::pair<char,std::size_t> >();
+               state_ = true;
+               error_token_.clear();
+            }
+
+            bool operator() (const lexer::token& t)
+            {
+               if (
+                    !t.value.empty()                       &&
+                    (lexer::token::e_string != t.type)     &&
+                    (lexer::token::e_symbol != t.type)     &&
+                    exprtk::details::is_bracket(t.value[0])
+                  )
+               {
+                  details::char_t c = t.value[0];
+
+                       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))
+                  {
+                     if (stack_.empty())
+                     {
+                        state_       = false;
+                        error_token_ = t;
+
+                        return false;
+                     }
+                     else if (c != stack_.top().first)
+                     {
+                        state_       = false;
+                        error_token_ = t;
+
+                        return false;
+                     }
+                     else
+                        stack_.pop();
+                  }
+               }
+
+               return true;
+            }
+
+         private:
+
+            bool state_;
+            std::stack<std::pair<char,std::size_t> > stack_;
+            lexer::token error_token_;
+         };
+
+         class numeric_checker : public lexer::token_scanner
+         {
+         public:
+
+            using lexer::token_scanner::operator();
+
+            numeric_checker()
+            : token_scanner (1),
+              current_index_(0)
+            {}
+
+            bool result()
+            {
+               return error_list_.empty();
+            }
+
+            void reset()
+            {
+               error_list_.clear();
+               current_index_ = 0;
+            }
+
+            bool operator() (const lexer::token& t)
+            {
+               if (token::e_number == t.type)
+               {
+                  double v;
+
+                  if (!exprtk::details::string_to_real(t.value,v))
+                  {
+                     error_list_.push_back(current_index_);
+                  }
+               }
+
+               ++current_index_;
+
+               return true;
+            }
+
+            std::size_t error_count() const
+            {
+               return error_list_.size();
+            }
+
+            std::size_t error_index(const std::size_t& i)
+            {
+               if (i < error_list_.size())
+                  return error_list_[i];
+               else
+                  return std::numeric_limits<std::size_t>::max();
+            }
+
+            void clear_errors()
+            {
+               error_list_.clear();
+            }
+
+         private:
+
+            std::size_t current_index_;
+            std::vector<std::size_t> error_list_;
+         };
+
+         class symbol_replacer : public lexer::token_modifier
+         {
+         private:
+
+            typedef std::map<std::string,std::pair<std::string,token::token_type>,details::ilesscompare> replace_map_t;
+
+         public:
+
+            bool remove(const std::string& target_symbol)
+            {
+               const replace_map_t::iterator itr = replace_map_.find(target_symbol);
+
+               if (replace_map_.end() == itr)
+                  return false;
+
+               replace_map_.erase(itr);
+
+               return true;
+            }
+
+            bool add_replace(const std::string& target_symbol,
+                             const std::string& replace_symbol,
+                             const lexer::token::token_type token_type = lexer::token::e_symbol)
+            {
+               const replace_map_t::iterator itr = replace_map_.find(target_symbol);
+
+               if (replace_map_.end() != itr)
+               {
+                  return false;
+               }
+
+               replace_map_[target_symbol] = std::make_pair(replace_symbol,token_type);
+
+               return true;
+            }
+
+            void clear()
+            {
+               replace_map_.clear();
+            }
+
+         private:
+
+            bool modify(lexer::token& t)
+            {
+               if (lexer::token::e_symbol == t.type)
+               {
+                  if (replace_map_.empty())
+                     return false;
+
+                  const replace_map_t::iterator itr = replace_map_.find(t.value);
+
+                  if (replace_map_.end() != itr)
+                  {
+                     t.value = itr->second.first;
+                     t.type  = itr->second.second;
+
+                     return true;
+                  }
+               }
+
+               return false;
+            }
+
+            replace_map_t replace_map_;
+         };
+
+         class sequence_validator : public lexer::token_scanner
+         {
+         private:
+
+            typedef std::pair<lexer::token::token_type,lexer::token::token_type> token_pair_t;
+            typedef std::set<token_pair_t> set_t;
+
+         public:
+
+            using lexer::token_scanner::operator();
+
+            sequence_validator()
+            : lexer::token_scanner(2)
+            {
+               add_invalid(lexer::token::e_number, lexer::token::e_number);
+               add_invalid(lexer::token::e_string, lexer::token::e_string);
+               add_invalid(lexer::token::e_number, lexer::token::e_string);
+               add_invalid(lexer::token::e_string, lexer::token::e_number);
+
+               add_invalid_set1(lexer::token::e_assign );
+               add_invalid_set1(lexer::token::e_shr    );
+               add_invalid_set1(lexer::token::e_shl    );
+               add_invalid_set1(lexer::token::e_lte    );
+               add_invalid_set1(lexer::token::e_ne     );
+               add_invalid_set1(lexer::token::e_gte    );
+               add_invalid_set1(lexer::token::e_lt     );
+               add_invalid_set1(lexer::token::e_gt     );
+               add_invalid_set1(lexer::token::e_eq     );
+               add_invalid_set1(lexer::token::e_comma  );
+               add_invalid_set1(lexer::token::e_add    );
+               add_invalid_set1(lexer::token::e_sub    );
+               add_invalid_set1(lexer::token::e_div    );
+               add_invalid_set1(lexer::token::e_mul    );
+               add_invalid_set1(lexer::token::e_mod    );
+               add_invalid_set1(lexer::token::e_pow    );
+               add_invalid_set1(lexer::token::e_colon  );
+               add_invalid_set1(lexer::token::e_ternary);
+            }
+
+            bool result()
+            {
+               return error_list_.empty();
+            }
+
+            bool operator() (const lexer::token& t0, const lexer::token& t1)
+            {
+               const set_t::value_type p = std::make_pair(t0.type,t1.type);
+
+               if (invalid_bracket_check(t0.type,t1.type))
+               {
+                  error_list_.push_back(std::make_pair(t0,t1));
+               }
+               else if (invalid_comb_.find(p) != invalid_comb_.end())
+               {
+                  error_list_.push_back(std::make_pair(t0,t1));
+               }
+
+               return true;
+            }
+
+            std::size_t error_count() const
+            {
+               return error_list_.size();
+            }
+
+            std::pair<lexer::token,lexer::token> error(const std::size_t index)
+            {
+               if (index < error_list_.size())
+               {
+                  return error_list_[index];
+               }
+               else
+               {
+                  static const lexer::token error_token;
+                  return std::make_pair(error_token,error_token);
+               }
+            }
+
+            void clear_errors()
+            {
+               error_list_.clear();
+            }
+
+         private:
+
+            void add_invalid(lexer::token::token_type base, lexer::token::token_type t)
+            {
+               invalid_comb_.insert(std::make_pair(base,t));
+            }
+
+            void add_invalid_set1(lexer::token::token_type t)
+            {
+               add_invalid(t, lexer::token::e_assign);
+               add_invalid(t, lexer::token::e_shr   );
+               add_invalid(t, lexer::token::e_shl   );
+               add_invalid(t, lexer::token::e_lte   );
+               add_invalid(t, lexer::token::e_ne    );
+               add_invalid(t, lexer::token::e_gte   );
+               add_invalid(t, lexer::token::e_lt    );
+               add_invalid(t, lexer::token::e_gt    );
+               add_invalid(t, lexer::token::e_eq    );
+               add_invalid(t, lexer::token::e_comma );
+               add_invalid(t, lexer::token::e_div   );
+               add_invalid(t, lexer::token::e_mul   );
+               add_invalid(t, lexer::token::e_mod   );
+               add_invalid(t, lexer::token::e_pow   );
+               add_invalid(t, lexer::token::e_colon );
+            }
+
+            bool invalid_bracket_check(lexer::token::token_type base, lexer::token::token_type t)
+            {
+               if (details::is_right_bracket(static_cast<char>(base)))
+               {
+                  switch (t)
+                  {
+                     case lexer::token::e_assign : return (']' != base);
+                     case lexer::token::e_string : return (')' != base);
+                     default                     : return false;
+                  }
+               }
+               else if (details::is_left_bracket(static_cast<char>(base)))
+               {
+                  if (details::is_right_bracket(static_cast<char>(t)))
+                     return false;
+                  else if (details::is_left_bracket(static_cast<char>(t)))
+                     return false;
+                  else
+                  {
+                     switch (t)
+                     {
+                        case lexer::token::e_number  : return false;
+                        case lexer::token::e_symbol  : return false;
+                        case lexer::token::e_string  : return false;
+                        case lexer::token::e_add     : return false;
+                        case lexer::token::e_sub     : return false;
+                        case lexer::token::e_colon   : return false;
+                        case lexer::token::e_ternary : return false;
+                        default                      : return true ;
+                     }
+                  }
+               }
+               else if (details::is_right_bracket(static_cast<char>(t)))
+               {
+                  switch (base)
+                  {
+                     case lexer::token::e_number  : return false;
+                     case lexer::token::e_symbol  : return false;
+                     case lexer::token::e_string  : return false;
+                     case lexer::token::e_eof     : return false;
+                     case lexer::token::e_colon   : return false;
+                     case lexer::token::e_ternary : return false;
+                     default                      : return true ;
+                  }
+               }
+               else if (details::is_left_bracket(static_cast<char>(t)))
+               {
+                  switch (base)
+                  {
+                     case lexer::token::e_rbracket    : return true;
+                     case lexer::token::e_rsqrbracket : return true;
+                     case lexer::token::e_rcrlbracket : return true;
+                     default                          : return false;
+                  }
+               }
+
+               return false;
+            }
+
+            set_t invalid_comb_;
+            std::vector<std::pair<lexer::token,lexer::token> > error_list_;
+         };
+
+         class sequence_validator_3tokens : public lexer::token_scanner
+         {
+         private:
+
+            typedef lexer::token::token_type token_t;
+            typedef std::pair<token_t,std::pair<token_t,token_t> > token_triplet_t;
+            typedef std::set<token_triplet_t> set_t;
+
+         public:
+
+            using lexer::token_scanner::operator();
+
+            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_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   );
+            }
+
+            bool result()
+            {
+               return error_list_.empty();
+            }
+
+            bool operator() (const lexer::token& t0, const lexer::token& t1, const lexer::token& t2)
+            {
+               const set_t::value_type p = std::make_pair(t0.type,std::make_pair(t1.type,t2.type));
+
+               if (invalid_comb_.find(p) != invalid_comb_.end())
+               {
+                  error_list_.push_back(std::make_pair(t0,t1));
+               }
+
+               return true;
+            }
+
+            std::size_t error_count() const
+            {
+               return error_list_.size();
+            }
+
+            std::pair<lexer::token,lexer::token> error(const std::size_t index)
+            {
+               if (index < error_list_.size())
+               {
+                  return error_list_[index];
+               }
+               else
+               {
+                  static const lexer::token error_token;
+                  return std::make_pair(error_token,error_token);
+               }
+            }
+
+            void clear_errors()
+            {
+               error_list_.clear();
+            }
+
+         private:
+
+            void add_invalid(token_t t0, token_t t1, token_t t2)
+            {
+               invalid_comb_.insert(std::make_pair(t0,std::make_pair(t1,t2)));
+            }
+
+            set_t invalid_comb_;
+            std::vector<std::pair<lexer::token,lexer::token> > error_list_;
+         };
+
+         struct helper_assembly
+         {
+            inline bool register_scanner(lexer::token_scanner* scanner)
+            {
+               if (token_scanner_list.end() != std::find(token_scanner_list.begin(),
+                                                         token_scanner_list.end  (),
+                                                         scanner))
+               {
+                  return false;
+               }
+
+               token_scanner_list.push_back(scanner);
+
+               return true;
+            }
+
+            inline bool register_modifier(lexer::token_modifier* modifier)
+            {
+               if (token_modifier_list.end() != std::find(token_modifier_list.begin(),
+                                                          token_modifier_list.end  (),
+                                                          modifier))
+               {
+                  return false;
+               }
+
+               token_modifier_list.push_back(modifier);
+
+               return true;
+            }
+
+            inline bool register_joiner(lexer::token_joiner* joiner)
+            {
+               if (token_joiner_list.end() != std::find(token_joiner_list.begin(),
+                                                        token_joiner_list.end  (),
+                                                        joiner))
+               {
+                  return false;
+               }
+
+               token_joiner_list.push_back(joiner);
+
+               return true;
+            }
+
+            inline bool register_inserter(lexer::token_inserter* inserter)
+            {
+               if (token_inserter_list.end() != std::find(token_inserter_list.begin(),
+                                                          token_inserter_list.end  (),
+                                                          inserter))
+               {
+                  return false;
+               }
+
+               token_inserter_list.push_back(inserter);
+
+               return true;
+            }
+
+            inline bool run_modifiers(lexer::generator& g)
+            {
+               error_token_modifier = reinterpret_cast<lexer::token_modifier*>(0);
+
+               for (std::size_t i = 0; i < token_modifier_list.size(); ++i)
+               {
+                  lexer::token_modifier& modifier = (*token_modifier_list[i]);
+
+                  modifier.reset();
+                  modifier.process(g);
+
+                  if (!modifier.result())
+                  {
+                     error_token_modifier = token_modifier_list[i];
+
+                     return false;
+                  }
+               }
+
+               return true;
+            }
+
+            inline bool run_joiners(lexer::generator& g)
+            {
+               error_token_joiner = reinterpret_cast<lexer::token_joiner*>(0);
+
+               for (std::size_t i = 0; i < token_joiner_list.size(); ++i)
+               {
+                  lexer::token_joiner& joiner = (*token_joiner_list[i]);
+
+                  joiner.reset();
+                  joiner.process(g);
+
+                  if (!joiner.result())
+                  {
+                     error_token_joiner = token_joiner_list[i];
+
+                     return false;
+                  }
+               }
+
+               return true;
+            }
+
+            inline bool run_inserters(lexer::generator& g)
+            {
+               error_token_inserter = reinterpret_cast<lexer::token_inserter*>(0);
+
+               for (std::size_t i = 0; i < token_inserter_list.size(); ++i)
+               {
+                  lexer::token_inserter& inserter = (*token_inserter_list[i]);
+
+                  inserter.reset();
+                  inserter.process(g);
+
+                  if (!inserter.result())
+                  {
+                     error_token_inserter = token_inserter_list[i];
+
+                     return false;
+                  }
+               }
+
+               return true;
+            }
+
+            inline bool run_scanners(lexer::generator& g)
+            {
+               error_token_scanner = reinterpret_cast<lexer::token_scanner*>(0);
+
+               for (std::size_t i = 0; i < token_scanner_list.size(); ++i)
+               {
+                  lexer::token_scanner& scanner = (*token_scanner_list[i]);
+
+                  scanner.reset();
+                  scanner.process(g);
+
+                  if (!scanner.result())
+                  {
+                     error_token_scanner = token_scanner_list[i];
+
+                     return false;
+                  }
+               }
+
+               return true;
+            }
+
+            std::vector<lexer::token_scanner*>  token_scanner_list;
+            std::vector<lexer::token_modifier*> token_modifier_list;
+            std::vector<lexer::token_joiner*>   token_joiner_list;
+            std::vector<lexer::token_inserter*> token_inserter_list;
+
+            lexer::token_scanner*  error_token_scanner;
+            lexer::token_modifier* error_token_modifier;
+            lexer::token_joiner*   error_token_joiner;
+            lexer::token_inserter* error_token_inserter;
+         };
+      }
+
+      class parser_helper
+      {
+      public:
+
+         typedef token         token_t;
+         typedef generator generator_t;
+
+         inline bool init(const std::string& str)
+         {
+            if (!lexer_.process(str))
+            {
+               return false;
+            }
+
+            lexer_.begin();
+
+            next_token();
+
+            return true;
+         }
+
+         inline generator_t& lexer()
+         {
+            return lexer_;
+         }
+
+         inline const generator_t& lexer() const
+         {
+            return lexer_;
+         }
+
+         inline void store_token()
+         {
+            lexer_.store();
+            store_current_token_ = current_token_;
+         }
+
+         inline void restore_token()
+         {
+            lexer_.restore();
+            current_token_ = store_current_token_;
+         }
+
+         inline void next_token()
+         {
+            current_token_ = lexer_.next_token();
+         }
+
+         inline const token_t& current_token() const
+         {
+            return current_token_;
+         }
+
+         enum token_advance_mode
+         {
+            e_hold    = 0,
+            e_advance = 1
+         };
+
+         inline void advance_token(const token_advance_mode mode)
+         {
+            if (e_advance == mode)
+            {
+               next_token();
+            }
+         }
+
+         inline bool token_is(const token_t::token_type& ttype, const token_advance_mode mode = e_advance)
+         {
+            if (current_token().type != ttype)
+            {
+               return false;
+            }
+
+            advance_token(mode);
+
+            return true;
+         }
+
+         inline bool token_is(const token_t::token_type& ttype,
+                              const std::string& value,
+                              const token_advance_mode mode = e_advance)
+         {
+            if (
+                 (current_token().type != ttype) ||
+                 !exprtk::details::imatch(value,current_token().value)
+               )
+            {
+               return false;
+            }
+
+            advance_token(mode);
+
+            return true;
+         }
+
+         inline bool peek_token_is(const token_t::token_type& ttype)
+         {
+            return (lexer_.peek_next_token().type == ttype);
+         }
+
+         inline bool peek_token_is(const std::string& s)
+         {
+            return (exprtk::details::imatch(lexer_.peek_next_token().value,s));
+         }
+
+      private:
+
+         generator_t lexer_;
+         token_t     current_token_;
+         token_t     store_current_token_;
+      };
+   }
+
+   template <typename T>
+   class vector_view
+   {
+   public:
+
+      typedef T* data_ptr_t;
+
+      vector_view(data_ptr_t data, const std::size_t& size)
+      : size_(size),
+        data_(data),
+        data_ref_(0)
+      {}
+
+      vector_view(const vector_view<T>& vv)
+      : size_(vv.size_),
+        data_(vv.data_),
+        data_ref_(0)
+      {}
+
+      inline void rebase(data_ptr_t data)
+      {
+         data_ = data;
+
+         if (!data_ref_.empty())
+         {
+            for (std::size_t i = 0; i < data_ref_.size(); ++i)
+            {
+               (*data_ref_[i]) = data;
+            }
+         }
+      }
+
+      inline data_ptr_t data() const
+      {
+         return data_;
+      }
+
+      inline std::size_t size() const
+      {
+         return size_;
+      }
+
+      inline const T& operator[](const std::size_t index) const
+      {
+         return data_[index];
+      }
+
+      inline T& operator[](const std::size_t index)
+      {
+         return data_[index];
+      }
+
+      void set_ref(data_ptr_t* data_ref)
+      {
+         data_ref_.push_back(data_ref);
+      }
+
+   private:
+
+      const std::size_t size_;
+      data_ptr_t  data_;
+      std::vector<data_ptr_t*> data_ref_;
+   };
+
+   template <typename T>
+   inline vector_view<T> make_vector_view(T* data,
+                                          const std::size_t size, const std::size_t offset = 0)
+   {
+      return vector_view<T>(data + offset, size);
+   }
+
+   template <typename T>
+   inline vector_view<T> make_vector_view(std::vector<T>& v,
+                                          const std::size_t size, const std::size_t offset = 0)
+   {
+      return vector_view<T>(v.data() + offset, size);
+   }
+
+   template <typename T> class results_context;
+
+   template <typename T>
+   struct type_store
+   {
+      enum store_type
+      {
+         e_unknown,
+         e_scalar ,
+         e_vector ,
+         e_string
+      };
+
+      type_store()
+      : data(0),
+        size(0),
+        type(e_unknown)
+      {}
+
+      union
+      {
+          void*  data;
+          T*     vec_data;
+      };
+
+      std::size_t size;
+      store_type  type;
+
+      class parameter_list
+      {
+      public:
+
+         parameter_list(std::vector<type_store>& pl)
+         : parameter_list_(pl)
+         {}
+
+         inline bool empty() const
+         {
+            return parameter_list_.empty();
+         }
+
+         inline std::size_t size() const
+         {
+            return parameter_list_.size();
+         }
+
+         inline type_store& operator[](const std::size_t& index)
+         {
+            return parameter_list_[index];
+         }
+
+         inline const type_store& operator[](const std::size_t& index) const
+         {
+            return parameter_list_[index];
+         }
+
+         inline type_store& front()
+         {
+            return parameter_list_[0];
+         }
+
+         inline const type_store& front() const
+         {
+            return parameter_list_[0];
+         }
+
+         inline type_store& back()
+         {
+            return parameter_list_.back();
+         }
+
+         inline const type_store& back() const
+         {
+            return parameter_list_.back();
+         }
+
+      private:
+
+         std::vector<type_store>& parameter_list_;
+
+         friend class results_context<T>;
+      };
+
+      template <typename ViewType>
+      struct type_view
+      {
+         typedef type_store<T> type_store_t;
+         typedef ViewType      value_t;
+
+         type_view(type_store_t& ts)
+         : ts_(ts),
+           data_(reinterpret_cast<value_t*>(ts_.data))
+         {}
+
+         type_view(const type_store_t& ts)
+         : ts_(const_cast<type_store_t&>(ts)),
+           data_(reinterpret_cast<value_t*>(ts_.data))
+         {}
+
+         inline std::size_t size() const
+         {
+            return ts_.size;
+         }
+
+         inline value_t& operator[](const std::size_t& i)
+         {
+            return data_[i];
+         }
+
+         inline const value_t& operator[](const std::size_t& i) const
+         {
+            return data_[i];
+         }
+
+         inline const value_t* begin() const { return data_; }
+         inline       value_t* begin()       { return data_; }
+
+         inline const value_t* end() const
+         {
+            return static_cast<value_t*>(data_ + ts_.size);
+         }
+
+         inline value_t* end()
+         {
+            return static_cast<value_t*>(data_ + ts_.size);
+         }
+
+         type_store_t& ts_;
+         value_t* data_;
+      };
+
+      typedef type_view<T>    vector_view;
+      typedef type_view<char> string_view;
+
+      struct scalar_view
+      {
+         typedef type_store<T> type_store_t;
+         typedef T value_t;
+
+         scalar_view(type_store_t& ts)
+         : v_(*reinterpret_cast<value_t*>(ts.data))
+         {}
+
+         scalar_view(const type_store_t& ts)
+         : v_(*reinterpret_cast<value_t*>(const_cast<type_store_t&>(ts).data))
+         {}
+
+         inline value_t& operator() ()
+         {
+            return v_;
+         }
+
+         inline const value_t& operator() () const
+         {
+            return v_;
+         }
+
+         template <typename IntType>
+         inline bool to_int(IntType& i) const
+         {
+            if (!exprtk::details::numeric::is_integer(v_))
+               return false;
+
+            i = static_cast<IntType>(v_);
+
+            return true;
+         }
+
+         template <typename UIntType>
+         inline bool to_uint(UIntType& u) const
+         {
+            if (v_ < T(0))
+               return false;
+            else if (!exprtk::details::numeric::is_integer(v_))
+               return false;
+
+            u = static_cast<UIntType>(v_);
+
+            return true;
+         }
+
+         T& v_;
+      };
+   };
+
+   template <typename StringView>
+   inline std::string to_str(const StringView& view)
+   {
+      return std::string(view.begin(),view.size());
+   }
+
+   #ifndef exprtk_disable_return_statement
+   namespace details
+   {
+      template <typename T> class return_node;
+      template <typename T> class return_envelope_node;
+   }
+   #endif
+
+   template <typename T>
+   class results_context
+   {
+   public:
+
+      typedef type_store<T> type_store_t;
+
+      results_context()
+      : results_available_(false)
+      {}
+
+      inline std::size_t count() const
+      {
+         if (results_available_)
+            return parameter_list_.size();
+         else
+            return 0;
+      }
+
+      inline type_store_t& operator[](const std::size_t& index)
+      {
+         return parameter_list_[index];
+      }
+
+      inline const type_store_t& operator[](const std::size_t& index) const
+      {
+         return parameter_list_[index];
+      }
+
+   private:
+
+      inline void clear()
+      {
+         results_available_ = false;
+      }
+
+      typedef std::vector<type_store_t> ts_list_t;
+      typedef typename type_store_t::parameter_list parameter_list_t;
+
+      inline void assign(const parameter_list_t& pl)
+      {
+         parameter_list_    = pl.parameter_list_;
+         results_available_ = true;
+      }
+
+      bool results_available_;
+      ts_list_t parameter_list_;
+
+      #ifndef exprtk_disable_return_statement
+      friend class details::return_node<T>;
+      friend class details::return_envelope_node<T>;
+      #endif
+   };
+
+   namespace details
+   {
+      enum operator_type
+      {
+         e_default , e_null    , e_add     , e_sub     ,
+         e_mul     , e_div     , e_mod     , e_pow     ,
+         e_atan2   , e_min     , e_max     , e_avg     ,
+         e_sum     , e_prod    , e_lt      , e_lte     ,
+         e_eq      , e_equal   , e_ne      , e_nequal  ,
+         e_gte     , e_gt      , e_and     , e_nand    ,
+         e_or      , e_nor     , e_xor     , e_xnor    ,
+         e_mand    , e_mor     , e_scand   , e_scor    ,
+         e_shr     , e_shl     , e_abs     , e_acos    ,
+         e_acosh   , e_asin    , e_asinh   , e_atan    ,
+         e_atanh   , e_ceil    , e_cos     , e_cosh    ,
+         e_exp     , e_expm1   , e_floor   , e_log     ,
+         e_log10   , e_log2    , e_log1p   , e_logn    ,
+         e_neg     , e_pos     , e_round   , e_roundn  ,
+         e_root    , e_sqrt    , e_sin     , e_sinc    ,
+         e_sinh    , e_sec     , e_csc     , e_tan     ,
+         e_tanh    , e_cot     , e_clamp   , e_iclamp  ,
+         e_inrange , e_sgn     , e_r2d     , e_d2r     ,
+         e_d2g     , e_g2d     , e_hypot   , e_notl    ,
+         e_erf     , e_erfc    , e_ncdf    , e_frac    ,
+         e_trunc   , e_assign  , e_addass  , e_subass  ,
+         e_mulass  , e_divass  , e_modass  , e_in      ,
+         e_like    , e_ilike   , e_multi   , e_smulti  ,
+         e_swap    ,
+
+         // Do not add new functions/operators after this point.
+         e_sf00 = 1000, e_sf01 = 1001, e_sf02 = 1002, e_sf03 = 1003,
+         e_sf04 = 1004, e_sf05 = 1005, e_sf06 = 1006, e_sf07 = 1007,
+         e_sf08 = 1008, e_sf09 = 1009, e_sf10 = 1010, e_sf11 = 1011,
+         e_sf12 = 1012, e_sf13 = 1013, e_sf14 = 1014, e_sf15 = 1015,
+         e_sf16 = 1016, e_sf17 = 1017, e_sf18 = 1018, e_sf19 = 1019,
+         e_sf20 = 1020, e_sf21 = 1021, e_sf22 = 1022, e_sf23 = 1023,
+         e_sf24 = 1024, e_sf25 = 1025, e_sf26 = 1026, e_sf27 = 1027,
+         e_sf28 = 1028, e_sf29 = 1029, e_sf30 = 1030, e_sf31 = 1031,
+         e_sf32 = 1032, e_sf33 = 1033, e_sf34 = 1034, e_sf35 = 1035,
+         e_sf36 = 1036, e_sf37 = 1037, e_sf38 = 1038, e_sf39 = 1039,
+         e_sf40 = 1040, e_sf41 = 1041, e_sf42 = 1042, e_sf43 = 1043,
+         e_sf44 = 1044, e_sf45 = 1045, e_sf46 = 1046, e_sf47 = 1047,
+         e_sf48 = 1048, e_sf49 = 1049, e_sf50 = 1050, e_sf51 = 1051,
+         e_sf52 = 1052, e_sf53 = 1053, e_sf54 = 1054, e_sf55 = 1055,
+         e_sf56 = 1056, e_sf57 = 1057, e_sf58 = 1058, e_sf59 = 1059,
+         e_sf60 = 1060, e_sf61 = 1061, e_sf62 = 1062, e_sf63 = 1063,
+         e_sf64 = 1064, e_sf65 = 1065, e_sf66 = 1066, e_sf67 = 1067,
+         e_sf68 = 1068, e_sf69 = 1069, e_sf70 = 1070, e_sf71 = 1071,
+         e_sf72 = 1072, e_sf73 = 1073, e_sf74 = 1074, e_sf75 = 1075,
+         e_sf76 = 1076, e_sf77 = 1077, e_sf78 = 1078, e_sf79 = 1079,
+         e_sf80 = 1080, e_sf81 = 1081, e_sf82 = 1082, e_sf83 = 1083,
+         e_sf84 = 1084, e_sf85 = 1085, e_sf86 = 1086, e_sf87 = 1087,
+         e_sf88 = 1088, e_sf89 = 1089, e_sf90 = 1090, e_sf91 = 1091,
+         e_sf92 = 1092, e_sf93 = 1093, e_sf94 = 1094, e_sf95 = 1095,
+         e_sf96 = 1096, e_sf97 = 1097, e_sf98 = 1098, e_sf99 = 1099,
+         e_sffinal  = 1100,
+         e_sf4ext00 = 2000, e_sf4ext01 = 2001, e_sf4ext02 = 2002, e_sf4ext03 = 2003,
+         e_sf4ext04 = 2004, e_sf4ext05 = 2005, e_sf4ext06 = 2006, e_sf4ext07 = 2007,
+         e_sf4ext08 = 2008, e_sf4ext09 = 2009, e_sf4ext10 = 2010, e_sf4ext11 = 2011,
+         e_sf4ext12 = 2012, e_sf4ext13 = 2013, e_sf4ext14 = 2014, e_sf4ext15 = 2015,
+         e_sf4ext16 = 2016, e_sf4ext17 = 2017, e_sf4ext18 = 2018, e_sf4ext19 = 2019,
+         e_sf4ext20 = 2020, e_sf4ext21 = 2021, e_sf4ext22 = 2022, e_sf4ext23 = 2023,
+         e_sf4ext24 = 2024, e_sf4ext25 = 2025, e_sf4ext26 = 2026, e_sf4ext27 = 2027,
+         e_sf4ext28 = 2028, e_sf4ext29 = 2029, e_sf4ext30 = 2030, e_sf4ext31 = 2031,
+         e_sf4ext32 = 2032, e_sf4ext33 = 2033, e_sf4ext34 = 2034, e_sf4ext35 = 2035,
+         e_sf4ext36 = 2036, e_sf4ext37 = 2037, e_sf4ext38 = 2038, e_sf4ext39 = 2039,
+         e_sf4ext40 = 2040, e_sf4ext41 = 2041, e_sf4ext42 = 2042, e_sf4ext43 = 2043,
+         e_sf4ext44 = 2044, e_sf4ext45 = 2045, e_sf4ext46 = 2046, e_sf4ext47 = 2047,
+         e_sf4ext48 = 2048, e_sf4ext49 = 2049, e_sf4ext50 = 2050, e_sf4ext51 = 2051,
+         e_sf4ext52 = 2052, e_sf4ext53 = 2053, e_sf4ext54 = 2054, e_sf4ext55 = 2055,
+         e_sf4ext56 = 2056, e_sf4ext57 = 2057, e_sf4ext58 = 2058, e_sf4ext59 = 2059,
+         e_sf4ext60 = 2060, e_sf4ext61 = 2061
+      };
+
+      inline std::string to_str(const operator_type opr)
+      {
+         switch (opr)
+         {
+            case e_add    : return  "+"  ;
+            case e_sub    : return  "-"  ;
+            case e_mul    : return  "*"  ;
+            case e_div    : return  "/"  ;
+            case e_mod    : return  "%"  ;
+            case e_pow    : return  "^"  ;
+            case e_assign : return ":="  ;
+            case e_addass : return "+="  ;
+            case e_subass : return "-="  ;
+            case e_mulass : return "*="  ;
+            case e_divass : return "/="  ;
+            case e_modass : return "%="  ;
+            case e_lt     : return  "<"  ;
+            case e_lte    : return "<="  ;
+            case e_eq     : return "=="  ;
+            case e_equal  : return  "="  ;
+            case e_ne     : return "!="  ;
+            case e_nequal : return "<>"  ;
+            case e_gte    : return ">="  ;
+            case e_gt     : return  ">"  ;
+            case e_and    : return "and" ;
+            case e_or     : return "or"  ;
+            case e_xor    : return "xor" ;
+            case e_nand   : return "nand";
+            case e_nor    : return "nor" ;
+            case e_xnor   : return "xnor";
+            default       : return "N/A" ;
+         }
+      }
+
+      struct base_operation_t
+      {
+         base_operation_t(const operator_type t, const unsigned int& np)
+         : type(t),
+           num_params(np)
+         {}
+
+         operator_type type;
+         unsigned int num_params;
+      };
+
+      namespace loop_unroll
+      {
+         #ifndef exprtk_disable_superscalar_unroll
+         const unsigned int global_loop_batch_size = 16;
+         #else
+         const unsigned int global_loop_batch_size = 4;
+         #endif
+
+         struct details
+         {
+            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)))
+            {}
+
+            unsigned int batch_size;
+            int   remainder;
+            int upper_bound;
+         };
+      }
+
+      #ifdef exprtk_enable_debugging
+      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
+      inline void dump_ptr(const std::string&, const void*) {}
+      inline void dump_ptr(const std::string&, const void*, const std::size_t) {}
+      #endif
+
+      template <typename T>
+      class vec_data_store
+      {
+      public:
+
+         typedef vec_data_store<T> type;
+         typedef T* data_t;
+
+      private:
+
+         struct control_block
+         {
+            control_block()
+            : ref_count(1),
+              size     (0),
+              data     (0),
+              destruct (true)
+            {}
+
+            control_block(const std::size_t& dsize)
+            : 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)
+            {}
+
+           ~control_block()
+            {
+               if (data && destruct && (0 == ref_count))
+               {
+                  dump_ptr("~control_block() data",data);
+                  delete[] data;
+                  data = reinterpret_cast<data_t>(0);
+               }
+            }
+
+            static inline control_block* create(const std::size_t& dsize, data_t data_ptr = data_t(0), bool dstrct = false)
+            {
+               if (dsize)
+               {
+                  if (0 == data_ptr)
+                     return (new control_block(dsize));
+                  else
+                     return (new control_block(dsize, data_ptr, dstrct));
+               }
+               else
+                  return (new control_block);
+            }
+
+            static inline void destroy(control_block*& cntrl_blck)
+            {
+               if (cntrl_blck)
+               {
+                  if (
+                       (0 !=   cntrl_blck->ref_count) &&
+                       (0 == --cntrl_blck->ref_count)
+                     )
+                  {
+                     delete cntrl_blck;
+                  }
+
+                  cntrl_blck = 0;
+               }
+            }
+
+            std::size_t ref_count;
+            std::size_t size;
+            data_t      data;
+            bool        destruct;
+
+         private:
+
+            control_block(const control_block&);
+            control_block& operator=(const control_block&);
+
+            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);
+            }
+         };
+
+      public:
+
+         vec_data_store()
+         : control_block_(control_block::create(0))
+         {}
+
+         vec_data_store(const std::size_t& size)
+         : control_block_(control_block::create(size,(data_t)(0),true))
+         {}
+
+         vec_data_store(const std::size_t& size, data_t data, bool dstrct = false)
+         : control_block_(control_block::create(size, data, dstrct))
+         {}
+
+         vec_data_store(const type& vds)
+         {
+            control_block_ = vds.control_block_;
+            control_block_->ref_count++;
+         }
+
+        ~vec_data_store()
+         {
+            control_block::destroy(control_block_);
+         }
+
+         type& operator=(const type& vds)
+         {
+            if (this != &vds)
+            {
+               std::size_t final_size = min_size(control_block_, vds.control_block_);
+
+               vds.control_block_->size = final_size;
+                   control_block_->size = final_size;
+
+               if (control_block_->destruct || (0 == control_block_->data))
+               {
+                  control_block::destroy(control_block_);
+
+                  control_block_ = vds.control_block_;
+                  control_block_->ref_count++;
+               }
+            }
+
+            return (*this);
+         }
+
+         inline data_t data()
+         {
+            return control_block_->data;
+         }
+
+         inline data_t data() const
+         {
+            return control_block_->data;
+         }
+
+         inline std::size_t size()
+         {
+            return control_block_->size;
+         }
+
+         inline std::size_t size() const
+         {
+            return control_block_->size;
+         }
+
+         inline data_t& ref()
+         {
+            return control_block_->data;
+         }
+
+         inline void dump() const
+         {
+            #ifdef exprtk_enable_debugging
+            exprtk_debug(("size: %d\taddress:%p\tdestruct:%c\n",
+                          size(),
+                          data(),
+                          (control_block_->destruct ? 'T' : 'F')));
+
+            for (std::size_t i = 0; i < size(); ++i)
+            {
+               if (5 == i)
+                  exprtk_debug(("\n"));
+
+               exprtk_debug(("%15.10f ",data()[i]));
+            }
+            exprtk_debug(("\n"));
+            #endif
+         }
+
+         static inline void match_sizes(type& vds0, type& vds1)
+         {
+            std::size_t size = min_size(vds0.control_block_,vds1.control_block_);
+            vds0.control_block_->size = size;
+            vds1.control_block_->size = size;
+         }
+
+      private:
+
+         static inline std::size_t min_size(control_block* cb0, control_block* cb1)
+         {
+            const std::size_t size0 = cb0->size;
+            const std::size_t size1 = cb1->size;
+
+            if (size0 && size1)
+               return std::min(size0,size1);
+            else
+               return (size0) ? size0 : size1;
+         }
+
+         control_block* control_block_;
+      };
+
+      namespace numeric
+      {
+         namespace details
+         {
+            template <typename T>
+            inline T process_impl(const operator_type operation, const T arg)
+            {
+               switch (operation)
+               {
+                  case e_abs   : return numeric::abs  (arg);
+                  case e_acos  : return numeric::acos (arg);
+                  case e_acosh : return numeric::acosh(arg);
+                  case e_asin  : return numeric::asin (arg);
+                  case e_asinh : return numeric::asinh(arg);
+                  case e_atan  : return numeric::atan (arg);
+                  case e_atanh : return numeric::atanh(arg);
+                  case e_ceil  : return numeric::ceil (arg);
+                  case e_cos   : return numeric::cos  (arg);
+                  case e_cosh  : return numeric::cosh (arg);
+                  case e_exp   : return numeric::exp  (arg);
+                  case e_expm1 : return numeric::expm1(arg);
+                  case e_floor : return numeric::floor(arg);
+                  case e_log   : return numeric::log  (arg);
+                  case e_log10 : return numeric::log10(arg);
+                  case e_log2  : return numeric::log2 (arg);
+                  case e_log1p : return numeric::log1p(arg);
+                  case e_neg   : return numeric::neg  (arg);
+                  case e_pos   : return numeric::pos  (arg);
+                  case e_round : return numeric::round(arg);
+                  case e_sin   : return numeric::sin  (arg);
+                  case e_sinc  : return numeric::sinc (arg);
+                  case e_sinh  : return numeric::sinh (arg);
+                  case e_sqrt  : return numeric::sqrt (arg);
+                  case e_tan   : return numeric::tan  (arg);
+                  case e_tanh  : return numeric::tanh (arg);
+                  case e_cot   : return numeric::cot  (arg);
+                  case e_sec   : return numeric::sec  (arg);
+                  case e_csc   : return numeric::csc  (arg);
+                  case e_r2d   : return numeric::r2d  (arg);
+                  case e_d2r   : return numeric::d2r  (arg);
+                  case e_d2g   : return numeric::d2g  (arg);
+                  case e_g2d   : return numeric::g2d  (arg);
+                  case e_notl  : return numeric::notl (arg);
+                  case e_sgn   : return numeric::sgn  (arg);
+                  case e_erf   : return numeric::erf  (arg);
+                  case e_erfc  : return numeric::erfc (arg);
+                  case e_ncdf  : return numeric::ncdf (arg);
+                  case e_frac  : return numeric::frac (arg);
+                  case e_trunc : return numeric::trunc(arg);
+
+                  default      : exprtk_debug(("numeric::details::process_impl<T> - Invalid unary operation.\n"));
+                                 return std::numeric_limits<T>::quiet_NaN();
+               }
+            }
+
+            template <typename T>
+            inline T process_impl(const operator_type operation, const T arg0, const T arg1)
+            {
+               switch (operation)
+               {
+                  case e_add    : return (arg0 + arg1);
+                  case e_sub    : return (arg0 - arg1);
+                  case e_mul    : return (arg0 * arg1);
+                  case e_div    : return (arg0 / arg1);
+                  case e_mod    : return modulus<T>(arg0,arg1);
+                  case e_pow    : return pow<T>(arg0,arg1);
+                  case e_atan2  : return atan2<T>(arg0,arg1);
+                  case e_min    : return std::min<T>(arg0,arg1);
+                  case e_max    : return std::max<T>(arg0,arg1);
+                  case e_logn   : return logn<T>(arg0,arg1);
+                  case e_lt     : return (arg0 <  arg1) ? T(1) : T(0);
+                  case e_lte    : return (arg0 <= arg1) ? T(1) : T(0);
+                  case e_eq     : return std::equal_to<T>()(arg0,arg1) ? T(1) : T(0);
+                  case e_ne     : return std::not_equal_to<T>()(arg0,arg1) ? T(1) : T(0);
+                  case e_gte    : return (arg0 >= arg1) ? T(1) : T(0);
+                  case e_gt     : return (arg0 >  arg1) ? T(1) : T(0);
+                  case e_and    : return and_opr <T>(arg0,arg1);
+                  case e_nand   : return nand_opr<T>(arg0,arg1);
+                  case e_or     : return or_opr  <T>(arg0,arg1);
+                  case e_nor    : return nor_opr <T>(arg0,arg1);
+                  case e_xor    : return xor_opr <T>(arg0,arg1);
+                  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_hypot  : return hypot   <T>(arg0,arg1);
+                  case e_shr    : return shr     <T>(arg0,arg1);
+                  case e_shl    : return shl     <T>(arg0,arg1);
+
+                  default       : exprtk_debug(("numeric::details::process_impl<T> - Invalid binary operation.\n"));
+                                  return std::numeric_limits<T>::quiet_NaN();
+               }
+            }
+
+            template <typename T>
+            inline T process_impl(const operator_type operation, const T arg0, const T arg1, int_type_tag)
+            {
+               switch (operation)
+               {
+                  case e_add    : return (arg0 + arg1);
+                  case e_sub    : return (arg0 - arg1);
+                  case e_mul    : return (arg0 * arg1);
+                  case e_div    : return (arg0 / arg1);
+                  case e_mod    : return arg0 % arg1;
+                  case e_pow    : return pow<T>(arg0,arg1);
+                  case e_min    : return std::min<T>(arg0,arg1);
+                  case e_max    : return std::max<T>(arg0,arg1);
+                  case e_logn   : return logn<T>(arg0,arg1);
+                  case e_lt     : return (arg0 <  arg1) ? T(1) : T(0);
+                  case e_lte    : return (arg0 <= arg1) ? T(1) : T(0);
+                  case e_eq     : return (arg0 == arg1) ? T(1) : T(0);
+                  case e_ne     : return (arg0 != arg1) ? T(1) : T(0);
+                  case e_gte    : return (arg0 >= arg1) ? T(1) : T(0);
+                  case e_gt     : return (arg0 >  arg1) ? T(1) : T(0);
+                  case e_and    : return ((arg0 != T(0)) && (arg1 != T(0))) ? T(1) : T(0);
+                  case e_nand   : return ((arg0 != T(0)) && (arg1 != T(0))) ? T(0) : T(1);
+                  case e_or     : return ((arg0 != T(0)) || (arg1 != T(0))) ? T(1) : T(0);
+                  case e_nor    : return ((arg0 != T(0)) || (arg1 != T(0))) ? T(0) : T(1);
+                  case e_xor    : return arg0 ^ arg1;
+                  case e_xnor   : return !(arg0 ^ arg1);
+                  case e_root   : return root<T>(arg0,arg1);
+                  case e_equal  : return arg0 == arg1;
+                  case e_nequal : return arg0 != arg1;
+                  case e_hypot  : return hypot<T>(arg0,arg1);
+                  case e_shr    : return arg0 >> arg1;
+                  case e_shl    : return arg0 << arg1;
+
+                  default       : exprtk_debug(("numeric::details::process_impl<IntType> - Invalid binary operation.\n"));
+                                  return std::numeric_limits<T>::quiet_NaN();
+               }
+            }
+         }
+
+         template <typename T>
+         inline T process(const operator_type operation, const T arg)
+         {
+            return exprtk::details::numeric::details::process_impl(operation,arg);
+         }
+
+         template <typename T>
+         inline T process(const operator_type operation, const T arg0, const T arg1)
+         {
+            return exprtk::details::numeric::details::process_impl(operation, arg0, arg1);
+         }
+      }
+
+      template <typename T>
+      class expression_node
+      {
+      public:
+
+         enum node_type
+         {
+            e_none          , e_null          , e_constant    , e_unary        ,
+            e_binary        , e_binary_ext    , e_trinary     , e_quaternary   ,
+            e_vararg        , e_conditional   , e_while       , e_repeat       ,
+            e_for           , e_switch        , e_mswitch     , e_return       ,
+            e_retenv        , e_variable      , e_stringvar   , e_stringconst  ,
+            e_stringvarrng  , e_cstringvarrng , e_strgenrange , e_strconcat    ,
+            e_stringvarsize , e_strswap       , e_stringsize  , e_stringvararg ,
+            e_function      , e_vafunction    , e_genfunction , e_strfunction  ,
+            e_strcondition  , e_strccondition , e_add         , e_sub          ,
+            e_mul           , e_div           , e_mod         , e_pow          ,
+            e_lt            , e_lte           , e_gt          , e_gte          ,
+            e_eq            , e_ne            , e_and         , e_nand         ,
+            e_or            , e_nor           , e_xor         , e_xnor         ,
+            e_in            , e_like          , e_ilike       , e_inranges     ,
+            e_ipow          , e_ipowinv       , e_abs         , e_acos         ,
+            e_acosh         , e_asin          , e_asinh       , e_atan         ,
+            e_atanh         , e_ceil          , e_cos         , e_cosh         ,
+            e_exp           , e_expm1         , e_floor       , e_log          ,
+            e_log10         , e_log2          , e_log1p       , e_neg          ,
+            e_pos           , e_round         , e_sin         , e_sinc         ,
+            e_sinh          , e_sqrt          , e_tan         , e_tanh         ,
+            e_cot           , e_sec           , e_csc         , e_r2d          ,
+            e_d2r           , e_d2g           , e_g2d         , e_notl         ,
+            e_sgn           , e_erf           , e_erfc        , e_ncdf         ,
+            e_frac          , e_trunc         , e_uvouv       , e_vov          ,
+            e_cov           , e_voc           , e_vob         , e_bov          ,
+            e_cob           , e_boc           , e_vovov       , e_vovoc        ,
+            e_vocov         , e_covov         , e_covoc       , e_vovovov      ,
+            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
+         };
+
+         typedef T value_type;
+         typedef expression_node<T>* expression_ptr;
+
+         virtual ~expression_node()
+         {}
+
+         inline virtual T value() const
+         {
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline virtual expression_node<T>* branch(const std::size_t& index = 0) const
+         {
+            return reinterpret_cast<expression_ptr>(index * 0);
+         }
+
+         inline virtual node_type type() const
+         {
+            return e_none;
+         }
+      };
+
+      template <typename T>
+      inline bool is_generally_string_node(const expression_node<T>* node);
+
+      inline bool is_true(const double v)
+      {
+         return std::not_equal_to<double>()(0.0,v);
+      }
+
+      inline bool is_true(const long double v)
+      {
+         return std::not_equal_to<long double>()(0.0L,v);
+      }
+
+      inline bool is_true(const float v)
+      {
+         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)
+      {
+         return std::not_equal_to<T>()(T(0),node->value());
+      }
+
+      template <typename T>
+      inline bool is_false(const expression_node<T>* node)
+      {
+         return std::equal_to<T>()(T(0),node->value());
+      }
+
+      template <typename T>
+      inline bool is_unary_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_unary == node->type());
+      }
+
+      template <typename T>
+      inline bool is_neg_unary_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_neg == node->type());
+      }
+
+      template <typename T>
+      inline bool is_binary_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_binary == node->type());
+      }
+
+      template <typename T>
+      inline bool is_variable_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_variable == node->type());
+      }
+
+      template <typename T>
+      inline bool is_ivariable_node(const expression_node<T>* node)
+      {
+         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()
+                );
+      }
+
+      template <typename T>
+      inline bool is_vector_elem_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_vecelem == 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_celem_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_rbveccelem == node->type());
+      }
+
+      template <typename T>
+      inline bool is_vector_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_vector == node->type());
+      }
+
+      template <typename T>
+      inline bool is_ivector_node(const expression_node<T>* node)
+      {
+         if (node)
+         {
+            switch (node->type())
+            {
+               case details::expression_node<T>::e_vector      :
+               case details::expression_node<T>::e_vecvalass   :
+               case details::expression_node<T>::e_vecvecass   :
+               case details::expression_node<T>::e_vecopvalass :
+               case details::expression_node<T>::e_vecopvecass :
+               case details::expression_node<T>::e_vecvecswap  :
+               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;
+               default                                         : return false;
+            }
+         }
+         else
+            return false;
+      }
+
+      template <typename T>
+      inline bool is_constant_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_constant == node->type());
+      }
+
+      template <typename T>
+      inline bool is_null_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_null == node->type());
+      }
+
+      template <typename T>
+      inline bool is_break_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_break == node->type());
+      }
+
+      template <typename T>
+      inline bool is_continue_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_continue == node->type());
+      }
+
+      template <typename T>
+      inline bool is_swap_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_swap == node->type());
+      }
+
+      template <typename T>
+      inline bool is_function(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_function == node->type());
+      }
+
+      template <typename T>
+      inline bool is_return_node(const expression_node<T>* node)
+      {
+         return node && (details::expression_node<T>::e_return == node->type());
+      }
+
+      template <typename T> class unary_node;
+
+      template <typename T>
+      inline bool is_negate_node(const expression_node<T>* node)
+      {
+         if (node && is_unary_node(node))
+         {
+            return (details::e_neg == static_cast<const unary_node<T>*>(node)->operation());
+         }
+         else
+            return false;
+      }
+
+      template <typename T>
+      inline bool branch_deletable(expression_node<T>* node)
+      {
+         return !is_variable_node(node) &&
+                !is_string_node  (node) ;
+      }
+
+      template <std::size_t N, typename T>
+      inline bool all_nodes_valid(expression_node<T>* (&b)[N])
+      {
+         for (std::size_t i = 0; i < N; ++i)
+         {
+            if (0 == b[i]) return false;
+         }
+
+         return true;
+      }
+
+      template <typename T,
+                typename Allocator,
+                template <typename, typename> class Sequence>
+      inline bool all_nodes_valid(const Sequence<expression_node<T>*,Allocator>& b)
+      {
+         for (std::size_t i = 0; i < b.size(); ++i)
+         {
+            if (0 == b[i]) return false;
+         }
+
+         return true;
+      }
+
+      template <std::size_t N, typename T>
+      inline bool all_nodes_variables(expression_node<T>* (&b)[N])
+      {
+         for (std::size_t i = 0; i < N; ++i)
+         {
+            if (0 == b[i])
+               return false;
+            else if (!is_variable_node(b[i]))
+               return false;
+         }
+
+         return true;
+      }
+
+      template <typename T,
+                typename Allocator,
+                template <typename, typename> class Sequence>
+      inline bool all_nodes_variables(Sequence<expression_node<T>*,Allocator>& b)
+      {
+         for (std::size_t i = 0; i < b.size(); ++i)
+         {
+            if (0 == b[i])
+               return false;
+            else if (!is_variable_node(b[i]))
+               return false;
+         }
+
+         return true;
+      }
+
+      template <typename NodeAllocator, typename T, std::size_t N>
+      inline void free_all_nodes(NodeAllocator& node_allocator, expression_node<T>* (&b)[N])
+      {
+         for (std::size_t i = 0; i < N; ++i)
+         {
+            free_node(node_allocator,b[i]);
+         }
+      }
+
+      template <typename NodeAllocator,
+                typename T,
+                typename Allocator,
+                template <typename, typename> class Sequence>
+      inline void free_all_nodes(NodeAllocator& node_allocator, Sequence<expression_node<T>*,Allocator>& b)
+      {
+         for (std::size_t i = 0; i < b.size(); ++i)
+         {
+            free_node(node_allocator,b[i]);
+         }
+
+         b.clear();
+      }
+
+      template <typename NodeAllocator, typename T>
+      inline void free_node(NodeAllocator& node_allocator, expression_node<T>*& node, const bool force_delete = false)
+      {
+         if (0 != node)
+         {
+            if (
+                 (is_variable_node(node) || is_string_node(node)) ||
+                 force_delete
+               )
+               return;
+
+            node_allocator.free(node);
+            node = reinterpret_cast<expression_node<T>*>(0);
+         }
+      }
+
+      template <typename T>
+      inline void destroy_node(expression_node<T>*& node)
+      {
+         delete node;
+         node = reinterpret_cast<expression_node<T>*>(0);
+      }
+
+      template <typename Type>
+      class vector_holder
+      {
+      private:
+
+         typedef Type value_type;
+         typedef value_type* value_ptr;
+         typedef const value_ptr const_value_ptr;
+
+         class vector_holder_base
+         {
+         public:
+
+            virtual ~vector_holder_base() {}
+
+            inline value_ptr operator[](const std::size_t& index) const
+            {
+               return value_at(index);
+            }
+
+            inline std::size_t size() const
+            {
+               return vector_size();
+            }
+
+            inline value_ptr data() const
+            {
+               return value_at(0);
+            }
+
+            virtual inline bool rebaseable() const
+            {
+               return false;
+            }
+
+            virtual void set_ref(value_ptr*) {}
+
+         protected:
+
+            virtual value_ptr value_at(const std::size_t&) const = 0;
+            virtual std::size_t vector_size()              const = 0;
+         };
+
+         class array_vector_impl : public vector_holder_base
+         {
+         public:
+
+            array_vector_impl(const Type* vec, const std::size_t& vec_size)
+            : vec_(vec),
+              size_(vec_size)
+            {}
+
+         protected:
+
+            value_ptr value_at(const std::size_t& index) const
+            {
+               if (index < size_)
+                  return const_cast<const_value_ptr>(vec_ + index);
+               else
+                  return const_value_ptr(0);
+            }
+
+            std::size_t vector_size() const
+            {
+               return size_;
+            }
+
+         private:
+
+            array_vector_impl operator=(const array_vector_impl&);
+
+            const Type* vec_;
+            const std::size_t size_;
+         };
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         class sequence_vector_impl : public vector_holder_base
+         {
+         public:
+
+            typedef Sequence<Type,Allocator> sequence_t;
+
+            sequence_vector_impl(sequence_t& seq)
+            : sequence_(seq)
+            {}
+
+         protected:
+
+            value_ptr value_at(const std::size_t& index) const
+            {
+               return (index < sequence_.size()) ? (&sequence_[index]) : const_value_ptr(0);
+            }
+
+            std::size_t vector_size() const
+            {
+               return sequence_.size();
+            }
+
+         private:
+
+            sequence_vector_impl operator=(const sequence_vector_impl&);
+
+            sequence_t& sequence_;
+         };
+
+         class vector_view_impl : public vector_holder_base
+         {
+         public:
+
+            typedef exprtk::vector_view<Type> vector_view_t;
+
+            vector_view_impl(vector_view_t& vec_view)
+            : vec_view_(vec_view)
+            {}
+
+            void set_ref(value_ptr* ref)
+            {
+               vec_view_.set_ref(ref);
+            }
+
+            virtual inline bool rebaseable() const
+            {
+               return true;
+            }
+
+         protected:
+
+            value_ptr value_at(const std::size_t& index) const
+            {
+               return (index < vec_view_.size()) ? (&vec_view_[index]) : const_value_ptr(0);
+            }
+
+            std::size_t vector_size() const
+            {
+               return vec_view_.size();
+            }
+
+         private:
+
+            vector_view_impl operator=(const vector_view_impl&);
+
+            vector_view_t& vec_view_;
+         };
+
+      public:
+
+         typedef typename details::vec_data_store<Type> vds_t;
+
+         vector_holder(Type* vec, const std::size_t& vec_size)
+         : vector_holder_base_(new(buffer)array_vector_impl(vec,vec_size))
+         {}
+
+         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)
+         : vector_holder_base_(new(buffer)sequence_vector_impl<Allocator,std::vector>(vec))
+         {}
+
+         vector_holder(exprtk::vector_view<Type>& vec)
+         : vector_holder_base_(new(buffer)vector_view_impl(vec))
+         {}
+
+         inline value_ptr operator[](const std::size_t& index) const
+         {
+            return (*vector_holder_base_)[index];
+         }
+
+         inline std::size_t size() const
+         {
+            return vector_holder_base_->size();
+         }
+
+         inline value_ptr data() const
+         {
+            return vector_holder_base_->data();
+         }
+
+         void set_ref(value_ptr* ref)
+         {
+            vector_holder_base_->set_ref(ref);
+         }
+
+         bool rebaseable() const
+         {
+            return vector_holder_base_->rebaseable();
+         }
+
+      private:
+
+         mutable vector_holder_base* vector_holder_base_;
+         uchar_t buffer[64];
+      };
+
+      template <typename T>
+      class null_node : public expression_node<T>
+      {
+      public:
+
+         inline T value() const
+         {
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_null;
+         }
+      };
+
+      template <typename T>
+      class null_eq_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         null_eq_node(expression_ptr brnch, const bool equality = true)
+         : branch_(brnch),
+           branch_deletable_(branch_deletable(branch_)),
+           equality_(equality)
+         {}
+
+        ~null_eq_node()
+         {
+            if (branch_ && branch_deletable_)
+            {
+               destroy_node(branch_);
+            }
+         }
+
+         inline T value() const
+         {
+            const T v = branch_->value();
+            const bool result = details::numeric::is_nan(v);
+
+            if (result)
+               return (equality_) ? T(1) : T(0);
+            else
+               return (equality_) ? T(0) : T(1);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_nulleq;
+         }
+
+         inline operator_type operation() const
+         {
+            return details::e_eq;
+         }
+
+         inline expression_node<T>* branch(const std::size_t&) const
+         {
+            return branch_;
+         }
+
+      private:
+
+         expression_ptr branch_;
+         const bool branch_deletable_;
+         bool equality_;
+      };
+
+      template <typename T>
+      class literal_node : public expression_node<T>
+      {
+      public:
+
+         explicit literal_node(const T& v)
+         : value_(v)
+         {}
+
+         inline T value() const
+         {
+            return value_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_constant;
+         }
+
+         inline expression_node<T>* branch(const std::size_t&) const
+         {
+            return reinterpret_cast<expression_node<T>*>(0);
+         }
+
+      private:
+
+         literal_node(literal_node<T>&) {}
+         literal_node<T>& operator=(literal_node<T>&) { return (*this); }
+
+         const T value_;
+      };
+
+      template <typename T>
+      struct range_pack;
+
+      template <typename T>
+      struct range_data_type;
+
+      template <typename T>
+      class range_interface
+      {
+      public:
+
+         typedef range_pack<T> range_t;
+
+         virtual ~range_interface()
+         {}
+
+         virtual range_t& range_ref() = 0;
+
+         virtual const range_t& range_ref() const = 0;
+      };
+
+      #ifndef exprtk_disable_string_capabilities
+      template <typename T>
+      class string_base_node
+      {
+      public:
+
+         typedef range_data_type<T> range_data_type_t;
+
+         virtual ~string_base_node()
+         {}
+
+         virtual std::string str () const = 0;
+
+         virtual char_cptr   base() const = 0;
+
+         virtual std::size_t size() const = 0;
+      };
+
+      template <typename T>
+      class string_literal_node : public expression_node <T>,
+                                  public string_base_node<T>,
+                                  public range_interface <T>
+      {
+      public:
+
+         typedef range_pack<T> range_t;
+
+         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_.cache.first  = rp_.n0_c.second;
+            rp_.cache.second = rp_.n1_c.second;
+         }
+
+         inline T value() const
+         {
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_stringconst;
+         }
+
+         inline expression_node<T>* branch(const std::size_t&) const
+         {
+            return reinterpret_cast<expression_node<T>*>(0);
+         }
+
+         std::string str() const
+         {
+            return value_;
+         }
+
+         char_cptr base() const
+         {
+            return value_.data();
+         }
+
+         std::size_t size() const
+         {
+            return value_.size();
+         }
+
+         range_t& range_ref()
+         {
+            return rp_;
+         }
+
+         const range_t& range_ref() const
+         {
+            return rp_;
+         }
+
+      private:
+
+         string_literal_node(const string_literal_node<T>&);
+         string_literal_node<T>& operator=(const string_literal_node<T>&);
+
+         const std::string value_;
+         range_t rp_;
+      };
+      #endif
+
+      template <typename T>
+      class unary_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         unary_node(const operator_type& opr,
+                    expression_ptr brnch)
+         : operation_(opr),
+           branch_(brnch),
+           branch_deletable_(branch_deletable(branch_))
+         {}
+
+        ~unary_node()
+         {
+            if (branch_ && branch_deletable_)
+            {
+               destroy_node(branch_);
+            }
+         }
+
+         inline T value() const
+         {
+            const T arg = branch_->value();
+
+            return numeric::process<T>(operation_,arg);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_unary;
+         }
+
+         inline operator_type operation() const
+         {
+            return operation_;
+         }
+
+         inline expression_node<T>* branch(const std::size_t&) const
+         {
+            return branch_;
+         }
+
+         inline void release()
+         {
+            branch_deletable_ = false;
+         }
+
+      protected:
+
+         operator_type operation_;
+         expression_ptr branch_;
+         bool branch_deletable_;
+      };
+
+      template <typename T, std::size_t D, bool B>
+      struct construct_branch_pair
+      {
+         template <std::size_t N>
+         static inline void process(std::pair<expression_node<T>*,bool> (&)[N], expression_node<T>*)
+         {}
+      };
+
+      template <typename T, std::size_t D>
+      struct construct_branch_pair<T,D,true>
+      {
+         template <std::size_t N>
+         static inline void process(std::pair<expression_node<T>*,bool> (&branch)[N], expression_node<T>* b)
+         {
+            if (b)
+            {
+               branch[D] = std::make_pair(b,branch_deletable(b));
+            }
+         }
+      };
+
+      template <std::size_t N, typename T>
+      inline void init_branches(std::pair<expression_node<T>*,bool> (&branch)[N],
+                                expression_node<T>* b0,
+                                expression_node<T>* b1 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b2 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b3 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b4 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b5 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b6 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b7 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b8 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b9 = reinterpret_cast<expression_node<T>*>(0))
+      {
+         construct_branch_pair<T,0,(N > 0)>::process(branch,b0);
+         construct_branch_pair<T,1,(N > 1)>::process(branch,b1);
+         construct_branch_pair<T,2,(N > 2)>::process(branch,b2);
+         construct_branch_pair<T,3,(N > 3)>::process(branch,b3);
+         construct_branch_pair<T,4,(N > 4)>::process(branch,b4);
+         construct_branch_pair<T,5,(N > 5)>::process(branch,b5);
+         construct_branch_pair<T,6,(N > 6)>::process(branch,b6);
+         construct_branch_pair<T,7,(N > 7)>::process(branch,b7);
+         construct_branch_pair<T,8,(N > 8)>::process(branch,b8);
+         construct_branch_pair<T,9,(N > 9)>::process(branch,b9);
+      }
+
+      struct cleanup_branches
+      {
+         template <typename T, std::size_t N>
+         static inline void execute(std::pair<expression_node<T>*,bool> (&branch)[N])
+         {
+            for (std::size_t i = 0; i < N; ++i)
+            {
+               if (branch[i].first && branch[i].second)
+               {
+                  destroy_node(branch[i].first);
+               }
+            }
+         }
+
+         template <typename T,
+                   typename Allocator,
+                   template <typename, typename> class Sequence>
+         static inline void execute(Sequence<std::pair<expression_node<T>*,bool>,Allocator>& branch)
+         {
+            for (std::size_t i = 0; i < branch.size(); ++i)
+            {
+               if (branch[i].first && branch[i].second)
+               {
+                  destroy_node(branch[i].first);
+               }
+            }
+         }
+      };
+
+      template <typename T>
+      class binary_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         binary_node(const operator_type& opr,
+                     expression_ptr branch0,
+                     expression_ptr branch1)
+         : operation_(opr)
+         {
+            init_branches<2>(branch_, branch0, branch1);
+         }
+
+        ~binary_node()
+         {
+            cleanup_branches::execute<T,2>(branch_);
+         }
+
+         inline T value() const
+         {
+            const T arg0 = branch_[0].first->value();
+            const T arg1 = branch_[1].first->value();
+
+            return numeric::process<T>(operation_,arg0,arg1);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_binary;
+         }
+
+         inline operator_type operation()
+         {
+            return operation_;
+         }
+
+         inline expression_node<T>* branch(const std::size_t& index = 0) const
+         {
+            if (0 == index)
+               return branch_[0].first;
+            else if (1 == index)
+               return branch_[1].first;
+            else
+               return reinterpret_cast<expression_ptr>(0);
+         }
+
+      protected:
+
+         operator_type operation_;
+         branch_t branch_[2];
+      };
+
+      template <typename T, typename Operation>
+      class binary_ext_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         binary_ext_node(expression_ptr branch0, expression_ptr branch1)
+         {
+            init_branches<2>(branch_, branch0, branch1);
+         }
+
+        ~binary_ext_node()
+         {
+            cleanup_branches::execute<T,2>(branch_);
+         }
+
+         inline T value() const
+         {
+            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
+         {
+            return expression_node<T>::e_binary_ext;
+         }
+
+         inline operator_type operation()
+         {
+            return Operation::operation();
+         }
+
+         inline expression_node<T>* branch(const std::size_t& index = 0) const
+         {
+            if (0 == index)
+               return branch_[0].first;
+            else if (1 == index)
+               return branch_[1].first;
+            else
+               return reinterpret_cast<expression_ptr>(0);
+         }
+
+      protected:
+
+         branch_t branch_[2];
+      };
+
+      template <typename T>
+      class trinary_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         trinary_node(const operator_type& opr,
+                      expression_ptr branch0,
+                      expression_ptr branch1,
+                      expression_ptr branch2)
+         : operation_(opr)
+         {
+            init_branches<3>(branch_, branch0, branch1, branch2);
+         }
+
+        ~trinary_node()
+         {
+            cleanup_branches::execute<T,3>(branch_);
+         }
+
+         inline T value() const
+         {
+            const T arg0 = branch_[0].first->value();
+            const T arg1 = branch_[1].first->value();
+            const T arg2 = branch_[2].first->value();
+
+            switch (operation_)
+            {
+               case e_inrange : return (arg1 < arg0) ? T(0) : ((arg1 > arg2) ? T(0) : T(1));
+
+               case e_clamp   : return (arg1 < arg0) ? arg0 : (arg1 > arg2 ? arg2 : arg1);
+
+               case e_iclamp  : if ((arg1 <= arg0) || (arg1 >= arg2))
+                                   return arg1;
+                                else
+                                   return ((T(2) * arg1  <= (arg2 + arg0)) ? arg0 : arg2);
+
+               default        : exprtk_debug(("trinary_node::value() - Error: Invalid operation\n"));
+                                return std::numeric_limits<T>::quiet_NaN();
+            }
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_trinary;
+         }
+
+      protected:
+
+         operator_type operation_;
+         branch_t branch_[3];
+      };
+
+      template <typename T>
+      class quaternary_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         quaternary_node(const operator_type& opr,
+                         expression_ptr branch0,
+                         expression_ptr branch1,
+                         expression_ptr branch2,
+                         expression_ptr branch3)
+         : operation_(opr)
+         {
+            init_branches<4>(branch_, branch0, branch1, branch2, branch3);
+         }
+
+        ~quaternary_node()
+         {
+            cleanup_branches::execute<T,4>(branch_);
+         }
+
+         inline T value() const
+         {
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_quaternary;
+         }
+
+      protected:
+
+         operator_type operation_;
+         branch_t branch_[4];
+      };
+
+      template <typename T>
+      class conditional_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         conditional_node(expression_ptr test,
+                          expression_ptr consequent,
+                          expression_ptr alternative)
+         : test_(test),
+           consequent_(consequent),
+           alternative_(alternative),
+           test_deletable_(branch_deletable(test_)),
+           consequent_deletable_(branch_deletable(consequent_)),
+           alternative_deletable_(branch_deletable(alternative_))
+         {}
+
+        ~conditional_node()
+         {
+            if (test_ && test_deletable_)
+            {
+               destroy_node(test_);
+            }
+
+            if (consequent_ && consequent_deletable_ )
+            {
+               destroy_node(consequent_);
+            }
+
+            if (alternative_ && alternative_deletable_)
+            {
+               destroy_node(alternative_);
+            }
+         }
+
+         inline T value() const
+         {
+            if (is_true(test_))
+               return consequent_->value();
+            else
+               return alternative_->value();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_conditional;
+         }
+
+      private:
+
+         expression_ptr test_;
+         expression_ptr consequent_;
+         expression_ptr alternative_;
+         const bool test_deletable_;
+         const bool consequent_deletable_;
+         const bool alternative_deletable_;
+      };
+
+      template <typename T>
+      class cons_conditional_node : public expression_node<T>
+      {
+      public:
+
+         // Consequent only conditional statement node
+         typedef expression_node<T>* expression_ptr;
+
+         cons_conditional_node(expression_ptr test,
+                               expression_ptr consequent)
+         : test_(test),
+           consequent_(consequent),
+           test_deletable_(branch_deletable(test_)),
+           consequent_deletable_(branch_deletable(consequent_))
+         {}
+
+        ~cons_conditional_node()
+         {
+            if (test_ && test_deletable_)
+            {
+               destroy_node(test_);
+            }
+
+            if (consequent_ && consequent_deletable_)
+            {
+               destroy_node(consequent_);
+            }
+         }
+
+         inline T value() const
+         {
+            if (is_true(test_))
+               return consequent_->value();
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_conditional;
+         }
+
+      private:
+
+         expression_ptr test_;
+         expression_ptr consequent_;
+         const bool test_deletable_;
+         const bool consequent_deletable_;
+      };
+
+      #ifndef exprtk_disable_break_continue
+      template <typename T>
+      class break_exception
+      {
+      public:
+
+         break_exception(const T& v)
+         : value(v)
+         {}
+
+         T value;
+      };
+
+      class continue_exception
+      {};
+
+      template <typename T>
+      class break_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         break_node(expression_ptr ret = expression_ptr(0))
+         : return_(ret),
+           return_deletable_(branch_deletable(return_))
+         {}
+
+        ~break_node()
+         {
+            if (return_deletable_)
+            {
+               destroy_node(return_);
+            }
+         }
+
+         inline T value() const
+         {
+            throw break_exception<T>(return_ ? return_->value() : std::numeric_limits<T>::quiet_NaN());
+            #ifndef _MSC_VER
+            return std::numeric_limits<T>::quiet_NaN();
+            #endif
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_break;
+         }
+
+      private:
+
+         expression_ptr return_;
+         const bool return_deletable_;
+      };
+
+      template <typename T>
+      class continue_node : public expression_node<T>
+      {
+      public:
+
+         inline T value() const
+         {
+            throw continue_exception();
+            #ifndef _MSC_VER
+            return std::numeric_limits<T>::quiet_NaN();
+            #endif
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_break;
+         }
+      };
+      #endif
+
+      template <typename T>
+      class while_loop_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         while_loop_node(expression_ptr condition, expression_ptr loop_body)
+         : condition_(condition),
+           loop_body_(loop_body),
+           condition_deletable_(branch_deletable(condition_)),
+           loop_body_deletable_(branch_deletable(loop_body_))
+         {}
+
+        ~while_loop_node()
+         {
+            if (condition_ && condition_deletable_)
+            {
+               destroy_node(condition_);
+            }
+
+            if (loop_body_ && loop_body_deletable_)
+            {
+               destroy_node(loop_body_);
+            }
+         }
+
+         inline T value() const
+         {
+            T result = T(0);
+
+            while (is_true(condition_))
+            {
+               result = loop_body_->value();
+            }
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_while;
+         }
+
+      private:
+
+         expression_ptr condition_;
+         expression_ptr loop_body_;
+         const bool condition_deletable_;
+         const bool loop_body_deletable_;
+      };
+
+      template <typename T>
+      class repeat_until_loop_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         repeat_until_loop_node(expression_ptr condition, expression_ptr loop_body)
+         : condition_(condition),
+           loop_body_(loop_body),
+           condition_deletable_(branch_deletable(condition_)),
+           loop_body_deletable_(branch_deletable(loop_body_))
+         {}
+
+        ~repeat_until_loop_node()
+         {
+            if (condition_ && condition_deletable_)
+            {
+               destroy_node(condition_);
+            }
+
+            if (loop_body_ && loop_body_deletable_)
+            {
+               destroy_node(loop_body_);
+            }
+         }
+
+         inline T value() const
+         {
+            T result = T(0);
+
+            do
+            {
+               result = loop_body_->value();
+            }
+            while (is_false(condition_));
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_repeat;
+         }
+
+      private:
+
+         expression_ptr condition_;
+         expression_ptr loop_body_;
+         const bool condition_deletable_;
+         const bool loop_body_deletable_;
+      };
+
+      template <typename T>
+      class for_loop_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         for_loop_node(expression_ptr initialiser,
+                       expression_ptr condition,
+                       expression_ptr incrementor,
+                       expression_ptr loop_body)
+         : initialiser_(initialiser),
+           condition_  (condition  ),
+           incrementor_(incrementor),
+           loop_body_  (loop_body  ),
+           initialiser_deletable_(branch_deletable(initialiser_)),
+           condition_deletable_  (branch_deletable(condition_  )),
+           incrementor_deletable_(branch_deletable(incrementor_)),
+           loop_body_deletable_  (branch_deletable(loop_body_  ))
+         {}
+
+        ~for_loop_node()
+         {
+            if (initialiser_ && initialiser_deletable_)
+            {
+               destroy_node(initialiser_);
+            }
+
+            if (condition_ && condition_deletable_)
+            {
+               destroy_node(condition_);
+            }
+
+            if (incrementor_ && incrementor_deletable_)
+            {
+               destroy_node(incrementor_);
+            }
+
+            if (loop_body_ && loop_body_deletable_)
+            {
+               destroy_node(loop_body_);
+            }
+         }
+
+         inline T value() const
+         {
+            T result = T(0);
+
+            if (initialiser_)
+               initialiser_->value();
+
+            if (incrementor_)
+            {
+               while (is_true(condition_))
+               {
+                  result = loop_body_->value();
+                  incrementor_->value();
+               }
+            }
+            else
+            {
+               while (is_true(condition_))
+               {
+                  result = loop_body_->value();
+               }
+            }
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_for;
+         }
+
+      private:
+
+         expression_ptr initialiser_      ;
+         expression_ptr condition_        ;
+         expression_ptr incrementor_      ;
+         expression_ptr loop_body_        ;
+         const bool initialiser_deletable_;
+         const bool condition_deletable_  ;
+         const bool incrementor_deletable_;
+         const bool loop_body_deletable_  ;
+      };
+
+      #ifndef exprtk_disable_break_continue
+      template <typename T>
+      class while_loop_bc_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         while_loop_bc_node(expression_ptr condition, expression_ptr loop_body)
+         : condition_(condition),
+           loop_body_(loop_body),
+           condition_deletable_(branch_deletable(condition_)),
+           loop_body_deletable_(branch_deletable(loop_body_))
+         {}
+
+        ~while_loop_bc_node()
+         {
+            if (condition_ && condition_deletable_)
+            {
+               destroy_node(condition_);
+            }
+
+            if (loop_body_ && loop_body_deletable_)
+            {
+               destroy_node(loop_body_);
+            }
+         }
+
+         inline T value() const
+         {
+            T result = T(0);
+
+            while (is_true(condition_))
+            {
+               try
+               {
+                  result = loop_body_->value();
+               }
+               catch(const break_exception<T>& e)
+               {
+                  return e.value;
+               }
+               catch(const continue_exception&)
+               {}
+            }
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_while;
+         }
+
+      private:
+
+         expression_ptr condition_;
+         expression_ptr loop_body_;
+         const bool condition_deletable_;
+         const bool loop_body_deletable_;
+      };
+
+      template <typename T>
+      class repeat_until_loop_bc_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         repeat_until_loop_bc_node(expression_ptr condition, expression_ptr loop_body)
+         : condition_(condition),
+           loop_body_(loop_body),
+           condition_deletable_(branch_deletable(condition_)),
+           loop_body_deletable_(branch_deletable(loop_body_))
+         {}
+
+        ~repeat_until_loop_bc_node()
+         {
+            if (condition_ && condition_deletable_)
+            {
+               destroy_node(condition_);
+            }
+
+            if (loop_body_ && loop_body_deletable_)
+            {
+               destroy_node(loop_body_);
+            }
+         }
+
+         inline T value() const
+         {
+            T result = T(0);
+
+            do
+            {
+               try
+               {
+                  result = loop_body_->value();
+               }
+               catch(const break_exception<T>& e)
+               {
+                  return e.value;
+               }
+               catch(const continue_exception&)
+               {}
+            }
+            while (is_false(condition_));
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_repeat;
+         }
+
+      private:
+
+         expression_ptr condition_;
+         expression_ptr loop_body_;
+         const bool condition_deletable_;
+         const bool loop_body_deletable_;
+      };
+
+      template <typename T>
+      class for_loop_bc_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         for_loop_bc_node(expression_ptr initialiser,
+                       expression_ptr condition,
+                       expression_ptr incrementor,
+                       expression_ptr loop_body)
+         : initialiser_(initialiser),
+           condition_  (condition  ),
+           incrementor_(incrementor),
+           loop_body_  (loop_body  ),
+           initialiser_deletable_(branch_deletable(initialiser_)),
+           condition_deletable_  (branch_deletable(condition_  )),
+           incrementor_deletable_(branch_deletable(incrementor_)),
+           loop_body_deletable_  (branch_deletable(loop_body_  ))
+         {}
+
+        ~for_loop_bc_node()
+         {
+            if (initialiser_ && initialiser_deletable_)
+            {
+               destroy_node(initialiser_);
+            }
+
+            if (condition_ && condition_deletable_)
+            {
+               destroy_node(condition_);
+            }
+
+            if (incrementor_ && incrementor_deletable_)
+            {
+               destroy_node(incrementor_);
+            }
+
+            if (loop_body_ && loop_body_deletable_)
+            {
+               destroy_node(loop_body_);
+            }
+         }
+
+         inline T value() const
+         {
+            T result = T(0);
+
+            if (initialiser_)
+               initialiser_->value();
+
+            if (incrementor_)
+            {
+               while (is_true(condition_))
+               {
+                  try
+                  {
+                     result = loop_body_->value();
+                  }
+                  catch(const break_exception<T>& e)
+                  {
+                     return e.value;
+                  }
+                  catch(const continue_exception&)
+                  {}
+
+                  incrementor_->value();
+               }
+            }
+            else
+            {
+               while (is_true(condition_))
+               {
+                  try
+                  {
+                     result = loop_body_->value();
+                  }
+                  catch(const break_exception<T>& e)
+                  {
+                     return e.value;
+                  }
+                  catch(const continue_exception&)
+                  {}
+               }
+            }
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_for;
+         }
+
+      private:
+
+         expression_ptr initialiser_;
+         expression_ptr condition_  ;
+         expression_ptr incrementor_;
+         expression_ptr loop_body_  ;
+         const bool initialiser_deletable_;
+         const bool condition_deletable_  ;
+         const bool incrementor_deletable_;
+         const bool loop_body_deletable_  ;
+      };
+      #endif
+
+      template <typename T>
+      class switch_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         explicit switch_node(const Sequence<expression_ptr,Allocator>& arg_list)
+         {
+            if (1 != (arg_list.size() & 1))
+               return;
+
+            arg_list_.resize(arg_list.size());
+            delete_branch_.resize(arg_list.size());
+
+            for (std::size_t i = 0; i < arg_list.size(); ++i)
+            {
+               if (arg_list[i])
+               {
+                       arg_list_[i] = arg_list[i];
+                  delete_branch_[i] = static_cast<unsigned char>(branch_deletable(arg_list_[i]) ? 1 : 0);
+               }
+               else
+               {
+                  arg_list_.clear();
+                  delete_branch_.clear();
+                  return;
+               }
+            }
+         }
+
+        ~switch_node()
+         {
+            for (std::size_t i = 0; i < arg_list_.size(); ++i)
+            {
+               if (arg_list_[i] && delete_branch_[i])
+               {
+                  destroy_node(arg_list_[i]);
+               }
+            }
+         }
+
+         inline T value() const
+         {
+            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)
+               {
+                  expression_ptr condition  = arg_list_[i    ];
+                  expression_ptr consequent = arg_list_[i + 1];
+
+                  if (is_true(condition))
+                  {
+                     return consequent->value();
+                  }
+               }
+
+               return arg_list_[upper_bound]->value();
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_switch;
+         }
+
+      protected:
+
+         std::vector<expression_ptr> arg_list_;
+         std::vector<unsigned char> delete_branch_;
+      };
+
+      template <typename T, typename Switch_N>
+      class switch_n_node : public switch_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         explicit switch_n_node(const Sequence<expression_ptr,Allocator>& arg_list)
+         : switch_node<T>(arg_list)
+         {}
+
+         inline T value() const
+         {
+            return Switch_N::process(switch_node<T>::arg_list_);
+         }
+      };
+
+      template <typename T>
+      class multi_switch_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         explicit multi_switch_node(const Sequence<expression_ptr,Allocator>& arg_list)
+         {
+            if (0 != (arg_list.size() & 1))
+               return;
+
+            arg_list_.resize(arg_list.size());
+            delete_branch_.resize(arg_list.size());
+
+            for (std::size_t i = 0; i < arg_list.size(); ++i)
+            {
+               if (arg_list[i])
+               {
+                       arg_list_[i] = arg_list[i];
+                  delete_branch_[i] = static_cast<unsigned char>(branch_deletable(arg_list_[i]) ? 1 : 0);
+               }
+               else
+               {
+                  arg_list_.clear();
+                  delete_branch_.clear();
+                  return;
+               }
+            }
+         }
+
+        ~multi_switch_node()
+         {
+            for (std::size_t i = 0; i < arg_list_.size(); ++i)
+            {
+               if (arg_list_[i] && delete_branch_[i])
+               {
+                  destroy_node(arg_list_[i]);
+               }
+            }
+         }
+
+         inline T value() const
+         {
+            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);
+
+            for (std::size_t i = 0; i < upper_bound; i += 2)
+            {
+               expression_ptr condition  = arg_list_[i    ];
+               expression_ptr consequent = arg_list_[i + 1];
+
+               if (is_true(condition))
+               {
+                  result = consequent->value();
+               }
+            }
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_mswitch;
+         }
+
+      private:
+
+         std::vector<expression_ptr> arg_list_;
+         std::vector<unsigned char> delete_branch_;
+      };
+
+      template <typename T>
+      class ivariable
+      {
+      public:
+
+         virtual ~ivariable()
+         {}
+
+         virtual T& ref() = 0;
+         virtual const T& ref() const = 0;
+      };
+
+      template <typename T>
+      class variable_node : public expression_node<T>,
+                            public ivariable      <T>
+      {
+      public:
+
+         static T null_value;
+
+         explicit variable_node()
+         : value_(&null_value)
+         {}
+
+         explicit variable_node(T& v)
+         : value_(&v)
+         {}
+
+         inline bool operator <(const variable_node<T>& v) const
+         {
+            return this < (&v);
+         }
+
+         inline T value() const
+         {
+            return (*value_);
+         }
+
+         inline T& ref()
+         {
+            return (*value_);
+         }
+
+         inline const T& ref() const
+         {
+            return (*value_);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_variable;
+         }
+
+      private:
+
+         T* value_;
+      };
+
+      template <typename T>
+      T variable_node<T>::null_value = T(std::numeric_limits<T>::quiet_NaN());
+
+      template <typename T>
+      struct range_pack
+      {
+         typedef expression_node<T>*           expression_node_ptr;
+         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))
+         {}
+
+         void clear()
+         {
+            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 free()
+         {
+            if (n0_e.first && n0_e.second)
+            {
+               n0_e.first = false;
+
+               if (
+                    !is_variable_node(n0_e.second) &&
+                    !is_string_node  (n0_e.second)
+                  )
+               {
+                  destroy_node(n0_e.second);
+               }
+            }
+
+            if (n1_e.first && n1_e.second)
+            {
+               n1_e.first = false;
+
+               if (
+                    !is_variable_node(n1_e.second) &&
+                    !is_string_node  (n1_e.second)
+                  )
+               {
+                  destroy_node(n1_e.second);
+               }
+            }
+         }
+
+         bool const_range()
+         {
+           return ( n0_c.first &&  n1_c.first) &&
+                  (!n0_e.first && !n1_e.first);
+         }
+
+         bool var_range()
+         {
+           return ( n0_e.first &&  n1_e.first) &&
+                  (!n0_c.first && !n1_c.first);
+         }
+
+         bool operator() (std::size_t& r0, std::size_t& r1,
+                          const std::size_t& size = std::numeric_limits<std::size_t>::max()) const
+         {
+            if (n0_c.first)
+               r0 = n0_c.second;
+            else if (n0_e.first)
+            {
+               const T r0_value = n0_e.second->value();
+
+               if (r0_value < 0)
+                  return false;
+               else
+                  r0 = static_cast<std::size_t>(details::numeric::to_int64(r0_value));
+            }
+            else
+               return false;
+
+            if (n1_c.first)
+               r1 = n1_c.second;
+            else if (n1_e.first)
+            {
+               const T r1_value = n1_e.second->value();
+
+               if (r1_value < 0)
+                  return false;
+               else
+                  r1 = static_cast<std::size_t>(details::numeric::to_int64(r1_value));
+            }
+            else
+               return false;
+
+            if (
+                 (std::numeric_limits<std::size_t>::max() != size) &&
+                 (std::numeric_limits<std::size_t>::max() == r1  )
+               )
+            {
+               r1 = size - 1;
+            }
+
+            cache.first  = r0;
+            cache.second = r1;
+
+            return (r0 <= r1);
+         }
+
+         inline std::size_t const_size() const
+         {
+            return (n1_c.second - n0_c.second + 1);
+         }
+
+         inline std::size_t cache_size() const
+         {
+            return (cache.second - cache.first + 1);
+         }
+
+         std::pair<bool,expression_node_ptr> n0_e;
+         std::pair<bool,expression_node_ptr> n1_e;
+         std::pair<bool,std::size_t        > n0_c;
+         std::pair<bool,std::size_t        > n1_c;
+         mutable cached_range_t             cache;
+      };
+
+      template <typename T>
+      class string_base_node;
+
+      template <typename T>
+      struct range_data_type
+      {
+         typedef range_pack<T> range_t;
+         typedef string_base_node<T>* strbase_ptr_t;
+
+         range_data_type()
+         : range(0),
+           data (0),
+           size (0),
+           type_size(0),
+           str_node (0)
+         {}
+
+         range_t*      range;
+         void*         data;
+         std::size_t   size;
+         std::size_t   type_size;
+         strbase_ptr_t str_node;
+      };
+
+      template <typename T> class vector_node;
+
+      template <typename T>
+      class vector_interface
+      {
+      public:
+
+         typedef vector_node<T>*   vector_node_ptr;
+         typedef vec_data_store<T>           vds_t;
+
+         virtual ~vector_interface()
+         {}
+
+         virtual std::size_t size   () const = 0;
+
+         virtual vector_node_ptr vec() const = 0;
+
+         virtual vector_node_ptr vec()       = 0;
+
+         virtual       vds_t& vds   ()       = 0;
+
+         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>
+      {
+      public:
+
+         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;
+
+         explicit vector_node(vector_holder_t* vh)
+         : 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)
+         {}
+
+         inline T value() const
+         {
+            return vds().data()[0];
+         }
+
+         vector_node_ptr vec() const
+         {
+            return const_cast<vector_node_ptr>(this);
+         }
+
+         vector_node_ptr vec()
+         {
+            return this;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vector;
+         }
+
+         std::size_t size() const
+         {
+            return vds().size();
+         }
+
+         vds_t& vds()
+         {
+            return vds_;
+         }
+
+         const vds_t& vds() const
+         {
+            return vds_;
+         }
+
+         inline vector_holder_t& vec_holder()
+         {
+            return (*vector_holder_);
+         }
+
+      private:
+
+         vector_holder_t* vector_holder_;
+         vds_t                      vds_;
+      };
+
+      template <typename T>
+      class vector_elem_node : 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;
+
+         vector_elem_node(expression_ptr index, vector_holder_ptr vec_holder)
+         : index_(index),
+           vec_holder_(vec_holder),
+           vector_base_((*vec_holder)[0]),
+           index_deletable_(branch_deletable(index_))
+         {}
+
+        ~vector_elem_node()
+         {
+            if (index_ && index_deletable_)
+            {
+               destroy_node(index_);
+            }
+         }
+
+         inline T value() const
+         {
+            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_->value())));
+         }
+
+         inline T& ref()
+         {
+            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_->value())));
+         }
+
+         inline const T& ref() const
+         {
+            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_->value())));
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vecelem;
+         }
+
+         inline vector_holder_t& vec_holder()
+         {
+            return (*vec_holder_);
+         }
+
+      private:
+
+         expression_ptr index_;
+         vector_holder_ptr vec_holder_;
+         T* vector_base_;
+         const bool index_deletable_;
+      };
+
+      template <typename T>
+      class rebasevector_elem_node : 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;
+
+         rebasevector_elem_node(expression_ptr index, vector_holder_ptr vec_holder)
+         : index_(index),
+           index_deletable_(branch_deletable(index_)),
+           vector_holder_(vec_holder),
+           vds_((*vector_holder_).size(),(*vector_holder_)[0])
+         {
+            vector_holder_->set_ref(&vds_.ref());
+         }
+
+        ~rebasevector_elem_node()
+         {
+            if (index_ && index_deletable_)
+            {
+               destroy_node(index_);
+            }
+         }
+
+         inline T value() const
+         {
+            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_->value())));
+         }
+
+         inline T& ref()
+         {
+            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_->value())));
+         }
+
+         inline const T& ref() const
+         {
+            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_->value())));
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_rbvecelem;
+         }
+
+         inline vector_holder_t& vec_holder()
+         {
+            return (*vector_holder_);
+         }
+
+      private:
+
+         expression_ptr index_;
+         const bool index_deletable_;
+         vector_holder_ptr vector_holder_;
+         vds_t             vds_;
+      };
+
+      template <typename T>
+      class rebasevector_celem_node : 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;
+
+         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_holder_->set_ref(&vds_.ref());
+         }
+
+         inline T value() const
+         {
+            return *(vds_.data() + index_);
+         }
+
+         inline T& ref()
+         {
+            return *(vds_.data() + index_);
+         }
+
+         inline const T& ref() const
+         {
+            return *(vds_.data() + index_);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_rbveccelem;
+         }
+
+         inline vector_holder_t& vec_holder()
+         {
+            return (*vector_holder_);
+         }
+
+      private:
+
+         const std::size_t index_;
+         vector_holder_ptr vector_holder_;
+         vds_t vds_;
+      };
+
+      template <typename T>
+      class vector_assignment_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         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_assignment_node()
+         {
+            for (std::size_t i = 0; i < initialiser_list_.size(); ++i)
+            {
+               if (branch_deletable(initialiser_list_[i]))
+               {
+                  destroy_node(initialiser_list_[i]);
+               }
+            }
+         }
+
+         inline T value() const
+         {
+            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();
+
+               for (std::size_t i = 0; i < il_size; ++i)
+               {
+                  *(vector_base_ + i) = initialiser_list_[i]->value();
+               }
+
+               if (il_size < size_)
+               {
+                  for (std::size_t i = il_size; i < size_; ++i)
+                  {
+                     *(vector_base_ + i) = T(0);
+                  }
+               }
+            }
+
+            return *(vector_base_);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vecdefass;
+         }
+
+      private:
+
+         vector_assignment_node<T>& operator=(const vector_assignment_node<T>&);
+
+         mutable T* vector_base_;
+         std::vector<expression_ptr> initialiser_list_;
+         const std::size_t size_;
+         const bool single_value_initialse_;
+      };
+
+      template <typename T>
+      class swap_node : 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
+         {
+            std::swap(var0_->ref(),var1_->ref());
+            return var1_->ref();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_swap;
+         }
+
+      private:
+
+         variable_node_ptr var0_;
+         variable_node_ptr var1_;
+      };
+
+      template <typename T>
+      class swap_generic_node : 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
+         {
+            std::swap(var0_->ref(),var1_->ref());
+            return var1_->ref();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_swap;
+         }
+
+      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)
+         {
+            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();
+               }
+            }
+
+            if (is_ivector_node(binary_node<T>::branch_[1].first))
+            {
+               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();
+               }
+            }
+
+            if (vec0_node_ptr_ && vec1_node_ptr_)
+            {
+               vec_size_ = std::min(vec0_node_ptr_->vds().size(),
+                                    vec1_node_ptr_->vds().size());
+
+               initialised_ = true;
+            }
+         }
+
+         inline T value() const
+         {
+            if (initialised_)
+            {
+               binary_node<T>::branch_[0].first->value();
+               binary_node<T>::branch_[1].first->value();
+
+               T* vec0 = vec0_node_ptr_->vds().data();
+               T* vec1 = vec1_node_ptr_->vds().data();
+
+               for (std::size_t i = 0; i < vec_size_; ++i)
+               {
+                  std::swap(vec0[i],vec1[i]);
+               }
+
+               return vec1_node_ptr_->value();
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         vector_node_ptr vec() const
+         {
+            return vec0_node_ptr_;
+         }
+
+         vector_node_ptr vec()
+         {
+            return vec0_node_ptr_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vecvecswap;
+         }
+
+         std::size_t size() const
+         {
+            return vec_size_;
+         }
+
+         vds_t& vds()
+         {
+            return vds_;
+         }
+
+         const vds_t& vds() const
+         {
+            return vds_;
+         }
+
+      private:
+
+         vector_node<T>* vec0_node_ptr_;
+         vector_node<T>* vec1_node_ptr_;
+         std::size_t     vec_size_;
+         bool            initialised_;