]> sigrok.org Git - pulseview.git/commitdiff
Session: Fix issue #67 by improving error handling master github/master
authorSoeren Apel <redacted>
Thu, 14 Mar 2024 20:58:47 +0000 (21:58 +0100)
committerSoeren Apel <redacted>
Thu, 14 Mar 2024 20:59:45 +0000 (21:59 +0100)
164 files changed:
CMakeLists.txt
INSTALL
README
config.h.in
contrib/pulseview_cross.nsi.in
doc/pulseview.1
icons/add-math-signal.svg [new file with mode: 0644]
icons/math.svg [new file with mode: 0644]
l10n/de.ts
l10n/es_MX.ts [new file with mode: 0644]
l10n/ja_jp.ts [new file with mode: 0644]
l10n/zh_cn.ts [new file with mode: 0644]
main.cpp
manual/CMakeLists.txt
manual/analysis.txt
manual/decoders.txt
manual/images/pv_ann_export_format.png [new file with mode: 0644]
manual/images/pv_ann_export_menu.png [new file with mode: 0644]
manual/images/pv_binary_decoder_output_view.png [new file with mode: 0644]
manual/images/pv_binary_decoder_output_view_i2s.png [new file with mode: 0644]
manual/images/pv_class_selectors.png [new file with mode: 0644]
manual/images/pv_marker_deltas.png [new file with mode: 0644]
manual/manual.txt
manual/overview.txt
pulseview.qrc
pv/application.cpp
pv/application.hpp
pv/binding/device.cpp
pv/binding/device.hpp
pv/binding/inputoutput.cpp
pv/binding/inputoutput.hpp
pv/data/analog.cpp
pv/data/analog.hpp
pv/data/analogsegment.cpp
pv/data/analogsegment.hpp
pv/data/decode/annotation.cpp
pv/data/decode/annotation.hpp
pv/data/decode/decoder.cpp
pv/data/decode/decoder.hpp
pv/data/decode/row.cpp
pv/data/decode/row.hpp
pv/data/decode/rowdata.cpp
pv/data/decode/rowdata.hpp
pv/data/decodesignal.cpp
pv/data/decodesignal.hpp
pv/data/logic.cpp
pv/data/logic.hpp
pv/data/logicsegment.cpp
pv/data/logicsegment.hpp
pv/data/mathsignal.cpp [new file with mode: 0644]
pv/data/mathsignal.hpp [new file with mode: 0644]
pv/data/segment.cpp
pv/data/segment.hpp
pv/data/signalbase.cpp
pv/data/signalbase.hpp
pv/data/signaldata.hpp
pv/devices/inputfile.cpp
pv/devices/inputfile.hpp
pv/dialogs/connect.cpp
pv/dialogs/connect.hpp
pv/dialogs/inputoutputoptions.hpp
pv/dialogs/settings.cpp
pv/dialogs/settings.hpp
pv/dialogs/storeprogress.cpp
pv/dialogs/storeprogress.hpp
pv/exprtk.hpp [new file with mode: 0644]
pv/globalsettings.cpp
pv/globalsettings.hpp
pv/logging.cpp
pv/mainwindow.cpp
pv/mainwindow.hpp
pv/metadata_obj.cpp [new file with mode: 0644]
pv/metadata_obj.hpp [new file with mode: 0644]
pv/popups/channels.cpp
pv/popups/channels.hpp
pv/prop/bool.hpp
pv/prop/enum.cpp
pv/prop/enum.hpp
pv/prop/int.cpp
pv/prop/int.hpp
pv/prop/property.hpp
pv/prop/string.hpp
pv/session.cpp
pv/session.hpp
pv/storesession.cpp
pv/subwindows/decoder_selector/model.cpp
pv/subwindows/decoder_selector/subwindow.cpp
pv/subwindows/decoder_selector/subwindow.hpp
pv/subwindows/subwindowbase.hpp
pv/toolbars/mainbar.cpp
pv/toolbars/mainbar.hpp
pv/util.cpp
pv/util.hpp
pv/views/decoder_binary/QHexView.cpp [new file with mode: 0644]
pv/views/decoder_binary/QHexView.hpp [new file with mode: 0644]
pv/views/decoder_binary/view.cpp [new file with mode: 0644]
pv/views/decoder_binary/view.hpp [new file with mode: 0644]
pv/views/decoder_output/QHexView.cpp [deleted file]
pv/views/decoder_output/QHexView.hpp [deleted file]
pv/views/decoder_output/view.cpp [deleted file]
pv/views/decoder_output/view.hpp [deleted file]
pv/views/tabular_decoder/model.cpp [new file with mode: 0644]
pv/views/tabular_decoder/view.cpp [new file with mode: 0644]
pv/views/tabular_decoder/view.hpp [new file with mode: 0644]
pv/views/trace/analogsignal.cpp
pv/views/trace/analogsignal.hpp
pv/views/trace/cursor.cpp
pv/views/trace/cursor.hpp
pv/views/trace/cursorpair.cpp
pv/views/trace/cursorpair.hpp
pv/views/trace/decodetrace.cpp
pv/views/trace/decodetrace.hpp
pv/views/trace/flag.cpp
pv/views/trace/flag.hpp
pv/views/trace/header.hpp
pv/views/trace/logicsignal.cpp
pv/views/trace/logicsignal.hpp
pv/views/trace/marginwidget.cpp
pv/views/trace/marginwidget.hpp
pv/views/trace/mathsignal.cpp [new file with mode: 0644]
pv/views/trace/mathsignal.hpp [new file with mode: 0644]
pv/views/trace/ruler.cpp
pv/views/trace/ruler.hpp
pv/views/trace/signal.cpp
pv/views/trace/signal.hpp
pv/views/trace/timeitem.hpp
pv/views/trace/timemarker.cpp
pv/views/trace/timemarker.hpp
pv/views/trace/trace.cpp
pv/views/trace/trace.hpp
pv/views/trace/tracegroup.cpp
pv/views/trace/tracegroup.hpp
pv/views/trace/tracepalette.hpp
pv/views/trace/tracetreeitem.hpp
pv/views/trace/tracetreeitemowner.hpp
pv/views/trace/triggermarker.hpp
pv/views/trace/view.cpp
pv/views/trace/view.hpp
pv/views/trace/viewitem.hpp
pv/views/trace/viewitemiterator.hpp
pv/views/trace/viewitemowner.hpp
pv/views/trace/viewitempaintparams.hpp
pv/views/trace/viewport.cpp
pv/views/trace/viewport.hpp
pv/views/trace/viewwidget.hpp
pv/views/viewbase.cpp
pv/views/viewbase.hpp
pv/widgets/decodermenu.cpp
pv/widgets/devicetoolbutton.cpp
pv/widgets/exportmenu.cpp
pv/widgets/flowlayout.cpp
pv/widgets/flowlayout.hpp
pv/widgets/importmenu.cpp
pv/widgets/popup.cpp
pv/widgets/sweeptimingwidget.cpp
pv/widgets/timestampspinbox.cpp
pv/widgets/timestampspinbox.hpp
pv/widgets/wellarray.cpp
signalhandler.cpp
signalhandler.hpp
test/CMakeLists.txt
test/test.hpp
test/view/ruler.cpp
translations.qrc

index 5330663dbf755100dfb99c42296e14a9681d8a43..ec86073dcf956eb97fc03345f95e0b95abbcca70 100644 (file)
@@ -21,9 +21,9 @@
 
 cmake_minimum_required(VERSION 2.8.12)
 
-include(GNUInstallDirs)
+project(pulseview C CXX)
 
-project(pulseview)
+include(GNUInstallDirs)
 
 # Let AUTOMOC and AUTOUIC process GENERATED files.
 if(POLICY CMP0071)
@@ -48,6 +48,7 @@ option(ENABLE_DECODE "Build with libsigrokdecode" TRUE)
 option(ENABLE_FLOW "Build with libsigrokflow" FALSE)
 option(ENABLE_TESTS "Enable unit tests" FALSE)
 option(STATIC_PKGDEPS_LIBS "Statically link to (pkg-config) libraries" FALSE)
+option(ENABLE_TS_UPDATE "Update .ts source files (Qt l10n)" FALSE)
 
 if(WIN32)
        # On Windows/MinGW we need to statically link to libraries.
@@ -74,15 +75,66 @@ add_subdirectory(manual)
 #= Dependencies
 #-------------------------------------------------------------------------------
 
+include(CheckCSourceCompiles)
+include(CheckCXXCompilerFlag)
+include(CheckCXXSourceCompiles)
+include(CMakePushCheckState)
+include(memaccess)
+
+find_package(PkgConfig)
+
+if(CMAKE_VERSION VERSION_EQUAL "3.8.0" OR CMAKE_VERSION VERSION_GREATER "3.8.0")
+       check_cxx_compiler_flag("-std=c++17" HAVE_STD_CXX_17)
+       check_cxx_compiler_flag("-std=c++14" HAVE_STD_CXX_14)
+       check_cxx_compiler_flag("-std=c++11" HAVE_STD_CXX_11)
+       if(HAVE_STD_CXX_17)
+               message(STATUS "Using C++17 for the application build")
+               set(CMAKE_CXX_STANDARD 17)
+               set(REQUIRED_STD_CXX_FLAGS "-std=c++17")
+       elseif(HAVE_STD_CXX_14)
+               message(STATUS "Using C++14 for the application build")
+               set(CMAKE_CXX_STANDARD 14)
+               set(REQUIRED_STD_CXX_FLAGS "-std=c++14")
+       elseif(HAVE_STD_CXX_11)
+               message(STATUS "Using C++11 for the application build")
+               set(CMAKE_CXX_STANDARD 11)
+               set(REQUIRED_STD_CXX_FLAGS "-std=c++11")
+       else()
+               message(FATAL_ERROR "Need modern C++, at least language standard 11")
+       endif()
+else()
+       check_cxx_compiler_flag("-std=c++14" HAVE_STD_CXX_14)
+       check_cxx_compiler_flag("-std=c++11" HAVE_STD_CXX_11)
+       if(HAVE_STD_CXX_14)
+               message(STATUS "Using C++14 for the application build")
+               set(CMAKE_CXX_STANDARD 14)
+               set(REQUIRED_STD_CXX_FLAGS "-std=c++14")
+       elseif(HAVE_STD_CXX_11)
+               message(STATUS "Using C++11 for the application build")
+               set(CMAKE_CXX_STANDARD 11)
+               set(REQUIRED_STD_CXX_FLAGS "-std=c++11")
+       else()
+               message(FATAL_ERROR "Need modern C++, at least language standard 11")
+       endif()
+endif()
+
 list(APPEND PKGDEPS glib-2.0>=2.28.0)
-list(APPEND PKGDEPS glibmm-2.4>=2.28.0)
+
+# Try to find the prefered glibmm-2.4. If not found then add glibmm-2.68
+# to the dependency list.
+pkg_check_modules(GLIBMM_2_4 glibmm-2.4>=2.28.0)
+if(GLIBMM_2_4_FOUND)
+       list(APPEND PKGDEPS glibmm-2.4>=2.28.0)
+else()
+       list(APPEND PKGDEPS glibmm-2.68>=2.68.0)
+endif()
 
 if(ENABLE_FLOW)
        list(APPEND PKGDEPS gstreamermm-1.0>=1.8.0)
        list(APPEND PKGDEPS libsigrokflow>=0.1.0)
 endif()
 
-set(LIBSR_CXX_BINDING "libsigrokcxx>=0.5.1")
+set(LIBSR_CXX_BINDING "libsigrokcxx>=0.5.2")
 list(APPEND PKGDEPS "${LIBSR_CXX_BINDING}")
 
 if(ENABLE_DECODE)
@@ -93,7 +145,6 @@ if(ANDROID)
        list(APPEND PKGDEPS libsigrokandroidutils>=0.1.0)
 endif()
 
-find_package(PkgConfig)
 pkg_check_modules(LIBSRCXX ${LIBSR_CXX_BINDING})
 if(NOT LIBSRCXX_FOUND OR NOT LIBSRCXX_VERSION)
        message(FATAL_ERROR "libsigrok C++ bindings missing, check libsigrok's 'configure' output (missing dependencies?)")
@@ -102,20 +153,35 @@ pkg_check_modules(PKGDEPS REQUIRED ${PKGDEPS})
 
 set(CMAKE_AUTOMOC TRUE)
 
-find_package(Qt5 5.3 COMPONENTS Core Gui LinguistTools Widgets Svg REQUIRED)
-
-message(STATUS "Qt version: ${Qt5_VERSION}")
+# Check for Qt5, and check for Qt6 if Qt5 is not found.
+set(QT_COMPONENTS Core Gui LinguistTools Widgets Svg)
+find_package(Qt5 5.3 QUIET COMPONENTS Core)
+if(Qt5_FOUND)
+       find_package(Qt5 5.3 COMPONENTS ${QT_COMPONENTS} REQUIRED)
+       message(STATUS "Qt version: ${Qt5_VERSION}")
+else()
+       find_package(Qt6 6.2 COMPONENTS ${QT_COMPONENTS} REQUIRED)
+       message(STATUS "Qt version: ${Qt6_VERSION}")
+endif()
 
 if(WIN32)
-       # MXE workaround: Use pkg-config to find Qt5 libs.
+       # MXE workaround: Use pkg-config to find Qt5 and Qt6 libs.
        # https://github.com/mxe/mxe/issues/1642
        # Not required (and doesn't work) on MSYS2.
        if(NOT DEFINED ENV{MSYSTEM})
-               pkg_check_modules(QT5ALL REQUIRED Qt5Widgets>=5.3 Qt5Gui>=5.3 Qt5Svg>=5.3)
+               if(Qt5_FOUND)
+                       pkg_check_modules(QT5ALL REQUIRED Qt5Widgets>=5.3 Qt5Gui>=5.3 Qt5Svg>=5.3)
+               else()
+                       pkg_check_modules(QT6ALL REQUIRED Qt6Widgets>=6.2 Qt6Gui>=6.2 Qt6Svg>=6.2)
+               endif()
        endif()
 endif()
 
-set(QT_LIBRARIES Qt5::Gui Qt5::Widgets Qt5::Svg)
+if(Qt5_FOUND)
+       set(QT_LIBRARIES Qt5::Gui Qt5::Widgets Qt5::Svg)
+else()
+       set(QT_LIBRARIES Qt6::Gui Qt6::Widgets Qt6::Svg)
+endif()
 
 set(BOOSTCOMPS filesystem serialization system)
 if(ENABLE_TESTS)
@@ -123,6 +189,14 @@ if(ENABLE_TESTS)
 endif()
 
 if(ENABLE_STACKTRACE)
+       include(FindBacktrace)
+       if (Backtrace_FOUND)
+               set(_Boost_STACKTRACE_BACKTRACE_HEADERS "boost/stacktrace.hpp")
+               list(APPEND BOOSTCOMPS stacktrace_backtrace)
+       else()
+               set(_Boost_STACKTRACE_BASIC_HEADERS     "boost/stacktrace.hpp")
+               list(APPEND BOOSTCOMPS stacktrace_basic)
+       endif()
        find_package(Boost 1.65.1 COMPONENTS ${BOOSTCOMPS} REQUIRED)
 else()
        find_package(Boost 1.55 COMPONENTS ${BOOSTCOMPS} REQUIRED)
@@ -143,48 +217,66 @@ find_package(Threads REQUIRED)
 
 # Helper for checking for atomics
 function(check_working_cxx_atomics varname additional_lib)
-  include(CheckCXXSourceCompiles)
-  include(CMakePushCheckState)
-  cmake_push_check_state()
-  set(CMAKE_REQUIRED_FLAGS "-std=c++11")
-  set(CMAKE_REQUIRED_LIBRARIES "${additional_lib}")
-  set(CMAKE_REQUIRED_QUIET 1)
-  CHECK_CXX_SOURCE_COMPILES("
+       cmake_push_check_state()
+       set(CMAKE_REQUIRED_FLAGS "${REQUIRED_STD_CXX_FLAGS}")
+       set(CMAKE_REQUIRED_LIBRARIES "${additional_lib}")
+       set(CMAKE_REQUIRED_QUIET 1)
+       CHECK_CXX_SOURCE_COMPILES("
 #include <atomic>
 std::atomic<int> x;
 int main() {
-  return std::atomic_fetch_add_explicit(&x, 1, std::memory_order_seq_cst);
+       return std::atomic_fetch_add_explicit(&x, 1, std::memory_order_seq_cst);
 }
 " ${varname})
-  cmake_pop_check_state()
+       cmake_pop_check_state()
 endfunction(check_working_cxx_atomics)
 
 # First check if atomics work without the library.
 # If not, check if the library exists, and atomics work with it.
 check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB "")
 if(HAVE_CXX_ATOMICS_WITHOUT_LIB)
-  message(STATUS "Atomics provided by the C-library - yes")
+       message(STATUS "Atomics provided by the C-library - yes")
 else()
-  message(STATUS "Atomics provided by the C-library - no")
-  find_library(LIBATOMIC_LIBRARY NAMES atomic PATH_SUFFIXES lib)
-  if(LIBATOMIC_LIBRARY)
-    check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB "${LIBATOMIC_LIBRARY}")
-    if (HAVE_CXX_ATOMICS_WITH_LIB)
-      message(STATUS "Atomics provided by libatomic - yes")
-    else()
-      message(STATUS "Atomics provided by libatomic - no")
-      message(FATAL_ERROR "Compiler must support std::atomic!")
-    endif()
-  else()
-    message(FATAL_ERROR "Compiler appears to require libatomic, but cannot find it.")
-  endif()
+       message(STATUS "Atomics provided by the C-library - no")
+       find_library(LIBATOMIC_LIBRARY NAMES atomic PATH_SUFFIXES lib)
+       if(LIBATOMIC_LIBRARY)
+               check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB "${LIBATOMIC_LIBRARY}")
+               if (HAVE_CXX_ATOMICS_WITH_LIB)
+                       message(STATUS "Atomics provided by libatomic - yes")
+               else()
+                       message(STATUS "Atomics provided by libatomic - no")
+                       message(FATAL_ERROR "Compiler must support std::atomic!")
+               endif()
+       else()
+               message(FATAL_ERROR "Compiler appears to require libatomic, but cannot find it.")
+       endif()
+endif()
+
+# Check availability of features which depend on library versions.
+# TODO Ideally use check_symbol_exists() instead, reduce boilerplate.
+if(ENABLE_DECODE)
+       cmake_push_check_state()
+       set(CMAKE_REQUIRED_INCLUDES "${PKGDEPS_INCLUDE_DIRS}")
+       set(CMAKE_REQUIRED_LIBRARIES "sigrokdecode")
+       foreach (LPATH ${PKGDEPS_LIBRARY_DIRS})
+               list(APPEND CMAKE_REQUIRED_LINK_OPTIONS "-L${LPATH}")
+       endforeach ()
+       check_c_source_compiles("
+       #include <libsigrokdecode/libsigrokdecode.h>
+       int main(int argc, char *argv[])
+       {
+               (void)argc;
+               (void)argv;
+               return srd_session_send_eof(NULL);
+       }
+       " HAVE_SRD_SESSION_SEND_EOF)
+       cmake_pop_check_state()
 endif()
 
 #===============================================================================
 #= System Introspection
 #-------------------------------------------------------------------------------
 
-include(memaccess)
 memaccess_check_unaligned_le(HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS)
 
 #===============================================================================
@@ -194,7 +286,11 @@ memaccess_check_unaligned_le(HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS)
 set(PV_TITLE PulseView)
 set(PV_VERSION_STRING "0.5.0")
 
-set(PV_GLIBMM_VERSION ${PKGDEPS_glibmm-2.4_VERSION})
+if(GLIBMM_2_4_FOUND)
+       set(PV_GLIBMM_VERSION ${PKGDEPS_glibmm-2.4_VERSION})
+else()
+       set(PV_GLIBMM_VERSION ${PKGDEPS_glibmm-2.68_VERSION})
+endif()
 
 include(GetGitRevisionDescription)
 
@@ -239,6 +335,7 @@ set(pulseview_SOURCES
        pv/globalsettings.cpp
        pv/logging.cpp
        pv/mainwindow.cpp
+       pv/metadata_obj.cpp
        pv/session.cpp
        pv/storesession.cpp
        pv/util.cpp
@@ -249,6 +346,7 @@ set(pulseview_SOURCES
        pv/data/analogsegment.cpp
        pv/data/logic.cpp
        pv/data/logicsegment.cpp
+       pv/data/mathsignal.cpp
        pv/data/signalbase.cpp
        pv/data/signaldata.cpp
        pv/data/segment.cpp
@@ -276,6 +374,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
@@ -311,9 +410,11 @@ 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
+       pv/metadata_obj.hpp
        pv/session.hpp
        pv/storesession.hpp
        pv/binding/device.hpp
@@ -321,6 +422,7 @@ set(pulseview_HEADERS
        pv/data/analogsegment.hpp
        pv/data/logic.hpp
        pv/data/logicsegment.hpp
+       pv/data/mathsignal.hpp
        pv/data/signalbase.hpp
        pv/dialogs/connect.hpp
        pv/dialogs/inputoutputoptions.hpp
@@ -341,6 +443,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
@@ -389,8 +492,10 @@ if(ENABLE_DECODE)
                pv/subwindows/decoder_selector/item.cpp
                pv/subwindows/decoder_selector/model.cpp
                pv/subwindows/decoder_selector/subwindow.cpp
-               pv/views/decoder_output/view.cpp
-               pv/views/decoder_output/QHexView.cpp
+               pv/views/decoder_binary/view.cpp
+               pv/views/decoder_binary/QHexView.cpp
+               pv/views/tabular_decoder/model.cpp
+               pv/views/tabular_decoder/view.cpp
                pv/views/trace/decodetrace.cpp
                pv/widgets/decodergroupbox.cpp
                pv/widgets/decodermenu.cpp
@@ -399,8 +504,9 @@ if(ENABLE_DECODE)
        list(APPEND pulseview_HEADERS
                pv/data/decodesignal.hpp
                pv/subwindows/decoder_selector/subwindow.hpp
-               pv/views/decoder_output/view.hpp
-               pv/views/decoder_output/QHexView.hpp
+               pv/views/decoder_binary/view.hpp
+               pv/views/decoder_binary/QHexView.hpp
+               pv/views/tabular_decoder/view.hpp
                pv/views/trace/decodetrace.hpp
                pv/widgets/decodergroupbox.hpp
                pv/widgets/decodermenu.hpp
@@ -421,7 +527,11 @@ if(ANDROID)
        )
 endif()
 
-qt5_add_resources(pulseview_RESOURCES_RCC ${pulseview_RESOURCES})
+if(Qt5_FOUND)
+       qt5_add_resources(pulseview_RESOURCES_RCC ${pulseview_RESOURCES})
+else()
+       qt6_add_resources(pulseview_RESOURCES_RCC ${pulseview_RESOURCES})
+endif()
 
 #===============================================================================
 #= Translations
@@ -433,10 +543,19 @@ if (NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
        configure_file("translations.qrc" "translations.qrc" COPYONLY)
 endif ()
 
-qt5_add_translation(QM_FILES ${TS_FILES})
-qt5_create_translation(QM_FILES ${pulseview_SOURCES} ${TS_FILES})
-
-qt5_add_resources(pulseview_RESOURCES_RCC ${CMAKE_BINARY_DIR}/translations.qrc)
+if(Qt5_FOUND)
+       qt5_add_translation(QM_FILES ${TS_FILES})
+       qt5_add_resources(pulseview_RESOURCES_RCC ${CMAKE_BINARY_DIR}/translations.qrc)
+       if (ENABLE_TS_UPDATE)
+               qt5_create_translation(QM_FILES ${pulseview_SOURCES} ${TS_FILES})
+       endif ()
+else()
+       qt6_add_translation(QM_FILES ${TS_FILES})
+       qt6_add_resources(pulseview_RESOURCES_RCC ${CMAKE_BINARY_DIR}/translations.qrc)
+       if (ENABLE_TS_UPDATE)
+               qt6_create_translation(QM_FILES ${pulseview_SOURCES} ${TS_FILES})
+       endif ()
+endif()
 
 #===============================================================================
 #= Global Definitions
@@ -445,8 +564,12 @@ qt5_add_resources(pulseview_RESOURCES_RCC ${CMAKE_BINARY_DIR}/translations.qrc)
 add_definitions(-DQT_NO_KEYWORDS)
 add_definitions(-D__STDC_LIMIT_MACROS)
 add_definitions(-Wall -Wextra)
-add_definitions(-std=c++11)
+add_definitions(${REQUIRED_STD_CXX_FLAGS})
+
 add_definitions(-DBOOST_MATH_DISABLE_FLOAT128=1)
+if(WIN32)
+       add_definitions(-Wa,-mbig-obj -O3)
+endif()
 
 if(ENABLE_FLOW)
        add_definitions(-DENABLE_FLOW)
@@ -465,7 +588,10 @@ if(ENABLE_SIGNALS)
 endif()
 
 if(ENABLE_STACKTRACE)
-       add_definitions(-DENABLE_STACKTRACE)
+       add_definitions(-DENABLE_STACKTRACE -no-pie -fno-pie)
+       if (Backtrace_FOUND)
+               add_definitions(-DBOOST_STACKTRACE_USE_BACKTRACE)
+       endif()
 endif()
 
 #===============================================================================
@@ -511,17 +637,23 @@ if(WIN32)
        # We also need QWindowsIntegrationPlugin, Qt5PlatformSupport (only for
        # Qt < 5.8.0), and all Qt libs and their dependencies.
        add_definitions(-DQT_STATICPLUGIN)
-       list(APPEND PULSEVIEW_LINK_LIBS Qt5::QSvgPlugin)
-       list(APPEND PULSEVIEW_LINK_LIBS Qt5::QWindowsIntegrationPlugin)
-       if(Qt5Gui_VERSION VERSION_LESS 5.8.0)
-               list(APPEND PULSEVIEW_LINK_LIBS -lQt5PlatformSupport)
+       if(Qt5_FOUND)
+               list(APPEND PULSEVIEW_LINK_LIBS Qt5::QSvgPlugin)
+               list(APPEND PULSEVIEW_LINK_LIBS Qt5::QWindowsIntegrationPlugin)
+               if(Qt5Gui_VERSION VERSION_LESS 5.8.0)
+                       list(APPEND PULSEVIEW_LINK_LIBS -lQt5PlatformSupport)
+               endif()
+               list(APPEND PULSEVIEW_LINK_LIBS ${QT5ALL_LDFLAGS})
+       else()
+               list(APPEND PULSEVIEW_LINK_LIBS Qt6::QSvgPlugin)
+               list(APPEND PULSEVIEW_LINK_LIBS Qt6::QWindowsIntegrationPlugin)
+               list(APPEND PULSEVIEW_LINK_LIBS ${QT6ALL_LDFLAGS})
        endif()
-       list(APPEND PULSEVIEW_LINK_LIBS ${QT5ALL_LDFLAGS})
 endif()
 
 if(ENABLE_STACKTRACE)
-       # Needed to resolve dladdr.
-       list(APPEND PULSEVIEW_LINK_LIBS "-ldl")
+       list(APPEND PULSEVIEW_LINK_LIBS ${CMAKE_DL_LIBS} ${Backtrace_LIBRARIES})
+       link_libraries("-no-pie -fno-pie")
 endif()
 
 if(ANDROID)
diff --git a/INSTALL b/INSTALL
index 0d685f17ccdd0fdbe0bb11e9942da6e4da0677ec..e777959f83adf2b85806780ff70bd4ed6a2d6fbf 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -15,9 +15,9 @@ Requirements
  - cmake >= 2.8.12
  - libglib >= 2.28.0
  - glibmm-2.4 (>= 2.28.0)
- - gstreamermm-1.0 (>= 1.8.0)
  - Qt5 (>= 5.3), including the following components:
-    - Qt5Core, Qt5Gui, Qt5Widgets, Qt5Svg, Qt5LinguistTools
+    - Qt5Core, Qt5Gui, Qt5Widgets, Qt5Svg,
+      Qt5LinguistTools (qttools5-dev, qttools5-dev-tools)
     - Qt translation package (optional; needed at runtime, not build time)
  - libboost >= 1.55 (including the following libs):
     - libboost-system
@@ -25,8 +25,8 @@ Requirements
     - libboost-serialization
     - libboost-test (optional, only needed to run the unit tests)
     - libboost-stacktrace (optional, only needed for debugging)
- - libsigrokcxx >= 0.5.1 (libsigrok C++ bindings)
- - libsigrokdecode >= 0.6.0
+ - libsigrokcxx >= 0.5.2 (libsigrok C++ bindings)
+ - libsigrokdecode >= 0.5.2
  - libsigrokandroidutils >= 0.1.0 (optional, only needed on Android)
  - asciidoctor (optional, only needed to build the HTML manual)
  - asciidoctor-pdf (optional, only needed to build the PDF manual)
diff --git a/README b/README
index a35cb8d58c09c610aa51eee3ced239701c22e32b..61de9b21df76ce1647796ed31b64dc3cdc1f7be0 100644 (file)
--- a/README
+++ b/README
@@ -71,6 +71,11 @@ icons/information.svg: Bobarino
     GFDL 1.2 or later / CC-BY-SA 3.0
     https://en.wikipedia.org/wiki/File:Information.svg#Licensing
 
+icons/add-math-channel.svg: Inductiveload
+  https://en.wikipedia.org/wiki/File:Icon_Mathematical_Plot.svg
+  License:
+    Public Domain
+
 QDarkStyleSheet: Colin Duquesnoy
   https://github.com/ColinDuquesnoy/QDarkStyleSheet
   License:
@@ -83,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
 ------------
@@ -99,7 +109,7 @@ Mailing list
 IRC
 ---
 
-You can find the sigrok developers in the #sigrok IRC channel on Freenode.
+You can find the sigrok developers in the #sigrok IRC channel on Libera.Chat.
 
 
 Website
index 7720001a94861339fbfe0458f63c100cb405b1ac..f5855d18ac8c44ec2911bb9362669bae97c2e3aa 100644 (file)
@@ -34,6 +34,9 @@
 /* Platform properties */
 #cmakedefine HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS
 
+/* Presence of features which depend on library versions. */
+#cmakedefine HAVE_SRD_SESSION_SEND_EOF 1
+
 #define PV_GLIBMM_VERSION "@PV_GLIBMM_VERSION@"
 
 #endif
index 88945f44b214a5e7be774fb016b6c156ef8a265e..1cbd9eb1630741384d80756ab3fe0f51a8b8f9c6 100644 (file)
@@ -43,7 +43,11 @@ Name "PulseView"
 OutFile "pulseview-@PV_VERSION_STRING@-installer.exe"
 
 # Where to install the application.
-InstallDir "$PROGRAMFILES\sigrok\PulseView"
+!ifdef PE64
+       InstallDir "$PROGRAMFILES64\sigrok\PulseView"
+!else
+       InstallDir "$PROGRAMFILES\sigrok\PulseView"
+!endif
 
 # Request admin privileges for Windows Vista and Windows 7.
 # http://nsis.sourceforge.net/Docs/Chapter4.html
@@ -184,6 +188,12 @@ Section "PulseView (required)" Section1
                0 SW_SHOWNORMAL \
                "" "Open-source, portable sigrok GUI"
 
+       # Create a shortcut for the PulseView application in "safe mode".
+       CreateShortCut "$SMPROGRAMS\sigrok\PulseView\PulseView (Safe Mode).lnk" \
+               "$INSTDIR\pulseview.exe" "-c -D" "$INSTDIR\pulseview.exe" \
+               0 SW_SHOWNORMAL \
+               "" "Open-source, portable sigrok GUI (Safe Mode)"
+
        # Create a shortcut for the PulseView application running in debug mode.
        CreateShortCut "$SMPROGRAMS\sigrok\PulseView\PulseView (Debug).lnk" \
                "$INSTDIR\pulseview.exe" "-l 5" "$INSTDIR\pulseview.exe" \
@@ -296,6 +306,7 @@ Section "Uninstall"
 
        # Delete the links from the start menu.
        Delete "$SMPROGRAMS\sigrok\PulseView\PulseView.lnk"
+       Delete "$SMPROGRAMS\sigrok\PulseView\PulseView (Safe Mode).lnk"
        Delete "$SMPROGRAMS\sigrok\PulseView\PulseView (Debug).lnk"
        Delete "$SMPROGRAMS\sigrok\PulseView\Uninstall PulseView.lnk"
        Delete "$SMPROGRAMS\sigrok\PulseView\Zadig (PulseView).lnk"
index 0aecb3c098e86b68648a0bc3fa64b98a18a9db0a..749f6daba85c696ac3790bc685228388804d654a 100644 (file)
@@ -1,4 +1,4 @@
-.TH PULSEVIEW 1 "December 17, 2019"
+.TH PULSEVIEW 1 "March 31, 2020"
 .SH "NAME"
 PulseView \- Qt-based LA/scope/MSO GUI for sigrok
 .SH "SYNOPSIS"
@@ -103,14 +103,17 @@ Start / stop an acquisition.
 .B "Left/right arrow keys"
 Scroll left/right.
 .TP
+.B "+/-"
+Zoom in/out.
+.TP
 .B "Up/down arrow keys"
 Zoom in/out.
 .TP
-.B "Home"
-Jump to the start of the sample data.
+.B "Home/End"
+Jump to the start/end of the sample data.
 .TP
-.B "End"
-Jump to the end of the sample data.
+.B "1/2"
+Attach left/right side of the cursors to the mouse.
 .TP
 .B "CTRL+o"
 Open file.
@@ -130,17 +133,14 @@ Ungroup the traces in the currently selected trace group.
 .B "CTRL+up/down arrow keys"
 Scroll down/up.
 .TP
-.B "CTRL++"
-Zoom in.
-.TP
-.B "CTRL+-"
-Zoom out.
-.TP
 .B "CTRL+q"
 Quit, i.e. shutdown PulseView (closing all session tabs).
 .TP
 .B "CTRL+w"
 Close the current session tab.
+.TP
+.B "SHIFT+mouse wheel"
+Scroll horizontally instead of zooming in/out.
 .SH "EXIT STATUS"
 .B PulseView
 exits with 0 on success, 1 on most failures.
diff --git a/icons/add-math-signal.svg b/icons/add-math-signal.svg
new file mode 100644 (file)
index 0000000..4fa7124
--- /dev/null
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   id="svg11300"
+   height="48px"
+   width="48px"
+   version="1.1"
+   sodipodi:docname="add-math-channel.svg"
+   inkscape:version="0.92.4 5da689c313, 2019-01-14">
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1920"
+     inkscape:window-height="1022"
+     id="namedview124"
+     showgrid="false"
+     inkscape:zoom="17.0625"
+     inkscape:cx="12.893773"
+     inkscape:cy="24"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg11300" />
+  <defs
+     id="defs3">
+    <radialGradient
+       id="radialGradient6719"
+       cx="605.71"
+       xlink:href="#linearGradient5060"
+       gradientUnits="userSpaceOnUse"
+       cy="486.65"
+       r="117.14"
+       gradientTransform="matrix(-2.7744,0,0,1.9697,112.76,-872.89)" />
+    <linearGradient
+       id="linearGradient5060">
+      <stop
+         id="stop5062"
+         offset="0" />
+      <stop
+         id="stop5064"
+         stop-opacity="0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       id="radialGradient6717"
+       cx="605.71"
+       xlink:href="#linearGradient5060"
+       gradientUnits="userSpaceOnUse"
+       cy="486.65"
+       r="117.14"
+       gradientTransform="matrix(2.7744,0,0,1.9697,-1891.6,-872.89)" />
+    <linearGradient
+       id="linearGradient6715"
+       y2="609.51"
+       gradientUnits="userSpaceOnUse"
+       y1="366.65"
+       gradientTransform="matrix(2.7744,0,0,1.9697,-1892.2,-872.89)"
+       x2="302.86"
+       x1="302.86">
+      <stop
+         id="stop5050"
+         stop-opacity="0"
+         offset="0" />
+      <stop
+         id="stop5056"
+         offset=".5" />
+      <stop
+         id="stop5052"
+         stop-opacity="0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       id="radialGradient2939"
+       cx="25.458"
+       gradientUnits="userSpaceOnUse"
+       cy="35.596"
+       r="20.531"
+       gradientTransform="matrix(2.1282,0,0,2.1283,-28.519,-40.418)">
+      <stop
+         id="stop2935"
+         stop-color="#9cbcde"
+         offset="0" />
+      <stop
+         id="stop2937"
+         stop-color="#204a87"
+         offset="1" />
+    </radialGradient>
+  </defs>
+  <metadata
+     id="metadata4">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Jakub Steiner</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:source>http://jimmac.musichall.cz</dc:source>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
+        <dc:title>Accessibility</dc:title>
+        <dc:subject>
+          <rdf:Bag>
+            <rdf:li>accessibility</rdf:li>
+            <rdf:li>assist</rdf:li>
+          </rdf:Bag>
+        </dc:subject>
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Reproduction" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/Distribution" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Notice" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/Attribution" />
+        <cc:permits
+           rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://web.resource.org/cc/ShareAlike" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <path
+     style="fill:none;stroke:#ff0000;stroke-width:1px"
+     inkscape:connector-curvature="0"
+     d="m 12.5,36 c 3,-8 8.155,-12.981 10.5,-13 3.431,-0.028 5,6 9,6 3.606,0 7,-11 8,-19"
+     id="path9990" />
+  <path
+     style="fill:none;stroke:#000000;stroke-width:2;stroke-opacity:1"
+     inkscape:connector-curvature="0"
+     d="M 12,10 V 36 H 38"
+     id="path9988" />
+  <path
+     style="fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-opacity:1;fill-opacity:1;stroke-linejoin:miter;paint-order:normal;stroke-linecap:round"
+     inkscape:connector-curvature="0"
+     d="m 12,7 -3,6 h 6 L 12,7"
+     id="path9992" />
+  <path
+     style="fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-opacity:1;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-linecap:round"
+     inkscape:connector-curvature="0"
+     d="m 41,36 -6,-3 v 6 l 6,-3"
+     id="path9994" />
+  <g
+     style="fill:#000000;stroke:#000000;stroke-width:0.14230999;stroke-opacity:1;fill-opacity:1"
+     transform="matrix(3.5134,0,0,3.5134,-53.717,-11.668)"
+     id="g10167">
+    <g
+       style="fill:#000000;fill-rule:evenodd;stroke:#000000;stroke-width:2.07540011;stroke-miterlimit:10.43299961;stroke-opacity:1;fill-opacity:1"
+       stroke-miterlimit="10.433"
+       transform="matrix(0.068571,0,0,-0.068571,-12,36)"
+       xml:space="preserve"
+       id="g10169"><path
+         inkscape:connector-curvature="0"
+         d="m 474.28,425.93 h 4.29 c 0.99,0 1.49,0 1.49,0.99 0,0.55 -0.5,0.55 -1.35,0.55 h -4.13 c 0.55,2.89 0.5,2.79 1.05,5.68 0.19,1.05 0.89,4.58 1.19,5.18 0.45,0.94 1.3,1.69 2.34,1.69 0.2,0 1.5,0 2.45,-0.89 -2.2,-0.2 -2.7,-1.95 -2.7,-2.69 0,-1.15 0.9,-1.75 1.85,-1.75 1.29,0 2.74,1.1 2.74,2.99 0,2.29 -2.29,3.44 -4.34,3.44 -1.69,0 -4.83,-0.9 -6.32,-5.83 -0.3,-1.05 -0.45,-1.54 -1.65,-7.82 h -3.43 c -0.95,0 -1.5,0 -1.5,-0.95 0,-0.59 0.45,-0.59 1.4,-0.59 h 3.29 l -3.74,-19.68 c -0.9,-4.83 -1.75,-9.37 -4.33,-9.37 -0.2,0 -1.45,0 -2.4,0.9 2.29,0.15 2.74,1.94 2.74,2.69 0,1.15 -0.89,1.74 -1.84,1.74 -1.29,0 -2.74,-1.09 -2.74,-2.98 0,-2.25 2.19,-3.44 4.24,-3.44 2.73,0 4.73,2.94 5.62,4.83 1.6,3.14 2.74,9.17 2.79,9.51 z"
+         id="path10171"
+         style="stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1" /><path
+         inkscape:connector-curvature="0"
+         d="m 501.49,394.04 v 0.03 0.03 l -0.01,0.01 v 0.02 0.01 l -0.01,0.02 v 0.02 l -0.01,0.01 -0.01,0.02 -0.01,0.02 -0.01,0.03 -0.01,0.02 -0.02,0.02 -0.02,0.03 -0.02,0.03 -0.02,0.03 -0.03,0.03 -0.03,0.04 -0.01,0.02 -0.02,0.02 -0.02,0.02 -0.02,0.02 -0.02,0.02 -0.02,0.03 -0.02,0.02 -0.02,0.02 -0.02,0.03 -0.03,0.02 -0.02,0.03 -0.03,0.03 -0.02,0.03 -0.03,0.03 -0.03,0.03 -0.03,0.03 -0.03,0.03 -0.03,0.03 -0.04,0.04 -0.03,0.03 -0.04,0.04 -0.03,0.03 -0.04,0.04 -0.04,0.04 c -6.23,6.28 -7.82,15.69 -7.82,23.31 0,8.67 1.89,17.34 8.02,23.57 0.65,0.59 0.65,0.69 0.65,0.84 0,0.35 -0.2,0.5 -0.5,0.5 -0.5,0 -4.98,-3.39 -7.92,-9.72 -2.54,-5.47 -3.14,-11 -3.14,-15.19 0,-3.88 0.55,-9.91 3.29,-15.54 2.99,-6.12 7.27,-9.36 7.77,-9.36 0.3,0 0.5,0.15 0.5,0.49 z"
+         id="path10173"
+         style="stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1" /><path
+         inkscape:connector-curvature="0"
+         d="m 521.64,421.04 0.03,0.13 0.03,0.15 0.05,0.17 0.04,0.18 0.06,0.19 0.06,0.21 0.06,0.21 0.07,0.23 0.08,0.23 0.09,0.24 0.1,0.25 0.1,0.24 0.11,0.25 0.12,0.25 0.13,0.25 0.14,0.25 0.15,0.24 0.15,0.24 0.17,0.23 0.18,0.22 0.18,0.22 0.2,0.2 0.21,0.19 0.22,0.18 0.23,0.16 0.25,0.14 0.12,0.07 0.13,0.06 0.13,0.05 0.14,0.05 0.14,0.05 0.14,0.04 0.14,0.03 0.15,0.03 0.15,0.02 0.16,0.02 0.16,0.01 h 0.16 c 0.25,0 1.45,0 2.49,-0.65 -1.4,-0.25 -2.39,-1.49 -2.39,-2.68 0,-0.8 0.55,-1.75 1.89,-1.75 1.1,0 2.69,0.9 2.69,2.89 0,2.59 -2.94,3.29 -4.63,3.29 -2.89,0 -4.64,-2.64 -5.23,-3.79 -1.24,3.29 -3.94,3.79 -5.38,3.79 -5.18,0 -8.02,-6.43 -8.02,-7.67 0,-0.5 0.5,-0.5 0.6,-0.5 0.39,0 0.54,0.1 0.64,0.54 1.7,5.29 4.99,6.53 6.68,6.53 0.94,0 2.69,-0.45 2.69,-3.33 0,-1.55 -0.85,-4.89 -2.69,-11.86 -0.8,-3.09 -2.54,-5.18 -4.73,-5.18 -0.3,0 -1.45,0 -2.49,0.65 1.24,0.25 2.34,1.29 2.34,2.69 0,1.34 -1.1,1.74 -1.85,1.74 -1.49,0 -2.73,-1.3 -2.73,-2.89 0,-2.29 2.48,-3.29 4.68,-3.29 3.28,0 5.08,3.49 5.22,3.79 0.61,-1.85 2.4,-3.79 5.39,-3.79 5.13,0 7.97,6.43 7.97,7.68 0,0.49 -0.45,0.49 -0.6,0.49 -0.45,0 -0.55,-0.2 -0.65,-0.55 -1.64,-5.33 -5.03,-6.52 -6.62,-6.52 -1.94,0 -2.74,1.59 -2.74,3.29 0,1.09 0.3,2.19 0.85,4.38 z"
+         id="path10175"
+         style="stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1" /><path
+         inkscape:connector-curvature="0"
+         d="m 547.4,418.45 -0.01,0.37 v 0.39 l -0.01,0.39 -0.01,0.41 -0.04,0.84 -0.06,0.88 -0.09,0.92 -0.1,0.94 -0.14,0.98 -0.16,1 -0.2,1.02 -0.23,1.04 -0.27,1.05 -0.3,1.06 -0.35,1.06 -0.39,1.07 -0.22,0.53 -0.22,0.54 -0.24,0.53 -0.25,0.53 c -2.99,6.12 -7.27,9.36 -7.77,9.36 -0.3,0 -0.5,-0.2 -0.5,-0.5 0,-0.15 0,-0.25 0.94,-1.15 4.89,-4.92 7.73,-12.85 7.73,-23.26 0,-8.52 -1.85,-17.28 -8.02,-23.56 -0.65,-0.6 -0.65,-0.69 -0.65,-0.85 0,-0.29 0.2,-0.49 0.5,-0.49 0.5,0 4.98,3.38 7.92,9.71 2.54,5.48 3.14,11.01 3.14,15.19 z"
+         id="path10177"
+         style="stroke:#000000;stroke-opacity:1;fill:#000000;fill-opacity:1" /></g>  </g>
+</svg>
diff --git a/icons/math.svg b/icons/math.svg
new file mode 100644 (file)
index 0000000..afdaeb6
--- /dev/null
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="73.108994mm"
+   height="72.888451mm"
+   viewBox="0 0 73.108993 72.88845"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.4 5da689c313, 2019-01-14"
+   sodipodi:docname="math.svg">
+  <defs
+     id="defs2" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.3994141"
+     inkscape:cx="136.49166"
+     inkscape:cy="131.54997"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer2"
+     showgrid="false"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1920"
+     inkscape:window-height="1022"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="bg"
+     style="display:inline"
+     transform="translate(-9.0421371,-8.6010609)">
+    <rect
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
+       id="rect901"
+       width="33.522072"
+       height="33.522072"
+       x="9.0421381"
+       y="8.6010609"
+       ry="7.3880887" />
+    <rect
+       ry="7.3880887"
+       y="8.6010609"
+       x="48.629059"
+       height="33.522072"
+       width="33.522072"
+       id="rect4606"
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" />
+    <rect
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
+       id="rect4608"
+       width="33.522072"
+       height="33.522072"
+       x="48.629059"
+       y="47.967442"
+       ry="7.3880887" />
+    <rect
+       ry="7.3880887"
+       y="47.967442"
+       x="9.0421371"
+       height="33.522072"
+       width="33.522072"
+       id="rect4610"
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" />
+  </g>
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="text"
+     style="display:inline"
+     transform="translate(-9.0421371,-8.6010609)">
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.3812499px;line-height:125%;font-family:Sans;-inkscape-font-specification:Helvetica;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="-26.023714"
+       y="23.928589"
+       id="text4615"><tspan
+         sodipodi:role="line"
+         id="tspan4613"
+         x="-26.023714"
+         y="26.073511"
+         style="line-height:25.34355545px;stroke-width:0.26458332px" /></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.3812499px;line-height:125%;font-family:Sans;-inkscape-font-specification:Helvetica;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="-6.8615932"
+       y="27.153271"
+       id="text4619"><tspan
+         sodipodi:role="line"
+         id="tspan4617"
+         x="-6.8615932"
+         y="29.298193"
+         style="stroke-width:0.26458332px" /></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.3812499px;line-height:125%;font-family:Sans;-inkscape-font-specification:Helvetica;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="-22.715614"
+       y="52.267971"
+       id="text4635"><tspan
+         sodipodi:role="line"
+         id="tspan4633"
+         x="-22.715614"
+         y="54.412891"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:42.33333206px;font-family:Sans;-inkscape-font-specification:'Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332px" /></text>
+    <g
+       id="g4686"
+       style="fill:#fcfcfc;fill-opacity:1">
+      <text
+         id="text4627"
+         y="39.476654"
+         x="13.673477"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.3812499px;line-height:125%;font-family:Sans;-inkscape-font-specification:Helvetica;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#fcfcfc;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         xml:space="preserve"><tspan
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:42.33333206px;font-family:Sans;-inkscape-font-specification:'Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#fcfcfc;fill-opacity:1;stroke-width:0.26458332px"
+           y="39.476654"
+           x="13.673477"
+           id="tspan4625"
+           sodipodi:role="line">+</tspan></text>
+      <text
+         transform="scale(1.4000838,0.71424298)"
+         id="text4631"
+         y="51.256554"
+         x="36.829548"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.33394933px;line-height:125%;font-family:Sans;-inkscape-font-specification:Helvetica;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#fcfcfc;fill-opacity:1;stroke:none;stroke-width:0.37043878px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         xml:space="preserve"><tspan
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:59.27021027px;font-family:Sans;-inkscape-font-specification:'Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#fcfcfc;fill-opacity:1;stroke-width:0.37043878px"
+           y="51.256554"
+           x="36.829548"
+           id="tspan4629"
+           sodipodi:role="line">-</tspan></text>
+      <text
+         transform="rotate(-45)"
+         xml:space="preserve"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.3812499px;line-height:125%;font-family:Sans;-inkscape-font-specification:Helvetica;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#fcfcfc;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         x="-39.823532"
+         y="78.197281"
+         id="text4639"><tspan
+           sodipodi:role="line"
+           id="tspan4637"
+           x="-39.823532"
+           y="78.197281"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:42.33333206px;font-family:Sans;-inkscape-font-specification:'Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#fcfcfc;fill-opacity:1;stroke-width:0.26458332px">+</tspan></text>
+      <text
+         transform="scale(1.4000838,0.71424297)"
+         id="text4631-7"
+         y="106.67771"
+         x="36.993214"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:3.33394957px;line-height:125%;font-family:Sans;-inkscape-font-specification:Helvetica;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;display:inline;fill:#fcfcfc;fill-opacity:1;stroke:none;stroke-width:0.37043881px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         xml:space="preserve"><tspan
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:59.27021027px;font-family:Sans;-inkscape-font-specification:'Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#fcfcfc;fill-opacity:1;stroke-width:0.37043881px"
+           y="106.67771"
+           x="36.993214"
+           id="tspan4629-6"
+           sodipodi:role="line">-</tspan></text>
+      <circle
+         r="2.976748"
+         cy="57.78373"
+         cx="65.500908"
+         id="path4659"
+         style="opacity:1;fill:#fcfcfc;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
+      <circle
+         r="2.976748"
+         cy="71.785759"
+         cx="65.500366"
+         id="path4659-0"
+         style="display:inline;opacity:1;fill:#fcfcfc;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
+    </g>
+  </g>
+</svg>
index c08343c763846554415485cb9ddb7bbf83b65193..5553fa8b0469d2fa98775b8be5f2703014e82b9a 100644 (file)
@@ -4,7 +4,7 @@
 <context>
     <name>Application</name>
     <message>
-        <location filename="../pv/application.cpp" line="121"/>
+        <location filename="../pv/application.cpp" line="129"/>
         <source>Some parts of the application may still use the previous language. Re-opening the affected windows or restarting the application will remedy this.</source>
         <translation>Einige Teile der Anwendung verwenden vielleicht noch die vorherige Sprache. Sollte das der Fall sein, kann dies durch ein Schließen und neu Öffnen der betroffenen Fenster oder der Anwendung behoben werden.</translation>
     </message>
@@ -47,7 +47,7 @@
 <context>
     <name>QHexView</name>
     <message>
-        <location filename="../pv/views/decoder_output/QHexView.cpp" line="288"/>
+        <location filename="../pv/views/decoder_binary/QHexView.cpp" line="291"/>
         <source>No data available</source>
         <translation>Keine Daten vorhanden</translation>
     </message>
         <translation>Suche nach Geräten, die von Treiber %1 angesprochen werden können...</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="113"/>
+        <location filename="../main.cpp" line="114"/>
         <source>Stack trace of previous crash:</source>
         <translatorcomment>Internal message</translatorcomment>
         <translation></translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="127"/>
+        <location filename="../main.cpp" line="128"/>
         <source>Don&apos;t show this message again</source>
         <translation>Diese Meldung in Zukunft nicht mehr anzeigen</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="130"/>
+        <location filename="../main.cpp" line="131"/>
         <source>When %1 last crashed, it created a stack trace.
 A human-readable form has been saved to disk and was written to the log. You may access it from the settings dialog.</source>
         <translatorcomment>Internal message</translatorcomment>
@@ -93,66 +93,67 @@ A human-readable form has been saved to disk and was written to the log. You may
 <context>
     <name>pv::MainWindow</name>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="69"/>
+        <location filename="../pv/mainwindow.cpp" line="70"/>
         <source>PulseView</source>
         <translatorcomment>Name</translatorcomment>
         <translation></translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="276"/>
+        <location filename="../pv/mainwindow.cpp" line="279"/>
         <source>Decoder Selector</source>
         <translation>Protokolldekoder</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="331"/>
+        <location filename="../pv/mainwindow.cpp" line="332"/>
         <source>Session %1</source>
         <translation>Analysesitzung %1</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="513"/>
+        <location filename="../pv/mainwindow.cpp" line="514"/>
         <source>Create New Session</source>
         <translation>Neue Analysesitzung</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="519"/>
+        <location filename="../pv/mainwindow.cpp" line="520"/>
         <source>Start/Stop Acquisition</source>
         <translation>Datenerfassung starten/stoppen</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="527"/>
+        <location filename="../pv/mainwindow.cpp" line="528"/>
         <source>Settings</source>
         <translation>Einstellungen</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="577"/>
+        <location filename="../pv/mainwindow.cpp" line="580"/>
         <source>Reload</source>
         <translation>Neu laden</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="628"/>
-        <location filename="../pv/mainwindow.cpp" line="822"/>
-        <location filename="../pv/mainwindow.cpp" line="848"/>
+        <location filename="../pv/mainwindow.cpp" line="635"/>
+        <location filename="../pv/mainwindow.cpp" line="829"/>
+        <location filename="../pv/mainwindow.cpp" line="855"/>
         <source>Confirmation</source>
         <translation>Bestätigung</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="629"/>
+        <location filename="../pv/mainwindow.cpp" line="636"/>
         <source>There is unsaved data. Close anyway?</source>
         <translation>Es gibt noch ungespeicherte Daten. Trotzdem beenden?</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="577"/>
+        <location filename="../pv/mainwindow.cpp" line="580"/>
+        <location filename="../pv/mainwindow.cpp" line="583"/>
         <source>Run</source>
         <translation>Starten</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="582"/>
+        <location filename="../pv/mainwindow.cpp" line="589"/>
         <source>Stop</source>
         <translation>Stoppen</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="823"/>
-        <location filename="../pv/mainwindow.cpp" line="849"/>
+        <location filename="../pv/mainwindow.cpp" line="830"/>
+        <location filename="../pv/mainwindow.cpp" line="856"/>
         <source>This session contains unsaved data. Close it anyway?</source>
         <translation>Die Daten dieser Analysesitzung wurden nicht gespeichert. Trotzdem schließen?</translation>
     </message>
@@ -160,47 +161,51 @@ A human-readable form has been saved to disk and was written to the log. You may
 <context>
     <name>pv::Session</name>
     <message>
-        <location filename="../pv/session.cpp" line="409"/>
+        <location filename="../pv/session.cpp" line="521"/>
         <source>Failed to select device</source>
         <translation>Fehler beim Auswählen des Gerätes</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="458"/>
+        <location filename="../pv/session.cpp" line="578"/>
         <source>Failed to open device</source>
         <translation>Fehler beim Öffnen des Gerätes</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="560"/>
+        <location filename="../pv/session.cpp" line="684"/>
         <source>Error</source>
         <translation>Fehler</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="561"/>
         <source>Unexpected input format: %s</source>
-        <translation>Unerwartetes Importformat: %s</translation>
+        <translation type="vanished">Unerwartetes Importformat: %s</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="596"/>
+        <location filename="../pv/session.cpp" line="685"/>
+        <source>Unexpected input format: %1</source>
+        <translation>Unerwartetes Importformat: %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="720"/>
         <source>Failed to load %1</source>
         <translation>Fehler beim Laden von %1</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="631"/>
+        <location filename="../pv/session.cpp" line="759"/>
         <source>No active device set, can&apos;t start acquisition.</source>
         <translation>Kein Gerät aktiv, kann Datenerfassung nicht starten.</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="644"/>
+        <location filename="../pv/session.cpp" line="772"/>
         <source>No channels enabled.</source>
         <translation>Keine aktiven Kanäle vorhanden.</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="1095"/>
+        <location filename="../pv/session.cpp" line="1261"/>
         <source>Out of memory, acquisition stopped.</source>
         <translation>Nicht genügend Arbeitsspeicher vorhanden, Datenerfassung wurde gestoppt.</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="1301"/>
+        <location filename="../pv/session.cpp" line="1467"/>
         <source>Can&apos;t handle more than 64 logic channels.</source>
         <translatorcomment>Internal message</translatorcomment>
         <translation></translation>
@@ -209,28 +214,28 @@ A human-readable form has been saved to disk and was written to the log. You may
 <context>
     <name>pv::StoreSession</name>
     <message>
-        <location filename="../pv/storesession.cpp" line="115"/>
+        <location filename="../pv/storesession.cpp" line="114"/>
         <source>Can&apos;t save logic channel without data.</source>
         <translation>Kann Logikkanal nicht speichern, da er keine Daten beinhaltet.</translation>
     </message>
     <message>
-        <location filename="../pv/storesession.cpp" line="131"/>
+        <location filename="../pv/storesession.cpp" line="130"/>
         <source>Can&apos;t save analog channel without data.</source>
         <translation>Kann Analogkanal nicht speichern, da er keine Daten beinhaltet.</translation>
     </message>
     <message>
-        <location filename="../pv/storesession.cpp" line="143"/>
+        <location filename="../pv/storesession.cpp" line="142"/>
         <source>No channels enabled.</source>
         <translation>Keine Kanäle aktiviert.</translation>
     </message>
     <message>
-        <location filename="../pv/storesession.cpp" line="168"/>
+        <location filename="../pv/storesession.cpp" line="167"/>
         <source>Can&apos;t save range without sample data.</source>
         <translation>In dem gewählten Bereich befinden sich keine Daten zum Speichern.</translation>
     </message>
     <message>
-        <location filename="../pv/storesession.cpp" line="189"/>
-        <location filename="../pv/storesession.cpp" line="296"/>
+        <location filename="../pv/storesession.cpp" line="188"/>
+        <location filename="../pv/storesession.cpp" line="295"/>
         <source>Error while saving: </source>
         <translation>Fehler beim Speichern: </translation>
     </message>
@@ -238,32 +243,32 @@ A human-readable form has been saved to disk and was written to the log. You may
 <context>
     <name>pv::data::DecodeSignal</name>
     <message>
-        <location filename="../pv/data/decodesignal.cpp" line="195"/>
+        <location filename="../pv/data/decodesignal.cpp" line="198"/>
         <source>No decoders</source>
         <translation>Keine Protokolldekoder</translation>
     </message>
     <message>
-        <location filename="../pv/data/decodesignal.cpp" line="202"/>
+        <location filename="../pv/data/decodesignal.cpp" line="205"/>
         <source>There are no channels assigned to this decoder</source>
         <translation>Dem Protokolldekoder sind keine Kanäle zugeordnet</translation>
     </message>
     <message>
-        <location filename="../pv/data/decodesignal.cpp" line="216"/>
+        <location filename="../pv/data/decodesignal.cpp" line="219"/>
         <source>One or more required channels have not been specified</source>
         <translation>Mindestens ein notwendiger Kanal wurde noch nicht zugeordnet</translation>
     </message>
     <message>
-        <location filename="../pv/data/decodesignal.cpp" line="235"/>
+        <location filename="../pv/data/decodesignal.cpp" line="238"/>
         <source>No input data</source>
         <translation>Keine Daten zum Auswerten vorhanden</translation>
     </message>
     <message>
-        <location filename="../pv/data/decodesignal.cpp" line="1161"/>
+        <location filename="../pv/data/decodesignal.cpp" line="1180"/>
         <source>Decoder reported an error</source>
         <translation>Protokolldekoder meldet Fehler</translation>
     </message>
     <message>
-        <location filename="../pv/data/decodesignal.cpp" line="1297"/>
+        <location filename="../pv/data/decodesignal.cpp" line="1316"/>
         <source>Failed to create decoder instance</source>
         <translation>Fehler beim Erzeugen des Protokolldekoders</translation>
     </message>
@@ -271,52 +276,52 @@ A human-readable form has been saved to disk and was written to the log. You may
 <context>
     <name>pv::data::SignalBase</name>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="409"/>
+        <location filename="../pv/data/signalbase.cpp" line="485"/>
         <source>Signal average</source>
         <translation>Durchschnittlicher Signalpegel</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="410"/>
+        <location filename="../pv/data/signalbase.cpp" line="486"/>
         <source>0.9V (for 1.8V CMOS)</source>
         <translation>0.9V (für 1.8V CMOS)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="411"/>
+        <location filename="../pv/data/signalbase.cpp" line="487"/>
         <source>1.8V (for 3.3V CMOS)</source>
         <translation>1.8V (für 3.3V CMOS)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="412"/>
+        <location filename="../pv/data/signalbase.cpp" line="488"/>
         <source>2.5V (for 5.0V CMOS)</source>
         <translation>2.5V (für 5.0V CMOS)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="413"/>
+        <location filename="../pv/data/signalbase.cpp" line="489"/>
         <source>1.5V (for TTL)</source>
         <translation>1.5V (für TTL)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="418"/>
+        <location filename="../pv/data/signalbase.cpp" line="494"/>
         <source>Signal average +/- 15%</source>
         <translation>Durchschnittlicher Signalpegel +/- 15%</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="419"/>
+        <location filename="../pv/data/signalbase.cpp" line="495"/>
         <source>0.3V/1.2V (for 1.8V CMOS)</source>
         <translation>0.3V/1.2V (für 1.8V CMOS)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="420"/>
+        <location filename="../pv/data/signalbase.cpp" line="496"/>
         <source>0.7V/2.5V (for 3.3V CMOS)</source>
         <translation>0.7V/2.5V (für 3.3V CMOS)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="421"/>
+        <location filename="../pv/data/signalbase.cpp" line="497"/>
         <source>1.3V/3.7V (for 5.0V CMOS)</source>
         <translation>1.3V/3.7V (für 5.0V CMOS)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="422"/>
+        <location filename="../pv/data/signalbase.cpp" line="498"/>
         <source>0.8V/2.0V (for TTL)</source>
         <translation>0.8V/2.0V (für TTL)</translation>
     </message>
@@ -548,7 +553,7 @@ A human-readable form has been saved to disk and was written to the log. You may
     <message>
         <location filename="../pv/dialogs/settings.cpp" line="409"/>
         <source>Always show all &amp;rows, even if no annotation is visible</source>
-        <translation>Immer alle &amp;Reihen anzeigen, auch wenn hierfür keine dekodierten Werte vorliegen</translation>
+        <translation>Immer alle &amp;Kategorien (Zeilen) anzeigen, auch wenn hierfür keine dekodierten Werte vorliegen</translation>
     </message>
     <message>
         <location filename="../pv/dialogs/settings.cpp" line="417"/>
@@ -557,17 +562,25 @@ A human-readable form has been saved to disk and was written to the log. You may
     </message>
     <message>
         <location filename="../pv/dialogs/settings.cpp" line="418"/>
+        <source>%s = sample range; %d: decoder name; %r: row name; %c: class name</source>
+        <translation>%s = Start-/Endsample; %d: Dekodername; %r: Kategorie (Name der Zeile); %c: Unterkategorie</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="421"/>
+        <source>%1: longest annotation text; %a: all annotation texts; %q: use quotation marks</source>
+        <translation>%1: Längste Beschreibung des dekodierten Wertes; %a: Alle Beschreibungen des dekodierten Wertes; %q: Benutze Anführungszeichen</translation>
+    </message>
+    <message>
         <source>%s = sample range; %d: decoder name; %r: row name; %q: use quotation marks</source>
-        <translation>%s = Start-/Endsample; %d: Dekodername; %c Name der Kategorie; %q: Benutze Anführungszeichen</translation>
+        <translation type="vanished">%s = Start-/Endsample; %d: Dekodername; %c Name der Kategorie; %q: Benutze Anführungszeichen</translation>
     </message>
     <message>
         <source>%s = sample range; %d: decoder name; %c: row name; %q: use quotations marks</source>
         <translation type="vanished">%s = Start-/Endsample; %d: Dekodername; %c Name der Kategorie; %q: Benutze Anführungszeichen</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="421"/>
         <source>%1: longest annotation text; %a: all annotation texts</source>
-        <translation>%1: Längste Beschreibung des dekodierten Wertes; %a: Alle Beschreibungen des dekodierten Wertes</translation>
+        <translation type="vanished">%1: Längste Beschreibung des dekodierten Wertes; %a: Alle Beschreibungen des dekodierten Wertes</translation>
     </message>
     <message>
         <location filename="../pv/dialogs/settings.cpp" line="441"/>
@@ -615,32 +628,37 @@ A human-readable form has been saved to disk and was written to the log. You may
         <translation>Unterstützte Protokolldekoder:</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="537"/>
+        <location filename="../pv/dialogs/settings.cpp" line="504"/>
+        <source>Available Translations:</source>
+        <translation>Verfügbare Übersetzungen:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="552"/>
         <source>Log level:</source>
         <translation>Log-Level:</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="542"/>
+        <location filename="../pv/dialogs/settings.cpp" line="557"/>
         <source> lines</source>
         <translation> Zeilen</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="551"/>
+        <location filename="../pv/dialogs/settings.cpp" line="566"/>
         <source>Length of background buffer:</source>
         <translation>Länge des Logpuffers:</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="557"/>
+        <location filename="../pv/dialogs/settings.cpp" line="572"/>
         <source>&amp;Save to File</source>
         <translation>&amp;Speichern</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="564"/>
+        <location filename="../pv/dialogs/settings.cpp" line="579"/>
         <source>&amp;Pop out</source>
         <translation>&amp;Abdocken</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="633"/>
+        <location filename="../pv/dialogs/settings.cpp" line="648"/>
         <source>You selected a dark theme.
 Should I set the user-adjustable colors to better suit your choice?
 
@@ -651,7 +669,7 @@ Sollen die benutzerspezifischen Farben entsprechend angepasst werden, damit sie
 Bei einer Änderung benötigt PulseView eventuell einen Neustart, damit alles korrekt angezeigt wird.</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="639"/>
+        <location filename="../pv/dialogs/settings.cpp" line="654"/>
         <source>You selected a bright theme.
 Should I set the user-adjustable colors to better suit your choice?
 
@@ -662,37 +680,37 @@ Sollen die benutzerspezifischen Farben entsprechend angepasst werden, damit sie
 Bei einer Änderung benötigt PulseView eventuell einen Neustart, damit alles korrekt angezeigt wird.</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="792"/>
+        <location filename="../pv/dialogs/settings.cpp" line="807"/>
         <source>Save Log</source>
         <translation>Log speichern</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="792"/>
+        <location filename="../pv/dialogs/settings.cpp" line="807"/>
         <source>Log Files (*.txt *.log);;All Files (*)</source>
         <translation>Logdateien (*.txt *.log);;Alle Dateien (*)</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="804"/>
+        <location filename="../pv/dialogs/settings.cpp" line="819"/>
         <source>Success</source>
         <translation>Erfolg</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="804"/>
+        <location filename="../pv/dialogs/settings.cpp" line="819"/>
         <source>Log saved to %1.</source>
         <translation>Log als %1 gespeichert.</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="814"/>
+        <location filename="../pv/dialogs/settings.cpp" line="829"/>
         <source>Error</source>
         <translation>Fehler</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="814"/>
+        <location filename="../pv/dialogs/settings.cpp" line="829"/>
         <source>File %1 could not be written to.</source>
         <translation>Konnte Datei %1 nicht speichern.</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="828"/>
+        <location filename="../pv/dialogs/settings.cpp" line="843"/>
         <source>%1 Log</source>
         <translation></translation>
     </message>
@@ -878,301 +896,430 @@ Bei einer Änderung benötigt PulseView eventuell einen Neustart, damit alles ko
 <context>
     <name>pv::toolbars::MainBar</name>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="120"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="121"/>
         <source>New &amp;View</source>
         <translation>Neue &amp;Ansicht</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="126"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="127"/>
         <source>&amp;Open...</source>
         <translation>&amp;Öffnen...</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="133"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="134"/>
         <source>Restore Session Setu&amp;p...</source>
         <translation>&amp;Konfiguration der Analysesitzung laden...</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="137"/>
         <source>&amp;Save As...</source>
-        <translation>&amp;Speichern als...</translation>
+        <translation type="vanished">&amp;Speichern als...</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="144"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="138"/>
+        <source>&amp;Save...</source>
+        <translation>&amp;Speichern...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="145"/>
+        <source>Save &amp;As...</source>
+        <translation>Speichern &amp;als...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="151"/>
         <source>Save Selected &amp;Range As...</source>
         <translation>Ausgewählten &amp;Bereich speichern als...</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="151"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="158"/>
         <source>Save Session Setu&amp;p...</source>
         <translation>&amp;Konfiguration der Analysesitzung speichern...</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="157"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="164"/>
         <source>&amp;Export</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="163"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="170"/>
         <source>&amp;Import</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="167"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="174"/>
         <source>&amp;Connect to Device...</source>
         <translation>Mit Gerät &amp;verbinden...</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="228"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="236"/>
         <source>Add protocol decoder</source>
         <translation>Protokolldekoder hinzufügen</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="244"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="252"/>
         <source>Configure Device</source>
         <translation>Gerät konfigurieren</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="248"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="256"/>
         <source>Configure Channels</source>
         <translation>Kanäle konfigurieren</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="357"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="370"/>
         <source>Failed to get sample rate list:</source>
         <translation>Konnte Liste unterstützter Abtastraten nicht abfragen:</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="420"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="433"/>
         <source>Failed to get sample rate:</source>
         <translation>Konnte Abtastrate nicht abfragen:</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="461"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="474"/>
         <source>Failed to get sample limit list:</source>
         <translation>Konnte Liste der maximal erlaubten Abtastraten nicht abfragen:</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="551"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="564"/>
         <source>Failed to configure samplerate:</source>
         <translation>Konnte Abtastrate nicht einstellen:</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="578"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="591"/>
         <source>Failed to configure sample count:</source>
         <translation>Konnte Anzahl der Abtastpunkte nicht einstellen:</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="616"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="629"/>
         <source>Missing Cursors</source>
         <translation>Fehlende Auswahl</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="616"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="629"/>
         <source>You need to set the cursors before you can save the data enclosed by them to a session file (e.g. using the Show Cursors button).</source>
         <translation>Du musst die Auswahl-Markierer setzen, bevor du die darin befindlichen Daten abspeichern kannst. Verwende hierzu bspw. den Knopf für die Auswahl-Markierer.</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="634"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="647"/>
         <source>Invalid Range</source>
         <translation>Auswahl ungültig</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="634"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="647"/>
         <source>The cursors don&apos;t define a valid range of samples.</source>
         <translation>Die Auswahl-Markierer geben keinen gültigen Datenbereich an.</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="646"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="659"/>
         <source>%1 files </source>
         <translation>%1-Dateien </translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="654"/>
-        <location filename="../pv/toolbars/mainbar.cpp" line="699"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="667"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="717"/>
         <source>All Files</source>
         <translation>Alle Dateien</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="658"/>
-        <location filename="../pv/toolbars/mainbar.cpp" line="817"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="671"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="848"/>
         <source>Save File</source>
         <translation>Speichern</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="670"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="683"/>
         <source>Export %1</source>
         <translation>%1 exportieren</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="696"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="714"/>
         <source>%1 files</source>
         <translation>%1-Dateien</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="707"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="725"/>
         <source>Import File</source>
         <translation>Dateiimport</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="716"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="734"/>
         <source>Import %1</source>
         <translation>%1 importieren</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="789"/>
-        <location filename="../pv/toolbars/mainbar.cpp" line="834"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="807"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="865"/>
         <source>Open File</source>
         <translation>Öffnen</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="789"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="807"/>
         <source>sigrok Sessions (*.sr);;All Files (*)</source>
         <translation>sigrok-Datenformat (*.sr);;Alle Dateien (*)</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="817"/>
-        <location filename="../pv/toolbars/mainbar.cpp" line="834"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="848"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="865"/>
         <source>PulseView Session Setups (*.pvs);;All Files (*)</source>
         <translation>Analysesitzungs-Konfigurationen (*.pvs);;Alle Dateien (*)</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="895"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="926"/>
         <source>Total sampling time: %1</source>
         <translatorcomment>Internal message</translatorcomment>
         <translation></translation>
     </message>
 </context>
 <context>
-    <name>pv::views::decoder_output::View</name>
+    <name>pv::views::decoder_binary::View</name>
     <message>
-        <location filename="../pv/views/decoder_output/view.cpp" line="83"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="83"/>
         <source>Decoder:</source>
         <translation>Dekoder:</translation>
     </message>
     <message>
-        <location filename="../pv/views/decoder_output/view.cpp" line="87"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="87"/>
         <source>Show data as</source>
         <translation>Zeige Daten als</translation>
     </message>
     <message>
-        <location filename="../pv/views/decoder_output/view.cpp" line="93"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="93"/>
         <source>Hexdump</source>
         <translation>Hex-Dump</translation>
     </message>
     <message>
-        <location filename="../pv/views/decoder_output/view.cpp" line="110"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="110"/>
         <source>&amp;Save...</source>
         <translation>&amp;Speichern...</translation>
     </message>
     <message>
-        <location filename="../pv/views/decoder_output/view.cpp" line="258"/>
-        <location filename="../pv/views/decoder_output/view.cpp" line="298"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="258"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="298"/>
         <source>Save Binary Data</source>
         <translation>Binäre Daten speichern</translation>
     </message>
     <message>
-        <location filename="../pv/views/decoder_output/view.cpp" line="258"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="258"/>
         <source>Binary Data Files (*.bin);;All Files (*)</source>
         <translation>Binärdateien (*.bin);;Alle Dateien (*)</translation>
     </message>
     <message>
-        <location filename="../pv/views/decoder_output/view.cpp" line="277"/>
-        <location filename="../pv/views/decoder_output/view.cpp" line="329"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="277"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="329"/>
         <source>Error</source>
         <translation>Fehler</translation>
     </message>
     <message>
-        <location filename="../pv/views/decoder_output/view.cpp" line="277"/>
-        <location filename="../pv/views/decoder_output/view.cpp" line="329"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="277"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="329"/>
         <source>File %1 could not be written to.</source>
         <translation>Konnte Datei %1 nicht speichern.</translation>
     </message>
     <message>
-        <location filename="../pv/views/decoder_output/view.cpp" line="298"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="298"/>
         <source>Hex Dumps (*.txt);;All Files (*)</source>
         <translation>Hex-Dumps (*.txt);;Alle Dateien (*)</translation>
     </message>
 </context>
+<context>
+    <name>pv::views::decoder_output::View</name>
+    <message>
+        <source>Decoder:</source>
+        <translation type="vanished">Dekoder:</translation>
+    </message>
+    <message>
+        <source>Show data as</source>
+        <translation type="vanished">Zeige Daten als</translation>
+    </message>
+    <message>
+        <source>Hexdump</source>
+        <translation type="vanished">Hex-Dump</translation>
+    </message>
+    <message>
+        <source>&amp;Save...</source>
+        <translation type="vanished">&amp;Speichern...</translation>
+    </message>
+    <message>
+        <source>Save Binary Data</source>
+        <translation type="vanished">Binäre Daten speichern</translation>
+    </message>
+    <message>
+        <source>Binary Data Files (*.bin);;All Files (*)</source>
+        <translation type="vanished">Binärdateien (*.bin);;Alle Dateien (*)</translation>
+    </message>
+    <message>
+        <source>Error</source>
+        <translation type="vanished">Fehler</translation>
+    </message>
+    <message>
+        <source>File %1 could not be written to.</source>
+        <translation type="vanished">Konnte Datei %1 nicht speichern.</translation>
+    </message>
+    <message>
+        <source>Hex Dumps (*.txt);;All Files (*)</source>
+        <translation type="vanished">Hex-Dumps (*.txt);;Alle Dateien (*)</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::tabular_decoder::AnnotationCollectionModel</name>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="56"/>
+        <source>Sample</source>
+        <translation>Sample</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="57"/>
+        <source>Time</source>
+        <translation>Zeit</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="58"/>
+        <source>Decoder</source>
+        <translation>Dekoder</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="59"/>
+        <source>Ann Row</source>
+        <translation>Kategorie</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="60"/>
+        <source>Ann Class</source>
+        <translation>Unterkategorie</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="61"/>
+        <source>Value</source>
+        <translation>Wert</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
+        <source>s</source>
+        <translation>s</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
+        <source>sa</source>
+        <translation>sa</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::tabular_decoder::View</name>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="176"/>
+        <source>Decoder:</source>
+        <translation>Dekoder:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="198"/>
+        <source>Hide Hidden Rows/Classes</source>
+        <translation>Verstecke nicht angezeigte (Unter-)Kategorien</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="202"/>
+        <source>&amp;Save...</source>
+        <translation>&amp;Speichern...</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="374"/>
+        <source>Save Annotations as CSV</source>
+        <translation>Dekodierte Werte als CSV speichern</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="374"/>
+        <source>CSV Files (*.csv);;Text Files (*.txt);;All Files (*)</source>
+        <translation>CSV-Dateien (*.csv);;Textdateien (*.txt);;Alle Dateien (*)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="442"/>
+        <source>Error</source>
+        <translation>Fehler</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="442"/>
+        <source>File %1 could not be written to.</source>
+        <translation>Konnte Datei %1 nicht speichern.</translation>
+    </message>
+</context>
 <context>
     <name>pv::views::trace::AnalogSignal</name>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="988"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="994"/>
         <source>Number of pos vertical divs</source>
         <translation>Anzahl Unterteilungen im Positiven</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="995"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1001"/>
         <source>Number of neg vertical divs</source>
         <translation>Anzahl Unterteilungen im Negativen</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1000"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1006"/>
         <source> pixels</source>
         <translation> Pixel</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1004"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1010"/>
         <source>Div height</source>
         <translation>Höhe einer Unterteilung</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1021"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1027"/>
         <source>V/div</source>
         <translation>V/div</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1025"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1031"/>
         <source>Vertical resolution</source>
         <translation>Vertikale Auflösung</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1034"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1040"/>
         <source>Autoranging</source>
         <translation>Automatische Skalierung</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1039"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1045"/>
         <source>none</source>
         <translation>keine</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1041"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1047"/>
         <source>to logic via threshold</source>
         <translation>zu Logik mittels Schwellwert</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1043"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1049"/>
         <source>to logic via schmitt-trigger</source>
         <translation>zu Logik mittels Schmitt-Trigger</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1049"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1055"/>
         <source>Conversion</source>
         <translation>Konvertierung</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1058"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1064"/>
         <source>Conversion threshold(s)</source>
         <translation>Konvertierungs-Schwellwert(e)</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1068"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1074"/>
         <source>analog</source>
         <translation>nur analog</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1069"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1075"/>
         <source>converted</source>
         <translation>nur konvertiert</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1070"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1076"/>
         <source>analog+converted</source>
         <translation>analog+konvertiert</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1075"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="1081"/>
         <source>Show traces for</source>
         <translation>Anzuzeigende Signale</translation>
     </message>
@@ -1193,12 +1340,12 @@ Bei einer Änderung benötigt PulseView eventuell einen Neustart, damit alles ko
         <translation>Intervall anzeigen</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/cursorpair.cpp" line="139"/>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="140"/>
         <source>Display frequency</source>
         <translation>Frequenz anzeigen</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/cursorpair.cpp" line="150"/>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="152"/>
         <source>Display samples</source>
         <translation>Samples anzeigen</translation>
     </message>
@@ -1206,118 +1353,118 @@ Bei einer Änderung benötigt PulseView eventuell einen Neustart, damit alles ko
 <context>
     <name>pv::views::trace::DecodeTrace</name>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="423"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="449"/>
         <source>&lt;p&gt;&lt;i&gt;No decoders in the stack&lt;/i&gt;&lt;/p&gt;</source>
         <translation>&lt;p&gt;&lt;i&gt;Keine Protokolldekoder vorhanden&lt;/i&gt;&lt;/p&gt;</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="434"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="460"/>
         <source>&lt;i&gt;* Required channels&lt;/i&gt;</source>
         <translation>&lt;i&gt;* Notwendige Kanäle&lt;/i&gt;</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="438"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="464"/>
         <source>Stack Decoder</source>
         <translation>Protokolldekoder stapeln</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="439"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="465"/>
         <source>Stack a higher-level decoder on top of this one</source>
         <translation>Weiteren Protokolldekoder auf diesen stapeln</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="453"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="479"/>
         <source>Delete</source>
         <translation>Löschen</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="495"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="521"/>
         <source>Resume decoding</source>
         <translation>Dekodierung fortsetzen</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="502"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="528"/>
         <source>Pause decoding</source>
         <translation>Dekodierung anhalten</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="510"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="536"/>
         <source>Copy annotation text to clipboard</source>
         <translation>Dekodierten Wert in die Zwischenablage kopieren</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="519"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="545"/>
         <source>Export all annotations</source>
         <translation>Alle dekodierten Werte exportieren</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="526"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="552"/>
         <source>Export all annotations for this row</source>
-        <translation>Alle dekodierten Werte dieser Zeile exportieren</translation>
+        <translation>Alle dekodierten Werte dieser Kategorie (Zeile) exportieren</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="535"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="561"/>
         <source>Export all annotations, starting here</source>
         <translation>Alle dekodierten Werte ab hier exportieren</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="542"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="568"/>
         <source>Export annotations for this row, starting here</source>
-        <translation>Alle dekodierten Werte dieser Zeile ab hier exportieren</translation>
+        <translation>Alle dekodierten Werte dieser Kategorie (Zeile) ab hier exportieren</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="551"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="577"/>
         <source>Export all annotations within cursor range</source>
         <translation>Alle dekodierten Werte innerhalb des gewählten Bereiches exportieren</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="558"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="584"/>
         <source>Export annotations for this row within cursor range</source>
-        <translation>Alle dekodierten Werte dieser Zeile innerhalb des gewählten Bereiches exportieren</translation>
+        <translation>Alle dekodierten Werte dieser Kategorie (Zeile) innerhalb des gewählten Bereiches exportieren</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1071"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1076"/>
         <source>%1:
 %2</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1115"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1120"/>
         <source>&lt;b&gt;%1&lt;/b&gt; (%2) %3</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1185"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1190"/>
         <source>Export annotations</source>
         <translation>Dekodierte Werte exportieren</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1185"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1190"/>
         <source>Text Files (*.txt);;All Files (*)</source>
         <translation>Textdateien (*.txt);;Alle Dateien (*)</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1226"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1255"/>
         <source>Error</source>
         <translation>Fehler</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1226"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1255"/>
         <source>File %1 could not be written to.</source>
         <translation>Konnte Datei %1 nicht speichern.</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1278"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1308"/>
         <source>Show this row</source>
-        <translation>Diese Zeile anzeigen</translation>
+        <translation>Diese Kategorie anzeigen</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1291"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1319"/>
         <source>Show All</source>
         <translation>Alle anzeigen</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1299"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1327"/>
         <source>Hide All</source>
         <translation>Alle verstecken</translation>
     </message>
@@ -1325,17 +1472,17 @@ Bei einer Änderung benötigt PulseView eventuell einen Neustart, damit alles ko
 <context>
     <name>pv::views::trace::Flag</name>
     <message>
-        <location filename="../pv/views/trace/flag.cpp" line="126"/>
+        <location filename="../pv/views/trace/flag.cpp" line="132"/>
         <source>Text</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/flag.cpp" line="135"/>
+        <location filename="../pv/views/trace/flag.cpp" line="141"/>
         <source>Delete</source>
         <translation>Löschen</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/flag.cpp" line="140"/>
+        <location filename="../pv/views/trace/flag.cpp" line="146"/>
         <source>Disable snapping</source>
         <translation>Einrasten deaktivieren</translation>
     </message>
@@ -1409,12 +1556,17 @@ Bei einer Änderung benötigt PulseView eventuell einen Neustart, damit alles ko
         <translation>Als Nullpunkt setzen</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/ruler.cpp" line="169"/>
+        <location filename="../pv/views/trace/ruler.cpp" line="162"/>
+        <source>Reset zero point</source>
+        <translation>Nullpunkt zurücksetzen</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="175"/>
         <source>Disable mouse hover marker</source>
         <translation>Mauszeigerbalken deaktivieren</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/ruler.cpp" line="169"/>
+        <location filename="../pv/views/trace/ruler.cpp" line="175"/>
         <source>Enable mouse hover marker</source>
         <translation>Mauszeigerbalken aktivieren</translation>
     </message>
@@ -1422,12 +1574,12 @@ Bei einer Änderung benötigt PulseView eventuell einen Neustart, damit alles ko
 <context>
     <name>pv::views::trace::Signal</name>
     <message>
-        <location filename="../pv/views/trace/signal.cpp" line="148"/>
+        <location filename="../pv/views/trace/signal.cpp" line="153"/>
         <source>Name</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/signal.cpp" line="159"/>
+        <location filename="../pv/views/trace/signal.cpp" line="164"/>
         <source>Disable</source>
         <translation>Deaktivieren</translation>
     </message>
@@ -1473,7 +1625,7 @@ Bei einer Änderung benötigt PulseView eventuell einen Neustart, damit alles ko
 <context>
     <name>pv::views::trace::TimeMarker</name>
     <message>
-        <location filename="../pv/views/trace/timemarker.cpp" line="186"/>
+        <location filename="../pv/views/trace/timemarker.cpp" line="191"/>
         <source>Time</source>
         <translation>Zeit</translation>
     </message>
diff --git a/l10n/es_MX.ts b/l10n/es_MX.ts
new file mode 100644 (file)
index 0000000..77afc7f
--- /dev/null
@@ -0,0 +1,2248 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="es_MX">
+<context>
+    <name>Application</name>
+    <message>
+        <location filename="../pv/application.cpp" line="137"/>
+        <source>Some parts of the application may still use the previous language. Re-opening the affected windows or restarting the application will remedy this.</source>
+        <translation>Algunas partes de la aplicación podrían seguir usando el idioma anterior. Volver a abrir las ventanas afectadas o reiniciar la aplicación solucionará esto.</translation>
+    </message>
+</context>
+<context>
+    <name>QApplication</name>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="274"/>
+        <source>Error when scanning device driver &apos;%1&apos;: %2</source>
+        <translation>Error al escanear el controlador del dispositivo &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="70"/>
+        <source>Querying config key %1 is not allowed</source>
+        <translation>No se permite consultar la clave de configuración %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="79"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>La consulta de la clave de configuración %1 resultó en %2</translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="93"/>
+        <source>Unknown type supplied when attempting to query %1</source>
+        <translation>Tipo desconocido proporcionado al intentar consultar %1</translation>
+    </message>
+</context>
+<context>
+    <name>QHexView</name>
+    <message>
+        <location filename="../pv/views/decoder_binary/QHexView.cpp" line="339"/>
+        <source>No data available</source>
+        <translation>Datos no disponibles</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../main.cpp" line="116"/>
+        <source>Stack trace of previous crash:</source>
+        <translation>Seguimiento de pila del fallo anterior:</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="130"/>
+        <source>Don&apos;t show this message again</source>
+        <translation>No volver a mostrar este mensaje</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="133"/>
+        <source>When %1 last crashed, it created a stack trace.
+A human-readable form has been saved to disk and was written to the log. You may access it from the settings dialog.</source>
+        <translation>Cuando %1 se bloqueó por última vez, creó un seguimiento de pila.
+Se guardó un formulario legible para humanosen el disco y fue escrito en el log. Puedes acceder a el desde el dialogo de configuración.</translation>
+    </message>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="65"/>
+        <source>Cancel</source>
+        <translation>Cancelar</translation>
+    </message>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="96"/>
+        <source>Scanning for devices that driver %1 can access...</source>
+        <translation>Buscando dispositivos a los que el controlador %1 puede acceder...</translation>
+    </message>
+</context>
+<context>
+    <name>pv::MainWindow</name>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="70"/>
+        <source>PulseView</source>
+        <translation>PulseView</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="284"/>
+        <source>Decoder Selector</source>
+        <translation>Selección de decodificador</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="337"/>
+        <source>Session %1</source>
+        <translation>Sesión %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="519"/>
+        <source>Create New Session</source>
+        <translation>Crear nueva sesión</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="525"/>
+        <source>Start/Stop Acquisition</source>
+        <translation>Iniciar/Detener adquisición</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="533"/>
+        <source>Settings</source>
+        <translation>Ajustes</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="589"/>
+        <source>Reload</source>
+        <translation>Recargar</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="589"/>
+        <location filename="../pv/mainwindow.cpp" line="592"/>
+        <source>Run</source>
+        <translation>Ejecutar</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="598"/>
+        <source>Stop</source>
+        <translation>Detener</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="644"/>
+        <location filename="../pv/mainwindow.cpp" line="867"/>
+        <location filename="../pv/mainwindow.cpp" line="893"/>
+        <source>Confirmation</source>
+        <translation>Confirmación</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="645"/>
+        <source>There is unsaved data. Close anyway?</source>
+        <translation>Hay datos sin guardar. ¿Cerrar de todos modos?</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="868"/>
+        <location filename="../pv/mainwindow.cpp" line="894"/>
+        <source>This session contains unsaved data. Close it anyway?</source>
+        <translation>Esta sesión contiene datos sin guardar. ¿Cerrar de todos modos?</translation>
+    </message>
+</context>
+<context>
+    <name>pv::Session</name>
+    <message>
+        <location filename="../pv/session.cpp" line="559"/>
+        <source>Failed to select device</source>
+        <translatorcomment>Si en el panel &quot;Sources and forms&quot; la cadena de texto está dentro de &quot;show_session_error(tr(&quot;Failed to ...&quot;),e)&quot; traducir &quot;Failed &quot; como &quot;Error&quot;</translatorcomment>
+        <translation>Error al seleccionar dispositivo</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="616"/>
+        <source>Failed to open device</source>
+        <translation>Error al abrir dispositivo</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="722"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+    <message>
+        <source>Unexpected input format: %s</source>
+        <translation type="vanished">Formato de entrada inesperado: %s</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="396"/>
+        <source>Can&apos;t restore generated signal of unknown type %1 (%2)</source>
+        <translation>No se puede restaurar la señal generada de tipo desconocido %1 ( %2)</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="723"/>
+        <source>Unexpected input format: %1</source>
+        <translation>Formato de entrada inesperado: %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="758"/>
+        <source>Failed to load %1</source>
+        <translation>Error al cargar %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="797"/>
+        <source>No active device set, can&apos;t start acquisition.</source>
+        <translation>No hay un dispositivo activo configurado, no se puede iniciar la adquisición.</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="810"/>
+        <source>No channels enabled.</source>
+        <translation>No hay canales habilitados.</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="1318"/>
+        <source>Out of memory, acquisition stopped.</source>
+        <translation>Sin memoria, la adquisición se detuvo.</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="1525"/>
+        <source>Can&apos;t handle more than 64 logic channels.</source>
+        <translation>No puede manejar más de 64 canales lógicos.</translation>
+    </message>
+</context>
+<context>
+    <name>pv::StoreSession</name>
+    <message>
+        <location filename="../pv/storesession.cpp" line="114"/>
+        <source>Can&apos;t save logic channel without data.</source>
+        <translation>No se puede guardar el canal lógico sin datos.</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="130"/>
+        <source>Can&apos;t save analog channel without data.</source>
+        <translation>No se puede guardar el canal analógico sin datos.</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="142"/>
+        <source>No channels enabled.</source>
+        <translation>No hay canales habilitados.</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="167"/>
+        <source>Can&apos;t save range without sample data.</source>
+        <translation>No se puede guardar el rango sin datos de muestra.</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="191"/>
+        <location filename="../pv/storesession.cpp" line="298"/>
+        <location filename="../pv/storesession.cpp" line="313"/>
+        <source>Error while saving: </source>
+        <translation>Error al guardar: </translation>
+    </message>
+</context>
+<context>
+    <name>pv::binding::Device</name>
+    <message>
+        <location filename="../pv/binding/device.cpp" line="82"/>
+        <source>Note for device developers: Ignoring device configuration capability &apos;%1&apos; as it is missing GET and/or SET</source>
+        <translation>Nota para desarrolladores de dispositivos: Ignorar la capacidad de configuración del dispositivo &apos;%1&apos;, ya que falta GET y/o SET</translation>
+    </message>
+    <message>
+        <location filename="../pv/binding/device.cpp" line="107"/>
+        <source>No Limit</source>
+        <translation>Sín límite</translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::DecodeSignal</name>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="223"/>
+        <source>No decoders</source>
+        <translation>Sin decodificadores</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="230"/>
+        <source>There are no channels assigned to this decoder</source>
+        <translation>No hay canales asignados a este decodificador</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="244"/>
+        <source>One or more required channels have not been specified</source>
+        <translation>No se han especificado uno o más canales requeridos</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="260"/>
+        <source>No input data</source>
+        <translation>Sin datos de entrada</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="1325"/>
+        <source>Decoder reported an error</source>
+        <translation>El decodificador reportó un error</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="1484"/>
+        <source>Failed to create decoder instance</source>
+        <translation>Error al crear la instancia del decodificador</translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::MathSignal</name>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="107"/>
+        <source>Math%1</source>
+        <translation>Math%1</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="306"/>
+        <source>No expression defined, nothing to do</source>
+        <translation>Sin expresión definida, nada que hacer</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="345"/>
+        <source>%1 at line %2, column %3: %4</source>
+        <translation>%1 en línea %2, columna %3: %4</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="364"/>
+        <location filename="../pv/data/mathsignal.cpp" line="536"/>
+        <source>&quot;%1&quot; isn&apos;t a valid analog signal</source>
+        <translation>&quot;%1&quot; no es una señal analógica válida</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="374"/>
+        <location filename="../pv/data/mathsignal.cpp" line="611"/>
+        <source>No data will be generated as %1 must be enabled</source>
+        <translation>No se generarán datos ya que %1 debe estar habilitado</translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::SignalBase</name>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="525"/>
+        <source>Signal average</source>
+        <translation>Promedio de la señal</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="526"/>
+        <source>0.9V (for 1.8V CMOS)</source>
+        <translation>0.9V (para 1.8V CMOS)</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="527"/>
+        <source>1.8V (for 3.3V CMOS)</source>
+        <translation>1.8V (para 3.3V CMOS)</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="528"/>
+        <source>2.5V (for 5.0V CMOS)</source>
+        <translation>2.5V (para 5.0V CMOS)</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="529"/>
+        <source>1.5V (for TTL)</source>
+        <translation>1.5V (para TTL)</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="534"/>
+        <source>Signal average +/- 15%</source>
+        <translation>Promedio de la señal +/- 15%</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="535"/>
+        <source>0.3V/1.2V (for 1.8V CMOS)</source>
+        <translation>0.3V/1.2V (para 1.8V CMOS)</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="536"/>
+        <source>0.7V/2.5V (for 3.3V CMOS)</source>
+        <translation>0.7V/2.5V (para 3.3V CMOS)</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="537"/>
+        <source>1.3V/3.7V (for 5.0V CMOS)</source>
+        <translation>1.3V/3.7V (para 5.0V CMOS)</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="538"/>
+        <source>0.8V/2.0V (for TTL)</source>
+        <translation>0.8V/2.0V (para TTL)</translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::Connect</name>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="58"/>
+        <source>&amp;Scan for devices using driver above</source>
+        <translation>E&amp;scanea por dispositivos utilizando el controlador de arriba</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="63"/>
+        <source>Connect to Device</source>
+        <translation>Conectarse al dispositivo</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="75"/>
+        <source>Step 1: Choose the driver</source>
+        <translation>Paso 1: Elige el controlador</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="79"/>
+        <source>&amp;USB</source>
+        <translation>&amp;USB</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="80"/>
+        <source>Serial &amp;Port</source>
+        <translation>&amp;Puerto serial</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="81"/>
+        <source>&amp;TCP/IP</source>
+        <translation>&amp;TCP/IP</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="116"/>
+        <source>Protocol:</source>
+        <translation>Protocolo:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="134"/>
+        <source>Step 2: Choose the interface</source>
+        <translation>Paso 2: Elige la interfaz</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="140"/>
+        <source>Step 3: Scan for devices</source>
+        <translation>Paso 3: Escanea por dispositivos</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="146"/>
+        <source>Step 4: Select the device</source>
+        <translation>Paso 4: Selecciona el dispositivo</translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::Settings</name>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="134"/>
+        <location filename="../pv/dialogs/settings.cpp" line="213"/>
+        <source>General</source>
+        <translation>General</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="143"/>
+        <source>Views</source>
+        <translation>Vistas</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="153"/>
+        <location filename="../pv/dialogs/settings.cpp" line="415"/>
+        <source>Decoders</source>
+        <translation>Decodificadores</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="163"/>
+        <source>About</source>
+        <translation>Acerca de</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="172"/>
+        <source>Logging</source>
+        <translation>Registros</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="241"/>
+        <source>User interface language</source>
+        <translation>Idioma de la interfaz de usuario</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="252"/>
+        <source>User interface theme</source>
+        <translation>Tema de la interfaz de usuario</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="254"/>
+        <source>(You may need to restart PulseView for all UI elements to update)</source>
+        <translation>(Es posible que deba reiniciar PulseView para que se actualicen todos los elementos de la interfaz de usuario)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="260"/>
+        <source>System Default</source>
+        <translation>Por defecto del sistema</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="273"/>
+        <source>Qt widget style</source>
+        <translation>Estilo de widget Qt</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="275"/>
+        <source>(Dark themes look best with the Fusion style)</source>
+        <translation>(Los temas oscuros se ven mejor con el estilo Fusion)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="282"/>
+        <source>Save session &amp;setup along with .sr file</source>
+        <translation>Guardar &amp;configuración de sesión junto al archivo .sr</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="286"/>
+        <source>Start acquisition for all open sessions when clicking &apos;Run&apos;</source>
+        <translation>Iniciar adquisición para todas las sesiones abiertas al dar clic en &apos;Ejecutar&apos;</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="301"/>
+        <source>Trace View</source>
+        <translation>Vista de señales</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="309"/>
+        <source>Use colored trace &amp;background</source>
+        <translation>Usar &amp;fondo de trazos coloreado</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="313"/>
+        <source>Constantly perform &amp;zoom-to-fit during acquisition</source>
+        <translation>Realizar constantemente zoom para &amp;ajustar durante la adquisición</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="317"/>
+        <source>Perform a zoom-to-&amp;fit when acquisition stops</source>
+        <translation>Realizar un zoom para ajustar cuando la adquisición se &amp;detenga</translation>
+    </message>
+    <message>
+        <source>Show time zero at the trigger</source>
+        <translation type="vanished">Mostrar el tiempo cero en el trigger</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="325"/>
+        <source>Always keep &amp;newest samples at the right edge during capture</source>
+        <translation>Mantener siempre las muestras más &amp;recientes en el borde derecho durante la captura</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="333"/>
+        <source>Show data &amp;sampling points</source>
+        <translation>Mostrar puntos de datos mue&amp;streados</translation>
+    </message>
+    <message>
+        <source>Fill high areas of logic signals</source>
+        <translation type="vanished">Rellenar áreas altas de señales lógicas</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="321"/>
+        <source>Show time zero at the &amp;trigger</source>
+        <translation>Mostrar tiempo cero en el &amp;disparo</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="329"/>
+        <source>Allow &amp;vertical dragging in the view area</source>
+        <translation>Permitir arrastre &amp;vertical in el área de vista</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="337"/>
+        <source>Fill &amp;high areas of logic signals</source>
+        <translation>Llenar áreas en &amp;alto de señales lógicas</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="344"/>
+        <source>Color to fill high areas of logic signals with</source>
+        <translation>Color para llenar áreas altas de señales lógicas</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="348"/>
+        <source>Show analog minor grid in addition to div grid</source>
+        <translation>Mostrar cuadrícula menor analógica además de cuadrícula por división</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="352"/>
+        <source>Highlight mouse cursor using a vertical marker line</source>
+        <translation>Resaltar el cursor del mouse usando una línea de marcador vertical</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="356"/>
+        <source>Keep active item on ruler selected when editing popup is closed</source>
+        <translation>Mantener el elemento activo seleccionado en la regla cuando se cierre la ventana de edición emergente</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="360"/>
+        <location filename="../pv/dialogs/settings.cpp" line="386"/>
+        <location filename="../pv/dialogs/settings.cpp" line="395"/>
+        <source> pixels</source>
+        <translation> píxeles</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="365"/>
+        <source>Maximum distance from edges before markers snap to them</source>
+        <translation>Distancia máxima desde los bordes antes de que los marcadores se ajusten a ellos</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="372"/>
+        <source>Color to fill cursor area with</source>
+        <translation>Color para llenar el área del cursor</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="375"/>
+        <source>None</source>
+        <translation>Ninguna</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="376"/>
+        <source>Background</source>
+        <translation>Fondo</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="377"/>
+        <source>Dots</source>
+        <translation>Puntos</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="382"/>
+        <source>Conversion threshold display mode (analog traces only)</source>
+        <translation>Modo de visualización del umbral de conversión (solo trazos analógicos)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="391"/>
+        <source>Default analog trace div height</source>
+        <translation>Altura de división de trazo analógico por defecto</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="400"/>
+        <source>Default logic trace height</source>
+        <translation>Altura de trazo lógico por defecto</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="423"/>
+        <source>Allow configuration of &amp;initial signal state</source>
+        <translation>Permitir configuración de estado de señal &amp;inicial</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="427"/>
+        <source>Always show all &amp;rows, even if no annotation is visible</source>
+        <translation>Mostrar siempre todas las &amp;filas, incluso si no hay ninguna anotación visible</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="435"/>
+        <source>Annotation export format</source>
+        <translation>Formato de exportación de anotaciones</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="436"/>
+        <source>%s = sample range; %d: decoder name; %r: row name; %c: class name</source>
+        <translation>%s = rango de muestra; %d: nombre del decodificador; %r: nombre de fila; %c: nombre de clase</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="439"/>
+        <source>%1: longest annotation text; %a: all annotation texts; %q: use quotation marks</source>
+        <translation>%1: texto de anotación más largo; %a: todos los textos de anotación; %q: use comillas</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="459"/>
+        <source>%1&lt;br /&gt;&lt;a href=&quot;http://%2&quot;&gt;%2&lt;/a&gt;</source>
+        <translation>%1&lt;br /&gt;&lt;a href=&quot;http://%2&quot;&gt;%2&lt;/a&gt;</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="460"/>
+        <source>GNU GPL, version 3 or later</source>
+        <translation>GNU GPL, versión 3 o posterior</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="471"/>
+        <source>Versions, libraries and features:</source>
+        <translation>Versiones, bibliotecas y características:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="478"/>
+        <source>Firmware search paths:</source>
+        <translation>Rutas de búsqueda de firmware:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="485"/>
+        <source>Protocol decoder search paths:</source>
+        <translation>Ruta de búsqueda del decodificador de protocolo:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="488"/>
+        <source>&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;(Note: Set environment variable SIGROKDECODE_DIR to add a custom directory)&lt;/td&gt;&lt;/tr&gt;</source>
+        <translation>&lt;tr&gt; &lt;td colspan = &quot;2&quot;&gt; (Nota: Establecer variable de entorno SIGROKDECODE_DIR para agregar un directorio personalizado) &lt;/td&gt; &lt;/tr&gt;</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="493"/>
+        <source>Supported hardware drivers:</source>
+        <translation>Controladores de hardware soportados:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="500"/>
+        <source>Supported input formats:</source>
+        <translation>Formatos de entrada soportados:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="507"/>
+        <source>Supported output formats:</source>
+        <translation>Formatos de salida soportados:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="515"/>
+        <source>Supported protocol decoders:</source>
+        <translation>Decodificadores de protocolo soportados:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="523"/>
+        <source>Available Translations:</source>
+        <translation>Traducciones disponibles:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="571"/>
+        <source>Log level:</source>
+        <translation>Nivel de registro:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="576"/>
+        <source> lines</source>
+        <translation> líneas</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="585"/>
+        <source>Length of background buffer:</source>
+        <translation>Longitud del búfer de fondo:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="591"/>
+        <source>&amp;Save to File</source>
+        <translation>&amp;Guardar en archivo</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="598"/>
+        <source>&amp;Pop out</source>
+        <translation>Des&amp;plegar</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="667"/>
+        <source>You selected a dark theme.
+Should I set the user-adjustable colors to better suit your choice?
+
+Please keep in mind that PulseView may need a restart to display correctly.</source>
+        <translation>Seleccionaste un tema oscuro.
+¿Debería de establecer los colores ajustables por el usuario a los que mejor se ajustan a tu elección?
+
+Por favor ten en cuenta que Pulseview tal vez se tenga que reiniciar para mostrarse correctamente.</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="673"/>
+        <source>You selected a bright theme.
+Should I set the user-adjustable colors to better suit your choice?
+
+Please keep in mind that PulseView may need a restart to display correctly.</source>
+        <translation>Seleccionaste un tema claro.
+¿Debería de establecer los colores ajustables por el usuario a los que mejor se ajustan a tu elección?
+
+Por favor ten en cuenta que Pulseview tal vez se tenga que reiniciar para mostrarse correctamente.</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="844"/>
+        <source>Save Log</source>
+        <translation>Guardar registro</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="844"/>
+        <source>Log Files (*.txt *.log);;All Files (*)</source>
+        <translation>Archivos de registro (*.txt *.log);;Todos los archivos (*)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="856"/>
+        <source>Success</source>
+        <translation>Éxito</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="856"/>
+        <source>Log saved to %1.</source>
+        <translation>Registro guardado en %1.</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="866"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="866"/>
+        <source>File %1 could not be written to.</source>
+        <translation>No se pudo escribir en el archivo%1.</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="880"/>
+        <source>%1 Log</source>
+        <translation>%1 Log</translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::StoreProgress</name>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="44"/>
+        <source>Saving...</source>
+        <translation>Guardando...</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="44"/>
+        <source>Cancel</source>
+        <translation>Cancelar</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="89"/>
+        <source>Failed to save session.</source>
+        <translation>Error al guardar sesión.</translation>
+    </message>
+</context>
+<context>
+    <name>pv::popups::Channels</name>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="62"/>
+        <location filename="../pv/popups/channels.cpp" line="63"/>
+        <location filename="../pv/popups/channels.cpp" line="278"/>
+        <location filename="../pv/popups/channels.cpp" line="305"/>
+        <source>All</source>
+        <translation>Todo</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="64"/>
+        <location filename="../pv/popups/channels.cpp" line="65"/>
+        <source>Logic</source>
+        <translation>Lógico</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="66"/>
+        <location filename="../pv/popups/channels.cpp" line="67"/>
+        <source>Analog</source>
+        <translation>Análogico</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="68"/>
+        <source>Named</source>
+        <translation>Nombrado</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="69"/>
+        <source>Unnamed</source>
+        <translation>Sin nombre</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="70"/>
+        <source>Changing</source>
+        <translation>Cambiando</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="71"/>
+        <source>Non-changing</source>
+        <translation>Sin cambios</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="141"/>
+        <source>Disable: </source>
+        <translation>Deshabilitar: </translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="149"/>
+        <source>Enable: </source>
+        <translation>Habilitar: </translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="286"/>
+        <location filename="../pv/popups/channels.cpp" line="306"/>
+        <source>None</source>
+        <translation>Ninguna</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Bool</name>
+    <message>
+        <location filename="../pv/prop/bool.cpp" line="51"/>
+        <location filename="../pv/prop/bool.cpp" line="82"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>La consulta de la clave de configuración %1 resultó en %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Double</name>
+    <message>
+        <location filename="../pv/prop/double.cpp" line="65"/>
+        <location filename="../pv/prop/double.cpp" line="96"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>La consulta de la clave de configuración %1 resultó en %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Enum</name>
+    <message>
+        <location filename="../pv/prop/enum.cpp" line="113"/>
+        <location filename="../pv/prop/enum.cpp" line="176"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>La consulta de la clave de configuración %1 resultó en %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Int</name>
+    <message>
+        <location filename="../pv/prop/int.cpp" line="63"/>
+        <location filename="../pv/prop/int.cpp" line="127"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>La consulta de la clave de configuración %1 resultó en %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::String</name>
+    <message>
+        <location filename="../pv/prop/string.cpp" line="59"/>
+        <location filename="../pv/prop/string.cpp" line="84"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>La consulta de la clave de configuración %1 resultó en %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::subwindows::decoder_selector::DecoderCollectionModel</name>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="40"/>
+        <source>Decoder</source>
+        <translation>Decodificador</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="41"/>
+        <source>Name</source>
+        <translation>Nombre</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="42"/>
+        <source>ID</source>
+        <translation>ID</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="49"/>
+        <source>All Decoders</source>
+        <translation>Todos los decodificadores</translation>
+    </message>
+</context>
+<context>
+    <name>pv::subwindows::decoder_selector::SubWindow</name>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="49"/>
+        <source>Select a decoder to see its description here.</source>
+        <translation>Selecciona un decodificador para ver su descripción aquí.</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="248"/>
+        <source>, %1</source>
+        <translation>, %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="265"/>
+        <source>&lt;p align=&apos;right&apos;&gt;Tags: %1&lt;/p&gt;</source>
+        <translation>&lt;p align=&apos;right&apos;&gt;Etiquetas: %1&lt;/p&gt;</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="312"/>
+        <source>Protocol decoder &lt;b&gt;%1&lt;/b&gt; requires input type &lt;b&gt;%2&lt;/b&gt; which several decoders provide.&lt;br&gt;Choose which one to use:&lt;br&gt;</source>
+        <translation>Decodificador de protocolo &lt;b&gt;%1&lt;/b&gt; requiere tipo de entrada &lt;b&gt;%2&lt;/b&gt; que proporcionan varios decodificadores.&lt;br&gt;Elige cúal usar:&lt;br&gt;</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="320"/>
+        <source>Choose Decoder</source>
+        <translation>Elige decodificador</translation>
+    </message>
+</context>
+<context>
+    <name>pv::toolbars::MainBar</name>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="124"/>
+        <source>New &amp;View</source>
+        <translation>Nueva &amp;Vista</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="130"/>
+        <source>&amp;Open...</source>
+        <translation>&amp;Abrir...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="141"/>
+        <source>Restore Session Setu&amp;p...</source>
+        <translation>Restaurar Configu&amp;ración de Sesión...</translation>
+    </message>
+    <message>
+        <source>&amp;Save As...</source>
+        <translation type="vanished">G&amp;uardar Como...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="145"/>
+        <source>&amp;Save...</source>
+        <translation>&amp;Guardar...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="156"/>
+        <source>Save &amp;As...</source>
+        <translation>Guardar Como...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="162"/>
+        <source>Save Selected &amp;Range As...</source>
+        <translation>Guardar &amp;Rango Seleccionado Como...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="173"/>
+        <source>Save Session Setu&amp;p...</source>
+        <translation>Guardar Confi&amp;guración de Sesión...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="179"/>
+        <source>&amp;Export</source>
+        <translation>&amp;Exportar</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="185"/>
+        <source>&amp;Import</source>
+        <translation>&amp;Importar</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="189"/>
+        <source>&amp;Connect to Device...</source>
+        <translation>&amp;Conectar a Dispositivo...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="251"/>
+        <source>Add protocol decoder</source>
+        <translation>Agregar decodificador de protocolo</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="261"/>
+        <source>Add math signal</source>
+        <translation>Agregar señal matemática</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="277"/>
+        <source>Configure Device</source>
+        <translation>Configurar Dispositivo</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="281"/>
+        <source>Configure Channels</source>
+        <translation>Configurar Canales</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="395"/>
+        <source>Failed to get sample rate list:</source>
+        <translation>Error al obtener la lista de frecuencia de muestreo:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="458"/>
+        <source>Failed to get sample rate:</source>
+        <translation>Error al obtener la frecuencia de muestreo:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="499"/>
+        <source>Failed to get sample limit list:</source>
+        <translation>Error al obtener la lista de límites de muestra:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="589"/>
+        <source>Failed to configure samplerate:</source>
+        <translation>Error al configurar frecuencia de muestreo:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="616"/>
+        <source>Failed to configure sample count:</source>
+        <translation>Error al configurar cuenta de muestras:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="654"/>
+        <source>Missing Cursors</source>
+        <translation>Cursores Faltantes</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="654"/>
+        <source>You need to set the cursors before you can save the data enclosed by them to a session file (e.g. using the Show Cursors button).</source>
+        <translation>Debes configurar los cursores antes de poder guardar los datos encerrados en un archivo de sesión (por ejemplo, usando el botón Mostrar Cursores).</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="672"/>
+        <source>Invalid Range</source>
+        <translation>Rango Inválido</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="672"/>
+        <source>The cursors don&apos;t define a valid range of samples.</source>
+        <translation>Los cursores no definen un rango válido de muestras.</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="684"/>
+        <source>%1 files </source>
+        <translation>%1 archivos </translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="692"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="742"/>
+        <source>All Files</source>
+        <translation>Todos los archivos</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="696"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="873"/>
+        <source>Save File</source>
+        <translation>Guardar Archivo</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="708"/>
+        <source>Export %1</source>
+        <translation>Exportar %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="739"/>
+        <source>%1 files</source>
+        <translation>%1 archivos</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="750"/>
+        <source>Import File</source>
+        <translation>Importar Archivo</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="759"/>
+        <source>Import %1</source>
+        <translation>Importar %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="832"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="890"/>
+        <source>Open File</source>
+        <translation>Abrir Archivo</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="832"/>
+        <source>sigrok Sessions (*.sr);;All Files (*)</source>
+        <translation>Sesiones sigrok (*sr);;Todos los archivos (*)</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="873"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="890"/>
+        <source>PulseView Session Setups (*.pvs);;All Files (*)</source>
+        <translation>Configurciones de Sesión de PulseView (*.pvs);;Todos los Archivos (*)</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="958"/>
+        <source>Total sampling time: %1</source>
+        <translation>Tiempo de muestreo total: %1</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::decoder_binary::View</name>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="83"/>
+        <source>Decoder:</source>
+        <translation>Decodificador:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="87"/>
+        <source>Show data as</source>
+        <translation>Muestra datos como</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="93"/>
+        <source>Hexdump</source>
+        <translation>Volcado hexadecimal</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="110"/>
+        <source>&amp;Save...</source>
+        <translation>&amp;Guardar...</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="270"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="310"/>
+        <source>Save Binary Data</source>
+        <translation>Guardar Datos Binarios</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="270"/>
+        <source>Binary Data Files (*.bin);;All Files (*)</source>
+        <translation>Archivos de Datos Binarios (*.bin);;Todos los archivos (*)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="289"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="349"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="289"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="349"/>
+        <source>File %1 could not be written to.</source>
+        <translation>No se pudo escribir en el archivo%1.</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="310"/>
+        <source>Hex Dumps (*.txt);;All Files (*)</source>
+        <translation>Volcados hexadecimales (*.txt);;Todos los archivos (*)</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::tabular_decoder::AnnotationCollectionModel</name>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="56"/>
+        <source>Sample</source>
+        <translation>Muestra</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="57"/>
+        <source>Time</source>
+        <translation>Tiempo</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="58"/>
+        <source>Decoder</source>
+        <translation>Decodificador</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="59"/>
+        <source>Ann Row</source>
+        <translation>Fila de anotación</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="60"/>
+        <source>Ann Class</source>
+        <translation>Clase de anotación</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="61"/>
+        <source>Value</source>
+        <translation>Valor</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
+        <source>s</source>
+        <translation>s</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
+        <source>sa</source>
+        <translation>sa</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::tabular_decoder::View</name>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="176"/>
+        <source>Decoder:</source>
+        <translation>Decodificador:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="198"/>
+        <source>Hide Hidden Rows/Classes</source>
+        <translation>Ocultar Filas/Columnas ocultas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="202"/>
+        <source>&amp;Save...</source>
+        <translation>&amp;Guardar...</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="378"/>
+        <source>Save Annotations as CSV</source>
+        <translation>Guardar anotaciones como CSV</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="378"/>
+        <source>CSV Files (*.csv);;Text Files (*.txt);;All Files (*)</source>
+        <translation>Archivos CSV (*.csv);;Archivos de texto (*.txt);;Todos los archivos (*)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="446"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="446"/>
+        <source>File %1 could not be written to.</source>
+        <translation>No se pudo escribir en el archivo%1.</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::AnalogSignal</name>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="802"/>
+        <source>Number of pos vertical divs</source>
+        <translation>Número de divisiones verticales positivas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="810"/>
+        <source>Number of neg vertical divs</source>
+        <translation>Número de divisiones verticales negativas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="815"/>
+        <source> pixels</source>
+        <translation> píxeles</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="819"/>
+        <source>Div height</source>
+        <translation>Altura de división</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="837"/>
+        <source>V/div</source>
+        <translation>V/división</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="841"/>
+        <source>Vertical resolution</source>
+        <translation>Resolución vertical</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="850"/>
+        <source>Autoranging</source>
+        <translation>Autorango</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="855"/>
+        <source>none</source>
+        <translation>ninguna</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="857"/>
+        <source>to logic via threshold</source>
+        <translation>a nivel lógico a partir de umbral</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="859"/>
+        <source>to logic via schmitt-trigger</source>
+        <translation>a nivel lógico a partir de schmitt-trigger</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="865"/>
+        <source>Conversion</source>
+        <translation>Conversión</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="874"/>
+        <source>Conversion threshold(s)</source>
+        <translation>Umbral(es) de conversión</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="884"/>
+        <source>analog</source>
+        <translation>señal análogica</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="885"/>
+        <source>converted</source>
+        <translation>señal convertida</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="886"/>
+        <source>analog+converted</source>
+        <translation>señales analógica + convertida</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="891"/>
+        <source>Show traces for</source>
+        <translation>Mostrar trazos de</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Cursor</name>
+    <message>
+        <location filename="../pv/views/trace/cursor.cpp" line="97"/>
+        <source>Disable snapping</source>
+        <translation>Deshabilita snapping</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::CursorPair</name>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="128"/>
+        <source>Display interval</source>
+        <translation>Mostrar intervalo</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="140"/>
+        <source>Display frequency</source>
+        <translation>Mostrar frecuencia</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="152"/>
+        <source>Display samples</source>
+        <translation>Mostrar muestras</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::DecodeTrace</name>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="456"/>
+        <source>&lt;p&gt;&lt;i&gt;No decoders in the stack&lt;/i&gt;&lt;/p&gt;</source>
+        <translation>&lt;p&gt;&lt;i&gt;No hay decodificadores en la pila.&lt;/i&gt;&lt;/p&gt;</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="467"/>
+        <source>&lt;i&gt;* Required channels&lt;/i&gt;</source>
+        <translation>&lt;i&gt;* Canales requeridos&lt;/i&gt;</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="471"/>
+        <source>Stack Decoder</source>
+        <translation>Apilar Decodificador</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="472"/>
+        <source>Stack a higher-level decoder on top of this one</source>
+        <translation>Apilar un decodificador de nivel superior encima de este</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="486"/>
+        <source>Delete</source>
+        <translation>Eliminar</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="528"/>
+        <source>Resume decoding</source>
+        <translation>Reanudar decodificación</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="535"/>
+        <source>Pause decoding</source>
+        <translation>Pausar decodificación</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="543"/>
+        <source>Copy annotation text to clipboard</source>
+        <translation>Copiar texto de anotación al portapapeles</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="552"/>
+        <source>Export all annotations</source>
+        <translation>Exportar todas las anotaciones</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="559"/>
+        <source>Export all annotations for this row</source>
+        <translation>Exportar todas las anotaciones para esta fila</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="568"/>
+        <source>Export all annotations, starting here</source>
+        <translation>Exportar todas las anotaciones, comenzando aquí</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="575"/>
+        <source>Export annotations for this row, starting here</source>
+        <translation>Exportar todas las anotaciones para esta fila, comenzando aquí</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="584"/>
+        <source>Export all annotations within cursor range</source>
+        <translation>Exportar todas las anotaciones dentro del rango del cursor</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="591"/>
+        <source>Export annotations for this row within cursor range</source>
+        <translation>Exportar todas las anotaciones para esta fila dentro del rango del cursor</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1079"/>
+        <source>%1:
+%2</source>
+        <translation>%1\n%2</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1123"/>
+        <source>&lt;b&gt;%1&lt;/b&gt; (%2) %3</source>
+        <translation>&lt;b&gt;%1&lt;/b&gt; (%2) %3</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1192"/>
+        <source>Export annotations</source>
+        <translation>Exportar anotaciones</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1192"/>
+        <source>Text Files (*.txt);;All Files (*)</source>
+        <translation>Archivos de texto (*.txt);;Todos los archivos (*)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1257"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1257"/>
+        <source>File %1 could not be written to.</source>
+        <translation>No se pudo escribir en el archivo%1.</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1324"/>
+        <source>Show this row</source>
+        <translation>Mostrar esta fila</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1335"/>
+        <source>Show All</source>
+        <translation>Mostrar todo</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1343"/>
+        <source>Hide All</source>
+        <translation>Ocultar todo</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Flag</name>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="144"/>
+        <source>Text</source>
+        <translation>Texto</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="153"/>
+        <source>Delete</source>
+        <translation>Eliminar</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="158"/>
+        <source>Disable snapping</source>
+        <translation>Deshabilitar snapping</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Header</name>
+    <message>
+        <location filename="../pv/views/trace/header.cpp" line="137"/>
+        <source>Group</source>
+        <translation>Grupo</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::LogicSignal</name>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="423"/>
+        <source>No trigger</source>
+        <translation>Sin trigger</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="428"/>
+        <source>Trigger on rising edge</source>
+        <translation>Trigger en flanco de subida</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="433"/>
+        <source>Trigger on high level</source>
+        <translation>Trigger en nivel alto</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="438"/>
+        <source>Trigger on falling edge</source>
+        <translation>Trigger en flanco de bajada</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="443"/>
+        <source>Trigger on low level</source>
+        <translation>Trigger en nivel bajo</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="448"/>
+        <source>Trigger on rising or falling edge</source>
+        <translation>Trigger en flanco de subida o bajada</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="535"/>
+        <source> pixels</source>
+        <translation> pixeles</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="539"/>
+        <source>Trace height</source>
+        <translation>Altura del trazo</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="563"/>
+        <source>Trigger</source>
+        <translation>Trigger</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::MathEditDialog</name>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="88"/>
+        <source>Math Expression Editor</source>
+        <translation>Editor de expresiones matemáticas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="93"/>
+        <source>Inputs:</source>
+        <translation>Entradas:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="99"/>
+        <source>Variables:</source>
+        <translation>Variables:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="101"/>
+        <source>Basic operators:</source>
+        <translation>Operadores básicos:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="108"/>
+        <source>Assignments:</source>
+        <translation>Asignaciones:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="118"/>
+        <source>General purpose functions:</source>
+        <translation>Funciones de propósito general:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="119"/>
+        <source>abs(x)         Absolute value of x</source>
+        <translation>abs(x)            Valor absoluto de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="120"/>
+        <source>avg(x, y, ...) Average of all input values</source>
+        <translation>avg(x, y, ...)    Promedio de todos los valores de entrada</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="121"/>
+        <source>ceil(x)                Smallest integer that is greater than or equal to x</source>
+        <translation>ceil(x)           Entero más pequeño que es mayor o igual a x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="122"/>
+        <source>clamp(lb, x, ub)       Clamp x in range between lb and ub, where lb &lt; ub</source>
+        <translation>clamp(lb, x, ub)  Fija x en el rango entre lb y ub, donde lb &lt; ub</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="123"/>
+        <source>equal(x, y)    Equality test between x and y using normalised epsilon</source>
+        <translation>equal(x, y)       Prueba de igualdad entre x e y usando epsilon normalizado</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="124"/>
+        <source>erf(x)         Error function of x</source>
+        <translation>erf(x)            Función error de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="125"/>
+        <source>erfc(x)                Complimentary error function of x</source>
+        <translation>erfc(x)           Función de error complementario de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="126"/>
+        <source>exp(x)         e to the power of x</source>
+        <translation>exp(x)            e a la potencia de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="127"/>
+        <source>expm1(x)       e to the power of x minus 1, where x is very small.</source>
+        <translation>expm1(x)  e a la potencia de x menos 1, donde z es muy pequeño.</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="128"/>
+        <source>floor(x)               Largest integer that is less than or equal to x</source>
+        <translation>floor(x)          Entero más grande que is menor o igual a x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="129"/>
+        <source>frac(x)                Fractional portion of x</source>
+        <translation>frac(x)           Porción fraccional de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="130"/>
+        <source>hypot(x)               Hypotenuse of x and y (i.e. sqrt(x*x + y*y))</source>
+        <translation>hypot(x)          Hipotenusa de x e y (es decir sqrt(x*x + y*y))</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="131"/>
+        <source>iclamp(lb, x, ub)      Inverse-clamp x outside of the range lb and ub, where lb &lt; ub.
+               If x is within the range it will snap to the closest bound</source>
+        <translation>iclamp(lb, x, ub) Fijación inversa de x fuera del rango lb y ub, donde lb &lt; ub.
+               Si x está en el rango se fijará al límite más cercano</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="132"/>
+        <source>inrange(lb, x, ub)     In-range returns true when x is within the range lb and ub, where lb &lt; ub.</source>
+        <translation>inrange(lb, x, ub)        En-rango regresa verdadero cuando x está en el rango lb y ub, donde lb &lt; ub.</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="133"/>
+        <source>log(x)         Natural logarithm of x</source>
+        <translation>log(x)            Logaritmo natural de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="134"/>
+        <source>log10(x)               Base 10 logarithm of x</source>
+        <translation>log10(x)          Logaritmo base 10 de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="138"/>
+        <source>log1p(x)               Natural logarithm of 1 + x, where x is very small</source>
+        <translation>log1p(x)          Logaritmo natural de 1 + x, donde x es muy pequeño</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="139"/>
+        <source>log2(x)                Base 2 logarithm of x</source>
+        <translation>log2(x)           Logaritmo base 2 de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="140"/>
+        <source>logn(x)                Base N logarithm of x, where n is a positive integer</source>
+        <translation>logn(x)           Logaritmo base N de x, donde n es un entero positivo</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="141"/>
+        <source>max(x, y, ...) Largest value of all the inputs</source>
+        <translation>max(x, y, ...)    Valor más grande de todas las entradas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="142"/>
+        <source>min(x, y, ...) Smallest value of all the inputs</source>
+        <translation>min(x, y, ...)    Valor más pequeño de todas las entradas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="143"/>
+        <source>mul(x, y, ...) Product of all the inputs</source>
+        <translation>mul(x, y, ...)    Producto de todas las entradas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="144"/>
+        <source>ncdf(x)                Normal cumulative distribution function</source>
+        <translation>ncdf(x)           Función de distribución acumulativa normal</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="145"/>
+        <source>nequal(x, y)   Not-equal test between x and y using normalised epsilon</source>
+        <translation>nequal(x, y)      Prueba de no igualdad entre x e y usando epsilon normalizado</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="146"/>
+        <source>pow(x, y)      x to the power of y</source>
+        <translation>pow(x, y) x a la potencia de y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="147"/>
+        <source>root(x, n)     Nth-Root of x, where n is a positive integer</source>
+        <translation>root(x, n)        Enésima raíz de x, donde n es un entero positivo</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="148"/>
+        <source>round(x)               Round x to the nearest integer</source>
+        <translation>round(x)          Redondear x al entero más cercano</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="149"/>
+        <source>roundn(x, n)   Round x to n decimal places, where n &gt; 0 and is an integer</source>
+        <translation>roundn(x, n)      Redondear x a n lugares decimales, donde n &gt; 0 y es un entero</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="150"/>
+        <source>sgn(x)         Sign of x; -1 if x &lt; 0, +1 if x &gt; 0, else zero</source>
+        <translation>sgn(x)            Sigon de x; -1 si x &lt; 0, +1 si x &gt; 0, otro cero</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="151"/>
+        <source>sqrt(x)                Square root of x, where x &gt;= 0</source>
+        <translation>sqrt(x)           Raíz cuadrada de x, donde x &gt;= 0</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="152"/>
+        <source>sum(x, y, ..,) Sum of all the inputs</source>
+        <translation>sum(x, y, ..,)    Suma de todas las entradas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="153"/>
+        <source>swap(x, y)     Swap the values of the variables x and y and return the current value of y</source>
+        <translation>swap(x, y)        Intercambia los valores de las variables x e y y regresa el valor actual de y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="154"/>
+        <source>trunc(x)               Integer portion of x</source>
+        <translation>trunc(x)          Porción entera de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="158"/>
+        <source>Trigonometry functions:</source>
+        <translation>Funciones trigonométricas:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="159"/>
+        <source>acos(x)                Arc cosine of x expressed in radians. Interval [-1,+1]</source>
+        <translation>acos(x)           Arco coseno de x expresado en radianes. Intervalo [-1,+1]</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="160"/>
+        <source>acosh(x)               Inverse hyperbolic cosine of x expressed in radians</source>
+        <translation>acosh(x)          Coseno hiperbólico inverso de x expresado en radianes</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="161"/>
+        <source>asin(x)                Arc sine of x expressed in radians. Interval [-1,+1]</source>
+        <translation>asin(x)           Arco seno de x expresado en radianes. Intervalo [-1,+1]</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="162"/>
+        <source>asinh(x)               Inverse hyperbolic sine of x expressed in radians</source>
+        <translation>asinh(x)          Seno hiperbólico inverso de x expresado en radianes</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="163"/>
+        <source>atan(x)                Arc tangent of x expressed in radians. Interval [-1,+1]</source>
+        <translation>atan(x)           Arco tangente de x expresado en radianes. Intervalo [-1,+1]</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="164"/>
+        <source>atan2(x, y)    Arc tangent of (x / y) expressed in radians. [-pi,+pi]  </source>
+        <translation>atan2(x, y)       Arco tangente de (x / y) expresado en radianes. [-pi,+pi]  </translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="165"/>
+        <source>atanh(x)               Inverse hyperbolic tangent of x expressed in radians</source>
+        <translation>atanh(x)          Tangente hiperbólica inversa de x expresada en radianes</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="166"/>
+        <source>cos(x)         Cosine of x</source>
+        <translation>cos(x)            Coseno de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="167"/>
+        <source>cosh(x)                Hyperbolic cosine of x</source>
+        <translation>cosh(x)           Coseno hiperbólico de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="168"/>
+        <source>cot(x)         Cotangent of x</source>
+        <translation>cot(x)            Cotangente de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="169"/>
+        <source>csc(x)         Cosectant of x</source>
+        <translation>csc(x)            Cosecante de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="170"/>
+        <source>sec(x)         Secant of x</source>
+        <translation>sec(x)            Secante de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="171"/>
+        <source>sin(x)         Sine of x</source>
+        <translation>sin(x)            Seno de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="172"/>
+        <source>sinc(x)                Sine cardinal of x</source>
+        <translation>sinc(x)           Seno cardinal de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="173"/>
+        <source>sinh(x)                Hyperbolic sine of x</source>
+        <translation>sinh(x)           Seno hiperbólico de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="174"/>
+        <source>tan(x)         Tangent of x</source>
+        <translation>tan(x)            Tangente de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="175"/>
+        <source>tanh(x)                Hyperbolic tangent of x</source>
+        <translation>tanh(x)           Tangente hiperbólica de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="176"/>
+        <source>deg2rad(x)     Convert x from degrees to radians</source>
+        <translation>deg2rad(x)        Convierte x de grados sexagesimales a radianes</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="177"/>
+        <source>deg2grad(x)    Convert x from degrees to gradians</source>
+        <translation>deg2grad(x)       Convierte x de grados sexagesimales a grados centesimales</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="178"/>
+        <source>rad2deg(x)     Convert x from radians to degrees</source>
+        <translation>rad2deg(x)        Convierte x de radianes a grados sexagesimales</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="179"/>
+        <source>grad2deg(x)    Convert x from gradians to degrees</source>
+        <translation>grad2deg(x)       Convierte x de grados centesimales a grados sexagesimales</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="183"/>
+        <source>Logic operators:</source>
+        <translation>Operadores lógicos:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="200"/>
+        <source>Comparisons:</source>
+        <translation>Comparaciones:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="201"/>
+        <source>x = y or x == y        True only if x is strictly equal to y</source>
+        <translation>x = y o x == y    Verdadero solo si x es estrictemente igual a y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="202"/>
+        <source>x &lt;&gt; y or x != y True only if x does not equal y</source>
+        <translation>x &lt;&gt; y o x != y     Verdadero solo si x no es igual a y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="203"/>
+        <source>x &lt; y               True only if x is less than y</source>
+        <translation>x &lt; y          Verdadero solo si x es menor que y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="204"/>
+        <source>x &lt;= y              True only if x is less than or equal to y</source>
+        <translation>x &lt;= y         Verdadero solo si x es menor o igual a y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="205"/>
+        <source>x &gt; y               True only if x is greater than y</source>
+        <translation>x &gt; y          Verdadero solo si x es mayor que y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="206"/>
+        <source>x &gt;= y              True only if x is greater than or equal to y</source>
+        <translation>x &gt;= y         Verdadero solo si x es mayor o igual a y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="207"/>
+        <source>Flow control:</source>
+        <translation>Fujo de cotrol:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="208"/>
+        <source>{ ... }                Beginning and end of instruction block</source>
+        <translation>{ ... }           Inicio y fin de un bloque de instrucciones</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="209"/>
+        <source>if (x, y, z)   If x is true then return y else return z
+if (x) y;                      variant without implied else
+if (x) { y };          variant with an instruction block
+if (x) y; else z;              variant with explicit else
+if (x) { y } else { z };       variant with instruction blocks</source>
+        <translation>if (x, y, z)      Si x es verdadero entonces regresa y si no regresa z
+if (x) y;                      variante sin si no implicado
+if (x) { y };          variante con un bloque de instrucción
+if (x) y; else z;              variante con si no explícito
+if (x) { y } else { z };       variante con bloques de instrucciones</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="210"/>
+        <source>x ? y : z      Ternary operator, equivalent to &apos;if (x, y, z)&apos;</source>
+        <translation>x ? y : z Operador ternario, equivalente a &apos;if (x, y, z)&apos;</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="211"/>
+        <source>switch {               The first true case condition that is encountered will
+ case x &gt; 1: a;     determine the result of the switch. If none of the case
+ case x &lt; 1: b;     conditions hold true, the default action is used
+ default:     c;       to determine the return value
+}</source>
+        <translation>switch {          La primera condición de caso verdadera que es encontrada
+ case x &gt; 1: a;     determina el resultado del interruptor. Si ninguno de las condiciones de
+ case x &lt; 1: b;     los casos se mantienen verdaderas, la acción predeterminada se usa
+ default:     c;       para determinar el valor de retorno
+}</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="215"/>
+        <source>while (conditon) {     Evaluates expression repeatedly as long as condition is true,
+ expression;   returning the last value of expression
+}</source>
+        <translation>while (conditon) {        Evalúa la expresión repetidamente siempre que la condición sea verdadera,
+expresión; devuelve el último valor de la expresión
+}</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="216"/>
+        <source>repeat         Evalues expression repeatedly as long as condition is false,
+ expression;   returning the last value of expression
+until (condition)
+</source>
+        <translation>repeat            Evalúa la expresión repetidamente siempre que la condición sea falsa
+expresión; devuelve el último valor de la expresión
+hasta que (condición)
+</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="217"/>
+        <source>for (var x := 0; condition; x += 1) {  Repeatedly evaluates expression while the condition is true,
+ expression                    while evaluating the &apos;increment&apos; expression on each loop
+}</source>
+        <translation>for (var x := 0; condición; x += 1) {    Evalua repetidamente la expresión mientras la condición sea verdadera,
+ expresión                    mientras se evalúa la expresión de &apos;incremento&apos; en cada bucle
+}</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="218"/>
+        <source>break          Terminates the execution of the nearest enclosed loop, returning NaN</source>
+        <translation>break             Finaliza la ejecución del bucle cerrado más cercano, devolviendo NaN</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="219"/>
+        <source>break[x]               Terminates the execution of the nearest enclosed loop, returning x</source>
+        <translation>break[x]          Finaliza la ejecución del bucle cerrado más cercano, devolviendo x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="220"/>
+        <source>continue               Interrupts loop execution and resumes with the next loop iteration</source>
+        <translation>continue          Interrumpe la ejecución del bucle y se reanuda con la siguiente iteración de bucle</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="221"/>
+        <source>return[x]              Returns immediately from within the current expression, returning x</source>
+        <translation>return[x]         Devuelve inmediatamente desde la expresión actual, regresando x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="222"/>
+        <source>~(expr; expr; ...)     Evaluates each sub-expression and returns the value of the last one
+~{expr; expr; ...}</source>
+        <translation>~(expr; expr; ...)        Evalúa cada subexpresión y devuelve el valor de la última
+~{expr; expr; ...}</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="234"/>
+        <source>Copy to expression</source>
+        <translation>Copiar a expresión</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="247"/>
+        <source>Basics</source>
+        <translation>Básicas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="248"/>
+        <source>Functions 1</source>
+        <translation>Funciones 1</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="249"/>
+        <source>Functions 2</source>
+        <translation>Funciones 2</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="250"/>
+        <source>Trigonometry</source>
+        <translation>Trigonometría</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="251"/>
+        <source>Logic</source>
+        <translation>Lógica</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="252"/>
+        <source>Flow Control 1</source>
+        <translation>Flujo de control 1</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="253"/>
+        <source>Flow Control 2</source>
+        <translation>Flujo de control 2</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="254"/>
+        <source>Examples</source>
+        <translation>Ejemplos</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::MathSignal</name>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="317"/>
+        <source>Expression</source>
+        <translation>Espresión</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="321"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="331"/>
+        <source>same as session</source>
+        <translation>igual que la sesión</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="322"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="332"/>
+        <source>100</source>
+        <translation>100</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="323"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="333"/>
+        <source>10000</source>
+        <translation>10000</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="324"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="334"/>
+        <source>1000000</source>
+        <translation>1000000</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="327"/>
+        <source>Number of Samples</source>
+        <translation>Número de muestras</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="335"/>
+        <source>Sample rate</source>
+        <translation>Tasa de muestreo</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Ruler</name>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="153"/>
+        <source>Create marker here</source>
+        <translation>Crear marcador aquí</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="157"/>
+        <source>Set as zero point</source>
+        <translation>Establecer como punto cero</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="162"/>
+        <source>Reset zero point</source>
+        <translation>Reiniciar punto cero</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="175"/>
+        <source>Disable mouse hover marker</source>
+        <translation>Deshabilitar marcador de desplazamiento del ratón</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="175"/>
+        <source>Enable mouse hover marker</source>
+        <translation>Habilitar marcador de desplazamiento del ratón</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Signal</name>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="153"/>
+        <source>Name</source>
+        <translation>Nombre</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="167"/>
+        <source>Remove</source>
+        <translation>Eliminar</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="169"/>
+        <source>Disable</source>
+        <translation>Deshabilitar</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::StandardBar</name>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="54"/>
+        <source>Zoom &amp;In</source>
+        <translation>&amp;Acercar</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="62"/>
+        <source>Zoom &amp;Out</source>
+        <translation>A&amp;lejar</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="70"/>
+        <source>Zoom to &amp;Fit</source>
+        <translation>Hacer Zoom para &amp;Encajar</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="82"/>
+        <source>Show &amp;Cursors</source>
+        <translation>Mostrar &amp;Cursores</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="85"/>
+        <source>Display last segment only</source>
+        <translation>Mostrar solo el último segmento</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="90"/>
+        <source>Display last complete segment only</source>
+        <translation>Mostrar solo el último segmento completo</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="95"/>
+        <source>Display a single segment</source>
+        <translation>Mostrar un solo segmento</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::TimeMarker</name>
+    <message>
+        <location filename="../pv/views/trace/timemarker.cpp" line="198"/>
+        <source>Time</source>
+        <translation>Tiempo</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Trace</name>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="229"/>
+        <source>Create marker here</source>
+        <translation>Crear marcador aquí</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="338"/>
+        <source>Color</source>
+        <translation>Color</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="403"/>
+        <source>Name</source>
+        <translation>Nombre</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::TraceGroup</name>
+    <message>
+        <location filename="../pv/views/trace/tracegroup.cpp" line="140"/>
+        <source>Ungroup</source>
+        <translation>Desagrupar</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::View</name>
+    <message>
+        <location filename="../pv/views/trace/view.cpp" line="1610"/>
+        <source>Create marker here</source>
+        <translation>Crear marcador aquí</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::DecoderGroupBox</name>
+    <message>
+        <location filename="../pv/widgets/decodergroupbox.cpp" line="48"/>
+        <source>Show/hide this decoder trace</source>
+        <translation>Mostrar/ocultar este trazo de decodificador</translation>
+    </message>
+    <message>
+        <location filename="../pv/widgets/decodergroupbox.cpp" line="58"/>
+        <source>Delete this decoder trace</source>
+        <translation>Eliminar este trazo de decodificador</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::DeviceToolButton</name>
+    <message>
+        <location filename="../pv/widgets/devicetoolbutton.cpp" line="80"/>
+        <location filename="../pv/widgets/devicetoolbutton.cpp" line="87"/>
+        <source>&lt;No Device&gt;</source>
+        <translation>&lt;Sin dispositivo&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::ExportMenu</name>
+    <message>
+        <location filename="../pv/widgets/exportmenu.cpp" line="71"/>
+        <source>Export %1...</source>
+        <translation>Exportar %1...</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::ImportMenu</name>
+    <message>
+        <location filename="../pv/widgets/importmenu.cpp" line="68"/>
+        <source>Import %1...</source>
+        <translation>Importar %1...</translation>
+    </message>
+</context>
+</TS>
diff --git a/l10n/ja_jp.ts b/l10n/ja_jp.ts
new file mode 100644 (file)
index 0000000..164ea30
--- /dev/null
@@ -0,0 +1,2201 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ja_JP">
+<context>
+    <name>Application</name>
+    <message>
+        <location filename="../pv/application.cpp" line="131"/>
+        <source>Some parts of the application may still use the previous language. Re-opening the affected windows or restarting the application will remedy this.</source>
+        <translation>アプリケーションの一部では、以前の言語が引き続き使用される場合があります。 影響を受けたウィンドウを再度開くか、アプリケーションを再起動すると、これが改善されます。</translation>
+    </message>
+</context>
+<context>
+    <name>QApplication</name>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="274"/>
+        <source>Error when scanning device driver &apos;%1&apos;: %2</source>
+        <translation>デバイスドライバ&apos;%1&apos;をスキャンするときにエラーが発生しました:%2</translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="70"/>
+        <source>Querying config key %1 is not allowed</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="79"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="93"/>
+        <source>Unknown type supplied when attempting to query %1</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>QHexView</name>
+    <message>
+        <location filename="../pv/views/decoder_binary/QHexView.cpp" line="339"/>
+        <source>No data available</source>
+        <translation>データなし</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../main.cpp" line="116"/>
+        <source>Stack trace of previous crash:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="130"/>
+        <source>Don&apos;t show this message again</source>
+        <translation>このメッセージを二度と表示しない</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="133"/>
+        <source>When %1 last crashed, it created a stack trace.
+A human-readable form has been saved to disk and was written to the log. You may access it from the settings dialog.</source>
+        <translation>%1が最後にクラッシュしたとき、スタックトレースが作成されました。
+人間が読める形式がディスクに保存され、ログに書き込まれました。 設定ダイアログからアクセスできます。</translation>
+    </message>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="65"/>
+        <source>Cancel</source>
+        <translation>キャンセル</translation>
+    </message>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="96"/>
+        <source>Scanning for devices that driver %1 can access...</source>
+        <translation>ドライバー %1 がアクセスできるデバイスをスキャンしています...</translation>
+    </message>
+</context>
+<context>
+    <name>pv::MainWindow</name>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="70"/>
+        <source>PulseView</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="279"/>
+        <source>Decoder Selector</source>
+        <translation>デコーダー選択</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="332"/>
+        <source>Session %1</source>
+        <translation>セッション %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="514"/>
+        <source>Create New Session</source>
+        <translation>新規セッションを作成</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="520"/>
+        <source>Start/Stop Acquisition</source>
+        <translation>取得の開始/停止</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="528"/>
+        <source>Settings</source>
+        <translation>設定</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="580"/>
+        <source>Reload</source>
+        <translation>再読込</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="580"/>
+        <location filename="../pv/mainwindow.cpp" line="583"/>
+        <source>Run</source>
+        <translation>実行</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="589"/>
+        <source>Stop</source>
+        <translation>停止</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="635"/>
+        <location filename="../pv/mainwindow.cpp" line="860"/>
+        <location filename="../pv/mainwindow.cpp" line="886"/>
+        <source>Confirmation</source>
+        <translation>確認</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="636"/>
+        <source>There is unsaved data. Close anyway?</source>
+        <translation>保存されていないデータがあります。 閉じますか?</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="861"/>
+        <location filename="../pv/mainwindow.cpp" line="887"/>
+        <source>This session contains unsaved data. Close it anyway?</source>
+        <translation>このセッションには、保存されていないデータが含まれています。 閉じますか?</translation>
+    </message>
+</context>
+<context>
+    <name>pv::Session</name>
+    <message>
+        <location filename="../pv/session.cpp" line="396"/>
+        <source>Can&apos;t restore generated signal of unknown type %1 (%2)</source>
+        <translation>不明なタイプ%1 (%2)の生成された信号を復元できません</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="559"/>
+        <source>Failed to select device</source>
+        <translation>デバイスの選択に失敗しました</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="616"/>
+        <source>Failed to open device</source>
+        <translation>デバイスを開くことができませんでした</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="722"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="723"/>
+        <source>Unexpected input format: %1</source>
+        <translation>予期しない入力形式:%1%</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="758"/>
+        <source>Failed to load %1</source>
+        <translation>%1 のロードに失敗しました</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="797"/>
+        <source>No active device set, can&apos;t start acquisition.</source>
+        <translation>アクティブなデバイスが設定されていないため、取得を開始できません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="810"/>
+        <source>No channels enabled.</source>
+        <translation>チャネルが有効になっていません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="1311"/>
+        <source>Out of memory, acquisition stopped.</source>
+        <translation>メモリが不足しているため、取得が停止しました。</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="1518"/>
+        <source>Can&apos;t handle more than 64 logic channels.</source>
+        <translation>64を超えるロジックチャネルを処理することはできません。</translation>
+    </message>
+</context>
+<context>
+    <name>pv::StoreSession</name>
+    <message>
+        <location filename="../pv/storesession.cpp" line="114"/>
+        <source>Can&apos;t save logic channel without data.</source>
+        <translation>データなしでロジックチャネルを保存することはできません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="130"/>
+        <source>Can&apos;t save analog channel without data.</source>
+        <translation>データなしでアナログチャンネルを保存することはできません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="142"/>
+        <source>No channels enabled.</source>
+        <translation>チャネルが有効になっていません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="167"/>
+        <source>Can&apos;t save range without sample data.</source>
+        <translation>サンプルデータなしでは範囲を保存できません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="188"/>
+        <location filename="../pv/storesession.cpp" line="295"/>
+        <location filename="../pv/storesession.cpp" line="310"/>
+        <source>Error while saving: </source>
+        <translation>保存中のエラー:</translation>
+    </message>
+</context>
+<context>
+    <name>pv::binding::Device</name>
+    <message>
+        <location filename="../pv/binding/device.cpp" line="82"/>
+        <source>Note for device developers: Ignoring device configuration capability &apos;%1&apos; as it is missing GET and/or SET</source>
+        <translation>デバイス開発者への注意:GETやSETがないため、デバイス構成機能&apos;%1&apos;を無視します</translation>
+    </message>
+    <message>
+        <location filename="../pv/binding/device.cpp" line="107"/>
+        <source>No Limit</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::DecodeSignal</name>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="220"/>
+        <source>No decoders</source>
+        <translation>デコーダーなし</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="227"/>
+        <source>There are no channels assigned to this decoder</source>
+        <translation>このデコーダーに割り当てられたチャネルはありません</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="241"/>
+        <source>One or more required channels have not been specified</source>
+        <translation>1つ以上の必要なチャネルが指定されていません</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="257"/>
+        <source>No input data</source>
+        <translation>入力データなし</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="1310"/>
+        <source>Decoder reported an error</source>
+        <translation>デコーダーがエラーを報告しました</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="1464"/>
+        <source>Failed to create decoder instance</source>
+        <translation>デコーダーインスタンスの作成に失敗しました</translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::MathSignal</name>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="107"/>
+        <source>Math%1</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="306"/>
+        <source>No expression defined, nothing to do</source>
+        <translation>式は定義されていません、何もしません</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="345"/>
+        <source>%1 at line %2, column %3: %4</source>
+        <translation>%1  行 %2, 列 %3: %4</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="364"/>
+        <location filename="../pv/data/mathsignal.cpp" line="536"/>
+        <source>&quot;%1&quot; isn&apos;t a valid analog signal</source>
+        <translation>&quot;%1&quot; は有効なアナログ信号ではありません</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="374"/>
+        <location filename="../pv/data/mathsignal.cpp" line="611"/>
+        <source>No data will be generated as %1 must be enabled</source>
+        <translation>%1を有効にする必要があるため、データは生成されません</translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::SignalBase</name>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="525"/>
+        <source>Signal average</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="526"/>
+        <source>0.9V (for 1.8V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="527"/>
+        <source>1.8V (for 3.3V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="528"/>
+        <source>2.5V (for 5.0V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="529"/>
+        <source>1.5V (for TTL)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="534"/>
+        <source>Signal average +/- 15%</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="535"/>
+        <source>0.3V/1.2V (for 1.8V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="536"/>
+        <source>0.7V/2.5V (for 3.3V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="537"/>
+        <source>1.3V/3.7V (for 5.0V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="538"/>
+        <source>0.8V/2.0V (for TTL)</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::Connect</name>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="58"/>
+        <source>&amp;Scan for devices using driver above</source>
+        <translation>上記のドライバーを使用してデバイスをスキャンする</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="63"/>
+        <source>Connect to Device</source>
+        <translation>デバイスに接続</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="75"/>
+        <source>Step 1: Choose the driver</source>
+        <translation>ステップ1:ドライバーを選択</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="79"/>
+        <source>&amp;USB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="80"/>
+        <source>Serial &amp;Port</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="81"/>
+        <source>&amp;TCP/IP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="116"/>
+        <source>Protocol:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="134"/>
+        <source>Step 2: Choose the interface</source>
+        <translation>ステップ2:インターフェースを選択</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="140"/>
+        <source>Step 3: Scan for devices</source>
+        <translation>ステップ3:デバイスをスキャン</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="146"/>
+        <source>Step 4: Select the device</source>
+        <translation>ステップ4:デバイスを選択</translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::Settings</name>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="134"/>
+        <location filename="../pv/dialogs/settings.cpp" line="213"/>
+        <source>General</source>
+        <translation>全般</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="143"/>
+        <source>Views</source>
+        <translation>表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="153"/>
+        <location filename="../pv/dialogs/settings.cpp" line="406"/>
+        <source>Decoders</source>
+        <translation>デコーダー</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="163"/>
+        <source>About</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="172"/>
+        <source>Logging</source>
+        <translation>ロギング</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="236"/>
+        <source>User interface language</source>
+        <translation>表示言語</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="247"/>
+        <source>User interface theme</source>
+        <translation>表示のテーマ</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="249"/>
+        <source>(You may need to restart PulseView for all UI elements to update)</source>
+        <translation>(すべてのUI要素を更新するには、PulseViewを再起動する必要がある場合があります)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="255"/>
+        <source>System Default</source>
+        <translation>システムのデフォルト</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="268"/>
+        <source>Qt widget style</source>
+        <translation>Qt widget スタイル</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="270"/>
+        <source>(Dark themes look best with the Fusion style)</source>
+        <translation>(ダークテーマはFusionスタイルで最もよく見えます)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="277"/>
+        <source>Save session &amp;setup along with .sr file</source>
+        <translation>セッション設定を.srファイルと一緒に保存します</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="281"/>
+        <source>Start acquisition for all open sessions when clicking &apos;Run&apos;</source>
+        <translation>[実行]をクリックすると、開いているすべてのセッションの取得を開始します</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="296"/>
+        <source>Trace View</source>
+        <translation>トレースビュー</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="304"/>
+        <source>Use colored trace &amp;background</source>
+        <translation>色付きのトレースと背景を使用する(&amp;b)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="308"/>
+        <source>Constantly perform &amp;zoom-to-fit during acquisition</source>
+        <translation>取得中は常にズームツーフィットを実行します(&amp;z)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="312"/>
+        <source>Perform a zoom-to-&amp;fit when acquisition stops</source>
+        <translation>取得が停止したときにズームツーフィットする(&amp;f)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="316"/>
+        <source>Show time zero at the &amp;trigger</source>
+        <translation>トリガーで時間ゼロを表示する(&amp;t)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="320"/>
+        <source>Always keep &amp;newest samples at the right edge during capture</source>
+        <translation>キャプチャ中は常に最新のサンプルを右端に保持(&amp;n)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="324"/>
+        <source>Allow &amp;vertical dragging in the view area</source>
+        <translation>ビューエリアでの垂直方向のドラッグを許可する(&amp;v)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="328"/>
+        <source>Show data &amp;sampling points</source>
+        <translation>データサンプリングポイントを表示する(&amp;s)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="332"/>
+        <source>Fill &amp;high areas of logic signals</source>
+        <translation>ロジック信号の高い領域を埋める(&amp;h)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="339"/>
+        <source>Color to fill high areas of logic signals with</source>
+        <translation>論理信号の高い領域を塗りつぶす色</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="343"/>
+        <source>Show analog minor grid in addition to div grid</source>
+        <translation>divグリッドに加えてアナログマイナーグリッドを表示する</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="347"/>
+        <source>Highlight mouse cursor using a vertical marker line</source>
+        <translation>垂直マーカーラインを使用してマウスカーソルを強調表示します</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="351"/>
+        <location filename="../pv/dialogs/settings.cpp" line="377"/>
+        <location filename="../pv/dialogs/settings.cpp" line="386"/>
+        <source> pixels</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="356"/>
+        <source>Maximum distance from edges before markers snap to them</source>
+        <translation>マーカーがエッジにスナップする前のエッジからの最大距離</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="363"/>
+        <source>Color to fill cursor area with</source>
+        <translation>カーソル領域を塗りつぶす色</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="366"/>
+        <source>None</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="367"/>
+        <source>Background</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="368"/>
+        <source>Dots</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="373"/>
+        <source>Conversion threshold display mode (analog traces only)</source>
+        <translation>変換閾値表示モード(アナログトレースのみ)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="382"/>
+        <source>Default analog trace div height</source>
+        <translation>デフォルトのアナログトレースdivの高さ</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="391"/>
+        <source>Default logic trace height</source>
+        <translation>デフォルトのロジックトレースの高さ</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="414"/>
+        <source>Allow configuration of &amp;initial signal state</source>
+        <translation>初期信号状態の構成を許可する(&amp;i)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="418"/>
+        <source>Always show all &amp;rows, even if no annotation is visible</source>
+        <translation>注釈が表示されていない場合でも、常にすべての行を表示します(&amp;r)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="426"/>
+        <source>Annotation export format</source>
+        <translation>注釈のエクスポート形式</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="427"/>
+        <source>%s = sample range; %d: decoder name; %r: row name; %c: class name</source>
+        <translation>%s = サンプル範囲; %d: デコーダー名; %r: 行名; %c: クラス名</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="430"/>
+        <source>%1: longest annotation text; %a: all annotation texts; %q: use quotation marks</source>
+        <translation>%1: 最長の注釈テキスト; %a: すべての注釈テキスト; %q: 引用符を使用</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="450"/>
+        <source>%1&lt;br /&gt;&lt;a href=&quot;http://%2&quot;&gt;%2&lt;/a&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="451"/>
+        <source>GNU GPL, version 3 or later</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="462"/>
+        <source>Versions, libraries and features:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="469"/>
+        <source>Firmware search paths:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="476"/>
+        <source>Protocol decoder search paths:</source>
+        <translation>プロトコルデコーダの検索パス:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="479"/>
+        <source>&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;(Note: Set environment variable SIGROKDECODE_DIR to add a custom directory)&lt;/td&gt;&lt;/tr&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="484"/>
+        <source>Supported hardware drivers:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="491"/>
+        <source>Supported input formats:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="498"/>
+        <source>Supported output formats:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="506"/>
+        <source>Supported protocol decoders:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="514"/>
+        <source>Available Translations:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="562"/>
+        <source>Log level:</source>
+        <translation>ログレベル:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="567"/>
+        <source> lines</source>
+        <translation>行数</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="576"/>
+        <source>Length of background buffer:</source>
+        <translation>バックグラウンドバッファの長さ:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="582"/>
+        <source>&amp;Save to File</source>
+        <translation>ファイルに保存(&amp;S)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="589"/>
+        <source>&amp;Pop out</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="658"/>
+        <source>You selected a dark theme.
+Should I set the user-adjustable colors to better suit your choice?
+
+Please keep in mind that PulseView may need a restart to display correctly.</source>
+        <translation>ダークテーマを選択しました。
+選択に合わせてユーザー調整可能な色を設定する必要があります
+
+正しく表示するには、PulseViewを再起動する必要がある場合があることに注意してください。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="664"/>
+        <source>You selected a bright theme.
+Should I set the user-adjustable colors to better suit your choice?
+
+Please keep in mind that PulseView may need a restart to display correctly.</source>
+        <translation>明るいテーマを選択しました。
+選択に合わせてユーザー調整可能な色を設定する必要があります
+
+正しく表示するには、PulseViewを再起動する必要がある場合があることに注意してください。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="829"/>
+        <source>Save Log</source>
+        <translation>ログ保存</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="829"/>
+        <source>Log Files (*.txt *.log);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="841"/>
+        <source>Success</source>
+        <translation>成功</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="841"/>
+        <source>Log saved to %1.</source>
+        <translation>ログは %1 に保存されました。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="851"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="851"/>
+        <source>File %1 could not be written to.</source>
+        <translation>ファイル %1 に書き込めませんでした。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="865"/>
+        <source>%1 Log</source>
+        <translation>%1 ログ</translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::StoreProgress</name>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="44"/>
+        <source>Saving...</source>
+        <translation>保存中...</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="44"/>
+        <source>Cancel</source>
+        <translation>キャンセル</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="89"/>
+        <source>Failed to save session.</source>
+        <translation>セッションの保存に失敗しました。</translation>
+    </message>
+</context>
+<context>
+    <name>pv::popups::Channels</name>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="62"/>
+        <location filename="../pv/popups/channels.cpp" line="63"/>
+        <location filename="../pv/popups/channels.cpp" line="273"/>
+        <location filename="../pv/popups/channels.cpp" line="300"/>
+        <source>All</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="64"/>
+        <location filename="../pv/popups/channels.cpp" line="65"/>
+        <source>Logic</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="66"/>
+        <location filename="../pv/popups/channels.cpp" line="67"/>
+        <source>Analog</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="68"/>
+        <source>Named</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="69"/>
+        <source>Unnamed</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="70"/>
+        <source>Changing</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="71"/>
+        <source>Non-changing</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="141"/>
+        <source>Disable: </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="149"/>
+        <source>Enable: </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="281"/>
+        <location filename="../pv/popups/channels.cpp" line="301"/>
+        <source>None</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Bool</name>
+    <message>
+        <location filename="../pv/prop/bool.cpp" line="51"/>
+        <location filename="../pv/prop/bool.cpp" line="82"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Double</name>
+    <message>
+        <location filename="../pv/prop/double.cpp" line="65"/>
+        <location filename="../pv/prop/double.cpp" line="96"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Enum</name>
+    <message>
+        <location filename="../pv/prop/enum.cpp" line="113"/>
+        <location filename="../pv/prop/enum.cpp" line="176"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Int</name>
+    <message>
+        <location filename="../pv/prop/int.cpp" line="63"/>
+        <location filename="../pv/prop/int.cpp" line="127"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::String</name>
+    <message>
+        <location filename="../pv/prop/string.cpp" line="59"/>
+        <location filename="../pv/prop/string.cpp" line="84"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>pv::subwindows::decoder_selector::DecoderCollectionModel</name>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="40"/>
+        <source>Decoder</source>
+        <translation>デコーダー</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="41"/>
+        <source>Name</source>
+        <translation>名前</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="42"/>
+        <source>ID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="49"/>
+        <source>All Decoders</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::subwindows::decoder_selector::SubWindow</name>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="49"/>
+        <source>Select a decoder to see its description here.</source>
+        <translation>ここに説明を表示するには、デコーダーを選択してください。</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="248"/>
+        <source>, %1</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="265"/>
+        <source>&lt;p align=&apos;right&apos;&gt;Tags: %1&lt;/p&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="312"/>
+        <source>Protocol decoder &lt;b&gt;%1&lt;/b&gt; requires input type &lt;b&gt;%2&lt;/b&gt; which several decoders provide.&lt;br&gt;Choose which one to use:&lt;br&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="320"/>
+        <source>Choose Decoder</source>
+        <translation>デコーダーを選択</translation>
+    </message>
+</context>
+<context>
+    <name>pv::toolbars::MainBar</name>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="124"/>
+        <source>New &amp;View</source>
+        <translation>新しいビュー(&amp;V)</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="130"/>
+        <source>&amp;Open...</source>
+        <translation>開く(&amp;O)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="137"/>
+        <source>Restore Session Setu&amp;p...</source>
+        <translation>セッション設定を復元(&amp;p)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="141"/>
+        <source>&amp;Save...</source>
+        <translation>保存(&amp;S)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="148"/>
+        <source>Save &amp;As...</source>
+        <translation>名前をつけて保存(&amp;A)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="154"/>
+        <source>Save Selected &amp;Range As...</source>
+        <translation>選択した範囲を名前を付けて保存(&amp;R)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="161"/>
+        <source>Save Session Setu&amp;p...</source>
+        <translation>セッション設定を保存(&amp;p)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="167"/>
+        <source>&amp;Export</source>
+        <translation>エクスポート(&amp;E)</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="173"/>
+        <source>&amp;Import</source>
+        <translation>インポート(&amp;I)</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="177"/>
+        <source>&amp;Connect to Device...</source>
+        <translation>デバイスへ接続(&amp;C)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="239"/>
+        <source>Add protocol decoder</source>
+        <translation>プロトコルデコーダーを追加</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="249"/>
+        <source>Add math signal</source>
+        <translation>math信号を追加</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="265"/>
+        <source>Configure Device</source>
+        <translation>デバイスの構成</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="269"/>
+        <source>Configure Channels</source>
+        <translation>チャンネルの構成</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="383"/>
+        <source>Failed to get sample rate list:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="446"/>
+        <source>Failed to get sample rate:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="487"/>
+        <source>Failed to get sample limit list:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="577"/>
+        <source>Failed to configure samplerate:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="604"/>
+        <source>Failed to configure sample count:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="642"/>
+        <source>Missing Cursors</source>
+        <translation>カーソルがありません</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="642"/>
+        <source>You need to set the cursors before you can save the data enclosed by them to a session file (e.g. using the Show Cursors button).</source>
+        <translation>カーソルで囲まれたデータをセッションファイルに保存する前に、カーソルを設定する必要があります(たとえば、[カーソルの表示]ボタンを使用)。</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="660"/>
+        <source>Invalid Range</source>
+        <translation>範囲が無効です</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="660"/>
+        <source>The cursors don&apos;t define a valid range of samples.</source>
+        <translation>カーソルはサンプルの有効な範囲を定義していません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="672"/>
+        <source>%1 files </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="680"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="730"/>
+        <source>All Files</source>
+        <translation>すべてのファイル</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="684"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="861"/>
+        <source>Save File</source>
+        <translation>ファイル保存</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="696"/>
+        <source>Export %1</source>
+        <translation>エクスポート %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="727"/>
+        <source>%1 files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="738"/>
+        <source>Import File</source>
+        <translation>インポートファイル</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="747"/>
+        <source>Import %1</source>
+        <translation>インポート %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="820"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="878"/>
+        <source>Open File</source>
+        <translation>ファイルを開く</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="820"/>
+        <source>sigrok Sessions (*.sr);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="861"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="878"/>
+        <source>PulseView Session Setups (*.pvs);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="946"/>
+        <source>Total sampling time: %1</source>
+        <translation>総サンプリング時間: %1</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::decoder_binary::View</name>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="83"/>
+        <source>Decoder:</source>
+        <translation>デコーダー:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="87"/>
+        <source>Show data as</source>
+        <translation>データ表示形式</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="93"/>
+        <source>Hexdump</source>
+        <translation>16進ダンプ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="110"/>
+        <source>&amp;Save...</source>
+        <translation>保存(&amp;S)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="266"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="306"/>
+        <source>Save Binary Data</source>
+        <translation>バイナリデータを保存</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="266"/>
+        <source>Binary Data Files (*.bin);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="285"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="337"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="285"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="337"/>
+        <source>File %1 could not be written to.</source>
+        <translation>ファイル%1に書き込めませんでした。</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="306"/>
+        <source>Hex Dumps (*.txt);;All Files (*)</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::tabular_decoder::AnnotationCollectionModel</name>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="56"/>
+        <source>Sample</source>
+        <translation>サンプル</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="57"/>
+        <source>Time</source>
+        <translation>時間</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="58"/>
+        <source>Decoder</source>
+        <translation>デコーダー</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="59"/>
+        <source>Ann Row</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="60"/>
+        <source>Ann Class</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="61"/>
+        <source>Value</source>
+        <translation>値</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
+        <source>s</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
+        <source>sa</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::tabular_decoder::View</name>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="176"/>
+        <source>Decoder:</source>
+        <translation>デコーダー:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="198"/>
+        <source>Hide Hidden Rows/Classes</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="202"/>
+        <source>&amp;Save...</source>
+        <translation>保存(&amp;S)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="374"/>
+        <source>Save Annotations as CSV</source>
+        <translation>注釈をCSVとして保存</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="374"/>
+        <source>CSV Files (*.csv);;Text Files (*.txt);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="442"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="442"/>
+        <source>File %1 could not be written to.</source>
+        <translation>ファイル%1に書き込めませんでした。</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::AnalogSignal</name>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="802"/>
+        <source>Number of pos vertical divs</source>
+        <translation>正の垂直divの数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="810"/>
+        <source>Number of neg vertical divs</source>
+        <translation>負の垂直divの数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="815"/>
+        <source> pixels</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="819"/>
+        <source>Div height</source>
+        <translation>Divの高さ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="837"/>
+        <source>V/div</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="841"/>
+        <source>Vertical resolution</source>
+        <translation>垂直解像度</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="850"/>
+        <source>Autoranging</source>
+        <translation>オートレンジ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="855"/>
+        <source>none</source>
+        <translation>なし</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="857"/>
+        <source>to logic via threshold</source>
+        <translation>閾値でロジック変換</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="859"/>
+        <source>to logic via schmitt-trigger</source>
+        <translation>シュミットトリガーでロジック変換</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="865"/>
+        <source>Conversion</source>
+        <translation>変換</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="874"/>
+        <source>Conversion threshold(s)</source>
+        <translation>変換閾値</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="884"/>
+        <source>analog</source>
+        <translation>アナログ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="885"/>
+        <source>converted</source>
+        <translation>変換済</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="886"/>
+        <source>analog+converted</source>
+        <translation>アナログ+変換済</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="891"/>
+        <source>Show traces for</source>
+        <translation>トレースを表示</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Cursor</name>
+    <message>
+        <location filename="../pv/views/trace/cursor.cpp" line="97"/>
+        <source>Disable snapping</source>
+        <translation>スナップを無効にする</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::CursorPair</name>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="128"/>
+        <source>Display interval</source>
+        <translation>間隔表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="140"/>
+        <source>Display frequency</source>
+        <translation>周波数表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="152"/>
+        <source>Display samples</source>
+        <translation>サンプル数表示</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::DecodeTrace</name>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="446"/>
+        <source>&lt;p&gt;&lt;i&gt;No decoders in the stack&lt;/i&gt;&lt;/p&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="457"/>
+        <source>&lt;i&gt;* Required channels&lt;/i&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="461"/>
+        <source>Stack Decoder</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="462"/>
+        <source>Stack a higher-level decoder on top of this one</source>
+        <translation>この上に高レベルのデコーダーをスタック</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="476"/>
+        <source>Delete</source>
+        <translation>削除</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="518"/>
+        <source>Resume decoding</source>
+        <translation>デコードを再開</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="525"/>
+        <source>Pause decoding</source>
+        <translation>デコードを一時中断</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="533"/>
+        <source>Copy annotation text to clipboard</source>
+        <translation>注釈テキストをクリップボードにコピー</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="542"/>
+        <source>Export all annotations</source>
+        <translation>すべての注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="549"/>
+        <source>Export all annotations for this row</source>
+        <translation>この行のすべての注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="558"/>
+        <source>Export all annotations, starting here</source>
+        <translation>ここから始めて、すべての注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="565"/>
+        <source>Export annotations for this row, starting here</source>
+        <translation>ここから始めて、この行の注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="574"/>
+        <source>Export all annotations within cursor range</source>
+        <translation>カーソル範囲内のすべての注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="581"/>
+        <source>Export annotations for this row within cursor range</source>
+        <translation>カーソル範囲内のこの行の注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1060"/>
+        <source>%1:
+%2</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1104"/>
+        <source>&lt;b&gt;%1&lt;/b&gt; (%2) %3</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1173"/>
+        <source>Export annotations</source>
+        <translation>注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1173"/>
+        <source>Text Files (*.txt);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1238"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1238"/>
+        <source>File %1 could not be written to.</source>
+        <translation>ファイル%1に書き込めませんでした。</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1291"/>
+        <source>Show this row</source>
+        <translation>この行を表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1302"/>
+        <source>Show All</source>
+        <translation>すべて表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1310"/>
+        <source>Hide All</source>
+        <translation>すべて非表示</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Flag</name>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="132"/>
+        <source>Text</source>
+        <translation>テキスト</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="141"/>
+        <source>Delete</source>
+        <translation>削除</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="146"/>
+        <source>Disable snapping</source>
+        <translation>スナップを無効</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Header</name>
+    <message>
+        <location filename="../pv/views/trace/header.cpp" line="137"/>
+        <source>Group</source>
+        <translation>グループ</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::LogicSignal</name>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="423"/>
+        <source>No trigger</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="428"/>
+        <source>Trigger on rising edge</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="433"/>
+        <source>Trigger on high level</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="438"/>
+        <source>Trigger on falling edge</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="443"/>
+        <source>Trigger on low level</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="448"/>
+        <source>Trigger on rising or falling edge</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="535"/>
+        <source> pixels</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="539"/>
+        <source>Trace height</source>
+        <translation>トレースの高さ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="563"/>
+        <source>Trigger</source>
+        <translation>トリガー</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::MathEditDialog</name>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="88"/>
+        <source>Math Expression Editor</source>
+        <translation>数式エディタ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="93"/>
+        <source>Inputs:</source>
+        <translation>入力:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="99"/>
+        <source>Variables:</source>
+        <translation>変数:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="101"/>
+        <source>Basic operators:</source>
+        <translation>基本的な演算子:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="108"/>
+        <source>Assignments:</source>
+        <translation>割り当て:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="118"/>
+        <source>General purpose functions:</source>
+        <translation>汎用機能:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="119"/>
+        <source>abs(x)         Absolute value of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="120"/>
+        <source>avg(x, y, ...) Average of all input values</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="121"/>
+        <source>ceil(x)                Smallest integer that is greater than or equal to x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="122"/>
+        <source>clamp(lb, x, ub)       Clamp x in range between lb and ub, where lb &lt; ub</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="123"/>
+        <source>equal(x, y)    Equality test between x and y using normalised epsilon</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="124"/>
+        <source>erf(x)         Error function of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="125"/>
+        <source>erfc(x)                Complimentary error function of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="126"/>
+        <source>exp(x)         e to the power of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="127"/>
+        <source>expm1(x)       e to the power of x minus 1, where x is very small.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="128"/>
+        <source>floor(x)               Largest integer that is less than or equal to x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="129"/>
+        <source>frac(x)                Fractional portion of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="130"/>
+        <source>hypot(x)               Hypotenuse of x and y (i.e. sqrt(x*x + y*y))</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="131"/>
+        <source>iclamp(lb, x, ub)      Inverse-clamp x outside of the range lb and ub, where lb &lt; ub.
+               If x is within the range it will snap to the closest bound</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="132"/>
+        <source>inrange(lb, x, ub)     In-range returns true when x is within the range lb and ub, where lb &lt; ub.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="133"/>
+        <source>log(x)         Natural logarithm of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="134"/>
+        <source>log10(x)               Base 10 logarithm of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="138"/>
+        <source>log1p(x)               Natural logarithm of 1 + x, where x is very small</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="139"/>
+        <source>log2(x)                Base 2 logarithm of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="140"/>
+        <source>logn(x)                Base N logarithm of x, where n is a positive integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="141"/>
+        <source>max(x, y, ...) Largest value of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="142"/>
+        <source>min(x, y, ...) Smallest value of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="143"/>
+        <source>mul(x, y, ...) Product of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="144"/>
+        <source>ncdf(x)                Normal cumulative distribution function</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="145"/>
+        <source>nequal(x, y)   Not-equal test between x and y using normalised epsilon</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="146"/>
+        <source>pow(x, y)      x to the power of y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="147"/>
+        <source>root(x, n)     Nth-Root of x, where n is a positive integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="148"/>
+        <source>round(x)               Round x to the nearest integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="149"/>
+        <source>roundn(x, n)   Round x to n decimal places, where n &gt; 0 and is an integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="150"/>
+        <source>sgn(x)         Sign of x; -1 if x &lt; 0, +1 if x &gt; 0, else zero</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="151"/>
+        <source>sqrt(x)                Square root of x, where x &gt;= 0</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="152"/>
+        <source>sum(x, y, ..,) Sum of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="153"/>
+        <source>swap(x, y)     Swap the values of the variables x and y and return the current value of y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="154"/>
+        <source>trunc(x)               Integer portion of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="158"/>
+        <source>Trigonometry functions:</source>
+        <translation>三角関数:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="159"/>
+        <source>acos(x)                Arc cosine of x expressed in radians. Interval [-1,+1]</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="160"/>
+        <source>acosh(x)               Inverse hyperbolic cosine of x expressed in radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="161"/>
+        <source>asin(x)                Arc sine of x expressed in radians. Interval [-1,+1]</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="162"/>
+        <source>asinh(x)               Inverse hyperbolic sine of x expressed in radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="163"/>
+        <source>atan(x)                Arc tangent of x expressed in radians. Interval [-1,+1]</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="164"/>
+        <source>atan2(x, y)    Arc tangent of (x / y) expressed in radians. [-pi,+pi]  </source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="165"/>
+        <source>atanh(x)               Inverse hyperbolic tangent of x expressed in radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="166"/>
+        <source>cos(x)         Cosine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="167"/>
+        <source>cosh(x)                Hyperbolic cosine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="168"/>
+        <source>cot(x)         Cotangent of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="169"/>
+        <source>csc(x)         Cosectant of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="170"/>
+        <source>sec(x)         Secant of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="171"/>
+        <source>sin(x)         Sine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="172"/>
+        <source>sinc(x)                Sine cardinal of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="173"/>
+        <source>sinh(x)                Hyperbolic sine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="174"/>
+        <source>tan(x)         Tangent of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="175"/>
+        <source>tanh(x)                Hyperbolic tangent of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="176"/>
+        <source>deg2rad(x)     Convert x from degrees to radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="177"/>
+        <source>deg2grad(x)    Convert x from degrees to gradians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="178"/>
+        <source>rad2deg(x)     Convert x from radians to degrees</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="179"/>
+        <source>grad2deg(x)    Convert x from gradians to degrees</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="183"/>
+        <source>Logic operators:</source>
+        <translation>論理演算子:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="200"/>
+        <source>Comparisons:</source>
+        <translation>比較:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="201"/>
+        <source>x = y or x == y        True only if x is strictly equal to y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="202"/>
+        <source>x &lt;&gt; y or x != y True only if x does not equal y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="203"/>
+        <source>x &lt; y               True only if x is less than y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="204"/>
+        <source>x &lt;= y              True only if x is less than or equal to y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="205"/>
+        <source>x &gt; y               True only if x is greater than y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="206"/>
+        <source>x &gt;= y              True only if x is greater than or equal to y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="207"/>
+        <source>Flow control:</source>
+        <translation>フロー制御:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="208"/>
+        <source>{ ... }                Beginning and end of instruction block</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="209"/>
+        <source>if (x, y, z)   If x is true then return y else return z
+if (x) y;                      variant without implied else
+if (x) { y };          variant with an instruction block
+if (x) y; else z;              variant with explicit else
+if (x) { y } else { z };       variant with instruction blocks</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="210"/>
+        <source>x ? y : z      Ternary operator, equivalent to &apos;if (x, y, z)&apos;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="211"/>
+        <source>switch {               The first true case condition that is encountered will
+ case x &gt; 1: a;     determine the result of the switch. If none of the case
+ case x &lt; 1: b;     conditions hold true, the default action is used
+ default:     c;       to determine the return value
+}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="215"/>
+        <source>while (conditon) {     Evaluates expression repeatedly as long as condition is true,
+ expression;   returning the last value of expression
+}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="216"/>
+        <source>repeat         Evalues expression repeatedly as long as condition is false,
+ expression;   returning the last value of expression
+until (condition)
+</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="217"/>
+        <source>for (var x := 0; condition; x += 1) {  Repeatedly evaluates expression while the condition is true,
+ expression                    while evaluating the &apos;increment&apos; expression on each loop
+}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="218"/>
+        <source>break          Terminates the execution of the nearest enclosed loop, returning NaN</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="219"/>
+        <source>break[x]               Terminates the execution of the nearest enclosed loop, returning x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="220"/>
+        <source>continue               Interrupts loop execution and resumes with the next loop iteration</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="221"/>
+        <source>return[x]              Returns immediately from within the current expression, returning x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="222"/>
+        <source>~(expr; expr; ...)     Evaluates each sub-expression and returns the value of the last one
+~{expr; expr; ...}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="234"/>
+        <source>Copy to expression</source>
+        <translation>式にコピー</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="247"/>
+        <source>Basics</source>
+        <translation>基本</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="248"/>
+        <source>Functions 1</source>
+        <translation>関数 1</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="249"/>
+        <source>Functions 2</source>
+        <translation>関数 2</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="250"/>
+        <source>Trigonometry</source>
+        <translation>三角関数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="251"/>
+        <source>Logic</source>
+        <translation>論理</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="252"/>
+        <source>Flow Control 1</source>
+        <translation>フロー制御 1</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="253"/>
+        <source>Flow Control 2</source>
+        <translation>フロー制御 2</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="254"/>
+        <source>Examples</source>
+        <translation>例</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::MathSignal</name>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="313"/>
+        <source>Expression</source>
+        <translation>式</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="317"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="327"/>
+        <source>same as session</source>
+        <translation>セッションと同じ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="318"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="328"/>
+        <source>100</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="319"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="329"/>
+        <source>10000</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="320"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="330"/>
+        <source>1000000</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="323"/>
+        <source>Number of Samples</source>
+        <translation>サンプル数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="331"/>
+        <source>Sample rate</source>
+        <translation>サンプルレート</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Ruler</name>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="153"/>
+        <source>Create marker here</source>
+        <translation>ここでマーカーを作成</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="157"/>
+        <source>Set as zero point</source>
+        <translation>ゼロポイントとして設定</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="162"/>
+        <source>Reset zero point</source>
+        <translation>ゼロポイントをリセット</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="175"/>
+        <source>Disable mouse hover marker</source>
+        <translation>マウスホバーマーカーを無効</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="175"/>
+        <source>Enable mouse hover marker</source>
+        <translation>マウスホバーマーカーを有効</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Signal</name>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="153"/>
+        <source>Name</source>
+        <translation>名前</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="167"/>
+        <source>Remove</source>
+        <translation>削除</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="169"/>
+        <source>Disable</source>
+        <translation>無効</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::StandardBar</name>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="54"/>
+        <source>Zoom &amp;In</source>
+        <translation>ズームイン(&amp;I)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="62"/>
+        <source>Zoom &amp;Out</source>
+        <translation>ズームアウト(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="70"/>
+        <source>Zoom to &amp;Fit</source>
+        <translation>ウインドウに合わせる(&amp;F)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="82"/>
+        <source>Show &amp;Cursors</source>
+        <translation>カーソル表示(&amp;C)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="85"/>
+        <source>Display last segment only</source>
+        <translation>最後のセグメントのみ表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="90"/>
+        <source>Display last complete segment only</source>
+        <translation>最後の完全なセグメントのみ表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="95"/>
+        <source>Display a single segment</source>
+        <translation>単一のセグメントを表示</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::TimeMarker</name>
+    <message>
+        <location filename="../pv/views/trace/timemarker.cpp" line="191"/>
+        <source>Time</source>
+        <translation>時間</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Trace</name>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="229"/>
+        <source>Create marker here</source>
+        <translation>ここにマーカーを作成</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="338"/>
+        <source>Color</source>
+        <translation>色</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="403"/>
+        <source>Name</source>
+        <translation>名前</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::TraceGroup</name>
+    <message>
+        <location filename="../pv/views/trace/tracegroup.cpp" line="140"/>
+        <source>Ungroup</source>
+        <translation>グループ解除</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::DecoderGroupBox</name>
+    <message>
+        <location filename="../pv/widgets/decodergroupbox.cpp" line="48"/>
+        <source>Show/hide this decoder trace</source>
+        <translation>このデコーダートレースを表示/非表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/widgets/decodergroupbox.cpp" line="58"/>
+        <source>Delete this decoder trace</source>
+        <translation>このデコーダートレースを削除</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::DeviceToolButton</name>
+    <message>
+        <location filename="../pv/widgets/devicetoolbutton.cpp" line="75"/>
+        <location filename="../pv/widgets/devicetoolbutton.cpp" line="82"/>
+        <source>&lt;No Device&gt;</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::ExportMenu</name>
+    <message>
+        <location filename="../pv/widgets/exportmenu.cpp" line="71"/>
+        <source>Export %1...</source>
+        <translation>エクスポート %1...</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::ImportMenu</name>
+    <message>
+        <location filename="../pv/widgets/importmenu.cpp" line="68"/>
+        <source>Import %1...</source>
+        <translation>インポート %1...</translation>
+    </message>
+</context>
+</TS>
diff --git a/l10n/zh_cn.ts b/l10n/zh_cn.ts
new file mode 100644 (file)
index 0000000..937a96c
--- /dev/null
@@ -0,0 +1,2207 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_CN" sourcelanguage="en">
+<context>
+    <name>Application</name>
+    <message>
+        <location filename="../pv/application.cpp" line="135"/>
+        <source>Some parts of the application may still use the previous language. Re-opening the affected windows or restarting the application will remedy this.</source>
+        <translation>应用程序的某些部分可能仍然使用以前的语言。重新打开受影响的窗口或重新启动应用程序将解决此问题。</translation>
+    </message>
+</context>
+<context>
+    <name>QApplication</name>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="274"/>
+        <source>Error when scanning device driver &apos;%1&apos;: %2</source>
+        <translation>扫描设备驱动程序时出错 &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="70"/>
+        <source>Querying config key %1 is not allowed</source>
+        <translation>不允许查询 %1 配置</translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="79"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>查询配置 &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="93"/>
+        <source>Unknown type supplied when attempting to query %1</source>
+        <translation>查询 %1 时 返回未知类型</translation>
+    </message>
+</context>
+<context>
+    <name>QHexView</name>
+    <message>
+        <location filename="../pv/views/decoder_binary/QHexView.cpp" line="339"/>
+        <source>No data available</source>
+        <translation>无可用数据</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../main.cpp" line="116"/>
+        <source>Stack trace of previous crash:</source>
+        <translation>跟踪上次崩溃日志:</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="130"/>
+        <source>Don&apos;t show this message again</source>
+        <translation>不再显示此消息</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="133"/>
+        <source>When %1 last crashed, it created a stack trace.
+A human-readable form has been saved to disk and was written to the log. You may access it from the settings dialog.</source>
+        <translation>上次崩溃时 %1 ,创建了日志文件。
+可在 设置\Logging 中查看。</translation>
+    </message>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="65"/>
+        <source>Cancel</source>
+        <translation>取消</translation>
+    </message>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="96"/>
+        <source>Scanning for devices that driver %1 can access...</source>
+        <translation>正在扫描驱动程序 %1 可以访问的设备...</translation>
+    </message>
+</context>
+<context>
+    <name>pv::MainWindow</name>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="70"/>
+        <source>PulseView</source>
+        <translation>PulseView</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="284"/>
+        <source>Decoder Selector</source>
+        <translation>协议解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="337"/>
+        <source>Session %1</source>
+        <translation>会话 %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="519"/>
+        <source>Create New Session</source>
+        <translation>创建新会话</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="525"/>
+        <source>Start/Stop Acquisition</source>
+        <translation>开始/停止 采集</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="533"/>
+        <source>Settings</source>
+        <translation>设置</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="589"/>
+        <source>Reload</source>
+        <translation>重新加载</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="589"/>
+        <location filename="../pv/mainwindow.cpp" line="592"/>
+        <source>Run</source>
+        <translation>开始</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="598"/>
+        <source>Stop</source>
+        <translation>停止</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="644"/>
+        <location filename="../pv/mainwindow.cpp" line="867"/>
+        <location filename="../pv/mainwindow.cpp" line="893"/>
+        <source>Confirmation</source>
+        <translation>确认</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="645"/>
+        <source>There is unsaved data. Close anyway?</source>
+        <translation>数据未保存。是否关闭?</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="868"/>
+        <location filename="../pv/mainwindow.cpp" line="894"/>
+        <source>This session contains unsaved data. Close it anyway?</source>
+        <translation>此会话数据未保存。是否关闭?</translation>
+    </message>
+</context>
+<context>
+    <name>pv::Session</name>
+    <message>
+        <location filename="../pv/session.cpp" line="396"/>
+        <source>Can&apos;t restore generated signal of unknown type %1 (%2)</source>
+        <translation>无法还原生成的未知类型的信号 %1 (%2)</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="559"/>
+        <source>Failed to select device</source>
+        <translation>无法选择的设备</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="616"/>
+        <source>Failed to open device</source>
+        <translation>无法打开的设备</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="722"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="723"/>
+        <source>Unexpected input format: %1</source>
+        <translation>不支持的输入格式: %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="758"/>
+        <source>Failed to load %1</source>
+        <translation>无法加载 %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="797"/>
+        <source>No active device set, can&apos;t start acquisition.</source>
+        <translation>未设置活动设备,无法采集。</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="810"/>
+        <source>No channels enabled.</source>
+        <translation>未启用任何通道。</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="1311"/>
+        <source>Out of memory, acquisition stopped.</source>
+        <translation>内存不足,采集停止。</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="1518"/>
+        <source>Can&apos;t handle more than 64 logic channels.</source>
+        <translation>无法处理超过64个逻辑通道。</translation>
+    </message>
+</context>
+<context>
+    <name>pv::StoreSession</name>
+    <message>
+        <location filename="../pv/storesession.cpp" line="114"/>
+        <source>Can&apos;t save logic channel without data.</source>
+        <translation>无法保存,逻辑通道内没有数据。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="130"/>
+        <source>Can&apos;t save analog channel without data.</source>
+        <translation>无法保存,模拟通道内没有数据。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="142"/>
+        <source>No channels enabled.</source>
+        <translation>未启用任何通道。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="167"/>
+        <source>Can&apos;t save range without sample data.</source>
+        <translation>无法保存,光标范围内没有数据。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="192"/>
+        <location filename="../pv/storesession.cpp" line="299"/>
+        <location filename="../pv/storesession.cpp" line="314"/>
+        <source>Error while saving: </source>
+        <translation>保存时出错: </translation>
+    </message>
+</context>
+<context>
+    <name>pv::binding::Device</name>
+    <message>
+        <location filename="../pv/binding/device.cpp" line="82"/>
+        <source>Note for device developers: Ignoring device configuration capability &apos;%1&apos; as it is missing GET and/or SET</source>
+        <translation>设备开发人员注意:缺少 GET/SET,忽略该设备配置功能 &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../pv/binding/device.cpp" line="107"/>
+        <source>No Limit</source>
+        <translation>没有限制</translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::DecodeSignal</name>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="223"/>
+        <source>No decoders</source>
+        <translation>没有解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="230"/>
+        <source>There are no channels assigned to this decoder</source>
+        <translation>解码器没有选择通道</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="244"/>
+        <source>One or more required channels have not been specified</source>
+        <translation>解码器缺少一个或多个通道</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="260"/>
+        <source>No input data</source>
+        <translation>无数据输入</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="1325"/>
+        <source>Decoder reported an error</source>
+        <translation>解码器解码错误</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="1484"/>
+        <source>Failed to create decoder instance</source>
+        <translation>无法创建的解码器实例</translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::MathSignal</name>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="107"/>
+        <source>Math%1</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="306"/>
+        <source>No expression defined, nothing to do</source>
+        <translation>未定义表达式,不执行任何操作</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="345"/>
+        <source>%1 at line %2, column %3: %4</source>
+        <translation>%1 行 %2, 列 %3: %4</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="364"/>
+        <location filename="../pv/data/mathsignal.cpp" line="536"/>
+        <source>&quot;%1&quot; isn&apos;t a valid analog signal</source>
+        <translation>&quot;%1&quot; 不是有效的模拟信号</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="374"/>
+        <location filename="../pv/data/mathsignal.cpp" line="611"/>
+        <source>No data will be generated as %1 must be enabled</source>
+        <translation>不会生成数据,因为必须启用 %1</translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::SignalBase</name>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="525"/>
+        <source>Signal average</source>
+        <translation>平均信号电平</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="526"/>
+        <source>0.9V (for 1.8V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="527"/>
+        <source>1.8V (for 3.3V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="528"/>
+        <source>2.5V (for 5.0V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="529"/>
+        <source>1.5V (for TTL)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="534"/>
+        <source>Signal average +/- 15%</source>
+        <translation>平均信号电平 +/- 15%</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="535"/>
+        <source>0.3V/1.2V (for 1.8V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="536"/>
+        <source>0.7V/2.5V (for 3.3V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="537"/>
+        <source>1.3V/3.7V (for 5.0V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="538"/>
+        <source>0.8V/2.0V (for TTL)</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::Connect</name>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="58"/>
+        <source>&amp;Scan for devices using driver above</source>
+        <translation>使用上述驱动程序扫描设备</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="63"/>
+        <source>Connect to Device</source>
+        <translation>连接到设备</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="75"/>
+        <source>Step 1: Choose the driver</source>
+        <translation>步骤1:选择驱动程序</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="79"/>
+        <source>&amp;USB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="80"/>
+        <source>Serial &amp;Port</source>
+        <translation>串行和端口</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="81"/>
+        <source>&amp;TCP/IP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="116"/>
+        <source>Protocol:</source>
+        <translation>协议:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="134"/>
+        <source>Step 2: Choose the interface</source>
+        <translation>步骤2:选择接口</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="140"/>
+        <source>Step 3: Scan for devices</source>
+        <translation>步骤3:扫描设备</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="146"/>
+        <source>Step 4: Select the device</source>
+        <translation>步骤4:选择设备</translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::Settings</name>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="134"/>
+        <location filename="../pv/dialogs/settings.cpp" line="213"/>
+        <source>General</source>
+        <translation>常规</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="143"/>
+        <source>Views</source>
+        <translation>显示</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="153"/>
+        <location filename="../pv/dialogs/settings.cpp" line="411"/>
+        <source>Decoders</source>
+        <translation>解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="163"/>
+        <source>About</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="172"/>
+        <source>Logging</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="241"/>
+        <source>User interface language</source>
+        <translation>语言</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="252"/>
+        <source>User interface theme</source>
+        <translation>主题</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="254"/>
+        <source>(You may need to restart PulseView for all UI elements to update)</source>
+        <translation>(您可能需要重新启动PulseView才能更新所有UI元素)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="260"/>
+        <source>System Default</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="273"/>
+        <source>Qt widget style</source>
+        <translation>QT 软件界面风格</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="275"/>
+        <source>(Dark themes look best with the Fusion style)</source>
+        <translation>(深色主题与Fusion风格搭配效果最佳)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="282"/>
+        <source>Save session &amp;setup along with .sr file</source>
+        <translation>将会话设置与 .sr文件一起保存</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="286"/>
+        <source>Start acquisition for all open sessions when clicking &apos;Run&apos;</source>
+        <translation>单击“开始”时启动所有打开会话的采集</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="301"/>
+        <source>Trace View</source>
+        <translation>采集视图</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="309"/>
+        <source>Use colored trace &amp;background</source>
+        <translation>使用彩色通道背景</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="313"/>
+        <source>Constantly perform &amp;zoom-to-fit during acquisition</source>
+        <translation>采集数据过程中不断调整和缩放以适应窗口 ( &amp;Z )</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="317"/>
+        <source>Perform a zoom-to-&amp;fit when acquisition stops</source>
+        <translation>采集停止时执行调整和缩放以适应窗口 ( &amp;A )</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="321"/>
+        <source>Show time zero at the &amp;trigger</source>
+        <translation>在触发器处显示时间零点 ( &amp;T )</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="325"/>
+        <source>Always keep &amp;newest samples at the right edge during capture</source>
+        <translation>采集过程中始终跟踪右侧最新数据 ( &amp;Q )</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="329"/>
+        <source>Allow &amp;vertical dragging in the view area</source>
+        <translation>允许在视图区域中垂直拖动</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="333"/>
+        <source>Show data &amp;sampling points</source>
+        <translation>显示数据采样点</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="337"/>
+        <source>Fill &amp;high areas of logic signals</source>
+        <translation>填充逻辑信号高电平的区域 ( &amp;H )</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="344"/>
+        <source>Color to fill high areas of logic signals with</source>
+        <translation>填充逻辑信号高电平区域的颜色</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="348"/>
+        <source>Show analog minor grid in addition to div grid</source>
+        <translation>模拟通道除了div网格之外,还显示更多垂直细分网格</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="352"/>
+        <source>Highlight mouse cursor using a vertical marker line</source>
+        <translation>用竖线突出显示鼠标光标位置</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="356"/>
+        <location filename="../pv/dialogs/settings.cpp" line="382"/>
+        <location filename="../pv/dialogs/settings.cpp" line="391"/>
+        <source> pixels</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="361"/>
+        <source>Maximum distance from edges before markers snap to them</source>
+        <translation>光标吸附到边沿前的最大距离</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="368"/>
+        <source>Color to fill cursor area with</source>
+        <translation>填充光标范围内的颜色</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="371"/>
+        <source>None</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="372"/>
+        <source>Background</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="373"/>
+        <source>Dots</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="378"/>
+        <source>Conversion threshold display mode (analog traces only)</source>
+        <translation>转换阈值显示模式(仅限模拟通道)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="387"/>
+        <source>Default analog trace div height</source>
+        <translation>模拟通道垂直高度</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="396"/>
+        <source>Default logic trace height</source>
+        <translation>逻辑通道垂直高度</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="419"/>
+        <source>Allow configuration of &amp;initial signal state</source>
+        <translation>允许配置初始信号状态</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="423"/>
+        <source>Always show all &amp;rows, even if no annotation is visible</source>
+        <translation>始终显示所有解码,即使未解码</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="431"/>
+        <source>Annotation export format</source>
+        <translation>解码数据导出格式</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="432"/>
+        <source>%s = sample range; %d: decoder name; %r: row name; %c: class name</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="435"/>
+        <source>%1: longest annotation text; %a: all annotation texts; %q: use quotation marks</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="455"/>
+        <source>%1&lt;br /&gt;&lt;a href=&quot;http://%2&quot;&gt;%2&lt;/a&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="456"/>
+        <source>GNU GPL, version 3 or later</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="467"/>
+        <source>Versions, libraries and features:</source>
+        <translation>版本、库和功能:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="474"/>
+        <source>Firmware search paths:</source>
+        <translation>固件搜索路径:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="481"/>
+        <source>Protocol decoder search paths:</source>
+        <translation>协议解码器搜索路径:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="484"/>
+        <source>&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;(Note: Set environment variable SIGROKDECODE_DIR to add a custom directory)&lt;/td&gt;&lt;/tr&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="489"/>
+        <source>Supported hardware drivers:</source>
+        <translation>支持的硬件驱动程序:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="496"/>
+        <source>Supported input formats:</source>
+        <translation>支持的输入格式:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="503"/>
+        <source>Supported output formats:</source>
+        <translation>支持的输出格式:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="511"/>
+        <source>Supported protocol decoders:</source>
+        <translation>支持的协议解码器:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="519"/>
+        <source>Available Translations:</source>
+        <translation>可用翻译:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="567"/>
+        <source>Log level:</source>
+        <translation>日志级别:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="572"/>
+        <source> lines</source>
+        <translation> 行</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="581"/>
+        <source>Length of background buffer:</source>
+        <translation>日志缓存长度:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="587"/>
+        <source>&amp;Save to File</source>
+        <translation>保存到文件</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="594"/>
+        <source>&amp;Pop out</source>
+        <translation>独立弹出日志窗口</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="663"/>
+        <source>You selected a dark theme.
+Should I set the user-adjustable colors to better suit your choice?
+
+Please keep in mind that PulseView may need a restart to display correctly.</source>
+        <translation>您选择了一个深色主题。
+是否相应地调整特定的颜色,以更好地协调?
+PulseView可能需要重新启动才能正确显示。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="669"/>
+        <source>You selected a bright theme.
+Should I set the user-adjustable colors to better suit your choice?
+
+Please keep in mind that PulseView may need a restart to display correctly.</source>
+        <translation>您选择了一个明亮主题。
+是否相应地调整特定的颜色,以更好地协调?
+PulseView可能需要重新启动才能正确显示。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="834"/>
+        <source>Save Log</source>
+        <translation>保存日志</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="834"/>
+        <source>Log Files (*.txt *.log);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="846"/>
+        <source>Success</source>
+        <translation>成功</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="846"/>
+        <source>Log saved to %1.</source>
+        <translation>日志已保存到 %1。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="856"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="856"/>
+        <source>File %1 could not be written to.</source>
+        <translation>文件 %1 无法保存。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="870"/>
+        <source>%1 Log</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::StoreProgress</name>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="44"/>
+        <source>Saving...</source>
+        <translation>保存中...</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="44"/>
+        <source>Cancel</source>
+        <translation>取消</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="89"/>
+        <source>Failed to save session.</source>
+        <translation>未能保存会话。</translation>
+    </message>
+</context>
+<context>
+    <name>pv::popups::Channels</name>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="62"/>
+        <location filename="../pv/popups/channels.cpp" line="63"/>
+        <location filename="../pv/popups/channels.cpp" line="278"/>
+        <location filename="../pv/popups/channels.cpp" line="305"/>
+        <source>All</source>
+        <translation>全部</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="64"/>
+        <location filename="../pv/popups/channels.cpp" line="65"/>
+        <source>Logic</source>
+        <translation>逻辑</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="66"/>
+        <location filename="../pv/popups/channels.cpp" line="67"/>
+        <source>Analog</source>
+        <translation>模拟</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="68"/>
+        <source>Named</source>
+        <translation>更名过</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="69"/>
+        <source>Unnamed</source>
+        <translation>未更名</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="70"/>
+        <source>Changing</source>
+        <translation>有数据的</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="71"/>
+        <source>Non-changing</source>
+        <translation>无数据的</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="141"/>
+        <source>Disable: </source>
+        <translation>禁用: </translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="149"/>
+        <source>Enable: </source>
+        <translation>启用: </translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="286"/>
+        <location filename="../pv/popups/channels.cpp" line="306"/>
+        <source>None</source>
+        <translation>全关</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Bool</name>
+    <message>
+        <location filename="../pv/prop/bool.cpp" line="51"/>
+        <location filename="../pv/prop/bool.cpp" line="82"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>查询配置 &apos;%1&apos;: %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Double</name>
+    <message>
+        <location filename="../pv/prop/double.cpp" line="65"/>
+        <location filename="../pv/prop/double.cpp" line="96"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>查询配置 &apos;%1&apos;: %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Enum</name>
+    <message>
+        <location filename="../pv/prop/enum.cpp" line="113"/>
+        <location filename="../pv/prop/enum.cpp" line="176"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>查询配置 &apos;%1&apos;: %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Int</name>
+    <message>
+        <location filename="../pv/prop/int.cpp" line="63"/>
+        <location filename="../pv/prop/int.cpp" line="127"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>查询配置 &apos;%1&apos;: %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::String</name>
+    <message>
+        <location filename="../pv/prop/string.cpp" line="59"/>
+        <location filename="../pv/prop/string.cpp" line="84"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>查询配置 &apos;%1&apos;: %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::subwindows::decoder_selector::DecoderCollectionModel</name>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="40"/>
+        <source>Decoder</source>
+        <translation>解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="41"/>
+        <source>Name</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="42"/>
+        <source>ID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="49"/>
+        <source>All Decoders</source>
+        <translation>所有解码器</translation>
+    </message>
+</context>
+<context>
+    <name>pv::subwindows::decoder_selector::SubWindow</name>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="49"/>
+        <source>Select a decoder to see its description here.</source>
+        <translation>选择解码器以在此处查看其说明。</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="248"/>
+        <source>, %1</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="265"/>
+        <source>&lt;p align=&apos;right&apos;&gt;Tags: %1&lt;/p&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="312"/>
+        <source>Protocol decoder &lt;b&gt;%1&lt;/b&gt; requires input type &lt;b&gt;%2&lt;/b&gt; which several decoders provide.&lt;br&gt;Choose which one to use:&lt;br&gt;</source>
+        <translation>协议解码器 &lt;b&gt;%1&lt;/b&gt; 有不同的&lt;b&gt;%2&lt;/b&gt; 协议类型。&lt;br&gt;选择要使用的:&lt;br&gt;</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="320"/>
+        <source>Choose Decoder</source>
+        <translation>选择解码器</translation>
+    </message>
+</context>
+<context>
+    <name>pv::toolbars::MainBar</name>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="124"/>
+        <source>New &amp;View</source>
+        <translation>新建视图</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="130"/>
+        <source>&amp;Open...</source>
+        <translation>打开... ( &amp;O )</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="141"/>
+        <source>Restore Session Setu&amp;p...</source>
+        <translation>从文件打开会话设置...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="145"/>
+        <source>&amp;Save...</source>
+        <translation>保存... ( &amp;S )</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="156"/>
+        <source>Save &amp;As...</source>
+        <translation>另存为... ( &amp;A )</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="162"/>
+        <source>Save Selected &amp;Range As...</source>
+        <translation>将所选范围另存为... ( &amp;R )</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="173"/>
+        <source>Save Session Setu&amp;p...</source>
+        <translation>保存会话设置...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="179"/>
+        <source>&amp;Export</source>
+        <translation>导出</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="185"/>
+        <source>&amp;Import</source>
+        <translation>导入</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="189"/>
+        <source>&amp;Connect to Device...</source>
+        <translation>连接到设备...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="251"/>
+        <source>Add protocol decoder</source>
+        <translation>添加协议解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="261"/>
+        <source>Add math signal</source>
+        <translation>添加 math 信号</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="277"/>
+        <source>Configure Device</source>
+        <translation>设备配置</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="281"/>
+        <source>Configure Channels</source>
+        <translation>通道配置</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="395"/>
+        <source>Failed to get sample rate list:</source>
+        <translation>无法获取采样率列表:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="458"/>
+        <source>Failed to get sample rate:</source>
+        <translation>无法获取采样率:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="499"/>
+        <source>Failed to get sample limit list:</source>
+        <translation>无法获取采样限制列表:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="589"/>
+        <source>Failed to configure samplerate:</source>
+        <translation>无法配置采样率:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="616"/>
+        <source>Failed to configure sample count:</source>
+        <translation>无法配置采样计数:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="654"/>
+        <source>Missing Cursors</source>
+        <translation>缺少光标</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="654"/>
+        <source>You need to set the cursors before you can save the data enclosed by them to a session file (e.g. using the Show Cursors button).</source>
+        <translation>您需要设置光标,然后才能将光标范围内的数据保存到会话文件中(例如,使用“显示光标”按钮)。</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="672"/>
+        <source>Invalid Range</source>
+        <translation>无效的范围</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="672"/>
+        <source>The cursors don&apos;t define a valid range of samples.</source>
+        <translation>光标没有定义有效的采样范围。</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="684"/>
+        <source>%1 files </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="692"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="742"/>
+        <source>All Files</source>
+        <translation>所有文件</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="696"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="873"/>
+        <source>Save File</source>
+        <translation>保存文件</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="708"/>
+        <source>Export %1</source>
+        <translation>导出 %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="739"/>
+        <source>%1 files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="750"/>
+        <source>Import File</source>
+        <translation>导入文件</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="759"/>
+        <source>Import %1</source>
+        <translation>导入 %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="832"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="890"/>
+        <source>Open File</source>
+        <translation>打开文件</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="832"/>
+        <source>sigrok Sessions (*.sr);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="873"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="890"/>
+        <source>PulseView Session Setups (*.pvs);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="958"/>
+        <source>Total sampling time: %1</source>
+        <translation>总采样时间: %1</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::decoder_binary::View</name>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="83"/>
+        <source>Decoder:</source>
+        <translation>解码器:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="87"/>
+        <source>Show data as</source>
+        <translation>数据显示格式</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="93"/>
+        <source>Hexdump</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="110"/>
+        <source>&amp;Save...</source>
+        <translation>保存... ( &amp;S )</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="270"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="310"/>
+        <source>Save Binary Data</source>
+        <translation>保存二进制数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="270"/>
+        <source>Binary Data Files (*.bin);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="289"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="349"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="289"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="349"/>
+        <source>File %1 could not be written to.</source>
+        <translation>文件 %1 无法保存。</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="310"/>
+        <source>Hex Dumps (*.txt);;All Files (*)</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::tabular_decoder::AnnotationCollectionModel</name>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="56"/>
+        <source>Sample</source>
+        <translation>采样</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="57"/>
+        <source>Time</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="58"/>
+        <source>Decoder</source>
+        <translation>解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="59"/>
+        <source>Ann Row</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="60"/>
+        <source>Ann Class</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="61"/>
+        <source>Value</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
+        <source>s</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
+        <source>sa</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::tabular_decoder::View</name>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="176"/>
+        <source>Decoder:</source>
+        <translation>解码器:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="198"/>
+        <source>Hide Hidden Rows/Classes</source>
+        <translation>隐藏隐藏的行/列</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="202"/>
+        <source>&amp;Save...</source>
+        <translation>保存... ( &amp;S )</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="378"/>
+        <source>Save Annotations as CSV</source>
+        <translation>将解码数据另存为CSV</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="378"/>
+        <source>CSV Files (*.csv);;Text Files (*.txt);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="446"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="446"/>
+        <source>File %1 could not be written to.</source>
+        <translation>文件 %1 无法保存。</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::AnalogSignal</name>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="802"/>
+        <source>Number of pos vertical divs</source>
+        <translation>正区间div垂直细分网格数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="810"/>
+        <source>Number of neg vertical divs</source>
+        <translation>负区间div垂直细分网格数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="815"/>
+        <source> pixels</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="819"/>
+        <source>Div height</source>
+        <translation>Div高度</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="837"/>
+        <source>V/div</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="841"/>
+        <source>Vertical resolution</source>
+        <translation>垂直分辨率</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="850"/>
+        <source>Autoranging</source>
+        <translation>自动量程</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="855"/>
+        <source>none</source>
+        <translation>无</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="857"/>
+        <source>to logic via threshold</source>
+        <translation>通过阈值转换为逻辑</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="859"/>
+        <source>to logic via schmitt-trigger</source>
+        <translation>通过施密特触发器转换为逻辑</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="865"/>
+        <source>Conversion</source>
+        <translation>转换</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="874"/>
+        <source>Conversion threshold(s)</source>
+        <translation>转换阈值</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="884"/>
+        <source>analog</source>
+        <translation>模拟</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="885"/>
+        <source>converted</source>
+        <translation>转换</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="886"/>
+        <source>analog+converted</source>
+        <translation>模拟+转换</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="891"/>
+        <source>Show traces for</source>
+        <translation>显示采集</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Cursor</name>
+    <message>
+        <location filename="../pv/views/trace/cursor.cpp" line="97"/>
+        <source>Disable snapping</source>
+        <translation>禁用捕捉</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::CursorPair</name>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="128"/>
+        <source>Display interval</source>
+        <translation>显示间隔</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="140"/>
+        <source>Display frequency</source>
+        <translation>显示频率</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="152"/>
+        <source>Display samples</source>
+        <translation>显示采样</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::DecodeTrace</name>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="456"/>
+        <source>&lt;p&gt;&lt;i&gt;No decoders in the stack&lt;/i&gt;&lt;/p&gt;</source>
+        <translation>&lt;p&gt;&lt;i&gt;堆叠中没有解码器&lt;/i&gt;&lt;/p&gt;</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="467"/>
+        <source>&lt;i&gt;* Required channels&lt;/i&gt;</source>
+        <translation>&lt;i&gt;* 所需通道&lt;/i&gt;</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="471"/>
+        <source>Stack Decoder</source>
+        <translation>堆叠解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="472"/>
+        <source>Stack a higher-level decoder on top of this one</source>
+        <translation>在这之上面堆叠一个更高级别的解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="486"/>
+        <source>Delete</source>
+        <translation>删除</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="528"/>
+        <source>Resume decoding</source>
+        <translation>继续解码</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="535"/>
+        <source>Pause decoding</source>
+        <translation>暂停解码</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="543"/>
+        <source>Copy annotation text to clipboard</source>
+        <translation>将解码数据复制到剪贴板</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="552"/>
+        <source>Export all annotations</source>
+        <translation>导出所有解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="559"/>
+        <source>Export all annotations for this row</source>
+        <translation>导出此行的解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="568"/>
+        <source>Export all annotations, starting here</source>
+        <translation>导出从这以后所有的解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="575"/>
+        <source>Export annotations for this row, starting here</source>
+        <translation>导出从这以后此行的解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="584"/>
+        <source>Export all annotations within cursor range</source>
+        <translation>导出光标范围内所有的解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="591"/>
+        <source>Export annotations for this row within cursor range</source>
+        <translation>导出光标范围内此行的解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1079"/>
+        <source>%1:
+%2</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1123"/>
+        <source>&lt;b&gt;%1&lt;/b&gt; (%2) %3</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1192"/>
+        <source>Export annotations</source>
+        <translation>导出解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1192"/>
+        <source>Text Files (*.txt);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1257"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1257"/>
+        <source>File %1 could not be written to.</source>
+        <translation>文件 %1 无法保存。</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1324"/>
+        <source>Show this row</source>
+        <translation>显示此行</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1335"/>
+        <source>Show All</source>
+        <translation>全部显示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1343"/>
+        <source>Hide All</source>
+        <translation>全部隐藏</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Flag</name>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="132"/>
+        <source>Text</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="141"/>
+        <source>Delete</source>
+        <translation>删除</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="146"/>
+        <source>Disable snapping</source>
+        <translation>禁用捕捉</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Header</name>
+    <message>
+        <location filename="../pv/views/trace/header.cpp" line="137"/>
+        <source>Group</source>
+        <translation>组</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::LogicSignal</name>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="423"/>
+        <source>No trigger</source>
+        <translation>无触发</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="428"/>
+        <source>Trigger on rising edge</source>
+        <translation>上升沿触发</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="433"/>
+        <source>Trigger on high level</source>
+        <translation>高电平触发</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="438"/>
+        <source>Trigger on falling edge</source>
+        <translation>下降沿触发</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="443"/>
+        <source>Trigger on low level</source>
+        <translation>低电平触发</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="448"/>
+        <source>Trigger on rising or falling edge</source>
+        <translation>上升沿或下降沿触发</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="535"/>
+        <source> pixels</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="539"/>
+        <source>Trace height</source>
+        <translation>通道高度</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="563"/>
+        <source>Trigger</source>
+        <translation>触发</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::MathEditDialog</name>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="88"/>
+        <source>Math Expression Editor</source>
+        <translation>Math 表达式编辑器</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="93"/>
+        <source>Inputs:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="99"/>
+        <source>Variables:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="101"/>
+        <source>Basic operators:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="108"/>
+        <source>Assignments:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="118"/>
+        <source>General purpose functions:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="119"/>
+        <source>abs(x)         Absolute value of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="120"/>
+        <source>avg(x, y, ...) Average of all input values</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="121"/>
+        <source>ceil(x)                Smallest integer that is greater than or equal to x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="122"/>
+        <source>clamp(lb, x, ub)       Clamp x in range between lb and ub, where lb &lt; ub</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="123"/>
+        <source>equal(x, y)    Equality test between x and y using normalised epsilon</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="124"/>
+        <source>erf(x)         Error function of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="125"/>
+        <source>erfc(x)                Complimentary error function of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="126"/>
+        <source>exp(x)         e to the power of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="127"/>
+        <source>expm1(x)       e to the power of x minus 1, where x is very small.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="128"/>
+        <source>floor(x)               Largest integer that is less than or equal to x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="129"/>
+        <source>frac(x)                Fractional portion of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="130"/>
+        <source>hypot(x)               Hypotenuse of x and y (i.e. sqrt(x*x + y*y))</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="131"/>
+        <source>iclamp(lb, x, ub)      Inverse-clamp x outside of the range lb and ub, where lb &lt; ub.
+               If x is within the range it will snap to the closest bound</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="132"/>
+        <source>inrange(lb, x, ub)     In-range returns true when x is within the range lb and ub, where lb &lt; ub.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="133"/>
+        <source>log(x)         Natural logarithm of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="134"/>
+        <source>log10(x)               Base 10 logarithm of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="138"/>
+        <source>log1p(x)               Natural logarithm of 1 + x, where x is very small</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="139"/>
+        <source>log2(x)                Base 2 logarithm of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="140"/>
+        <source>logn(x)                Base N logarithm of x, where n is a positive integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="141"/>
+        <source>max(x, y, ...) Largest value of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="142"/>
+        <source>min(x, y, ...) Smallest value of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="143"/>
+        <source>mul(x, y, ...) Product of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="144"/>
+        <source>ncdf(x)                Normal cumulative distribution function</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="145"/>
+        <source>nequal(x, y)   Not-equal test between x and y using normalised epsilon</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="146"/>
+        <source>pow(x, y)      x to the power of y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="147"/>
+        <source>root(x, n)     Nth-Root of x, where n is a positive integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="148"/>
+        <source>round(x)               Round x to the nearest integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="149"/>
+        <source>roundn(x, n)   Round x to n decimal places, where n &gt; 0 and is an integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="150"/>
+        <source>sgn(x)         Sign of x; -1 if x &lt; 0, +1 if x &gt; 0, else zero</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="151"/>
+        <source>sqrt(x)                Square root of x, where x &gt;= 0</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="152"/>
+        <source>sum(x, y, ..,) Sum of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="153"/>
+        <source>swap(x, y)     Swap the values of the variables x and y and return the current value of y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="154"/>
+        <source>trunc(x)               Integer portion of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="158"/>
+        <source>Trigonometry functions:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="159"/>
+        <source>acos(x)                Arc cosine of x expressed in radians. Interval [-1,+1]</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="160"/>
+        <source>acosh(x)               Inverse hyperbolic cosine of x expressed in radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="161"/>
+        <source>asin(x)                Arc sine of x expressed in radians. Interval [-1,+1]</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="162"/>
+        <source>asinh(x)               Inverse hyperbolic sine of x expressed in radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="163"/>
+        <source>atan(x)                Arc tangent of x expressed in radians. Interval [-1,+1]</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="164"/>
+        <source>atan2(x, y)    Arc tangent of (x / y) expressed in radians. [-pi,+pi]  </source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="165"/>
+        <source>atanh(x)               Inverse hyperbolic tangent of x expressed in radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="166"/>
+        <source>cos(x)         Cosine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="167"/>
+        <source>cosh(x)                Hyperbolic cosine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="168"/>
+        <source>cot(x)         Cotangent of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="169"/>
+        <source>csc(x)         Cosectant of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="170"/>
+        <source>sec(x)         Secant of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="171"/>
+        <source>sin(x)         Sine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="172"/>
+        <source>sinc(x)                Sine cardinal of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="173"/>
+        <source>sinh(x)                Hyperbolic sine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="174"/>
+        <source>tan(x)         Tangent of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="175"/>
+        <source>tanh(x)                Hyperbolic tangent of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="176"/>
+        <source>deg2rad(x)     Convert x from degrees to radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="177"/>
+        <source>deg2grad(x)    Convert x from degrees to gradians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="178"/>
+        <source>rad2deg(x)     Convert x from radians to degrees</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="179"/>
+        <source>grad2deg(x)    Convert x from gradians to degrees</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="183"/>
+        <source>Logic operators:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="200"/>
+        <source>Comparisons:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="201"/>
+        <source>x = y or x == y        True only if x is strictly equal to y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="202"/>
+        <source>x &lt;&gt; y or x != y True only if x does not equal y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="203"/>
+        <source>x &lt; y               True only if x is less than y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="204"/>
+        <source>x &lt;= y              True only if x is less than or equal to y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="205"/>
+        <source>x &gt; y               True only if x is greater than y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="206"/>
+        <source>x &gt;= y              True only if x is greater than or equal to y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="207"/>
+        <source>Flow control:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="208"/>
+        <source>{ ... }                Beginning and end of instruction block</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="209"/>
+        <source>if (x, y, z)   If x is true then return y else return z
+if (x) y;                      variant without implied else
+if (x) { y };          variant with an instruction block
+if (x) y; else z;              variant with explicit else
+if (x) { y } else { z };       variant with instruction blocks</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="210"/>
+        <source>x ? y : z      Ternary operator, equivalent to &apos;if (x, y, z)&apos;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="211"/>
+        <source>switch {               The first true case condition that is encountered will
+ case x &gt; 1: a;     determine the result of the switch. If none of the case
+ case x &lt; 1: b;     conditions hold true, the default action is used
+ default:     c;       to determine the return value
+}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="215"/>
+        <source>while (conditon) {     Evaluates expression repeatedly as long as condition is true,
+ expression;   returning the last value of expression
+}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="216"/>
+        <source>repeat         Evalues expression repeatedly as long as condition is false,
+ expression;   returning the last value of expression
+until (condition)
+</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="217"/>
+        <source>for (var x := 0; condition; x += 1) {  Repeatedly evaluates expression while the condition is true,
+ expression                    while evaluating the &apos;increment&apos; expression on each loop
+}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="218"/>
+        <source>break          Terminates the execution of the nearest enclosed loop, returning NaN</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="219"/>
+        <source>break[x]               Terminates the execution of the nearest enclosed loop, returning x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="220"/>
+        <source>continue               Interrupts loop execution and resumes with the next loop iteration</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="221"/>
+        <source>return[x]              Returns immediately from within the current expression, returning x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="222"/>
+        <source>~(expr; expr; ...)     Evaluates each sub-expression and returns the value of the last one
+~{expr; expr; ...}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="234"/>
+        <source>Copy to expression</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="247"/>
+        <source>Basics</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="248"/>
+        <source>Functions 1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="249"/>
+        <source>Functions 2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="250"/>
+        <source>Trigonometry</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="251"/>
+        <source>Logic</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="252"/>
+        <source>Flow Control 1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="253"/>
+        <source>Flow Control 2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="254"/>
+        <source>Examples</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::MathSignal</name>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="317"/>
+        <source>Expression</source>
+        <translation>表达式</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="321"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="331"/>
+        <source>same as session</source>
+        <translation>与会话相同</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="322"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="332"/>
+        <source>100</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="323"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="333"/>
+        <source>10000</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="324"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="334"/>
+        <source>1000000</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="327"/>
+        <source>Number of Samples</source>
+        <translation>采样数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="335"/>
+        <source>Sample rate</source>
+        <translation>采样率</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Ruler</name>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="153"/>
+        <source>Create marker here</source>
+        <translation>在此处创建标记</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="157"/>
+        <source>Set as zero point</source>
+        <translation>设为零点</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="162"/>
+        <source>Reset zero point</source>
+        <translation>重置零点</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="175"/>
+        <source>Disable mouse hover marker</source>
+        <translation>禁用鼠标位置标记</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="175"/>
+        <source>Enable mouse hover marker</source>
+        <translation>启用鼠标位置标记</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Signal</name>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="153"/>
+        <source>Name</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="167"/>
+        <source>Remove</source>
+        <translation>删除</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="169"/>
+        <source>Disable</source>
+        <translation>关闭</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::StandardBar</name>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="54"/>
+        <source>Zoom &amp;In</source>
+        <translation>放大</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="62"/>
+        <source>Zoom &amp;Out</source>
+        <translation>缩小</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="70"/>
+        <source>Zoom to &amp;Fit</source>
+        <translation>适应窗口</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="82"/>
+        <source>Show &amp;Cursors</source>
+        <translation>显示光标</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="85"/>
+        <source>Display last segment only</source>
+        <translation>仅显示最后一段</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="90"/>
+        <source>Display last complete segment only</source>
+        <translation>仅显示最后一个完整分段</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="95"/>
+        <source>Display a single segment</source>
+        <translation>仅显示一段</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::TimeMarker</name>
+    <message>
+        <location filename="../pv/views/trace/timemarker.cpp" line="191"/>
+        <source>Time</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Trace</name>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="229"/>
+        <source>Create marker here</source>
+        <translation>在此处创建标记</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="338"/>
+        <source>Color</source>
+        <translation>颜色</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="403"/>
+        <source>Name</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::TraceGroup</name>
+    <message>
+        <location filename="../pv/views/trace/tracegroup.cpp" line="140"/>
+        <source>Ungroup</source>
+        <translation>取消组合</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::View</name>
+    <message>
+        <location filename="../pv/views/trace/view.cpp" line="1610"/>
+        <source>Create marker here</source>
+        <translation>在此处创建标记</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::DecoderGroupBox</name>
+    <message>
+        <location filename="../pv/widgets/decodergroupbox.cpp" line="48"/>
+        <source>Show/hide this decoder trace</source>
+        <translation>显示/隐藏此解码器采集</translation>
+    </message>
+    <message>
+        <location filename="../pv/widgets/decodergroupbox.cpp" line="58"/>
+        <source>Delete this decoder trace</source>
+        <translation>删除此解码器采集</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::DeviceToolButton</name>
+    <message>
+        <location filename="../pv/widgets/devicetoolbutton.cpp" line="80"/>
+        <location filename="../pv/widgets/devicetoolbutton.cpp" line="87"/>
+        <source>&lt;No Device&gt;</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::ExportMenu</name>
+    <message>
+        <location filename="../pv/widgets/exportmenu.cpp" line="71"/>
+        <source>Export %1...</source>
+        <translation>导出 %1...</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::ImportMenu</name>
+    <message>
+        <location filename="../pv/widgets/importmenu.cpp" line="68"/>
+        <source>Import %1...</source>
+        <translation>导入 %1...</translation>
+    </message>
+</context>
+</TS>
index da5fffcdb3af28d4ada2044e32e88c4a2cd7cd3b..0867187c57b23815dc6bb0de32efbe16001233cf 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -60,6 +60,7 @@
 #include "pv/mainwindow.hpp"
 #include "pv/session.hpp"
 #include "pv/util.hpp"
+#include "pv/data/segment.hpp"
 
 #ifdef ANDROID
 #include <libsigrokandroidutils/libsigrokandroidutils.h>
 
 #ifdef _WIN32
 #include <QtPlugin>
+#ifdef QT_STATIC
 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
 Q_IMPORT_PLUGIN(QSvgPlugin)
 #endif
+#endif
 
 using std::exception;
 using std::ifstream;
@@ -274,8 +277,10 @@ int main(int argc, char *argv[])
        for (int i = 0; i < argc; i++)
                open_files.emplace_back(argv[i]);
 
-       qRegisterMetaType<pv::util::Timestamp>("util::Timestamp");
        qRegisterMetaType<uint64_t>("uint64_t");
+       qRegisterMetaType<pv::util::Timestamp>("util::Timestamp");
+       qRegisterMetaType<SharedPtrToSegment>("SharedPtrToSegment");
+       qRegisterMetaType<shared_ptr<pv::data::SignalBase>>("shared_ptr<SignalBase>");
 
        // Prepare the global settings since logging needs them early on
        pv::GlobalSettings settings;
@@ -327,7 +332,7 @@ int main(int argc, char *argv[])
                // Create the device manager, initialise the drivers
                pv::DeviceManager device_manager(context, driver, do_scan);
 
-               a.collect_version_info(context);
+               a.collect_version_info(device_manager);
                if (show_version) {
                        a.print_version_info();
                } else {
@@ -347,10 +352,9 @@ int main(int argc, char *argv[])
 #ifdef ENABLE_SIGNALS
                        if (SignalHandler::prepare_signals()) {
                                SignalHandler *const handler = new SignalHandler(&w);
-                               QObject::connect(handler, SIGNAL(int_received()),
-                                       &w, SLOT(close()));
-                               QObject::connect(handler, SIGNAL(term_received()),
-                                       &w, SLOT(close()));
+                               QObject::connect(handler, SIGNAL(int_received()), &w, SLOT(close()));
+                               QObject::connect(handler, SIGNAL(term_received()), &w, SLOT(close()));
+                               QObject::connect(handler, SIGNAL(usr1_received()), &w, SLOT(on_run_stop_clicked()));
                        } else
                                qWarning() << "Could not prepare signal handler.";
 #endif
index c425f36b3bdd7eb3cf246b6c593a97218a8edcfd..3b881a56d6f3e61216742750a3bdcc48feb294fe 100644 (file)
@@ -19,6 +19,8 @@
 
 cmake_minimum_required(VERSION 2.8.12)
 
+project(PV_MANUAL)
+
 # External dependencies, required and optional tools.
 find_program(ASCIIDOCTOR_EXECUTABLE NAMES asciidoctor)
 find_program(ASCIIDOCTOR_PDF_EXECUTABLE NAMES asciidoctor-pdf)
@@ -34,6 +36,22 @@ set(MANUAL_SRC "${CMAKE_CURRENT_SOURCE_DIR}/manual.txt")
 set(MANUAL_OUT_HTML "${CMAKE_CURRENT_BINARY_DIR}/manual.html")
 set(MANUAL_OUT_PDF "${CMAKE_CURRENT_BINARY_DIR}/manual.pdf")
 
+# Make in-source images/ content available to the output hierarchy for
+# the inspection of created output documents during development in the
+# case of out-of-source build configurations.
+if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/images")
+       message(STATUS "creating symlink for manual's images/ subdirectory")
+       execute_process(
+               COMMAND ${CMAKE_COMMAND} -E create_symlink
+                       "${CMAKE_CURRENT_SOURCE_DIR}/images"
+                       "${CMAKE_CURRENT_BINARY_DIR}/images"
+               RESULT_VARIABLE IMAGES_LINK_RESULT
+       )
+       if (NOT IMAGES_LINK_RESULT EQUAL 0)
+               message(WARNING "manual rendering will lack images")
+       endif ()
+endif ()
+
 # Manual related make(1) targets.
 add_custom_target(manual-html
        COMMAND ${ASCIIDOCTOR_EXECUTABLE}
@@ -57,7 +75,7 @@ if (ASCIIDOCTOR_PDF_EXECUTABLE)
                BYPRODUCTS ${MANUAL_OUT_PDF}
                DEPENDS ${MANUAL_SRC}
                WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
-               COMMENT "Generating manual, HTML output"
+               COMMENT "Generating manual, PDF output"
        )
 else ()
        add_custom_target(manual-pdf
index bc2c11d0e426736dd872217365650089fe4bdbb6..b6547fe409f6f9b8eb076c563d4ea7df25641308 100644 (file)
@@ -30,10 +30,12 @@ you'd expect. To do so, you'll want to use cursors and markers.
 
 In the picture above, you can enable the cursor by clicking on the cursor button.
 You can move both of its boundaries around by clicking on the blue flags in the
-time scale area. The area between the two boundary lines shows the time distance
-and its inverse (i.e. the frequency). If you can't see it, just zoom in until it
-shows. You can also move both boundaries at the same time by dragging the label
-where this information is shown.
+time scale area. The area between the two boundary lines shows the time distance,
+its inverse (i.e. the frequency) and/or the number of samples encompassed. If there's
+not enough space to see these, you can either zoom in until it shows, hover the mouse
+cursor over the label in the middle or right-click on the label to  configure what
+you want to see. You can also move both boundaries at the same time by dragging said
+label.
 
 image::pv_cursors_markers.png[]
 
@@ -48,6 +50,19 @@ the ruler or a signal trace.
 You can click on its label and you'll have the option to change its name, or
 drag it to reposition it.
 
+When you have multiple markers, you can have PulseView show you the time difference
+between the markers by hovering over one of them, like so:
+
+image::pv_marker_deltas.png[]
+
+This works on the cursor, too.
+
+Speaking of which - if you want to place or move the cursor ranges quickly, you
+can also press '1' and '2' on your keyboard to attach either side to your mouse
+cursor. They will stay put when you either press Esc or click with the left
+mouse button. This also works when the cursor isn't even showing, so using this
+method allows you to place the cursor quickly without having to enable it first.
+
 [NOTE]
 For timing comparison purposes, you can also enable a vertical marker line that
 follows your mouse cursor: _Settings_ -> _Views_ -> _Highlight mouse cursor_
@@ -55,7 +70,7 @@ follows your mouse cursor: _Settings_ -> _Views_ -> _Highlight mouse cursor_
 [NOTE]
 There is also a special kind of marker that appears for each time the data
 acquisition device has triggered. It cannot be moved and appears as a vertical
-dashed line.
+dashed blue line.
 
 === Special-Purpose Decoders
 
index d50a6983e02457ec686e8d4e105ac55cd3c378c3..6915f82c0292e1f0f7961eae93db14d532f9f210 100644 (file)
@@ -50,7 +50,7 @@ image::pv_decoders_3.png[]
 With the stacked decoder added, we can now see that PulseView has decoded the meaning
 of the I²C commands, so that we don't need to bother searching the reference manual.
 In this view, we can see that the I²C packet was a command to read the date and time,
-which was then reported to be 10.03.2013 23:35:30.
+which was then reported to be "10.03.2013 23:35:30".
 
 In this example, we added the I²C and DS1307 decoders separately. However, when opening
 the decoder selector window, you can also double-click on the DS1307 decoder and PulseView
@@ -79,6 +79,48 @@ as you can now visually understand where the ranges for high and low are placed.
 Aside from the default conversion threshold(s), you can choose from a few common presets
 or enter custom values as well. They take the form "0.0V" and "0.0V/0.0V", respectively.
 
+=== Per-row Settings and Actions
+
+Sometimes, you don't want to see all protocol decoder rows or all of the annotation classes
+available in a row. To do so, simply click on the arrow or label of the row you want to
+customize.
+
+image::pv_class_selectors.png[]
+
+From that menu, you can either show/hide the entire row or choose the annotation classes
+you want to see. Everything is visible by default but if you want to focus on specific
+protocol messages or status annotations like warnings or errors, this should help.
+
+Also, if you are examining really long traces, disabling annotations for the most-often
+occuring class (e.g. bit annotations for SPI) then drawing performance will increase, too.
+
+=== Binary Decoder Output
+
+While all protocol decoders create visible annotations, some of them also create binary
+output data which isn't immediately visible at the moment. However, you can examine it
+by opening the Binary Decoder Output View as shown below.
+
+image::pv_binary_decoder_output_view.png[]
+
+Once opened, you need to select a decoder with binary output for it to show anything -
+among which are I2C, I2S, EEPROM24xx, SPI and UART. Having acquired some I2S data and
+using the I2S protocol decoder lets you have the sound data as raw .wav file data, for
+example:
+
+image::pv_binary_decoder_output_view_i2s.png[]
+
+Using the save icon at the top then lets you save this data either as a binary file
+(in this case creating a valid .wav file) or various types of hex dumps. If you want to
+only save a certain part of the binary data, simply select that part before saving.
+
+You may have noticed that the bytes are grouped by color somehow. The meaning behind
+this is that every chunk of bytes emitted by the protocol decoder receives one color,
+the next chunk another color and so on. As there are currently three colors, the cycle
+repeats. This makes it easier to visually organize the data that you see - in the case
+of the I2S decoder, the header has one color because it's sent out in one go and
+following that, every sample for left/right consists of 4 bytes with the same color
+since they're sent out one by one.
+
 === Troubleshooting
 
 In case a protocol decoder doesn't provide the expected result, there are several things
@@ -88,29 +130,29 @@ The first check you should perform is whether the time unit in the ruler
 is given as "sa". This is short for "samples" and means that the device didn't provide
 a sample rate and so PulseView has no way of showing a time scale in seconds or
 fractions thereof. While some decoders can run without timing information, or only
-optionally make use of the time scale, others may not be able to interpret the
-input data since timing information is an essential part of the very protocol.
+optionally make use of it, others may not be able to interpret the input data since
+timing information may be an essential part of that protocol.
 
 Another issue to remain aware of is that decoders need enough samples per protocol step
 to reliably interpret the information. In typical cases the minimum sample rate should
-be four to five times the rate of the fastest activity in the protocol.
+be 4-5 times the rate of the fastest activity in the protocol (e.g. its clock signal).
 
 If a protocol decoder runs but shows you annotations that don't seem to make any sense,
 it's worth double-checking the decoder settings. One common source of error is the
 baud rate. For example, the CAN protocol decoder doesn't know what baud rate
 is used on the bus that you captured, so it could be that a different baud rate is used
-than the one you set. Also, if this is still not the reason for the malfunction, it's
-worth checking whether any of the signals have been captured inverted. Again using the
-CAN bus as an example, the decoder will decode the signal just fine if it's inverted but
+than the one you set. If this is still not the reason for the malfunction, it's worth
+checking whether any of the signals have been captured inverted. Again using the CAN
+bus as an example, the decoder will decode the signal just fine if it's inverted but
 it'll show data even when the signal looks "idle".
 
 When a protocol decoder stops execution because of an unmet constraint (required input
 not connected, essential parameter not specified) or a bug in the decoder itself, you
 will be presented a static red message in the protocol decoder's display area.
-In that case, you check the log output in the settings menu. There you'll find the Python
-error description which you can use to either adjust the configuration,
-or debug the decoder (and let us know of the fix) or you can copy that information and
-file a bug report so that we can fix it.
+In that case, you can check the log output in the settings menu. There you'll find the
+Python error description which you can use to either adjust the configuration,
+debug the decoder (and let us know of the fix) or create a bug report so that we can
+fix it.
 
 Further helpful knowledge and explanations on logic analyzers can be found in our
 https://sigrok.org/wiki/FAQ#Where_can_I_learn_more_about_logic_analyzers.3F["Learn about logic analyzers" FAQ item].
@@ -122,12 +164,19 @@ can do so by right-clicking into the area of the decode signal (not on the signa
 on the left). You are shown several export methods to choose from, with the last one
 being only available if the cursor is enabled.
 
+image::pv_ann_export_menu.png[]
+
 After you chose a method that suits your needs, you are prompted for a file to export
 the annotations to. The contents of the file very much depend on the option you chose
 but also on the annotation export format string that you can define in the _Decoders_
 menu of the settings dialog. If the default output isn't useful to you, you can
 customize it there.
 
+image::pv_ann_export_format.png[]
+
+For example, the string "%s %d: %1" will generate this type of output for the DS1307
+RTC clock protocol decoder: "253-471 DS1307: Read date/time: Sunday, 10.03.2013 23:35:30"
+
 === Creating a Protocol Decoder
 
 Protocol decoders are written in Python and can be created using nothing more than a
diff --git a/manual/images/pv_ann_export_format.png b/manual/images/pv_ann_export_format.png
new file mode 100644 (file)
index 0000000..c75c488
Binary files /dev/null and b/manual/images/pv_ann_export_format.png differ
diff --git a/manual/images/pv_ann_export_menu.png b/manual/images/pv_ann_export_menu.png
new file mode 100644 (file)
index 0000000..e1368fd
Binary files /dev/null and b/manual/images/pv_ann_export_menu.png differ
diff --git a/manual/images/pv_binary_decoder_output_view.png b/manual/images/pv_binary_decoder_output_view.png
new file mode 100644 (file)
index 0000000..d8cb49d
Binary files /dev/null and b/manual/images/pv_binary_decoder_output_view.png differ
diff --git a/manual/images/pv_binary_decoder_output_view_i2s.png b/manual/images/pv_binary_decoder_output_view_i2s.png
new file mode 100644 (file)
index 0000000..8033592
Binary files /dev/null and b/manual/images/pv_binary_decoder_output_view_i2s.png differ
diff --git a/manual/images/pv_class_selectors.png b/manual/images/pv_class_selectors.png
new file mode 100644 (file)
index 0000000..c1550ab
Binary files /dev/null and b/manual/images/pv_class_selectors.png differ
diff --git a/manual/images/pv_marker_deltas.png b/manual/images/pv_marker_deltas.png
new file mode 100644 (file)
index 0000000..46f5bf3
Binary files /dev/null and b/manual/images/pv_marker_deltas.png differ
index c4a31277760461b5b1bf571543a0ad2caf610824..11983e5f2662fcfc6c5a23be8d9341bbaa377886 100644 (file)
@@ -1,6 +1,6 @@
 PulseView User Manual
 =====================
-unreleased development snapshot, dated 2018-10-29
+unreleased pulseview development snapshot, manual last changed 2020-02-29
 :doctype: book
 :imagesdir: ./images
 :sectnums:
index bfd87f854d19f9b0e0c2ce9725e11d6d66ef21a5..bde7575022d51fc6269d99898bae6446466611a1 100644 (file)
@@ -15,7 +15,7 @@ as $5. These can easily be found by searching for _24MHz Logic Analyzer_. There
 Cypress FX2 boards such as the Lcsoft Mini Board, which can usually be found by searching for
 _Cypress FX2 Board_ or similar.
 
-In addition, a good set of https://sigrok.org/wiki/Probe_comparison[quality probe hooks] is recommended.
+Additionally, a good set of https://sigrok.org/wiki/Probe_comparison[quality probe hooks] is recommended.
 
 Aside from FX2-based logic analyzers, sigrok also supports FX2-based oscilloscopes such as the
 https://sigrok.org/wiki/Hantek_6022BE[Hantek 6022BE], non-FX2 devices like the
index 4427a759cf3266060af90815b94ee6dfadebd908..706fe8d03274a355b73dc91e209f5814b060ab89 100644 (file)
@@ -1,6 +1,7 @@
 <RCC>
     <qresource prefix="/">
        <file>icons/add-decoder.svg</file>
+       <file>icons/add-math-signal.svg</file>
        <file>icons/application-exit.png</file>
        <file>icons/channels.svg</file>
        <file>icons/decoder-delete.svg</file>
@@ -9,6 +10,7 @@
        <file>icons/document-new.png</file>
        <file>icons/document-open.png</file>
        <file>icons/document-save-as.png</file>
+       <file>icons/math.svg</file>
        <file>icons/edit-paste.svg</file>
        <file>icons/help-browser.png</file>
        <file>icons/information.svg</file>
index 3e4691d0776434398467cd0cd0b3230bc37317ec..cce6393c39054fb4a961fdc1bd789863e9495d69 100644 (file)
@@ -36,6 +36,8 @@
 #include <libsigrokdecode/libsigrokdecode.h>
 #endif
 
+#include <pv/exprtk.hpp>
+
 #include "application.hpp"
 #include "config.h"
 #include "globalsettings.hpp"
@@ -65,15 +67,15 @@ Application::Application(int &argc, char* argv[]) :
        setOrganizationDomain("sigrok.org");
 }
 
-QStringList Application::get_languages()
+const QStringList Application::get_languages() const
 {
-       QStringList files = QDir(":/l10n/").entryList(QStringList("*.qm"), QDir::Files);
+       const QStringList files = QDir(":/l10n/").entryList(QStringList("*.qm"), QDir::Files);
 
        QStringList result;
        result << "en";  // Add default language to the set
 
        // Remove file extensions
-       for (QString file : files)
+       for (const QString& file : files)
                result << file.split(".").front();
 
        result.sort(Qt::CaseInsensitive);
@@ -81,6 +83,16 @@ QStringList Application::get_languages()
        return result;
 }
 
+const QString Application::get_language_editors(const QString& language) const
+{
+       if (language == "de") return "Sören Apel, Uwe Hermann";
+       if (language == "es_MX") return "Carlos Diaz, Ulices Avila Hernandez";
+       if (language == "ja_jp") return "Yukari Shoji";
+       if (language == "zh_cn") return "ZtyPro";
+
+       return QString();
+}
+
 void Application::switch_language(const QString& language)
 {
        removeTranslator(&app_translator_);
@@ -96,7 +108,11 @@ void Application::switch_language(const QString& language)
                        qWarning() << "Translation resource" << resource << "not found";
 
                // Qt translations
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               QString tr_path(QLibraryInfo::path(QLibraryInfo::TranslationsPath));
+#else
                QString tr_path(QLibraryInfo::location(QLibraryInfo::TranslationsPath));
+#endif
 
                if (qt_translator_.load("qt_" + language, tr_path))
                        installTranslator(&qt_translator_);
@@ -133,13 +149,14 @@ void Application::on_setting_changed(const QString &key, const QVariant &value)
                switch_language(value.toString());
 }
 
-void Application::collect_version_info(shared_ptr<sigrok::Context> context)
+void Application::collect_version_info(pv::DeviceManager &device_manager)
 {
        // Library versions and features
        version_info_.emplace_back(applicationName(), applicationVersion());
        version_info_.emplace_back("Qt", qVersion());
        version_info_.emplace_back("glibmm", PV_GLIBMM_VERSION);
        version_info_.emplace_back("Boost", BOOST_LIB_VERSION);
+       version_info_.emplace_back("exprtk", QString::fromUtf8(exprtk::information::date));
 
        version_info_.emplace_back("libsigrok", QString("%1/%2 (rt: %3/%4)")
                .arg(SR_PACKAGE_VERSION_STRING, SR_LIB_VERSION_STRING,
@@ -200,17 +217,18 @@ void Application::collect_version_info(shared_ptr<sigrok::Context> context)
 #endif
 
        // Device drivers
-       for (auto& entry : context->drivers())
-               driver_list_.emplace_back(QString::fromUtf8(entry.first.c_str()),
-                       QString::fromUtf8(entry.second->long_name().c_str()));
+       for (auto& entry : device_manager.context()->drivers())
+               if (device_manager.driver_supported(entry.second))
+                       driver_list_.emplace_back(QString::fromUtf8(entry.first.c_str()),
+                               QString::fromUtf8(entry.second->long_name().c_str()));
 
        // Input formats
-       for (auto& entry : context->input_formats())
+       for (auto& entry : device_manager.context()->input_formats())
                input_format_list_.emplace_back(QString::fromUtf8(entry.first.c_str()),
                        QString::fromUtf8(entry.second->description().c_str()));
 
        // Output formats
-       for (auto& entry : context->output_formats())
+       for (auto& entry : device_manager.context()->output_formats())
                output_format_list_.emplace_back(QString::fromUtf8(entry.first.c_str()),
                        QString::fromUtf8(entry.second->description().c_str()));
 
index 61fe46e2480dc855210e586230c2e6960e070101..bfe4657009ae82407e139261914eb22ff461d2c9 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <libsigrokcxx/libsigrokcxx.hpp>
 
+#include "devicemanager.hpp"
 #include "globalsettings.hpp"
 
 using std::shared_ptr;
@@ -41,11 +42,13 @@ class Application : public QApplication, public pv::GlobalSettingsInterface
 public:
        Application(int &argc, char* argv[]);
 
-       QStringList get_languages();
+       const QStringList get_languages() const;
+       const QString get_language_editors(const QString& language) const;
        void switch_language(const QString& language);
+
        void on_setting_changed(const QString &key, const QVariant &value);
 
-       void collect_version_info(shared_ptr<sigrok::Context> context);
+       void collect_version_info(pv::DeviceManager &device_manager);
        void print_version_info();
 
        vector< pair<QString, QString> > get_version_info() const;
index 37c9d43d55248218b4e7e247cd6173fa1696b664..855052cddc7e909a7c77c62783f50154ea702dd1 100644 (file)
@@ -59,20 +59,30 @@ Device::Device(shared_ptr<sigrok::Configurable> configurable) :
 
        for (auto key : keys) {
 
+               string descr_str;
+               try {
+                       descr_str = key->description();
+               } catch (Error& e) {
+                       descr_str = key->name();
+               }
+               const QString descr = QString::fromStdString(descr_str);
+
                auto capabilities = configurable->config_capabilities(key);
 
                if (!capabilities.count(Capability::GET) ||
-                       !capabilities.count(Capability::SET))
-                       continue;
+                       !capabilities.count(Capability::SET)) {
 
-               string name_str;
-               try {
-                       name_str = key->description();
-               } catch (Error& e) {
-                       name_str = key->name();
-               }
+                       // Ignore common read-only keys
+                       if ((key->id() == SR_CONF_CONTINUOUS) || (key->id() == SR_CONF_TRIGGER_MATCH) ||
+                           (key->id() == SR_CONF_CONN) || (key->id() == SR_CONF_SERIALCOMM) || (key->id() == SR_CONF_NUM_LOGIC_CHANNELS) ||
+                           (key->id() == SR_CONF_NUM_ANALOG_CHANNELS) || (key->id() == SR_CONF_SESSIONFILE) || (key->id() == SR_CONF_CAPTUREFILE) ||
+                           (key->id() == SR_CONF_CAPTURE_UNITSIZE))
+                               continue;
 
-               const QString name = QString::fromStdString(name_str);
+                       qDebug() << QString(tr("Note for device developers: Ignoring device configuration capability '%1' " \
+                               "as it is missing GET and/or SET")).arg(descr);
+                       continue;
+               }
 
                const Property::Getter get = [&, key]() {
                        return configurable_->config_get(key); };
@@ -88,7 +98,13 @@ Device::Device(shared_ptr<sigrok::Configurable> configurable) :
                        break;
 
                case SR_CONF_CAPTURE_RATIO:
-                       bind_int(name, "", "%", pair<int64_t, int64_t>(0, 100), get, set);
+                       bind_int(descr, "", "%", pair<int64_t, int64_t>(0, 100), get, set);
+                       break;
+
+               case SR_CONF_LIMIT_FRAMES:
+                       // Value 0 means that there is no limit
+                       bind_int(descr, "", "", pair<int64_t, int64_t>(0, 1000000), get, set,
+                               tr("No Limit"));
                        break;
 
                case SR_CONF_PATTERN_MODE:
@@ -99,7 +115,7 @@ Device::Device(shared_ptr<sigrok::Configurable> configurable) :
                case SR_CONF_CLOCK_EDGE:
                case SR_CONF_DATA_SOURCE:
                case SR_CONF_EXTERNAL_CLOCK_SOURCE:
-                       bind_enum(name, "", key, capabilities, get, set);
+                       bind_enum(descr, "", key, capabilities, get, set);
                        break;
 
                case SR_CONF_FILTER:
@@ -107,33 +123,34 @@ Device::Device(shared_ptr<sigrok::Configurable> configurable) :
                case SR_CONF_RLE:
                case SR_CONF_POWER_OFF:
                case SR_CONF_AVERAGING:
-                       bind_bool(name, "", get, set);
+               case SR_CONF_CONTINUOUS:
+                       bind_bool(descr, "", get, set);
                        break;
 
                case SR_CONF_TIMEBASE:
-                       bind_enum(name, "", key, capabilities, get, set, print_timebase);
+                       bind_enum(descr, "", key, capabilities, get, set, print_timebase);
                        break;
 
                case SR_CONF_VDIV:
-                       bind_enum(name, "", key, capabilities, get, set, print_vdiv);
+                       bind_enum(descr, "", key, capabilities, get, set, print_vdiv);
                        break;
 
                case SR_CONF_VOLTAGE_THRESHOLD:
-                       bind_enum(name, "", key, capabilities, get, set, print_voltage_threshold);
+                       bind_enum(descr, "", key, capabilities, get, set, print_voltage_threshold);
                        break;
 
                case SR_CONF_PROBE_FACTOR:
                        if (capabilities.count(Capability::LIST))
-                               bind_enum(name, "", key, capabilities, get, set, print_probe_factor);
+                               bind_enum(descr, "", key, capabilities, get, set, print_probe_factor);
                        else
-                               bind_int(name, "", "", pair<int64_t, int64_t>(1, 500), get, set);
+                               bind_int(descr, "", "", pair<int64_t, int64_t>(1, 500), get, set);
                        break;
 
                case SR_CONF_AVG_SAMPLES:
                        if (capabilities.count(Capability::LIST))
-                               bind_enum(name, "", key, capabilities, get, set, print_averages);
+                               bind_enum(descr, "", key, capabilities, get, set, print_averages);
                        else
-                               bind_int(name, "", "", pair<int64_t, int64_t>(0, INT32_MAX), get, set);
+                               bind_int(descr, "", "", pair<int64_t, int64_t>(0, INT32_MAX), get, set);
                        break;
 
                default:
@@ -178,13 +195,13 @@ void Device::bind_enum(const QString &name, const QString &desc,
 }
 
 void Device::bind_int(const QString &name, const QString &desc, QString suffix,
-       optional< pair<int64_t, int64_t> > range,
-       Property::Getter getter, Property::Setter setter)
+       optional< pair<int64_t, int64_t> > range, Property::Getter getter,
+       Property::Setter setter, QString special_value_text)
 {
        assert(configurable_);
 
        properties_.push_back(shared_ptr<Property>(new Int(name, desc, suffix,
-               range, getter, setter)));
+               range, getter, setter, special_value_text)));
 }
 
 QString Device::print_timebase(Glib::VariantBase gvar)
index 9f5daf574a250beef9e3b5030e75599f88766fc1..30d89ddd493a106e2c423844661465dbc268df06 100644 (file)
@@ -62,7 +62,8 @@ private:
                function<QString (Glib::VariantBase)> printer = print_gvariant);
        void bind_int(const QString &name, const QString &desc, QString suffix,
                boost::optional< pair<int64_t, int64_t> > range,
-               prop::Property::Getter getter, prop::Property::Setter setter);
+               prop::Property::Getter getter, prop::Property::Setter setter,
+               QString special_value_text = "");
 
        static QString print_timebase(Glib::VariantBase gvar);
        static QString print_vdiv(Glib::VariantBase gvar);
index f9a061c76e78c7b7e77a33ec1d04250328263b70..9ba387fbc0cb01a01854010463848ae0dbf3c3ce 100644 (file)
@@ -59,7 +59,7 @@ namespace binding {
 InputOutput::InputOutput(
        const map<string, shared_ptr<Option>> &options)
 {
-       for (const pair<string, shared_ptr<Option>>& o : options) {
+       for (const pair<const string, shared_ptr<Option>>& o : options) {
                const shared_ptr<Option> &opt = o.second;
                assert(opt);
 
index acfb61cb6b401ceaaa4b839088cb0b36803b6387..c77313aa605a6f6bb9749cf8eb5caf6266520082 100644 (file)
@@ -81,4 +81,4 @@ private:
 }  // namespace binding
 }  // namespace pv
 
-#endif // PULSEVIEW_PV_BINDING_INPUTOUTPUT_H
+#endif // PULSEVIEW_PV_BINDING_INPUTOUTPUT_HPP
index f8fe473d3681ebc31f0850d052af2d952d8fbae4..da9736b53fe29210d808fbb54a86c06c64889833 100644 (file)
@@ -31,13 +31,19 @@ namespace pv {
 namespace data {
 
 Analog::Analog() :
-       SignalData()
+       SignalData(),
+       samplerate_(1)  // Default is 1 Hz to prevent division-by-zero errors
 {
 }
 
 void Analog::push_segment(shared_ptr<AnalogSegment> &segment)
 {
        segments_.push_back(segment);
+
+       if ((samplerate_ == 1) && (segment->samplerate() > 1))
+               samplerate_ = segment->samplerate();
+
+       connect(segment.get(), SIGNAL(completed()), this, SLOT(on_segment_completed()));
 }
 
 const deque< shared_ptr<AnalogSegment> >& Analog::analog_segments() const
@@ -58,17 +64,21 @@ uint32_t Analog::get_segment_count() const
 
 void Analog::clear()
 {
-       segments_.clear();
+       if (!segments_.empty()) {
+               segments_.clear();
 
-       samples_cleared();
+               samples_cleared();
+       }
 }
 
-double Analog::get_samplerate() const
+void Analog::set_samplerate(double value)
 {
-       if (segments_.empty())
-               return 1.0;
+       samplerate_ = value;
+}
 
-       return segments_.front()->samplerate();
+double Analog::get_samplerate() const
+{
+       return samplerate_;
 }
 
 uint64_t Analog::max_sample_count() const
@@ -81,7 +91,7 @@ uint64_t Analog::max_sample_count() const
        return l;
 }
 
-void Analog::notify_samples_added(QObject* segment, uint64_t start_sample,
+void Analog::notify_samples_added(shared_ptr<Segment> segment, uint64_t start_sample,
        uint64_t end_sample)
 {
        samples_added(segment, start_sample, end_sample);
@@ -92,5 +102,10 @@ void Analog::notify_min_max_changed(float min, float max)
        min_max_changed(min, max);
 }
 
+void Analog::on_segment_completed()
+{
+       segment_completed();
+}
+
 } // namespace data
 } // namespace pv
index c0b9a0134d72d9b5028f75b5d19040a71d81c991..560732f8bef8bebf238e948b37249ccd8d4f5141 100644 (file)
@@ -26,6 +26,7 @@
 #include <memory>
 
 #include <QObject>
+#include "pv/data/segment.hpp"
 
 using std::deque;
 using std::shared_ptr;
@@ -53,11 +54,13 @@ public:
 
        void clear();
 
+       void set_samplerate(double value);
+
        double get_samplerate() const;
 
        uint64_t max_sample_count() const;
 
-       void notify_samples_added(QObject* segment, uint64_t start_sample,
+       void notify_samples_added(shared_ptr<Segment> segment, uint64_t start_sample,
                uint64_t end_sample);
 
        void notify_min_max_changed(float min, float max);
@@ -65,12 +68,16 @@ public:
 Q_SIGNALS:
        void samples_cleared();
 
-       void samples_added(QObject* segment, uint64_t start_sample,
+       void samples_added(SharedPtrToSegment segment, uint64_t start_sample,
                uint64_t end_sample);
 
        void min_max_changed(float min, float max);
 
+private Q_SLOTS:
+       void on_segment_completed();
+
 private:
+       double samplerate_;
        deque< shared_ptr<AnalogSegment> > segments_;
 };
 
index 342612aae02e15954671d2044f3bdb337682cd09..579013097a713d3792da81aea0bebf09d45fb95e 100644 (file)
@@ -89,11 +89,21 @@ void AnalogSegment::append_interleaved_samples(const float *data,
        append_payload_to_envelope_levels();
 
        if (sample_count > 1)
-               owner_.notify_samples_added(this, prev_sample_count + 1,
-                       prev_sample_count + 1 + sample_count);
+               owner_.notify_samples_added(shared_ptr<Segment>(shared_from_this()),
+                       prev_sample_count + 1, prev_sample_count + 1 + sample_count);
        else
-               owner_.notify_samples_added(this, prev_sample_count + 1,
-                       prev_sample_count + 1);
+               owner_.notify_samples_added(shared_ptr<Segment>(shared_from_this()),
+                       prev_sample_count + 1, prev_sample_count + 1);
+}
+
+float AnalogSegment::get_sample(int64_t sample_num) const
+{
+       assert(sample_num >= 0);
+       assert(sample_num <= (int64_t)sample_count_);
+
+       lock_guard<recursive_mutex> lock(mutex_);  // Because of free_unused_memory()
+
+       return *((const float*)get_raw_sample(sample_num));
 }
 
 void AnalogSegment::get_samples(int64_t start_sample, int64_t end_sample,
index df25f0b74a669109663a08e0df4dbeff3b5a7641..633170439b8b0b0cc40372b1c538e4ec38b7501e 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <QObject>
 
+using std::enable_shared_from_this;
 using std::pair;
 
 namespace AnalogSegmentTest {
@@ -38,7 +39,7 @@ namespace data {
 
 class Analog;
 
-class AnalogSegment : public Segment
+class AnalogSegment : public Segment, public enable_shared_from_this<Segment>
 {
        Q_OBJECT
 
@@ -80,6 +81,7 @@ public:
        void append_interleaved_samples(const float *data,
                size_t sample_count, size_t stride);
 
+       float get_sample(int64_t sample_num) const;
        void get_samples(int64_t start_sample, int64_t end_sample, float* dest) const;
 
        const pair<float, float> get_min_max() const;
index e32dcc9fdc7f0b529dde1b90666b21abbe64e69e..f8a5c47b66236a8be66327a2c9ea05568970749f 100644 (file)
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-extern "C" {
 #include <libsigrokdecode/libsigrokdecode.h>
-}
 
 #include <cassert>
 #include <vector>
 
 #include <pv/data/decode/annotation.hpp>
+#include <pv/data/decode/decoder.hpp>
+#include "pv/data/decode/rowdata.hpp"
 
 using std::vector;
 
@@ -32,61 +32,46 @@ namespace pv {
 namespace data {
 namespace decode {
 
-Annotation::Annotation(const srd_proto_data *const pdata, const Row *row) :
-       start_sample_(pdata->start_sample),
-       end_sample_(pdata->end_sample),
-       row_(row)
+Annotation::Annotation(uint64_t start_sample, uint64_t end_sample,
+       const vector<QString>* texts, uint32_t ann_class_id, const RowData *data) :
+       start_sample_(start_sample),
+       end_sample_(end_sample),
+       texts_(texts),
+       ann_class_id_(ann_class_id),
+       data_(data)
 {
-       assert(pdata);
-       const srd_proto_data_annotation *const pda =
-               (const srd_proto_data_annotation*)pdata->data;
-       assert(pda);
-
-       ann_class_id_ = (Class)(pda->ann_class);
-
-       annotations_ = new vector<QString>();
-
-       const char *const *annotations = (char**)pda->ann_text;
-       while (*annotations) {
-               annotations_->push_back(QString::fromUtf8(*annotations));
-               annotations++;
-       }
-
-       annotations_->shrink_to_fit();
 }
 
 Annotation::Annotation(Annotation&& a) :
        start_sample_(a.start_sample_),
        end_sample_(a.end_sample_),
-       annotations_(a.annotations_),
-       row_(a.row_),
-       ann_class_id_(a.ann_class_id_)
+       texts_(a.texts_),
+       ann_class_id_(a.ann_class_id_),
+       data_(a.data_)
 {
-       a.annotations_ = nullptr;
 }
 
 Annotation& Annotation::operator=(Annotation&& a)
 {
        if (&a != this) {
-               if (annotations_)
-                       delete annotations_;
-
                start_sample_ = a.start_sample_;
                end_sample_ = a.end_sample_;
-               annotations_ = a.annotations_;
-               row_ = a.row_;
+               texts_ = a.texts_;
                ann_class_id_ = a.ann_class_id_;
-
-               a.annotations_ = nullptr;
+               data_ = a.data_;
        }
 
        return *this;
 }
 
-Annotation::~Annotation()
+const RowData* Annotation::row_data() const
 {
-       if (annotations_)
-               delete annotations_;
+       return data_;
+}
+
+const Row* Annotation::row() const
+{
+       return data_->row();
 }
 
 uint64_t Annotation::start_sample() const
@@ -99,19 +84,63 @@ uint64_t Annotation::end_sample() const
        return end_sample_;
 }
 
-Annotation::Class Annotation::ann_class_id() const
+uint64_t Annotation::length() const
+{
+       return end_sample_ - start_sample_;
+}
+
+uint32_t Annotation::ann_class_id() const
 {
        return ann_class_id_;
 }
 
+const QString Annotation::ann_class_name() const
+{
+       const AnnotationClass* ann_class =
+               data_->row()->decoder()->get_ann_class_by_id(ann_class_id_);
+
+       return QString(ann_class->name);
+}
+
+const QString Annotation::ann_class_description() const
+{
+       const AnnotationClass* ann_class =
+               data_->row()->decoder()->get_ann_class_by_id(ann_class_id_);
+
+       return QString(ann_class->description);
+}
+
 const vector<QString>* Annotation::annotations() const
 {
-       return annotations_;
+       return texts_;
 }
 
-const Row* Annotation::row() const
+const QString Annotation::longest_annotation() const
+{
+       return texts_->front();
+}
+
+bool Annotation::visible() const
+{
+       const Row* row = data_->row();
+
+       return (row->visible() && row->class_is_visible(ann_class_id_)
+               && row->decoder()->visible());
+}
+
+const QColor Annotation::color() const
+{
+       return data_->row()->get_class_color(ann_class_id_);
+}
+
+const QColor Annotation::bright_color() const
+{
+       return data_->row()->get_bright_class_color(ann_class_id_);
+}
+
+const QColor Annotation::dark_color() const
 {
-       return row_;
+       return data_->row()->get_dark_class_color(ann_class_id_);
 }
 
 bool Annotation::operator<(const Annotation &other) const
index 1bb9ad34c046a215321d6e276b3ddea6bb453106..aab5119c4b94596071cc01a4c7ddeb4bf439c580 100644 (file)
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEW_DECODE_ANNOTATION_HPP
-#define PULSEVIEW_PV_VIEW_DECODE_ANNOTATION_HPP
+#ifndef PULSEVIEW_PV_DATA_DECODE_ANNOTATION_HPP
+#define PULSEVIEW_PV_DATA_DECODE_ANNOTATION_HPP
 
 #include <cstdint>
 #include <vector>
 
+#include <QColor>
 #include <QString>
 
+#include "pv/data/decode/row.hpp"
+
 using std::vector;
 
 struct srd_proto_data;
@@ -33,37 +36,48 @@ namespace pv {
 namespace data {
 namespace decode {
 
-class Row;
+class RowData;
 
 class Annotation
 {
 public:
-       typedef uint32_t Class;
-
-public:
-       Annotation(const srd_proto_data *const pdata, const Row *row);
+       Annotation(uint64_t start_sample, uint64_t end_sample,
+               const vector<QString>* texts, uint32_t ann_class_id, const RowData *data);
        Annotation(Annotation&& a);
        Annotation& operator=(Annotation&& a);
-       ~Annotation();
+
+       const RowData* row_data() const;
+       const Row* row() const;
 
        uint64_t start_sample() const;
        uint64_t end_sample() const;
-       Class ann_class_id() const;
+       uint64_t length() const;
+
+       uint32_t ann_class_id() const;
+       const QString ann_class_name() const;
+       const QString ann_class_description() const;
+
        const vector<QString>* annotations() const;
-       const Row* row() const;
+       const QString longest_annotation() const;
+
+       bool visible() const;
+
+       const QColor color() const;
+       const QColor bright_color() const;
+       const QColor dark_color() const;
 
        bool operator<(const Annotation &other) const;
 
 private:
        uint64_t start_sample_;
        uint64_t end_sample_;
-       vector<QString>* annotations_;
-       const Row *row_;
-       Class ann_class_id_;
+       const vector<QString>* texts_;
+       uint32_t ann_class_id_;
+       const RowData* data_;
 };
 
 } // namespace decode
 } // namespace data
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEW_DECODE_ANNOTATION_HPP
+#endif // PULSEVIEW_PV_DATA_DECODE_ANNOTATION_HPP
index 0f12fec8bd753024c53c859f4a44427bfb7a5f81..ebcbc1c9bfa45a2c25e297becd0e243910f90596 100644 (file)
@@ -36,8 +36,31 @@ namespace pv {
 namespace data {
 namespace decode {
 
-Decoder::Decoder(const srd_decoder *const dec) :
+AnnotationClass::AnnotationClass(size_t _id, char* _name, char* _description, Row* _row) :
+       id(_id),
+       name(_name),
+       description(_description),
+       row(_row),
+       visible_(true)
+{
+}
+
+bool AnnotationClass::visible() const
+{
+       return visible_;
+}
+
+void AnnotationClass::set_visible(bool visible)
+{
+       visible_ = visible;
+
+       visibility_changed();
+}
+
+
+Decoder::Decoder(const srd_decoder *const dec, uint8_t stack_level) :
        srd_decoder_(dec),
+       stack_level_(stack_level),
        visible_(true),
        decoder_inst_(nullptr)
 {
@@ -47,7 +70,10 @@ Decoder::Decoder(const srd_decoder *const dec) :
                char **ann_class = (char**)l->data;
                char *name = ann_class[0];
                char *desc = ann_class[1];
-               ann_classes_.push_back({i++, name, desc, nullptr, true}); // Visible by default
+               ann_classes_.emplace_back(i++, name, desc, nullptr);
+
+               connect(&(ann_classes_.back()), SIGNAL(visibility_changed()),
+                       this, SLOT(on_class_visibility_changed()));
        }
 
        // Query the binary output classes
@@ -63,7 +89,6 @@ Decoder::Decoder(const srd_decoder *const dec) :
        uint32_t row_count = 0;
        for (const GSList *rl = srd_decoder_->annotation_rows; rl; rl = rl->next)
                row_count++;
-       rows_.reserve(row_count);
 
        i = 0;
        for (const GSList *rl = srd_decoder_->annotation_rows; rl; rl = rl->next) {
@@ -74,6 +99,9 @@ Decoder::Decoder(const srd_decoder *const dec) :
                // FIXME PV can crash from .at() if a PD's ann classes are defined incorrectly
                for (const GSList *cl = srd_row->ann_classes; cl; cl = cl->next)
                        ann_classes_.at((size_t)cl->data).row = &(rows_.back());
+
+               connect(&(rows_.back()), SIGNAL(visibility_changed()),
+                       this, SLOT(on_row_visibility_changed()));
        }
 
        if (rows_.empty()) {
@@ -96,6 +124,11 @@ const srd_decoder* Decoder::get_srd_decoder() const
        return srd_decoder_;
 }
 
+uint8_t Decoder::get_stack_level() const
+{
+       return stack_level_;
+}
+
 const char* Decoder::name() const
 {
        return srd_decoder_->name;
@@ -109,6 +142,8 @@ bool Decoder::visible() const
 void Decoder::set_visible(bool visible)
 {
        visible_ = visible;
+
+       annotation_visibility_changed();
 }
 
 const vector<DecodeChannel*>& Decoder::channels() const
@@ -264,6 +299,14 @@ AnnotationClass* Decoder::get_ann_class_by_id(size_t id)
        return &(ann_classes_[id]);
 }
 
+const AnnotationClass* Decoder::get_ann_class_by_id(size_t id) const
+{
+       if (id >= ann_classes_.size())
+               return nullptr;
+
+       return &(ann_classes_[id]);
+}
+
 uint32_t Decoder::get_binary_class_count() const
 {
        return bin_classes_.size();
@@ -274,6 +317,36 @@ const DecodeBinaryClassInfo* Decoder::get_binary_class(uint32_t id) const
        return &(bin_classes_.at(id));
 }
 
+void Decoder::on_row_visibility_changed()
+{
+       annotation_visibility_changed();
+}
+
+void Decoder::on_class_visibility_changed()
+{
+       annotation_visibility_changed();
+}
+
+bool Decoder::has_logic_output() const
+{
+       return (srd_decoder_->logic_output_channels != nullptr);
+}
+
+const vector<DecoderLogicOutputChannel> Decoder::logic_output_channels() const
+{
+       vector<DecoderLogicOutputChannel> result;
+
+       for (GSList *l = srd_decoder_->logic_output_channels; l; l = l->next) {
+               const srd_decoder_logic_output_channel* ch_data =
+                       (srd_decoder_logic_output_channel*)l->data;
+
+               result.emplace_back(QString::fromUtf8(ch_data->id),
+                       QString::fromUtf8(ch_data->desc));
+       }
+
+       return result;
+}
+
 }  // namespace decode
 }  // namespace data
 }  // namespace pv
index 545992f8a24a379b2381c93739432656aa45bcc7..ac1052e62c9dfcb1cc270455686bfc0b619e003f 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef PULSEVIEW_PV_DATA_DECODE_DECODER_HPP
 #define PULSEVIEW_PV_DATA_DECODE_DECODER_HPP
 
+#include <deque>
 #include <map>
 #include <memory>
 #include <set>
 
 #include <glib.h>
 
-#include <pv/data/signalbase.hpp>
+#include <QObject>
+
 #include <pv/data/decode/row.hpp>
 
+using std::deque;
 using std::map;
+using std::shared_ptr;
 using std::string;
 using std::vector;
 
@@ -50,13 +54,27 @@ namespace decode {
 
 class Decoder;
 
-struct AnnotationClass
+class AnnotationClass: public QObject
 {
+       Q_OBJECT
+
+public:
+       AnnotationClass(size_t _id, char* _name, char* _description, Row* _row);
+
+       bool visible() const;
+       void set_visible(bool visible);
+
+Q_SIGNALS:
+       void visibility_changed();
+
+public:
        size_t id;
        char* name;
        char* description;
        Row* row;
-       bool visible;
+
+private:
+       bool visible_;
 };
 
 struct DecodeChannel
@@ -64,13 +82,19 @@ struct DecodeChannel
        uint16_t id;     ///< Global numerical ID for the decode channels in the stack
        uint16_t bit_id; ///< Tells which bit within a sample represents this channel
        const bool is_optional;
-       const pv::data::SignalBase *assigned_signal;
+       shared_ptr<const pv::data::SignalBase> assigned_signal;
        const QString name, desc;
        int initial_pin_state;
        const shared_ptr<Decoder> decoder_;
        const srd_channel *pdch_;
 };
 
+struct DecoderLogicOutputChannel {
+       DecoderLogicOutputChannel (QString id, QString desc) :
+               id(id), desc(desc) {};
+       QString id, desc;
+};
+
 struct DecodeBinaryClassInfo
 {
        uint32_t bin_class_id;
@@ -79,15 +103,19 @@ struct DecodeBinaryClassInfo
 };
 
 
-class Decoder
+class Decoder : public QObject
 {
+       Q_OBJECT
+
 public:
-       Decoder(const srd_decoder *const dec);
+       Decoder(const srd_decoder *const dec, uint8_t stack_level);
 
        virtual ~Decoder();
 
        const srd_decoder* get_srd_decoder() const;
 
+       uint8_t get_stack_level() const;
+
        const char* name() const;
 
        bool visible() const;
@@ -97,7 +125,6 @@ public:
        void set_channels(vector<DecodeChannel*> channels);
 
        const map<string, GVariant*>& options() const;
-
        void set_option(const char *id, GVariant *value);
 
        void apply_all_options();
@@ -113,18 +140,30 @@ public:
        vector<const AnnotationClass*> ann_classes() const;
        vector<AnnotationClass*> ann_classes();
        AnnotationClass* get_ann_class_by_id(size_t id);
+       const AnnotationClass* get_ann_class_by_id(size_t id) const;
 
        uint32_t get_binary_class_count() const;
        const DecodeBinaryClassInfo* get_binary_class(uint32_t id) const;
 
+       bool has_logic_output() const;
+       const vector<DecoderLogicOutputChannel> logic_output_channels() const;
+
+Q_SIGNALS:
+       void annotation_visibility_changed();
+
+private Q_SLOTS:
+       void on_row_visibility_changed();
+       void on_class_visibility_changed();
+
 private:
        const srd_decoder* const srd_decoder_;
+       uint8_t stack_level_;
 
        bool visible_;
 
        vector<DecodeChannel*> channels_;
-       vector<Row> rows_;
-       vector<AnnotationClass> ann_classes_;
+       deque<Row> rows_;
+       deque<AnnotationClass> ann_classes_;
        vector<DecodeBinaryClassInfo> bin_classes_;
        map<string, GVariant*> options_;
        srd_decoder_inst *decoder_inst_;
index f1895ae4ef7c71bb100c46d645fc356cc4d78b57..6212c92195bb888838348122f14624d0e83f2edc 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <cassert>
+#include <QDebug>
 
 #include "decoder.hpp"
 #include "row.hpp"
@@ -110,17 +111,86 @@ bool Row::visible() const
 void Row::set_visible(bool visible)
 {
        visible_ = visible;
+
+       visibility_changed();
+}
+
+void Row::set_base_color(QColor base_color)
+{
+       // For the row color, use the base color hue and add an offset that's
+       // not a dividend of 360
+
+       const int h = (base_color.toHsv().hue() + 20 * index_) % 360;
+       const int s = DECODE_COLOR_SATURATION;
+       const int v = DECODE_COLOR_VALUE;
+       color_.setHsl(h, s, v);
+
+       vector<AnnotationClass*> classes = ann_classes();
+       for (const AnnotationClass* ann_class : classes) {
+
+               // For each class color, use the row color hue and add an offset that's
+               // not a dividend of 360 and not a multiple of the row offset
+
+               QColor ann_color(color_);
+               const int h = (ann_color.toHsv().hue() + 55 * ann_class->id) % 360;
+               const int s = DECODE_COLOR_SATURATION;
+               const int v = DECODE_COLOR_VALUE;
+               ann_color.setHsl(h, s, v);
+
+               ann_class_color_[ann_class->id] = ann_color;
+               ann_bright_class_color_[ann_class->id] = ann_color.lighter();
+               ann_dark_class_color_[ann_class->id] = ann_color.darker();
+       }
+}
+
+const QColor Row::color() const
+{
+       return color_;
+}
+
+const QColor Row::get_class_color(uint32_t ann_class_id) const
+{
+       try {
+               return ann_class_color_.at(ann_class_id);
+       } catch (std::out_of_range &e) {
+               qWarning() << "Warning: annotation type" << decoder_->get_ann_class_by_id(ann_class_id)->name
+                       << "(" << ann_class_id << ")" << "not assigned to any annotation row!";
+               return QColor(20, 20, 20);
+       }
+}
+
+const QColor Row::get_bright_class_color(uint32_t ann_class_id) const
+{
+       try {
+               return ann_bright_class_color_.at(ann_class_id);
+       } catch (std::out_of_range &e) {
+               return QColor(20, 20, 20);
+       }
+}
+
+const QColor Row::get_dark_class_color(uint32_t ann_class_id) const
+{
+       try {
+               return ann_dark_class_color_.at(ann_class_id);
+       } catch (std::out_of_range &e) {
+               return QColor(20, 20, 20);
+       }
 }
 
 bool Row::has_hidden_classes() const
 {
        for (const AnnotationClass* c : ann_classes())
-               if (!c->visible)
+               if (!c->visible())
                        return true;
 
        return false;
 }
 
+bool Row::class_is_visible(uint32_t ann_class_id) const
+{
+       return decoder_->get_ann_class_by_id(ann_class_id)->visible();
+}
+
 bool Row::operator<(const Row& other) const
 {
        return (decoder_ < other.decoder_) ||
index b877b58b85b4ca25300dad8d45e12042ad30a7a8..80aa114c1edb8dde8514c67f7ee67139a55c0d91 100644 (file)
 #ifndef PULSEVIEW_PV_DATA_DECODE_ROW_HPP
 #define PULSEVIEW_PV_DATA_DECODE_ROW_HPP
 
+#include <map>
 #include <vector>
 
-#include <pv/data/decode/annotation.hpp>
-#include <pv/data/decode/decoder.hpp>
+#include <QObject>
+#include <QColor>
 
 struct srd_decoder;
 struct srd_decoder_annotation_row;
 
+using std::map;
+using std::vector;
+
 namespace pv {
 namespace data {
 namespace decode {
 
-struct AnnotationClass;
+#define DECODE_COLOR_SATURATION (180) /* 0-255 */
+#define DECODE_COLOR_VALUE (170) /* 0-255 */
+
+class AnnotationClass;
 class Decoder;
 
-class Row
+class Row: public QObject
 {
+       Q_OBJECT
+
 public:
        Row();
 
@@ -54,16 +63,31 @@ public:
        bool visible() const;
        void set_visible(bool visible);
 
+       void set_base_color(QColor base_color);
+       const QColor color() const;
+       const QColor get_class_color(uint32_t ann_class_id) const;
+       const QColor get_bright_class_color(uint32_t ann_class_id) const;
+       const QColor get_dark_class_color(uint32_t ann_class_id) const;
+
        bool has_hidden_classes() const;
+       bool class_is_visible(uint32_t ann_class_id) const;
 
        bool operator<(const Row& other) const;
        bool operator==(const Row& other) const;
 
+Q_SIGNALS:
+       void visibility_changed();
+
 private:
        uint32_t index_;
        Decoder* decoder_;
        const srd_decoder_annotation_row* srd_row_;
        bool visible_;
+
+       QColor color_;
+       map<uint32_t, QColor> ann_class_color_;
+       map<uint32_t, QColor> ann_bright_class_color_;
+       map<uint32_t, QColor> ann_dark_class_color_;
 };
 
 }  // namespace decode
index 7b6ec2d3f8b71bb0728cd1381a2867bb072f55c5..3d250f49472eb03194d1001c6dd693c2ef644ae2 100644 (file)
@@ -36,6 +36,11 @@ RowData::RowData(Row* row) :
        assert(row);
 }
 
+const Row* RowData::row() const
+{
+       return row_;
+}
+
 uint64_t RowData::get_max_sample() const
 {
        if (annotations_.empty())
@@ -58,7 +63,7 @@ void RowData::get_annotation_subset(
 
        uint32_t max_ann_class_id = 0;
        for (AnnotationClass* c : row_->ann_classes()) {
-               if (!c->visible)
+               if (!c->visible())
                        all_ann_classes_enabled = false;
                else
                        all_ann_classes_disabled = false;
@@ -78,7 +83,7 @@ void RowData::get_annotation_subset(
                        vector<size_t> class_visible;
                        class_visible.resize(max_ann_class_id + 1, 0);
                        for (AnnotationClass* c : row_->ann_classes())
-                               if (c->visible)
+                               if (c->visible())
                                        class_visible[c->id] = 1;
 
                        for (const auto& annotation : annotations_)
@@ -90,8 +95,36 @@ void RowData::get_annotation_subset(
        }
 }
 
-void RowData::emplace_annotation(srd_proto_data *pdata)
+const deque<Annotation>& RowData::annotations() const
+{
+       return annotations_;
+}
+
+const Annotation* RowData::emplace_annotation(srd_proto_data *pdata)
 {
+       const srd_proto_data_annotation *const pda = (const srd_proto_data_annotation*)pdata->data;
+
+       uint32_t ann_class_id = pda->ann_class;
+
+       // Look up the longest annotation text to see if we have it in storage.
+       // This implies that if the longest text is the same, the shorter texts
+       // are expected to be the same, too. PDs that violate this assumption
+       // should be considered broken.
+       const char* const* ann_texts = (char**)pda->ann_text;
+       const QString ann0 = QString::fromUtf8(ann_texts[0]);
+       vector<QString>* storage_entry = &(ann_texts_[ann0]);
+
+       if (storage_entry->empty()) {
+               while (*ann_texts) {
+                       storage_entry->emplace_back(QString::fromUtf8(*ann_texts));
+                       ann_texts++;
+               }
+               storage_entry->shrink_to_fit();
+       }
+
+
+       const Annotation* result = nullptr;
+
        // We insert the annotation in a way so that the annotation list
        // is sorted by start sample. Otherwise, we'd have to sort when
        // painting, which is expensive
@@ -108,11 +141,17 @@ void RowData::emplace_annotation(srd_proto_data *pdata)
                if (it != annotations_.begin())
                        it++;
 
-               annotations_.emplace(it, pdata, row_);
+               it = annotations_.emplace(it, pdata->start_sample, pdata->end_sample,
+                       storage_entry, ann_class_id, this);
+               result = &(*it);
        } else {
-               annotations_.emplace_back(pdata, row_);
+               annotations_.emplace_back(pdata->start_sample, pdata->end_sample,
+                       storage_entry, ann_class_id, this);
+               result = &(annotations_.back());
                prev_ann_start_sample_ = pdata->start_sample;
        }
+
+       return result;
 }
 
 }  // namespace decode
index 01ea94f4586c7390601c28ea61e68c76a5ca960d..ab109001bd96d72441f5f0bff45a150e9266d861 100644 (file)
 #ifndef PULSEVIEW_PV_DATA_DECODE_ROWDATA_HPP
 #define PULSEVIEW_PV_DATA_DECODE_ROWDATA_HPP
 
+#include <unordered_map>
 #include <vector>
 
+#include <QtGlobal>
+#include <QHash>
+#include <QString>
+
 #include <libsigrokdecode/libsigrokdecode.h>
 
 #include <pv/data/decode/annotation.hpp>
 
 using std::deque;
-using std::vector;
+using std::unordered_map;
+
+#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
+namespace std {
+       template<> struct hash<QString> {
+               std::size_t operator()(const QString& s) const noexcept {
+                       return (size_t) qHash(s);
+               }
+       };
+}
+#endif
 
 namespace pv {
 namespace data {
@@ -40,6 +55,8 @@ class RowData
 public:
        RowData(Row* row);
 
+       const Row* row() const;
+
        uint64_t get_max_sample() const;
 
        uint64_t get_annotation_count() const;
@@ -52,10 +69,13 @@ public:
        void get_annotation_subset(deque<const pv::data::decode::Annotation*> &dest,
                uint64_t start_sample, uint64_t end_sample) const;
 
-       void emplace_annotation(srd_proto_data *pdata);
+       const deque<Annotation>& annotations() const;
+
+       const Annotation* emplace_annotation(srd_proto_data *pdata);
 
 private:
        deque<Annotation> annotations_;
+       unordered_map<QString, vector<QString> > ann_texts_;  // unordered_map since pointers must not change
        Row* row_;
        uint64_t prev_ann_start_sample_;
 };
index 3420fa1c7efbed0c899de170fc4d191118c7ebdf..9c695a1baa8247dfa09be12c8cdd9b2ee5956d34 100644 (file)
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "config.h"
+
 #include <cstring>
 #include <forward_list>
 #include <limits>
 
 #include <QDebug>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#include <QRegularExpression>
+#endif
 
 #include "logic.hpp"
 #include "logicsegment.hpp"
@@ -33,7 +38,7 @@
 #include <pv/globalsettings.hpp>
 #include <pv/session.hpp>
 
-using std::forward_list;
+using std::dynamic_pointer_cast;
 using std::lock_guard;
 using std::make_shared;
 using std::min;
@@ -68,6 +73,20 @@ DecodeSignal::~DecodeSignal()
        reset_decode(true);
 }
 
+void DecodeSignal::set_name(QString name)
+{
+       SignalBase::set_name(name);
+
+       update_output_signals();
+}
+
+void DecodeSignal::set_color(QColor color)
+{
+       SignalBase::set_color(color);
+
+       update_output_signals();
+}
+
 const vector< shared_ptr<Decoder> >& DecodeSignal::decoder_stack() const
 {
        return stack_;
@@ -84,15 +103,19 @@ void DecodeSignal::stack_decoder(const srd_decoder *decoder, bool restart_decode
        if ((stack_.empty()) || ((stack_.size() > 0) && (name() == prev_dec_name)))
                set_name(QString::fromUtf8(decoder->name));
 
-       const shared_ptr<Decoder> dec = make_shared<Decoder>(decoder);
+       const shared_ptr<Decoder> dec = make_shared<Decoder>(decoder, stack_.size());
        stack_.push_back(dec);
 
+       connect(dec.get(), SIGNAL(annotation_visibility_changed()),
+               this, SLOT(on_annotation_visibility_changed()));
+
        // Include the newly created decode channels in the channel lists
        update_channel_list();
 
        stack_config_changed_ = true;
        auto_assign_signals(dec);
        commit_decoder_channels();
+       update_output_signals();
 
        decoder_stacked((void*)dec.get());
 
@@ -106,11 +129,12 @@ void DecodeSignal::remove_decoder(int index)
        assert(index < (int)stack_.size());
 
        // Find the decoder in the stack
-       auto iter = stack_.begin();
-       for (int i = 0; i < index; i++, iter++)
-               assert(iter != stack_.end());
+       auto iter = stack_.begin() + index;
+       assert(iter != stack_.end());
 
-       decoder_removed(iter->get());
+       shared_ptr<Decoder> dec = *iter;
+
+       decoder_removed(dec.get());
 
        // Delete the element
        stack_.erase(iter);
@@ -141,6 +165,8 @@ bool DecodeSignal::toggle_decoder_visibility(int index)
 
 void DecodeSignal::reset_decode(bool shutting_down)
 {
+       resume_decode();  // Make sure the decode thread isn't blocked by pausing
+
        if (stack_config_changed_ || shutting_down)
                stop_srd_session();
        else
@@ -158,16 +184,18 @@ void DecodeSignal::reset_decode(bool shutting_down)
                logic_mux_thread_.join();
        }
 
-       resume_decode();  // Make sure the decode thread isn't blocked by pausing
-
        current_segment_id_ = 0;
        segments_.clear();
 
+       for (const shared_ptr<decode::Decoder>& dec : stack_)
+               if (dec->has_logic_output())
+                       output_logic_[dec->get_srd_decoder()]->clear();
+
        logic_mux_data_.reset();
        logic_mux_data_invalid_ = true;
 
        if (!error_message_.isEmpty()) {
-               error_message_ = QString();
+               error_message_.clear();
                // TODO Emulate noquote()
                qDebug().nospace() << name() << ": Error cleared";
        }
@@ -228,13 +256,8 @@ void DecodeSignal::begin_decode()
                logic_mux_data_ = make_shared<Logic>(ch_count);
        }
 
-       // Receive notifications when new sample data is available
-       connect_input_notifiers();
-
-       if (get_input_segment_count() == 0) {
+       if (get_input_segment_count() == 0)
                set_error_message(tr("No input data"));
-               return;
-       }
 
        // Make sure the logic output data is complete and up-to-date
        logic_mux_interrupt_ = false;
@@ -264,12 +287,6 @@ bool DecodeSignal::is_paused() const
        return decode_paused_;
 }
 
-QString DecodeSignal::error_message() const
-{
-       lock_guard<mutex> lock(output_mutex_);
-       return error_message_;
-}
-
 const vector<decode::DecodeChannel> DecodeSignal::get_channels() const
 {
        return channels_;
@@ -279,6 +296,9 @@ void DecodeSignal::auto_assign_signals(const shared_ptr<Decoder> dec)
 {
        bool new_assignment = false;
 
+       // Disconnect all input signal notifications so we don't have duplicate connections
+       disconnect_input_notifiers();
+
        // Try to auto-select channels that don't have signals assigned yet
        for (decode::DecodeChannel& ch : channels_) {
                // If a decoder is given, auto-assign only its channels
@@ -289,7 +309,11 @@ void DecodeSignal::auto_assign_signals(const shared_ptr<Decoder> dec)
                        continue;
 
                QString ch_name = ch.name.toLower();
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               ch_name = ch_name.replace(QRegularExpression("[-_.]"), " ");
+#else
                ch_name = ch_name.replace(QRegExp("[-_.]"), " ");
+#endif
 
                shared_ptr<data::SignalBase> match;
                for (const shared_ptr<data::SignalBase>& s : session_.signalbases()) {
@@ -297,7 +321,11 @@ void DecodeSignal::auto_assign_signals(const shared_ptr<Decoder> dec)
                                continue;
 
                        QString s_name = s->name().toLower();
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+                       s_name = s_name.replace(QRegularExpression("[-_.]"), " ");
+#else
                        s_name = s_name.replace(QRegExp("[-_.]"), " ");
+#endif
 
                        if (s->logic_data() &&
                                ((ch_name.contains(s_name)) || (s_name.contains(ch_name)))) {
@@ -313,13 +341,22 @@ void DecodeSignal::auto_assign_signals(const shared_ptr<Decoder> dec)
                        }
                }
 
-               if (match) {
-                       ch.assigned_signal = match.get();
+               // Prevent using a signal more than once as D1 would match e.g. D1 and D10
+               bool signal_not_already_used = true;
+               for (decode::DecodeChannel& ch : channels_)
+                       if (ch.assigned_signal && (ch.assigned_signal == match))
+                               signal_not_already_used = false;
+
+               if (match && signal_not_already_used) {
+                       ch.assigned_signal = match;
                        new_assignment = true;
                }
        }
 
        if (new_assignment) {
+               // Receive notifications when new sample data is available
+               connect_input_notifiers();
+
                logic_mux_data_invalid_ = true;
                stack_config_changed_ = true;
                commit_decoder_channels();
@@ -327,14 +364,20 @@ void DecodeSignal::auto_assign_signals(const shared_ptr<Decoder> dec)
        }
 }
 
-void DecodeSignal::assign_signal(const uint16_t channel_id, const SignalBase *signal)
+void DecodeSignal::assign_signal(const uint16_t channel_id, shared_ptr<const SignalBase> signal)
 {
+       // Disconnect all input signal notifications so we don't have duplicate connections
+       disconnect_input_notifiers();
+
        for (decode::DecodeChannel& ch : channels_)
                if (ch.id == channel_id) {
                        ch.assigned_signal = signal;
                        logic_mux_data_invalid_ = true;
                }
 
+       // Receive notifications when new sample data is available
+       connect_input_notifiers();
+
        stack_config_changed_ = true;
        commit_decoder_channels();
        channels_updated();
@@ -345,7 +388,63 @@ int DecodeSignal::get_assigned_signal_count() const
 {
        // Count all channels that have a signal assigned to them
        return count_if(channels_.begin(), channels_.end(),
-               [](decode::DecodeChannel ch) { return ch.assigned_signal; });
+               [](decode::DecodeChannel ch) { return ch.assigned_signal.get(); });
+}
+
+void DecodeSignal::update_output_signals()
+{
+       for (const shared_ptr<decode::Decoder>& dec : stack_) {
+               assert(dec);
+
+               if (dec->has_logic_output()) {
+                       const vector<decode::DecoderLogicOutputChannel> logic_channels =
+                               dec->logic_output_channels();
+
+                       // All signals of a decoder share the same LogicSegment, so it's
+                       // sufficient to check for only the first channel
+                       const decode::DecoderLogicOutputChannel& first_ch = logic_channels[0];
+
+                       bool ch_exists = false;
+                       for (const shared_ptr<SignalBase>& signal : output_signals_)
+                               if (signal->internal_name() == first_ch.id)
+                                       ch_exists = true;
+
+                       if (!ch_exists) {
+                               shared_ptr<Logic> logic_data = make_shared<Logic>(logic_channels.size());
+                               logic_data->set_samplerate(get_samplerate());
+                               output_logic_[dec->get_srd_decoder()] = logic_data;
+                               output_logic_muxed_data_[dec->get_srd_decoder()] = vector<uint8_t>();
+
+                               shared_ptr<LogicSegment> logic_segment = make_shared<data::LogicSegment>(
+                                       *logic_data, 0, (logic_data->num_channels() + 7) / 8, get_samplerate());
+                               logic_data->push_segment(logic_segment);
+
+                               uint index = 0;
+                               for (const decode::DecoderLogicOutputChannel& logic_ch : logic_channels) {
+                                       shared_ptr<data::SignalBase> signal =
+                                               make_shared<data::SignalBase>(nullptr, LogicChannel);
+                                       signal->set_internal_name(logic_ch.id);
+                                       signal->set_index(index);
+                                       signal->set_data(logic_data);
+                                       output_signals_.push_back(signal);
+                                       session_.add_generated_signal(signal);
+                                       index++;
+                               }
+                       } else {
+                               shared_ptr<Logic> logic_data = output_logic_[dec->get_srd_decoder()];
+                               logic_data->set_samplerate(get_samplerate());
+                               for (shared_ptr<LogicSegment>& segment : logic_data->logic_segments())
+                                       segment->set_samplerate(get_samplerate());
+                       }
+               }
+       }
+
+       for (shared_ptr<SignalBase> s : output_signals_) {
+               s->set_name(s->internal_name() + " (" + name() + ")");
+               s->set_color(color());
+       }
+
+       // TODO Assert that all sample rates are the same as the session's
 }
 
 void DecodeSignal::set_initial_pin_state(const uint16_t channel_id, const int init_state)
@@ -359,7 +458,7 @@ void DecodeSignal::set_initial_pin_state(const uint16_t channel_id, const int in
        begin_decode();
 }
 
-double DecodeSignal::samplerate() const
+double DecodeSignal::get_samplerate() const
 {
        double result = 0;
 
@@ -392,18 +491,21 @@ int64_t DecodeSignal::get_working_sample_count(uint32_t segment_id) const
 
        for (const decode::DecodeChannel& ch : channels_)
                if (ch.assigned_signal) {
+                       if (!ch.assigned_signal->logic_data())
+                               return 0;
+
                        no_signals_assigned = false;
 
                        const shared_ptr<Logic> logic_data = ch.assigned_signal->logic_data();
-                       if (!logic_data || logic_data->logic_segments().empty())
+                       if (logic_data->logic_segments().empty())
                                return 0;
 
-                       try {
-                               const shared_ptr<LogicSegment> segment = logic_data->logic_segments().at(segment_id);
-                               count = min(count, (int64_t)segment->get_sample_count());
-                       } catch (out_of_range&) {
+                       if (segment_id >= logic_data->logic_segments().size())
                                return 0;
-                       }
+
+                       const shared_ptr<const LogicSegment> segment = logic_data->logic_segments()[segment_id]->get_shared_ptr();
+                       if (segment)
+                               count = min(count, (int64_t)segment->get_sample_count());
                }
 
        return (no_signals_assigned ? 0 : count);
@@ -459,7 +561,6 @@ vector<const Row*> DecodeSignal::get_rows(bool visible_only) const
        return rows;
 }
 
-
 uint64_t DecodeSignal::get_annotation_count(const Row* row, uint32_t segment_id) const
 {
        if (segment_id >= segments_.size())
@@ -510,18 +611,14 @@ void DecodeSignal::get_annotation_subset(deque<const Annotation*> &dest,
 uint32_t DecodeSignal::get_binary_data_chunk_count(uint32_t segment_id,
        const Decoder* dec, uint32_t bin_class_id) const
 {
-       if (segments_.size() == 0)
+       if ((segments_.size() == 0) || (segment_id >= segments_.size()))
                return 0;
 
-       try {
-               const DecodeSegment *segment = &(segments_.at(segment_id));
+       const DecodeSegment *segment = &(segments_[segment_id]);
 
-               for (const DecodeBinaryClass& bc : segment->binary_classes)
-                       if ((bc.decoder == dec) && (bc.info->bin_class_id == bin_class_id))
-                               return bc.chunks.size();
-       } catch (out_of_range&) {
-               // Do nothing
-       }
+       for (const DecodeBinaryClass& bc : segment->binary_classes)
+               if ((bc.decoder == dec) && (bc.info->bin_class_id == bin_class_id))
+                       return bc.chunks.size();
 
        return 0;
 }
@@ -530,18 +627,17 @@ void DecodeSignal::get_binary_data_chunk(uint32_t segment_id,
        const  Decoder* dec, uint32_t bin_class_id, uint32_t chunk_id,
        const vector<uint8_t> **dest, uint64_t *size)
 {
-       try {
-               const DecodeSegment *segment = &(segments_.at(segment_id));
+       if (segment_id >= segments_.size())
+               return;
 
-               for (const DecodeBinaryClass& bc : segment->binary_classes)
-                       if ((bc.decoder == dec) && (bc.info->bin_class_id == bin_class_id)) {
-                               if (dest) *dest = &(bc.chunks.at(chunk_id).data);
-                               if (size) *size = bc.chunks.at(chunk_id).data.size();
-                               return;
-                       }
-       } catch (out_of_range&) {
-               // Do nothing
-       }
+       const DecodeSegment *segment = &(segments_[segment_id]);
+
+       for (const DecodeBinaryClass& bc : segment->binary_classes)
+               if ((bc.decoder == dec) && (bc.info->bin_class_id == bin_class_id)) {
+                       if (dest) *dest = &(bc.chunks.at(chunk_id).data);
+                       if (size) *size = bc.chunks.at(chunk_id).data.size();
+                       return;
+               }
 }
 
 void DecodeSignal::get_merged_binary_data_chunks_by_sample(uint32_t segment_id,
@@ -550,39 +646,38 @@ void DecodeSignal::get_merged_binary_data_chunks_by_sample(uint32_t segment_id,
 {
        assert(dest != nullptr);
 
-       try {
-               const DecodeSegment *segment = &(segments_.at(segment_id));
-
-               const DecodeBinaryClass* bin_class = nullptr;
-               for (const DecodeBinaryClass& bc : segment->binary_classes)
-                       if ((bc.decoder == dec) && (bc.info->bin_class_id == bin_class_id))
-                               bin_class = &bc;
-
-               // Determine overall size before copying to resize dest vector only once
-               uint64_t size = 0;
-               uint64_t matches = 0;
-               for (const DecodeBinaryDataChunk& chunk : bin_class->chunks)
-                       if ((chunk.sample >= start_sample) && (chunk.sample < end_sample)) {
-                               size += chunk.data.size();
-                               matches++;
-                       }
-               dest->resize(size);
-
-               uint64_t offset = 0;
-               uint64_t matches2 = 0;
-               for (const DecodeBinaryDataChunk& chunk : bin_class->chunks)
-                       if ((chunk.sample >= start_sample) && (chunk.sample < end_sample)) {
-                               memcpy(dest->data() + offset, chunk.data.data(), chunk.data.size());
-                               offset += chunk.data.size();
-                               matches2++;
-
-                               // Make sure we don't overwrite memory if the array grew in the meanwhile
-                               if (matches2 == matches)
-                                       break;
-                       }
-       } catch (out_of_range&) {
-               // Do nothing
-       }
+       if (segment_id >= segments_.size())
+               return;
+
+       const DecodeSegment *segment = &(segments_[segment_id]);
+
+       const DecodeBinaryClass* bin_class = nullptr;
+       for (const DecodeBinaryClass& bc : segment->binary_classes)
+               if ((bc.decoder == dec) && (bc.info->bin_class_id == bin_class_id))
+                       bin_class = &bc;
+
+       // Determine overall size before copying to resize dest vector only once
+       uint64_t size = 0;
+       uint64_t matches = 0;
+       for (const DecodeBinaryDataChunk& chunk : bin_class->chunks)
+               if ((chunk.sample >= start_sample) && (chunk.sample < end_sample)) {
+                       size += chunk.data.size();
+                       matches++;
+               }
+       dest->resize(size);
+
+       uint64_t offset = 0;
+       uint64_t matches2 = 0;
+       for (const DecodeBinaryDataChunk& chunk : bin_class->chunks)
+               if ((chunk.sample >= start_sample) && (chunk.sample < end_sample)) {
+                       memcpy(dest->data() + offset, chunk.data.data(), chunk.data.size());
+                       offset += chunk.data.size();
+                       matches2++;
+
+                       // Make sure we don't overwrite memory if the array grew in the meanwhile
+                       if (matches2 == matches)
+                               break;
+               }
 }
 
 void DecodeSignal::get_merged_binary_data_chunks_by_offset(uint32_t segment_id,
@@ -591,58 +686,67 @@ void DecodeSignal::get_merged_binary_data_chunks_by_offset(uint32_t segment_id,
 {
        assert(dest != nullptr);
 
-       try {
-               const DecodeSegment *segment = &(segments_.at(segment_id));
-
-               const DecodeBinaryClass* bin_class = nullptr;
-               for (const DecodeBinaryClass& bc : segment->binary_classes)
-                       if ((bc.decoder == dec) && (bc.info->bin_class_id == bin_class_id))
-                               bin_class = &bc;
-
-               // Determine overall size before copying to resize dest vector only once
-               uint64_t size = 0;
-               uint64_t offset = 0;
-               for (const DecodeBinaryDataChunk& chunk : bin_class->chunks) {
-                       if (offset >= start)
-                               size += chunk.data.size();
-                       offset += chunk.data.size();
-                       if (offset >= end)
-                               break;
-               }
-               dest->resize(size);
-
-               offset = 0;
-               uint64_t dest_offset = 0;
-               for (const DecodeBinaryDataChunk& chunk : bin_class->chunks) {
-                       if (offset >= start) {
-                               memcpy(dest->data() + dest_offset, chunk.data.data(), chunk.data.size());
-                               dest_offset += chunk.data.size();
-                       }
-                       offset += chunk.data.size();
-                       if (offset >= end)
-                               break;
+       if (segment_id >= segments_.size())
+               return;
+
+       const DecodeSegment *segment = &(segments_[segment_id]);
+
+       const DecodeBinaryClass* bin_class = nullptr;
+       for (const DecodeBinaryClass& bc : segment->binary_classes)
+               if ((bc.decoder == dec) && (bc.info->bin_class_id == bin_class_id))
+                       bin_class = &bc;
+
+       // Determine overall size before copying to resize dest vector only once
+       uint64_t size = 0;
+       uint64_t offset = 0;
+       for (const DecodeBinaryDataChunk& chunk : bin_class->chunks) {
+               if (offset >= start)
+                       size += chunk.data.size();
+               offset += chunk.data.size();
+               if (offset >= end)
+                       break;
+       }
+       dest->resize(size);
+
+       offset = 0;
+       uint64_t dest_offset = 0;
+       for (const DecodeBinaryDataChunk& chunk : bin_class->chunks) {
+               if (offset >= start) {
+                       memcpy(dest->data() + dest_offset, chunk.data.data(), chunk.data.size());
+                       dest_offset += chunk.data.size();
                }
-       } catch (out_of_range&) {
-               // Do nothing
+               offset += chunk.data.size();
+               if (offset >= end)
+                       break;
        }
 }
 
 const DecodeBinaryClass* DecodeSignal::get_binary_data_class(uint32_t segment_id,
        const Decoder* dec, uint32_t bin_class_id) const
 {
-       try {
-               const DecodeSegment *segment = &(segments_.at(segment_id));
+       if (segment_id >= segments_.size())
+               return nullptr;
 
-               for (const DecodeBinaryClass& bc : segment->binary_classes)
-                       if ((bc.decoder == dec) && (bc.info->bin_class_id == bin_class_id))
-                               return &bc;
-       } catch (out_of_range&) {
-               // Do nothing
-       }
+       const DecodeSegment *segment = &(segments_[segment_id]);
+
+       for (const DecodeBinaryClass& bc : segment->binary_classes)
+               if ((bc.decoder == dec) && (bc.info->bin_class_id == bin_class_id))
+                       return &bc;
 
        return nullptr;
 }
 
+const deque<const Annotation*>* DecodeSignal::get_all_annotations_by_segment(
+       uint32_t segment_id) const
+{
+       if (segment_id >= segments_.size())
+               return nullptr;
+
+       const DecodeSegment *segment = &(segments_[segment_id]);
+
+       return &(segment->all_annotations);
+}
+
 void DecodeSignal::save_settings(QSettings &settings) const
 {
        SignalBase::save_settings(settings);
@@ -654,7 +758,11 @@ void DecodeSignal::save_settings(QSettings &settings) const
        for (const shared_ptr<Decoder>& decoder : stack_) {
                settings.beginGroup("decoder" + QString::number(decoder_idx++));
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               settings.setValue("id", (const char *)decoder->get_srd_decoder()->id);
+#else
                settings.setValue("id", decoder->get_srd_decoder()->id);
+#endif
                settings.setValue("visible", decoder->visible());
 
                // Save decoder options
@@ -686,7 +794,7 @@ void DecodeSignal::save_settings(QSettings &settings) const
                i = 0;
                for (const AnnotationClass* ann_class : decoder->ann_classes()) {
                        settings.beginGroup("ann_class" + QString::number(i));
-                       settings.setValue("visible", ann_class->visible);
+                       settings.setValue("visible", ann_class->visible());
                        settings.endGroup();
                        i++;
                }
@@ -716,6 +824,8 @@ void DecodeSignal::save_settings(QSettings &settings) const
 
                settings.endGroup();
        }
+
+       // TODO Save logic output signal settings
 }
 
 void DecodeSignal::restore_settings(QSettings &settings)
@@ -738,7 +848,10 @@ void DecodeSignal::restore_settings(QSettings &settings)
                                continue;
 
                        if (QString::fromUtf8(dec->id) == id) {
-                               shared_ptr<Decoder> decoder = make_shared<Decoder>(dec);
+                               shared_ptr<Decoder> decoder = make_shared<Decoder>(dec, stack_.size());
+
+                               connect(decoder.get(), SIGNAL(annotation_visibility_changed()),
+                                       this, SLOT(on_annotation_visibility_changed()));
 
                                stack_.push_back(decoder);
                                decoder->set_visible(settings.value("visible", true).toBool());
@@ -770,7 +883,7 @@ void DecodeSignal::restore_settings(QSettings &settings)
                                i = 0;
                                for (AnnotationClass* ann_class : decoder->ann_classes()) {
                                        settings.beginGroup("ann_class" + QString::number(i));
-                                       ann_class->visible = settings.value("visible", true).toBool();
+                                       ann_class->set_visible(settings.value("visible", true).toBool());
                                        settings.endGroup();
                                        i++;
                                }
@@ -786,7 +899,7 @@ void DecodeSignal::restore_settings(QSettings &settings)
        // Restore channel mapping
        unsigned int channels = settings.value("channels").toInt();
 
-       const unordered_set< shared_ptr<data::SignalBase> > signalbases =
+       const vector< shared_ptr<data::SignalBase> > signalbases =
                session_.signalbases();
 
        for (unsigned int channel_id = 0; channel_id < channels; channel_id++) {
@@ -803,27 +916,49 @@ void DecodeSignal::restore_settings(QSettings &settings)
                QString assigned_signal_name = settings.value("assigned_signal_name").toString();
 
                for (const shared_ptr<data::SignalBase>& signal : signalbases)
-                       if (signal->name() == assigned_signal_name)
-                               channel->assigned_signal = signal.get();
+                       if ((signal->name() == assigned_signal_name) && (signal->type() != SignalBase::DecodeChannel))
+                               channel->assigned_signal = signal;
 
                channel->initial_pin_state = settings.value("initial_pin_state").toInt();
 
                settings.endGroup();
        }
 
+       connect_input_notifiers();
+
        // Update the internal structures
        stack_config_changed_ = true;
        update_channel_list();
        commit_decoder_channels();
+       update_output_signals();
+
+       // TODO Restore logic output signal settings
 
        begin_decode();
 }
 
-void DecodeSignal::set_error_message(QString msg)
+bool DecodeSignal::all_input_segments_complete(uint32_t segment_id) const
 {
-       error_message_ = msg;
-       // TODO Emulate noquote()
-       qDebug().nospace() << name() << ": " << msg;
+       bool all_complete = true;
+
+       for (const decode::DecodeChannel& ch : channels_)
+               if (ch.assigned_signal) {
+                       if (!ch.assigned_signal->logic_data())
+                               continue;
+
+                       const shared_ptr<Logic> logic_data = ch.assigned_signal->logic_data();
+                       if (logic_data->logic_segments().empty())
+                               return false;
+
+                       if (segment_id >= logic_data->logic_segments().size())
+                               return false;
+
+                       const shared_ptr<const LogicSegment> segment = logic_data->logic_segments()[segment_id]->get_shared_ptr();
+                       if (segment && !segment->is_complete())
+                               all_complete = false;
+               }
+
+       return all_complete;
 }
 
 uint32_t DecodeSignal::get_input_segment_count() const
@@ -847,7 +982,7 @@ uint32_t DecodeSignal::get_input_segment_count() const
        return (no_signals_assigned ? 0 : count);
 }
 
-uint32_t DecodeSignal::get_input_samplerate(uint32_t segment_id) const
+double DecodeSignal::get_input_samplerate(uint32_t segment_id) const
 {
        double samplerate = 0;
 
@@ -858,8 +993,10 @@ uint32_t DecodeSignal::get_input_samplerate(uint32_t segment_id) const
                                continue;
 
                        try {
-                               const shared_ptr<LogicSegment> segment = logic_data->logic_segments().at(segment_id);
-                               samplerate = segment->samplerate();
+                               const shared_ptr<const LogicSegment> segment =
+                                       logic_data->logic_segments().at(segment_id)->get_shared_ptr();
+                               if (segment)
+                                       samplerate = segment->samplerate();
                        } catch (out_of_range&) {
                                // Do nothing
                        }
@@ -986,7 +1123,7 @@ void DecodeSignal::mux_logic_samples(uint32_t segment_id, const int64_t start, c
                return;
 
        // Fetch the channel segments and their data
-       vector<shared_ptr<LogicSegment> > segments;
+       vector<shared_ptr<const LogicSegment> > segments;
        vector<const uint8_t*> signal_data;
        vector<uint8_t> signal_in_bytepos;
        vector<uint8_t> signal_in_bitpos;
@@ -995,14 +1132,19 @@ void DecodeSignal::mux_logic_samples(uint32_t segment_id, const int64_t start, c
                if (ch.assigned_signal) {
                        const shared_ptr<Logic> logic_data = ch.assigned_signal->logic_data();
 
-                       shared_ptr<LogicSegment> segment;
-                       try {
-                               segment = logic_data->logic_segments().at(segment_id);
-                       } catch (out_of_range&) {
+                       shared_ptr<const LogicSegment> segment;
+                       if (segment_id < logic_data->logic_segments().size()) {
+                               segment = logic_data->logic_segments().at(segment_id)->get_shared_ptr();
+                       } else {
                                qDebug() << "Muxer error for" << name() << ":" << ch.assigned_signal->name() \
                                        << "has no logic segment" << segment_id;
+                               logic_mux_interrupt_ = true;
                                return;
                        }
+
+                       if (!segment)
+                               return;
+
                        segments.push_back(segment);
 
                        uint8_t* data = new uint8_t[(end - start) * segment->unit_size()];
@@ -1014,7 +1156,6 @@ void DecodeSignal::mux_logic_samples(uint32_t segment_id, const int64_t start, c
                        signal_in_bitpos.push_back(bitpos % 8);
                }
 
-
        shared_ptr<LogicSegment> output_segment;
        try {
                output_segment = logic_mux_data_->logic_segments().at(segment_id);
@@ -1022,6 +1163,7 @@ void DecodeSignal::mux_logic_samples(uint32_t segment_id, const int64_t start, c
                qDebug() << "Muxer error for" << name() << ": no logic mux segment" \
                        << segment_id << "in mux_logic_samples(), mux segments size is" \
                        << logic_mux_data_->logic_segments().size();
+               logic_mux_interrupt_ = true;
                return;
        }
 
@@ -1065,82 +1207,104 @@ void DecodeSignal::mux_logic_samples(uint32_t segment_id, const int64_t start, c
 
 void DecodeSignal::logic_mux_proc()
 {
-       uint32_t segment_id = 0;
+       uint32_t input_segment_count;
+       do {
+               input_segment_count = get_input_segment_count();
+               if (input_segment_count == 0) {
+                       // Wait for input data
+                       unique_lock<mutex> logic_mux_lock(logic_mux_mutex_);
+                       logic_mux_cond_.wait(logic_mux_lock);
+               }
+       } while ((!logic_mux_interrupt_) && (input_segment_count == 0));
+
+       if (logic_mux_interrupt_)
+               return;
 
        assert(logic_mux_data_);
 
+       uint32_t segment_id = 0;
+
        // Create initial logic mux segment
        shared_ptr<LogicSegment> output_segment =
-               make_shared<LogicSegment>(*logic_mux_data_, segment_id,
-                       logic_mux_unit_size_, 0);
+               make_shared<LogicSegment>(*logic_mux_data_, segment_id, logic_mux_unit_size_, 0);
        logic_mux_data_->push_segment(output_segment);
 
        output_segment->set_samplerate(get_input_samplerate(0));
 
+       // Logic mux data is being updated
+       logic_mux_data_invalid_ = false;
+
+       uint64_t samples_to_process;
        do {
-               const uint64_t input_sample_count = get_working_sample_count(segment_id);
-               const uint64_t output_sample_count = output_segment->get_sample_count();
+               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;
+                       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 unit_size = output_segment->unit_size();
-                       const uint64_t chunk_sample_count = DecodeChunkLength / unit_size;
+                       if (samples_to_process > 0) {
+                               const uint64_t unit_size = output_segment->unit_size();
+                               const uint64_t chunk_sample_count = DecodeChunkLength / unit_size;
 
-                       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);
+                               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);
 
-                               mux_logic_samples(segment_id, start_sample, start_sample + sample_count);
-                               processed_samples += sample_count;
+                                       mux_logic_samples(segment_id, start_sample, start_sample + sample_count);
+                                       processed_samples += sample_count;
 
-                               // ...and process the newly muxed logic data
-                               decode_input_cond_.notify_one();
-                       } while (!logic_mux_interrupt_ && (processed_samples < samples_to_process));
-               }
+                                       // ...and process the newly muxed logic data
+                                       decode_input_cond_.notify_one();
+                               } while (!logic_mux_interrupt_ && (processed_samples < samples_to_process));
+                       }
+               } while (!logic_mux_interrupt_ && (samples_to_process > 0));
 
-               if (samples_to_process == 0) {
-                       // TODO Optimize this by caching the input segment count and only
-                       // querying it when the cached value was reached
-                       if (segment_id < get_input_segment_count() - 1) {
-                               // Process next segment
-                               segment_id++;
+               if (!logic_mux_interrupt_) {
+                       // samples_to_process is now 0, we've exhausted the currently available input data
 
-                               output_segment =
-                                       make_shared<LogicSegment>(*logic_mux_data_, segment_id,
-                                               logic_mux_unit_size_, 0);
-                               logic_mux_data_->push_segment(output_segment);
+                       // If the input segments are complete, we've completed this segment
+                       if (all_input_segments_complete(segment_id)) {
+                               if (!output_segment->is_complete())
+                                       output_segment->set_complete();
 
-                               output_segment->set_samplerate(get_input_samplerate(segment_id));
+                               if (segment_id < get_input_segment_count() - 1) {
 
-                       } else {
-                               // All segments have been processed
-                               logic_mux_data_invalid_ = false;
+                                       // Process next segment
+                                       segment_id++;
+
+                                       output_segment =
+                                               make_shared<LogicSegment>(*logic_mux_data_, segment_id,
+                                                       logic_mux_unit_size_, 0);
+                                       logic_mux_data_->push_segment(output_segment);
 
-                               // Wait for more input
+                                       output_segment->set_samplerate(get_input_samplerate(segment_id));
+                               } else {
+                                       // Wait for more input data if we're processing the currently last segment
+                                       unique_lock<mutex> logic_mux_lock(logic_mux_mutex_);
+                                       logic_mux_cond_.wait(logic_mux_lock);
+                               }
+                       } else {
+                               // Input segments aren't all complete yet but samples_to_process is 0, wait for more input data
                                unique_lock<mutex> logic_mux_lock(logic_mux_mutex_);
                                logic_mux_cond_.wait(logic_mux_lock);
                        }
                }
-
        } while (!logic_mux_interrupt_);
 }
 
 void DecodeSignal::decode_data(
        const int64_t abs_start_samplenum, const int64_t sample_count,
-       const shared_ptr<LogicSegment> input_segment)
+       const shared_ptr<const LogicSegment> input_segment)
 {
        const int64_t unit_size = input_segment->unit_size();
        const int64_t chunk_sample_count = DecodeChunkLength / unit_size;
 
        for (int64_t i = abs_start_samplenum;
-               error_message_.isEmpty() && !decode_interrupt_ &&
-                       (i < (abs_start_samplenum + sample_count));
+               !decode_interrupt_ && (i < (abs_start_samplenum + sample_count));
                i += chunk_sample_count) {
 
                const int64_t chunk_end = min(i + chunk_sample_count,
@@ -1157,8 +1321,10 @@ void DecodeSignal::decode_data(
                input_segment->get_samples(i, chunk_end, chunk);
 
                if (srd_session_send(srd_session_, i, chunk_end, chunk,
-                               data_size, unit_size) != SRD_OK)
+                               data_size, unit_size) != SRD_OK) {
                        set_error_message(tr("Decoder reported an error"));
+                       decode_interrupt_ = true;
+               }
 
                delete[] chunk;
 
@@ -1184,16 +1350,20 @@ void DecodeSignal::decode_proc()
        current_segment_id_ = 0;
 
        // If there is no input data available yet, wait until it is or we're interrupted
-       if (logic_mux_data_->logic_segments().size() == 0) {
-               unique_lock<mutex> input_wait_lock(input_mutex_);
-               decode_input_cond_.wait(input_wait_lock);
-       }
+       do {
+               if (logic_mux_data_->logic_segments().size() == 0) {
+                       // Wait for input data
+                       unique_lock<mutex> input_wait_lock(input_mutex_);
+                       decode_input_cond_.wait(input_wait_lock);
+               }
+       } while ((!decode_interrupt_) && (logic_mux_data_->logic_segments().size() == 0));
 
        if (decode_interrupt_)
                return;
 
-       shared_ptr<LogicSegment> input_segment = logic_mux_data_->logic_segments().front();
-       assert(input_segment);
+       shared_ptr<const LogicSegment> input_segment = logic_mux_data_->logic_segments().front()->get_shared_ptr();
+       if (!input_segment)
+               return;
 
        // Create the initial segment and set its sample rate so that we can pass it to SRD
        create_decode_segment();
@@ -1202,57 +1372,71 @@ void DecodeSignal::decode_proc()
 
        start_srd_session();
 
-       uint64_t sample_count = 0;
+       uint64_t samples_to_process = 0;
        uint64_t abs_start_samplenum = 0;
        do {
                // Keep processing new samples until we exhaust the input data
                do {
-                       lock_guard<mutex> input_lock(input_mutex_);
-                       sample_count = input_segment->get_sample_count() - abs_start_samplenum;
+                       samples_to_process = input_segment->get_sample_count() - abs_start_samplenum;
 
-                       if (sample_count > 0) {
-                               decode_data(abs_start_samplenum, sample_count, input_segment);
-                               abs_start_samplenum += sample_count;
+                       if (samples_to_process > 0) {
+                               decode_data(abs_start_samplenum, samples_to_process, input_segment);
+                               abs_start_samplenum += samples_to_process;
                        }
-               } while (error_message_.isEmpty() && (sample_count > 0) && !decode_interrupt_);
-
-               if (error_message_.isEmpty() && !decode_interrupt_ && sample_count == 0) {
-                       if (current_segment_id_ < logic_mux_data_->logic_segments().size() - 1) {
-                               // Process next segment
-                               current_segment_id_++;
-
-                               try {
-                                       input_segment = logic_mux_data_->logic_segments().at(current_segment_id_);
-                               } catch (out_of_range&) {
-                                       qDebug() << "Decode error for" << name() << ": no logic mux segment" \
-                                               << current_segment_id_ << "in decode_proc(), mux segments size is" \
-                                               << logic_mux_data_->logic_segments().size();
-                                       return;
+               } while (!decode_interrupt_ && (samples_to_process > 0));
+
+               if (!decode_interrupt_) {
+                       // samples_to_process is now 0, we've exhausted the currently available input data
+
+                       // If the input segment is complete, we've exhausted this segment
+                       if (input_segment->is_complete()) {
+#if defined HAVE_SRD_SESSION_SEND_EOF && HAVE_SRD_SESSION_SEND_EOF
+                               // Tell protocol decoders about the end of
+                               // the input data, which may result in more
+                               // annotations being emitted
+                               (void)srd_session_send_eof(srd_session_);
+                               new_annotations();
+#endif
+
+                               if (current_segment_id_ < (logic_mux_data_->logic_segments().size() - 1)) {
+                                       // Process next segment
+                                       current_segment_id_++;
+
+                                       try {
+                                               input_segment = logic_mux_data_->logic_segments().at(current_segment_id_);
+                                       } catch (out_of_range&) {
+                                               qDebug() << "Decode error for" << name() << ": no logic mux segment" \
+                                                       << current_segment_id_ << "in decode_proc(), mux segments size is" \
+                                                       << logic_mux_data_->logic_segments().size();
+                                               decode_interrupt_ = true;
+                                               return;
+                                       }
+                                       abs_start_samplenum = 0;
+
+                                       // Create the next segment and set its metadata
+                                       create_decode_segment();
+                                       segments_.at(current_segment_id_).samplerate = input_segment->samplerate();
+                                       segments_.at(current_segment_id_).start_time = input_segment->start_time();
+
+                                       // Reset decoder state but keep the decoder stack intact
+                                       terminate_srd_session();
+                               } else {
+                                       // All segments have been processed
+                                       if (!decode_interrupt_)
+                                               decode_finished();
+
+                                       // Wait for more input data
+                                       unique_lock<mutex> input_wait_lock(input_mutex_);
+                                       decode_input_cond_.wait(input_wait_lock);
                                }
-                               abs_start_samplenum = 0;
-
-                               // Create the next segment and set its metadata
-                               create_decode_segment();
-                               segments_.at(current_segment_id_).samplerate = input_segment->samplerate();
-                               segments_.at(current_segment_id_).start_time = input_segment->start_time();
-
-                               // Reset decoder state but keep the decoder stack intact
-                               terminate_srd_session();
                        } else {
-                               // All segments have been processed
-                               decode_finished();
-
-                               // Wait for new input data or an interrupt was requested
+                               // Input segment isn't complete yet but samples_to_process is 0, wait for more input data
                                unique_lock<mutex> input_wait_lock(input_mutex_);
                                decode_input_cond_.wait(input_wait_lock);
                        }
-               }
-       } while (error_message_.isEmpty() && !decode_interrupt_);
 
-       // Potentially reap decoders when the application no longer is
-       // interested in their (pending) results.
-       if (decode_interrupt_)
-               terminate_srd_session();
+               }
+       } while (!decode_interrupt_);
 }
 
 void DecodeSignal::start_srd_session()
@@ -1284,6 +1468,9 @@ void DecodeSignal::start_srd_session()
                return;
        }
 
+       // Update the samplerates for the output logic channels
+       update_output_signals();
+
        // Create the session
        srd_session_new(&srd_session_);
        assert(srd_session_);
@@ -1317,6 +1504,9 @@ void DecodeSignal::start_srd_session()
        srd_pd_output_callback_add(srd_session_, SRD_OUTPUT_BINARY,
                DecodeSignal::binary_callback, this);
 
+       srd_pd_output_callback_add(srd_session_, SRD_OUTPUT_LOGIC,
+               DecodeSignal::logic_output_callback, this);
+
        srd_session_start(srd_session_);
 
        // We just recreated the srd session, so all stack changes are applied now
@@ -1331,6 +1521,9 @@ void DecodeSignal::terminate_srd_session()
        // those stacks which still are processing data while the
        // application no longer wants them to.
        if (srd_session_) {
+#if defined HAVE_SRD_SESSION_SEND_EOF && HAVE_SRD_SESSION_SEND_EOF
+               (void)srd_session_send_eof(srd_session_);
+#endif
                srd_session_terminate_reset(srd_session_);
 
                // Metadata is cleared also, so re-set it
@@ -1360,27 +1553,42 @@ void DecodeSignal::stop_srd_session()
 
 void DecodeSignal::connect_input_notifiers()
 {
-       // Disconnect the notification slot from the previous set of signals
-       disconnect(this, SLOT(on_data_cleared()));
-       disconnect(this, SLOT(on_data_received()));
-
        // Connect the currently used signals to our slot
        for (decode::DecodeChannel& ch : channels_) {
                if (!ch.assigned_signal)
                        continue;
+               const data::SignalBase *signal = ch.assigned_signal.get();
 
-               const data::SignalBase *signal = ch.assigned_signal;
                connect(signal, SIGNAL(samples_cleared()),
-                       this, SLOT(on_data_cleared()));
+                       this, SLOT(on_data_cleared()), Qt::UniqueConnection);
                connect(signal, SIGNAL(samples_added(uint64_t, uint64_t, uint64_t)),
-                       this, SLOT(on_data_received()));
+                       this, SLOT(on_data_received()), Qt::UniqueConnection);
+
+               if (signal->logic_data())
+                       connect(signal->logic_data().get(), SIGNAL(segment_completed()),
+                               this, SLOT(on_input_segment_completed()), Qt::UniqueConnection);
+       }
+}
+
+void DecodeSignal::disconnect_input_notifiers()
+{
+       // Disconnect the notification slot from the previous set of signals
+       for (decode::DecodeChannel& ch : channels_) {
+               if (!ch.assigned_signal)
+                       continue;
+               const data::SignalBase *signal = ch.assigned_signal.get();
+               disconnect(signal, nullptr, this, SLOT(on_data_cleared()));
+               disconnect(signal, nullptr, this, SLOT(on_data_received()));
+
+               if (signal->logic_data())
+                       disconnect(signal->logic_data().get(), nullptr, this, SLOT(on_input_segment_completed()));
        }
 }
 
 void DecodeSignal::create_decode_segment()
 {
        // Create annotation segment
-       segments_.emplace_back(DecodeSegment());
+       segments_.emplace_back();
 
        // Add annotation classes
        for (const shared_ptr<Decoder>& dec : stack_)
@@ -1408,6 +1616,9 @@ void DecodeSignal::annotation_callback(srd_proto_data *pdata, void *decode_signa
        if (ds->decode_interrupt_)
                return;
 
+       if (ds->segments_.empty())
+               return;
+
        lock_guard<mutex> lock(ds->output_mutex_);
 
        // Get the decoder and the annotation data
@@ -1436,8 +1647,67 @@ void DecodeSignal::annotation_callback(srd_proto_data *pdata, void *decode_signa
        if (!row)
                row = dec->get_row_by_id(0);
 
-       // Add the annotation
-       ds->segments_[ds->current_segment_id_].annotation_rows.at(row).emplace_annotation(pdata);
+       RowData& row_data = ds->segments_[ds->current_segment_id_].annotation_rows.at(row);
+
+       // Add the annotation to the row
+       const Annotation* ann = row_data.emplace_annotation(pdata);
+
+       // We insert the annotation into the global annotation list in a way so that
+       // the annotation list is sorted by start sample and length. Otherwise, we'd
+       // have to sort the model, which is expensive
+       deque<const Annotation*>& all_annotations =
+               ds->segments_[ds->current_segment_id_].all_annotations;
+
+       if (all_annotations.empty()) {
+               all_annotations.emplace_back(ann);
+       } else {
+               const uint64_t new_ann_len = (pdata->end_sample - pdata->start_sample);
+               bool ann_has_earlier_start = (pdata->start_sample < all_annotations.back()->start_sample());
+               bool ann_is_longer = (new_ann_len >
+                       (all_annotations.back()->end_sample() - all_annotations.back()->start_sample()));
+
+               if (ann_has_earlier_start && ann_is_longer) {
+                       bool ann_has_same_start;
+                       auto it = all_annotations.end();
+
+                       do {
+                               it--;
+                               ann_has_earlier_start = (pdata->start_sample < (*it)->start_sample());
+                               ann_has_same_start = (pdata->start_sample == (*it)->start_sample());
+                               ann_is_longer = (new_ann_len > (*it)->length());
+                       } while ((ann_has_earlier_start || (ann_has_same_start && ann_is_longer)) && (it != all_annotations.begin()));
+
+                       // Allow inserting at the front
+                       if (it != all_annotations.begin())
+                               it++;
+
+                       all_annotations.emplace(it, ann);
+               } else
+                       all_annotations.emplace_back(ann);
+       }
+
+       // When emplace_annotation() inserts instead of appends an annotation,
+       // the pointers in all_annotations that follow the inserted annotation and
+       // point to annotations for this row are off by one and must be updated
+       if (&(row_data.annotations().back()) != ann) {
+               // Search backwards until we find the annotation we just added
+               auto row_it = row_data.annotations().end();
+               auto all_it = all_annotations.end();
+               do {
+                       all_it--;
+                       if ((*all_it)->row_data() == &row_data)
+                               row_it--;
+               } while (&(*row_it) != ann);
+
+               // Update the annotation addresses for this row's annotations until the end
+               do {
+                       if ((*all_it)->row_data() == &row_data) {
+                               *all_it = &(*row_it);
+                               row_it++;
+                       }
+                       all_it++;
+               } while (all_it != all_annotations.end());
+       }
 }
 
 void DecodeSignal::binary_callback(srd_proto_data *pdata, void *decode_signal)
@@ -1489,6 +1759,77 @@ void DecodeSignal::binary_callback(srd_proto_data *pdata, void *decode_signal)
        ds->new_binary_data(ds->current_segment_id_, (void*)dec, pdb->bin_class);
 }
 
+void DecodeSignal::logic_output_callback(srd_proto_data *pdata, void *decode_signal)
+{
+       assert(pdata);
+       assert(decode_signal);
+
+       DecodeSignal *const ds = (DecodeSignal*)decode_signal;
+       assert(ds);
+
+       if (ds->decode_interrupt_)
+               return;
+
+       lock_guard<mutex> lock(ds->output_mutex_);
+
+       assert(pdata->pdo);
+       assert(pdata->pdo->di);
+       const srd_decoder *const decc = pdata->pdo->di->decoder;
+       assert(decc);
+
+       const srd_proto_data_logic *const pdl = (const srd_proto_data_logic*)pdata->data;
+       assert(pdl);
+
+       // FIXME Only one group supported for now
+       if (pdl->logic_group > 0) {
+               qWarning() << "Received logic output state change for group" << pdl->logic_group << "from decoder" \
+                       << QString::fromUtf8(decc->name) << "but only group 0 is currently supported";
+               return;
+       }
+
+       shared_ptr<Logic> output_logic = ds->output_logic_.at(decc);
+
+       vector< shared_ptr<Segment> > segments = output_logic->segments();
+
+       shared_ptr<LogicSegment> last_segment;
+
+       if (!segments.empty())
+               last_segment = dynamic_pointer_cast<LogicSegment>(segments.back());
+       else {
+               // Happens when the data was cleared - all segments are gone then
+               // segment_id is always 0 as it's the first segment
+               last_segment = make_shared<data::LogicSegment>(
+                       *output_logic, 0, (output_logic->num_channels() + 7) / 8, output_logic->get_samplerate());
+               output_logic->push_segment(last_segment);
+       }
+
+       if (pdata->start_sample < pdata->end_sample) {
+               vector<uint8_t> data;
+               const unsigned int unit_size = last_segment->unit_size();
+               data.resize(unit_size * (1 + pdl->repeat_count));
+
+               if (unit_size == 1)
+                       for (unsigned int i = 0; i <= pdl->repeat_count; i++)
+                               data.data()[i * unit_size] = *((uint8_t*)pdl->data);
+               else if (unit_size == 2)
+                       for (unsigned int i = 0; i <= pdl->repeat_count; i++)
+                               data.data()[i * unit_size] = *((uint16_t*)pdl->data);
+               else if (unit_size <= 4)
+                       for (unsigned int i = 0; i <= pdl->repeat_count; i++)
+                               data.data()[i * unit_size] = *((uint32_t*)pdl->data);
+               else if (unit_size <= 8)
+                       for (unsigned int i = 0; i <= pdl->repeat_count; i++)
+                               data.data()[i * unit_size] = *((uint64_t*)pdl->data);
+               else
+                       for (unsigned int i = 0; i <= pdl->repeat_count; i++)
+                               memcpy((void*)&data.data()[i * unit_size], (void*)pdl->data, unit_size);
+
+               last_segment->append_payload(data.data(), data.size());
+       } else
+               qWarning() << "Ignoring malformed logic output state change for group" << pdl->logic_group << "from decoder" \
+                       << QString::fromUtf8(decc->name) << "from" << pdata->start_sample << "to" << pdata->end_sample;
+}
+
 void DecodeSignal::on_capture_state_changed(int state)
 {
        // If a new acquisition was started, we need to start decoding from scratch
@@ -1506,16 +1847,33 @@ void DecodeSignal::on_data_cleared()
 void DecodeSignal::on_data_received()
 {
        // If we detected a lack of input data when trying to start decoding,
-       // we have set an error message. Only try again if we now have data
+       // we have set an error message. Bail out if we still don't have data
        // to work with
        if ((!error_message_.isEmpty()) && (get_input_segment_count() == 0))
                return;
 
+       if (!error_message_.isEmpty()) {
+               error_message_.clear();
+               // TODO Emulate noquote()
+               qDebug().nospace() << name() << ": Input data available, error cleared";
+       }
+
        if (!logic_mux_thread_.joinable())
                begin_decode();
        else
                logic_mux_cond_.notify_one();
 }
 
+void DecodeSignal::on_input_segment_completed()
+{
+       if (!logic_mux_thread_.joinable())
+               logic_mux_cond_.notify_one();
+}
+
+void DecodeSignal::on_annotation_visibility_changed()
+{
+       annotation_visibility_changed();
+}
+
 } // namespace data
 } // namespace pv
index cee4ccf0eadcf6db1a214483057231e4274c7dbf..333e953b2d63e66c415b403092ad48cc2975b0f5 100644 (file)
@@ -26,8 +26,8 @@
 #include <unordered_set>
 #include <vector>
 
+#include <QDebug>
 #include <QSettings>
-#include <QString>
 
 #include <libsigrokdecode/libsigrokdecode.h>
 
@@ -77,11 +77,17 @@ struct DecodeBinaryClass
 
 struct DecodeSegment
 {
-       map<const Row*, RowData> annotation_rows;
+       // Constructor is a no-op
+       DecodeSegment() { };
+       // Copy constructor is a no-op
+       DecodeSegment(DecodeSegment&& ds) { (void)ds; qCritical() << "Empty DecodeSegment copy constructor called"; };
+
+       map<const Row*, RowData> annotation_rows;  // Note: Row is the same for all segments while RowData is not
        pv::util::Timestamp start_time;
        double samplerate;
        int64_t samples_decoded_incl, samples_decoded_excl;
        vector<DecodeBinaryClass> binary_classes;
+       deque<const Annotation*> all_annotations;
 };
 
 class DecodeSignal : public SignalBase
@@ -97,6 +103,16 @@ public:
        DecodeSignal(pv::Session &session);
        virtual ~DecodeSignal();
 
+       /**
+        * Sets the name of the signal.
+        */
+       virtual void set_name(QString name);
+
+       /**
+        * Set the color of the signal.
+        */
+       virtual void set_color(QColor color);
+
        bool is_decode_signal() const;
        const vector< shared_ptr<Decoder> >& decoder_stack() const;
 
@@ -109,16 +125,17 @@ public:
        void pause_decode();
        void resume_decode();
        bool is_paused() const;
-       QString error_message() const;
 
        const vector<decode::DecodeChannel> get_channels() const;
        void auto_assign_signals(const shared_ptr<Decoder> dec);
-       void assign_signal(const uint16_t channel_id, const SignalBase *signal);
+       void assign_signal(const uint16_t channel_id, shared_ptr<const SignalBase> signal);
        int get_assigned_signal_count() const;
 
+       void update_output_signals();
+
        void set_initial_pin_state(const uint16_t channel_id, const int init_state);
 
-       double samplerate() const;
+       virtual double get_samplerate() const;
        const pv::util::Timestamp start_time() const;
 
        /**
@@ -176,15 +193,16 @@ public:
        const DecodeBinaryClass* get_binary_data_class(uint32_t segment_id,
                const Decoder* dec, uint32_t bin_class_id) const;
 
+       const deque<const Annotation*>* get_all_annotations_by_segment(uint32_t segment_id) const;
+
        virtual void save_settings(QSettings &settings) const;
 
        virtual void restore_settings(QSettings &settings);
 
 private:
-       void set_error_message(QString msg);
-
+       bool all_input_segments_complete(uint32_t segment_id) const;
        uint32_t get_input_segment_count() const;
-       uint32_t get_input_samplerate(uint32_t segment_id) const;
+       double get_input_samplerate(uint32_t segment_id) const;
 
        Decoder* get_decoder_by_instance(const srd_decoder *const srd_dec);
 
@@ -196,7 +214,7 @@ private:
        void logic_mux_proc();
 
        void decode_data(const int64_t abs_start_samplenum, const int64_t sample_count,
-               const shared_ptr<LogicSegment> input_segment);
+               const shared_ptr<const LogicSegment> input_segment);
        void decode_proc();
 
        void start_srd_session();
@@ -204,11 +222,13 @@ private:
        void stop_srd_session();
 
        void connect_input_notifiers();
+       void disconnect_input_notifiers();
 
        void create_decode_segment();
 
        static void annotation_callback(srd_proto_data *pdata, void *decode_signal);
        static void binary_callback(srd_proto_data *pdata, void *decode_signal);
+       static void logic_output_callback(srd_proto_data *pdata, void *decode_signal);
 
 Q_SIGNALS:
        void decoder_stacked(void* decoder); ///< decoder is of type decode::Decoder*
@@ -218,11 +238,15 @@ Q_SIGNALS:
        void decode_reset();
        void decode_finished();
        void channels_updated();
+       void annotation_visibility_changed();
 
 private Q_SLOTS:
        void on_capture_state_changed(int state);
        void on_data_cleared();
        void on_data_received();
+       void on_input_segment_completed();
+
+       void on_annotation_visibility_changed();
 
 private:
        pv::Session &session_;
@@ -238,7 +262,7 @@ private:
        vector< shared_ptr<Decoder> > stack_;
        bool stack_config_changed_;
 
-       vector<DecodeSegment> segments_;
+       deque<DecodeSegment> segments_;
        uint32_t current_segment_id_;
 
        mutable mutex input_mutex_, output_mutex_, decode_pause_mutex_, logic_mux_mutex_;
@@ -250,7 +274,9 @@ private:
 
        bool decode_paused_;
 
-       QString error_message_;
+       map<const srd_decoder*, shared_ptr<Logic>> output_logic_;
+       map<const srd_decoder*, vector<uint8_t>> output_logic_muxed_data_;
+       vector< shared_ptr<SignalBase>> output_signals_;
 };
 
 } // namespace data
index 516060a0aec9013c139f8597dc0bf9bfd70de6aa..4a13e568bc80ff38770a245b1e9b38b1861d15a9 100644 (file)
@@ -32,6 +32,7 @@ namespace data {
 
 Logic::Logic(unsigned int num_channels) :
        SignalData(),
+       samplerate_(1),  // Default is 1 Hz to prevent division-by-zero errors
        num_channels_(num_channels)
 {
        assert(num_channels_ > 0);
@@ -45,6 +46,11 @@ unsigned int Logic::num_channels() const
 void Logic::push_segment(shared_ptr<LogicSegment> &segment)
 {
        segments_.push_back(segment);
+
+       if ((samplerate_ == 1) && (segment->samplerate() > 1))
+               samplerate_ = segment->samplerate();
+
+       connect(segment.get(), SIGNAL(completed()), this, SLOT(on_segment_completed()));
 }
 
 const deque< shared_ptr<LogicSegment> >& Logic::logic_segments() const
@@ -52,6 +58,11 @@ const deque< shared_ptr<LogicSegment> >& Logic::logic_segments() const
        return segments_;
 }
 
+deque< shared_ptr<LogicSegment> >& Logic::logic_segments()
+{
+       return segments_;
+}
+
 vector< shared_ptr<Segment> > Logic::segments() const
 {
        return vector< shared_ptr<Segment> >(segments_.begin(), segments_.end());
@@ -64,17 +75,21 @@ uint32_t Logic::get_segment_count() const
 
 void Logic::clear()
 {
-       segments_.clear();
+       if (!segments_.empty()) {
+               segments_.clear();
+
+               samples_cleared();
+       }
+}
 
-       samples_cleared();
+void Logic::set_samplerate(double value)
+{
+       samplerate_ = value;
 }
 
 double Logic::get_samplerate() const
 {
-       if (segments_.empty())
-               return 1.0;
-
-       return segments_.front()->samplerate();
+       return samplerate_;
 }
 
 uint64_t Logic::max_sample_count() const
@@ -87,11 +102,16 @@ uint64_t Logic::max_sample_count() const
        return l;
 }
 
-void Logic::notify_samples_added(QObject* segment, uint64_t start_sample,
+void Logic::notify_samples_added(shared_ptr<Segment> segment, uint64_t start_sample,
        uint64_t end_sample)
 {
        samples_added(segment, start_sample, end_sample);
 }
 
+void Logic::on_segment_completed()
+{
+       segment_completed();
+}
+
 } // namespace data
 } // namespace pv
index e0fb7dd14dd05dab2dc93451d999350f0d6dd60f..07f8222c1946cc699828c6ac3cdc6c3d761eb67d 100644 (file)
@@ -21,6 +21,7 @@
 #define PULSEVIEW_PV_DATA_LOGIC_HPP
 
 #include "signaldata.hpp"
+#include "segment.hpp"
 
 #include <deque>
 
@@ -47,6 +48,7 @@ public:
        void push_segment(shared_ptr<LogicSegment> &segment);
 
        const deque< shared_ptr<LogicSegment> >& logic_segments() const;
+       deque< shared_ptr<LogicSegment> >& logic_segments();
 
        vector< shared_ptr<Segment> > segments() const;
 
@@ -54,20 +56,26 @@ public:
 
        void clear();
 
+       void set_samplerate(double value);
+
        double get_samplerate() const;
 
        uint64_t max_sample_count() const;
 
-       void notify_samples_added(QObject* segment, uint64_t start_sample,
+       void notify_samples_added(shared_ptr<Segment> segment, uint64_t start_sample,
                uint64_t end_sample);
 
 Q_SIGNALS:
        void samples_cleared();
 
-       void samples_added(QObject* segment, uint64_t start_sample,
+       void samples_added(SharedPtrToSegment segment, uint64_t start_sample,
                uint64_t end_sample);
 
+private Q_SLOTS:
+       void on_segment_completed();
+
 private:
+       double samplerate_;
        const unsigned int num_channels_;
        deque< shared_ptr<LogicSegment> > segments_;
 };
index b9e57caa9e797725ed5532e226353cef527b7649..739b2b9ee2a97652de2d4d4e3530041c1613af2c 100644 (file)
@@ -63,10 +63,24 @@ LogicSegment::LogicSegment(pv::data::Logic& owner, uint32_t segment_id,
 LogicSegment::~LogicSegment()
 {
        lock_guard<recursive_mutex> lock(mutex_);
+
        for (MipMapLevel &l : mip_map_)
                free(l.data);
 }
 
+shared_ptr<const LogicSegment> LogicSegment::get_shared_ptr() const
+{
+       shared_ptr<const Segment> ptr = nullptr;
+
+       try {
+               ptr = shared_from_this();
+       } catch (std::exception& e) {
+               /* Do nothing, ptr remains a null pointer */
+       }
+
+       return ptr ? std::dynamic_pointer_cast<const LogicSegment>(ptr) : nullptr;
+}
+
 template <class T>
 void LogicSegment::downsampleTmain(const T*&in, T &acc, T &prev)
 {
@@ -328,6 +342,7 @@ void LogicSegment::append_payload(shared_ptr<sigrok::Logic> logic)
 
 void LogicSegment::append_payload(void *data, uint64_t data_size)
 {
+       assert(unit_size_ > 0);
        assert((data_size % unit_size_) == 0);
 
        lock_guard<recursive_mutex> lock(mutex_);
@@ -341,11 +356,36 @@ void LogicSegment::append_payload(void *data, uint64_t data_size)
        append_payload_to_mipmap();
 
        if (sample_count > 1)
-               owner_.notify_samples_added(this, prev_sample_count + 1,
-                       prev_sample_count + 1 + sample_count);
+               owner_.notify_samples_added(SharedPtrToSegment(shared_from_this()),
+                       prev_sample_count + 1, prev_sample_count + 1 + sample_count);
        else
-               owner_.notify_samples_added(this, prev_sample_count + 1,
-                       prev_sample_count + 1);
+               owner_.notify_samples_added(SharedPtrToSegment(shared_from_this()),
+                       prev_sample_count + 1, prev_sample_count + 1);
+}
+
+void LogicSegment::append_subsignal_payload(unsigned int index, void *data,
+       uint64_t data_size, vector<uint8_t>& destination)
+{
+       if (index == 0)
+               destination.resize(data_size * unit_size_, 0);
+
+       // Set the bits for this sub-signal where needed
+       // Note: the bytes in *data must either be 0 or 1, nothing else
+       unsigned int index_byte_offs = index / 8;
+       uint8_t* output_data = destination.data() + index_byte_offs;
+       uint8_t* input_data = (uint8_t*)data;
+
+       for (uint64_t i = 0; i < data_size; i++) {
+               assert((i * unit_size_ + index_byte_offs) < destination.size());
+               *output_data |= (input_data[i] << index);
+               output_data += unit_size_;
+       }
+
+       if (index == owner_.num_channels() - 1) {
+               // We gathered sample data of all sub-signals, let's append it
+               append_payload(destination.data(), destination.size());
+               destination.clear();
+       }
 }
 
 void LogicSegment::get_samples(int64_t start_sample,
@@ -628,7 +668,7 @@ void LogicSegment::append_payload_to_mipmap()
                else if (unit_size_ == 4)
                        downsampleT<uint32_t>(src_ptr, dest_ptr, count);
                else if (unit_size_ == 8)
-                       downsampleT<uint8_t>(src_ptr, dest_ptr, count);
+                       downsampleT<uint64_t>(src_ptr, dest_ptr, count);
                else
                        downsampleGeneric(src_ptr, dest_ptr, count);
                len_sample -= count;
index 67959b31ec17e402bca93466249355fd60cc5ea4..35e8eca95f9212091475e2b0bd2bd77fd593e7f8 100644 (file)
 
 #include "segment.hpp"
 
-#include <utility>
 #include <vector>
 
 #include <QObject>
 
+using std::enable_shared_from_this;
 using std::pair;
 using std::shared_ptr;
 using std::vector;
@@ -48,7 +48,7 @@ namespace data {
 
 class Logic;
 
-class LogicSegment : public Segment
+class LogicSegment : public Segment, public enable_shared_from_this<Segment>
 {
        Q_OBJECT
 
@@ -75,9 +75,30 @@ public:
 
        virtual ~LogicSegment();
 
+       /**
+        * Using enable_shared_from_this prevents the normal use of shared_ptr
+        * instances by users of LogicSegment instances. Instead, shared_ptrs may
+        * only be created by the instance itself.
+        * See https://en.cppreference.com/w/cpp/memory/enable_shared_from_this
+        */
+       shared_ptr<const LogicSegment> get_shared_ptr() const;
+
        void append_payload(shared_ptr<sigrok::Logic> logic);
        void append_payload(void *data, uint64_t data_size);
 
+       /**
+        * Appends sample data for a single channel where each byte
+        * represents one sample - if it's 0 the state is low, if 1 high.
+        * Other values are not permitted.
+        * Assumes that all channels are having samples added and in the
+        * order of 0..n, not n..0.
+        * Also assumes the the number of samples added for each channel
+        * is constant for every invokation for 0..n. The number of samples
+        * hence may only change when index is 0.
+        */
+       void append_subsignal_payload(unsigned int index, void *data,
+               uint64_t data_size, vector<uint8_t>& destination);
+
        void get_samples(int64_t start_sample, int64_t end_sample, uint8_t* dest) const;
 
        /**
diff --git a/pv/data/mathsignal.cpp b/pv/data/mathsignal.cpp
new file mode 100644 (file)
index 0000000..adb2713
--- /dev/null
@@ -0,0 +1,619 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <limits>
+
+#include <QDebug>
+
+#include "mathsignal.hpp"
+
+#include <extdef.h>
+#include <pv/globalsettings.hpp>
+#include <pv/session.hpp>
+#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 {
+
+#define MATH_ERR_NONE           0
+#define MATH_ERR_EMPTY_EXPR     1
+#define MATH_ERR_EXPRESSION     2
+#define MATH_ERR_INVALID_SIGNAL 3
+#define MATH_ERR_ENABLE         4
+
+const int64_t MathSignal::ChunkLength = 256 * 1024;
+
+
+template<typename T>
+struct fnc_sample : public exprtk::igeneric_function<T>
+{
+       typedef typename exprtk::igeneric_function<T>::parameter_list_t parameter_list_t;
+       typedef typename exprtk::igeneric_function<T>::generic_type generic_type;
+       typedef typename generic_type::scalar_view scalar_t;
+       typedef typename generic_type::string_view string_t;
+
+       fnc_sample(MathSignal& owner) :
+               exprtk::igeneric_function<T>("ST"),  // Require channel name and sample number
+               owner_(owner),
+               sig_data(nullptr)
+       {
+       }
+
+       T operator()(parameter_list_t parameters)
+       {
+               const string_t exprtk_sig_name = string_t(parameters[0]);
+               const scalar_t exprtk_sample_num = scalar_t(parameters[1]);
+
+               const std::string str_sig_name = to_str(exprtk_sig_name);
+               const double sample_num = exprtk_sample_num();
+
+               if (sample_num < 0)
+                       return 0;
+
+               if (!sig_data)
+                       sig_data = owner_.signal_from_name(str_sig_name);
+
+               if (!sig_data)
+                       // There doesn't actually exist a signal with that name
+                       return 0;
+
+               owner_.update_signal_sample(sig_data, current_segment, sample_num);
+
+               return T(sig_data->sample_value);
+       }
+
+       MathSignal& owner_;
+       uint32_t current_segment;
+       signal_data* sig_data;
+};
+
+
+MathSignal::MathSignal(pv::Session &session) :
+       SignalBase(nullptr, SignalBase::MathChannel),
+       session_(session),
+       use_custom_sample_rate_(false),
+       use_custom_sample_count_(false),
+       expression_(""),
+       error_type_(MATH_ERR_NONE),
+       exprtk_unknown_symbol_table_(nullptr),
+       exprtk_symbol_table_(nullptr),
+       exprtk_expression_(nullptr),
+       exprtk_parser_(nullptr),
+       fnc_sample_(nullptr)
+{
+       uint32_t sig_idx = session_.get_next_signal_index(MathChannel);
+       set_name(QString(tr("Math%1")).arg(sig_idx));
+       set_color(AnalogSignalColors[(sig_idx - 1) % countof(AnalogSignalColors)]);
+
+       set_data(std::make_shared<data::Analog>());
+
+       connect(&session_, SIGNAL(capture_state_changed(int)),
+               this, SLOT(on_capture_state_changed(int)));
+}
+
+MathSignal::~MathSignal()
+{
+       reset_generation();
+}
+
+void MathSignal::save_settings(QSettings &settings) const
+{
+       SignalBase::save_settings(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)
+{
+       SignalBase::restore_settings(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::get_expression() const
+{
+       return expression_;
+}
+
+void MathSignal::set_expression(QString expression)
+{
+       expression_ = expression;
+
+       begin_generation();
+}
+
+void MathSignal::set_error(uint8_t type, QString msg)
+{
+       error_type_ = type;
+       error_message_ = msg;
+       // TODO Emulate noquote()
+       qDebug().nospace() << name() << ": " << msg << "(Expression: '" + expression_ + "')";
+
+       error_message_changed(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 (auto input_signal : input_signals_) {
+                               const shared_ptr<SignalBase>& sb = input_signal.second.sb;
+
+                               shared_ptr<Analog> a = sb->analog_data();
+                               auto analog_segments = a->analog_segments();
+
+                               if (analog_segments.size() == 0) {
+                                       result = 0;
+                                       continue;
+                               }
+
+                               const uint32_t highest_segment_id = (analog_segments.size() - 1);
+                               if (segment_id > highest_segment_id)
+                                       continue;
+
+                               const shared_ptr<AnalogSegment> segment = analog_segments.at(segment_id);
+                               result = min(result, (int64_t)segment->get_sample_count());
+                       }
+               } else
+                       result = session_.get_segment_sample_count(segment_id);
+       }
+
+       return result;
+}
+
+void MathSignal::update_completeness(uint32_t segment_id, uint64_t output_sample_count)
+{
+       bool output_complete = true;
+
+       if (input_signals_.size() > 0) {
+               for (auto input_signal : input_signals_) {
+                       const shared_ptr<SignalBase>& sb = input_signal.second.sb;
+
+                       shared_ptr<Analog> a = sb->analog_data();
+                       auto analog_segments = a->analog_segments();
+
+                       if (analog_segments.size() == 0) {
+                               output_complete = false;
+                               continue;
+                       }
+
+                       const uint32_t highest_segment_id = (analog_segments.size() - 1);
+                       if (segment_id > highest_segment_id) {
+                               output_complete = false;
+                               continue;
+                       }
+
+                       const shared_ptr<AnalogSegment> segment = analog_segments.at(segment_id);
+                       if (!segment->is_complete()) {
+                               output_complete = false;
+                               continue;
+                       }
+
+                       if (output_sample_count < segment->get_sample_count())
+                               output_complete = false;
+               }
+       } else {
+               // We're done when we generated as many samples as the stopped session is long
+               if ((session_.get_capture_state() != Session::Stopped) ||
+                       (output_sample_count < session_.get_segment_sample_count(segment_id)))
+                       output_complete = false;
+       }
+
+       if (output_complete)
+               analog_data()->analog_segments().at(segment_id)->set_complete();
+}
+
+void MathSignal::reset_generation()
+{
+       if (gen_thread_.joinable()) {
+               gen_interrupt_ = true;
+               gen_input_cond_.notify_one();
+               gen_thread_.join();
+       }
+
+       data_->clear();
+       input_signals_.clear();
+
+       if (exprtk_parser_) {
+               delete exprtk_parser_;
+               exprtk_parser_ = nullptr;
+       }
+
+       if (exprtk_expression_) {
+               delete exprtk_expression_;
+               exprtk_expression_ = nullptr;
+       }
+
+       if (exprtk_symbol_table_) {
+               delete exprtk_symbol_table_;
+               exprtk_symbol_table_ = nullptr;
+       }
+
+       if (exprtk_unknown_symbol_table_) {
+               delete exprtk_unknown_symbol_table_;
+               exprtk_unknown_symbol_table_ = nullptr;
+       }
+
+       if (fnc_sample_) {
+               delete fnc_sample_;
+               fnc_sample_ = nullptr;
+       }
+
+       if (!error_message_.isEmpty()) {
+               error_message_.clear();
+               error_type_ = MATH_ERR_NONE;
+               // TODO Emulate noquote()
+               qDebug().nospace() << name() << ": Error cleared";
+       }
+
+       generation_chunk_size_ = ChunkLength;
+}
+
+void MathSignal::begin_generation()
+{
+       reset_generation();
+
+       if (expression_.isEmpty()) {
+               set_error(MATH_ERR_EMPTY_EXPR, tr("No expression defined, nothing to do"));
+               return;
+       }
+
+       disconnect(&session_, SIGNAL(data_received()), this, SLOT(on_data_received()));
+
+       for (const shared_ptr<SignalBase>& sb : session_.signalbases()) {
+               if (sb->analog_data())
+                       disconnect(sb->analog_data().get(), nullptr, this, SLOT(on_data_received()));
+               disconnect(sb.get(), nullptr, this, SLOT(on_enabled_changed()));
+       }
+
+       fnc_sample_ = new fnc_sample<double>(*this);
+
+       exprtk_unknown_symbol_table_ = new exprtk::symbol_table<double>();
+
+       exprtk_symbol_table_ = new exprtk::symbol_table<double>();
+       exprtk_symbol_table_->add_constant("T", 1 / session_.get_samplerate());
+       exprtk_symbol_table_->add_function("sample", *fnc_sample_);
+       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_unknown_symbol_table_);
+       exprtk_expression_->register_symbol_table(*exprtk_symbol_table_);
+
+       exprtk_parser_ = new exprtk::parser<double>();
+       exprtk_parser_->enable_unknown_symbol_resolver();
+
+       if (!exprtk_parser_->compile(expression_.toStdString(), *exprtk_expression_)) {
+               QString error_details;
+               size_t error_count = exprtk_parser_->error_count();
+
+               for (size_t i = 0; i < error_count; i++) {
+                       typedef exprtk::parser_error::type error_t;
+                       error_t error = exprtk_parser_->get_error(i);
+                       exprtk::parser_error::update_error(error, expression_.toStdString());
+
+                       QString error_detail = tr("%1 at line %2, column %3: %4");
+                       if ((error_count > 1) && (i < (error_count - 1)))
+                               error_detail += "\n";
+
+                       error_details += error_detail \
+                               .arg(exprtk::parser_error::to_str(error.mode).c_str()) \
+                               .arg(error.line_no) \
+                               .arg(error.column_no) \
+                               .arg(error.diagnostic.c_str());
+               }
+               set_error(MATH_ERR_EXPRESSION, error_details);
+       } else {
+               // Resolve unknown scalars to signals and add them to the input signal list
+               vector<string> unknowns;
+               exprtk_unknown_symbol_table_->get_variable_list(unknowns);
+               for (string& unknown : unknowns) {
+                       signal_data* sig_data = signal_from_name(unknown);
+                       const shared_ptr<SignalBase> signal = (sig_data) ? (sig_data->sb) : nullptr;
+                       if (!signal || (!signal->analog_data())) {
+                               set_error(MATH_ERR_INVALID_SIGNAL, QString(tr("\"%1\" isn't a valid analog signal")) \
+                                       .arg(QString::fromStdString(unknown)));
+                       } else
+                               sig_data->ref = &(exprtk_unknown_symbol_table_->variable_ref(unknown));
+               }
+       }
+
+       QString disabled_signals;
+       if (!all_input_signals_enabled(disabled_signals) && error_message_.isEmpty())
+               set_error(MATH_ERR_ENABLE,
+                       tr("No data will be generated as %1 must be enabled").arg(disabled_signals));
+
+       if (error_message_.isEmpty()) {
+               // Connect to the session data notification if we have no input signals
+               if (input_signals_.empty())
+                       connect(&session_, SIGNAL(data_received()),     this, SLOT(on_data_received()));
+
+               gen_interrupt_ = false;
+               gen_thread_ = std::thread(&MathSignal::generation_proc, this);
+       }
+}
+
+uint64_t MathSignal::generate_samples(uint32_t segment_id, const uint64_t start_sample,
+       const int64_t sample_count)
+{
+       uint64_t count = 0;
+
+       shared_ptr<Analog> analog = dynamic_pointer_cast<Analog>(data_);
+       shared_ptr<AnalogSegment> segment = analog->analog_segments().at(segment_id);
+
+       // Keep the math functions segment IDs in sync
+       fnc_sample_->current_segment = 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_time_ = exprtk_current_sample_ / sample_rate;
+
+               for (auto& entry : input_signals_) {
+                       signal_data* sig_data  = &(entry.second);
+                       update_signal_sample(sig_data, segment_id, exprtk_current_sample_);
+               }
+
+               double value = exprtk_expression_->value();
+               sample_data[i] = value;
+               exprtk_current_sample_ += 1;
+               count++;
+
+               // If during the evaluation of the expression it was found that this
+               // math signal itself is being accessed, the chunk size was reduced
+               // to 1, which means we must stop after this sample we just generated
+               if (generation_chunk_size_ == 1)
+                       break;
+       }
+
+       segment->append_interleaved_samples(sample_data, count, 1);
+
+       delete[] sample_data;
+
+       return count;
+}
+
+void MathSignal::generation_proc()
+{
+       // 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;
+
+       uint32_t segment_id = 0;
+       shared_ptr<Analog> analog = 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) {
+                       uint64_t processed_samples = 0;
+                       do {
+                               const uint64_t start_sample = output_sample_count + processed_samples;
+                               uint64_t sample_count =
+                                       min(samples_to_process - processed_samples,     generation_chunk_size_);
+
+                               sample_count = generate_samples(segment_id, start_sample, sample_count);
+                               processed_samples += sample_count;
+
+                               // Notify consumers of this signal's data
+                               samples_added(segment_id, start_sample, start_sample + processed_samples);
+                       } while (!gen_interrupt_ && (processed_samples < samples_to_process));
+               }
+
+               update_completeness(segment_id, output_sample_count);
+
+               if (output_segment->is_complete() && (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);
+               }
+
+               if (!gen_interrupt_ && (samples_to_process == 0)) {
+                       // Wait for more input
+                       unique_lock<mutex> gen_input_lock(input_mutex_);
+                       gen_input_cond_.wait(gen_input_lock);
+               }
+       } while (!gen_interrupt_);
+}
+
+signal_data* MathSignal::signal_from_name(const std::string& name)
+{
+       // If the expression contains the math signal itself, we must add every sample to
+       // the output segment immediately so that it can be accessed
+       const QString sig_name = QString::fromStdString(name);
+       if (sig_name == this->name())
+               generation_chunk_size_ = 1;
+
+       // Look up signal in the map and if it doesn't exist yet, add it for future use
+
+       auto element = input_signals_.find(name);
+
+       if (element != input_signals_.end()) {
+               return &(element->second);
+       } else {
+               const vector< shared_ptr<SignalBase> > signalbases = session_.signalbases();
+
+               for (const shared_ptr<SignalBase>& sb : signalbases)
+                       if (sb->name() == sig_name) {
+                               if (!sb->analog_data())
+                                       continue;
+
+                               connect(sb->analog_data().get(), SIGNAL(samples_added(SharedPtrToSegment, uint64_t, uint64_t)),
+                                       this, SLOT(on_data_received()));
+                               connect(sb->analog_data().get(), SIGNAL(segment_completed()),
+                                       this, SLOT(on_data_received()));
+
+                               connect(sb.get(), SIGNAL(enabled_changed(bool)),
+                                       this, SLOT(on_enabled_changed()));
+
+                               return &(input_signals_.insert({name, signal_data(sb)}).first->second);
+                       }
+       }
+
+       // If we reach this point, no valid signal was found with the supplied name
+       if (error_type_ == MATH_ERR_NONE)
+               set_error(MATH_ERR_INVALID_SIGNAL, QString(tr("\"%1\" isn't a valid analog signal")) \
+                       .arg(QString::fromStdString(name)));
+
+       return nullptr;
+}
+
+void MathSignal::update_signal_sample(signal_data* sig_data, uint32_t segment_id, uint64_t sample_num)
+{
+       assert(sig_data);
+
+       // Update the value only if a different sample is requested
+       if (sig_data->sample_num == sample_num)
+               return;
+
+       assert(sig_data->sb);
+       const shared_ptr<pv::data::Analog> analog = sig_data->sb->analog_data();
+       assert(analog);
+
+       assert(segment_id < analog->analog_segments().size());
+
+       const shared_ptr<AnalogSegment> segment = analog->analog_segments().at(segment_id);
+
+       sig_data->sample_num = sample_num;
+
+       if (sample_num < segment->get_sample_count())
+               sig_data->sample_value = segment->get_sample(sample_num);
+       else
+               sig_data->sample_value = 0;
+
+       // We only have a reference if this signal is used as a scalar;
+       // if it's used by a function, it's null
+       if (sig_data->ref)
+               *(sig_data->ref) = sig_data->sample_value;
+}
+
+bool MathSignal::all_input_signals_enabled(QString &disabled_signals) const
+{
+       bool all_enabled = true;
+
+       disabled_signals.clear();
+
+       for (auto input_signal : input_signals_) {
+               const shared_ptr<SignalBase>& sb = input_signal.second.sb;
+
+               if (!sb->enabled()) {
+                       all_enabled = false;
+                       disabled_signals += disabled_signals.isEmpty() ?
+                               sb->name() : ", " + sb->name();
+               }
+       }
+
+       return all_enabled;
+}
+
+void MathSignal::on_capture_state_changed(int state)
+{
+       if (state == Session::Running)
+               begin_generation();
+
+       // Make sure we don't miss any input samples, just in case
+       if (state == Session::Stopped)
+               gen_input_cond_.notify_one();
+}
+
+void MathSignal::on_data_received()
+{
+       gen_input_cond_.notify_one();
+}
+
+void MathSignal::on_enabled_changed()
+{
+       QString disabled_signals;
+       if (!all_input_signals_enabled(disabled_signals) &&
+               ((error_type_ == MATH_ERR_NONE) || (error_type_ == MATH_ERR_ENABLE)))
+               set_error(MATH_ERR_ENABLE,
+                       tr("No data will be generated as %1 must be enabled").arg(disabled_signals));
+       else if (disabled_signals.isEmpty() && (error_type_ == MATH_ERR_ENABLE)) {
+               error_type_ = MATH_ERR_NONE;
+               error_message_.clear();
+       }
+}
+
+} // namespace data
+} // namespace pv
diff --git a/pv/data/mathsignal.hpp b/pv/data/mathsignal.hpp
new file mode 100644 (file)
index 0000000..98adbe8
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_DATA_MATHSIGNAL_HPP
+#define PULSEVIEW_PV_DATA_MATHSIGNAL_HPP
+
+#define exprtk_disable_rtl_io_file /* Potential security issue, doubt anyone would use those anyway */
+#define exprtk_disable_rtl_vecops  /* Vector ops are rather useless for math channels */
+#define exprtk_disable_caseinsensitivity /* So that we can have both 't' and 'T' */
+
+#include <limits>
+
+#include <QString>
+
+#include <pv/exprtk.hpp>
+#include <pv/util.hpp>
+#include <pv/data/analog.hpp>
+#include <pv/data/signalbase.hpp>
+
+using std::atomic;
+using std::condition_variable;
+using std::mutex;
+using std::numeric_limits;
+using std::shared_ptr;
+
+namespace pv {
+class Session;
+
+namespace data {
+
+class SignalBase;
+
+template<typename T>
+struct fnc_sample;
+
+struct signal_data {
+       signal_data(const shared_ptr<SignalBase> _sb) :
+               sb(_sb), sample_num(numeric_limits<uint64_t>::max()), sample_value(0), ref(nullptr)
+       {}
+
+       const shared_ptr<SignalBase> sb;
+       uint64_t sample_num;
+       double sample_value;
+       double* ref;
+};
+
+class MathSignal : public SignalBase
+{
+       Q_OBJECT
+       Q_PROPERTY(QString expression READ get_expression WRITE set_expression NOTIFY expression_changed)
+
+private:
+       static const int64_t ChunkLength;
+
+public:
+       MathSignal(pv::Session &session);
+       virtual ~MathSignal();
+
+       virtual void save_settings(QSettings &settings) const;
+       virtual void restore_settings(QSettings &settings);
+
+       QString get_expression() const;
+       void set_expression(QString expression);
+
+private:
+       void set_error(uint8_t type, 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 update_completeness(uint32_t segment_id, uint64_t output_sample_count);
+
+       void reset_generation();
+       virtual void begin_generation();
+
+       uint64_t generate_samples(uint32_t segment_id, const uint64_t start_sample,
+               const int64_t sample_count);
+       void generation_proc();
+
+       signal_data* signal_from_name(const std::string& name);
+       void update_signal_sample(signal_data* sig_data, uint32_t segment_id, uint64_t sample_num);
+
+       bool all_input_signals_enabled(QString &disabled_signals) const;
+
+Q_SIGNALS:
+       void expression_changed(QString expression);
+
+private Q_SLOTS:
+       void on_capture_state_changed(int state);
+       void on_data_received();
+
+       void on_enabled_changed();
+
+private:
+       pv::Session &session_;
+
+       uint64_t custom_sample_rate_;
+       uint64_t custom_sample_count_;
+       bool use_custom_sample_rate_, use_custom_sample_count_;
+       uint64_t generation_chunk_size_;
+       map<std::string, signal_data> input_signals_;
+
+       QString expression_;
+
+       uint8_t error_type_;
+
+       mutable mutex input_mutex_;
+       mutable condition_variable gen_input_cond_;
+
+       std::thread gen_thread_;
+       atomic<bool> gen_interrupt_;
+
+       exprtk::symbol_table<double> *exprtk_unknown_symbol_table_, *exprtk_symbol_table_;
+       exprtk::expression<double> *exprtk_expression_;
+       exprtk::parser<double> *exprtk_parser_;
+       double exprtk_current_time_, exprtk_current_sample_;
+
+       fnc_sample<double>* fnc_sample_;
+
+       // Give sig_sample access to the private helper functions
+       friend struct fnc_sample<double>;
+};
+
+} // namespace data
+} // namespace pv
+
+#endif // PULSEVIEW_PV_DATA_MATHSIGNAL_HPP
index 2bac00621a0eb2de8dfd15c65f1cdfa5b9f22e4c..4533c9b41ef7eae4d5f092c05cb58970f8af7b8d 100644 (file)
@@ -46,7 +46,6 @@ Segment::Segment(uint32_t segment_id, uint64_t samplerate, unsigned int unit_siz
        mem_optimization_requested_(false),
        is_complete_(false)
 {
-       lock_guard<recursive_mutex> lock(mutex_);
        assert(unit_size_ > 0);
 
        // Determine the number of samples we can fit in one chunk
@@ -70,7 +69,6 @@ Segment::~Segment()
 
 uint64_t Segment::get_sample_count() const
 {
-       lock_guard<recursive_mutex> lock(mutex_);
        return sample_count_;
 }
 
@@ -102,6 +100,8 @@ uint32_t Segment::segment_id() const
 void Segment::set_complete()
 {
        is_complete_ = true;
+
+       completed();
 }
 
 bool Segment::is_complete() const
@@ -211,21 +211,34 @@ void Segment::append_samples(void* data, uint64_t samples)
        sample_count_ += samples;
 }
 
-void Segment::get_raw_samples(uint64_t start, uint64_t count,
-       uint8_t* dest) const
+const uint8_t* Segment::get_raw_sample(uint64_t sample_num) const
+{
+       assert(sample_num <= sample_count_);
+
+       uint64_t chunk_num = (sample_num * unit_size_) / chunk_size_;
+       uint64_t chunk_offs = (sample_num * unit_size_) % chunk_size_;
+
+       lock_guard<recursive_mutex> lock(mutex_);  // Because of free_unused_memory()
+
+       const uint8_t* chunk = data_chunks_[chunk_num];
+
+       return chunk + chunk_offs;
+}
+
+void Segment::get_raw_samples(uint64_t start, uint64_t count, uint8_t* dest) const
 {
        assert(start < sample_count_);
        assert(start + count <= sample_count_);
        assert(count > 0);
        assert(dest != nullptr);
 
-       lock_guard<recursive_mutex> lock(mutex_);
-
        uint8_t* dest_ptr = dest;
 
        uint64_t chunk_num = (start * unit_size_) / chunk_size_;
        uint64_t chunk_offs = (start * unit_size_) % chunk_size_;
 
+       lock_guard<recursive_mutex> lock(mutex_);  // Because of free_unused_memory()
+
        while (count > 0) {
                const uint8_t* chunk = data_chunks_[chunk_num];
 
index f5a00c9581530529044cb33d283f52cbb7c1d162..f4601d0444c0a9bab0aa88dd9efe3e1996dde9e4 100644 (file)
 
 #include "pv/util.hpp"
 
+#include <atomic>
+#include <memory>
 #include <mutex>
 #include <thread>
-#include <vector>
+#include <deque>
 
 #include <QObject>
 
+using std::atomic;
 using std::recursive_mutex;
-using std::vector;
+using std::deque;
 
 namespace SegmentTest {
 struct SmallSize8Single;
@@ -81,9 +84,13 @@ public:
 
        void free_unused_memory();
 
+Q_SIGNALS:
+       void completed();
+
 protected:
        void append_single_sample(void *data);
        void append_samples(void *data, uint64_t samples);
+       const uint8_t* get_raw_sample(uint64_t sample_num) const;
        void get_raw_samples(uint64_t start, uint64_t count, uint8_t *dest) const;
 
        SegmentDataIterator* begin_sample_iteration(uint64_t start);
@@ -94,10 +101,10 @@ protected:
 
        uint32_t segment_id_;
        mutable recursive_mutex mutex_;
-       vector<uint8_t*> data_chunks_;
+       deque<uint8_t*> data_chunks_;
        uint8_t* current_chunk_;
        uint64_t used_samples_, unused_samples_;
-       uint64_t sample_count_;
+       atomic<uint64_t> sample_count_;
        pv::util::Timestamp start_time_;
        double samplerate_;
        uint64_t chunk_size_;
@@ -121,4 +128,8 @@ protected:
 } // namespace data
 } // namespace pv
 
+typedef std::shared_ptr<pv::data::Segment> SharedPtrToSegment;
+
+Q_DECLARE_METATYPE(SharedPtrToSegment);
+
 #endif // PULSEVIEW_PV_DATA_SEGMENT_HPP
index fde99e2b4619a3cadf2b2acd01a862759c183d3f..97f705084e91fc654c85ab7ce07cbfec64a3df50 100644 (file)
@@ -28,8 +28,9 @@
 
 #include <QDebug>
 
-#include <pv/binding/decoder.hpp>
+#include <extdef.h>
 #include <pv/session.hpp>
+#include <pv/binding/decoder.hpp>
 
 using std::dynamic_pointer_cast;
 using std::make_shared;
@@ -41,24 +42,107 @@ using std::unique_lock;
 namespace pv {
 namespace data {
 
+const QColor SignalBase::AnalogSignalColors[8] =
+{
+       QColor(0xC4, 0xA0, 0x00),       // Yellow   HSV:  49 / 100 / 77
+       QColor(0x87, 0x20, 0x7A),       // Magenta  HSV: 308 /  70 / 53
+       QColor(0x20, 0x4A, 0x87),       // Blue     HSV: 216 /  76 / 53
+       QColor(0x4E, 0x9A, 0x06),       // Green    HSV:  91 /  96 / 60
+       QColor(0xBF, 0x6E, 0x00),       // Orange   HSV:  35 / 100 / 75
+       QColor(0x5E, 0x20, 0x80),       // Purple   HSV: 280 /  75 / 50
+       QColor(0x20, 0x80, 0x7A),       // Turqoise HSV: 177 /  75 / 50
+       QColor(0x80, 0x20, 0x24)        // Red      HSV: 358 /  75 / 50
+};
+
+const QColor SignalBase::LogicSignalColors[10] =
+{
+       QColor(0x16, 0x19, 0x1A),       // Black
+       QColor(0x8F, 0x52, 0x02),       // Brown
+       QColor(0xCC, 0x00, 0x00),       // Red
+       QColor(0xF5, 0x79, 0x00),       // Orange
+       QColor(0xED, 0xD4, 0x00),       // Yellow
+       QColor(0x73, 0xD2, 0x16),       // Green
+       QColor(0x34, 0x65, 0xA4),       // Blue
+       QColor(0x75, 0x50, 0x7B),       // Violet
+       QColor(0x88, 0x8A, 0x85),       // Grey
+       QColor(0xEE, 0xEE, 0xEC),       // White
+};
+
+
 const int SignalBase::ColorBGAlpha = 8 * 256 / 100;
 const uint64_t SignalBase::ConversionBlockSize = 4096;
 const uint32_t SignalBase::ConversionDelay = 1000;  // 1 second
 
+
+SignalGroup::SignalGroup(const QString& name)
+{
+       name_ = name;
+}
+
+void SignalGroup::append_signal(shared_ptr<SignalBase> signal)
+{
+       if (!signal)
+               return;
+
+       signals_.push_back(signal);
+       signal->set_group(this);
+}
+
+void SignalGroup::remove_signal(shared_ptr<SignalBase> signal)
+{
+       if (!signal)
+               return;
+
+       signals_.erase(std::remove_if(signals_.begin(), signals_.end(),
+               [&](shared_ptr<SignalBase> s) { return s == signal; }),
+               signals_.end());
+}
+
+deque<shared_ptr<SignalBase>> SignalGroup::signals() const
+{
+       return signals_;
+}
+
+void SignalGroup::clear()
+{
+       for (shared_ptr<SignalBase> sb : signals_)
+               sb->set_group(nullptr);
+
+       signals_.clear();
+}
+
+const QString SignalGroup::name() const
+{
+       return name_;
+}
+
+
 SignalBase::SignalBase(shared_ptr<sigrok::Channel> channel, ChannelType channel_type) :
        channel_(channel),
        channel_type_(channel_type),
+       group_(nullptr),
        conversion_type_(NoConversion),
        min_value_(0),
-       max_value_(0)
+       max_value_(0),
+       index_(0),
+       error_message_("")
 {
-       if (channel_)
-               internal_name_ = QString::fromStdString(channel_->name());
+       if (channel_) {
+               set_internal_name(QString::fromStdString(channel_->name()));
+               set_index(channel_->index());
+       }
 
        connect(&delayed_conversion_starter_, SIGNAL(timeout()),
                this, SLOT(on_delayed_conversion_start()));
        delayed_conversion_starter_.setSingleShot(true);
        delayed_conversion_starter_.setInterval(ConversionDelay);
+
+       // Only logic and analog SR channels can have their colors auto-set
+       // because for them, we have an index that can be used
+       if (channel_type == LogicChannel)
+               set_color(LogicSignalColors[index() % countof(LogicSignalColors)]);
+       else if (channel_type == AnalogChannel)
+               set_color(AnalogSignalColors[index() % countof(AnalogSignalColors)]);
 }
 
 SignalBase::~SignalBase()
@@ -71,32 +155,10 @@ shared_ptr<sigrok::Channel> SignalBase::channel() const
        return channel_;
 }
 
-QString SignalBase::name() const
-{
-       return (channel_) ? QString::fromStdString(channel_->name()) : name_;
-}
-
-QString SignalBase::internal_name() const
-{
-       return internal_name_;
-}
-
-QString SignalBase::display_name() const
+bool SignalBase::is_generated() const
 {
-       if ((name() != internal_name_) && (!internal_name_.isEmpty()))
-               return name() + " (" + internal_name_ + ")";
-       else
-               return name();
-}
-
-void SignalBase::set_name(QString name)
-{
-       if (channel_)
-               channel_->set_name(name.toUtf8().constData());
-
-       name_ = name;
-
-       name_changed(name);
+       // Only signals associated with a device have a corresponding sigrok channel
+       return channel_ == nullptr;
 }
 
 bool SignalBase::enabled() const
@@ -119,17 +181,68 @@ SignalBase::ChannelType SignalBase::type() const
 
 unsigned int SignalBase::index() const
 {
-       return (channel_) ? channel_->index() : 0;
+       return index_;
+}
+
+void SignalBase::set_index(unsigned int index)
+{
+       index_ = index;
 }
 
 unsigned int SignalBase::logic_bit_index() const
 {
        if (channel_type_ == LogicChannel)
-               return channel_->index();
+               return index_;
        else
                return 0;
 }
 
+void SignalBase::set_group(SignalGroup* group)
+{
+       group_ = group;
+}
+
+SignalGroup* SignalBase::group() const
+{
+       return group_;
+}
+
+QString SignalBase::name() const
+{
+       return (channel_) ? QString::fromStdString(channel_->name()) : name_;
+}
+
+QString SignalBase::internal_name() const
+{
+       return internal_name_;
+}
+
+void SignalBase::set_internal_name(QString internal_name)
+{
+       internal_name_ = internal_name;
+
+       // Use this name also for the QObject instance
+       setObjectName(internal_name);
+}
+
+QString SignalBase::display_name() const
+{
+       if ((name() != internal_name_) && (!internal_name_.isEmpty()))
+               return name() + " (" + internal_name_ + ")";
+       else
+               return name();
+}
+
+void SignalBase::set_name(QString name)
+{
+       if (channel_)
+               channel_->set_name(name.toUtf8().constData());
+
+       name_ = name;
+
+       name_changed(name);
+}
+
 QColor SignalBase::color() const
 {
        return color_;
@@ -150,21 +263,23 @@ QColor SignalBase::bgcolor() const
        return bgcolor_;
 }
 
+QString SignalBase::get_error_message() const
+{
+       return error_message_;
+}
+
 void SignalBase::set_data(shared_ptr<pv::data::SignalData> data)
 {
        if (data_) {
                disconnect(data.get(), SIGNAL(samples_cleared()),
                        this, SLOT(on_samples_cleared()));
-               disconnect(data.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)),
-                       this, SLOT(on_samples_added(QObject*, uint64_t, uint64_t)));
-
-               if (channel_type_ == AnalogChannel) {
-                       shared_ptr<Analog> analog = analog_data();
-                       assert(analog);
+               disconnect(data.get(), SIGNAL(samples_added(shared_ptr<Segment>, uint64_t, uint64_t)),
+                       this, SLOT(on_samples_added(shared_ptr<Segment>, uint64_t, uint64_t)));
 
+               shared_ptr<Analog> analog = analog_data();
+               if (analog)
                        disconnect(analog.get(), SIGNAL(min_max_changed(float, float)),
                                this, SLOT(on_min_max_changed(float, float)));
-               }
        }
 
        data_ = data;
@@ -172,66 +287,77 @@ void SignalBase::set_data(shared_ptr<pv::data::SignalData> data)
        if (data_) {
                connect(data.get(), SIGNAL(samples_cleared()),
                        this, SLOT(on_samples_cleared()));
-               connect(data.get(), SIGNAL(samples_added(QObject*, uint64_t, uint64_t)),
-                       this, SLOT(on_samples_added(QObject*, uint64_t, uint64_t)));
-
-               if (channel_type_ == AnalogChannel) {
-                       shared_ptr<Analog> analog = analog_data();
-                       assert(analog);
+               connect(data.get(), SIGNAL(samples_added(SharedPtrToSegment, uint64_t, uint64_t)),
+                       this, SLOT(on_samples_added(SharedPtrToSegment, uint64_t, uint64_t)));
 
+               shared_ptr<Analog> analog = analog_data();
+               if (analog)
                        connect(analog.get(), SIGNAL(min_max_changed(float, float)),
                                this, SLOT(on_min_max_changed(float, float)));
-               }
        }
 }
 
-shared_ptr<data::Analog> SignalBase::analog_data() const
+void SignalBase::clear_sample_data()
 {
-       shared_ptr<Analog> result = nullptr;
+       if (analog_data())
+               analog_data()->clear();
 
-       if (channel_type_ == AnalogChannel)
-               result = dynamic_pointer_cast<Analog>(data_);
+       if (logic_data())
+               logic_data()->clear();
+}
 
-       return result;
+shared_ptr<data::Analog> SignalBase::analog_data() const
+{
+       if (!data_)
+               return nullptr;
+
+       return dynamic_pointer_cast<Analog>(data_);
 }
 
 shared_ptr<data::Logic> SignalBase::logic_data() const
 {
-       shared_ptr<Logic> result = nullptr;
+       if (!data_)
+               return nullptr;
 
-       if (channel_type_ == LogicChannel)
-               result = dynamic_pointer_cast<Logic>(data_);
+       shared_ptr<Logic> result;
 
        if (((conversion_type_ == A2LConversionByThreshold) ||
                (conversion_type_ == A2LConversionBySchmittTrigger)))
                result = dynamic_pointer_cast<Logic>(converted_data_);
+       else
+               result = dynamic_pointer_cast<Logic>(data_);
 
        return result;
 }
 
+shared_ptr<pv::data::SignalData> SignalBase::data() const
+{
+       return data_;
+}
+
 bool SignalBase::segment_is_complete(uint32_t segment_id) const
 {
        bool result = true;
 
-       if (channel_type_ == AnalogChannel)
+       shared_ptr<Analog> adata = analog_data();
+       if (adata)
        {
-               shared_ptr<Analog> data = dynamic_pointer_cast<Analog>(data_);
-               auto segments = data->analog_segments();
+               auto segments = adata->analog_segments();
                try {
                        result = segments.at(segment_id)->is_complete();
                } catch (out_of_range&) {
                        // Do nothing
                }
-       }
-
-       if (channel_type_ == LogicChannel)
-       {
-               shared_ptr<Logic> data = dynamic_pointer_cast<Logic>(data_);
-               auto segments = data->logic_segments();
-               try {
-                       result = segments.at(segment_id)->is_complete();
-               } catch (out_of_range&) {
-                       // Do nothing
+       } else {
+               shared_ptr<Logic> ldata = logic_data();
+               if (ldata) {
+                       shared_ptr<Logic> data = dynamic_pointer_cast<Logic>(data_);
+                       auto segments = data->logic_segments();
+                       try {
+                               result = segments.at(segment_id)->is_complete();
+                       } catch (out_of_range&) {
+                               // Do nothing
+                       }
                }
        }
 
@@ -242,21 +368,16 @@ bool SignalBase::has_samples() const
 {
        bool result = false;
 
-       if (channel_type_ == AnalogChannel)
+       shared_ptr<Analog> adata = analog_data();
+       if (adata)
        {
-               shared_ptr<Analog> data = dynamic_pointer_cast<Analog>(data_);
-               if (data) {
-                       auto segments = data->analog_segments();
-                       if ((segments.size() > 0) && (segments.front()->get_sample_count() > 0))
-                               result = true;
-               }
-       }
-
-       if (channel_type_ == LogicChannel)
-       {
-               shared_ptr<Logic> data = dynamic_pointer_cast<Logic>(data_);
-               if (data) {
-                       auto segments = data->logic_segments();
+               auto segments = adata->analog_segments();
+               if ((segments.size() > 0) && (segments.front()->get_sample_count() > 0))
+                       result = true;
+       } else {
+               shared_ptr<Logic> ldata = logic_data();
+               if (ldata) {
+                       auto segments = ldata->logic_segments();
                        if ((segments.size() > 0) && (segments.front()->get_sample_count() > 0))
                                result = true;
                }
@@ -267,18 +388,13 @@ bool SignalBase::has_samples() const
 
 double SignalBase::get_samplerate() const
 {
-       if (channel_type_ == AnalogChannel)
-       {
-               shared_ptr<Analog> data = dynamic_pointer_cast<Analog>(data_);
-               if (data)
-                       return data->get_samplerate();
-       }
-
-       if (channel_type_ == LogicChannel)
-       {
-               shared_ptr<Logic> data = dynamic_pointer_cast<Logic>(data_);
-               if (data)
-                       return data->get_samplerate();
+       shared_ptr<Analog> adata = analog_data();
+       if (adata)
+               return adata->get_samplerate();
+       else {
+               shared_ptr<Logic> ldata = logic_data();
+               if (ldata)
+                       return ldata->get_samplerate();
        }
 
        // Default samplerate is 1 Hz
@@ -474,7 +590,12 @@ void SignalBase::restore_settings(QSettings &settings)
                QVariant value = settings.value("color");
 
                // Workaround for Qt QColor serialization bug on OSX
-               if ((QMetaType::Type)(value.type()) == QMetaType::QColor)
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               bool is_qcolor = (QMetaType::Type)(value.typeId()) == QMetaType::QColor;
+#else
+               bool is_qcolor = (QMetaType::Type)(value.type()) == QMetaType::QColor;
+#endif
+               if (is_qcolor)
                        set_color(value.value<QColor>());
                else
                        set_color(QColor::fromRgba(value.value<uint32_t>()));
@@ -504,23 +625,25 @@ void SignalBase::restore_settings(QSettings &settings)
 
 bool SignalBase::conversion_is_a2l() const
 {
-       return ((channel_type_ == AnalogChannel) &&
-               ((conversion_type_ == A2LConversionByThreshold) ||
+       return (((conversion_type_ == A2LConversionByThreshold) ||
                (conversion_type_ == A2LConversionBySchmittTrigger)));
 }
 
-void SignalBase::convert_single_segment_range(AnalogSegment *asegment,
-       LogicSegment *lsegment, uint64_t start_sample, uint64_t end_sample)
+void SignalBase::convert_single_segment_range(shared_ptr<AnalogSegment> asegment,
+       shared_ptr<LogicSegment> lsegment, uint64_t start_sample, uint64_t end_sample)
 {
        if (end_sample > start_sample) {
                tie(min_value_, max_value_) = asegment->get_min_max();
 
                // Create sigrok::Analog instance
                float *asamples = new float[ConversionBlockSize];
+               assert(asamples);
                uint8_t *lsamples = new uint8_t[ConversionBlockSize];
+               assert(lsamples);
 
                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;
@@ -581,6 +704,7 @@ void SignalBase::convert_single_segment_range(AnalogSegment *asegment,
 
                                lsegment->append_payload(logic->data_pointer(), logic->data_length());
                                samples_added(lsegment->segment_id(), i, i + ConversionBlockSize);
+
                                i += ConversionBlockSize;
                        }
 
@@ -604,9 +728,12 @@ void SignalBase::convert_single_segment_range(AnalogSegment *asegment,
                delete[] lsamples;
                delete[] asamples;
        }
+
+       samples_added(lsegment->segment_id(), start_sample, end_sample);
 }
 
-void SignalBase::convert_single_segment(AnalogSegment *asegment, LogicSegment *lsegment)
+void SignalBase::convert_single_segment(shared_ptr<AnalogSegment> asegment,
+       shared_ptr<LogicSegment> lsegment)
 {
        uint64_t start_sample, end_sample, old_end_sample;
        start_sample = end_sample = 0;
@@ -636,6 +763,9 @@ void SignalBase::convert_single_segment(AnalogSegment *asegment, LogicSegment *l
                // we do another round of sample conversion.
        } while ((complete_state != old_complete_state) ||
                (end_sample - old_end_sample >= ConversionBlockSize));
+
+       if (complete_state)
+               lsegment->set_complete();
 }
 
 void SignalBase::conversion_thread_proc()
@@ -660,8 +790,9 @@ void SignalBase::conversion_thread_proc()
 
        uint32_t segment_id = 0;
 
-       AnalogSegment *asegment = analog_data->analog_segments().front().get();
+       shared_ptr<AnalogSegment> asegment = analog_data->analog_segments().front();
        assert(asegment);
+       connect(asegment.get(), SIGNAL(completed()), this, SLOT(on_input_segment_completed()));
 
        const shared_ptr<Logic> logic_data = dynamic_pointer_cast<Logic>(converted_data_);
        assert(logic_data);
@@ -673,7 +804,7 @@ void SignalBase::conversion_thread_proc()
                logic_data->push_segment(new_segment);
        }
 
-       LogicSegment *lsegment = logic_data->logic_segments().front().get();
+       shared_ptr<LogicSegment> lsegment = logic_data->logic_segments().front();
        assert(lsegment);
 
        do {
@@ -682,11 +813,16 @@ void SignalBase::conversion_thread_proc()
                // Only advance to next segment if the current input segment is complete
                if (asegment->is_complete() &&
                        analog_data->analog_segments().size() > logic_data->logic_segments().size()) {
+
+                       disconnect(asegment.get(), SIGNAL(completed()), this, SLOT(on_input_segment_completed()));
+
                        // There are more segments to process
                        segment_id++;
 
                        try {
-                               asegment = analog_data->analog_segments().at(segment_id).get();
+                               asegment = analog_data->analog_segments().at(segment_id);
+                               disconnect(asegment.get(), SIGNAL(completed()), this, SLOT(on_input_segment_completed()));
+                               connect(asegment.get(), SIGNAL(completed()), this, SLOT(on_input_segment_completed()));
                        } catch (out_of_range&) {
                                qDebug() << "Conversion error for" << name() << ": no analog segment" \
                                        << segment_id << ", segments size is" << analog_data->analog_segments().size();
@@ -697,7 +833,7 @@ void SignalBase::conversion_thread_proc()
                                *logic_data.get(), segment_id, 1, asegment->samplerate());
                        logic_data->push_segment(new_segment);
 
-                       lsegment = logic_data->logic_segments().back().get();
+                       lsegment = logic_data->logic_segments().back();
                } else {
                        // No more samples/segments to process, wait for data or interrupt
                        if (!conversion_interrupt_) {
@@ -706,6 +842,8 @@ void SignalBase::conversion_thread_proc()
                        }
                }
        } while (!conversion_interrupt_);
+
+       disconnect(asegment.get(), SIGNAL(completed()), this, SLOT(on_input_segment_completed()));
 }
 
 void SignalBase::start_conversion(bool delayed_start)
@@ -717,13 +855,22 @@ void SignalBase::start_conversion(bool delayed_start)
 
        stop_conversion();
 
-       if (converted_data_)
+       if (converted_data_ && (converted_data_->get_segment_count() > 0)) {
                converted_data_->clear();
-       samples_cleared();
+               samples_cleared();
+       }
 
        conversion_interrupt_ = false;
-       conversion_thread_ = std::thread(
-               &SignalBase::conversion_thread_proc, this);
+       conversion_thread_ = std::thread(&SignalBase::conversion_thread_proc, this);
+}
+
+void SignalBase::set_error_message(QString msg)
+{
+       error_message_ = msg;
+       // TODO Emulate noquote()
+       qDebug().nospace() << name() << ": " << msg;
+
+       error_message_changed(msg);
 }
 
 void SignalBase::stop_conversion()
@@ -737,13 +884,13 @@ void SignalBase::stop_conversion()
 
 void SignalBase::on_samples_cleared()
 {
-       if (converted_data_)
+       if (converted_data_ && (converted_data_->get_segment_count() > 0)) {
                converted_data_->clear();
-
-       samples_cleared();
+               samples_cleared();
+       }
 }
 
-void SignalBase::on_samples_added(QObject* segment, uint64_t start_sample,
+void SignalBase::on_samples_added(SharedPtrToSegment segment, uint64_t start_sample,
        uint64_t end_sample)
 {
        if (conversion_type_ != NoConversion) {
@@ -757,8 +904,16 @@ void SignalBase::on_samples_added(QObject* segment, uint64_t start_sample,
                }
        }
 
-       data::Segment* s = qobject_cast<data::Segment*>(segment);
-       samples_added(s->segment_id(), start_sample, end_sample);
+       samples_added(segment->segment_id(), start_sample, end_sample);
+}
+
+void SignalBase::on_input_segment_completed()
+{
+       if (conversion_type_ != NoConversion)
+               if (conversion_thread_.joinable()) {
+                       // Notify the conversion thread since it's running
+                       conversion_input_cond_.notify_one();
+               }
 }
 
 void SignalBase::on_min_max_changed(float min, float max)
index c3e0d3d64ef56d02b289451c7cb3d54ca89c44f9..19f8143dc77e3499a92ecb0d17fb07fe411605be 100644 (file)
@@ -22,6 +22,7 @@
 #define PULSEVIEW_PV_DATA_SIGNALBASE_HPP
 
 #include <atomic>
+#include <deque>
 #include <condition_variable>
 #include <thread>
 #include <vector>
 
 #include <libsigrokcxx/libsigrokcxx.hpp>
 
+#include "segment.hpp"
+
 using std::atomic;
 using std::condition_variable;
+using std::deque;
+using std::enable_shared_from_this;
 using std::map;
 using std::mutex;
 using std::pair;
@@ -55,12 +60,35 @@ class AnalogSegment;
 class DecoderStack;
 class Logic;
 class LogicSegment;
+class Segment;
+class SignalBase;
 class SignalData;
 
-class SignalBase : public QObject
+class SignalGroup : public QObject
 {
        Q_OBJECT
 
+public:
+       SignalGroup(const QString& name);
+
+       void append_signal(shared_ptr<SignalBase> signal);
+       void remove_signal(shared_ptr<SignalBase> signal);
+       deque<shared_ptr<SignalBase>> signals() const;
+       void clear();
+
+       const QString name() const;
+
+private:
+       deque<shared_ptr<SignalBase>> signals_;
+       QString name_;
+};
+
+
+class SignalBase : public QObject, public enable_shared_from_this<SignalBase>
+{
+       Q_OBJECT
+       Q_PROPERTY(QString error_message READ get_error_message NOTIFY error_message_changed)
+
 public:
        enum ChannelType {
                AnalogChannel = 1, ///< Analog data
@@ -85,6 +113,9 @@ public:
                DynamicPreset = 0  ///< Conversion uses calculated values
        };
 
+       static const QColor AnalogSignalColors[8];
+       static const QColor LogicSignalColors[10];
+
 private:
        static const int ColorBGAlpha;
        static const uint64_t ConversionBlockSize;
@@ -97,9 +128,15 @@ public:
 public:
        /**
         * Returns the underlying SR channel.
+        * Generated channels don't have a SR channel.
         */
        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.
         */
@@ -122,6 +159,13 @@ public:
         */
        unsigned int index() const;
 
+       /**
+        * Sets the index number of this channel, i.e. a unique ID assigned by
+        * the device driver or the logic bit index (see below).
+        * Only use immediately after creating the signal and leave it untouched after.
+        */
+       void set_index(unsigned int index);
+
        /**
         * Returns which bit of a given sample for this signal represents the
         * signal itself. This is relevant for compound signals like logic,
@@ -130,16 +174,32 @@ public:
         */
        unsigned int logic_bit_index() const;
 
+       /**
+        * Sets the signal group this signal belongs to
+        */
+       void set_group(SignalGroup* group);
+
+       /**
+        * Returns the signal group this signal belongs to or nullptr if none
+        */
+       SignalGroup* group() const;
+
        /**
         * Gets the name of this signal.
         */
        QString name() const;
 
        /**
-        * Gets the internal name of this signal, i.e. how the device calls it.
+        * Gets the internal name of this signal, i.e. how the device/generator calls it.
         */
        QString internal_name() const;
 
+       /**
+        * Sets the internal name of this signal, i.e. how the device/generator calls it.
+        * Only use immediately after creating the signal and leave it untouched after.
+        */
+       void set_internal_name(QString internal_name);
+
        /**
         * Produces a string for this signal that can be used for display,
         * i.e. it contains one or both of the signal/internal names.
@@ -159,18 +219,28 @@ public:
        /**
         * Set the color of the signal.
         */
-       void set_color(QColor color);
+       virtual void set_color(QColor color);
 
        /**
         * Get the background color of the signal.
         */
        QColor bgcolor() const;
 
+       /**
+        * Returns the current error message text.
+        */
+       virtual QString get_error_message() const;
+
        /**
         * Sets the internal data object.
         */
        void set_data(shared_ptr<pv::data::SignalData> data);
 
+       /**
+        * Clears all sample data and removes all associated segments.
+        */
+       void clear_sample_data();
+
        /**
         * Get the internal data as analog data object in case of analog type.
         */
@@ -181,6 +251,11 @@ public:
         */
        shared_ptr<pv::data::Logic> logic_data() const;
 
+       /**
+        * Get the primary internal data object, i.e. the data that was acquired from the device.
+        */
+       shared_ptr<pv::data::SignalData> data() const;
+
        /**
         * Determines whether a given segment is complete (i.e. end-of-frame has
         * been seen). It only considers the original data, not the converted data.
@@ -195,7 +270,7 @@ public:
        /**
         * Returns the sample rate for this signal.
         */
-       double get_samplerate() const;
+       virtual double get_samplerate() const;
 
        /**
         * Queries the kind of conversion performed on this channel.
@@ -278,37 +353,36 @@ public:
 #endif
 
        virtual void save_settings(QSettings &settings) const;
-
        virtual void restore_settings(QSettings &settings);
 
        void start_conversion(bool delayed_start=false);
 
+protected:
+       virtual void set_error_message(QString msg);
+
 private:
+       void stop_conversion();
+
        bool conversion_is_a2l() const;
 
        uint8_t convert_a2l_threshold(float threshold, float value);
        uint8_t convert_a2l_schmitt_trigger(float lo_thr, float hi_thr,
                float value, uint8_t &state);
 
-       void convert_single_segment_range(AnalogSegment *asegment,
-               LogicSegment *lsegment, uint64_t start_sample, uint64_t end_sample);
-       void convert_single_segment(pv::data::AnalogSegment *asegment,
-               pv::data::LogicSegment *lsegment);
+       void convert_single_segment_range(shared_ptr<AnalogSegment> asegment,
+               shared_ptr<LogicSegment> lsegment, uint64_t start_sample, uint64_t end_sample);
+       void convert_single_segment(shared_ptr<AnalogSegment> asegment,
+               shared_ptr<LogicSegment> lsegment);
        void conversion_thread_proc();
 
-       void stop_conversion();
-
 Q_SIGNALS:
        void enabled_changed(const bool &value);
-
        void name_changed(const QString &name);
-
        void color_changed(const QColor &color);
-
+       void error_message_changed(QString msg);
        void conversion_type_changed(const ConversionType t);
 
        void samples_cleared();
-
        void samples_added(uint64_t segment_id, uint64_t start_sample,
                uint64_t end_sample);
 
@@ -317,9 +391,11 @@ Q_SIGNALS:
 private Q_SLOTS:
        void on_samples_cleared();
 
-       void on_samples_added(QObject* segment, uint64_t start_sample,
+       void on_samples_added(SharedPtrToSegment segment, uint64_t start_sample,
                uint64_t end_sample);
 
+       void on_input_segment_completed();
+
        void on_min_max_changed(float min, float max);
 
        void on_capture_state_changed(int state);
@@ -329,6 +405,7 @@ private Q_SLOTS:
 protected:
        shared_ptr<sigrok::Channel> channel_;
        ChannelType channel_type_;
+       SignalGroup* group_;
        shared_ptr<pv::data::SignalData> data_;
        shared_ptr<pv::data::SignalData> converted_data_;
        ConversionType conversion_type_;
@@ -344,9 +421,14 @@ protected:
 
        QString internal_name_, name_;
        QColor color_, bgcolor_;
+       unsigned int index_;
+
+       QString error_message_;
 };
 
 } // namespace data
 } // namespace pv
 
+Q_DECLARE_METATYPE(shared_ptr<pv::data::SignalBase>);
+
 #endif // PULSEVIEW_PV_DATA_SIGNALBASE_HPP
index 5168fee8e4b58fb6c75dec7afe26364a4709d47a..7a9d6d39b96bada864b2f46017c802587dedf37e 100644 (file)
@@ -51,7 +51,12 @@ public:
 
        virtual uint64_t max_sample_count() const = 0;
 
+       virtual void set_samplerate(double value) = 0;
+
        virtual double get_samplerate() const = 0;
+
+Q_SIGNALS:
+       void segment_completed();
 };
 
 } // namespace data
index 94fb9c82cb6d4df2e2a47d261ff8220bb471ad3e..9cc99f4ec8060abe8a46af815024017b38bc7628 100644 (file)
@@ -101,7 +101,7 @@ void InputFile::save_meta_to_settings(QSettings &settings)
        settings.setValue("options", (int)options_.size());
 
        int i = 0;
-       for (const pair<string, Glib::VariantBase>& option : options_) {
+       for (const pair<const string, Glib::VariantBase>& option : options_) {
                settings.beginGroup("option" + QString::number(i));
                settings.setValue("name", QString::fromStdString(option.first));
                GlobalSettings::store_variantbase(settings, option.second);
index e08832f608d6247e9febce369f32c3b8aea843e5..165010f717f3cffe067eac15c7a50bcd11d4aa1f 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_DEVICE_INPUTFILE_HPP
-#define PULSEVIEW_PV_DEVICE_INPUTFILE_HPP
+#ifndef PULSEVIEW_PV_DEVICES_INPUTFILE_HPP
+#define PULSEVIEW_PV_DEVICES_INPUTFILE_HPP
 
 #include <atomic>
 
@@ -81,5 +81,5 @@ private:
 } // namespace devices
 } // namespace pv
 
-#endif // PULSEVIEW_PV_SESSIONS_INPUTFILE_HPP
+#endif // PULSEVIEW_PV_DEVICES_INPUTFILE_HPP
 
index d238d681097bc5fcfd87c2351e87949f09dc16de..be418df29cc24ddac4ab050855ab168dbb30f59e 100644 (file)
@@ -188,7 +188,7 @@ void Connect::populate_drivers()
                if (supported_device)
                        drivers_.addItem(QString("%1 (%2)").arg(
                                driver->long_name().c_str(), name.c_str()),
-                               qVariantFromValue(driver));
+                               QVariant::fromValue(driver));
        }
 }
 
@@ -276,7 +276,7 @@ void Connect::scan_pressed()
                text += QString(" with %1 channels").arg(device->device()->channels().size());
 
                QListWidgetItem *const item = new QListWidgetItem(text, &device_list_);
-               item->setData(Qt::UserRole, qVariantFromValue(device));
+               item->setData(Qt::UserRole, QVariant::fromValue(device));
                device_list_.addItem(item);
        }
 
index f34268ddb6fde5a383cd56e42dd165da04b497e7..fe158740760c2e3964350b9990a95146ec81f86a 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_CONNECT_HPP
-#define PULSEVIEW_PV_CONNECT_HPP
+#ifndef PULSEVIEW_PV_DIALOGS_CONNECT_HPP
+#define PULSEVIEW_PV_DIALOGS_CONNECT_HPP
 
 #include <memory>
 
@@ -106,4 +106,4 @@ private:
 } // namespace dialogs
 } // namespace pv
 
-#endif // PULSEVIEW_PV_CONNECT_HPP
+#endif // PULSEVIEW_PV_DIALOGS_CONNECT_HPP
index be6d33080f699a245355ebc92ee7b493aade746d..11c905a4ca61eef498b597f9f2afaf1724cb955b 100644 (file)
@@ -70,4 +70,4 @@ private:
 } // namespace dialogs
 } // namespace pv
 
-#endif // PULSEVIEW_PV_INPUTOUTPUTOPTIONS_HPP
+#endif // PULSEVIEW_PV_DIALOGS_INPUTOUTPUTOPTIONS_HPP
index 1abbd85c03e28d775e571bad73b99ede8da488c1..55b7e826291eb69f057025582febef7b65c319be 100644 (file)
@@ -84,7 +84,7 @@ public:
 };
 
 Settings::Settings(DeviceManager &device_manager, QWidget *parent) :
-       QDialog(parent, nullptr),
+       QDialog(parent),
        device_manager_(device_manager)
 {
        resize(600, 400);
@@ -220,10 +220,10 @@ QWidget *Settings::get_general_settings_form(QWidget *parent) const
        QComboBox *language_cb = new QComboBox();
        Application* a = qobject_cast<Application*>(QApplication::instance());
 
-       QString current_language = settings.value(GlobalSettings::Key_General_Language).toString();
-       for (QString language : a->get_languages()) {
-               QLocale locale = QLocale(language);
-               QString desc = locale.languageToString(locale.language());
+       QString current_language = settings.value(GlobalSettings::Key_General_Language, "en").toString();
+       for (const QString& language : a->get_languages()) {
+               const QLocale locale = QLocale(language);
+               const QString desc = locale.languageToString(locale.language());
                language_cb->addItem(desc, language);
 
                if (language == current_language) {
@@ -231,8 +231,13 @@ QWidget *Settings::get_general_settings_form(QWidget *parent) const
                        language_cb->setCurrentIndex(index);
                }
        }
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(language_cb, SIGNAL(currentTextChanged(const QString&)),
+               this, SLOT(on_general_language_changed(const QString&)));
+#else
        connect(language_cb, SIGNAL(currentIndexChanged(const QString&)),
                this, SLOT(on_general_language_changed(const QString&)));
+#endif
        general_layout->addRow(tr("User interface language"), language_cb);
 
        // Theme combobox
@@ -261,7 +266,7 @@ QWidget *Settings::get_general_settings_form(QWidget *parent) const
        if (current_style.isEmpty())
                style_cb->setCurrentIndex(0);
        else
-               style_cb->setCurrentIndex(style_cb->findText(current_style, nullptr));
+               style_cb->setCurrentIndex(style_cb->findText(current_style));
 
        connect(style_cb, SIGNAL(currentIndexChanged(int)),
                this, SLOT(on_general_style_changed(int)));
@@ -276,6 +281,11 @@ QWidget *Settings::get_general_settings_form(QWidget *parent) const
                SLOT(on_general_save_with_setup_changed(int)));
        general_layout->addRow(tr("Save session &setup along with .sr file"), cb);
 
+       cb = create_checkbox(GlobalSettings::Key_General_StartAllSessions,
+               SLOT(on_general_start_all_sessions_changed(int)));
+       general_layout->addRow(tr("Start acquisition for all open sessions when clicking 'Run'"), cb);
+
+
        return form;
 }
 
@@ -308,19 +318,23 @@ QWidget *Settings::get_view_settings_form(QWidget *parent) const
 
        cb = create_checkbox(GlobalSettings::Key_View_TriggerIsZeroTime,
                SLOT(on_view_triggerIsZero_changed(int)));
-       trace_view_layout->addRow(tr("Show time zero at the trigger"), cb);
+       trace_view_layout->addRow(tr("Show time zero at the &trigger"), cb);
 
        cb = create_checkbox(GlobalSettings::Key_View_StickyScrolling,
                SLOT(on_view_stickyScrolling_changed(int)));
        trace_view_layout->addRow(tr("Always keep &newest samples at the right edge during capture"), cb);
 
+       cb = create_checkbox(GlobalSettings::Key_View_AllowVerticalDragging,
+               SLOT(on_view_allowVerticalDragging_changed(int)));
+       trace_view_layout->addRow(tr("Allow &vertical dragging in the view area"), cb);
+
        cb = create_checkbox(GlobalSettings::Key_View_ShowSamplingPoints,
                SLOT(on_view_showSamplingPoints_changed(int)));
        trace_view_layout->addRow(tr("Show data &sampling points"), cb);
 
        cb = create_checkbox(GlobalSettings::Key_View_FillSignalHighAreas,
                SLOT(on_view_fillSignalHighAreas_changed(int)));
-       trace_view_layout->addRow(tr("Fill high areas of logic signals"), cb);
+       trace_view_layout->addRow(tr("Fill &high areas of logic signals"), cb);
 
        ColorButton* high_fill_cb = new ColorButton(parent);
        high_fill_cb->set_color(QColor::fromRgba(
@@ -337,6 +351,10 @@ QWidget *Settings::get_view_settings_form(QWidget *parent) const
                SLOT(on_view_showHoverMarker_changed(int)));
        trace_view_layout->addRow(tr("Highlight mouse cursor using a vertical marker line"), cb);
 
+       cb = create_checkbox(GlobalSettings::Key_View_KeepRulerItemSelected,
+               SLOT(on_view_keepRulerItemSelected_changed(int)));
+       trace_view_layout->addRow(tr("Keep active item on ruler selected when editing popup is closed"), cb);
+
        QSpinBox *snap_distance_sb = new QSpinBox();
        snap_distance_sb->setRange(0, 1000);
        snap_distance_sb->setSuffix(tr(" pixels"));
@@ -415,10 +433,10 @@ QWidget *Settings::get_decoder_settings_form(QWidget *parent)
        connect(ann_export_format_, SIGNAL(textChanged(const QString&)),
                this, SLOT(on_dec_exportFormat_changed(const QString&)));
        decoder_layout->addRow(tr("Annotation export format"), ann_export_format_);
-       QLabel *description_1 = new QLabel(tr("%s = sample range; %d: decoder name; %r: row name; %q: use quotation marks"));
+       QLabel *description_1 = new QLabel(tr("%s = sample range; %d: decoder name; %r: row name; %c: class name"));
        description_1->setAlignment(Qt::AlignRight);
        decoder_layout->addRow(description_1);
-       QLabel *description_2 = new QLabel(tr("%1: longest annotation text; %a: all annotation texts"));
+       QLabel *description_2 = new QLabel(tr("%1: longest annotation text; %a: all annotation texts; %q: use quotation marks"));
        description_2->setAlignment(Qt::AlignRight);
        decoder_layout->addRow(description_2);
 
@@ -467,6 +485,7 @@ QWidget *Settings::get_about_page(QWidget *parent) const
                tr("Protocol decoder search paths:") + "</b></td></tr>");
        for (QString &entry : a->get_pd_path_list())
                s.append(QString("<tr><td colspan=\"2\">%1</td></tr>").arg(entry));
+       s.append(tr("<tr><td colspan=\"2\">(Note: Set environment variable SIGROKDECODE_DIR to add a custom directory)</td></tr>"));
 #endif
 
        s.append("<tr><td colspan=\"2\"></td></tr>");
@@ -499,6 +518,21 @@ QWidget *Settings::get_about_page(QWidget *parent) const
                        .arg(entry.first, entry.second));
 #endif
 
+       s.append("<tr><td colspan=\"2\"></td></tr>");
+       s.append("<tr><td colspan=\"2\"><b>" +
+               tr("Available Translations:") + "</b></td></tr>");
+       for (const QString& language : a->get_languages()) {
+               if (language == "en")
+                       continue;
+
+               const QLocale locale = QLocale(language);
+               const QString desc = locale.languageToString(locale.language());
+               const QString editors = a->get_language_editors(language);
+
+               s.append(QString("<tr><td class=\"id\"><i>%1</i></td><td>(%2)</td></tr>")
+                       .arg(desc, editors));
+       }
+
        s.append("</table>");
 
        QTextDocument *supported_doc = new QTextDocument();
@@ -610,7 +644,7 @@ void Settings::on_general_language_changed(const QString &text)
        GlobalSettings settings;
        Application* a = qobject_cast<Application*>(QApplication::instance());
 
-       for (QString language : a->get_languages()) {
+       for (const QString& language : a->get_languages()) {
                QLocale locale = QLocale(language);
                QString desc = locale.languageToString(locale.language());
 
@@ -663,6 +697,12 @@ void Settings::on_general_save_with_setup_changed(int state)
        settings.setValue(GlobalSettings::Key_General_SaveWithSetup, state ? true : false);
 }
 
+void Settings::on_general_start_all_sessions_changed(int state)
+{
+       GlobalSettings settings;
+       settings.setValue(GlobalSettings::Key_General_StartAllSessions, state ? true : false);
+}
+
 void Settings::on_view_zoomToFitDuringAcq_changed(int state)
 {
        GlobalSettings settings;
@@ -693,6 +733,12 @@ void Settings::on_view_stickyScrolling_changed(int state)
        settings.setValue(GlobalSettings::Key_View_StickyScrolling, state ? true : false);
 }
 
+void Settings::on_view_allowVerticalDragging_changed(int state)
+{
+       GlobalSettings settings;
+       settings.setValue(GlobalSettings::Key_View_AllowVerticalDragging, state ? true : false);
+}
+
 void Settings::on_view_showSamplingPoints_changed(int state)
 {
        GlobalSettings settings;
@@ -723,6 +769,12 @@ void Settings::on_view_showHoverMarker_changed(int state)
        settings.setValue(GlobalSettings::Key_View_ShowHoverMarker, state ? true : false);
 }
 
+void Settings::on_view_keepRulerItemSelected_changed(int state)
+{
+       GlobalSettings settings;
+       settings.setValue(GlobalSettings::Key_View_KeepRulerItemSelected, state ? true : false);
+}
+
 void Settings::on_view_snapDistance_changed(int value)
 {
        GlobalSettings settings;
index 50f3be65c51fe9fb97569f917034c501c85f5220..bd3572ff977620daeff29968a9fc50a163312543 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_SETTINGS_HPP
-#define PULSEVIEW_PV_SETTINGS_HPP
+#ifndef PULSEVIEW_PV_DIALOGS_SETTINGS_HPP
+#define PULSEVIEW_PV_DIALOGS_SETTINGS_HPP
 
 #include <QCheckBox>
 #include <QColor>
@@ -53,25 +53,28 @@ public:
        QWidget *get_about_page(QWidget *parent) const;
        QWidget *get_logging_page(QWidget *parent) const;
 
+private Q_SLOTS:
        void accept();
        void reject();
 
-private Q_SLOTS:
        void on_page_changed(QListWidgetItem *current, QListWidgetItem *previous);
        void on_general_language_changed(const QString &text);
        void on_general_theme_changed(int value);
        void on_general_style_changed(int value);
        void on_general_save_with_setup_changed(int state);
+       void on_general_start_all_sessions_changed(int state);
        void on_view_zoomToFitDuringAcq_changed(int state);
        void on_view_zoomToFitAfterAcq_changed(int state);
        void on_view_triggerIsZero_changed(int state);
        void on_view_coloredBG_changed(int state);
        void on_view_stickyScrolling_changed(int state);
+       void on_view_allowVerticalDragging_changed(int state);
        void on_view_showSamplingPoints_changed(int state);
        void on_view_fillSignalHighAreas_changed(int state);
        void on_view_fillSignalHighAreaColor_changed(QColor color);
        void on_view_showAnalogMinorGrid_changed(int state);
        void on_view_showHoverMarker_changed(int state);
+       void on_view_keepRulerItemSelected_changed(int state);
        void on_view_snapDistance_changed(int value);
        void on_view_cursorFillColor_changed(QColor color);
        void on_view_conversionThresholdDispMode_changed(int state);
@@ -102,4 +105,4 @@ private:
 } // namespace dialogs
 } // namespace pv
 
-#endif // PULSEVIEW_PV_SETTINGS_HPP
+#endif // PULSEVIEW_PV_DIALOGS_SETTINGS_HPP
index 2bca3476c434b53ff65e99b4774145dbff248592..0bcfbf2c75ca772510068b0309a4053e74fef5a3 100644 (file)
@@ -43,12 +43,14 @@ StoreProgress::StoreProgress(const QString &file_name,
        const Session &session, QWidget *parent) :
        QProgressDialog(tr("Saving..."), tr("Cancel"), 0, 0, parent),
        session_(file_name.toStdString(), output_format, options, sample_range,
-               session)
+               session),
+       showing_error_(false)
 {
        connect(&session_, SIGNAL(progress_updated()),
                this, SLOT(on_progress_updated()));
        connect(&session_, SIGNAL(store_successful()),
                &session, SLOT(on_data_saved()));
+       connect(this, SIGNAL(canceled()), this, SLOT(on_cancel()));
 
        // Since we're not setting any progress in case of an error, the dialog
        // will pop up after the minimumDuration time has been reached - 4000 ms
@@ -79,6 +81,8 @@ void StoreProgress::run()
 
 void StoreProgress::show_error()
 {
+       showing_error_ = true;
+
        qDebug() << "Error trying to save:" << session_.error();
 
        QMessageBox msg(parentWidget());
@@ -109,11 +113,17 @@ void StoreProgress::on_progress_updated()
                setMaximum(p.second);
        } else {
                const QString err = session_.error();
-               if (!err.isEmpty())
+               if (err.isEmpty())
+                       close();
+               else if (!showing_error_)
                        show_error();
-               close();
        }
 }
 
+void StoreProgress::on_cancel()
+{
+       session_.cancel();
+}
+
 }  // namespace dialogs
 }  // namespace pv
index c42566289f83e5e7ee4656a4f6f94b87e6c32f3a..f355acc69e2f3b6c8ff35eac2a389d2b93aa56e9 100644 (file)
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_HPP
-#define PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_HPP
+#ifndef PULSEVIEW_PV_DIALOGS_STOREPROGRESS_HPP
+#define PULSEVIEW_PV_DIALOGS_STOREPROGRESS_HPP
 
+#include <atomic>
 #include <memory>
 #include <set>
 
@@ -27,6 +28,7 @@
 
 #include <pv/storesession.hpp>
 
+using std::atomic;
 using std::map;
 using std::pair;
 using std::shared_ptr;
@@ -60,12 +62,14 @@ private:
 
 private Q_SLOTS:
        void on_progress_updated();
+       void on_cancel();
 
 private:
        pv::StoreSession session_;
+       atomic<bool> showing_error_;
 };
 
 }  // namespace dialogs
 }  // namespace pv
 
-#endif // PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_HPP
+#endif // PULSEVIEW_PV_DIALOGS_STOREPROGRESS_HPP
diff --git a/pv/exprtk.hpp b/pv/exprtk.hpp
new file mode 100644 (file)
index 0000000..84e35b6
--- /dev/null
@@ -0,0 +1,39052 @@
+/*
+ ******************************************************************
+ *           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_;
+      };
+
+      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);
+
+      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);
+
+      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);
+
+      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);
+
+      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);
+
+      const std::string arithmetic_ops_list[] =
+                                  {
+                                    "+", "-", "*", "/", "%", "^"
+                                  };
+
+      static const std::size_t arithmetic_ops_list_size = sizeof(arithmetic_ops_list) / sizeof(std::string);
+
+      const std::string assignment_ops_list[] =
+                                  {
+                                    ":=", "+=", "-=",
+                                    "*=", "/=", "%="
+                                  };
+
+      static const std::size_t assignment_ops_list_size = sizeof(assignment_ops_list) / sizeof(std::string);
+
+      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_;
+         vds_t           vds_;
+      };
+
+      #ifndef exprtk_disable_string_capabilities
+      template <typename T>
+      class stringvar_node : public expression_node <T>,
+                             public string_base_node<T>,
+                             public range_interface <T>
+      {
+      public:
+
+         typedef range_pack<T> range_t;
+
+         static std::string null_value;
+
+         explicit stringvar_node()
+         : value_(&null_value)
+         {}
+
+         explicit stringvar_node(std::string& v)
+         : value_(&v)
+         {
+            rp_.n0_c = std::make_pair<bool,std::size_t>(true,0);
+            rp_.n1_c = std::make_pair<bool,std::size_t>(true,v.size() - 1);
+            rp_.cache.first  = rp_.n0_c.second;
+            rp_.cache.second = rp_.n1_c.second;
+         }
+
+         inline bool operator <(const stringvar_node<T>& v) const
+         {
+            return this < (&v);
+         }
+
+         inline T value() const
+         {
+            rp_.n1_c.second  = (*value_).size() - 1;
+            rp_.cache.second = rp_.n1_c.second;
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         std::string str() const
+         {
+            return ref();
+         }
+
+         char_cptr base() const
+         {
+            return &(*value_)[0];
+         }
+
+         std::size_t size() const
+         {
+            return ref().size();
+         }
+
+         std::string& ref()
+         {
+            return (*value_);
+         }
+
+         const std::string& ref() const
+         {
+            return (*value_);
+         }
+
+         range_t& range_ref()
+         {
+            return rp_;
+         }
+
+         const range_t& range_ref() const
+         {
+            return rp_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_stringvar;
+         }
+
+      private:
+
+         std::string* value_;
+         mutable range_t rp_;
+      };
+
+      template <typename T>
+      std::string stringvar_node<T>::null_value = std::string("");
+
+      template <typename T>
+      class string_range_node : public expression_node <T>,
+                                public string_base_node<T>,
+                                public range_interface <T>
+      {
+      public:
+
+         typedef range_pack<T> range_t;
+
+         static std::string null_value;
+
+         explicit string_range_node(std::string& v, const range_t& rp)
+         : value_(&v),
+           rp_(rp)
+         {}
+
+         virtual ~string_range_node()
+         {
+            rp_.free();
+         }
+
+         inline bool operator <(const string_range_node<T>& v) const
+         {
+            return this < (&v);
+         }
+
+         inline T value() const
+         {
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline std::string str() const
+         {
+            return (*value_);
+         }
+
+         char_cptr base() const
+         {
+            return &(*value_)[0];
+         }
+
+         std::size_t size() const
+         {
+            return ref().size();
+         }
+
+         inline range_t range() const
+         {
+            return rp_;
+         }
+
+         inline virtual std::string& ref()
+         {
+            return (*value_);
+         }
+
+         inline virtual const std::string& ref() const
+         {
+            return (*value_);
+         }
+
+         inline range_t& range_ref()
+         {
+            return rp_;
+         }
+
+         inline const range_t& range_ref() const
+         {
+            return rp_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_stringvarrng;
+         }
+
+      private:
+
+         std::string* value_;
+         range_t      rp_;
+      };
+
+      template <typename T>
+      std::string string_range_node<T>::null_value = std::string("");
+
+      template <typename T>
+      class const_string_range_node : public expression_node <T>,
+                                      public string_base_node<T>,
+                                      public range_interface <T>
+      {
+      public:
+
+         typedef range_pack<T> range_t;
+
+         explicit const_string_range_node(const std::string& v, const range_t& rp)
+         : value_(v),
+           rp_(rp)
+         {}
+
+        ~const_string_range_node()
+         {
+            rp_.free();
+         }
+
+         inline T value() const
+         {
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         std::string str() const
+         {
+            return value_;
+         }
+
+         char_cptr base() const
+         {
+            return value_.data();
+         }
+
+         std::size_t size() const
+         {
+            return value_.size();
+         }
+
+         range_t range() const
+         {
+            return rp_;
+         }
+
+         range_t& range_ref()
+         {
+            return rp_;
+         }
+
+         const range_t& range_ref() const
+         {
+            return rp_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_cstringvarrng;
+         }
+
+      private:
+
+         const_string_range_node<T>& operator=(const const_string_range_node<T>&);
+
+         const std::string value_;
+         range_t rp_;
+      };
+
+      template <typename T>
+      class generic_string_range_node : public expression_node <T>,
+                                        public string_base_node<T>,
+                                        public range_interface <T>
+      {
+      public:
+
+         typedef expression_node <T>*  expression_ptr;
+         typedef stringvar_node  <T>* strvar_node_ptr;
+         typedef string_base_node<T>*    str_base_ptr;
+         typedef range_pack      <T>          range_t;
+         typedef range_t*                   range_ptr;
+         typedef range_interface<T>          irange_t;
+         typedef irange_t*                 irange_ptr;
+
+         generic_string_range_node(expression_ptr str_branch, const range_t& brange)
+         : initialised_(false),
+           branch_(str_branch),
+           branch_deletable_(branch_deletable(branch_)),
+           str_base_ptr_ (0),
+           str_range_ptr_(0),
+           base_range_(brange)
+         {
+            range_.n0_c = std::make_pair<bool,std::size_t>(true,0);
+            range_.n1_c = std::make_pair<bool,std::size_t>(true,0);
+            range_.cache.first  = range_.n0_c.second;
+            range_.cache.second = range_.n1_c.second;
+
+            if (is_generally_string_node(branch_))
+            {
+               str_base_ptr_ = dynamic_cast<str_base_ptr>(branch_);
+
+               if (0 == str_base_ptr_)
+                  return;
+
+               str_range_ptr_ = dynamic_cast<irange_ptr>(branch_);
+
+               if (0 == str_range_ptr_)
+                  return;
+            }
+
+            initialised_ = (str_base_ptr_ && str_range_ptr_);
+         }
+
+        ~generic_string_range_node()
+         {
+            base_range_.free();
+
+            if (branch_ && branch_deletable_)
+            {
+               destroy_node(branch_);
+            }
+         }
+
+         inline T value() const
+         {
+            if (initialised_)
+            {
+               branch_->value();
+
+               std::size_t str_r0 = 0;
+               std::size_t str_r1 = 0;
+
+               std::size_t r0 = 0;
+               std::size_t r1 = 0;
+
+               range_t& range = str_range_ptr_->range_ref();
+
+               const std::size_t base_str_size = str_base_ptr_->size();
+
+               if (
+                    range      (str_r0, str_r1, base_str_size) &&
+                    base_range_(    r0,     r1, base_str_size)
+                  )
+               {
+                  const std::size_t size = (r1 - r0) + 1;
+
+                  range_.n1_c.second  = size - 1;
+                  range_.cache.second = range_.n1_c.second;
+
+                  value_.assign(str_base_ptr_->base() + str_r0 + r0, size);
+               }
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         std::string str() const
+         {
+            return value_;
+         }
+
+         char_cptr base() const
+         {
+            return &value_[0];
+         }
+
+         std::size_t size() const
+         {
+            return value_.size();
+         }
+
+         range_t& range_ref()
+         {
+            return range_;
+         }
+
+         const range_t& range_ref() const
+         {
+            return range_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_strgenrange;
+         }
+
+      private:
+
+         bool                initialised_;
+         expression_ptr           branch_;
+         const bool     branch_deletable_;
+         str_base_ptr       str_base_ptr_;
+         irange_ptr        str_range_ptr_;
+         mutable range_t      base_range_;
+         mutable range_t           range_;
+         mutable std::string       value_;
+      };
+
+      template <typename T>
+      class string_concat_node : public binary_node     <T>,
+                                 public string_base_node<T>,
+                                 public range_interface <T>
+      {
+      public:
+
+         typedef expression_node <T>*  expression_ptr;
+         typedef string_base_node<T>*    str_base_ptr;
+         typedef range_pack      <T>          range_t;
+         typedef range_t*                   range_ptr;
+         typedef range_interface<T>          irange_t;
+         typedef irange_t*                 irange_ptr;
+
+         string_concat_node(const operator_type& opr,
+                            expression_ptr branch0,
+                            expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           initialised_(false),
+           str0_base_ptr_ (0),
+           str1_base_ptr_ (0),
+           str0_range_ptr_(0),
+           str1_range_ptr_(0)
+         {
+            range_.n0_c = std::make_pair<bool,std::size_t>(true,0);
+            range_.n1_c = std::make_pair<bool,std::size_t>(true,0);
+
+            range_.cache.first  = range_.n0_c.second;
+            range_.cache.second = range_.n1_c.second;
+
+            if (is_generally_string_node(binary_node<T>::branch_[0].first))
+            {
+               str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
+
+               if (0 == str0_base_ptr_)
+                  return;
+
+               str0_range_ptr_ = dynamic_cast<irange_ptr>(binary_node<T>::branch_[0].first);
+
+               if (0 == str0_range_ptr_)
+                  return;
+            }
+
+            if (is_generally_string_node(binary_node<T>::branch_[1].first))
+            {
+               str1_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[1].first);
+
+               if (0 == str1_base_ptr_)
+                  return;
+
+               str1_range_ptr_ = dynamic_cast<irange_ptr>(binary_node<T>::branch_[1].first);
+
+               if (0 == str1_range_ptr_)
+                  return;
+            }
+
+            initialised_ = str0_base_ptr_  &&
+                           str1_base_ptr_  &&
+                           str0_range_ptr_ &&
+                           str1_range_ptr_ ;
+         }
+
+         inline T value() const
+         {
+            if (initialised_)
+            {
+               binary_node<T>::branch_[0].first->value();
+               binary_node<T>::branch_[1].first->value();
+
+               std::size_t str0_r0 = 0;
+               std::size_t str0_r1 = 0;
+
+               std::size_t str1_r0 = 0;
+               std::size_t str1_r1 = 0;
+
+               range_t& range0 = str0_range_ptr_->range_ref();
+               range_t& range1 = str1_range_ptr_->range_ref();
+
+               if (
+                    range0(str0_r0, str0_r1, str0_base_ptr_->size()) &&
+                    range1(str1_r0, str1_r1, str1_base_ptr_->size())
+                  )
+               {
+                  const std::size_t size0 = (str0_r1 - str0_r0) + 1;
+                  const std::size_t size1 = (str1_r1 - str1_r0) + 1;
+
+                  value_.assign(str0_base_ptr_->base() + str0_r0, size0);
+                  value_.append(str1_base_ptr_->base() + str1_r0, size1);
+
+                  range_.n1_c.second  = value_.size() - 1;
+                  range_.cache.second = range_.n1_c.second;
+               }
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         std::string str() const
+         {
+            return value_;
+         }
+
+         char_cptr base() const
+         {
+            return &value_[0];
+         }
+
+         std::size_t size() const
+         {
+            return value_.size();
+         }
+
+         range_t& range_ref()
+         {
+            return range_;
+         }
+
+         const range_t& range_ref() const
+         {
+            return range_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_strconcat;
+         }
+
+      private:
+
+         bool initialised_;
+         str_base_ptr str0_base_ptr_;
+         str_base_ptr str1_base_ptr_;
+         irange_ptr   str0_range_ptr_;
+         irange_ptr   str1_range_ptr_;
+         mutable range_t     range_;
+         mutable std::string value_;
+      };
+
+      template <typename T>
+      class swap_string_node : public binary_node     <T>,
+                               public string_base_node<T>,
+                               public range_interface <T>
+      {
+      public:
+
+         typedef expression_node <T>*  expression_ptr;
+         typedef stringvar_node  <T>* strvar_node_ptr;
+         typedef string_base_node<T>*    str_base_ptr;
+         typedef range_pack      <T>          range_t;
+         typedef range_t*                   range_ptr;
+         typedef range_interface<T>          irange_t;
+         typedef irange_t*                 irange_ptr;
+
+         swap_string_node(expression_ptr branch0, expression_ptr branch1)
+         : binary_node<T>(details::e_swap, branch0, branch1),
+           initialised_(false),
+           str0_node_ptr_(0),
+           str1_node_ptr_(0)
+         {
+            if (is_string_node(binary_node<T>::branch_[0].first))
+            {
+               str0_node_ptr_ = static_cast<strvar_node_ptr>(binary_node<T>::branch_[0].first);
+            }
+
+            if (is_string_node(binary_node<T>::branch_[1].first))
+            {
+               str1_node_ptr_ = static_cast<strvar_node_ptr>(binary_node<T>::branch_[1].first);
+            }
+
+            initialised_ = (str0_node_ptr_ && str1_node_ptr_);
+         }
+
+         inline T value() const
+         {
+            if (initialised_)
+            {
+               binary_node<T>::branch_[0].first->value();
+               binary_node<T>::branch_[1].first->value();
+
+               std::swap(str0_node_ptr_->ref(), str1_node_ptr_->ref());
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         std::string str() const
+         {
+            return str0_node_ptr_->str();
+         }
+
+         char_cptr base() const
+         {
+           return str0_node_ptr_->base();
+         }
+
+         std::size_t size() const
+         {
+            return str0_node_ptr_->size();
+         }
+
+         range_t& range_ref()
+         {
+            return str0_node_ptr_->range_ref();
+         }
+
+         const range_t& range_ref() const
+         {
+            return str0_node_ptr_->range_ref();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_strswap;
+         }
+
+      private:
+
+         bool initialised_;
+         strvar_node_ptr str0_node_ptr_;
+         strvar_node_ptr str1_node_ptr_;
+      };
+
+      template <typename T>
+      class swap_genstrings_node : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node <T>* expression_ptr;
+         typedef string_base_node<T>*   str_base_ptr;
+         typedef range_pack      <T>         range_t;
+         typedef range_t*                  range_ptr;
+         typedef range_interface<T>         irange_t;
+         typedef irange_t*                irange_ptr;
+
+         swap_genstrings_node(expression_ptr branch0,
+                              expression_ptr branch1)
+         : binary_node<T>(details::e_default, branch0, branch1),
+           str0_base_ptr_ (0),
+           str1_base_ptr_ (0),
+           str0_range_ptr_(0),
+           str1_range_ptr_(0),
+           initialised_(false)
+         {
+            if (is_generally_string_node(binary_node<T>::branch_[0].first))
+            {
+               str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
+
+               if (0 == str0_base_ptr_)
+                  return;
+
+               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[0].first);
+
+               if (0 == range)
+                  return;
+
+               str0_range_ptr_ = &(range->range_ref());
+            }
+
+            if (is_generally_string_node(binary_node<T>::branch_[1].first))
+            {
+               str1_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[1].first);
+
+               if (0 == str1_base_ptr_)
+                  return;
+
+               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[1].first);
+
+               if (0 == range)
+                  return;
+
+               str1_range_ptr_ = &(range->range_ref());
+            }
+
+            initialised_ = str0_base_ptr_  &&
+                           str1_base_ptr_  &&
+                           str0_range_ptr_ &&
+                           str1_range_ptr_ ;
+         }
+
+         inline T value() const
+         {
+            if (initialised_)
+            {
+               binary_node<T>::branch_[0].first->value();
+               binary_node<T>::branch_[1].first->value();
+
+               std::size_t str0_r0 = 0;
+               std::size_t str0_r1 = 0;
+
+               std::size_t str1_r0 = 0;
+               std::size_t str1_r1 = 0;
+
+               range_t& range0 = (*str0_range_ptr_);
+               range_t& range1 = (*str1_range_ptr_);
+
+               if (
+                    range0(str0_r0, str0_r1, str0_base_ptr_->size()) &&
+                    range1(str1_r0, str1_r1, str1_base_ptr_->size())
+                  )
+               {
+                  const std::size_t size0    = range0.cache_size();
+                  const std::size_t size1    = range1.cache_size();
+                  const std::size_t max_size = std::min(size0,size1);
+
+                  char_ptr s0 = const_cast<char_ptr>(str0_base_ptr_->base() + str0_r0);
+                  char_ptr s1 = const_cast<char_ptr>(str1_base_ptr_->base() + str1_r0);
+
+                  loop_unroll::details lud(max_size);
+                  char_cptr upper_bound = s0 + lud.upper_bound;
+
+                  while (s0 < upper_bound)
+                  {
+                     #define exprtk_loop(N)   \
+                     std::swap(s0[N], s1[N]); \
+
+                     exprtk_loop( 0) exprtk_loop( 1)
+                     exprtk_loop( 2) exprtk_loop( 3)
+                     #ifndef exprtk_disable_superscalar_unroll
+                     exprtk_loop( 4) exprtk_loop( 5)
+                     exprtk_loop( 6) exprtk_loop( 7)
+                     exprtk_loop( 8) exprtk_loop( 9)
+                     exprtk_loop(10) exprtk_loop(11)
+                     exprtk_loop(12) exprtk_loop(13)
+                     exprtk_loop(14) exprtk_loop(15)
+                     #endif
+
+                     s0 += lud.batch_size;
+                     s1 += lud.batch_size;
+                  }
+
+                  int i = 0;
+
+                  exprtk_disable_fallthrough_begin
+                  switch (lud.remainder)
+                  {
+                     #define case_stmt(N)                       \
+                     case N : { std::swap(s0[i], s1[i]); ++i; } \
+
+                     #ifndef exprtk_disable_superscalar_unroll
+                     case_stmt(15) case_stmt(14)
+                     case_stmt(13) case_stmt(12)
+                     case_stmt(11) case_stmt(10)
+                     case_stmt( 9) case_stmt( 8)
+                     case_stmt( 7) case_stmt( 6)
+                     case_stmt( 5) case_stmt( 4)
+                     #endif
+                     case_stmt( 3) case_stmt( 2)
+                     case_stmt( 1)
+                  }
+                  exprtk_disable_fallthrough_end
+
+                  #undef exprtk_loop
+                  #undef case_stmt
+               }
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_strswap;
+         }
+
+      private:
+
+         swap_genstrings_node(swap_genstrings_node<T>&);
+         swap_genstrings_node<T>& operator=(swap_genstrings_node<T>&);
+
+         str_base_ptr str0_base_ptr_;
+         str_base_ptr str1_base_ptr_;
+         range_ptr    str0_range_ptr_;
+         range_ptr    str1_range_ptr_;
+         bool         initialised_;
+      };
+
+      template <typename T>
+      class stringvar_size_node : public expression_node<T>
+      {
+      public:
+
+         static std::string null_value;
+
+         explicit stringvar_size_node()
+         : value_(&null_value)
+         {}
+
+         explicit stringvar_size_node(std::string& v)
+         : value_(&v)
+         {}
+
+         inline T value() const
+         {
+            return T((*value_).size());
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_stringvarsize;
+         }
+
+      private:
+
+         std::string* value_;
+      };
+
+      template <typename T>
+      std::string stringvar_size_node<T>::null_value = std::string("");
+
+      template <typename T>
+      class string_size_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node <T>* expression_ptr;
+         typedef string_base_node<T>*   str_base_ptr;
+
+         explicit string_size_node(expression_ptr brnch)
+         : branch_(brnch),
+           branch_deletable_(branch_deletable(branch_)),
+           str_base_ptr_(0)
+         {
+            if (is_generally_string_node(branch_))
+            {
+               str_base_ptr_ = dynamic_cast<str_base_ptr>(branch_);
+
+               if (0 == str_base_ptr_)
+                  return;
+            }
+         }
+
+        ~string_size_node()
+         {
+            if (branch_ && branch_deletable_)
+            {
+               destroy_node(branch_);
+            }
+         }
+
+         inline T value() const
+         {
+            T result = std::numeric_limits<T>::quiet_NaN();
+
+            if (str_base_ptr_)
+            {
+               branch_->value();
+               result = T(str_base_ptr_->size());
+            }
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_stringsize;
+         }
+
+      private:
+
+         expression_ptr           branch_;
+         const bool     branch_deletable_;
+         str_base_ptr       str_base_ptr_;
+      };
+
+      struct asn_assignment
+      {
+         static inline void execute(std::string& s, char_cptr data, const std::size_t size)
+         { s.assign(data,size); }
+      };
+
+      struct asn_addassignment
+      {
+         static inline void execute(std::string& s, char_cptr data, const std::size_t size)
+         { s.append(data,size); }
+      };
+
+      template <typename T, typename AssignmentProcess = asn_assignment>
+      class assignment_string_node : public binary_node     <T>,
+                                     public string_base_node<T>,
+                                     public range_interface <T>
+      {
+      public:
+
+         typedef expression_node <T>*  expression_ptr;
+         typedef stringvar_node  <T>* strvar_node_ptr;
+         typedef string_base_node<T>*    str_base_ptr;
+         typedef range_pack      <T>          range_t;
+         typedef range_t*                   range_ptr;
+         typedef range_interface<T>          irange_t;
+         typedef irange_t*                 irange_ptr;
+
+         assignment_string_node(const operator_type& opr,
+                                expression_ptr branch0,
+                                expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           initialised_(false),
+           str0_base_ptr_ (0),
+           str1_base_ptr_ (0),
+           str0_node_ptr_ (0),
+           str1_range_ptr_(0)
+         {
+            if (is_string_node(binary_node<T>::branch_[0].first))
+            {
+               str0_node_ptr_ = static_cast<strvar_node_ptr>(binary_node<T>::branch_[0].first);
+
+               str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
+            }
+
+            if (is_generally_string_node(binary_node<T>::branch_[1].first))
+            {
+               str1_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[1].first);
+
+               if (0 == str1_base_ptr_)
+                  return;
+
+               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[1].first);
+
+               if (0 == range)
+                  return;
+
+               str1_range_ptr_ = &(range->range_ref());
+            }
+
+            initialised_ = str0_base_ptr_  &&
+                           str1_base_ptr_  &&
+                           str0_node_ptr_  &&
+                           str1_range_ptr_ ;
+         }
+
+         inline T value() const
+         {
+            if (initialised_)
+            {
+               binary_node<T>::branch_[1].first->value();
+
+               std::size_t r0 = 0;
+               std::size_t r1 = 0;
+
+               range_t& range = (*str1_range_ptr_);
+
+               if (range(r0, r1, str1_base_ptr_->size()))
+               {
+                  AssignmentProcess::execute(str0_node_ptr_->ref(),
+                                             str1_base_ptr_->base() + r0,
+                                             (r1 - r0) + 1);
+
+                  binary_node<T>::branch_[0].first->value();
+               }
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         std::string str() const
+         {
+            return str0_node_ptr_->str();
+         }
+
+         char_cptr base() const
+         {
+           return str0_node_ptr_->base();
+         }
+
+         std::size_t size() const
+         {
+            return str0_node_ptr_->size();
+         }
+
+         range_t& range_ref()
+         {
+            return str0_node_ptr_->range_ref();
+         }
+
+         const range_t& range_ref() const
+         {
+            return str0_node_ptr_->range_ref();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_strass;
+         }
+
+      private:
+
+         bool            initialised_;
+         str_base_ptr    str0_base_ptr_;
+         str_base_ptr    str1_base_ptr_;
+         strvar_node_ptr str0_node_ptr_;
+         range_ptr       str1_range_ptr_;
+      };
+
+      template <typename T, typename AssignmentProcess = asn_assignment>
+      class assignment_string_range_node : public binary_node     <T>,
+                                           public string_base_node<T>,
+                                           public range_interface <T>
+      {
+      public:
+
+         typedef expression_node <T>*  expression_ptr;
+         typedef stringvar_node  <T>* strvar_node_ptr;
+         typedef string_base_node<T>*    str_base_ptr;
+         typedef range_pack      <T>          range_t;
+         typedef range_t*                   range_ptr;
+         typedef range_interface<T>          irange_t;
+         typedef irange_t*                 irange_ptr;
+
+         assignment_string_range_node(const operator_type& opr,
+                                      expression_ptr branch0,
+                                      expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           initialised_(false),
+           str0_base_ptr_ (0),
+           str1_base_ptr_ (0),
+           str0_node_ptr_ (0),
+           str0_range_ptr_(0),
+           str1_range_ptr_(0)
+         {
+            if (is_string_range_node(binary_node<T>::branch_[0].first))
+            {
+               str0_node_ptr_ = static_cast<strvar_node_ptr>(binary_node<T>::branch_[0].first);
+
+               str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
+
+               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[0].first);
+
+               if (0 == range)
+                  return;
+
+               str0_range_ptr_ = &(range->range_ref());
+            }
+
+            if (is_generally_string_node(binary_node<T>::branch_[1].first))
+            {
+               str1_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[1].first);
+
+               if (0 == str1_base_ptr_)
+                  return;
+
+               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[1].first);
+
+               if (0 == range)
+                  return;
+
+               str1_range_ptr_ = &(range->range_ref());
+            }
+
+            initialised_ = str0_base_ptr_  &&
+                           str1_base_ptr_  &&
+                           str0_node_ptr_  &&
+                           str0_range_ptr_ &&
+                           str1_range_ptr_ ;
+         }
+
+         inline T value() const
+         {
+            if (initialised_)
+            {
+               binary_node<T>::branch_[0].first->value();
+               binary_node<T>::branch_[1].first->value();
+
+               std::size_t s0_r0 = 0;
+               std::size_t s0_r1 = 0;
+
+               std::size_t s1_r0 = 0;
+               std::size_t s1_r1 = 0;
+
+               range_t& range0 = (*str0_range_ptr_);
+               range_t& range1 = (*str1_range_ptr_);
+
+               if (
+                    range0(s0_r0, s0_r1, str0_base_ptr_->size()) &&
+                    range1(s1_r0, s1_r1, str1_base_ptr_->size())
+                  )
+               {
+                  std::size_t size = std::min((s0_r1 - s0_r0), (s1_r1 - s1_r0)) + 1;
+
+                  std::copy(str1_base_ptr_->base() + s1_r0,
+                            str1_base_ptr_->base() + s1_r0 + size,
+                            const_cast<char_ptr>(base() + s0_r0));
+               }
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         std::string str() const
+         {
+            return str0_node_ptr_->str();
+         }
+
+         char_cptr base() const
+         {
+           return str0_node_ptr_->base();
+         }
+
+         std::size_t size() const
+         {
+            return str0_node_ptr_->size();
+         }
+
+         range_t& range_ref()
+         {
+            return str0_node_ptr_->range_ref();
+         }
+
+         const range_t& range_ref() const
+         {
+            return str0_node_ptr_->range_ref();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_strass;
+         }
+
+      private:
+
+         bool            initialised_;
+         str_base_ptr    str0_base_ptr_;
+         str_base_ptr    str1_base_ptr_;
+         strvar_node_ptr str0_node_ptr_;
+         range_ptr       str0_range_ptr_;
+         range_ptr       str1_range_ptr_;
+      };
+
+      template <typename T>
+      class conditional_string_node : public trinary_node    <T>,
+                                      public string_base_node<T>,
+                                      public range_interface <T>
+      {
+      public:
+
+         typedef expression_node <T>* expression_ptr;
+         typedef string_base_node<T>*   str_base_ptr;
+         typedef range_pack      <T>         range_t;
+         typedef range_t*                  range_ptr;
+         typedef range_interface<T>         irange_t;
+         typedef irange_t*                irange_ptr;
+
+         conditional_string_node(expression_ptr test,
+                                 expression_ptr consequent,
+                                 expression_ptr alternative)
+         : trinary_node<T>(details::e_default,consequent,alternative,test),
+           initialised_(false),
+           str0_base_ptr_ (0),
+           str1_base_ptr_ (0),
+           str0_range_ptr_(0),
+           str1_range_ptr_(0),
+           test_              (test),
+           consequent_  (consequent),
+           alternative_(alternative)
+         {
+            range_.n0_c = std::make_pair<bool,std::size_t>(true,0);
+            range_.n1_c = std::make_pair<bool,std::size_t>(true,0);
+
+            range_.cache.first  = range_.n0_c.second;
+            range_.cache.second = range_.n1_c.second;
+
+            if (is_generally_string_node(trinary_node<T>::branch_[0].first))
+            {
+               str0_base_ptr_ = dynamic_cast<str_base_ptr>(trinary_node<T>::branch_[0].first);
+
+               if (0 == str0_base_ptr_)
+                  return;
+
+               str0_range_ptr_ = dynamic_cast<irange_ptr>(trinary_node<T>::branch_[0].first);
+
+               if (0 == str0_range_ptr_)
+                  return;
+            }
+
+            if (is_generally_string_node(trinary_node<T>::branch_[1].first))
+            {
+               str1_base_ptr_ = dynamic_cast<str_base_ptr>(trinary_node<T>::branch_[1].first);
+
+               if (0 == str1_base_ptr_)
+                  return;
+
+               str1_range_ptr_ = dynamic_cast<irange_ptr>(trinary_node<T>::branch_[1].first);
+
+               if (0 == str1_range_ptr_)
+                  return;
+            }
+
+            initialised_ = str0_base_ptr_  &&
+                           str1_base_ptr_  &&
+                           str0_range_ptr_ &&
+                           str1_range_ptr_ ;
+
+         }
+
+         inline T value() const
+         {
+            if (initialised_)
+            {
+               std::size_t r0 = 0;
+               std::size_t r1 = 0;
+
+               if (is_true(test_))
+               {
+                  consequent_->value();
+
+                  range_t& range = str0_range_ptr_->range_ref();
+
+                  if (range(r0, r1, str0_base_ptr_->size()))
+                  {
+                     const std::size_t size = (r1 - r0) + 1;
+
+                     value_.assign(str0_base_ptr_->base() + r0, size);
+
+                     range_.n1_c.second  = value_.size() - 1;
+                     range_.cache.second = range_.n1_c.second;
+
+                     return T(1);
+                  }
+               }
+               else
+               {
+                  alternative_->value();
+
+                  range_t& range = str1_range_ptr_->range_ref();
+
+                  if (range(r0, r1, str1_base_ptr_->size()))
+                  {
+                     const std::size_t size = (r1 - r0) + 1;
+
+                     value_.assign(str1_base_ptr_->base() + r0, size);
+
+                     range_.n1_c.second  = value_.size() - 1;
+                     range_.cache.second = range_.n1_c.second;
+
+                     return T(0);
+                  }
+               }
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         std::string str() const
+         {
+            return value_;
+         }
+
+         char_cptr base() const
+         {
+            return &value_[0];
+         }
+
+         std::size_t size() const
+         {
+            return value_.size();
+         }
+
+         range_t& range_ref()
+         {
+            return range_;
+         }
+
+         const range_t& range_ref() const
+         {
+            return range_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_strcondition;
+         }
+
+      private:
+
+         bool initialised_;
+         str_base_ptr str0_base_ptr_;
+         str_base_ptr str1_base_ptr_;
+         irange_ptr   str0_range_ptr_;
+         irange_ptr   str1_range_ptr_;
+         mutable range_t     range_;
+         mutable std::string value_;
+
+         expression_ptr test_;
+         expression_ptr consequent_;
+         expression_ptr alternative_;
+      };
+
+      template <typename T>
+      class cons_conditional_str_node : public binary_node     <T>,
+                                        public string_base_node<T>,
+                                        public range_interface <T>
+      {
+      public:
+
+         typedef expression_node <T>* expression_ptr;
+         typedef string_base_node<T>*   str_base_ptr;
+         typedef range_pack      <T>         range_t;
+         typedef range_t*                  range_ptr;
+         typedef range_interface<T>         irange_t;
+         typedef irange_t*                irange_ptr;
+
+         cons_conditional_str_node(expression_ptr test,
+                                   expression_ptr consequent)
+         : binary_node<T>(details::e_default, consequent, test),
+           initialised_(false),
+           str0_base_ptr_ (0),
+           str0_range_ptr_(0),
+           test_      (test),
+           consequent_(consequent)
+         {
+            range_.n0_c = std::make_pair<bool,std::size_t>(true,0);
+            range_.n1_c = std::make_pair<bool,std::size_t>(true,0);
+
+            range_.cache.first  = range_.n0_c.second;
+            range_.cache.second = range_.n1_c.second;
+
+            if (is_generally_string_node(binary_node<T>::branch_[0].first))
+            {
+               str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
+
+               if (0 == str0_base_ptr_)
+                  return;
+
+               str0_range_ptr_ = dynamic_cast<irange_ptr>(binary_node<T>::branch_[0].first);
+
+               if (0 == str0_range_ptr_)
+                  return;
+            }
+
+            initialised_ = str0_base_ptr_ && str0_range_ptr_ ;
+         }
+
+         inline T value() const
+         {
+            if (initialised_)
+            {
+               if (is_true(test_))
+               {
+                  consequent_->value();
+
+                  range_t& range = str0_range_ptr_->range_ref();
+
+                  std::size_t r0 = 0;
+                  std::size_t r1 = 0;
+
+                  if (range(r0, r1, str0_base_ptr_->size()))
+                  {
+                     const std::size_t size = (r1 - r0) + 1;
+
+                     value_.assign(str0_base_ptr_->base() + r0, size);
+
+                     range_.n1_c.second  = value_.size() - 1;
+                     range_.cache.second = range_.n1_c.second;
+
+                     return T(1);
+                  }
+               }
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         std::string str() const
+         {
+            return value_;
+         }
+
+         char_cptr base() const
+         {
+            return &value_[0];
+         }
+
+         std::size_t size() const
+         {
+            return value_.size();
+         }
+
+         range_t& range_ref()
+         {
+            return range_;
+         }
+
+         const range_t& range_ref() const
+         {
+            return range_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_strccondition;
+         }
+
+      private:
+
+         bool initialised_;
+         str_base_ptr str0_base_ptr_;
+         irange_ptr   str0_range_ptr_;
+         mutable range_t     range_;
+         mutable std::string value_;
+
+         expression_ptr test_;
+         expression_ptr consequent_;
+      };
+
+      template <typename T, typename VarArgFunction>
+      class str_vararg_node  : public expression_node <T>,
+                               public string_base_node<T>,
+                               public range_interface <T>
+      {
+      public:
+
+         typedef expression_node <T>*  expression_ptr;
+         typedef string_base_node<T>*    str_base_ptr;
+         typedef range_pack      <T>          range_t;
+         typedef range_t*                   range_ptr;
+         typedef range_interface<T>          irange_t;
+         typedef irange_t*                 irange_ptr;
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         explicit str_vararg_node(const Sequence<expression_ptr,Allocator>& arg_list)
+         : final_node_(arg_list.back()),
+           final_deletable_(branch_deletable(final_node_)),
+           initialised_(false),
+           str_base_ptr_ (0),
+           str_range_ptr_(0)
+         {
+            if (0 == final_node_)
+               return;
+            else if (!is_generally_string_node(final_node_))
+               return;
+
+            str_base_ptr_ = dynamic_cast<str_base_ptr>(final_node_);
+
+            if (0 == str_base_ptr_)
+               return;
+
+            str_range_ptr_ = dynamic_cast<irange_ptr>(final_node_);
+
+            if (0 == str_range_ptr_)
+               return;
+
+            initialised_ = str_base_ptr_  && str_range_ptr_;
+
+            if (arg_list.size() > 1)
+            {
+               const std::size_t arg_list_size = arg_list.size() - 1;
+
+               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;
+                  }
+               }
+            }
+         }
+
+        ~str_vararg_node()
+         {
+            if (final_node_ && final_deletable_)
+            {
+               destroy_node(final_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())
+            {
+               VarArgFunction::process(arg_list_);
+            }
+
+            final_node_->value();
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         std::string str() const
+         {
+            return str_base_ptr_->str();
+         }
+
+         char_cptr base() const
+         {
+            return str_base_ptr_->base();
+         }
+
+         std::size_t size() const
+         {
+            return str_base_ptr_->size();
+         }
+
+         range_t& range_ref()
+         {
+            return str_range_ptr_->range_ref();
+         }
+
+         const range_t& range_ref() const
+         {
+            return str_range_ptr_->range_ref();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_stringvararg;
+         }
+
+      private:
+
+         expression_ptr final_node_;
+         bool           final_deletable_;
+         bool           initialised_;
+         str_base_ptr   str_base_ptr_;
+         irange_ptr     str_range_ptr_;
+         std::vector<expression_ptr> arg_list_;
+         std::vector<unsigned char> delete_branch_;
+      };
+      #endif
+
+      template <typename T, std::size_t N>
+      inline T axn(T a, T x)
+      {
+         // a*x^n
+         return a * exprtk::details::numeric::fast_exp<T,N>::result(x);
+      }
+
+      template <typename T, std::size_t N>
+      inline T axnb(T a, T x, T b)
+      {
+         // a*x^n+b
+         return a * exprtk::details::numeric::fast_exp<T,N>::result(x) + b;
+      }
+
+      template <typename T>
+      struct sf_base
+      {
+         typedef typename details::functor_t<T>::Type Type;
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::qfunc_t quaternary_functor_t;
+         typedef typename functor_t::tfunc_t    trinary_functor_t;
+         typedef typename functor_t::bfunc_t     binary_functor_t;
+         typedef typename functor_t::ufunc_t      unary_functor_t;
+      };
+
+      #define define_sfop3(NN,OP0,OP1)                   \
+      template <typename T>                              \
+      struct sf##NN##_op : public sf_base<T>             \
+      {                                                  \
+         typedef typename sf_base<T>::Type const Type;   \
+         static inline T process(Type x, Type y, Type z) \
+         {                                               \
+            return (OP0);                                \
+         }                                               \
+         static inline std::string id()                  \
+         {                                               \
+            return OP1;                                  \
+         }                                               \
+      };                                                 \
+
+      define_sfop3(00,(x + y) / z       ,"(t+t)/t")
+      define_sfop3(01,(x + y) * z       ,"(t+t)*t")
+      define_sfop3(02,(x + y) - z       ,"(t+t)-t")
+      define_sfop3(03,(x + y) + z       ,"(t+t)+t")
+      define_sfop3(04,(x - y) + z       ,"(t-t)+t")
+      define_sfop3(05,(x - y) / z       ,"(t-t)/t")
+      define_sfop3(06,(x - y) * z       ,"(t-t)*t")
+      define_sfop3(07,(x * y) + z       ,"(t*t)+t")
+      define_sfop3(08,(x * y) - z       ,"(t*t)-t")
+      define_sfop3(09,(x * y) / z       ,"(t*t)/t")
+      define_sfop3(10,(x * y) * z       ,"(t*t)*t")
+      define_sfop3(11,(x / y) + z       ,"(t/t)+t")
+      define_sfop3(12,(x / y) - z       ,"(t/t)-t")
+      define_sfop3(13,(x / y) / z       ,"(t/t)/t")
+      define_sfop3(14,(x / y) * z       ,"(t/t)*t")
+      define_sfop3(15,x / (y + z)       ,"t/(t+t)")
+      define_sfop3(16,x / (y - z)       ,"t/(t-t)")
+      define_sfop3(17,x / (y * z)       ,"t/(t*t)")
+      define_sfop3(18,x / (y / z)       ,"t/(t/t)")
+      define_sfop3(19,x * (y + z)       ,"t*(t+t)")
+      define_sfop3(20,x * (y - z)       ,"t*(t-t)")
+      define_sfop3(21,x * (y * z)       ,"t*(t*t)")
+      define_sfop3(22,x * (y / z)       ,"t*(t/t)")
+      define_sfop3(23,x - (y + z)       ,"t-(t+t)")
+      define_sfop3(24,x - (y - z)       ,"t-(t-t)")
+      define_sfop3(25,x - (y / z)       ,"t-(t/t)")
+      define_sfop3(26,x - (y * z)       ,"t-(t*t)")
+      define_sfop3(27,x + (y * z)       ,"t+(t*t)")
+      define_sfop3(28,x + (y / z)       ,"t+(t/t)")
+      define_sfop3(29,x + (y + z)       ,"t+(t+t)")
+      define_sfop3(30,x + (y - z)       ,"t+(t-t)")
+      define_sfop3(31,(axnb<T,2>(x,y,z)),"       ")
+      define_sfop3(32,(axnb<T,3>(x,y,z)),"       ")
+      define_sfop3(33,(axnb<T,4>(x,y,z)),"       ")
+      define_sfop3(34,(axnb<T,5>(x,y,z)),"       ")
+      define_sfop3(35,(axnb<T,6>(x,y,z)),"       ")
+      define_sfop3(36,(axnb<T,7>(x,y,z)),"       ")
+      define_sfop3(37,(axnb<T,8>(x,y,z)),"       ")
+      define_sfop3(38,(axnb<T,9>(x,y,z)),"       ")
+      define_sfop3(39,x * numeric::log(y)   + z,"")
+      define_sfop3(40,x * numeric::log(y)   - z,"")
+      define_sfop3(41,x * numeric::log10(y) + z,"")
+      define_sfop3(42,x * numeric::log10(y) - z,"")
+      define_sfop3(43,x * numeric::sin(y) + z  ,"")
+      define_sfop3(44,x * numeric::sin(y) - z  ,"")
+      define_sfop3(45,x * numeric::cos(y) + z  ,"")
+      define_sfop3(46,x * numeric::cos(y) - z  ,"")
+      define_sfop3(47,details::is_true(x) ? y : z,"")
+
+      #define define_sfop4(NN,OP0,OP1)                           \
+      template <typename T>                                      \
+      struct sf##NN##_op : public sf_base<T>                     \
+      {                                                          \
+         typedef typename sf_base<T>::Type const Type;           \
+         static inline T process(Type x, Type y, Type z, Type w) \
+         {                                                       \
+            return (OP0);                                        \
+         }                                                       \
+         static inline std::string id() { return OP1; }          \
+      };                                                         \
+
+      define_sfop4(48,(x + ((y + z) / w)),"t+((t+t)/t)")
+      define_sfop4(49,(x + ((y + z) * w)),"t+((t+t)*t)")
+      define_sfop4(50,(x + ((y - z) / w)),"t+((t-t)/t)")
+      define_sfop4(51,(x + ((y - z) * w)),"t+((t-t)*t)")
+      define_sfop4(52,(x + ((y * z) / w)),"t+((t*t)/t)")
+      define_sfop4(53,(x + ((y * z) * w)),"t+((t*t)*t)")
+      define_sfop4(54,(x + ((y / z) + w)),"t+((t/t)+t)")
+      define_sfop4(55,(x + ((y / z) / w)),"t+((t/t)/t)")
+      define_sfop4(56,(x + ((y / z) * w)),"t+((t/t)*t)")
+      define_sfop4(57,(x - ((y + z) / w)),"t-((t+t)/t)")
+      define_sfop4(58,(x - ((y + z) * w)),"t-((t+t)*t)")
+      define_sfop4(59,(x - ((y - z) / w)),"t-((t-t)/t)")
+      define_sfop4(60,(x - ((y - z) * w)),"t-((t-t)*t)")
+      define_sfop4(61,(x - ((y * z) / w)),"t-((t*t)/t)")
+      define_sfop4(62,(x - ((y * z) * w)),"t-((t*t)*t)")
+      define_sfop4(63,(x - ((y / z) / w)),"t-((t/t)/t)")
+      define_sfop4(64,(x - ((y / z) * w)),"t-((t/t)*t)")
+      define_sfop4(65,(((x + y) * z) - w),"((t+t)*t)-t")
+      define_sfop4(66,(((x - y) * z) - w),"((t-t)*t)-t")
+      define_sfop4(67,(((x * y) * z) - w),"((t*t)*t)-t")
+      define_sfop4(68,(((x / y) * z) - w),"((t/t)*t)-t")
+      define_sfop4(69,(((x + y) / z) - w),"((t+t)/t)-t")
+      define_sfop4(70,(((x - y) / z) - w),"((t-t)/t)-t")
+      define_sfop4(71,(((x * y) / z) - w),"((t*t)/t)-t")
+      define_sfop4(72,(((x / y) / z) - w),"((t/t)/t)-t")
+      define_sfop4(73,((x * y) + (z * w)),"(t*t)+(t*t)")
+      define_sfop4(74,((x * y) - (z * w)),"(t*t)-(t*t)")
+      define_sfop4(75,((x * y) + (z / w)),"(t*t)+(t/t)")
+      define_sfop4(76,((x * y) - (z / w)),"(t*t)-(t/t)")
+      define_sfop4(77,((x / y) + (z / w)),"(t/t)+(t/t)")
+      define_sfop4(78,((x / y) - (z / w)),"(t/t)-(t/t)")
+      define_sfop4(79,((x / y) - (z * w)),"(t/t)-(t*t)")
+      define_sfop4(80,(x / (y + (z * w))),"t/(t+(t*t))")
+      define_sfop4(81,(x / (y - (z * w))),"t/(t-(t*t))")
+      define_sfop4(82,(x * (y + (z * w))),"t*(t+(t*t))")
+      define_sfop4(83,(x * (y - (z * w))),"t*(t-(t*t))")
+
+      define_sfop4(84,(axn<T,2>(x,y) + axn<T,2>(z,w)),"")
+      define_sfop4(85,(axn<T,3>(x,y) + axn<T,3>(z,w)),"")
+      define_sfop4(86,(axn<T,4>(x,y) + axn<T,4>(z,w)),"")
+      define_sfop4(87,(axn<T,5>(x,y) + axn<T,5>(z,w)),"")
+      define_sfop4(88,(axn<T,6>(x,y) + axn<T,6>(z,w)),"")
+      define_sfop4(89,(axn<T,7>(x,y) + axn<T,7>(z,w)),"")
+      define_sfop4(90,(axn<T,8>(x,y) + axn<T,8>(z,w)),"")
+      define_sfop4(91,(axn<T,9>(x,y) + axn<T,9>(z,w)),"")
+      define_sfop4(92,((details::is_true(x) && details::is_true(y)) ? z : w),"")
+      define_sfop4(93,((details::is_true(x) || details::is_true(y)) ? z : w),"")
+      define_sfop4(94,((x <  y) ? z : w),"")
+      define_sfop4(95,((x <= y) ? z : w),"")
+      define_sfop4(96,((x >  y) ? z : w),"")
+      define_sfop4(97,((x >= y) ? z : w),"")
+      define_sfop4(98,(details::is_true(numeric::equal(x,y)) ? z : w),"")
+      define_sfop4(99,(x * numeric::sin(y) + z * numeric::cos(w)),"")
+
+      define_sfop4(ext00,((x + y) - (z * w)),"(t+t)-(t*t)")
+      define_sfop4(ext01,((x + y) - (z / w)),"(t+t)-(t/t)")
+      define_sfop4(ext02,((x + y) + (z * w)),"(t+t)+(t*t)")
+      define_sfop4(ext03,((x + y) + (z / w)),"(t+t)+(t/t)")
+      define_sfop4(ext04,((x - y) + (z * w)),"(t-t)+(t*t)")
+      define_sfop4(ext05,((x - y) + (z / w)),"(t-t)+(t/t)")
+      define_sfop4(ext06,((x - y) - (z * w)),"(t-t)-(t*t)")
+      define_sfop4(ext07,((x - y) - (z / w)),"(t-t)-(t/t)")
+      define_sfop4(ext08,((x + y) - (z - w)),"(t+t)-(t-t)")
+      define_sfop4(ext09,((x + y) + (z - w)),"(t+t)+(t-t)")
+      define_sfop4(ext10,((x + y) + (z + w)),"(t+t)+(t+t)")
+      define_sfop4(ext11,((x + y) * (z - w)),"(t+t)*(t-t)")
+      define_sfop4(ext12,((x + y) / (z - w)),"(t+t)/(t-t)")
+      define_sfop4(ext13,((x - y) - (z + w)),"(t-t)-(t+t)")
+      define_sfop4(ext14,((x - y) + (z + w)),"(t-t)+(t+t)")
+      define_sfop4(ext15,((x - y) * (z + w)),"(t-t)*(t+t)")
+      define_sfop4(ext16,((x - y) / (z + w)),"(t-t)/(t+t)")
+      define_sfop4(ext17,((x * y) - (z + w)),"(t*t)-(t+t)")
+      define_sfop4(ext18,((x / y) - (z + w)),"(t/t)-(t+t)")
+      define_sfop4(ext19,((x * y) + (z + w)),"(t*t)+(t+t)")
+      define_sfop4(ext20,((x / y) + (z + w)),"(t/t)+(t+t)")
+      define_sfop4(ext21,((x * y) + (z - w)),"(t*t)+(t-t)")
+      define_sfop4(ext22,((x / y) + (z - w)),"(t/t)+(t-t)")
+      define_sfop4(ext23,((x * y) - (z - w)),"(t*t)-(t-t)")
+      define_sfop4(ext24,((x / y) - (z - w)),"(t/t)-(t-t)")
+      define_sfop4(ext25,((x + y) * (z * w)),"(t+t)*(t*t)")
+      define_sfop4(ext26,((x + y) * (z / w)),"(t+t)*(t/t)")
+      define_sfop4(ext27,((x + y) / (z * w)),"(t+t)/(t*t)")
+      define_sfop4(ext28,((x + y) / (z / w)),"(t+t)/(t/t)")
+      define_sfop4(ext29,((x - y) / (z * w)),"(t-t)/(t*t)")
+      define_sfop4(ext30,((x - y) / (z / w)),"(t-t)/(t/t)")
+      define_sfop4(ext31,((x - y) * (z * w)),"(t-t)*(t*t)")
+      define_sfop4(ext32,((x - y) * (z / w)),"(t-t)*(t/t)")
+      define_sfop4(ext33,((x * y) * (z + w)),"(t*t)*(t+t)")
+      define_sfop4(ext34,((x / y) * (z + w)),"(t/t)*(t+t)")
+      define_sfop4(ext35,((x * y) / (z + w)),"(t*t)/(t+t)")
+      define_sfop4(ext36,((x / y) / (z + w)),"(t/t)/(t+t)")
+      define_sfop4(ext37,((x * y) / (z - w)),"(t*t)/(t-t)")
+      define_sfop4(ext38,((x / y) / (z - w)),"(t/t)/(t-t)")
+      define_sfop4(ext39,((x * y) * (z - w)),"(t*t)*(t-t)")
+      define_sfop4(ext40,((x * y) / (z * w)),"(t*t)/(t*t)")
+      define_sfop4(ext41,((x / y) * (z / w)),"(t/t)*(t/t)")
+      define_sfop4(ext42,((x / y) * (z - w)),"(t/t)*(t-t)")
+      define_sfop4(ext43,((x * y) * (z * w)),"(t*t)*(t*t)")
+      define_sfop4(ext44,(x + (y * (z / w))),"t+(t*(t/t))")
+      define_sfop4(ext45,(x - (y * (z / w))),"t-(t*(t/t))")
+      define_sfop4(ext46,(x + (y / (z * w))),"t+(t/(t*t))")
+      define_sfop4(ext47,(x - (y / (z * w))),"t-(t/(t*t))")
+      define_sfop4(ext48,(((x - y) - z) * w),"((t-t)-t)*t")
+      define_sfop4(ext49,(((x - y) - z) / w),"((t-t)-t)/t")
+      define_sfop4(ext50,(((x - y) + z) * w),"((t-t)+t)*t")
+      define_sfop4(ext51,(((x - y) + z) / w),"((t-t)+t)/t")
+      define_sfop4(ext52,((x + (y - z)) * w),"(t+(t-t))*t")
+      define_sfop4(ext53,((x + (y - z)) / w),"(t+(t-t))/t")
+      define_sfop4(ext54,((x + y) / (z + w)),"(t+t)/(t+t)")
+      define_sfop4(ext55,((x - y) / (z - w)),"(t-t)/(t-t)")
+      define_sfop4(ext56,((x + y) * (z + w)),"(t+t)*(t+t)")
+      define_sfop4(ext57,((x - y) * (z - w)),"(t-t)*(t-t)")
+      define_sfop4(ext58,((x - y) + (z - w)),"(t-t)+(t-t)")
+      define_sfop4(ext59,((x - y) - (z - w)),"(t-t)-(t-t)")
+      define_sfop4(ext60,((x / y) + (z * w)),"(t/t)+(t*t)")
+      define_sfop4(ext61,(((x * y) * z) / w),"((t*t)*t)/t")
+
+      #undef define_sfop3
+      #undef define_sfop4
+
+      template <typename T, typename SpecialFunction>
+      class sf3_node : public trinary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         sf3_node(const operator_type& opr,
+                  expression_ptr branch0,
+                  expression_ptr branch1,
+                  expression_ptr branch2)
+         : trinary_node<T>(opr, branch0, branch1, branch2)
+         {}
+
+         inline T value() const
+         {
+            const T x = trinary_node<T>::branch_[0].first->value();
+            const T y = trinary_node<T>::branch_[1].first->value();
+            const T z = trinary_node<T>::branch_[2].first->value();
+
+            return SpecialFunction::process(x, y, z);
+         }
+      };
+
+      template <typename T, typename SpecialFunction>
+      class sf4_node : public quaternary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         sf4_node(const operator_type& opr,
+                  expression_ptr branch0,
+                  expression_ptr branch1,
+                  expression_ptr branch2,
+                  expression_ptr branch3)
+         : quaternary_node<T>(opr, branch0, branch1, branch2, branch3)
+         {}
+
+         inline T value() const
+         {
+            const T x = quaternary_node<T>::branch_[0].first->value();
+            const T y = quaternary_node<T>::branch_[1].first->value();
+            const T z = quaternary_node<T>::branch_[2].first->value();
+            const T w = quaternary_node<T>::branch_[3].first->value();
+
+            return SpecialFunction::process(x, y, z, w);
+         }
+      };
+
+      template <typename T, typename SpecialFunction>
+      class sf3_var_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         sf3_var_node(const T& v0, const T& v1, const T& v2)
+         : v0_(v0),
+           v1_(v1),
+           v2_(v2)
+         {}
+
+         inline T value() const
+         {
+            return SpecialFunction::process(v0_, v1_, v2_);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_trinary;
+         }
+
+      private:
+
+         sf3_var_node(sf3_var_node<T,SpecialFunction>&);
+         sf3_var_node<T,SpecialFunction>& operator=(sf3_var_node<T,SpecialFunction>&);
+
+         const T& v0_;
+         const T& v1_;
+         const T& v2_;
+      };
+
+      template <typename T, typename SpecialFunction>
+      class sf4_var_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         sf4_var_node(const T& v0, const T& v1, const T& v2, const T& v3)
+         : v0_(v0),
+           v1_(v1),
+           v2_(v2),
+           v3_(v3)
+         {}
+
+         inline T value() const
+         {
+            return SpecialFunction::process(v0_, v1_, v2_, v3_);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_trinary;
+         }
+
+      private:
+
+         sf4_var_node(sf4_var_node<T,SpecialFunction>&);
+         sf4_var_node<T,SpecialFunction>& operator=(sf4_var_node<T,SpecialFunction>&);
+
+         const T& v0_;
+         const T& v1_;
+         const T& v2_;
+         const T& v3_;
+      };
+
+      template <typename T, typename VarArgFunction>
+      class vararg_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         explicit vararg_node(const Sequence<expression_ptr,Allocator>& arg_list)
+         {
+            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;
+               }
+            }
+         }
+
+        ~vararg_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
+         {
+            return VarArgFunction::process(arg_list_);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vararg;
+         }
+
+      private:
+
+         std::vector<expression_ptr> arg_list_;
+         std::vector<unsigned char> delete_branch_;
+      };
+
+      template <typename T, typename VarArgFunction>
+      class vararg_varnode : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         explicit vararg_varnode(const Sequence<expression_ptr,Allocator>& arg_list)
+         {
+            arg_list_.resize(arg_list.size());
+
+            for (std::size_t i = 0; i < arg_list.size(); ++i)
+            {
+               if (arg_list[i] && is_variable_node(arg_list[i]))
+               {
+                  variable_node<T>* var_node_ptr = static_cast<variable_node<T>*>(arg_list[i]);
+                  arg_list_[i] = (&var_node_ptr->ref());
+               }
+               else
+               {
+                  arg_list_.clear();
+                  return;
+               }
+            }
+         }
+
+         inline T value() const
+         {
+            if (!arg_list_.empty())
+               return VarArgFunction::process(arg_list_);
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vararg;
+         }
+
+      private:
+
+         std::vector<const T*> arg_list_;
+      };
+
+      template <typename T, typename VecFunction>
+      class vectorize_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         explicit vectorize_node(const expression_ptr v)
+         : ivec_ptr_(0),
+           v_(v),
+           v_deletable_(branch_deletable(v_))
+         {
+            if (is_ivector_node(v))
+            {
+               ivec_ptr_ = dynamic_cast<vector_interface<T>*>(v);
+            }
+            else
+               ivec_ptr_ = 0;
+         }
+
+        ~vectorize_node()
+         {
+            if (v_ && v_deletable_)
+            {
+               destroy_node(v_);
+            }
+         }
+
+         inline T value() const
+         {
+            if (ivec_ptr_)
+            {
+               v_->value();
+               return VecFunction::process(ivec_ptr_);
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vecfunc;
+         }
+
+      private:
+
+         vector_interface<T>* ivec_ptr_;
+         expression_ptr              v_;
+         const bool        v_deletable_;
+      };
+
+      template <typename T>
+      class assignment_node : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         assignment_node(const operator_type& opr,
+                         expression_ptr branch0,
+                         expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           var_node_ptr_(0)
+         {
+            if (is_variable_node(binary_node<T>::branch_[0].first))
+            {
+               var_node_ptr_ = static_cast<variable_node<T>*>(binary_node<T>::branch_[0].first);
+            }
+         }
+
+         inline T value() const
+         {
+            if (var_node_ptr_)
+            {
+               T& result = var_node_ptr_->ref();
+
+               result = binary_node<T>::branch_[1].first->value();
+
+               return result;
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+      private:
+
+         variable_node<T>* var_node_ptr_;
+      };
+
+      template <typename T>
+      class assignment_vec_elem_node : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         assignment_vec_elem_node(const operator_type& opr,
+                                  expression_ptr branch0,
+                                  expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           vec_node_ptr_(0)
+         {
+            if (is_vector_elem_node(binary_node<T>::branch_[0].first))
+            {
+               vec_node_ptr_ = static_cast<vector_elem_node<T>*>(binary_node<T>::branch_[0].first);
+            }
+         }
+
+         inline T value() const
+         {
+            if (vec_node_ptr_)
+            {
+               T& result = vec_node_ptr_->ref();
+
+               result = binary_node<T>::branch_[1].first->value();
+
+               return result;
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+      private:
+
+         vector_elem_node<T>* vec_node_ptr_;
+      };
+
+      template <typename T>
+      class assignment_rebasevec_elem_node : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         assignment_rebasevec_elem_node(const operator_type& opr,
+                                        expression_ptr branch0,
+                                        expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           rbvec_node_ptr_(0)
+         {
+            if (is_rebasevector_elem_node(binary_node<T>::branch_[0].first))
+            {
+               rbvec_node_ptr_ = static_cast<rebasevector_elem_node<T>*>(binary_node<T>::branch_[0].first);
+            }
+         }
+
+         inline T value() const
+         {
+            if (rbvec_node_ptr_)
+            {
+               T& result = rbvec_node_ptr_->ref();
+
+               result = binary_node<T>::branch_[1].first->value();
+
+               return result;
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+      private:
+
+         rebasevector_elem_node<T>* rbvec_node_ptr_;
+      };
+
+      template <typename T>
+      class assignment_rebasevec_celem_node : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         assignment_rebasevec_celem_node(const operator_type& opr,
+                                         expression_ptr branch0,
+                                         expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           rbvec_node_ptr_(0)
+         {
+            if (is_rebasevector_celem_node(binary_node<T>::branch_[0].first))
+            {
+               rbvec_node_ptr_ = static_cast<rebasevector_celem_node<T>*>(binary_node<T>::branch_[0].first);
+            }
+         }
+
+         inline T value() const
+         {
+            if (rbvec_node_ptr_)
+            {
+               T& result = rbvec_node_ptr_->ref();
+
+               result = binary_node<T>::branch_[1].first->value();
+
+               return result;
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+      private:
+
+         rebasevector_celem_node<T>* rbvec_node_ptr_;
+      };
+
+      template <typename T>
+      class assignment_vec_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;
+
+         assignment_vec_node(const operator_type& opr,
+                             expression_ptr branch0,
+                             expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           vec_node_ptr_(0)
+         {
+            if (is_vector_node(binary_node<T>::branch_[0].first))
+            {
+               vec_node_ptr_ = static_cast<vector_node<T>*>(binary_node<T>::branch_[0].first);
+               vds()         = vec_node_ptr_->vds();
+            }
+         }
+
+         inline T value() const
+         {
+            if (vec_node_ptr_)
+            {
+               const T v = binary_node<T>::branch_[1].first->value();
+
+               T* vec = vds().data();
+
+               loop_unroll::details lud(size());
+               const T* upper_bound = vec + lud.upper_bound;
+
+               while (vec < upper_bound)
+               {
+                  #define exprtk_loop(N) \
+                  vec[N] = v;            \
+
+                  exprtk_loop( 0) exprtk_loop( 1)
+                  exprtk_loop( 2) exprtk_loop( 3)
+                  #ifndef exprtk_disable_superscalar_unroll
+                  exprtk_loop( 4) exprtk_loop( 5)
+                  exprtk_loop( 6) exprtk_loop( 7)
+                  exprtk_loop( 8) exprtk_loop( 9)
+                  exprtk_loop(10) exprtk_loop(11)
+                  exprtk_loop(12) exprtk_loop(13)
+                  exprtk_loop(14) exprtk_loop(15)
+                  #endif
+
+                  vec += lud.batch_size;
+               }
+
+               exprtk_disable_fallthrough_begin
+               switch (lud.remainder)
+               {
+                  #define case_stmt(N) \
+                  case N : *vec++ = v; \
+
+                  #ifndef exprtk_disable_superscalar_unroll
+                  case_stmt(15) case_stmt(14)
+                  case_stmt(13) case_stmt(12)
+                  case_stmt(11) case_stmt(10)
+                  case_stmt( 9) case_stmt( 8)
+                  case_stmt( 7) case_stmt( 6)
+                  case_stmt( 5) case_stmt( 4)
+                  #endif
+                  case_stmt( 3) case_stmt( 2)
+                  case_stmt( 1)
+               }
+               exprtk_disable_fallthrough_end
+
+               #undef exprtk_loop
+               #undef case_stmt
+
+               return vec_node_ptr_->value();
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         vector_node_ptr vec() const
+         {
+            return vec_node_ptr_;
+         }
+
+         vector_node_ptr vec()
+         {
+            return vec_node_ptr_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vecvalass;
+         }
+
+         std::size_t size() const
+         {
+            return vds().size();
+         }
+
+         vds_t& vds()
+         {
+            return vds_;
+         }
+
+         const vds_t& vds() const
+         {
+            return vds_;
+         }
+
+      private:
+
+         vector_node<T>* vec_node_ptr_;
+         vds_t           vds_;
+      };
+
+      template <typename T>
+      class assignment_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;
+
+         assignment_vecvec_node(const operator_type& opr,
+                                expression_ptr branch0,
+                                expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           vec0_node_ptr_(0),
+           vec1_node_ptr_(0),
+           initialised_(false),
+           src_is_ivec_(false)
+         {
+            if (is_vector_node(binary_node<T>::branch_[0].first))
+            {
+               vec0_node_ptr_ = static_cast<vector_node<T>*>(binary_node<T>::branch_[0].first);
+               vds()          = vec0_node_ptr_->vds();
+            }
+
+            if (is_vector_node(binary_node<T>::branch_[1].first))
+            {
+               vec1_node_ptr_ = static_cast<vector_node<T>*>(binary_node<T>::branch_[1].first);
+               vds_t::match_sizes(vds(),vec1_node_ptr_->vds());
+            }
+            else 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 (!vi->side_effect())
+                  {
+                     vi->vds()    = vds();
+                     src_is_ivec_ = true;
+                  }
+                  else
+                     vds_t::match_sizes(vds(),vi->vds());
+               }
+            }
+
+            initialised_ = (vec0_node_ptr_ && vec1_node_ptr_);
+         }
+
+         inline T value() const
+         {
+            if (initialised_)
+            {
+               binary_node<T>::branch_[1].first->value();
+
+               if (src_is_ivec_)
+                  return vec0_node_ptr_->value();
+
+               T* vec0 = vec0_node_ptr_->vds().data();
+               T* vec1 = vec1_node_ptr_->vds().data();
+
+               loop_unroll::details lud(size());
+               const T* upper_bound = vec0 + lud.upper_bound;
+
+               while (vec0 < upper_bound)
+               {
+                  #define exprtk_loop(N) \
+                  vec0[N] = vec1[N];     \
+
+                  exprtk_loop( 0) exprtk_loop( 1)
+                  exprtk_loop( 2) exprtk_loop( 3)
+                  #ifndef exprtk_disable_superscalar_unroll
+                  exprtk_loop( 4) exprtk_loop( 5)
+                  exprtk_loop( 6) exprtk_loop( 7)
+                  exprtk_loop( 8) exprtk_loop( 9)
+                  exprtk_loop(10) exprtk_loop(11)
+                  exprtk_loop(12) exprtk_loop(13)
+                  exprtk_loop(14) exprtk_loop(15)
+                  #endif
+
+                  vec0 += lud.batch_size;
+                  vec1 += lud.batch_size;
+               }
+
+               exprtk_disable_fallthrough_begin
+               switch (lud.remainder)
+               {
+                  #define case_stmt(N)        \
+                  case N : *vec0++ = *vec1++; \
+
+                  #ifndef exprtk_disable_superscalar_unroll
+                  case_stmt(15) case_stmt(14)
+                  case_stmt(13) case_stmt(12)
+                  case_stmt(11) case_stmt(10)
+                  case_stmt( 9) case_stmt( 8)
+                  case_stmt( 7) case_stmt( 6)
+                  case_stmt( 5) case_stmt( 4)
+                  #endif
+                  case_stmt( 3) case_stmt( 2)
+                  case_stmt( 1)
+               }
+               exprtk_disable_fallthrough_end
+
+               #undef exprtk_loop
+               #undef case_stmt
+
+               return vec0_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_vecvecass;
+         }
+
+         std::size_t size() const
+         {
+            return vds().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_;
+         bool            initialised_;
+         bool            src_is_ivec_;
+         vds_t           vds_;
+      };
+
+      template <typename T, typename Operation>
+      class assignment_op_node : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         assignment_op_node(const operator_type& opr,
+                            expression_ptr branch0,
+                            expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           var_node_ptr_(0)
+         {
+            if (is_variable_node(binary_node<T>::branch_[0].first))
+            {
+               var_node_ptr_ = static_cast<variable_node<T>*>(binary_node<T>::branch_[0].first);
+            }
+         }
+
+         inline T value() const
+         {
+            if (var_node_ptr_)
+            {
+               T& v = var_node_ptr_->ref();
+               v = Operation::process(v,binary_node<T>::branch_[1].first->value());
+
+               return v;
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+      private:
+
+         variable_node<T>* var_node_ptr_;
+      };
+
+      template <typename T, typename Operation>
+      class assignment_vec_elem_op_node : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         assignment_vec_elem_op_node(const operator_type& opr,
+                                     expression_ptr branch0,
+                                     expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           vec_node_ptr_(0)
+         {
+            if (is_vector_elem_node(binary_node<T>::branch_[0].first))
+            {
+               vec_node_ptr_ = static_cast<vector_elem_node<T>*>(binary_node<T>::branch_[0].first);
+            }
+         }
+
+         inline T value() const
+         {
+            if (vec_node_ptr_)
+            {
+               T& v = vec_node_ptr_->ref();
+                  v = Operation::process(v,binary_node<T>::branch_[1].first->value());
+
+               return v;
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+      private:
+
+         vector_elem_node<T>* vec_node_ptr_;
+      };
+
+      template <typename T, typename Operation>
+      class assignment_rebasevec_elem_op_node : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         assignment_rebasevec_elem_op_node(const operator_type& opr,
+                                           expression_ptr branch0,
+                                           expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           rbvec_node_ptr_(0)
+         {
+            if (is_rebasevector_elem_node(binary_node<T>::branch_[0].first))
+            {
+               rbvec_node_ptr_ = static_cast<rebasevector_elem_node<T>*>(binary_node<T>::branch_[0].first);
+            }
+         }
+
+         inline T value() const
+         {
+            if (rbvec_node_ptr_)
+            {
+               T& v = rbvec_node_ptr_->ref();
+                  v = Operation::process(v,binary_node<T>::branch_[1].first->value());
+
+               return v;
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+      private:
+
+         rebasevector_elem_node<T>* rbvec_node_ptr_;
+      };
+
+      template <typename T, typename Operation>
+      class assignment_rebasevec_celem_op_node : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         assignment_rebasevec_celem_op_node(const operator_type& opr,
+                                            expression_ptr branch0,
+                                            expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           rbvec_node_ptr_(0)
+         {
+            if (is_rebasevector_celem_node(binary_node<T>::branch_[0].first))
+            {
+               rbvec_node_ptr_ = static_cast<rebasevector_celem_node<T>*>(binary_node<T>::branch_[0].first);
+            }
+         }
+
+         inline T value() const
+         {
+            if (rbvec_node_ptr_)
+            {
+               T& v = rbvec_node_ptr_->ref();
+                  v = Operation::process(v,binary_node<T>::branch_[1].first->value());
+
+               return v;
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+      private:
+
+         rebasevector_celem_node<T>* rbvec_node_ptr_;
+      };
+
+      template <typename T, typename Operation>
+      class assignment_vec_op_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;
+
+         assignment_vec_op_node(const operator_type& opr,
+                                expression_ptr branch0,
+                                expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           vec_node_ptr_(0)
+         {
+            if (is_vector_node(binary_node<T>::branch_[0].first))
+            {
+               vec_node_ptr_ = static_cast<vector_node<T>*>(binary_node<T>::branch_[0].first);
+               vds()         = vec_node_ptr_->vds();
+            }
+         }
+
+         inline T value() const
+         {
+            if (vec_node_ptr_)
+            {
+               const T v = binary_node<T>::branch_[1].first->value();
+
+               T* vec = vds().data();
+
+               loop_unroll::details lud(size());
+               const T* upper_bound = vec + lud.upper_bound;
+
+               while (vec < upper_bound)
+               {
+                  #define exprtk_loop(N)       \
+                  Operation::assign(vec[N],v); \
+
+                  exprtk_loop( 0) exprtk_loop( 1)
+                  exprtk_loop( 2) exprtk_loop( 3)
+                  #ifndef exprtk_disable_superscalar_unroll
+                  exprtk_loop( 4) exprtk_loop( 5)
+                  exprtk_loop( 6) exprtk_loop( 7)
+                  exprtk_loop( 8) exprtk_loop( 9)
+                  exprtk_loop(10) exprtk_loop(11)
+                  exprtk_loop(12) exprtk_loop(13)
+                  exprtk_loop(14) exprtk_loop(15)
+                  #endif
+
+                  vec += lud.batch_size;
+               }
+
+               exprtk_disable_fallthrough_begin
+               switch (lud.remainder)
+               {
+                  #define case_stmt(N)                  \
+                  case N : Operation::assign(*vec++,v); \
+
+                  #ifndef exprtk_disable_superscalar_unroll
+                  case_stmt(15) case_stmt(14)
+                  case_stmt(13) case_stmt(12)
+                  case_stmt(11) case_stmt(10)
+                  case_stmt( 9) case_stmt( 8)
+                  case_stmt( 7) case_stmt( 6)
+                  case_stmt( 5) case_stmt( 4)
+                  #endif
+                  case_stmt( 3) case_stmt( 2)
+                  case_stmt( 1)
+               }
+               exprtk_disable_fallthrough_end
+
+
+               #undef exprtk_loop
+               #undef case_stmt
+
+               return vec_node_ptr_->value();
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         vector_node_ptr vec() const
+         {
+            return vec_node_ptr_;
+         }
+
+         vector_node_ptr vec()
+         {
+            return vec_node_ptr_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vecopvalass;
+         }
+
+         std::size_t size() const
+         {
+            return vds().size();
+         }
+
+         vds_t& vds()
+         {
+            return vds_;
+         }
+
+         const vds_t& vds() const
+         {
+            return vds_;
+         }
+
+         bool side_effect() const
+         {
+            return true;
+         }
+
+      private:
+
+         vector_node<T>* vec_node_ptr_;
+         vds_t           vds_;
+      };
+
+      template <typename T, typename Operation>
+      class assignment_vecvec_op_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;
+
+         assignment_vecvec_op_node(const operator_type& opr,
+                                   expression_ptr branch0,
+                                   expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           vec0_node_ptr_(0),
+           vec1_node_ptr_(0),
+           initialised_(false)
+         {
+            if (is_vector_node(binary_node<T>::branch_[0].first))
+            {
+               vec0_node_ptr_ = static_cast<vector_node<T>*>(binary_node<T>::branch_[0].first);
+               vds()          = vec0_node_ptr_->vds();
+            }
+
+            if (is_vector_node(binary_node<T>::branch_[1].first))
+            {
+               vec1_node_ptr_ = static_cast<vector_node<T>*>(binary_node<T>::branch_[1].first);
+               vec1_node_ptr_->vds() = vds();
+            }
+            else 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();
+                  vec1_node_ptr_->vds() = vds();
+               }
+               else
+                  vds_t::match_sizes(vds(),vec1_node_ptr_->vds());
+            }
+
+            initialised_ = (vec0_node_ptr_ && vec1_node_ptr_);
+         }
+
+         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();
+               const T* vec1 = vec1_node_ptr_->vds().data();
+
+               loop_unroll::details lud(size());
+               const T* upper_bound = vec0 + lud.upper_bound;
+
+               while (vec0 < upper_bound)
+               {
+                  #define exprtk_loop(N)                          \
+                  vec0[N] = Operation::process(vec0[N], vec1[N]); \
+
+                  exprtk_loop( 0) exprtk_loop( 1)
+                  exprtk_loop( 2) exprtk_loop( 3)
+                  #ifndef exprtk_disable_superscalar_unroll
+                  exprtk_loop( 4) exprtk_loop( 5)
+                  exprtk_loop( 6) exprtk_loop( 7)
+                  exprtk_loop( 8) exprtk_loop( 9)
+                  exprtk_loop(10) exprtk_loop(11)
+                  exprtk_loop(12) exprtk_loop(13)
+                  exprtk_loop(14) exprtk_loop(15)
+                  #endif
+
+                  vec0 += lud.batch_size;
+                  vec1 += lud.batch_size;
+               }
+
+               int i = 0;
+
+               exprtk_disable_fallthrough_begin
+               switch (lud.remainder)
+               {
+                  #define case_stmt(N)                                              \
+                  case N : { vec0[i] = Operation::process(vec0[i], vec1[i]); ++i; } \
+
+                  #ifndef exprtk_disable_superscalar_unroll
+                  case_stmt(15) case_stmt(14)
+                  case_stmt(13) case_stmt(12)
+                  case_stmt(11) case_stmt(10)
+                  case_stmt( 9) case_stmt( 8)
+                  case_stmt( 7) case_stmt( 6)
+                  case_stmt( 5) case_stmt( 4)
+                  #endif
+                  case_stmt( 3) case_stmt( 2)
+                  case_stmt( 1)
+               }
+               exprtk_disable_fallthrough_end
+
+               #undef exprtk_loop
+               #undef case_stmt
+
+               return vec0_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_vecopvecass;
+         }
+
+         std::size_t size() const
+         {
+            return vds().size();
+         }
+
+         vds_t& vds()
+         {
+            return vds_;
+         }
+
+         const vds_t& vds() const
+         {
+            return vds_;
+         }
+
+         bool side_effect() const
+         {
+            return true;
+         }
+
+      private:
+
+         vector_node<T>* vec0_node_ptr_;
+         vector_node<T>* vec1_node_ptr_;
+         bool            initialised_;
+         vds_t           vds_;
+      };
+
+      template <typename T, typename Operation>
+      class vec_binop_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 vector_holder<T>*   vector_holder_ptr;
+         typedef vec_data_store<T>               vds_t;
+
+         vec_binop_vecvec_node(const operator_type& opr,
+                               expression_ptr branch0,
+                               expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           vec0_node_ptr_(0),
+           vec1_node_ptr_(0),
+           temp_         (0),
+           temp_vec_node_(0),
+           initialised_(false)
+         {
+            bool v0_is_ivec = false;
+            bool v1_is_ivec = false;
+
+            if (is_vector_node(binary_node<T>::branch_[0].first))
+            {
+               vec0_node_ptr_ = static_cast<vector_node_ptr>(binary_node<T>::branch_[0].first);
+            }
+            else 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();
+                  v0_is_ivec     = true;
+               }
+            }
+
+            if (is_vector_node(binary_node<T>::branch_[1].first))
+            {
+               vec1_node_ptr_ = static_cast<vector_node_ptr>(binary_node<T>::branch_[1].first);
+            }
+            else 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();
+                  v1_is_ivec     = true;
+               }
+            }
+
+            if (vec0_node_ptr_ && vec1_node_ptr_)
+            {
+               vector_holder<T>& vec0 = vec0_node_ptr_->vec_holder();
+               vector_holder<T>& vec1 = vec1_node_ptr_->vec_holder();
+
+               if (v0_is_ivec && (vec0.size() <= vec1.size()))
+                  vds_ = vds_t(vec0_node_ptr_->vds());
+               else if (v1_is_ivec && (vec1.size() <= vec0.size()))
+                  vds_ = vds_t(vec1_node_ptr_->vds());
+               else
+                  vds_ = vds_t(std::min(vec0.size(),vec1.size()));
+
+               temp_          = new vector_holder<T>(vds().data(),vds().size());
+               temp_vec_node_ = new vector_node<T>  (vds(),temp_);
+
+               initialised_ = true;
+            }
+         }
+
+        ~vec_binop_vecvec_node()
+         {
+            delete temp_;
+            delete temp_vec_node_;
+         }
+
+         inline T value() const
+         {
+            if (initialised_)
+            {
+               binary_node<T>::branch_[0].first->value();
+               binary_node<T>::branch_[1].first->value();
+
+               const T* vec0 = vec0_node_ptr_->vds().data();
+               const T* vec1 = vec1_node_ptr_->vds().data();
+                     T* vec2 = vds().data();
+
+               loop_unroll::details lud(size());
+               const T* upper_bound = vec2 + lud.upper_bound;
+
+               while (vec2 < upper_bound)
+               {
+                  #define exprtk_loop(N)                          \
+                  vec2[N] = Operation::process(vec0[N], vec1[N]); \
+
+                  exprtk_loop( 0) exprtk_loop( 1)
+                  exprtk_loop( 2) exprtk_loop( 3)
+                  #ifndef exprtk_disable_superscalar_unroll
+                  exprtk_loop( 4) exprtk_loop( 5)
+                  exprtk_loop( 6) exprtk_loop( 7)
+                  exprtk_loop( 8) exprtk_loop( 9)
+                  exprtk_loop(10) exprtk_loop(11)
+                  exprtk_loop(12) exprtk_loop(13)
+                  exprtk_loop(14) exprtk_loop(15)
+                  #endif
+
+                  vec0 += lud.batch_size;
+                  vec1 += lud.batch_size;
+                  vec2 += lud.batch_size;
+               }
+
+               int i = 0;
+
+               exprtk_disable_fallthrough_begin
+               switch (lud.remainder)
+               {
+                  #define case_stmt(N)                                              \
+                  case N : { vec2[i] = Operation::process(vec0[i], vec1[i]); ++i; } \
+
+                  #ifndef exprtk_disable_superscalar_unroll
+                  case_stmt(15) case_stmt(14)
+                  case_stmt(13) case_stmt(12)
+                  case_stmt(11) case_stmt(10)
+                  case_stmt( 9) case_stmt( 8)
+                  case_stmt( 7) case_stmt( 6)
+                  case_stmt( 5) case_stmt( 4)
+                  #endif
+                  case_stmt( 3) case_stmt( 2)
+                  case_stmt( 1)
+               }
+               exprtk_disable_fallthrough_end
+
+               #undef exprtk_loop
+               #undef case_stmt
+
+               return (vds().data())[0];
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         vector_node_ptr vec() const
+         {
+            return temp_vec_node_;
+         }
+
+         vector_node_ptr vec()
+         {
+            return temp_vec_node_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vecvecarith;
+         }
+
+         std::size_t size() const
+         {
+            return vds_.size();
+         }
+
+         vds_t& vds()
+         {
+            return vds_;
+         }
+
+         const vds_t& vds() const
+         {
+            return vds_;
+         }
+
+      private:
+
+         vector_node_ptr   vec0_node_ptr_;
+         vector_node_ptr   vec1_node_ptr_;
+         vector_holder_ptr temp_;
+         vector_node_ptr   temp_vec_node_;
+         bool              initialised_;
+         vds_t             vds_;
+      };
+
+      template <typename T, typename Operation>
+      class vec_binop_vecval_node : public binary_node     <T>,
+                                    public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node<T>*    expression_ptr;
+         typedef vector_node<T>*       vector_node_ptr;
+         typedef vector_holder<T>*   vector_holder_ptr;
+         typedef vec_data_store<T>               vds_t;
+
+         vec_binop_vecval_node(const operator_type& opr,
+                               expression_ptr branch0,
+                               expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           vec0_node_ptr_(0),
+           temp_         (0),
+           temp_vec_node_(0)
+         {
+            bool v0_is_ivec = false;
+
+            if (is_vector_node(binary_node<T>::branch_[0].first))
+            {
+               vec0_node_ptr_ = static_cast<vector_node_ptr>(binary_node<T>::branch_[0].first);
+            }
+            else 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();
+                  v0_is_ivec     = true;
+               }
+            }
+
+            if (vec0_node_ptr_)
+            {
+               if (v0_is_ivec)
+                  vds() = vec0_node_ptr_->vds();
+               else
+                  vds() = vds_t(vec0_node_ptr_->size());
+
+               temp_          = new vector_holder<T>(vds());
+               temp_vec_node_ = new vector_node<T>  (vds(),temp_);
+            }
+         }
+
+        ~vec_binop_vecval_node()
+         {
+            delete temp_;
+            delete temp_vec_node_;
+         }
+
+         inline T value() const
+         {
+            if (vec0_node_ptr_)
+            {
+                           binary_node<T>::branch_[0].first->value();
+               const T v = binary_node<T>::branch_[1].first->value();
+
+               const T* vec0 = vec0_node_ptr_->vds().data();
+                     T* vec1 = vds().data();
+
+               loop_unroll::details lud(size());
+               const T* upper_bound = vec0 + lud.upper_bound;
+
+               while (vec0 < upper_bound)
+               {
+                  #define exprtk_loop(N)                    \
+                  vec1[N] = Operation::process(vec0[N], v); \
+
+                  exprtk_loop( 0) exprtk_loop( 1)
+                  exprtk_loop( 2) exprtk_loop( 3)
+                  #ifndef exprtk_disable_superscalar_unroll
+                  exprtk_loop( 4) exprtk_loop( 5)
+                  exprtk_loop( 6) exprtk_loop( 7)
+                  exprtk_loop( 8) exprtk_loop( 9)
+                  exprtk_loop(10) exprtk_loop(11)
+                  exprtk_loop(12) exprtk_loop(13)
+                  exprtk_loop(14) exprtk_loop(15)
+                  #endif
+
+                  vec0 += lud.batch_size;
+                  vec1 += lud.batch_size;
+               }
+
+               int i = 0;
+
+               exprtk_disable_fallthrough_begin
+               switch (lud.remainder)
+               {
+                  #define case_stmt(N)                                        \
+                  case N : { vec1[i] = Operation::process(vec0[i], v); ++i; } \
+
+                  #ifndef exprtk_disable_superscalar_unroll
+                  case_stmt(15) case_stmt(14)
+                  case_stmt(13) case_stmt(12)
+                  case_stmt(11) case_stmt(10)
+                  case_stmt( 9) case_stmt( 8)
+                  case_stmt( 7) case_stmt( 6)
+                  case_stmt( 5) case_stmt( 4)
+                  #endif
+                  case_stmt( 3) case_stmt( 2)
+                  case_stmt( 1)
+               }
+               exprtk_disable_fallthrough_end
+
+               #undef exprtk_loop
+               #undef case_stmt
+
+               return (vds().data())[0];
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         vector_node_ptr vec() const
+         {
+            return temp_vec_node_;
+         }
+
+         vector_node_ptr vec()
+         {
+            return temp_vec_node_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vecvalarith;
+         }
+
+         std::size_t size() const
+         {
+            return vds().size();
+         }
+
+         vds_t& vds()
+         {
+            return vds_;
+         }
+
+         const vds_t& vds() const
+         {
+            return vds_;
+         }
+
+      private:
+
+         vector_node_ptr   vec0_node_ptr_;
+         vector_holder_ptr temp_;
+         vector_node_ptr   temp_vec_node_;
+         vds_t             vds_;
+      };
+
+      template <typename T, typename Operation>
+      class vec_binop_valvec_node : public binary_node     <T>,
+                                    public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node<T>*    expression_ptr;
+         typedef vector_node<T>*       vector_node_ptr;
+         typedef vector_holder<T>*   vector_holder_ptr;
+         typedef vec_data_store<T>               vds_t;
+
+         vec_binop_valvec_node(const operator_type& opr,
+                               expression_ptr branch0,
+                               expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           vec1_node_ptr_(0),
+           temp_         (0),
+           temp_vec_node_(0)
+         {
+            bool v1_is_ivec = false;
+
+            if (is_vector_node(binary_node<T>::branch_[1].first))
+            {
+               vec1_node_ptr_ = static_cast<vector_node_ptr>(binary_node<T>::branch_[1].first);
+            }
+            else 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();
+                  v1_is_ivec     = true;
+               }
+            }
+
+            if (vec1_node_ptr_)
+            {
+               if (v1_is_ivec)
+                  vds() = vec1_node_ptr_->vds();
+               else
+                  vds() = vds_t(vec1_node_ptr_->size());
+
+               temp_          = new vector_holder<T>(vds());
+               temp_vec_node_ = new vector_node<T>  (vds(),temp_);
+            }
+         }
+
+        ~vec_binop_valvec_node()
+         {
+            delete temp_;
+            delete temp_vec_node_;
+         }
+
+         inline T value() const
+         {
+            if (vec1_node_ptr_)
+            {
+               const T v = binary_node<T>::branch_[0].first->value();
+                           binary_node<T>::branch_[1].first->value();
+
+                     T* vec0 = vds().data();
+               const T* vec1 = vec1_node_ptr_->vds().data();
+
+               loop_unroll::details lud(size());
+               const T* upper_bound = vec0 + lud.upper_bound;
+
+               while (vec0 < upper_bound)
+               {
+                  #define exprtk_loop(N)                    \
+                  vec0[N] = Operation::process(v, vec1[N]); \
+
+                  exprtk_loop( 0) exprtk_loop( 1)
+                  exprtk_loop( 2) exprtk_loop( 3)
+                  #ifndef exprtk_disable_superscalar_unroll
+                  exprtk_loop( 4) exprtk_loop( 5)
+                  exprtk_loop( 6) exprtk_loop( 7)
+                  exprtk_loop( 8) exprtk_loop( 9)
+                  exprtk_loop(10) exprtk_loop(11)
+                  exprtk_loop(12) exprtk_loop(13)
+                  exprtk_loop(14) exprtk_loop(15)
+                  #endif
+
+                  vec0 += lud.batch_size;
+                  vec1 += lud.batch_size;
+               }
+
+               int i = 0;
+
+               exprtk_disable_fallthrough_begin
+               switch (lud.remainder)
+               {
+                  #define case_stmt(N)                                        \
+                  case N : { vec0[i] = Operation::process(v, vec1[i]); ++i; } \
+
+                  #ifndef exprtk_disable_superscalar_unroll
+                  case_stmt(15) case_stmt(14)
+                  case_stmt(13) case_stmt(12)
+                  case_stmt(11) case_stmt(10)
+                  case_stmt( 9) case_stmt( 8)
+                  case_stmt( 7) case_stmt( 6)
+                  case_stmt( 5) case_stmt( 4)
+                  #endif
+                  case_stmt( 3) case_stmt( 2)
+                  case_stmt( 1)
+               }
+               exprtk_disable_fallthrough_end
+
+               #undef exprtk_loop
+               #undef case_stmt
+
+               return (vds().data())[0];
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         vector_node_ptr vec() const
+         {
+            return temp_vec_node_;
+         }
+
+         vector_node_ptr vec()
+         {
+            return temp_vec_node_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vecvalarith;
+         }
+
+         std::size_t size() const
+         {
+            return vds().size();
+         }
+
+         vds_t& vds()
+         {
+            return vds_;
+         }
+
+         const vds_t& vds() const
+         {
+            return vds_;
+         }
+
+      private:
+
+         vector_node_ptr   vec1_node_ptr_;
+         vector_holder_ptr temp_;
+         vector_node_ptr   temp_vec_node_;
+         vds_t             vds_;
+      };
+
+      template <typename T, typename Operation>
+      class unary_vector_node : public unary_node      <T>,
+                                public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node<T>*    expression_ptr;
+         typedef vector_node<T>*       vector_node_ptr;
+         typedef vector_holder<T>*   vector_holder_ptr;
+         typedef vec_data_store<T>               vds_t;
+
+         unary_vector_node(const operator_type& opr, expression_ptr branch0)
+         : unary_node<T>(opr, branch0),
+           vec0_node_ptr_(0),
+           temp_         (0),
+           temp_vec_node_(0)
+         {
+            bool vec0_is_ivec = false;
+
+            if (is_vector_node(unary_node<T>::branch_))
+            {
+               vec0_node_ptr_ = static_cast<vector_node_ptr>(unary_node<T>::branch_);
+            }
+            else if (is_ivector_node(unary_node<T>::branch_))
+            {
+               vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
+
+               if (0 != (vi = dynamic_cast<vector_interface<T>*>(unary_node<T>::branch_)))
+               {
+                  vec0_node_ptr_ = vi->vec();
+                  vec0_is_ivec   = true;
+               }
+            }
+
+            if (vec0_node_ptr_)
+            {
+               if (vec0_is_ivec)
+                  vds_ = vec0_node_ptr_->vds();
+               else
+                  vds_ = vds_t(vec0_node_ptr_->size());
+
+               temp_          = new vector_holder<T>(vds());
+               temp_vec_node_ = new vector_node<T>  (vds(),temp_);
+            }
+         }
+
+        ~unary_vector_node()
+         {
+            delete temp_;
+            delete temp_vec_node_;
+         }
+
+         inline T value() const
+         {
+            unary_node<T>::branch_->value();
+
+            if (vec0_node_ptr_)
+            {
+               const T* vec0 = vec0_node_ptr_->vds().data();
+                     T* vec1 = vds().data();
+
+               loop_unroll::details lud(size());
+               const T* upper_bound = vec0 + lud.upper_bound;
+
+               while (vec0 < upper_bound)
+               {
+                  #define exprtk_loop(N)                 \
+                  vec1[N] = Operation::process(vec0[N]); \
+
+                  exprtk_loop( 0) exprtk_loop( 1)
+                  exprtk_loop( 2) exprtk_loop( 3)
+                  #ifndef exprtk_disable_superscalar_unroll
+                  exprtk_loop( 4) exprtk_loop( 5)
+                  exprtk_loop( 6) exprtk_loop( 7)
+                  exprtk_loop( 8) exprtk_loop( 9)
+                  exprtk_loop(10) exprtk_loop(11)
+                  exprtk_loop(12) exprtk_loop(13)
+                  exprtk_loop(14) exprtk_loop(15)
+                  #endif
+
+                  vec0 += lud.batch_size;
+                  vec1 += lud.batch_size;
+               }
+
+               int i = 0;
+
+               exprtk_disable_fallthrough_begin
+               switch (lud.remainder)
+               {
+                  #define case_stmt(N)                                     \
+                  case N : { vec1[i] = Operation::process(vec0[i]); ++i; } \
+
+                  #ifndef exprtk_disable_superscalar_unroll
+                  case_stmt(15) case_stmt(14)
+                  case_stmt(13) case_stmt(12)
+                  case_stmt(11) case_stmt(10)
+                  case_stmt( 9) case_stmt( 8)
+                  case_stmt( 7) case_stmt( 6)
+                  case_stmt( 5) case_stmt( 4)
+                  #endif
+                  case_stmt( 3) case_stmt( 2)
+                  case_stmt( 1)
+               }
+               exprtk_disable_fallthrough_end
+
+               #undef exprtk_loop
+               #undef case_stmt
+
+               return (vds().data())[0];
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         vector_node_ptr vec() const
+         {
+            return temp_vec_node_;
+         }
+
+         vector_node_ptr vec()
+         {
+            return temp_vec_node_;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vecunaryop;
+         }
+
+         std::size_t size() const
+         {
+            return vds().size();
+         }
+
+         vds_t& vds()
+         {
+            return vds_;
+         }
+
+         const vds_t& vds() const
+         {
+            return vds_;
+         }
+
+      private:
+
+         vector_node_ptr   vec0_node_ptr_;
+         vector_holder_ptr temp_;
+         vector_node_ptr   temp_vec_node_;
+         vds_t             vds_;
+      };
+
+      template <typename T>
+      class scand_node : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         scand_node(const operator_type& opr,
+                    expression_ptr branch0,
+                    expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1)
+         {}
+
+         inline T value() const
+         {
+            return (
+                     std::not_equal_to<T>()
+                        (T(0),binary_node<T>::branch_[0].first->value()) &&
+                     std::not_equal_to<T>()
+                        (T(0),binary_node<T>::branch_[1].first->value())
+                   ) ? T(1) : T(0);
+         }
+      };
+
+      template <typename T>
+      class scor_node : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         scor_node(const operator_type& opr,
+                   expression_ptr branch0,
+                   expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1)
+         {}
+
+         inline T value() const
+         {
+            return (
+                     std::not_equal_to<T>()
+                        (T(0),binary_node<T>::branch_[0].first->value()) ||
+                     std::not_equal_to<T>()
+                        (T(0),binary_node<T>::branch_[1].first->value())
+                   ) ? T(1) : T(0);
+         }
+      };
+
+      template <typename T, typename IFunction, std::size_t N>
+      class function_N_node : public expression_node<T>
+      {
+      public:
+
+         // Function of N paramters.
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+         typedef IFunction ifunction;
+
+         explicit function_N_node(ifunction* func)
+         : function_((N == func->param_count) ? func : reinterpret_cast<ifunction*>(0)),
+           parameter_count_(func->param_count)
+         {}
+
+        ~function_N_node()
+         {
+            cleanup_branches::execute<T,N>(branch_);
+         }
+
+         template <std::size_t NumBranches>
+         bool init_branches(expression_ptr (&b)[NumBranches])
+         {
+            // Needed for incompetent and broken msvc compiler versions
+            #ifdef _MSC_VER
+             #pragma warning(push)
+             #pragma warning(disable: 4127)
+            #endif
+            if (N != NumBranches)
+               return false;
+            else
+            {
+               for (std::size_t i = 0; i < NumBranches; ++i)
+               {
+                  if (b[i])
+                     branch_[i] = std::make_pair(b[i],branch_deletable(b[i]));
+                  else
+                     return false;
+               }
+               return true;
+            }
+            #ifdef _MSC_VER
+             #pragma warning(pop)
+            #endif
+         }
+
+         inline bool operator <(const function_N_node<T,IFunction,N>& fn) const
+         {
+            return this < (&fn);
+         }
+
+         inline T value() const
+         {
+            // Needed for incompetent and broken msvc compiler versions
+            #ifdef _MSC_VER
+             #pragma warning(push)
+             #pragma warning(disable: 4127)
+            #endif
+            if ((0 == function_) || (0 == N))
+               return std::numeric_limits<T>::quiet_NaN();
+            else
+            {
+               T v[N];
+               evaluate_branches<T,N>::execute(v,branch_);
+               return invoke<T,N>::execute(*function_,v);
+            }
+            #ifdef _MSC_VER
+             #pragma warning(pop)
+            #endif
+         }
+
+         template <typename T_, std::size_t BranchCount>
+         struct evaluate_branches
+         {
+            static inline void execute(T_ (&v)[BranchCount], const branch_t (&b)[BranchCount])
+            {
+               for (std::size_t i = 0; i < BranchCount; ++i)
+               {
+                  v[i] = b[i].first->value();
+               }
+            }
+         };
+
+         template <typename T_>
+         struct evaluate_branches <T_,5>
+         {
+            static inline void execute(T_ (&v)[5], const branch_t (&b)[5])
+            {
+               v[0] = b[0].first->value();
+               v[1] = b[1].first->value();
+               v[2] = b[2].first->value();
+               v[3] = b[3].first->value();
+               v[4] = b[4].first->value();
+            }
+         };
+
+         template <typename T_>
+         struct evaluate_branches <T_,4>
+         {
+            static inline void execute(T_ (&v)[4], const branch_t (&b)[4])
+            {
+               v[0] = b[0].first->value();
+               v[1] = b[1].first->value();
+               v[2] = b[2].first->value();
+               v[3] = b[3].first->value();
+            }
+         };
+
+         template <typename T_>
+         struct evaluate_branches <T_,3>
+         {
+            static inline void execute(T_ (&v)[3], const branch_t (&b)[3])
+            {
+               v[0] = b[0].first->value();
+               v[1] = b[1].first->value();
+               v[2] = b[2].first->value();
+            }
+         };
+
+         template <typename T_>
+         struct evaluate_branches <T_,2>
+         {
+            static inline void execute(T_ (&v)[2], const branch_t (&b)[2])
+            {
+               v[0] = b[0].first->value();
+               v[1] = b[1].first->value();
+            }
+         };
+
+         template <typename T_>
+         struct evaluate_branches <T_,1>
+         {
+            static inline void execute(T_ (&v)[1], const branch_t (&b)[1])
+            {
+               v[0] = b[0].first->value();
+            }
+         };
+
+         template <typename T_, std::size_t ParamCount>
+         struct invoke { static inline T execute(ifunction&, branch_t (&)[ParamCount]) { return std::numeric_limits<T_>::quiet_NaN(); } };
+
+         template <typename T_>
+         struct invoke<T_,20>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[20])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13],v[14],v[15],v[16],v[17],v[18],v[19]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,19>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[19])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13],v[14],v[15],v[16],v[17],v[18]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,18>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[18])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13],v[14],v[15],v[16],v[17]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,17>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[17])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13],v[14],v[15],v[16]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,16>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[16])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13],v[14],v[15]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,15>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[15])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13],v[14]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,14>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[14])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12],v[13]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,13>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[13])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11],v[12]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,12>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[12])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10],v[11]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,11>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[11])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9],v[10]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,10>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[10])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8],v[9]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,9>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[9])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7],v[8]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,8>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[8])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,7>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[7])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5],v[6]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,6>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[6])
+            { return f(v[0],v[1],v[2],v[3],v[4],v[5]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,5>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[5])
+            { return f(v[0],v[1],v[2],v[3],v[4]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,4>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[4])
+            { return f(v[0],v[1],v[2],v[3]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,3>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[3])
+            { return f(v[0],v[1],v[2]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,2>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[2])
+            { return f(v[0],v[1]); }
+         };
+
+         template <typename T_>
+         struct invoke<T_,1>
+         {
+            static inline T_ execute(ifunction& f, T_ (&v)[1])
+            { return f(v[0]); }
+         };
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_function;
+         }
+
+      private:
+
+         ifunction*  function_;
+         std::size_t parameter_count_;
+         branch_t    branch_[N];
+      };
+
+      template <typename T, typename IFunction>
+      class function_N_node<T,IFunction,0> : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef IFunction ifunction;
+
+         explicit function_N_node(ifunction* func)
+         : function_((0 == func->param_count) ? func : reinterpret_cast<ifunction*>(0))
+         {}
+
+         inline bool operator <(const function_N_node<T,IFunction,0>& fn) const
+         {
+            return this < (&fn);
+         }
+
+         inline T value() const
+         {
+            if (function_)
+               return (*function_)();
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_function;
+         }
+
+      private:
+
+         ifunction* function_;
+      };
+
+      template <typename T, typename VarArgFunction>
+      class vararg_function_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         vararg_function_node(VarArgFunction*  func,
+                              const std::vector<expression_ptr>& arg_list)
+         : function_(func),
+           arg_list_(arg_list)
+         {
+            value_list_.resize(arg_list.size(),std::numeric_limits<T>::quiet_NaN());
+         }
+
+        ~vararg_function_node()
+         {
+            for (std::size_t i = 0; i < arg_list_.size(); ++i)
+            {
+               if (arg_list_[i] && !details::is_variable_node(arg_list_[i]))
+               {
+                  destroy_node(arg_list_[i]);
+               }
+            }
+         }
+
+         inline bool operator <(const vararg_function_node<T,VarArgFunction>& fn) const
+         {
+            return this < (&fn);
+         }
+
+         inline T value() const
+         {
+            if (function_)
+            {
+               populate_value_list();
+               return (*function_)(value_list_);
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vafunction;
+         }
+
+      private:
+
+         inline void populate_value_list() const
+         {
+            for (std::size_t i = 0; i < arg_list_.size(); ++i)
+            {
+               value_list_[i] = arg_list_[i]->value();
+            }
+         }
+
+         VarArgFunction* function_;
+         std::vector<expression_ptr> arg_list_;
+         mutable std::vector<T> value_list_;
+      };
+
+      template <typename T, typename GenericFunction>
+      class generic_function_node : public expression_node<T>
+      {
+      public:
+
+         typedef type_store<T>                         type_store_t;
+         typedef expression_node<T>*                 expression_ptr;
+         typedef variable_node<T>                   variable_node_t;
+         typedef vector_node<T>                       vector_node_t;
+         typedef variable_node_t*               variable_node_ptr_t;
+         typedef vector_node_t*                   vector_node_ptr_t;
+         typedef range_interface<T>               range_interface_t;
+         typedef range_data_type<T>               range_data_type_t;
+         typedef range_pack<T>                              range_t;
+         typedef std::pair<expression_ptr,bool>            branch_t;
+         typedef std::pair<void*,std::size_t>                void_t;
+         typedef std::vector<T>                            tmp_vs_t;
+         typedef std::vector<type_store_t>         typestore_list_t;
+         typedef std::vector<range_data_type_t>        range_list_t;
+
+         generic_function_node(const std::vector<expression_ptr>& arg_list,
+                               GenericFunction* func = (GenericFunction*)(0))
+         : function_(func),
+           arg_list_(arg_list)
+         {}
+
+         virtual ~generic_function_node()
+         {
+            cleanup_branches::execute(branch_);
+         }
+
+         virtual bool init_branches()
+         {
+            expr_as_vec1_store_.resize(arg_list_.size(),T(0)               );
+            typestore_list_    .resize(arg_list_.size(),type_store_t()     );
+            range_list_        .resize(arg_list_.size(),range_data_type_t());
+            branch_            .resize(arg_list_.size(),branch_t((expression_ptr)0,false));
+
+            for (std::size_t i = 0; i < arg_list_.size(); ++i)
+            {
+               type_store_t& ts = typestore_list_[i];
+
+               if (0 == arg_list_[i])
+                  return false;
+               else if (is_ivector_node(arg_list_[i]))
+               {
+                  vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
+
+                  if (0 == (vi = dynamic_cast<vector_interface<T>*>(arg_list_[i])))
+                     return false;
+
+                  ts.size = vi->size();
+                  ts.data = vi->vds().data();
+                  ts.type = type_store_t::e_vector;
+                  vi->vec()->vec_holder().set_ref(&ts.vec_data);
+               }
+               #ifndef exprtk_disable_string_capabilities
+               else if (is_generally_string_node(arg_list_[i]))
+               {
+                  string_base_node<T>* sbn = reinterpret_cast<string_base_node<T>*>(0);
+
+                  if (0 == (sbn = dynamic_cast<string_base_node<T>*>(arg_list_[i])))
+                     return false;
+
+                  ts.size = sbn->size();
+                  ts.data = reinterpret_cast<void*>(const_cast<char_ptr>(sbn->base()));
+                  ts.type = type_store_t::e_string;
+
+                  range_list_[i].data      = ts.data;
+                  range_list_[i].size      = ts.size;
+                  range_list_[i].type_size = sizeof(char);
+                  range_list_[i].str_node  = sbn;
+
+                  range_interface_t* ri = reinterpret_cast<range_interface_t*>(0);
+
+                  if (0 == (ri = dynamic_cast<range_interface_t*>(arg_list_[i])))
+                     return false;
+
+                  range_t& rp = ri->range_ref();
+
+                  if (
+                       rp.const_range() &&
+                       is_const_string_range_node(arg_list_[i])
+                     )
+                  {
+                     ts.size = rp.const_size();
+                     ts.data = static_cast<char_ptr>(ts.data) + rp.n0_c.second;
+                     range_list_[i].range = reinterpret_cast<range_t*>(0);
+                  }
+                  else
+                     range_list_[i].range = &(ri->range_ref());
+               }
+               #endif
+               else if (is_variable_node(arg_list_[i]))
+               {
+                  variable_node_ptr_t var = variable_node_ptr_t(0);
+
+                  if (0 == (var = dynamic_cast<variable_node_ptr_t>(arg_list_[i])))
+                     return false;
+
+                  ts.size = 1;
+                  ts.data = &var->ref();
+                  ts.type = type_store_t::e_scalar;
+               }
+               else
+               {
+                  ts.size = 1;
+                  ts.data = reinterpret_cast<void*>(&expr_as_vec1_store_[i]);
+                  ts.type = type_store_t::e_scalar;
+               }
+
+               branch_[i] = std::make_pair(arg_list_[i],branch_deletable(arg_list_[i]));
+            }
+
+            return true;
+         }
+
+         inline bool operator <(const generic_function_node<T,GenericFunction>& fn) const
+         {
+            return this < (&fn);
+         }
+
+         inline T value() const
+         {
+            if (function_)
+            {
+               if (populate_value_list())
+               {
+                  typedef typename GenericFunction::parameter_list_t parameter_list_t;
+
+                  return (*function_)(parameter_list_t(typestore_list_));
+               }
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_genfunction;
+         }
+
+      protected:
+
+         inline virtual bool populate_value_list() const
+         {
+            for (std::size_t i = 0; i < branch_.size(); ++i)
+            {
+               expr_as_vec1_store_[i] = branch_[i].first->value();
+            }
+
+            for (std::size_t i = 0; i < branch_.size(); ++i)
+            {
+               range_data_type_t& rdt = range_list_[i];
+
+               if (rdt.range)
+               {
+                  range_t&    rp = (*rdt.range);
+                  std::size_t r0 = 0;
+                  std::size_t r1 = 0;
+
+                  if (rp(r0,r1,rdt.size))
+                  {
+                     type_store_t& ts = typestore_list_[i];
+
+                     ts.size = rp.cache_size();
+                     #ifndef exprtk_disable_string_capabilities
+                     if (ts.type == type_store_t::e_string)
+                        ts.data = const_cast<char_ptr>(rdt.str_node->base()) + rp.cache.first;
+                     else
+                     #endif
+                        ts.data = static_cast<char_ptr>(rdt.data) + (rp.cache.first * rdt.type_size);
+                  }
+                  else
+                     return false;
+               }
+            }
+
+            return true;
+         }
+
+         GenericFunction* function_;
+         mutable typestore_list_t typestore_list_;
+
+      private:
+
+         std::vector<expression_ptr> arg_list_;
+         std::vector<branch_t>         branch_;
+         mutable tmp_vs_t  expr_as_vec1_store_;
+         mutable range_list_t      range_list_;
+      };
+
+      #ifndef exprtk_disable_string_capabilities
+      template <typename T, typename StringFunction>
+      class string_function_node : public generic_function_node<T,StringFunction>,
+                                   public string_base_node<T>,
+                                   public range_interface <T>
+      {
+      public:
+
+         typedef generic_function_node<T,StringFunction> gen_function_t;
+         typedef range_pack<T> range_t;
+
+         string_function_node(StringFunction* func,
+                              const std::vector<typename gen_function_t::expression_ptr>& arg_list)
+         : gen_function_t(arg_list,func)
+         {
+            range_.n0_c = std::make_pair<bool,std::size_t>(true,0);
+            range_.n1_c = std::make_pair<bool,std::size_t>(true,0);
+            range_.cache.first  = range_.n0_c.second;
+            range_.cache.second = range_.n1_c.second;
+         }
+
+         inline bool operator <(const string_function_node<T,StringFunction>& fn) const
+         {
+            return this < (&fn);
+         }
+
+         inline T value() const
+         {
+            if (gen_function_t::function_)
+            {
+               if (gen_function_t::populate_value_list())
+               {
+                  typedef typename StringFunction::parameter_list_t parameter_list_t;
+
+                  const T result = (*gen_function_t::function_)
+                                      (
+                                        ret_string_,
+                                        parameter_list_t(gen_function_t::typestore_list_)
+                                      );
+
+                  range_.n1_c.second  = ret_string_.size() - 1;
+                  range_.cache.second = range_.n1_c.second;
+
+                  return result;
+               }
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_strfunction;
+         }
+
+         std::string str() const
+         {
+            return ret_string_;
+         }
+
+         char_cptr base() const
+         {
+           return &ret_string_[0];
+         }
+
+         std::size_t size() const
+         {
+            return ret_string_.size();
+         }
+
+         range_t& range_ref()
+         {
+            return range_;
+         }
+
+         const range_t& range_ref() const
+         {
+            return range_;
+         }
+
+      protected:
+
+         mutable range_t     range_;
+         mutable std::string ret_string_;
+      };
+      #endif
+
+      template <typename T, typename GenericFunction>
+      class multimode_genfunction_node : public generic_function_node<T,GenericFunction>
+      {
+      public:
+
+         typedef generic_function_node<T,GenericFunction> gen_function_t;
+         typedef range_pack<T> range_t;
+
+         multimode_genfunction_node(GenericFunction* func,
+                                    const std::size_t& param_seq_index,
+                                    const std::vector<typename gen_function_t::expression_ptr>& arg_list)
+         : gen_function_t(arg_list,func),
+           param_seq_index_(param_seq_index)
+         {}
+
+         inline T value() const
+         {
+            if (gen_function_t::function_)
+            {
+               if (gen_function_t::populate_value_list())
+               {
+                  typedef typename GenericFunction::parameter_list_t parameter_list_t;
+
+                  return (*gen_function_t::function_)
+                            (
+                              param_seq_index_,
+                              parameter_list_t(gen_function_t::typestore_list_)
+                            );
+               }
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_genfunction;
+         }
+
+      private:
+
+         std::size_t param_seq_index_;
+      };
+
+      #ifndef exprtk_disable_string_capabilities
+      template <typename T, typename StringFunction>
+      class multimode_strfunction_node : public string_function_node<T,StringFunction>
+      {
+      public:
+
+         typedef string_function_node<T,StringFunction> str_function_t;
+         typedef range_pack<T> range_t;
+
+         multimode_strfunction_node(StringFunction* func,
+                                    const std::size_t& param_seq_index,
+                                    const std::vector<typename str_function_t::expression_ptr>& arg_list)
+         : str_function_t(func,arg_list),
+           param_seq_index_(param_seq_index)
+         {}
+
+         inline T value() const
+         {
+            if (str_function_t::function_)
+            {
+               if (str_function_t::populate_value_list())
+               {
+                  typedef typename StringFunction::parameter_list_t parameter_list_t;
+
+                  const T result = (*str_function_t::function_)
+                                      (
+                                        param_seq_index_,
+                                        str_function_t::ret_string_,
+                                        parameter_list_t(str_function_t::typestore_list_)
+                                      );
+
+                  str_function_t::range_.n1_c.second  = str_function_t::ret_string_.size() - 1;
+                  str_function_t::range_.cache.second = str_function_t::range_.n1_c.second;
+
+                  return result;
+               }
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_strfunction;
+         }
+
+      private:
+
+         const std::size_t param_seq_index_;
+      };
+      #endif
+
+      class return_exception
+      {};
+
+      template <typename T>
+      class null_igenfunc
+      {
+      public:
+
+         virtual ~null_igenfunc()
+         {}
+
+         typedef type_store<T> generic_type;
+         typedef typename generic_type::parameter_list parameter_list_t;
+
+         inline virtual T operator() (parameter_list_t)
+         {
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+      };
+
+      #ifndef exprtk_disable_return_statement
+      template <typename T>
+      class return_node : public generic_function_node<T,null_igenfunc<T> >
+      {
+      public:
+
+         typedef null_igenfunc<T> igeneric_function_t;
+         typedef igeneric_function_t* igeneric_function_ptr;
+         typedef generic_function_node<T,igeneric_function_t> gen_function_t;
+         typedef results_context<T> results_context_t;
+
+         return_node(const std::vector<typename gen_function_t::expression_ptr>& arg_list,
+                     results_context_t& rc)
+         : gen_function_t  (arg_list),
+           results_context_(&rc)
+         {}
+
+         inline T value() const
+         {
+            if (
+                 (0 != results_context_) &&
+                 gen_function_t::populate_value_list()
+               )
+            {
+               typedef typename type_store<T>::parameter_list parameter_list_t;
+
+               results_context_->
+                  assign(parameter_list_t(gen_function_t::typestore_list_));
+
+               throw return_exception();
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_return;
+         }
+
+      private:
+
+         results_context_t* results_context_;
+      };
+
+      template <typename T>
+      class return_envelope_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef results_context<T>  results_context_t;
+
+         return_envelope_node(expression_ptr body, results_context_t& rc)
+         : results_context_(&rc  ),
+           return_invoked_ (false),
+           body_           (body ),
+           body_deletable_ (branch_deletable(body_))
+         {}
+
+        ~return_envelope_node()
+         {
+            if (body_ && body_deletable_)
+            {
+               destroy_node(body_);
+            }
+         }
+
+         inline T value() const
+         {
+            try
+            {
+               return_invoked_ = false;
+               results_context_->clear();
+
+               return body_->value();
+            }
+            catch(const return_exception&)
+            {
+               return_invoked_ = true;
+               return std::numeric_limits<T>::quiet_NaN();
+            }
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_retenv;
+         }
+
+         inline bool* retinvk_ptr()
+         {
+            return &return_invoked_;
+         }
+
+      private:
+
+         results_context_t* results_context_;
+         mutable bool       return_invoked_;
+         expression_ptr     body_;
+         const bool         body_deletable_;
+      };
+      #endif
+
+      #define exprtk_define_unary_op(OpName)                    \
+      template <typename T>                                     \
+      struct OpName##_op                                        \
+      {                                                         \
+         typedef typename functor_t<T>::Type Type;              \
+         typedef typename expression_node<T>::node_type node_t; \
+                                                                \
+         static inline T process(Type v)                        \
+         {                                                      \
+            return numeric:: OpName (v);                        \
+         }                                                      \
+                                                                \
+         static inline node_t type()                            \
+         {                                                      \
+            return expression_node<T>::e_##OpName;              \
+         }                                                      \
+                                                                \
+         static inline details::operator_type operation()       \
+         {                                                      \
+            return details::e_##OpName;                         \
+         }                                                      \
+      };                                                        \
+
+      exprtk_define_unary_op(abs  )
+      exprtk_define_unary_op(acos )
+      exprtk_define_unary_op(acosh)
+      exprtk_define_unary_op(asin )
+      exprtk_define_unary_op(asinh)
+      exprtk_define_unary_op(atan )
+      exprtk_define_unary_op(atanh)
+      exprtk_define_unary_op(ceil )
+      exprtk_define_unary_op(cos  )
+      exprtk_define_unary_op(cosh )
+      exprtk_define_unary_op(cot  )
+      exprtk_define_unary_op(csc  )
+      exprtk_define_unary_op(d2g  )
+      exprtk_define_unary_op(d2r  )
+      exprtk_define_unary_op(erf  )
+      exprtk_define_unary_op(erfc )
+      exprtk_define_unary_op(exp  )
+      exprtk_define_unary_op(expm1)
+      exprtk_define_unary_op(floor)
+      exprtk_define_unary_op(frac )
+      exprtk_define_unary_op(g2d  )
+      exprtk_define_unary_op(log  )
+      exprtk_define_unary_op(log10)
+      exprtk_define_unary_op(log2 )
+      exprtk_define_unary_op(log1p)
+      exprtk_define_unary_op(ncdf )
+      exprtk_define_unary_op(neg  )
+      exprtk_define_unary_op(notl )
+      exprtk_define_unary_op(pos  )
+      exprtk_define_unary_op(r2d  )
+      exprtk_define_unary_op(round)
+      exprtk_define_unary_op(sec  )
+      exprtk_define_unary_op(sgn  )
+      exprtk_define_unary_op(sin  )
+      exprtk_define_unary_op(sinc )
+      exprtk_define_unary_op(sinh )
+      exprtk_define_unary_op(sqrt )
+      exprtk_define_unary_op(tan  )
+      exprtk_define_unary_op(tanh )
+      exprtk_define_unary_op(trunc)
+      #undef exprtk_define_unary_op
+
+      template <typename T>
+      struct opr_base
+      {
+         typedef typename details::functor_t<T>::Type    Type;
+         typedef typename details::functor_t<T>::RefType RefType;
+         typedef typename details::functor_t<T>          functor_t;
+         typedef typename functor_t::qfunc_t  quaternary_functor_t;
+         typedef typename functor_t::tfunc_t     trinary_functor_t;
+         typedef typename functor_t::bfunc_t      binary_functor_t;
+         typedef typename functor_t::ufunc_t       unary_functor_t;
+      };
+
+      template <typename T>
+      struct add_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type    Type;
+         typedef typename opr_base<T>::RefType RefType;
+
+         static inline T process(Type t1, Type t2) { return t1 + t2; }
+         static inline T process(Type t1, Type t2, Type t3) { return t1 + t2 + t3; }
+         static inline void assign(RefType t1, Type t2) { t1 += t2; }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_add; }
+         static inline details::operator_type operation() { return details::e_add; }
+      };
+
+      template <typename T>
+      struct mul_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type    Type;
+         typedef typename opr_base<T>::RefType RefType;
+
+         static inline T process(Type t1, Type t2) { return t1 * t2; }
+         static inline T process(Type t1, Type t2, Type t3) { return t1 * t2 * t3; }
+         static inline void assign(RefType t1, Type t2) { t1 *= t2; }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_mul; }
+         static inline details::operator_type operation() { return details::e_mul; }
+      };
+
+      template <typename T>
+      struct sub_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type    Type;
+         typedef typename opr_base<T>::RefType RefType;
+
+         static inline T process(Type t1, Type t2) { return t1 - t2; }
+         static inline T process(Type t1, Type t2, Type t3) { return t1 - t2 - t3; }
+         static inline void assign(RefType t1, Type t2) { t1 -= t2; }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_sub; }
+         static inline details::operator_type operation() { return details::e_sub; }
+      };
+
+      template <typename T>
+      struct div_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type    Type;
+         typedef typename opr_base<T>::RefType RefType;
+
+         static inline T process(Type t1, Type t2) { return t1 / t2; }
+         static inline T process(Type t1, Type t2, Type t3) { return t1 / t2 / t3; }
+         static inline void assign(RefType t1, Type t2) { t1 /= t2; }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_div; }
+         static inline details::operator_type operation() { return details::e_div; }
+      };
+
+      template <typename T>
+      struct mod_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type    Type;
+         typedef typename opr_base<T>::RefType RefType;
+
+         static inline T process(Type t1, Type t2) { return numeric::modulus<T>(t1,t2); }
+         static inline void assign(RefType t1, Type t2) { t1 = numeric::modulus<T>(t1,t2); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_mod; }
+         static inline details::operator_type operation() { return details::e_mod; }
+      };
+
+      template <typename T>
+      struct pow_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type    Type;
+         typedef typename opr_base<T>::RefType RefType;
+
+         static inline T process(Type t1, Type t2) { return numeric::pow<T>(t1,t2); }
+         static inline void assign(RefType t1, Type t2) { t1 = numeric::pow<T>(t1,t2); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_pow; }
+         static inline details::operator_type operation() { return details::e_pow; }
+      };
+
+      template <typename T>
+      struct lt_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(Type t1, Type t2) { return ((t1 < t2) ? T(1) : T(0)); }
+         static inline T process(const std::string& t1, const std::string& t2) { return ((t1 < t2) ? T(1) : T(0)); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_lt; }
+         static inline details::operator_type operation() { return details::e_lt; }
+      };
+
+      template <typename T>
+      struct lte_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(Type t1, Type t2) { return ((t1 <= t2) ? T(1) : T(0)); }
+         static inline T process(const std::string& t1, const std::string& t2) { return ((t1 <= t2) ? T(1) : T(0)); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_lte; }
+         static inline details::operator_type operation() { return details::e_lte; }
+      };
+
+      template <typename T>
+      struct gt_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(Type t1, Type t2) { return ((t1 > t2) ? T(1) : T(0)); }
+         static inline T process(const std::string& t1, const std::string& t2) { return ((t1 > t2) ? T(1) : T(0)); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_gt; }
+         static inline details::operator_type operation() { return details::e_gt; }
+      };
+
+      template <typename T>
+      struct gte_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(Type t1, Type t2) { return ((t1 >= t2) ? T(1) : T(0)); }
+         static inline T process(const std::string& t1, const std::string& t2) { return ((t1 >= t2) ? T(1) : T(0)); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_gte; }
+         static inline details::operator_type operation() { return details::e_gte; }
+      };
+
+      template <typename T>
+      struct eq_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+         static inline T process(Type t1, Type t2) { return (std::equal_to<T>()(t1,t2) ? T(1) : T(0)); }
+         static inline T process(const std::string& t1, const std::string& t2) { return ((t1 == t2) ? T(1) : T(0)); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_eq; }
+         static inline details::operator_type operation() { return details::e_eq; }
+      };
+
+      template <typename T>
+      struct equal_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(Type t1, Type t2) { return numeric::equal(t1,t2); }
+         static inline T process(const std::string& t1, const std::string& t2) { return ((t1 == t2) ? T(1) : T(0)); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_eq; }
+         static inline details::operator_type operation() { return details::e_equal; }
+      };
+
+      template <typename T>
+      struct ne_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(Type t1, Type t2) { return (std::not_equal_to<T>()(t1,t2) ? T(1) : T(0)); }
+         static inline T process(const std::string& t1, const std::string& t2) { return ((t1 != t2) ? T(1) : T(0)); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_ne; }
+         static inline details::operator_type operation() { return details::e_ne; }
+      };
+
+      template <typename T>
+      struct and_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(Type t1, Type t2) { return (details::is_true(t1) && details::is_true(t2)) ? T(1) : T(0); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_and; }
+         static inline details::operator_type operation() { return details::e_and; }
+      };
+
+      template <typename T>
+      struct nand_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(Type t1, Type t2) { return (details::is_true(t1) && details::is_true(t2)) ? T(0) : T(1); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_nand; }
+         static inline details::operator_type operation() { return details::e_nand; }
+      };
+
+      template <typename T>
+      struct or_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(Type t1, Type t2) { return (details::is_true(t1) || details::is_true(t2)) ? T(1) : T(0); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_or; }
+         static inline details::operator_type operation() { return details::e_or; }
+      };
+
+      template <typename T>
+      struct nor_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(Type t1, Type t2) { return (details::is_true(t1) || details::is_true(t2)) ? T(0) : T(1); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_nor; }
+         static inline details::operator_type operation() { return details::e_nor; }
+      };
+
+      template <typename T>
+      struct xor_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(Type t1, Type t2) { return numeric::xor_opr<T>(t1,t2); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_nor; }
+         static inline details::operator_type operation() { return details::e_xor; }
+      };
+
+      template <typename T>
+      struct xnor_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(Type t1, Type t2) { return numeric::xnor_opr<T>(t1,t2); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_nor; }
+         static inline details::operator_type operation() { return details::e_xnor; }
+      };
+
+      template <typename T>
+      struct in_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(const T&, const T&) { return std::numeric_limits<T>::quiet_NaN(); }
+         static inline T process(const std::string& t1, const std::string& t2) { return ((std::string::npos != t2.find(t1)) ? T(1) : T(0)); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_in; }
+         static inline details::operator_type operation() { return details::e_in; }
+      };
+
+      template <typename T>
+      struct like_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(const T&, const T&) { return std::numeric_limits<T>::quiet_NaN(); }
+         static inline T process(const std::string& t1, const std::string& t2) { return (details::wc_match(t2,t1) ? T(1) : T(0)); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_like; }
+         static inline details::operator_type operation() { return details::e_like; }
+      };
+
+      template <typename T>
+      struct ilike_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(const T&, const T&) { return std::numeric_limits<T>::quiet_NaN(); }
+         static inline T process(const std::string& t1, const std::string& t2) { return (details::wc_imatch(t2,t1) ? T(1) : T(0)); }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_ilike; }
+         static inline details::operator_type operation() { return details::e_ilike; }
+      };
+
+      template <typename T>
+      struct inrange_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         static inline T process(const T& t0, const T& t1, const T& t2) { return ((t0 <= t1) && (t1 <= t2)) ? T(1) : T(0); }
+         static inline T process(const std::string& t0, const std::string& t1, const std::string& t2)
+         {
+            return ((t0 <= t1) && (t1 <= t2)) ? T(1) : T(0);
+         }
+         static inline typename expression_node<T>::node_type type() { return expression_node<T>::e_inranges; }
+         static inline details::operator_type operation() { return details::e_inrange; }
+      };
+
+      template <typename T>
+      inline T value(details::expression_node<T>* n)
+      {
+         return n->value();
+      }
+
+      template <typename T>
+      inline T value(T* t)
+      {
+         return (*t);
+      }
+
+      template <typename T>
+      struct vararg_add_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         template <typename Type,
+                   typename Allocator,
+                   template <typename, typename> class Sequence>
+         static inline T process(const Sequence<Type,Allocator>& arg_list)
+         {
+            switch (arg_list.size())
+            {
+               case 0  : return T(0);
+               case 1  : return process_1(arg_list);
+               case 2  : return process_2(arg_list);
+               case 3  : return process_3(arg_list);
+               case 4  : return process_4(arg_list);
+               case 5  : return process_5(arg_list);
+               default :
+                         {
+                            T result = T(0);
+
+                            for (std::size_t i = 0; i < arg_list.size(); ++i)
+                            {
+                              result += value(arg_list[i]);
+                            }
+
+                            return result;
+                         }
+            }
+         }
+
+         template <typename Sequence>
+         static inline T process_1(const Sequence& arg_list)
+         {
+            return value(arg_list[0]);
+         }
+
+         template <typename Sequence>
+         static inline T process_2(const Sequence& arg_list)
+         {
+            return value(arg_list[0]) + value(arg_list[1]);
+         }
+
+         template <typename Sequence>
+         static inline T process_3(const Sequence& arg_list)
+         {
+            return value(arg_list[0]) + value(arg_list[1]) +
+                   value(arg_list[2]) ;
+         }
+
+         template <typename Sequence>
+         static inline T process_4(const Sequence& arg_list)
+         {
+            return value(arg_list[0]) + value(arg_list[1]) +
+                   value(arg_list[2]) + value(arg_list[3]) ;
+         }
+
+         template <typename Sequence>
+         static inline T process_5(const Sequence& arg_list)
+         {
+            return value(arg_list[0]) + value(arg_list[1]) +
+                   value(arg_list[2]) + value(arg_list[3]) +
+                   value(arg_list[4]) ;
+         }
+      };
+
+      template <typename T>
+      struct vararg_mul_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         template <typename Type,
+                   typename Allocator,
+                   template <typename, typename> class Sequence>
+         static inline T process(const Sequence<Type,Allocator>& arg_list)
+         {
+            switch (arg_list.size())
+            {
+               case 0  : return T(0);
+               case 1  : return process_1(arg_list);
+               case 2  : return process_2(arg_list);
+               case 3  : return process_3(arg_list);
+               case 4  : return process_4(arg_list);
+               case 5  : return process_5(arg_list);
+               default :
+                         {
+                            T result = T(value(arg_list[0]));
+
+                            for (std::size_t i = 1; i < arg_list.size(); ++i)
+                            {
+                               result *= value(arg_list[i]);
+                            }
+
+                            return result;
+                         }
+            }
+         }
+
+         template <typename Sequence>
+         static inline T process_1(const Sequence& arg_list)
+         {
+            return value(arg_list[0]);
+         }
+
+         template <typename Sequence>
+         static inline T process_2(const Sequence& arg_list)
+         {
+            return value(arg_list[0]) * value(arg_list[1]);
+         }
+
+         template <typename Sequence>
+         static inline T process_3(const Sequence& arg_list)
+         {
+            return value(arg_list[0]) * value(arg_list[1]) *
+                   value(arg_list[2]) ;
+         }
+
+         template <typename Sequence>
+         static inline T process_4(const Sequence& arg_list)
+         {
+            return value(arg_list[0]) * value(arg_list[1]) *
+                   value(arg_list[2]) * value(arg_list[3]) ;
+         }
+
+         template <typename Sequence>
+         static inline T process_5(const Sequence& arg_list)
+         {
+            return value(arg_list[0]) * value(arg_list[1]) *
+                   value(arg_list[2]) * value(arg_list[3]) *
+                   value(arg_list[4]) ;
+         }
+      };
+
+      template <typename T>
+      struct vararg_avg_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         template <typename Type,
+                   typename Allocator,
+                   template <typename, typename> class Sequence>
+         static inline T process(const Sequence<Type,Allocator>& arg_list)
+         {
+            switch (arg_list.size())
+            {
+               case 0  : return T(0);
+               case 1  : return process_1(arg_list);
+               case 2  : return process_2(arg_list);
+               case 3  : return process_3(arg_list);
+               case 4  : return process_4(arg_list);
+               case 5  : return process_5(arg_list);
+               default : return vararg_add_op<T>::process(arg_list) / arg_list.size();
+            }
+         }
+
+         template <typename Sequence>
+         static inline T process_1(const Sequence& arg_list)
+         {
+            return value(arg_list[0]);
+         }
+
+         template <typename Sequence>
+         static inline T process_2(const Sequence& arg_list)
+         {
+            return (value(arg_list[0]) + value(arg_list[1])) / T(2);
+         }
+
+         template <typename Sequence>
+         static inline T process_3(const Sequence& arg_list)
+         {
+            return (value(arg_list[0]) + value(arg_list[1]) + value(arg_list[2])) / T(3);
+         }
+
+         template <typename Sequence>
+         static inline T process_4(const Sequence& arg_list)
+         {
+            return (value(arg_list[0]) + value(arg_list[1]) +
+                    value(arg_list[2]) + value(arg_list[3])) / T(4);
+         }
+
+         template <typename Sequence>
+         static inline T process_5(const Sequence& arg_list)
+         {
+            return (value(arg_list[0]) + value(arg_list[1]) +
+                    value(arg_list[2]) + value(arg_list[3]) +
+                    value(arg_list[4])) / T(5);
+         }
+      };
+
+      template <typename T>
+      struct vararg_min_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         template <typename Type,
+                   typename Allocator,
+                   template <typename, typename> class Sequence>
+         static inline T process(const Sequence<Type,Allocator>& arg_list)
+         {
+            switch (arg_list.size())
+            {
+               case 0  : return T(0);
+               case 1  : return process_1(arg_list);
+               case 2  : return process_2(arg_list);
+               case 3  : return process_3(arg_list);
+               case 4  : return process_4(arg_list);
+               case 5  : return process_5(arg_list);
+               default :
+                         {
+                            T result = T(value(arg_list[0]));
+
+                            for (std::size_t i = 1; i < arg_list.size(); ++i)
+                            {
+                               const T v = value(arg_list[i]);
+
+                               if (v < result)
+                                  result = v;
+                            }
+
+                            return result;
+                         }
+            }
+         }
+
+         template <typename Sequence>
+         static inline T process_1(const Sequence& arg_list)
+         {
+            return value(arg_list[0]);
+         }
+
+         template <typename Sequence>
+         static inline T process_2(const Sequence& arg_list)
+         {
+            return std::min<T>(value(arg_list[0]),value(arg_list[1]));
+         }
+
+         template <typename Sequence>
+         static inline T process_3(const Sequence& arg_list)
+         {
+            return std::min<T>(std::min<T>(value(arg_list[0]),value(arg_list[1])),value(arg_list[2]));
+         }
+
+         template <typename Sequence>
+         static inline T process_4(const Sequence& arg_list)
+         {
+            return std::min<T>(
+                        std::min<T>(value(arg_list[0]), value(arg_list[1])),
+                        std::min<T>(value(arg_list[2]), value(arg_list[3])));
+         }
+
+         template <typename Sequence>
+         static inline T process_5(const Sequence& arg_list)
+         {
+            return std::min<T>(
+                   std::min<T>(std::min<T>(value(arg_list[0]), value(arg_list[1])),
+                               std::min<T>(value(arg_list[2]), value(arg_list[3]))),
+                               value(arg_list[4]));
+         }
+      };
+
+      template <typename T>
+      struct vararg_max_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         template <typename Type,
+                   typename Allocator,
+                   template <typename, typename> class Sequence>
+         static inline T process(const Sequence<Type,Allocator>& arg_list)
+         {
+            switch (arg_list.size())
+            {
+               case 0  : return T(0);
+               case 1  : return process_1(arg_list);
+               case 2  : return process_2(arg_list);
+               case 3  : return process_3(arg_list);
+               case 4  : return process_4(arg_list);
+               case 5  : return process_5(arg_list);
+               default :
+                         {
+                            T result = T(value(arg_list[0]));
+
+                            for (std::size_t i = 1; i < arg_list.size(); ++i)
+                            {
+                               const T v = value(arg_list[i]);
+
+                               if (v > result)
+                                  result = v;
+                            }
+
+                            return result;
+                         }
+            }
+         }
+
+         template <typename Sequence>
+         static inline T process_1(const Sequence& arg_list)
+         {
+            return value(arg_list[0]);
+         }
+
+         template <typename Sequence>
+         static inline T process_2(const Sequence& arg_list)
+         {
+            return std::max<T>(value(arg_list[0]),value(arg_list[1]));
+         }
+
+         template <typename Sequence>
+         static inline T process_3(const Sequence& arg_list)
+         {
+            return std::max<T>(std::max<T>(value(arg_list[0]),value(arg_list[1])),value(arg_list[2]));
+         }
+
+         template <typename Sequence>
+         static inline T process_4(const Sequence& arg_list)
+         {
+            return std::max<T>(
+                        std::max<T>(value(arg_list[0]), value(arg_list[1])),
+                        std::max<T>(value(arg_list[2]), value(arg_list[3])));
+         }
+
+         template <typename Sequence>
+         static inline T process_5(const Sequence& arg_list)
+         {
+            return std::max<T>(
+                   std::max<T>(std::max<T>(value(arg_list[0]), value(arg_list[1])),
+                               std::max<T>(value(arg_list[2]), value(arg_list[3]))),
+                               value(arg_list[4]));
+         }
+      };
+
+      template <typename T>
+      struct vararg_mand_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         template <typename Type,
+                   typename Allocator,
+                   template <typename, typename> class Sequence>
+         static inline T process(const Sequence<Type,Allocator>& arg_list)
+         {
+            switch (arg_list.size())
+            {
+               case 1  : return process_1(arg_list);
+               case 2  : return process_2(arg_list);
+               case 3  : return process_3(arg_list);
+               case 4  : return process_4(arg_list);
+               case 5  : return process_5(arg_list);
+               default :
+                         {
+                            for (std::size_t i = 0; i < arg_list.size(); ++i)
+                            {
+                               if (std::equal_to<T>()(T(0), value(arg_list[i])))
+                                  return T(0);
+                            }
+
+                            return T(1);
+                         }
+            }
+         }
+
+         template <typename Sequence>
+         static inline T process_1(const Sequence& arg_list)
+         {
+            return std::not_equal_to<T>()
+                      (T(0), value(arg_list[0])) ? T(1) : T(0);
+         }
+
+         template <typename Sequence>
+         static inline T process_2(const Sequence& arg_list)
+         {
+            return (
+                     std::not_equal_to<T>()(T(0), value(arg_list[0])) &&
+                     std::not_equal_to<T>()(T(0), value(arg_list[1]))
+                   ) ? T(1) : T(0);
+         }
+
+         template <typename Sequence>
+         static inline T process_3(const Sequence& arg_list)
+         {
+            return (
+                     std::not_equal_to<T>()(T(0), value(arg_list[0])) &&
+                     std::not_equal_to<T>()(T(0), value(arg_list[1])) &&
+                     std::not_equal_to<T>()(T(0), value(arg_list[2]))
+                   ) ? T(1) : T(0);
+         }
+
+         template <typename Sequence>
+         static inline T process_4(const Sequence& arg_list)
+         {
+            return (
+                     std::not_equal_to<T>()(T(0), value(arg_list[0])) &&
+                     std::not_equal_to<T>()(T(0), value(arg_list[1])) &&
+                     std::not_equal_to<T>()(T(0), value(arg_list[2])) &&
+                     std::not_equal_to<T>()(T(0), value(arg_list[3]))
+                   ) ? T(1) : T(0);
+         }
+
+         template <typename Sequence>
+         static inline T process_5(const Sequence& arg_list)
+         {
+            return (
+                     std::not_equal_to<T>()(T(0), value(arg_list[0])) &&
+                     std::not_equal_to<T>()(T(0), value(arg_list[1])) &&
+                     std::not_equal_to<T>()(T(0), value(arg_list[2])) &&
+                     std::not_equal_to<T>()(T(0), value(arg_list[3])) &&
+                     std::not_equal_to<T>()(T(0), value(arg_list[4]))
+                   ) ? T(1) : T(0);
+         }
+      };
+
+      template <typename T>
+      struct vararg_mor_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         template <typename Type,
+                   typename Allocator,
+                   template <typename, typename> class Sequence>
+         static inline T process(const Sequence<Type,Allocator>& arg_list)
+         {
+            switch (arg_list.size())
+            {
+               case 1  : return process_1(arg_list);
+               case 2  : return process_2(arg_list);
+               case 3  : return process_3(arg_list);
+               case 4  : return process_4(arg_list);
+               case 5  : return process_5(arg_list);
+               default :
+                         {
+                            for (std::size_t i = 0; i < arg_list.size(); ++i)
+                            {
+                               if (std::not_equal_to<T>()(T(0), value(arg_list[i])))
+                                  return T(1);
+                            }
+
+                            return T(0);
+                         }
+            }
+         }
+
+         template <typename Sequence>
+         static inline T process_1(const Sequence& arg_list)
+         {
+            return std::not_equal_to<T>()
+                      (T(0), value(arg_list[0])) ? T(1) : T(0);
+         }
+
+         template <typename Sequence>
+         static inline T process_2(const Sequence& arg_list)
+         {
+            return (
+                     std::not_equal_to<T>()(T(0), value(arg_list[0])) ||
+                     std::not_equal_to<T>()(T(0), value(arg_list[1]))
+                   ) ? T(1) : T(0);
+         }
+
+         template <typename Sequence>
+         static inline T process_3(const Sequence& arg_list)
+         {
+            return (
+                     std::not_equal_to<T>()(T(0), value(arg_list[0])) ||
+                     std::not_equal_to<T>()(T(0), value(arg_list[1])) ||
+                     std::not_equal_to<T>()(T(0), value(arg_list[2]))
+                   ) ? T(1) : T(0);
+         }
+
+         template <typename Sequence>
+         static inline T process_4(const Sequence& arg_list)
+         {
+            return (
+                     std::not_equal_to<T>()(T(0), value(arg_list[0])) ||
+                     std::not_equal_to<T>()(T(0), value(arg_list[1])) ||
+                     std::not_equal_to<T>()(T(0), value(arg_list[2])) ||
+                     std::not_equal_to<T>()(T(0), value(arg_list[3]))
+                   ) ? T(1) : T(0);
+         }
+
+         template <typename Sequence>
+         static inline T process_5(const Sequence& arg_list)
+         {
+            return (
+                     std::not_equal_to<T>()(T(0), value(arg_list[0])) ||
+                     std::not_equal_to<T>()(T(0), value(arg_list[1])) ||
+                     std::not_equal_to<T>()(T(0), value(arg_list[2])) ||
+                     std::not_equal_to<T>()(T(0), value(arg_list[3])) ||
+                     std::not_equal_to<T>()(T(0), value(arg_list[4]))
+                   ) ? T(1) : T(0);
+         }
+      };
+
+      template <typename T>
+      struct vararg_multi_op : public opr_base<T>
+      {
+         typedef typename opr_base<T>::Type Type;
+
+         template <typename Type,
+                   typename Allocator,
+                   template <typename, typename> class Sequence>
+         static inline T process(const Sequence<Type,Allocator>& arg_list)
+         {
+            switch (arg_list.size())
+            {
+               case 0  : return std::numeric_limits<T>::quiet_NaN();
+               case 1  : return process_1(arg_list);
+               case 2  : return process_2(arg_list);
+               case 3  : return process_3(arg_list);
+               case 4  : return process_4(arg_list);
+               case 5  : return process_5(arg_list);
+               case 6  : return process_6(arg_list);
+               case 7  : return process_7(arg_list);
+               case 8  : return process_8(arg_list);
+               default :
+                         {
+                            for (std::size_t i = 0; i < (arg_list.size() - 1); ++i)
+                            {
+                               value(arg_list[i]);
+                            }
+
+                            return value(arg_list.back());
+                         }
+            }
+         }
+
+         template <typename Sequence>
+         static inline T process_1(const Sequence& arg_list)
+         {
+            return value(arg_list[0]);
+         }
+
+         template <typename Sequence>
+         static inline T process_2(const Sequence& arg_list)
+         {
+                   value(arg_list[0]);
+            return value(arg_list[1]);
+         }
+
+         template <typename Sequence>
+         static inline T process_3(const Sequence& arg_list)
+         {
+                   value(arg_list[0]);
+                   value(arg_list[1]);
+            return value(arg_list[2]);
+         }
+
+         template <typename Sequence>
+         static inline T process_4(const Sequence& arg_list)
+         {
+                   value(arg_list[0]);
+                   value(arg_list[1]);
+                   value(arg_list[2]);
+            return value(arg_list[3]);
+         }
+
+         template <typename Sequence>
+         static inline T process_5(const Sequence& arg_list)
+         {
+                   value(arg_list[0]);
+                   value(arg_list[1]);
+                   value(arg_list[2]);
+                   value(arg_list[3]);
+            return value(arg_list[4]);
+         }
+
+         template <typename Sequence>
+         static inline T process_6(const Sequence& arg_list)
+         {
+                   value(arg_list[0]);
+                   value(arg_list[1]);
+                   value(arg_list[2]);
+                   value(arg_list[3]);
+                   value(arg_list[4]);
+            return value(arg_list[5]);
+         }
+
+         template <typename Sequence>
+         static inline T process_7(const Sequence& arg_list)
+         {
+                   value(arg_list[0]);
+                   value(arg_list[1]);
+                   value(arg_list[2]);
+                   value(arg_list[3]);
+                   value(arg_list[4]);
+                   value(arg_list[5]);
+            return value(arg_list[6]);
+         }
+
+         template <typename Sequence>
+         static inline T process_8(const Sequence& arg_list)
+         {
+                   value(arg_list[0]);
+                   value(arg_list[1]);
+                   value(arg_list[2]);
+                   value(arg_list[3]);
+                   value(arg_list[4]);
+                   value(arg_list[5]);
+                   value(arg_list[6]);
+            return value(arg_list[7]);
+         }
+      };
+
+      template <typename T>
+      struct vec_add_op
+      {
+         typedef vector_interface<T>* ivector_ptr;
+
+         static inline T process(const ivector_ptr v)
+         {
+            const T* vec = v->vec()->vds().data();
+            const std::size_t vec_size = v->vec()->vds().size();
+
+            loop_unroll::details lud(vec_size);
+
+            if (vec_size <= static_cast<std::size_t>(lud.batch_size))
+            {
+               T result = T(0);
+               int i    = 0;
+
+               exprtk_disable_fallthrough_begin
+               switch (vec_size)
+               {
+                  #define case_stmt(N)         \
+                  case N : result += vec[i++]; \
+
+                  #ifndef exprtk_disable_superscalar_unroll
+                  case_stmt(16) case_stmt(15)
+                  case_stmt(14) case_stmt(13)
+                  case_stmt(12) case_stmt(11)
+                  case_stmt(10) case_stmt( 9)
+                  case_stmt( 8) case_stmt( 7)
+                  case_stmt( 6) case_stmt( 5)
+                  #endif
+                  case_stmt( 4) case_stmt( 3)
+                  case_stmt( 2) case_stmt( 1)
+               }
+               exprtk_disable_fallthrough_end
+
+               #undef case_stmt
+
+               return result;
+            }
+
+            T r[] = {
+                      T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0),
+                      T(0), T(0), T(0), T(0), T(0), T(0), T(0), T(0)
+                    };
+
+            const T* upper_bound = vec + lud.upper_bound;
+
+            while (vec < upper_bound)
+            {
+               #define exprtk_loop(N) \
+               r[N] += vec[N];        \
+
+               exprtk_loop( 0) exprtk_loop( 1)
+               exprtk_loop( 2) exprtk_loop( 3)
+               #ifndef exprtk_disable_superscalar_unroll
+               exprtk_loop( 4) exprtk_loop( 5)
+               exprtk_loop( 6) exprtk_loop( 7)
+               exprtk_loop( 8) exprtk_loop( 9)
+               exprtk_loop(10) exprtk_loop(11)
+               exprtk_loop(12) exprtk_loop(13)
+               exprtk_loop(14) exprtk_loop(15)
+               #endif
+
+               vec += lud.batch_size;
+            }
+
+            int i = 0;
+
+            exprtk_disable_fallthrough_begin
+            switch (lud.remainder)
+            {
+               #define case_stmt(N)       \
+               case N : r[0] += vec[i++]; \
+
+               #ifndef exprtk_disable_superscalar_unroll
+               case_stmt(15) case_stmt(14)
+               case_stmt(13) case_stmt(12)
+               case_stmt(11) case_stmt(10)
+               case_stmt( 9) case_stmt( 8)
+               case_stmt( 7) case_stmt( 6)
+               case_stmt( 5) case_stmt( 4)
+               #endif
+               case_stmt( 3) case_stmt( 2)
+               case_stmt( 1)
+            }
+            exprtk_disable_fallthrough_end
+
+            #undef exprtk_loop
+            #undef case_stmt
+
+            return (r[ 0] + r[ 1] + r[ 2] + r[ 3])
+                   #ifndef exprtk_disable_superscalar_unroll
+                 + (r[ 4] + r[ 5] + r[ 6] + r[ 7])
+                 + (r[ 8] + r[ 9] + r[10] + r[11])
+                 + (r[12] + r[13] + r[14] + r[15])
+                   #endif
+                   ;
+         }
+      };
+
+      template <typename T>
+      struct vec_mul_op
+      {
+         typedef vector_interface<T>* ivector_ptr;
+
+         static inline T process(const ivector_ptr v)
+         {
+            const T* vec = v->vec()->vds().data();
+            const std::size_t vec_size = v->vec()->vds().size();
+
+            loop_unroll::details lud(vec_size);
+
+            if (vec_size <= static_cast<std::size_t>(lud.batch_size))
+            {
+               T result = T(1);
+               int i    = 0;
+
+               exprtk_disable_fallthrough_begin
+               switch (vec_size)
+               {
+                  #define case_stmt(N)         \
+                  case N : result *= vec[i++]; \
+
+                  #ifndef exprtk_disable_superscalar_unroll
+                  case_stmt(16) case_stmt(15)
+                  case_stmt(14) case_stmt(13)
+                  case_stmt(12) case_stmt(11)
+                  case_stmt(10) case_stmt( 9)
+                  case_stmt( 8) case_stmt( 7)
+                  case_stmt( 6) case_stmt( 5)
+                  #endif
+                  case_stmt( 4) case_stmt( 3)
+                  case_stmt( 2) case_stmt( 1)
+               }
+               exprtk_disable_fallthrough_end
+
+               #undef case_stmt
+
+               return result;
+            }
+
+            T r[] = {
+                      T(1), T(1), T(1), T(1), T(1), T(1), T(1), T(1),
+                      T(1), T(1), T(1), T(1), T(1), T(1), T(1), T(1)
+                    };
+
+            const T* upper_bound = vec + lud.upper_bound;
+
+            while (vec < upper_bound)
+            {
+               #define exprtk_loop(N) \
+               r[N] *= vec[N];        \
+
+               exprtk_loop( 0) exprtk_loop( 1)
+               exprtk_loop( 2) exprtk_loop( 3)
+               #ifndef exprtk_disable_superscalar_unroll
+               exprtk_loop( 4) exprtk_loop( 5)
+               exprtk_loop( 6) exprtk_loop( 7)
+               exprtk_loop( 8) exprtk_loop( 9)
+               exprtk_loop(10) exprtk_loop(11)
+               exprtk_loop(12) exprtk_loop(13)
+               exprtk_loop(14) exprtk_loop(15)
+               #endif
+
+               vec += lud.batch_size;
+            }
+
+            int i = 0;
+
+            exprtk_disable_fallthrough_begin
+            switch (lud.remainder)
+            {
+               #define case_stmt(N)       \
+               case N : r[0] *= vec[i++]; \
+
+               #ifndef exprtk_disable_superscalar_unroll
+               case_stmt(15) case_stmt(14)
+               case_stmt(13) case_stmt(12)
+               case_stmt(11) case_stmt(10)
+               case_stmt( 9) case_stmt( 8)
+               case_stmt( 7) case_stmt( 6)
+               case_stmt( 5) case_stmt( 4)
+               #endif
+               case_stmt( 3) case_stmt( 2)
+               case_stmt( 1)
+            }
+            exprtk_disable_fallthrough_end
+
+            #undef exprtk_loop
+            #undef case_stmt
+
+            return (r[ 0] * r[ 1] * r[ 2] * r[ 3])
+                   #ifndef exprtk_disable_superscalar_unroll
+                 + (r[ 4] * r[ 5] * r[ 6] * r[ 7])
+                 + (r[ 8] * r[ 9] * r[10] * r[11])
+                 + (r[12] * r[13] * r[14] * r[15])
+                   #endif
+                   ;
+         }
+      };
+
+      template <typename T>
+      struct vec_avg_op
+      {
+         typedef vector_interface<T>* ivector_ptr;
+
+         static inline T process(const ivector_ptr v)
+         {
+            const std::size_t vec_size = v->vec()->vds().size();
+
+            return vec_add_op<T>::process(v) / vec_size;
+         }
+      };
+
+      template <typename T>
+      struct vec_min_op
+      {
+         typedef vector_interface<T>* ivector_ptr;
+
+         static inline T process(const ivector_ptr v)
+         {
+            const T* vec = v->vec()->vds().data();
+            const std::size_t vec_size = v->vec()->vds().size();
+
+            T result = vec[0];
+
+            for (std::size_t i = 1; i < vec_size; ++i)
+            {
+               T v_i = vec[i];
+
+               if (v_i < result)
+                  result = v_i;
+            }
+
+            return result;
+         }
+      };
+
+      template <typename T>
+      struct vec_max_op
+      {
+         typedef vector_interface<T>* ivector_ptr;
+
+         static inline T process(const ivector_ptr v)
+         {
+            const T* vec = v->vec()->vds().data();
+            const std::size_t vec_size = v->vec()->vds().size();
+
+            T result = vec[0];
+
+            for (std::size_t i = 1; i < vec_size; ++i)
+            {
+               T v_i = vec[i];
+
+               if (v_i > result)
+                  result = v_i;
+            }
+
+            return result;
+         }
+      };
+
+      template <typename T>
+      class vov_base_node : public expression_node<T>
+      {
+      public:
+
+         virtual ~vov_base_node()
+         {}
+
+         inline virtual operator_type operation() const
+         {
+            return details::e_default;
+         }
+
+         virtual const T& v0() const = 0;
+
+         virtual const T& v1() const = 0;
+      };
+
+      template <typename T>
+      class cov_base_node : public expression_node<T>
+      {
+      public:
+
+       virtual ~cov_base_node()
+          {}
+
+         inline virtual operator_type operation() const
+         {
+            return details::e_default;
+         }
+
+         virtual const T c() const = 0;
+
+         virtual const T& v() const = 0;
+      };
+
+      template <typename T>
+      class voc_base_node : public expression_node<T>
+      {
+      public:
+
+         virtual ~voc_base_node()
+         {}
+
+         inline virtual operator_type operation() const
+         {
+            return details::e_default;
+         }
+
+         virtual const T c() const = 0;
+
+         virtual const T& v() const = 0;
+      };
+
+      template <typename T>
+      class vob_base_node : public expression_node<T>
+      {
+      public:
+
+         virtual ~vob_base_node()
+         {}
+
+         virtual const T& v() const = 0;
+      };
+
+      template <typename T>
+      class bov_base_node : public expression_node<T>
+      {
+      public:
+
+         virtual ~bov_base_node()
+         {}
+
+         virtual const T& v() const = 0;
+      };
+
+      template <typename T>
+      class cob_base_node : public expression_node<T>
+      {
+      public:
+
+       virtual ~cob_base_node()
+       {}
+
+         inline virtual operator_type operation() const
+         {
+            return details::e_default;
+         }
+
+         virtual const T c() const = 0;
+
+         virtual void set_c(const T) = 0;
+
+         virtual expression_node<T>* move_branch(const std::size_t& index) = 0;
+      };
+
+      template <typename T>
+      class boc_base_node : public expression_node<T>
+      {
+      public:
+
+         virtual ~boc_base_node()
+         {}
+
+         inline virtual operator_type operation() const
+         {
+            return details::e_default;
+         }
+
+         virtual const T c() const = 0;
+
+         virtual void set_c(const T) = 0;
+
+         virtual expression_node<T>* move_branch(const std::size_t& index) = 0;
+      };
+
+      template <typename T>
+      class uv_base_node : public expression_node<T>
+      {
+      public:
+
+         virtual ~uv_base_node()
+         {}
+
+         inline virtual operator_type operation() const
+         {
+            return details::e_default;
+         }
+
+         virtual const T& v() const = 0;
+      };
+
+      template <typename T>
+      class sos_base_node : public expression_node<T>
+      {
+      public:
+
+         virtual ~sos_base_node()
+         {}
+
+         inline virtual operator_type operation() const
+         {
+            return details::e_default;
+         }
+      };
+
+      template <typename T>
+      class sosos_base_node : public expression_node<T>
+      {
+      public:
+
+         virtual ~sosos_base_node()
+         {}
+
+         inline virtual operator_type operation() const
+         {
+            return details::e_default;
+         }
+      };
+
+      template <typename T>
+      class T0oT1oT2_base_node : public expression_node<T>
+      {
+      public:
+
+         virtual ~T0oT1oT2_base_node()
+         {}
+
+         virtual std::string type_id() const = 0;
+      };
+
+      template <typename T>
+      class T0oT1oT2oT3_base_node : public expression_node<T>
+      {
+      public:
+
+         virtual ~T0oT1oT2oT3_base_node()
+         {}
+
+         virtual std::string type_id() const = 0;
+      };
+
+      template <typename T, typename Operation>
+      class unary_variable_node : public uv_base_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef Operation operation_t;
+
+         explicit unary_variable_node(const T& var)
+         : v_(var)
+         {}
+
+         inline T value() const
+         {
+            return Operation::process(v_);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return Operation::type();
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline const T& v() const
+         {
+            return v_;
+         }
+
+      private:
+
+         unary_variable_node(unary_variable_node<T,Operation>&);
+         unary_variable_node<T,Operation>& operator=(unary_variable_node<T,Operation>&);
+
+         const T& v_;
+      };
+
+      template <typename T>
+      class uvouv_node : public expression_node<T>
+      {
+      public:
+
+         // UOpr1(v0) Op UOpr2(v1)
+
+         typedef expression_node<T>* expression_ptr;
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::bfunc_t      bfunc_t;
+         typedef typename functor_t::ufunc_t      ufunc_t;
+
+         explicit uvouv_node(const T& var0,const T& var1,
+                             ufunc_t uf0, ufunc_t uf1, bfunc_t bf)
+         : v0_(var0),
+           v1_(var1),
+           u0_(uf0 ),
+           u1_(uf1 ),
+           f_ (bf  )
+         {}
+
+         inline T value() const
+         {
+            return f_(u0_(v0_),u1_(v1_));
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_uvouv;
+         }
+
+         inline operator_type operation() const
+         {
+            return details::e_default;
+         }
+
+         inline const T& v0()
+         {
+            return v0_;
+         }
+
+         inline const T& v1()
+         {
+            return v1_;
+         }
+
+         inline ufunc_t u0()
+         {
+            return u0_;
+         }
+
+         inline ufunc_t u1()
+         {
+            return u1_;
+         }
+
+         inline ufunc_t f()
+         {
+            return f_;
+         }
+
+      private:
+
+         uvouv_node(uvouv_node<T>&);
+         uvouv_node<T>& operator=(uvouv_node<T>&);
+
+         const T& v0_;
+         const T& v1_;
+         const ufunc_t u0_;
+         const ufunc_t u1_;
+         const bfunc_t f_;
+      };
+
+      template <typename T, typename Operation>
+      class unary_branch_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef Operation operation_t;
+
+         explicit unary_branch_node(expression_ptr brnch)
+         : branch_(brnch),
+           branch_deletable_(branch_deletable(branch_))
+         {}
+
+        ~unary_branch_node()
+         {
+            if (branch_ && branch_deletable_)
+            {
+               destroy_node(branch_);
+            }
+         }
+
+         inline T value() const
+         {
+            return Operation::process(branch_->value());
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return Operation::type();
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline expression_node<T>* branch(const std::size_t&) const
+         {
+            return branch_;
+         }
+
+         inline void release()
+         {
+            branch_deletable_ = false;
+         }
+
+      private:
+
+         unary_branch_node(unary_branch_node<T,Operation>&);
+         unary_branch_node<T,Operation>& operator=(unary_branch_node<T,Operation>&);
+
+         expression_ptr branch_;
+         bool           branch_deletable_;
+      };
+
+      template <typename T> struct is_const                { enum {result = 0}; };
+      template <typename T> struct is_const <const T>      { enum {result = 1}; };
+      template <typename T> struct is_const_ref            { enum {result = 0}; };
+      template <typename T> struct is_const_ref <const T&> { enum {result = 1}; };
+      template <typename T> struct is_ref                  { enum {result = 0}; };
+      template <typename T> struct is_ref<T&>              { enum {result = 1}; };
+      template <typename T> struct is_ref<const T&>        { enum {result = 0}; };
+
+      template <std::size_t State>
+      struct param_to_str { static std::string result() { static const std::string r("v"); return r; } };
+
+      template <>
+      struct param_to_str<0> { static std::string result() { static const std::string r("c"); return r; } };
+
+      #define exprtk_crtype(Type)                          \
+      param_to_str<is_const_ref< Type >::result>::result() \
+
+      template <typename T>
+      struct T0oT1oT2process
+      {
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::bfunc_t      bfunc_t;
+
+         struct mode0
+         {
+            static inline T process(const T& t0, const T& t1, const T& t2, const bfunc_t bf0, const bfunc_t bf1)
+            {
+               // (T0 o0 T1) o1 T2
+               return bf1(bf0(t0,t1),t2);
+            }
+
+            template <typename T0, typename T1, typename T2>
+            static inline std::string id()
+            {
+               static const std::string result = "(" + exprtk_crtype(T0) + "o"   +
+                                                       exprtk_crtype(T1) + ")o(" +
+                                                       exprtk_crtype(T2) + ")"   ;
+               return result;
+            }
+         };
+
+         struct mode1
+         {
+            static inline T process(const T& t0, const T& t1, const T& t2, const bfunc_t bf0, const bfunc_t bf1)
+            {
+               // T0 o0 (T1 o1 T2)
+               return bf0(t0,bf1(t1,t2));
+            }
+
+            template <typename T0, typename T1, typename T2>
+            static inline std::string id()
+            {
+               static const std::string result = "(" + exprtk_crtype(T0) + ")o(" +
+                                                       exprtk_crtype(T1) + "o"   +
+                                                       exprtk_crtype(T2) + ")"   ;
+               return result;
+            }
+         };
+      };
+
+      template <typename T>
+      struct T0oT1oT20T3process
+      {
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::bfunc_t      bfunc_t;
+
+         struct mode0
+         {
+            static inline T process(const T& t0, const T& t1,
+                                    const T& t2, const T& t3,
+                                    const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2)
+            {
+               // (T0 o0 T1) o1 (T2 o2 T3)
+               return bf1(bf0(t0,t1),bf2(t2,t3));
+            }
+
+            template <typename T0, typename T1, typename T2, typename T3>
+            static inline std::string id()
+            {
+               static const std::string result = "(" + exprtk_crtype(T0) + "o"  +
+                                                       exprtk_crtype(T1) + ")o" +
+                                                 "(" + exprtk_crtype(T2) + "o"  +
+                                                       exprtk_crtype(T3) + ")"  ;
+               return result;
+            }
+         };
+
+         struct mode1
+         {
+            static inline T process(const T& t0, const T& t1,
+                                    const T& t2, const T& t3,
+                                    const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2)
+            {
+               // (T0 o0 (T1 o1 (T2 o2 T3))
+               return bf0(t0,bf1(t1,bf2(t2,t3)));
+            }
+            template <typename T0, typename T1, typename T2, typename T3>
+            static inline std::string id()
+            {
+               static const std::string result = "(" + exprtk_crtype(T0) +  ")o((" +
+                                                       exprtk_crtype(T1) +  ")o("  +
+                                                       exprtk_crtype(T2) +  "o"    +
+                                                       exprtk_crtype(T3) +  "))"   ;
+               return result;
+            }
+         };
+
+         struct mode2
+         {
+            static inline T process(const T& t0, const T& t1,
+                                    const T& t2, const T& t3,
+                                    const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2)
+            {
+               // (T0 o0 ((T1 o1 T2) o2 T3)
+               return bf0(t0,bf2(bf1(t1,t2),t3));
+            }
+
+            template <typename T0, typename T1, typename T2, typename T3>
+            static inline std::string id()
+            {
+               static const std::string result = "(" + exprtk_crtype(T0) + ")o((" +
+                                                       exprtk_crtype(T1) + "o"    +
+                                                       exprtk_crtype(T2) + ")o("  +
+                                                       exprtk_crtype(T3) + "))"   ;
+               return result;
+            }
+         };
+
+         struct mode3
+         {
+            static inline T process(const T& t0, const T& t1,
+                                    const T& t2, const T& t3,
+                                    const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2)
+            {
+               // (((T0 o0 T1) o1 T2) o2 T3)
+               return bf2(bf1(bf0(t0,t1),t2),t3);
+            }
+
+            template <typename T0, typename T1, typename T2, typename T3>
+            static inline std::string id()
+            {
+               static const std::string result = "((" + exprtk_crtype(T0) + "o"    +
+                                                        exprtk_crtype(T1) + ")o("  +
+                                                        exprtk_crtype(T2) + "))o(" +
+                                                        exprtk_crtype(T3) + ")";
+               return result;
+            }
+         };
+
+         struct mode4
+         {
+            static inline T process(const T& t0, const T& t1,
+                                    const T& t2, const T& t3,
+                                    const bfunc_t bf0, const bfunc_t bf1, const bfunc_t bf2)
+            {
+               // ((T0 o0 (T1 o1 T2)) o2 T3
+               return bf2(bf0(t0,bf1(t1,t2)),t3);
+            }
+
+            template <typename T0, typename T1, typename T2, typename T3>
+            static inline std::string id()
+            {
+               static const std::string result = "((" + exprtk_crtype(T0) + ")o("  +
+                                                        exprtk_crtype(T1) + "o"    +
+                                                        exprtk_crtype(T2) + "))o(" +
+                                                        exprtk_crtype(T3) + ")"    ;
+               return result;
+            }
+         };
+      };
+
+      #undef exprtk_crtype
+
+      template <typename T, typename T0, typename T1>
+      struct nodetype_T0oT1 { static const typename expression_node<T>::node_type result; };
+      template <typename T, typename T0, typename T1>
+      const typename expression_node<T>::node_type nodetype_T0oT1<T,T0,T1>::result = expression_node<T>::e_none;
+
+      #define synthesis_node_type_define(T0_,T1_,v_)                                                            \
+      template <typename T, typename T0, typename T1>                                                           \
+      struct nodetype_T0oT1<T,T0_,T1_> { static const typename expression_node<T>::node_type result; };         \
+      template <typename T, typename T0, typename T1>                                                           \
+      const typename expression_node<T>::node_type nodetype_T0oT1<T,T0_,T1_>::result = expression_node<T>:: v_; \
+
+      synthesis_node_type_define(const T0&, const T1&,  e_vov)
+      synthesis_node_type_define(const T0&, const T1 ,  e_voc)
+      synthesis_node_type_define(const T0 , const T1&,  e_cov)
+      synthesis_node_type_define(      T0&,       T1&, e_none)
+      synthesis_node_type_define(const T0 , const T1 , e_none)
+      synthesis_node_type_define(      T0&, const T1 , e_none)
+      synthesis_node_type_define(const T0 ,       T1&, e_none)
+      synthesis_node_type_define(const T0&,       T1&, e_none)
+      synthesis_node_type_define(      T0&, const T1&, e_none)
+      #undef synthesis_node_type_define
+
+      template <typename T, typename T0, typename T1, typename T2>
+      struct nodetype_T0oT1oT2 { static const typename expression_node<T>::node_type result; };
+      template <typename T, typename T0, typename T1, typename T2>
+      const typename expression_node<T>::node_type nodetype_T0oT1oT2<T,T0,T1,T2>::result = expression_node<T>::e_none;
+
+      #define synthesis_node_type_define(T0_,T1_,T2_,v_)                                                               \
+      template <typename T, typename T0, typename T1, typename T2>                                                     \
+      struct nodetype_T0oT1oT2<T,T0_,T1_,T2_> { static const typename expression_node<T>::node_type result; };         \
+      template <typename T, typename T0, typename T1, typename T2>                                                     \
+      const typename expression_node<T>::node_type nodetype_T0oT1oT2<T,T0_,T1_,T2_>::result = expression_node<T>:: v_; \
+
+      synthesis_node_type_define(const T0&, const T1&, const T2&, e_vovov)
+      synthesis_node_type_define(const T0&, const T1&, const T2 , e_vovoc)
+      synthesis_node_type_define(const T0&, const T1 , const T2&, e_vocov)
+      synthesis_node_type_define(const T0 , const T1&, const T2&, e_covov)
+      synthesis_node_type_define(const T0 , const T1&, const T2 , e_covoc)
+      synthesis_node_type_define(const T0 , const T1 , const T2 , e_none )
+      synthesis_node_type_define(const T0 , const T1 , const T2&, e_none )
+      synthesis_node_type_define(const T0&, const T1 , const T2 , e_none )
+      synthesis_node_type_define(      T0&,       T1&,       T2&, e_none )
+      #undef synthesis_node_type_define
+
+      template <typename T, typename T0, typename T1, typename T2, typename T3>
+      struct nodetype_T0oT1oT2oT3 { static const typename expression_node<T>::node_type result; };
+      template <typename T, typename T0, typename T1, typename T2, typename T3>
+      const typename expression_node<T>::node_type nodetype_T0oT1oT2oT3<T,T0,T1,T2,T3>::result = expression_node<T>::e_none;
+
+      #define synthesis_node_type_define(T0_,T1_,T2_,T3_,v_)                                                                  \
+      template <typename T, typename T0, typename T1, typename T2, typename T3>                                               \
+      struct nodetype_T0oT1oT2oT3<T,T0_,T1_,T2_,T3_> { static const typename expression_node<T>::node_type result; };         \
+      template <typename T, typename T0, typename T1, typename T2, typename T3>                                               \
+      const typename expression_node<T>::node_type nodetype_T0oT1oT2oT3<T,T0_,T1_,T2_,T3_>::result = expression_node<T>:: v_; \
+
+      synthesis_node_type_define(const T0&, const T1&, const T2&, const T3&, e_vovovov)
+      synthesis_node_type_define(const T0&, const T1&, const T2&, const T3 , e_vovovoc)
+      synthesis_node_type_define(const T0&, const T1&, const T2 , const T3&, e_vovocov)
+      synthesis_node_type_define(const T0&, const T1 , const T2&, const T3&, e_vocovov)
+      synthesis_node_type_define(const T0 , const T1&, const T2&, const T3&, e_covovov)
+      synthesis_node_type_define(const T0 , const T1&, const T2 , const T3&, e_covocov)
+      synthesis_node_type_define(const T0&, const T1 , const T2&, const T3 , e_vocovoc)
+      synthesis_node_type_define(const T0 , const T1&, const T2&, const T3 , e_covovoc)
+      synthesis_node_type_define(const T0&, const T1 , const T2 , const T3&, e_vococov)
+      synthesis_node_type_define(const T0 , const T1 , const T2 , const T3 , e_none   )
+      synthesis_node_type_define(const T0 , const T1 , const T2 , const T3&, e_none   )
+      synthesis_node_type_define(const T0 , const T1 , const T2&, const T3 , e_none   )
+      synthesis_node_type_define(const T0 , const T1&, const T2 , const T3 , e_none   )
+      synthesis_node_type_define(const T0&, const T1 , const T2 , const T3 , e_none   )
+      synthesis_node_type_define(const T0 , const T1 , const T2&, const T3&, e_none   )
+      synthesis_node_type_define(const T0&, const T1&, const T2 , const T3 , e_none   )
+      #undef synthesis_node_type_define
+
+      template <typename T, typename T0, typename T1>
+      class T0oT1 : public expression_node<T>
+      {
+      public:
+
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::bfunc_t      bfunc_t;
+         typedef T value_type;
+         typedef T0oT1<T,T0,T1> node_type;
+
+         T0oT1(T0 p0, T1 p1, const bfunc_t p2)
+         : t0_(p0),
+           t1_(p1),
+           f_ (p2)
+         {}
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            static const typename expression_node<T>::node_type result = nodetype_T0oT1<T,T0,T1>::result;
+            return result;
+         }
+
+         inline operator_type operation() const
+         {
+            return e_default;
+         }
+
+         inline T value() const
+         {
+            return f_(t0_,t1_);
+         }
+
+         inline T0 t0() const
+         {
+            return t0_;
+         }
+
+         inline T1 t1() const
+         {
+            return t1_;
+         }
+
+         inline bfunc_t f() const
+         {
+            return f_;
+         }
+
+         template <typename Allocator>
+         static inline expression_node<T>* allocate(Allocator& allocator,
+                                                    T0 p0, T1 p1,
+                                                    bfunc_t p2)
+         {
+            return allocator
+                     .template allocate_type<node_type, T0, T1, bfunc_t&>
+                        (p0, p1, p2);
+         }
+
+      private:
+
+         T0oT1(T0oT1<T,T0,T1>&) {}
+         T0oT1<T,T0,T1>& operator=(T0oT1<T,T0,T1>&) { return (*this); }
+
+         T0 t0_;
+         T1 t1_;
+         const bfunc_t f_;
+      };
+
+      template <typename T, typename T0, typename T1, typename T2, typename ProcessMode>
+      class T0oT1oT2 : public T0oT1oT2_base_node<T>
+      {
+      public:
+
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::bfunc_t      bfunc_t;
+         typedef T value_type;
+         typedef T0oT1oT2<T,T0,T1,T2,ProcessMode> node_type;
+         typedef ProcessMode process_mode_t;
+
+         T0oT1oT2(T0 p0, T1 p1, T2 p2, const bfunc_t p3, const bfunc_t p4)
+         : t0_(p0),
+           t1_(p1),
+           t2_(p2),
+           f0_(p3),
+           f1_(p4)
+         {}
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            static const typename expression_node<T>::node_type result = nodetype_T0oT1oT2<T,T0,T1,T2>::result;
+            return result;
+         }
+
+         inline operator_type operation() const
+         {
+            return e_default;
+         }
+
+         inline T value() const
+         {
+            return ProcessMode::process(t0_, t1_, t2_, f0_, f1_);
+         }
+
+         inline T0 t0() const
+         {
+            return t0_;
+         }
+
+         inline T1 t1() const
+         {
+            return t1_;
+         }
+
+         inline T2 t2() const
+         {
+            return t2_;
+         }
+
+         bfunc_t f0() const
+         {
+            return f0_;
+         }
+
+         bfunc_t f1() const
+         {
+            return f1_;
+         }
+
+         std::string type_id() const
+         {
+            return id();
+         }
+
+         static inline std::string id()
+         {
+            return process_mode_t::template id<T0,T1,T2>();
+         }
+
+         template <typename Allocator>
+         static inline expression_node<T>* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2, bfunc_t p3, bfunc_t p4)
+         {
+            return allocator
+                      .template allocate_type<node_type, T0, T1, T2, bfunc_t, bfunc_t>
+                         (p0, p1, p2, p3, p4);
+         }
+
+      private:
+
+         T0oT1oT2(node_type&) {}
+         node_type& operator=(node_type&) { return (*this); }
+
+         T0 t0_;
+         T1 t1_;
+         T2 t2_;
+         const bfunc_t f0_;
+         const bfunc_t f1_;
+      };
+
+      template <typename T, typename T0_, typename T1_, typename T2_, typename T3_, typename ProcessMode>
+      class T0oT1oT2oT3 : public T0oT1oT2oT3_base_node<T>
+      {
+      public:
+
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::bfunc_t      bfunc_t;
+         typedef T value_type;
+         typedef T0_ T0;
+         typedef T1_ T1;
+         typedef T2_ T2;
+         typedef T3_ T3;
+         typedef T0oT1oT2oT3<T,T0,T1,T2,T3,ProcessMode> node_type;
+         typedef ProcessMode process_mode_t;
+
+         T0oT1oT2oT3(T0 p0, T1 p1, T2 p2, T3 p3, bfunc_t p4, bfunc_t p5, bfunc_t p6)
+         : t0_(p0),
+           t1_(p1),
+           t2_(p2),
+           t3_(p3),
+           f0_(p4),
+           f1_(p5),
+           f2_(p6)
+         {}
+
+         inline T value() const
+         {
+            return ProcessMode::process(t0_, t1_, t2_, t3_, f0_, f1_, f2_);
+         }
+
+         inline T0 t0() const
+         {
+            return t0_;
+         }
+
+         inline T1 t1() const
+         {
+            return t1_;
+         }
+
+         inline T2 t2() const
+         {
+            return t2_;
+         }
+
+         inline T3 t3() const
+         {
+            return t3_;
+         }
+
+         inline bfunc_t f0() const
+         {
+            return f0_;
+         }
+
+         inline bfunc_t f1() const
+         {
+            return f1_;
+         }
+
+         inline bfunc_t f2() const
+         {
+            return f2_;
+         }
+
+         inline std::string type_id() const
+         {
+            return id();
+         }
+
+         static inline std::string id()
+         {
+            return process_mode_t::template id<T0, T1, T2, T3>();
+         }
+
+         template <typename Allocator>
+         static inline expression_node<T>* allocate(Allocator& allocator,
+                                                    T0 p0, T1 p1, T2 p2, T3 p3,
+                                                    bfunc_t p4, bfunc_t p5, bfunc_t p6)
+         {
+            return allocator
+                      .template allocate_type<node_type, T0, T1, T2, T3, bfunc_t, bfunc_t>
+                         (p0, p1, p2, p3, p4, p5, p6);
+         }
+
+      private:
+
+         T0oT1oT2oT3(node_type&) {}
+         node_type& operator=(node_type&) { return (*this); }
+
+         T0 t0_;
+         T1 t1_;
+         T2 t2_;
+         T3 t3_;
+         const bfunc_t f0_;
+         const bfunc_t f1_;
+         const bfunc_t f2_;
+      };
+
+      template <typename T, typename T0, typename T1, typename T2>
+      class T0oT1oT2_sf3 : public T0oT1oT2_base_node<T>
+      {
+      public:
+
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::tfunc_t      tfunc_t;
+         typedef T value_type;
+         typedef T0oT1oT2_sf3<T,T0,T1,T2> node_type;
+
+         T0oT1oT2_sf3(T0 p0, T1 p1, T2 p2, const tfunc_t p3)
+         : t0_(p0),
+           t1_(p1),
+           t2_(p2),
+           f_ (p3)
+         {}
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            static const typename expression_node<T>::node_type result = nodetype_T0oT1oT2<T,T0,T1,T2>::result;
+            return result;
+         }
+
+         inline operator_type operation() const
+         {
+            return e_default;
+         }
+
+         inline T value() const
+         {
+            return f_(t0_, t1_, t2_);
+         }
+
+         inline T0 t0() const
+         {
+            return t0_;
+         }
+
+         inline T1 t1() const
+         {
+            return t1_;
+         }
+
+         inline T2 t2() const
+         {
+            return t2_;
+         }
+
+         tfunc_t f() const
+         {
+            return f_;
+         }
+
+         std::string type_id() const
+         {
+            return id();
+         }
+
+         static inline std::string id()
+         {
+            return "sf3";
+         }
+
+         template <typename Allocator>
+         static inline expression_node<T>* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2, tfunc_t p3)
+         {
+            return allocator
+                     .template allocate_type<node_type, T0, T1, T2, tfunc_t>
+                        (p0, p1, p2, p3);
+         }
+
+      private:
+
+         T0oT1oT2_sf3(node_type&) {}
+         node_type& operator=(node_type&) { return (*this); }
+
+         T0 t0_;
+         T1 t1_;
+         T2 t2_;
+         const tfunc_t f_;
+      };
+
+      template <typename T, typename T0, typename T1, typename T2>
+      class sf3ext_type_node : public T0oT1oT2_base_node<T>
+      {
+      public:
+
+         virtual ~sf3ext_type_node()
+         {}
+
+         virtual T0 t0() const = 0;
+
+         virtual T1 t1() const = 0;
+
+         virtual T2 t2() const = 0;
+      };
+
+      template <typename T, typename T0, typename T1, typename T2, typename SF3Operation>
+      class T0oT1oT2_sf3ext : public sf3ext_type_node<T,T0,T1,T2>
+      {
+      public:
+
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::tfunc_t      tfunc_t;
+         typedef T value_type;
+         typedef T0oT1oT2_sf3ext<T,T0,T1,T2,SF3Operation> node_type;
+
+         T0oT1oT2_sf3ext(T0 p0, T1 p1, T2 p2)
+         : t0_(p0),
+           t1_(p1),
+           t2_(p2)
+         {}
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            static const typename expression_node<T>::node_type result = nodetype_T0oT1oT2<T,T0,T1,T2>::result;
+            return result;
+         }
+
+         inline operator_type operation() const
+         {
+            return e_default;
+         }
+
+         inline T value() const
+         {
+            return SF3Operation::process(t0_, t1_, t2_);
+         }
+
+         T0 t0() const
+         {
+            return t0_;
+         }
+
+         T1 t1() const
+         {
+            return t1_;
+         }
+
+         T2 t2() const
+         {
+            return t2_;
+         }
+
+         std::string type_id() const
+         {
+            return id();
+         }
+
+         static inline std::string id()
+         {
+            return SF3Operation::id();
+         }
+
+         template <typename Allocator>
+         static inline expression_node<T>* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2)
+         {
+            return allocator
+                     .template allocate_type<node_type, T0, T1, T2>
+                        (p0, p1, p2);
+         }
+
+      private:
+
+         T0oT1oT2_sf3ext(node_type&) {}
+         node_type& operator=(node_type&) { return (*this); }
+
+         T0 t0_;
+         T1 t1_;
+         T2 t2_;
+      };
+
+      template <typename T>
+      inline bool is_sf3ext_node(const expression_node<T>* n)
+      {
+         switch (n->type())
+         {
+            case expression_node<T>::e_vovov : return true;
+            case expression_node<T>::e_vovoc : return true;
+            case expression_node<T>::e_vocov : return true;
+            case expression_node<T>::e_covov : return true;
+            case expression_node<T>::e_covoc : return true;
+            default                          : return false;
+         }
+      }
+
+      template <typename T, typename T0, typename T1, typename T2, typename T3>
+      class T0oT1oT2oT3_sf4 : public T0oT1oT2_base_node<T>
+      {
+      public:
+
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::qfunc_t      qfunc_t;
+         typedef T value_type;
+         typedef T0oT1oT2oT3_sf4<T,T0,T1,T2,T3> node_type;
+
+         T0oT1oT2oT3_sf4(T0 p0, T1 p1, T2 p2, T3 p3, const qfunc_t p4)
+         : t0_(p0),
+           t1_(p1),
+           t2_(p2),
+           t3_(p3),
+           f_ (p4)
+         {}
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            static const typename expression_node<T>::node_type result = nodetype_T0oT1oT2oT3<T,T0,T1,T2,T3>::result;
+            return result;
+         }
+
+         inline operator_type operation() const
+         {
+            return e_default;
+         }
+
+         inline T value() const
+         {
+            return f_(t0_, t1_, t2_, t3_);
+         }
+
+         inline T0 t0() const
+         {
+            return t0_;
+         }
+
+         inline T1 t1() const
+         {
+            return t1_;
+         }
+
+         inline T2 t2() const
+         {
+            return t2_;
+         }
+
+         inline T3 t3() const
+         {
+            return t3_;
+         }
+
+         qfunc_t f() const
+         {
+            return f_;
+         }
+
+         std::string type_id() const
+         {
+            return id();
+         }
+
+         static inline std::string id()
+         {
+            return "sf4";
+         }
+
+         template <typename Allocator>
+         static inline expression_node<T>* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2, T3 p3, qfunc_t p4)
+         {
+            return allocator
+                     .template allocate_type<node_type, T0, T1, T2, T3, qfunc_t>
+                        (p0, p1, p2, p3, p4);
+         }
+
+      private:
+
+         T0oT1oT2oT3_sf4(node_type&) {}
+         node_type& operator=(node_type&) { return (*this); }
+
+         T0 t0_;
+         T1 t1_;
+         T2 t2_;
+         T3 t3_;
+         const qfunc_t f_;
+      };
+
+      template <typename T, typename T0, typename T1, typename T2, typename T3, typename SF4Operation>
+      class T0oT1oT2oT3_sf4ext : public T0oT1oT2oT3_base_node<T>
+      {
+      public:
+
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::tfunc_t      tfunc_t;
+         typedef T value_type;
+         typedef T0oT1oT2oT3_sf4ext<T,T0,T1,T2,T3,SF4Operation> node_type;
+
+         T0oT1oT2oT3_sf4ext(T0 p0, T1 p1, T2 p2, T3 p3)
+         : t0_(p0),
+           t1_(p1),
+           t2_(p2),
+           t3_(p3)
+         {}
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            static const typename expression_node<T>::node_type result = nodetype_T0oT1oT2oT3<T,T0,T1,T2,T3>::result;
+            return result;
+         }
+
+         inline operator_type operation() const
+         {
+            return e_default;
+         }
+
+         inline T value() const
+         {
+            return SF4Operation::process(t0_, t1_, t2_, t3_);
+         }
+
+         inline T0 t0() const
+         {
+            return t0_;
+         }
+
+         inline T1 t1() const
+         {
+            return t1_;
+         }
+
+         inline T2 t2() const
+         {
+            return t2_;
+         }
+
+         inline T3 t3() const
+         {
+            return t3_;
+         }
+
+         std::string type_id() const
+         {
+            return id();
+         }
+
+         static inline std::string id()
+         {
+            return SF4Operation::id();
+         }
+
+         template <typename Allocator>
+         static inline expression_node<T>* allocate(Allocator& allocator, T0 p0, T1 p1, T2 p2, T3 p3)
+         {
+            return allocator
+                     .template allocate_type<node_type, T0, T1, T2, T3>
+                        (p0, p1, p2, p3);
+         }
+
+      private:
+
+         T0oT1oT2oT3_sf4ext(node_type&) {}
+         node_type& operator=(node_type&) { return (*this); }
+
+         T0 t0_;
+         T1 t1_;
+         T2 t2_;
+         T3 t3_;
+      };
+
+      template <typename T>
+      inline bool is_sf4ext_node(const expression_node<T>* n)
+      {
+         switch (n->type())
+         {
+            case expression_node<T>::e_vovovov : return true;
+            case expression_node<T>::e_vovovoc : return true;
+            case expression_node<T>::e_vovocov : return true;
+            case expression_node<T>::e_vocovov : return true;
+            case expression_node<T>::e_covovov : return true;
+            case expression_node<T>::e_covocov : return true;
+            case expression_node<T>::e_vocovoc : return true;
+            case expression_node<T>::e_covovoc : return true;
+            case expression_node<T>::e_vococov : return true;
+            default                            : return false;
+         }
+      }
+
+      template <typename T, typename T0, typename T1>
+      struct T0oT1_define
+      {
+         typedef details::T0oT1<T, T0, T1> type0;
+      };
+
+      template <typename T, typename T0, typename T1, typename T2>
+      struct T0oT1oT2_define
+      {
+         typedef details::T0oT1oT2<T, T0, T1, T2, typename T0oT1oT2process<T>::mode0> type0;
+         typedef details::T0oT1oT2<T, T0, T1, T2, typename T0oT1oT2process<T>::mode1> type1;
+         typedef details::T0oT1oT2_sf3<T, T0, T1, T2> sf3_type;
+         typedef details::sf3ext_type_node<T, T0, T1, T2> sf3_type_node;
+      };
+
+      template <typename T, typename T0, typename T1, typename T2, typename T3>
+      struct T0oT1oT2oT3_define
+      {
+         typedef details::T0oT1oT2oT3<T, T0, T1, T2, T3, typename T0oT1oT20T3process<T>::mode0> type0;
+         typedef details::T0oT1oT2oT3<T, T0, T1, T2, T3, typename T0oT1oT20T3process<T>::mode1> type1;
+         typedef details::T0oT1oT2oT3<T, T0, T1, T2, T3, typename T0oT1oT20T3process<T>::mode2> type2;
+         typedef details::T0oT1oT2oT3<T, T0, T1, T2, T3, typename T0oT1oT20T3process<T>::mode3> type3;
+         typedef details::T0oT1oT2oT3<T, T0, T1, T2, T3, typename T0oT1oT20T3process<T>::mode4> type4;
+         typedef details::T0oT1oT2oT3_sf4<T, T0, T1, T2, T3> sf4_type;
+      };
+
+      template <typename T, typename Operation>
+      class vov_node : public vov_base_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef Operation operation_t;
+
+         // variable op variable node
+         explicit vov_node(const T& var0, const T& var1)
+         : v0_(var0),
+           v1_(var1)
+         {}
+
+         inline T value() const
+         {
+            return Operation::process(v0_,v1_);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return Operation::type();
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline const T& v0() const
+         {
+            return v0_;
+         }
+
+         inline const T& v1() const
+         {
+            return v1_;
+         }
+
+      protected:
+
+         const T& v0_;
+         const T& v1_;
+
+      private:
+
+         vov_node(vov_node<T,Operation>&);
+         vov_node<T,Operation>& operator=(vov_node<T,Operation>&);
+      };
+
+      template <typename T, typename Operation>
+      class cov_node : public cov_base_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef Operation operation_t;
+
+         // constant op variable node
+         explicit cov_node(const T& const_var, const T& var)
+         : c_(const_var),
+           v_(var)
+         {}
+
+         inline T value() const
+         {
+            return Operation::process(c_,v_);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return Operation::type();
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline const T c() const
+         {
+            return c_;
+         }
+
+         inline const T& v() const
+         {
+            return v_;
+         }
+
+      protected:
+
+         const T  c_;
+         const T& v_;
+
+      private:
+
+         cov_node(const cov_node<T,Operation>&);
+         cov_node<T,Operation>& operator=(const cov_node<T,Operation>&);
+      };
+
+      template <typename T, typename Operation>
+      class voc_node : public voc_base_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef Operation operation_t;
+
+         // variable op constant node
+         explicit voc_node(const T& var, const T& const_var)
+         : v_(var),
+           c_(const_var)
+         {}
+
+         inline T value() const
+         {
+            return Operation::process(v_,c_);
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline const T c() const
+         {
+            return c_;
+         }
+
+         inline const T& v() const
+         {
+            return v_;
+         }
+
+      protected:
+
+         const T& v_;
+         const T  c_;
+
+      private:
+
+         voc_node(const voc_node<T,Operation>&);
+         voc_node<T,Operation>& operator=(const voc_node<T,Operation>&);
+      };
+
+      template <typename T, typename Operation>
+      class vob_node : public vob_base_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+         typedef Operation operation_t;
+
+         // variable op constant node
+         explicit vob_node(const T& var, const expression_ptr brnch)
+         : v_(var)
+         {
+            init_branches<1>(branch_,brnch);
+         }
+
+        ~vob_node()
+         {
+            cleanup_branches::execute<T,1>(branch_);
+         }
+
+         inline T value() const
+         {
+            return Operation::process(v_,branch_[0].first->value());
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline const T& v() const
+         {
+            return v_;
+         }
+
+         inline expression_node<T>* branch(const std::size_t&) const
+         {
+            return branch_[0].first;
+         }
+
+      private:
+
+         vob_node(const vob_node<T,Operation>&);
+         vob_node<T,Operation>& operator=(const vob_node<T,Operation>&);
+
+         const T& v_;
+         branch_t branch_[1];
+      };
+
+      template <typename T, typename Operation>
+      class bov_node : public bov_base_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+         typedef Operation operation_t;
+
+         // variable op constant node
+         explicit bov_node(const expression_ptr brnch, const T& var)
+         : v_(var)
+         {
+            init_branches<1>(branch_,brnch);
+         }
+
+        ~bov_node()
+         {
+            cleanup_branches::execute<T,1>(branch_);
+         }
+
+         inline T value() const
+         {
+            return Operation::process(branch_[0].first->value(),v_);
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline const T& v() const
+         {
+            return v_;
+         }
+
+         inline expression_node<T>* branch(const std::size_t&) const
+         {
+            return branch_[0].first;
+         }
+
+      private:
+
+         bov_node(const bov_node<T,Operation>&);
+         bov_node<T,Operation>& operator=(const bov_node<T,Operation>&);
+
+         const T& v_;
+         branch_t branch_[1];
+      };
+
+      template <typename T, typename Operation>
+      class cob_node : public cob_base_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+         typedef Operation operation_t;
+
+         // variable op constant node
+         explicit cob_node(const T const_var, const expression_ptr brnch)
+         : c_(const_var)
+         {
+            init_branches<1>(branch_,brnch);
+         }
+
+        ~cob_node()
+         {
+            cleanup_branches::execute<T,1>(branch_);
+         }
+
+         inline T value() const
+         {
+            return Operation::process(c_,branch_[0].first->value());
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline const T c() const
+         {
+            return c_;
+         }
+
+         inline void set_c(const T new_c)
+         {
+            (*const_cast<T*>(&c_)) = new_c;
+         }
+
+         inline expression_node<T>* branch(const std::size_t&) const
+         {
+            return branch_[0].first;
+         }
+
+         inline expression_node<T>* move_branch(const std::size_t&)
+         {
+            branch_[0].second = false;
+            return branch_[0].first;
+         }
+
+      private:
+
+         cob_node(const cob_node<T,Operation>&);
+         cob_node<T,Operation>& operator=(const cob_node<T,Operation>&);
+
+         const T  c_;
+         branch_t branch_[1];
+      };
+
+      template <typename T, typename Operation>
+      class boc_node : public boc_base_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+         typedef Operation operation_t;
+
+         // variable op constant node
+         explicit boc_node(const expression_ptr brnch, const T const_var)
+         : c_(const_var)
+         {
+            init_branches<1>(branch_,brnch);
+         }
+
+        ~boc_node()
+         {
+            cleanup_branches::execute<T,1>(branch_);
+         }
+
+         inline T value() const
+         {
+            return Operation::process(branch_[0].first->value(),c_);
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline const T c() const
+         {
+            return c_;
+         }
+
+         inline void set_c(const T new_c)
+         {
+            (*const_cast<T*>(&c_)) = new_c;
+         }
+
+         inline expression_node<T>* branch(const std::size_t&) const
+         {
+            return branch_[0].first;
+         }
+
+         inline expression_node<T>* move_branch(const std::size_t&)
+         {
+            branch_[0].second = false;
+            return branch_[0].first;
+         }
+
+      private:
+
+         boc_node(const boc_node<T,Operation>&);
+         boc_node<T,Operation>& operator=(const boc_node<T,Operation>&);
+
+         const T  c_;
+         branch_t branch_[1];
+      };
+
+      #ifndef exprtk_disable_string_capabilities
+      template <typename T, typename SType0, typename SType1, typename Operation>
+      class sos_node : public sos_base_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef Operation operation_t;
+
+         // string op string node
+         explicit sos_node(SType0 p0, SType1 p1)
+         : s0_(p0),
+           s1_(p1)
+         {}
+
+         inline T value() const
+         {
+            return Operation::process(s0_,s1_);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return Operation::type();
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline std::string& s0()
+         {
+            return s0_;
+         }
+
+         inline std::string& s1()
+         {
+            return s1_;
+         }
+
+      protected:
+
+         SType0 s0_;
+         SType1 s1_;
+
+      private:
+
+         sos_node(sos_node<T,SType0,SType1,Operation>&);
+         sos_node<T,SType0,SType1,Operation>& operator=(sos_node<T,SType0,SType1,Operation>&);
+      };
+
+      template <typename T, typename SType0, typename SType1, typename RangePack, typename Operation>
+      class str_xrox_node : public sos_base_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef Operation operation_t;
+
+         // string-range op string node
+         explicit str_xrox_node(SType0 p0, SType1 p1, RangePack rp0)
+         : s0_ (p0 ),
+           s1_ (p1 ),
+           rp0_(rp0)
+         {}
+
+        ~str_xrox_node()
+         {
+            rp0_.free();
+         }
+
+         inline T value() const
+         {
+            std::size_t r0 = 0;
+            std::size_t r1 = 0;
+
+            if (rp0_(r0, r1, s0_.size()))
+               return Operation::process(s0_.substr(r0, (r1 - r0) + 1), s1_);
+            else
+               return T(0);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return Operation::type();
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline std::string& s0()
+         {
+            return s0_;
+         }
+
+         inline std::string& s1()
+         {
+            return s1_;
+         }
+
+      protected:
+
+         SType0    s0_;
+         SType1    s1_;
+         RangePack rp0_;
+
+      private:
+
+         str_xrox_node(str_xrox_node<T,SType0,SType1,RangePack,Operation>&);
+         str_xrox_node<T,SType0,SType1,RangePack,Operation>& operator=(str_xrox_node<T,SType0,SType1,RangePack,Operation>&);
+      };
+
+      template <typename T, typename SType0, typename SType1, typename RangePack, typename Operation>
+      class str_xoxr_node : public sos_base_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef Operation operation_t;
+
+         // string op string range node
+         explicit str_xoxr_node(SType0 p0, SType1 p1, RangePack rp1)
+         : s0_ (p0 ),
+           s1_ (p1 ),
+           rp1_(rp1)
+         {}
+
+        ~str_xoxr_node()
+         {
+            rp1_.free();
+         }
+
+         inline T value() const
+         {
+            std::size_t r0 = 0;
+            std::size_t r1 = 0;
+
+            if (rp1_(r0, r1, s1_.size()))
+               return Operation::process(s0_, s1_.substr(r0, (r1 - r0) + 1));
+            else
+               return T(0);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return Operation::type();
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline std::string& s0()
+         {
+            return s0_;
+         }
+
+         inline std::string& s1()
+         {
+            return s1_;
+         }
+
+      protected:
+
+         SType0    s0_;
+         SType1    s1_;
+         RangePack rp1_;
+
+      private:
+
+         str_xoxr_node(str_xoxr_node<T,SType0,SType1,RangePack,Operation>&);
+         str_xoxr_node<T,SType0,SType1,RangePack,Operation>& operator=(str_xoxr_node<T,SType0,SType1,RangePack,Operation>&);
+      };
+
+      template <typename T, typename SType0, typename SType1, typename RangePack, typename Operation>
+      class str_xroxr_node : public sos_base_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef Operation operation_t;
+
+         // string-range op string-range node
+         explicit str_xroxr_node(SType0 p0, SType1 p1, RangePack rp0, RangePack rp1)
+         : s0_ (p0 ),
+           s1_ (p1 ),
+           rp0_(rp0),
+           rp1_(rp1)
+         {}
+
+        ~str_xroxr_node()
+         {
+            rp0_.free();
+            rp1_.free();
+         }
+
+         inline T value() const
+         {
+            std::size_t r0_0 = 0;
+            std::size_t r0_1 = 0;
+            std::size_t r1_0 = 0;
+            std::size_t r1_1 = 0;
+
+            if (
+                 rp0_(r0_0, r1_0, s0_.size()) &&
+                 rp1_(r0_1, r1_1, s1_.size())
+               )
+            {
+               return Operation::process(
+                                          s0_.substr(r0_0, (r1_0 - r0_0) + 1),
+                                          s1_.substr(r0_1, (r1_1 - r0_1) + 1)
+                                        );
+            }
+            else
+               return T(0);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return Operation::type();
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline std::string& s0()
+         {
+            return s0_;
+         }
+
+         inline std::string& s1()
+         {
+            return s1_;
+         }
+
+      protected:
+
+         SType0    s0_;
+         SType1    s1_;
+         RangePack rp0_;
+         RangePack rp1_;
+
+      private:
+
+         str_xroxr_node(str_xroxr_node<T,SType0,SType1,RangePack,Operation>&);
+         str_xroxr_node<T,SType0,SType1,RangePack,Operation>& operator=(str_xroxr_node<T,SType0,SType1,RangePack,Operation>&);
+      };
+
+      template <typename T, typename Operation>
+      class str_sogens_node : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node <T>* expression_ptr;
+         typedef string_base_node<T>*   str_base_ptr;
+         typedef range_pack      <T>         range_t;
+         typedef range_t*                  range_ptr;
+         typedef range_interface<T>         irange_t;
+         typedef irange_t*                irange_ptr;
+
+         str_sogens_node(const operator_type& opr,
+                         expression_ptr branch0,
+                         expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           str0_base_ptr_ (0),
+           str1_base_ptr_ (0),
+           str0_range_ptr_(0),
+           str1_range_ptr_(0)
+         {
+            if (is_generally_string_node(binary_node<T>::branch_[0].first))
+            {
+               str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
+
+               if (0 == str0_base_ptr_)
+                  return;
+
+               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[0].first);
+
+               if (0 == range)
+                  return;
+
+               str0_range_ptr_ = &(range->range_ref());
+            }
+
+            if (is_generally_string_node(binary_node<T>::branch_[1].first))
+            {
+               str1_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[1].first);
+
+               if (0 == str1_base_ptr_)
+                  return;
+
+               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[1].first);
+
+               if (0 == range)
+                  return;
+
+               str1_range_ptr_ = &(range->range_ref());
+            }
+         }
+
+         inline T value() const
+         {
+            if (
+                 str0_base_ptr_  &&
+                 str1_base_ptr_  &&
+                 str0_range_ptr_ &&
+                 str1_range_ptr_
+               )
+            {
+               binary_node<T>::branch_[0].first->value();
+               binary_node<T>::branch_[1].first->value();
+
+               std::size_t str0_r0 = 0;
+               std::size_t str0_r1 = 0;
+
+               std::size_t str1_r0 = 0;
+               std::size_t str1_r1 = 0;
+
+               range_t& range0 = (*str0_range_ptr_);
+               range_t& range1 = (*str1_range_ptr_);
+
+               if (
+                    range0(str0_r0, str0_r1, str0_base_ptr_->size()) &&
+                    range1(str1_r0, str1_r1, str1_base_ptr_->size())
+                  )
+               {
+                  return Operation::process(
+                                             str0_base_ptr_->str().substr(str0_r0,(str0_r1 - str0_r0) + 1),
+                                             str1_base_ptr_->str().substr(str1_r0,(str1_r1 - str1_r0) + 1)
+                                           );
+               }
+            }
+
+            return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return Operation::type();
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+      private:
+
+         str_sogens_node(str_sogens_node<T,Operation>&);
+         str_sogens_node<T,Operation>& operator=(str_sogens_node<T,Operation>&);
+
+         str_base_ptr str0_base_ptr_;
+         str_base_ptr str1_base_ptr_;
+         range_ptr    str0_range_ptr_;
+         range_ptr    str1_range_ptr_;
+      };
+
+      template <typename T, typename SType0, typename SType1, typename SType2, typename Operation>
+      class sosos_node : public sosos_base_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef Operation operation_t;
+
+         // variable op variable node
+         explicit sosos_node(SType0 p0, SType1 p1, SType2 p2)
+         : s0_(p0),
+           s1_(p1),
+           s2_(p2)
+         {}
+
+         inline T value() const
+         {
+            return Operation::process(s0_,s1_,s2_);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return Operation::type();
+         }
+
+         inline operator_type operation() const
+         {
+            return Operation::operation();
+         }
+
+         inline std::string& s0()
+         {
+            return s0_;
+         }
+
+         inline std::string& s1()
+         {
+            return s1_;
+         }
+
+         inline std::string& s2()
+         {
+            return s2_;
+         }
+
+      protected:
+
+         SType0 s0_;
+         SType1 s1_;
+         SType2 s2_;
+
+      private:
+
+         sosos_node(sosos_node<T,SType0,SType1,SType2,Operation>&);
+         sosos_node<T,SType0,SType1,SType2,Operation>& operator=(sosos_node<T,SType0,SType1,SType2,Operation>&);
+      };
+      #endif
+
+      template <typename T, typename PowOp>
+      class ipow_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef PowOp operation_t;
+
+         explicit ipow_node(const T& v)
+         : v_(v)
+         {}
+
+         inline T value() const
+         {
+            return PowOp::result(v_);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_ipow;
+         }
+
+      private:
+
+         ipow_node(const ipow_node<T,PowOp>&);
+         ipow_node<T,PowOp>& operator=(const ipow_node<T,PowOp>&);
+
+         const T& v_;
+      };
+
+      template <typename T, typename PowOp>
+      class bipow_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr, bool> branch_t;
+         typedef PowOp operation_t;
+
+         explicit bipow_node(expression_ptr brnch)
+         {
+            init_branches<1>(branch_, brnch);
+         }
+
+        ~bipow_node()
+         {
+            cleanup_branches::execute<T,1>(branch_);
+         }
+
+         inline T value() const
+         {
+            return PowOp::result(branch_[0].first->value());
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_ipow;
+         }
+
+      private:
+
+         bipow_node(const bipow_node<T,PowOp>&);
+         bipow_node<T,PowOp>& operator=(const bipow_node<T,PowOp>&);
+
+         branch_t branch_[1];
+      };
+
+      template <typename T, typename PowOp>
+      class ipowinv_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef PowOp operation_t;
+
+         explicit ipowinv_node(const T& v)
+         : v_(v)
+         {}
+
+         inline T value() const
+         {
+            return (T(1) / PowOp::result(v_));
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_ipowinv;
+         }
+
+      private:
+
+         ipowinv_node(const ipowinv_node<T,PowOp>&);
+         ipowinv_node<T,PowOp>& operator=(const ipowinv_node<T,PowOp>&);
+
+         const T& v_;
+      };
+
+      template <typename T, typename PowOp>
+      class bipowninv_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr, bool> branch_t;
+         typedef PowOp operation_t;
+
+         explicit bipowninv_node(expression_ptr brnch)
+         {
+            init_branches<1>(branch_, brnch);
+         }
+
+        ~bipowninv_node()
+         {
+            cleanup_branches::execute<T,1>(branch_);
+         }
+
+         inline T value() const
+         {
+            return (T(1) / PowOp::result(branch_[0].first->value()));
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_ipowinv;
+         }
+
+      private:
+
+         bipowninv_node(const bipowninv_node<T,PowOp>&);
+         bipowninv_node<T,PowOp>& operator=(const bipowninv_node<T,PowOp>&);
+
+         branch_t branch_[1];
+      };
+
+      template <typename T>
+      inline bool is_vov_node(const expression_node<T>* node)
+      {
+         return (0 != dynamic_cast<const vov_base_node<T>*>(node));
+      }
+
+      template <typename T>
+      inline bool is_cov_node(const expression_node<T>* node)
+      {
+         return (0 != dynamic_cast<const cov_base_node<T>*>(node));
+      }
+
+      template <typename T>
+      inline bool is_voc_node(const expression_node<T>* node)
+      {
+         return (0 != dynamic_cast<const voc_base_node<T>*>(node));
+      }
+
+      template <typename T>
+      inline bool is_cob_node(const expression_node<T>* node)
+      {
+         return (0 != dynamic_cast<const cob_base_node<T>*>(node));
+      }
+
+      template <typename T>
+      inline bool is_boc_node(const expression_node<T>* node)
+      {
+         return (0 != dynamic_cast<const boc_base_node<T>*>(node));
+      }
+
+      template <typename T>
+      inline bool is_t0ot1ot2_node(const expression_node<T>* node)
+      {
+         return (0 != dynamic_cast<const T0oT1oT2_base_node<T>*>(node));
+      }
+
+      template <typename T>
+      inline bool is_t0ot1ot2ot3_node(const expression_node<T>* node)
+      {
+         return (0 != dynamic_cast<const T0oT1oT2oT3_base_node<T>*>(node));
+      }
+
+      template <typename T>
+      inline bool is_uv_node(const expression_node<T>* node)
+      {
+         return (0 != dynamic_cast<const uv_base_node<T>*>(node));
+      }
+
+      template <typename T>
+      inline bool is_string_node(const expression_node<T>* node)
+      {
+         return node && (expression_node<T>::e_stringvar == node->type());
+      }
+
+      template <typename T>
+      inline bool is_string_range_node(const expression_node<T>* node)
+      {
+         return node && (expression_node<T>::e_stringvarrng == node->type());
+      }
+
+      template <typename T>
+      inline bool is_const_string_node(const expression_node<T>* node)
+      {
+         return node && (expression_node<T>::e_stringconst == node->type());
+      }
+
+      template <typename T>
+      inline bool is_const_string_range_node(const expression_node<T>* node)
+      {
+         return node && (expression_node<T>::e_cstringvarrng == node->type());
+      }
+
+      template <typename T>
+      inline bool is_string_assignment_node(const expression_node<T>* node)
+      {
+         return node && (expression_node<T>::e_strass == node->type());
+      }
+
+      template <typename T>
+      inline bool is_string_concat_node(const expression_node<T>* node)
+      {
+         return node && (expression_node<T>::e_strconcat == node->type());
+      }
+
+      template <typename T>
+      inline bool is_string_function_node(const expression_node<T>* node)
+      {
+         return node && (expression_node<T>::e_strfunction == node->type());
+      }
+
+      template <typename T>
+      inline bool is_string_condition_node(const expression_node<T>* node)
+      {
+         return node && (expression_node<T>::e_strcondition == node->type());
+      }
+
+      template <typename T>
+      inline bool is_string_ccondition_node(const expression_node<T>* node)
+      {
+         return node && (expression_node<T>::e_strccondition == node->type());
+      }
+
+      template <typename T>
+      inline bool is_string_vararg_node(const expression_node<T>* node)
+      {
+         return node && (expression_node<T>::e_stringvararg == node->type());
+      }
+
+      template <typename T>
+      inline bool is_genricstring_range_node(const expression_node<T>* node)
+      {
+         return node && (expression_node<T>::e_strgenrange == node->type());
+      }
+
+      template <typename T>
+      inline bool is_generally_string_node(const expression_node<T>* node)
+      {
+         if (node)
+         {
+            switch (node->type())
+            {
+               case expression_node<T>::e_stringvar     :
+               case expression_node<T>::e_stringconst   :
+               case expression_node<T>::e_stringvarrng  :
+               case expression_node<T>::e_cstringvarrng :
+               case expression_node<T>::e_strgenrange   :
+               case expression_node<T>::e_strass        :
+               case expression_node<T>::e_strconcat     :
+               case expression_node<T>::e_strfunction   :
+               case expression_node<T>::e_strcondition  :
+               case expression_node<T>::e_strccondition :
+               case expression_node<T>::e_stringvararg  : return true;
+               default                                  : return false;
+            }
+         }
+
+         return false;
+      }
+
+      class node_allocator
+      {
+      public:
+
+         template <typename ResultNode, typename OpType, typename ExprNode>
+         inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[1])
+         {
+            return allocate<ResultNode>(operation, branch[0]);
+         }
+
+         template <typename ResultNode, typename OpType, typename ExprNode>
+         inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[2])
+         {
+            return allocate<ResultNode>(operation, branch[0], branch[1]);
+         }
+
+         template <typename ResultNode, typename OpType, typename ExprNode>
+         inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[3])
+         {
+            return allocate<ResultNode>(operation, branch[0], branch[1], branch[2]);
+         }
+
+         template <typename ResultNode, typename OpType, typename ExprNode>
+         inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[4])
+         {
+            return allocate<ResultNode>(operation, branch[0], branch[1], branch[2], branch[3]);
+         }
+
+         template <typename ResultNode, typename OpType, typename ExprNode>
+         inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[5])
+         {
+            return allocate<ResultNode>(operation, branch[0],branch[1], branch[2], branch[3], branch[4]);
+         }
+
+         template <typename ResultNode, typename OpType, typename ExprNode>
+         inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[6])
+         {
+            return allocate<ResultNode>(operation, branch[0], branch[1], branch[2], branch[3], branch[4], branch[5]);
+         }
+
+         template <typename node_type>
+         inline expression_node<typename node_type::value_type>* allocate() const
+         {
+            return (new node_type());
+         }
+
+         template <typename node_type,
+                   typename Type,
+                   typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline expression_node<typename node_type::value_type>* allocate(const Sequence<Type,Allocator>& seq) const
+         {
+            return (new node_type(seq));
+         }
+
+         template <typename node_type, typename T1>
+         inline expression_node<typename node_type::value_type>* allocate(T1& t1) const
+         {
+            return (new node_type(t1));
+         }
+
+         template <typename node_type, typename T1>
+         inline expression_node<typename node_type::value_type>* allocate_c(const T1& t1) const
+         {
+            return (new node_type(t1));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2>
+         inline expression_node<typename node_type::value_type>* allocate(const T1& t1, const T2& t2) const
+         {
+            return (new node_type(t1, t2));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2>
+         inline expression_node<typename node_type::value_type>* allocate_cr(const T1& t1, T2& t2) const
+         {
+            return (new node_type(t1, t2));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2>
+         inline expression_node<typename node_type::value_type>* allocate_rc(T1& t1, const T2& t2) const
+         {
+            return (new node_type(t1, t2));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2>
+         inline expression_node<typename node_type::value_type>* allocate_rr(T1& t1, T2& t2) const
+         {
+            return (new node_type(t1, t2));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2>
+         inline expression_node<typename node_type::value_type>* allocate_tt(T1 t1, T2 t2) const
+         {
+            return (new node_type(t1, t2));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2, typename T3>
+         inline expression_node<typename node_type::value_type>* allocate_ttt(T1 t1, T2 t2, T3 t3) const
+         {
+            return (new node_type(t1, t2, t3));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2, typename T3, typename T4>
+         inline expression_node<typename node_type::value_type>* allocate_tttt(T1 t1, T2 t2, T3 t3, T4 t4) const
+         {
+            return (new node_type(t1, t2, t3, t4));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2, typename T3>
+         inline expression_node<typename node_type::value_type>* allocate_rrr(T1& t1, T2& t2, T3& t3) const
+         {
+            return (new node_type(t1, t2, t3));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2, typename T3, typename T4>
+         inline expression_node<typename node_type::value_type>* allocate_rrrr(T1& t1, T2& t2, T3& t3, T4& t4) const
+         {
+            return (new node_type(t1, t2, t3, t4));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2, typename T3, typename T4, typename T5>
+         inline expression_node<typename node_type::value_type>* allocate_rrrrr(T1& t1, T2& t2, T3& t3, T4& t4, T5& t5) const
+         {
+            return (new node_type(t1, t2, t3, t4, t5));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2, typename T3>
+         inline expression_node<typename node_type::value_type>* allocate(const T1& t1, const T2& t2,
+                                                                          const T3& t3) const
+         {
+            return (new node_type(t1, t2, t3));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2,
+                   typename T3, typename T4>
+         inline expression_node<typename node_type::value_type>* allocate(const T1& t1, const T2& t2,
+                                                                          const T3& t3, const T4& t4) const
+         {
+            return (new node_type(t1, t2, t3, t4));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2,
+                   typename T3, typename T4, typename T5>
+         inline expression_node<typename node_type::value_type>* allocate(const T1& t1, const T2& t2,
+                                                                          const T3& t3, const T4& t4,
+                                                                          const T5& t5) const
+         {
+            return (new node_type(t1, t2, t3, t4, t5));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2,
+                   typename T3, typename T4, typename T5, typename T6>
+         inline expression_node<typename node_type::value_type>* allocate(const T1& t1, const T2& t2,
+                                                                          const T3& t3, const T4& t4,
+                                                                          const T5& t5, const T6& t6) const
+         {
+            return (new node_type(t1, t2, t3, t4, t5, t6));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2,
+                   typename T3, typename T4,
+                   typename T5, typename T6, typename T7>
+         inline expression_node<typename node_type::value_type>* allocate(const T1& t1, const T2& t2,
+                                                                          const T3& t3, const T4& t4,
+                                                                          const T5& t5, const T6& t6,
+                                                                          const T7& t7) const
+         {
+            return (new node_type(t1, t2, t3, t4, t5, t6, t7));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2,
+                   typename T3, typename T4,
+                   typename T5, typename T6,
+                   typename T7, typename T8>
+         inline expression_node<typename node_type::value_type>* allocate(const T1& t1, const T2& t2,
+                                                                          const T3& t3, const T4& t4,
+                                                                          const T5& t5, const T6& t6,
+                                                                          const T7& t7, const T8& t8) const
+         {
+            return (new node_type(t1, t2, t3, t4, t5, t6, t7, t8));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2,
+                   typename T3, typename T4,
+                   typename T5, typename T6,
+                   typename T7, typename T8, typename T9>
+         inline expression_node<typename node_type::value_type>* allocate(const T1& t1, const T2& t2,
+                                                                          const T3& t3, const T4& t4,
+                                                                          const T5& t5, const T6& t6,
+                                                                          const T7& t7, const T8& t8,
+                                                                          const T9& t9) const
+         {
+            return (new node_type(t1, t2, t3, t4, t5, t6, t7, t8, t9));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2,
+                   typename T3, typename T4,
+                   typename T5, typename T6,
+                   typename T7, typename T8,
+                   typename T9, typename T10>
+         inline expression_node<typename node_type::value_type>* allocate(const T1& t1, const  T2&  t2,
+                                                                          const T3& t3, const  T4&  t4,
+                                                                          const T5& t5, const  T6&  t6,
+                                                                          const T7& t7, const  T8&  t8,
+                                                                          const T9& t9, const T10& t10) const
+         {
+            return (new node_type(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2, typename T3>
+         inline expression_node<typename node_type::value_type>* allocate_type(T1 t1, T2 t2, T3 t3) const
+         {
+            return (new node_type(t1, t2, t3));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2,
+                   typename T3, typename T4>
+         inline expression_node<typename node_type::value_type>* allocate_type(T1 t1, T2 t2,
+                                                                               T3 t3, T4 t4) const
+         {
+            return (new node_type(t1, t2, t3, t4));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2,
+                   typename T3, typename T4,
+                   typename T5>
+         inline expression_node<typename node_type::value_type>* allocate_type(T1 t1, T2 t2,
+                                                                               T3 t3, T4 t4,
+                                                                               T5 t5) const
+         {
+            return (new node_type(t1, t2, t3, t4, t5));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2,
+                   typename T3, typename T4,
+                   typename T5, typename T6>
+         inline expression_node<typename node_type::value_type>* allocate_type(T1 t1, T2 t2,
+                                                                               T3 t3, T4 t4,
+                                                                               T5 t5, T6 t6) const
+         {
+            return (new node_type(t1, t2, t3, t4, t5, t6));
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2,
+                   typename T3, typename T4,
+                   typename T5, typename T6, typename T7>
+         inline expression_node<typename node_type::value_type>* allocate_type(T1 t1, T2 t2,
+                                                                               T3 t3, T4 t4,
+                                                                               T5 t5, T6 t6,
+                                                                               T7 t7) const
+         {
+            return (new node_type(t1, t2, t3, t4, t5, t6, t7));
+         }
+
+         template <typename T>
+         void inline free(expression_node<T>*& e) const
+         {
+            delete e;
+            e = 0;
+         }
+      };
+
+      inline void load_operations_map(std::multimap<std::string,details::base_operation_t,details::ilesscompare>& m)
+      {
+         #define register_op(Symbol,Type,Args)                                               \
+         m.insert(std::make_pair(std::string(Symbol),details::base_operation_t(Type,Args))); \
+
+         register_op(      "abs", e_abs     , 1)
+         register_op(     "acos", e_acos    , 1)
+         register_op(    "acosh", e_acosh   , 1)
+         register_op(     "asin", e_asin    , 1)
+         register_op(    "asinh", e_asinh   , 1)
+         register_op(     "atan", e_atan    , 1)
+         register_op(    "atanh", e_atanh   , 1)
+         register_op(     "ceil", e_ceil    , 1)
+         register_op(      "cos", e_cos     , 1)
+         register_op(     "cosh", e_cosh    , 1)
+         register_op(      "exp", e_exp     , 1)
+         register_op(    "expm1", e_expm1   , 1)
+         register_op(    "floor", e_floor   , 1)
+         register_op(      "log", e_log     , 1)
+         register_op(    "log10", e_log10   , 1)
+         register_op(     "log2", e_log2    , 1)
+         register_op(    "log1p", e_log1p   , 1)
+         register_op(    "round", e_round   , 1)
+         register_op(      "sin", e_sin     , 1)
+         register_op(     "sinc", e_sinc    , 1)
+         register_op(     "sinh", e_sinh    , 1)
+         register_op(      "sec", e_sec     , 1)
+         register_op(      "csc", e_csc     , 1)
+         register_op(     "sqrt", e_sqrt    , 1)
+         register_op(      "tan", e_tan     , 1)
+         register_op(     "tanh", e_tanh    , 1)
+         register_op(      "cot", e_cot     , 1)
+         register_op(  "rad2deg", e_r2d     , 1)
+         register_op(  "deg2rad", e_d2r     , 1)
+         register_op( "deg2grad", e_d2g     , 1)
+         register_op( "grad2deg", e_g2d     , 1)
+         register_op(      "sgn", e_sgn     , 1)
+         register_op(      "not", e_notl    , 1)
+         register_op(      "erf", e_erf     , 1)
+         register_op(     "erfc", e_erfc    , 1)
+         register_op(     "ncdf", e_ncdf    , 1)
+         register_op(     "frac", e_frac    , 1)
+         register_op(    "trunc", e_trunc   , 1)
+         register_op(    "atan2", e_atan2   , 2)
+         register_op(      "mod", e_mod     , 2)
+         register_op(     "logn", e_logn    , 2)
+         register_op(      "pow", e_pow     , 2)
+         register_op(     "root", e_root    , 2)
+         register_op(   "roundn", e_roundn  , 2)
+         register_op(    "equal", e_equal   , 2)
+         register_op("not_equal", e_nequal  , 2)
+         register_op(    "hypot", e_hypot   , 2)
+         register_op(      "shr", e_shr     , 2)
+         register_op(      "shl", e_shl     , 2)
+         register_op(    "clamp", e_clamp   , 3)
+         register_op(   "iclamp", e_iclamp  , 3)
+         register_op(  "inrange", e_inrange , 3)
+         #undef register_op
+      }
+
+   } // namespace details
+
+   class function_traits
+   {
+   public:
+
+      function_traits()
+      : allow_zero_parameters_(false),
+        has_side_effects_(true),
+        min_num_args_(0),
+        max_num_args_(std::numeric_limits<std::size_t>::max())
+      {}
+
+      inline bool& allow_zero_parameters()
+      {
+         return allow_zero_parameters_;
+      }
+
+      inline bool& has_side_effects()
+      {
+         return has_side_effects_;
+      }
+
+      std::size_t& min_num_args()
+      {
+         return min_num_args_;
+      }
+
+      std::size_t& max_num_args()
+      {
+         return max_num_args_;
+      }
+
+   private:
+
+      bool allow_zero_parameters_;
+      bool has_side_effects_;
+      std::size_t min_num_args_;
+      std::size_t max_num_args_;
+   };
+
+   template <typename FunctionType>
+   void enable_zero_parameters(FunctionType& func)
+   {
+      func.allow_zero_parameters() = true;
+
+      if (0 != func.min_num_args())
+      {
+         func.min_num_args() = 0;
+      }
+   }
+
+   template <typename FunctionType>
+   void disable_zero_parameters(FunctionType& func)
+   {
+      func.allow_zero_parameters() = false;
+   }
+
+   template <typename FunctionType>
+   void enable_has_side_effects(FunctionType& func)
+   {
+      func.has_side_effects() = true;
+   }
+
+   template <typename FunctionType>
+   void disable_has_side_effects(FunctionType& func)
+   {
+      func.has_side_effects() = false;
+   }
+
+   template <typename FunctionType>
+   void set_min_num_args(FunctionType& func, const std::size_t& num_args)
+   {
+      func.min_num_args() = num_args;
+
+      if ((0 != func.min_num_args()) && func.allow_zero_parameters())
+         func.allow_zero_parameters() = false;
+   }
+
+   template <typename FunctionType>
+   void set_max_num_args(FunctionType& func, const std::size_t& num_args)
+   {
+      func.max_num_args() = num_args;
+   }
+
+   template <typename T>
+   class ifunction : public function_traits
+   {
+   public:
+
+      explicit ifunction(const std::size_t& pc)
+      : param_count(pc)
+      {}
+
+      virtual ~ifunction()
+      {}
+
+      #define empty_method_body                      \
+      {                                              \
+         return std::numeric_limits<T>::quiet_NaN(); \
+      }                                              \
+
+      inline virtual T operator() ()
+      empty_method_body
+
+       inline virtual T operator() (const T&)
+      empty_method_body
+
+       inline virtual T operator() (const T&,const T&)
+      empty_method_body
+
+       inline virtual T operator() (const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
+                                  const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
+                                   const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
+                                   const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
+                                   const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
+                                   const T&, const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
+                                   const T&, const T&, const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
+                                   const T&, const T&, const T&, const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
+                                   const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
+                                   const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
+                                   const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&)
+      empty_method_body
+
+      #undef empty_method_body
+
+      std::size_t param_count;
+   };
+
+   template <typename T>
+   class ivararg_function : public function_traits
+   {
+   public:
+
+      virtual ~ivararg_function()
+      {}
+
+      inline virtual T operator() (const std::vector<T>&)
+      {
+         exprtk_debug(("ivararg_function::operator() - Operator has not been overridden.\n"));
+         return std::numeric_limits<T>::quiet_NaN();
+      }
+   };
+
+   template <typename T>
+   class igeneric_function : public function_traits
+   {
+   public:
+
+      enum return_type
+      {
+         e_rtrn_scalar   = 0,
+         e_rtrn_string   = 1,
+         e_rtrn_overload = 2
+      };
+
+      typedef T type;
+      typedef type_store<T> generic_type;
+      typedef typename generic_type::parameter_list parameter_list_t;
+
+      igeneric_function(const std::string& param_seq = "", const return_type rtr_type = e_rtrn_scalar)
+      : parameter_sequence(param_seq),
+        rtrn_type(rtr_type)
+      {}
+
+      virtual ~igeneric_function()
+      {}
+
+      #define igeneric_function_empty_body(N)        \
+      {                                              \
+         exprtk_debug(("igeneric_function::operator() - Operator has not been overridden. ["#N"]\n")); \
+         return std::numeric_limits<T>::quiet_NaN(); \
+      }                                              \
+
+      // f(i_0,i_1,....,i_N) --> Scalar
+      inline virtual T operator() (parameter_list_t)
+      igeneric_function_empty_body(1)
+
+      // f(i_0,i_1,....,i_N) --> String
+      inline virtual T operator() (std::string&, parameter_list_t)
+      igeneric_function_empty_body(2)
+
+      // f(psi,i_0,i_1,....,i_N) --> Scalar
+      inline virtual T operator() (const std::size_t&, parameter_list_t)
+      igeneric_function_empty_body(3)
+
+      // f(psi,i_0,i_1,....,i_N) --> String
+      inline virtual T operator() (const std::size_t&, std::string&, parameter_list_t)
+      igeneric_function_empty_body(4)
+
+      std::string parameter_sequence;
+      return_type rtrn_type;
+   };
+
+   template <typename T> class parser;
+   template <typename T> class expression_helper;
+
+   template <typename T>
+   class symbol_table
+   {
+   public:
+
+      typedef T (*ff00_functor)();
+      typedef T (*ff01_functor)(T);
+      typedef T (*ff02_functor)(T, T);
+      typedef T (*ff03_functor)(T, T, T);
+      typedef T (*ff04_functor)(T, T, T, T);
+      typedef T (*ff05_functor)(T, T, T, T, T);
+      typedef T (*ff06_functor)(T, T, T, T, T, T);
+      typedef T (*ff07_functor)(T, T, T, T, T, T, T);
+      typedef T (*ff08_functor)(T, T, T, T, T, T, T, T);
+      typedef T (*ff09_functor)(T, T, T, T, T, T, T, T, T);
+      typedef T (*ff10_functor)(T, T, T, T, T, T, T, T, T, T);
+      typedef T (*ff11_functor)(T, T, T, T, T, T, T, T, T, T, T);
+      typedef T (*ff12_functor)(T, T, T, T, T, T, T, T, T, T, T, T);
+      typedef T (*ff13_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T);
+      typedef T (*ff14_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T, T);
+      typedef T (*ff15_functor)(T, T, T, T, T, T, T, T, T, T, T, T, T, T, T);
+
+   protected:
+
+       struct freefunc00 : public exprtk::ifunction<T>
+       {
+          using exprtk::ifunction<T>::operator();
+
+          explicit freefunc00(ff00_functor ff) : exprtk::ifunction<T>(0), f(ff) {}
+          inline T operator() ()
+          { return f(); }
+          ff00_functor f;
+       };
+
+      struct freefunc01 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc01(ff01_functor ff) : exprtk::ifunction<T>(1), f(ff) {}
+         inline T operator() (const T& v0)
+         { return f(v0); }
+         ff01_functor f;
+      };
+
+      struct freefunc02 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc02(ff02_functor ff) : exprtk::ifunction<T>(2), f(ff) {}
+         inline T operator() (const T& v0, const T& v1)
+         { return f(v0, v1); }
+         ff02_functor f;
+      };
+
+      struct freefunc03 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc03(ff03_functor ff) : exprtk::ifunction<T>(3), f(ff) {}
+         inline T operator() (const T& v0, const T& v1, const T& v2)
+         { return f(v0, v1, v2); }
+         ff03_functor f;
+      };
+
+      struct freefunc04 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc04(ff04_functor ff) : exprtk::ifunction<T>(4), f(ff) {}
+         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3)
+         { return f(v0, v1, v2, v3); }
+         ff04_functor f;
+      };
+
+      struct freefunc05 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc05(ff05_functor ff) : exprtk::ifunction<T>(5), f(ff) {}
+         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4)
+         { return f(v0, v1, v2, v3, v4); }
+         ff05_functor f;
+      };
+
+      struct freefunc06 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc06(ff06_functor ff) : exprtk::ifunction<T>(6), f(ff) {}
+         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, const T& v5)
+         { return f(v0, v1, v2, v3, v4, v5); }
+         ff06_functor f;
+      };
+
+      struct freefunc07 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc07(ff07_functor ff) : exprtk::ifunction<T>(7), f(ff) {}
+         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
+                              const T& v5, const T& v6)
+         { return f(v0, v1, v2, v3, v4, v5, v6); }
+         ff07_functor f;
+      };
+
+      struct freefunc08 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc08(ff08_functor ff) : exprtk::ifunction<T>(8), f(ff) {}
+         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
+                              const T& v5, const T& v6, const T& v7)
+         { return f(v0, v1, v2, v3, v4, v5, v6, v7); }
+         ff08_functor f;
+      };
+
+      struct freefunc09 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc09(ff09_functor ff) : exprtk::ifunction<T>(9), f(ff) {}
+         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
+                              const T& v5, const T& v6, const T& v7, const T& v8)
+         { return f(v0, v1, v2, v3, v4, v5, v6, v7, v8); }
+         ff09_functor f;
+      };
+
+      struct freefunc10 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc10(ff10_functor ff) : exprtk::ifunction<T>(10), f(ff) {}
+         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
+                              const T& v5, const T& v6, const T& v7, const T& v8, const T& v9)
+         { return f(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); }
+         ff10_functor f;
+      };
+
+      struct freefunc11 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc11(ff11_functor ff) : exprtk::ifunction<T>(11), f(ff) {}
+         inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
+                              const T& v5, const T& v6, const T& v7, const T& v8, const T& v9, const T& v10)
+         { return f(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10); }
+         ff11_functor f;
+      };
+
+      struct freefunc12 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc12(ff12_functor ff) : exprtk::ifunction<T>(12), f(ff) {}
+         inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04,
+                              const T& v05, const T& v06, const T& v07, const T& v08, const T& v09,
+                              const T& v10, const T& v11)
+         { return f(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11); }
+         ff12_functor f;
+      };
+
+      struct freefunc13 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc13(ff13_functor ff) : exprtk::ifunction<T>(13), f(ff) {}
+         inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04,
+                              const T& v05, const T& v06, const T& v07, const T& v08, const T& v09,
+                              const T& v10, const T& v11, const T& v12)
+         { return f(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12); }
+         ff13_functor f;
+      };
+
+      struct freefunc14 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc14(ff14_functor ff) : exprtk::ifunction<T>(14), f(ff) {}
+         inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04,
+                              const T& v05, const T& v06, const T& v07, const T& v08, const T& v09,
+                              const T& v10, const T& v11, const T& v12, const T& v13)
+         { return f(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12, v13); }
+         ff14_functor f;
+      };
+
+      struct freefunc15 : public exprtk::ifunction<T>
+      {
+         using exprtk::ifunction<T>::operator();
+
+         explicit freefunc15(ff15_functor ff) : exprtk::ifunction<T>(15), f(ff) {}
+         inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04,
+                              const T& v05, const T& v06, const T& v07, const T& v08, const T& v09,
+                              const T& v10, const T& v11, const T& v12, const T& v13, const T& v14)
+         { return f(v00, v01, v02, v03, v04, v05, v06, v07, v08, v09, v10, v11, v12, v13, v14); }
+         ff15_functor f;
+      };
+
+      template <typename Type, typename RawType>
+      struct type_store
+      {
+         typedef details::expression_node<T>*        expression_ptr;
+         typedef typename details::variable_node<T>  variable_node_t;
+         typedef ifunction<T>                        ifunction_t;
+         typedef ivararg_function<T>                 ivararg_function_t;
+         typedef igeneric_function<T>                igeneric_function_t;
+         typedef details::vector_holder<T>           vector_t;
+         #ifndef exprtk_disable_string_capabilities
+         typedef typename details::stringvar_node<T> stringvar_node_t;
+         #endif
+
+         typedef Type type_t;
+         typedef type_t* type_ptr;
+         typedef std::pair<bool,type_ptr> type_pair_t;
+         typedef std::map<std::string,type_pair_t,details::ilesscompare> type_map_t;
+         typedef typename type_map_t::iterator tm_itr_t;
+         typedef typename type_map_t::const_iterator tm_const_itr_t;
+
+         enum { lut_size = 256 };
+
+         type_map_t  map;
+         std::size_t size;
+
+         type_store()
+         : size(0)
+         {}
+
+         inline bool symbol_exists(const std::string& symbol_name) const
+         {
+            if (symbol_name.empty())
+               return false;
+            else if (map.end() != map.find(symbol_name))
+               return true;
+            else
+               return false;
+         }
+
+         template <typename PtrType>
+         inline std::string entity_name(const PtrType& ptr) const
+         {
+            if (map.empty())
+               return std::string();
+
+            tm_const_itr_t itr = map.begin();
+
+            while (map.end() != itr)
+            {
+               if (itr->second.second == ptr)
+               {
+                  return itr->first;
+               }
+               else
+                  ++itr;
+            }
+
+            return std::string();
+         }
+
+         inline bool is_constant(const std::string& symbol_name) const
+         {
+            if (symbol_name.empty())
+               return false;
+            else
+            {
+               const tm_const_itr_t itr = map.find(symbol_name);
+
+               if (map.end() == itr)
+                  return false;
+               else
+                  return (*itr).second.first;
+            }
+         }
+
+         template <typename Tie, typename RType>
+         inline bool add_impl(const std::string& symbol_name, RType t, const bool is_const)
+         {
+            if (symbol_name.size() > 1)
+            {
+               for (std::size_t i = 0; i < details::reserved_symbols_size; ++i)
+               {
+                  if (details::imatch(symbol_name, details::reserved_symbols[i]))
+                  {
+                     return false;
+                  }
+               }
+            }
+
+            const tm_itr_t itr = map.find(symbol_name);
+
+            if (map.end() == itr)
+            {
+               map[symbol_name] = Tie::make(t,is_const);
+               ++size;
+            }
+
+            return true;
+         }
+
+         struct tie_array
+         {
+            static inline std::pair<bool,vector_t*> make(std::pair<T*,std::size_t> v, const bool is_const = false)
+            {
+               return std::make_pair(is_const, new vector_t(v.first, v.second));
+            }
+         };
+
+         struct tie_stdvec
+         {
+            template <typename Allocator>
+            static inline std::pair<bool,vector_t*> make(std::vector<T,Allocator>& v, const bool is_const = false)
+            {
+               return std::make_pair(is_const, new vector_t(v));
+            }
+         };
+
+         struct tie_vecview
+         {
+            static inline std::pair<bool,vector_t*> make(exprtk::vector_view<T>& v, const bool is_const = false)
+            {
+               return std::make_pair(is_const, new vector_t(v));
+            }
+         };
+
+         struct tie_stddeq
+         {
+            template <typename Allocator>
+            static inline std::pair<bool,vector_t*> make(std::deque<T,Allocator>& v, const bool is_const = false)
+            {
+               return std::make_pair(is_const, new vector_t(v));
+            }
+         };
+
+         template <std::size_t v_size>
+         inline bool add(const std::string& symbol_name, T (&v)[v_size], const bool is_const = false)
+         {
+            return add_impl<tie_array,std::pair<T*,std::size_t> >
+                      (symbol_name, std::make_pair(v,v_size), is_const);
+         }
+
+         inline bool add(const std::string& symbol_name, T* v, const std::size_t v_size, const bool is_const = false)
+         {
+            return add_impl<tie_array,std::pair<T*,std::size_t> >
+                     (symbol_name, std::make_pair(v,v_size), is_const);
+         }
+
+         template <typename Allocator>
+         inline bool add(const std::string& symbol_name, std::vector<T,Allocator>& v, const bool is_const = false)
+         {
+            return add_impl<tie_stdvec,std::vector<T,Allocator>&>
+                      (symbol_name, v, is_const);
+         }
+
+         inline bool add(const std::string& symbol_name, exprtk::vector_view<T>& v, const bool is_const = false)
+         {
+            return add_impl<tie_vecview,exprtk::vector_view<T>&>
+                      (symbol_name, v, is_const);
+         }
+
+         template <typename Allocator>
+         inline bool add(const std::string& symbol_name, std::deque<T,Allocator>& v, const bool is_const = false)
+         {
+            return add_impl<tie_stddeq,std::deque<T,Allocator>&>
+                      (symbol_name, v, is_const);
+         }
+
+         inline bool add(const std::string& symbol_name, RawType& t, const bool is_const = false)
+         {
+            struct tie
+            {
+               static inline std::pair<bool,variable_node_t*> make(T& t,const bool is_const = false)
+               {
+                  return std::make_pair(is_const, new variable_node_t(t));
+               }
+
+               #ifndef exprtk_disable_string_capabilities
+               static inline std::pair<bool,stringvar_node_t*> make(std::string& t,const bool is_const = false)
+               {
+                  return std::make_pair(is_const, new stringvar_node_t(t));
+               }
+               #endif
+
+               static inline std::pair<bool,function_t*> make(function_t& t, const bool is_constant = false)
+               {
+                  return std::make_pair(is_constant,&t);
+               }
+
+               static inline std::pair<bool,vararg_function_t*> make(vararg_function_t& t, const bool is_const = false)
+               {
+                  return std::make_pair(is_const,&t);
+               }
+
+               static inline std::pair<bool,generic_function_t*> make(generic_function_t& t, const bool is_constant = false)
+               {
+                  return std::make_pair(is_constant,&t);
+               }
+            };
+
+            const tm_itr_t itr = map.find(symbol_name);
+
+            if (map.end() == itr)
+            {
+               map[symbol_name] = tie::make(t,is_const);
+               ++size;
+            }
+
+            return true;
+         }
+
+         inline type_ptr get(const std::string& symbol_name) const
+         {
+            const tm_const_itr_t itr = map.find(symbol_name);
+
+            if (map.end() == itr)
+               return reinterpret_cast<type_ptr>(0);
+            else
+               return itr->second.second;
+         }
+
+         template <typename TType, typename TRawType, typename PtrType>
+         struct ptr_match
+         {
+            static inline bool test(const PtrType, const void*)
+            {
+               return false;
+            }
+         };
+
+         template <typename TType, typename TRawType>
+         struct ptr_match<TType,TRawType,variable_node_t*>
+         {
+            static inline bool test(const variable_node_t* p, const void* ptr)
+            {
+               exprtk_debug(("ptr_match::test() - %p <--> %p\n",(void*)(&(p->ref())),ptr));
+               return (&(p->ref()) == ptr);
+            }
+         };
+
+         inline type_ptr get_from_varptr(const void* ptr) const
+         {
+            tm_const_itr_t itr = map.begin();
+
+            while (map.end() != itr)
+            {
+               type_ptr ret_ptr = itr->second.second;
+
+               if (ptr_match<Type,RawType,type_ptr>::test(ret_ptr,ptr))
+               {
+                  return ret_ptr;
+               }
+
+               ++itr;
+            }
+
+            return type_ptr(0);
+         }
+
+         inline bool remove(const std::string& symbol_name, const bool delete_node = true)
+         {
+            const tm_itr_t itr = map.find(symbol_name);
+
+            if (map.end() != itr)
+            {
+               struct deleter
+               {
+                  static inline void process(std::pair<bool,variable_node_t*>& n)  { delete n.second; }
+                  static inline void process(std::pair<bool,vector_t*>& n)         { delete n.second; }
+                  #ifndef exprtk_disable_string_capabilities
+                  static inline void process(std::pair<bool,stringvar_node_t*>& n) { delete n.second; }
+                  #endif
+                  static inline void process(std::pair<bool,function_t*>&)         {                  }
+               };
+
+               if (delete_node)
+               {
+                  deleter::process((*itr).second);
+               }
+
+               map.erase(itr);
+               --size;
+
+               return true;
+            }
+            else
+               return false;
+         }
+
+         inline RawType& type_ref(const std::string& symbol_name)
+         {
+            struct init_type
+            {
+               static inline double set(double)           { return (0.0);           }
+               static inline double set(long double)      { return (0.0);           }
+               static inline float  set(float)            { return (0.0f);          }
+               static inline std::string set(std::string) { return std::string(""); }
+            };
+
+            static RawType null_type = init_type::set(RawType());
+
+            const tm_const_itr_t itr = map.find(symbol_name);
+
+            if (map.end() == itr)
+               return null_type;
+            else
+               return itr->second.second->ref();
+         }
+
+         inline void clear(const bool delete_node = true)
+         {
+            struct deleter
+            {
+               static inline void process(std::pair<bool,variable_node_t*>& n)  { delete n.second; }
+               static inline void process(std::pair<bool,vector_t*>& n)         { delete n.second; }
+               static inline void process(std::pair<bool,function_t*>&)         {                  }
+               #ifndef exprtk_disable_string_capabilities
+               static inline void process(std::pair<bool,stringvar_node_t*>& n) { delete n.second; }
+               #endif
+            };
+
+            if (!map.empty())
+            {
+               if (delete_node)
+               {
+                  tm_itr_t itr = map.begin();
+                  tm_itr_t end = map.end  ();
+
+                  while (end != itr)
+                  {
+                     deleter::process((*itr).second);
+                     ++itr;
+                  }
+               }
+
+               map.clear();
+            }
+
+            size = 0;
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline std::size_t get_list(Sequence<std::pair<std::string,RawType>,Allocator>& list) const
+         {
+            std::size_t count = 0;
+
+            if (!map.empty())
+            {
+               tm_const_itr_t itr = map.begin();
+               tm_const_itr_t end = map.end  ();
+
+               while (end != itr)
+               {
+                  list.push_back(std::make_pair((*itr).first,itr->second.second->ref()));
+                  ++itr;
+                  ++count;
+               }
+            }
+
+            return count;
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline std::size_t get_list(Sequence<std::string,Allocator>& vlist) const
+         {
+            std::size_t count = 0;
+
+            if (!map.empty())
+            {
+               tm_const_itr_t itr = map.begin();
+               tm_const_itr_t end = map.end  ();
+
+               while (end != itr)
+               {
+                  vlist.push_back((*itr).first);
+                  ++itr;
+                  ++count;
+               }
+            }
+
+            return count;
+         }
+      };
+
+      typedef details::expression_node<T>* expression_ptr;
+      typedef typename details::variable_node<T> variable_t;
+      typedef typename details::vector_holder<T> vector_holder_t;
+      typedef variable_t* variable_ptr;
+      #ifndef exprtk_disable_string_capabilities
+      typedef typename details::stringvar_node<T> stringvar_t;
+      typedef stringvar_t* stringvar_ptr;
+      #endif
+      typedef ifunction        <T> function_t;
+      typedef ivararg_function <T> vararg_function_t;
+      typedef igeneric_function<T> generic_function_t;
+      typedef function_t* function_ptr;
+      typedef vararg_function_t*  vararg_function_ptr;
+      typedef generic_function_t* generic_function_ptr;
+
+      static const std::size_t lut_size = 256;
+
+      // Symbol Table Holder
+      struct control_block
+      {
+         struct st_data
+         {
+            type_store<typename details::variable_node<T>,T> variable_store;
+            #ifndef exprtk_disable_string_capabilities
+            type_store<typename details::stringvar_node<T>,std::string> stringvar_store;
+            #endif
+            type_store<ifunction<T>,ifunction<T> >                 function_store;
+            type_store<ivararg_function <T>,ivararg_function <T> > vararg_function_store;
+            type_store<igeneric_function<T>,igeneric_function<T> > generic_function_store;
+            type_store<igeneric_function<T>,igeneric_function<T> > string_function_store;
+            type_store<igeneric_function<T>,igeneric_function<T> > overload_function_store;
+            type_store<vector_holder_t,vector_holder_t>            vector_store;
+
+            st_data()
+            {
+               for (std::size_t i = 0; i < details::reserved_words_size; ++i)
+               {
+                  reserved_symbol_table_.insert(details::reserved_words[i]);
+               }
+
+               for (std::size_t i = 0; i < details::reserved_symbols_size; ++i)
+               {
+                  reserved_symbol_table_.insert(details::reserved_symbols[i]);
+               }
+            }
+
+           ~st_data()
+            {
+               for (std::size_t i = 0; i < free_function_list_.size(); ++i)
+               {
+                  delete free_function_list_[i];
+               }
+            }
+
+            inline bool is_reserved_symbol(const std::string& symbol) const
+            {
+               return (reserved_symbol_table_.end() != reserved_symbol_table_.find(symbol));
+            }
+
+            static inline st_data* create()
+            {
+               return (new st_data);
+            }
+
+            static inline void destroy(st_data*& sd)
+            {
+               delete sd;
+               sd = reinterpret_cast<st_data*>(0);
+            }
+
+            std::list<T>               local_symbol_list_;
+            std::list<std::string>     local_stringvar_list_;
+            std::set<std::string>      reserved_symbol_table_;
+            std::vector<ifunction<T>*> free_function_list_;
+         };
+
+         control_block()
+         : ref_count(1),
+           data_(st_data::create())
+         {}
+
+         explicit control_block(st_data* data)
+         : ref_count(1),
+           data_(data)
+         {}
+
+        ~control_block()
+         {
+            if (data_ && (0 == ref_count))
+            {
+               st_data::destroy(data_);
+            }
+         }
+
+         static inline control_block* create()
+         {
+            return (new control_block);
+         }
+
+         template <typename SymTab>
+         static inline void destroy(control_block*& cntrl_blck, SymTab* sym_tab)
+         {
+            if (cntrl_blck)
+            {
+               if (
+                    (0 !=   cntrl_blck->ref_count) &&
+                    (0 == --cntrl_blck->ref_count)
+                  )
+               {
+                  if (sym_tab)
+                     sym_tab->clear();
+
+                  delete cntrl_blck;
+               }
+
+               cntrl_blck = 0;
+            }
+         }
+
+         std::size_t ref_count;
+         st_data* data_;
+      };
+
+   public:
+
+      symbol_table()
+      : control_block_(control_block::create())
+      {
+         clear();
+      }
+
+     ~symbol_table()
+      {
+         control_block::destroy(control_block_,this);
+      }
+
+      symbol_table(const symbol_table<T>& st)
+      {
+         control_block_ = st.control_block_;
+         control_block_->ref_count++;
+      }
+
+      inline symbol_table<T>& operator=(const symbol_table<T>& st)
+      {
+         if (this != &st)
+         {
+            control_block::destroy(control_block_,reinterpret_cast<symbol_table<T>*>(0));
+
+            control_block_ = st.control_block_;
+            control_block_->ref_count++;
+         }
+
+         return (*this);
+      }
+
+      inline bool operator==(const symbol_table<T>& st) const
+      {
+         return (this == &st) || (control_block_ == st.control_block_);
+      }
+
+      inline void clear_variables(const bool delete_node = true)
+      {
+         local_data().variable_store.clear(delete_node);
+      }
+
+      inline void clear_functions()
+      {
+         local_data().function_store.clear();
+      }
+
+      inline void clear_strings()
+      {
+         #ifndef exprtk_disable_string_capabilities
+         local_data().stringvar_store.clear();
+         #endif
+      }
+
+      inline void clear_vectors()
+      {
+         local_data().vector_store.clear();
+      }
+
+      inline void clear_local_constants()
+      {
+         local_data().local_symbol_list_.clear();
+      }
+
+      inline void clear()
+      {
+         if (!valid()) return;
+         clear_variables      ();
+         clear_functions      ();
+         clear_strings        ();
+         clear_vectors        ();
+         clear_local_constants();
+      }
+
+      inline std::size_t variable_count() const
+      {
+         if (valid())
+            return local_data().variable_store.size;
+         else
+            return 0;
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      inline std::size_t stringvar_count() const
+      {
+         if (valid())
+            return local_data().stringvar_store.size;
+         else
+            return 0;
+      }
+      #endif
+
+      inline std::size_t function_count() const
+      {
+         if (valid())
+            return local_data().function_store.size;
+         else
+            return 0;
+      }
+
+      inline std::size_t vector_count() const
+      {
+         if (valid())
+            return local_data().vector_store.size;
+         else
+            return 0;
+      }
+
+      inline variable_ptr get_variable(const std::string& variable_name) const
+      {
+         if (!valid())
+            return reinterpret_cast<variable_ptr>(0);
+         else if (!valid_symbol(variable_name))
+            return reinterpret_cast<variable_ptr>(0);
+         else
+            return local_data().variable_store.get(variable_name);
+      }
+
+      inline variable_ptr get_variable(const T& var_ref) const
+      {
+         if (!valid())
+            return reinterpret_cast<variable_ptr>(0);
+         else
+            return local_data().variable_store.get_from_varptr(
+                                                  reinterpret_cast<const void*>(&var_ref));
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      inline stringvar_ptr get_stringvar(const std::string& string_name) const
+      {
+         if (!valid())
+            return reinterpret_cast<stringvar_ptr>(0);
+         else if (!valid_symbol(string_name))
+            return reinterpret_cast<stringvar_ptr>(0);
+         else
+            return local_data().stringvar_store.get(string_name);
+      }
+      #endif
+
+      inline function_ptr get_function(const std::string& function_name) const
+      {
+         if (!valid())
+            return reinterpret_cast<function_ptr>(0);
+         else if (!valid_symbol(function_name))
+            return reinterpret_cast<function_ptr>(0);
+         else
+            return local_data().function_store.get(function_name);
+      }
+
+      inline vararg_function_ptr get_vararg_function(const std::string& vararg_function_name) const
+      {
+         if (!valid())
+            return reinterpret_cast<vararg_function_ptr>(0);
+         else if (!valid_symbol(vararg_function_name))
+            return reinterpret_cast<vararg_function_ptr>(0);
+         else
+            return local_data().vararg_function_store.get(vararg_function_name);
+      }
+
+      inline generic_function_ptr get_generic_function(const std::string& function_name) const
+      {
+         if (!valid())
+            return reinterpret_cast<generic_function_ptr>(0);
+         else if (!valid_symbol(function_name))
+            return reinterpret_cast<generic_function_ptr>(0);
+         else
+            return local_data().generic_function_store.get(function_name);
+      }
+
+      inline generic_function_ptr get_string_function(const std::string& function_name) const
+      {
+         if (!valid())
+            return reinterpret_cast<generic_function_ptr>(0);
+         else if (!valid_symbol(function_name))
+            return reinterpret_cast<generic_function_ptr>(0);
+         else
+            return local_data().string_function_store.get(function_name);
+      }
+
+      inline generic_function_ptr get_overload_function(const std::string& function_name) const
+      {
+         if (!valid())
+            return reinterpret_cast<generic_function_ptr>(0);
+         else if (!valid_symbol(function_name))
+            return reinterpret_cast<generic_function_ptr>(0);
+         else
+            return local_data().overload_function_store.get(function_name);
+      }
+
+      typedef vector_holder_t* vector_holder_ptr;
+
+      inline vector_holder_ptr get_vector(const std::string& vector_name) const
+      {
+         if (!valid())
+            return reinterpret_cast<vector_holder_ptr>(0);
+         else if (!valid_symbol(vector_name))
+            return reinterpret_cast<vector_holder_ptr>(0);
+         else
+            return local_data().vector_store.get(vector_name);
+      }
+
+      inline T& variable_ref(const std::string& symbol_name)
+      {
+         static T null_var = T(0);
+         if (!valid())
+            return null_var;
+         else if (!valid_symbol(symbol_name))
+            return null_var;
+         else
+            return local_data().variable_store.type_ref(symbol_name);
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      inline std::string& stringvar_ref(const std::string& symbol_name)
+      {
+         static std::string null_stringvar;
+         if (!valid())
+            return null_stringvar;
+         else if (!valid_symbol(symbol_name))
+            return null_stringvar;
+         else
+            return local_data().stringvar_store.type_ref(symbol_name);
+      }
+      #endif
+
+      inline bool is_constant_node(const std::string& symbol_name) const
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(symbol_name))
+            return false;
+         else
+            return local_data().variable_store.is_constant(symbol_name);
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      inline bool is_constant_string(const std::string& symbol_name) const
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(symbol_name))
+            return false;
+         else if (!local_data().stringvar_store.symbol_exists(symbol_name))
+            return false;
+         else
+            return local_data().stringvar_store.is_constant(symbol_name);
+      }
+      #endif
+
+      inline bool create_variable(const std::string& variable_name, const T& value = T(0))
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(variable_name))
+            return false;
+         else if (symbol_exists(variable_name))
+            return false;
+
+         local_data().local_symbol_list_.push_back(value);
+         T& t = local_data().local_symbol_list_.back();
+
+         return add_variable(variable_name,t);
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      inline bool create_stringvar(const std::string& stringvar_name, const std::string& value = std::string(""))
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(stringvar_name))
+            return false;
+         else if (symbol_exists(stringvar_name))
+            return false;
+
+         local_data().local_stringvar_list_.push_back(value);
+         std::string& s = local_data().local_stringvar_list_.back();
+
+         return add_stringvar(stringvar_name,s);
+      }
+      #endif
+
+      inline bool add_variable(const std::string& variable_name, T& t, const bool is_constant = false)
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(variable_name))
+            return false;
+         else if (symbol_exists(variable_name))
+            return false;
+         else
+            return local_data().variable_store.add(variable_name, t, is_constant);
+      }
+
+      inline bool add_constant(const std::string& constant_name, const T& value)
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(constant_name))
+            return false;
+         else if (symbol_exists(constant_name))
+            return false;
+
+         local_data().local_symbol_list_.push_back(value);
+         T& t = local_data().local_symbol_list_.back();
+
+         return add_variable(constant_name, t, true);
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      inline bool add_stringvar(const std::string& stringvar_name, std::string& s, const bool is_constant = false)
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(stringvar_name))
+            return false;
+         else if (symbol_exists(stringvar_name))
+            return false;
+         else
+            return local_data().stringvar_store.add(stringvar_name, s, is_constant);
+      }
+      #endif
+
+      inline bool add_function(const std::string& function_name, function_t& function)
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(function_name))
+            return false;
+         else if (symbol_exists(function_name))
+            return false;
+         else
+            return local_data().function_store.add(function_name,function);
+      }
+
+      inline bool add_function(const std::string& vararg_function_name, vararg_function_t& vararg_function)
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(vararg_function_name))
+            return false;
+         else if (symbol_exists(vararg_function_name))
+            return false;
+         else
+            return local_data().vararg_function_store.add(vararg_function_name,vararg_function);
+      }
+
+      inline bool add_function(const std::string& function_name, generic_function_t& function)
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(function_name))
+            return false;
+         else if (symbol_exists(function_name))
+            return false;
+         else if (
+                   (
+                     (generic_function_t::e_rtrn_scalar == function.rtrn_type) ||
+                     (generic_function_t::e_rtrn_string == function.rtrn_type)
+                   ) &&
+                   std::string::npos != function.parameter_sequence.find_first_not_of("STVZ*?|")
+                 )
+            return false;
+         else if (
+                   (generic_function_t::e_rtrn_overload  == function.rtrn_type) &&
+                   std::string::npos != function.parameter_sequence.find_first_not_of("STVZ*?|:")
+                 )
+            return false;
+
+         switch (function.rtrn_type)
+         {
+            case generic_function_t::e_rtrn_scalar :
+               return local_data().generic_function_store.add(function_name,function);
+
+            case generic_function_t::e_rtrn_string :
+               return local_data().string_function_store.add(function_name,function);
+
+            case generic_function_t::e_rtrn_overload :
+               return local_data().overload_function_store.add(function_name,function);
+         }
+
+         return false;
+      }
+
+      #define exprtk_define_freefunction(NN)                                                \
+      inline bool add_function(const std::string& function_name, ff##NN##_functor function) \
+      {                                                                                     \
+         if (!valid())                                                                      \
+         { return false; }                                                                  \
+         if (!valid_symbol(function_name))                                                  \
+         { return false; }                                                                  \
+         if (symbol_exists(function_name))                                                  \
+         { return false; }                                                                  \
+                                                                                            \
+         exprtk::ifunction<T>* ifunc = new freefunc##NN(function);                          \
+                                                                                            \
+         local_data().free_function_list_.push_back(ifunc);                                 \
+                                                                                            \
+         return add_function(function_name,(*local_data().free_function_list_.back()));     \
+      }                                                                                     \
+
+      exprtk_define_freefunction(00) exprtk_define_freefunction(01)
+      exprtk_define_freefunction(02) exprtk_define_freefunction(03)
+      exprtk_define_freefunction(04) exprtk_define_freefunction(05)
+      exprtk_define_freefunction(06) exprtk_define_freefunction(07)
+      exprtk_define_freefunction(08) exprtk_define_freefunction(09)
+      exprtk_define_freefunction(10) exprtk_define_freefunction(11)
+      exprtk_define_freefunction(12) exprtk_define_freefunction(13)
+      exprtk_define_freefunction(14) exprtk_define_freefunction(15)
+
+      #undef exprtk_define_freefunction
+
+      inline bool add_reserved_function(const std::string& function_name, function_t& function)
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(function_name,false))
+            return false;
+         else if (symbol_exists(function_name,false))
+            return false;
+         else
+            return local_data().function_store.add(function_name,function);
+      }
+
+      inline bool add_reserved_function(const std::string& vararg_function_name, vararg_function_t& vararg_function)
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(vararg_function_name,false))
+            return false;
+         else if (symbol_exists(vararg_function_name,false))
+            return false;
+         else
+            return local_data().vararg_function_store.add(vararg_function_name,vararg_function);
+      }
+
+      inline bool add_reserved_function(const std::string& function_name, generic_function_t& function)
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(function_name,false))
+            return false;
+         else if (symbol_exists(function_name,false))
+            return false;
+         else if (
+                   (
+                     (generic_function_t::e_rtrn_scalar == function.rtrn_type) ||
+                     (generic_function_t::e_rtrn_string == function.rtrn_type)
+                   ) &&
+                   std::string::npos != function.parameter_sequence.find_first_not_of("STV*?|")
+                 )
+            return false;
+         else if (
+                   generic_function_t::e_rtrn_overload &&
+                   std::string::npos != function.parameter_sequence.find_first_not_of("STV*?|:")
+                 )
+            return false;
+
+         switch (function.rtrn_type)
+         {
+            case generic_function_t::e_rtrn_scalar :
+               return local_data().generic_function_store.add(function_name,function);
+
+            case generic_function_t::e_rtrn_string :
+               return local_data().string_function_store.add(function_name,function);
+
+            case generic_function_t::e_rtrn_overload :
+               return local_data().overload_function_store.add(function_name,function);
+         }
+
+         return false;
+      }
+
+      template <std::size_t N>
+      inline bool add_vector(const std::string& vector_name, T (&v)[N])
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(vector_name))
+            return false;
+         else if (symbol_exists(vector_name))
+            return false;
+         else
+            return local_data().vector_store.add(vector_name,v);
+      }
+
+      inline bool add_vector(const std::string& vector_name, T* v, const std::size_t& v_size)
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(vector_name))
+            return false;
+         else if (symbol_exists(vector_name))
+            return false;
+         else if (0 == v_size)
+            return false;
+         else
+            return local_data().vector_store.add(vector_name, v, v_size);
+      }
+
+      template <typename Allocator>
+      inline bool add_vector(const std::string& vector_name, std::vector<T,Allocator>& v)
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(vector_name))
+            return false;
+         else if (symbol_exists(vector_name))
+            return false;
+         else if (0 == v.size())
+            return false;
+         else
+            return local_data().vector_store.add(vector_name,v);
+      }
+
+      inline bool add_vector(const std::string& vector_name, exprtk::vector_view<T>& v)
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(vector_name))
+            return false;
+         else if (symbol_exists(vector_name))
+            return false;
+         else if (0 == v.size())
+            return false;
+         else
+            return local_data().vector_store.add(vector_name,v);
+      }
+
+      inline bool remove_variable(const std::string& variable_name, const bool delete_node = true)
+      {
+         if (!valid())
+            return false;
+         else
+            return local_data().variable_store.remove(variable_name, delete_node);
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      inline bool remove_stringvar(const std::string& string_name)
+      {
+         if (!valid())
+            return false;
+         else
+            return local_data().stringvar_store.remove(string_name);
+      }
+      #endif
+
+      inline bool remove_function(const std::string& function_name)
+      {
+         if (!valid())
+            return false;
+         else
+            return local_data().function_store.remove(function_name);
+      }
+
+      inline bool remove_vararg_function(const std::string& vararg_function_name)
+      {
+         if (!valid())
+            return false;
+         else
+            return local_data().vararg_function_store.remove(vararg_function_name);
+      }
+
+      inline bool remove_vector(const std::string& vector_name)
+      {
+         if (!valid())
+            return false;
+         else
+            return local_data().vector_store.remove(vector_name);
+      }
+
+      inline bool add_constants()
+      {
+         return add_pi      () &&
+                add_epsilon () &&
+                add_infinity() ;
+      }
+
+      inline bool add_pi()
+      {
+         const typename details::numeric::details::number_type<T>::type num_type;
+         static const T local_pi = details::numeric::details::const_pi_impl<T>(num_type);
+         return add_constant("pi",local_pi);
+      }
+
+      inline bool add_epsilon()
+      {
+         static const T local_epsilon = details::numeric::details::epsilon_type<T>::value();
+         return add_constant("epsilon",local_epsilon);
+      }
+
+      inline bool add_infinity()
+      {
+         static const T local_infinity = std::numeric_limits<T>::infinity();
+         return add_constant("inf",local_infinity);
+      }
+
+      template <typename Package>
+      inline bool add_package(Package& package)
+      {
+         return package.register_package(*this);
+      }
+
+      template <typename Allocator,
+                template <typename, typename> class Sequence>
+      inline std::size_t get_variable_list(Sequence<std::pair<std::string,T>,Allocator>& vlist) const
+      {
+         if (!valid())
+            return 0;
+         else
+            return local_data().variable_store.get_list(vlist);
+      }
+
+      template <typename Allocator,
+                template <typename, typename> class Sequence>
+      inline std::size_t get_variable_list(Sequence<std::string,Allocator>& vlist) const
+      {
+         if (!valid())
+            return 0;
+         else
+            return local_data().variable_store.get_list(vlist);
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      template <typename Allocator,
+                template <typename, typename> class Sequence>
+      inline std::size_t get_stringvar_list(Sequence<std::pair<std::string,std::string>,Allocator>& svlist) const
+      {
+         if (!valid())
+            return 0;
+         else
+            return local_data().stringvar_store.get_list(svlist);
+      }
+
+      template <typename Allocator,
+                template <typename, typename> class Sequence>
+      inline std::size_t get_stringvar_list(Sequence<std::string,Allocator>& svlist) const
+      {
+         if (!valid())
+            return 0;
+         else
+            return local_data().stringvar_store.get_list(svlist);
+      }
+      #endif
+
+      template <typename Allocator,
+                template <typename, typename> class Sequence>
+      inline std::size_t get_vector_list(Sequence<std::string,Allocator>& vlist) const
+      {
+         if (!valid())
+            return 0;
+         else
+            return local_data().vector_store.get_list(vlist);
+      }
+
+      inline bool symbol_exists(const std::string& symbol_name, const bool check_reserved_symb = true) const
+      {
+         /*
+            Function will return true if symbol_name exists as either a
+            reserved symbol, variable, stringvar, vector or function name
+            in any of the type stores.
+         */
+         if (!valid())
+            return false;
+         else if (local_data().variable_store.symbol_exists(symbol_name))
+            return true;
+         #ifndef exprtk_disable_string_capabilities
+         else if (local_data().stringvar_store.symbol_exists(symbol_name))
+            return true;
+         #endif
+         else if (local_data().vector_store.symbol_exists(symbol_name))
+            return true;
+         else if (local_data().function_store.symbol_exists(symbol_name))
+            return true;
+         else if (check_reserved_symb && local_data().is_reserved_symbol(symbol_name))
+            return true;
+         else
+            return false;
+      }
+
+      inline bool is_variable(const std::string& variable_name) const
+      {
+         if (!valid())
+            return false;
+         else
+            return local_data().variable_store.symbol_exists(variable_name);
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      inline bool is_stringvar(const std::string& stringvar_name) const
+      {
+         if (!valid())
+            return false;
+         else
+            return local_data().stringvar_store.symbol_exists(stringvar_name);
+      }
+
+      inline bool is_conststr_stringvar(const std::string& symbol_name) const
+      {
+         if (!valid())
+            return false;
+         else if (!valid_symbol(symbol_name))
+            return false;
+         else if (!local_data().stringvar_store.symbol_exists(symbol_name))
+            return false;
+
+         return (
+                  local_data().stringvar_store.symbol_exists(symbol_name) ||
+                  local_data().stringvar_store.is_constant  (symbol_name)
+                );
+      }
+      #endif
+
+      inline bool is_function(const std::string& function_name) const
+      {
+         if (!valid())
+            return false;
+         else
+            return local_data().function_store.symbol_exists(function_name);
+      }
+
+      inline bool is_vararg_function(const std::string& vararg_function_name) const
+      {
+         if (!valid())
+            return false;
+         else
+            return local_data().vararg_function_store.symbol_exists(vararg_function_name);
+      }
+
+      inline bool is_vector(const std::string& vector_name) const
+      {
+         if (!valid())
+            return false;
+         else
+            return local_data().vector_store.symbol_exists(vector_name);
+      }
+
+      inline std::string get_variable_name(const expression_ptr& ptr) const
+      {
+         return local_data().variable_store.entity_name(ptr);
+      }
+
+      inline std::string get_vector_name(const vector_holder_ptr& ptr) const
+      {
+         return local_data().vector_store.entity_name(ptr);
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      inline std::string get_stringvar_name(const expression_ptr& ptr) const
+      {
+         return local_data().stringvar_store.entity_name(ptr);
+      }
+
+      inline std::string get_conststr_stringvar_name(const expression_ptr& ptr) const
+      {
+         return local_data().stringvar_store.entity_name(ptr);
+      }
+      #endif
+
+      inline bool valid() const
+      {
+         // Symbol table sanity check.
+         return control_block_ && control_block_->data_;
+      }
+
+      inline void load_from(const symbol_table<T>& st)
+      {
+         {
+            std::vector<std::string> name_list;
+
+            st.local_data().function_store.get_list(name_list);
+
+            if (!name_list.empty())
+            {
+               for (std::size_t i = 0; i < name_list.size(); ++i)
+               {
+                  exprtk::ifunction<T>& ifunc = *st.get_function(name_list[i]);
+                  add_function(name_list[i],ifunc);
+               }
+            }
+         }
+
+         {
+            std::vector<std::string> name_list;
+
+            st.local_data().vararg_function_store.get_list(name_list);
+
+            if (!name_list.empty())
+            {
+               for (std::size_t i = 0; i < name_list.size(); ++i)
+               {
+                  exprtk::ivararg_function<T>& ivafunc = *st.get_vararg_function(name_list[i]);
+                  add_function(name_list[i],ivafunc);
+               }
+            }
+         }
+
+         {
+            std::vector<std::string> name_list;
+
+            st.local_data().generic_function_store.get_list(name_list);
+
+            if (!name_list.empty())
+            {
+               for (std::size_t i = 0; i < name_list.size(); ++i)
+               {
+                  exprtk::igeneric_function<T>& ifunc = *st.get_generic_function(name_list[i]);
+                  add_function(name_list[i],ifunc);
+               }
+            }
+         }
+
+         {
+            std::vector<std::string> name_list;
+
+            st.local_data().string_function_store.get_list(name_list);
+
+            if (!name_list.empty())
+            {
+               for (std::size_t i = 0; i < name_list.size(); ++i)
+               {
+                  exprtk::igeneric_function<T>& ifunc = *st.get_string_function(name_list[i]);
+                  add_function(name_list[i],ifunc);
+               }
+            }
+         }
+
+         {
+            std::vector<std::string> name_list;
+
+            st.local_data().overload_function_store.get_list(name_list);
+
+            if (!name_list.empty())
+            {
+               for (std::size_t i = 0; i < name_list.size(); ++i)
+               {
+                  exprtk::igeneric_function<T>& ifunc = *st.get_overload_function(name_list[i]);
+                  add_function(name_list[i],ifunc);
+               }
+            }
+         }
+      }
+
+   private:
+
+      inline bool valid_symbol(const std::string& symbol, const bool check_reserved_symb = true) const
+      {
+         if (symbol.empty())
+            return false;
+         else if (!details::is_letter(symbol[0]))
+            return false;
+         else if (symbol.size() > 1)
+         {
+            for (std::size_t i = 1; i < symbol.size(); ++i)
+            {
+               if (
+                    !details::is_letter_or_digit(symbol[i]) &&
+                    ('_' != symbol[i])
+                  )
+               {
+                  if (('.' == symbol[i]) && (i < (symbol.size() - 1)))
+                     continue;
+                  else
+                     return false;
+               }
+            }
+         }
+
+         return (check_reserved_symb) ? (!local_data().is_reserved_symbol(symbol)) : true;
+      }
+
+      inline bool valid_function(const std::string& symbol) const
+      {
+         if (symbol.empty())
+            return false;
+         else if (!details::is_letter(symbol[0]))
+            return false;
+         else if (symbol.size() > 1)
+         {
+            for (std::size_t i = 1; i < symbol.size(); ++i)
+            {
+               if (
+                    !details::is_letter_or_digit(symbol[i]) &&
+                    ('_' != symbol[i])
+                  )
+               {
+                  if (('.' == symbol[i]) && (i < (symbol.size() - 1)))
+                     continue;
+                  else
+                     return false;
+               }
+            }
+         }
+
+         return true;
+      }
+
+      typedef typename control_block::st_data local_data_t;
+
+      inline local_data_t& local_data()
+      {
+         return *(control_block_->data_);
+      }
+
+      inline const local_data_t& local_data() const
+      {
+         return *(control_block_->data_);
+      }
+
+      control_block* control_block_;
+
+      friend class parser<T>;
+   };
+
+   template <typename T>
+   class function_compositor;
+
+   template <typename T>
+   class expression
+   {
+   private:
+
+      typedef details::expression_node<T>*  expression_ptr;
+      typedef details::vector_holder<T>* vector_holder_ptr;
+      typedef std::vector<symbol_table<T> >  symtab_list_t;
+
+      struct control_block
+      {
+         enum data_type
+         {
+            e_unknown  ,
+            e_expr     ,
+            e_vecholder,
+            e_data     ,
+            e_vecdata  ,
+            e_string
+         };
+
+         struct data_pack
+         {
+            data_pack()
+            : pointer(0),
+              type(e_unknown),
+              size(0)
+            {}
+
+            data_pack(void* ptr, const data_type dt, const std::size_t sz = 0)
+            : pointer(ptr),
+              type(dt),
+              size(sz)
+            {}
+
+            void*       pointer;
+            data_type   type;
+            std::size_t size;
+         };
+
+         typedef std::vector<data_pack> local_data_list_t;
+         typedef results_context<T>     results_context_t;
+
+         control_block()
+         : ref_count(0),
+           expr     (0),
+           results  (0),
+           retinv_null(false),
+           return_invoked(&retinv_null)
+         {}
+
+         explicit control_block(expression_ptr e)
+         : ref_count(1),
+           expr     (e),
+           results  (0),
+           retinv_null(false),
+           return_invoked(&retinv_null)
+         {}
+
+        ~control_block()
+         {
+            if (expr && details::branch_deletable(expr))
+            {
+               destroy_node(expr);
+            }
+
+            if (!local_data_list.empty())
+            {
+               for (std::size_t i = 0; i < local_data_list.size(); ++i)
+               {
+                  switch (local_data_list[i].type)
+                  {
+                     case e_expr      : delete reinterpret_cast<expression_ptr>(local_data_list[i].pointer);
+                                        break;
+
+                     case e_vecholder : delete reinterpret_cast<vector_holder_ptr>(local_data_list[i].pointer);
+                                        break;
+
+                     case e_data      : delete (T*)(local_data_list[i].pointer);
+                                        break;
+
+                     case e_vecdata   : delete [] (T*)(local_data_list[i].pointer);
+                                        break;
+
+                     case e_string    : delete (std::string*)(local_data_list[i].pointer);
+                                        break;
+
+                     default          : break;
+                  }
+               }
+            }
+
+            if (results)
+            {
+               delete results;
+            }
+         }
+
+         static inline control_block* create(expression_ptr e)
+         {
+            return new control_block(e);
+         }
+
+         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;
+         expression_ptr expr;
+         local_data_list_t local_data_list;
+         results_context_t* results;
+         bool  retinv_null;
+         bool* return_invoked;
+
+         friend class function_compositor<T>;
+      };
+
+   public:
+
+      expression()
+      : control_block_(0)
+      {
+         set_expression(new details::null_node<T>());
+      }
+
+      expression(const expression<T>& e)
+      : control_block_    (e.control_block_    ),
+        symbol_table_list_(e.symbol_table_list_)
+      {
+         control_block_->ref_count++;
+      }
+
+      explicit expression(const symbol_table<T>& symbol_table)
+      : control_block_(0)
+      {
+         set_expression(new details::null_node<T>());
+         symbol_table_list_.push_back(symbol_table);
+      }
+
+      inline expression<T>& operator=(const expression<T>& e)
+      {
+         if (this != &e)
+         {
+            if (control_block_)
+            {
+               if (
+                    (0 !=   control_block_->ref_count) &&
+                    (0 == --control_block_->ref_count)
+                  )
+               {
+                  delete control_block_;
+               }
+
+               control_block_ = 0;
+            }
+
+            control_block_ = e.control_block_;
+            control_block_->ref_count++;
+            symbol_table_list_ = e.symbol_table_list_;
+         }
+
+         return *this;
+      }
+
+      inline bool operator==(const expression<T>& e) const
+      {
+         return (this == &e);
+      }
+
+      inline bool operator!() const
+      {
+         return (
+                  (0 == control_block_      ) ||
+                  (0 == control_block_->expr)
+                );
+      }
+
+      inline expression<T>& release()
+      {
+         control_block::destroy(control_block_);
+
+         return (*this);
+      }
+
+     ~expression()
+      {
+         control_block::destroy(control_block_);
+      }
+
+      inline T value() const
+      {
+         return control_block_->expr->value();
+      }
+
+      inline T operator() () const
+      {
+         return value();
+      }
+
+      inline operator T() const
+      {
+         return value();
+      }
+
+      inline operator bool() const
+      {
+         return details::is_true(value());
+      }
+
+      inline void register_symbol_table(symbol_table<T>& st)
+      {
+         symbol_table_list_.push_back(st);
+      }
+
+      inline const symbol_table<T>& get_symbol_table(const std::size_t& index = 0) const
+      {
+         return symbol_table_list_[index];
+      }
+
+      inline symbol_table<T>& get_symbol_table(const std::size_t& index = 0)
+      {
+         return symbol_table_list_[index];
+      }
+
+      typedef results_context<T> results_context_t;
+
+      inline const results_context_t& results() const
+      {
+         if (control_block_->results)
+            return (*control_block_->results);
+         else
+         {
+            static const results_context_t null_results;
+            return null_results;
+         }
+      }
+
+      inline bool return_invoked() const
+      {
+         return (*control_block_->return_invoked);
+      }
+
+   private:
+
+      inline symtab_list_t get_symbol_table_list() const
+      {
+         return symbol_table_list_;
+      }
+
+      inline void set_expression(const expression_ptr expr)
+      {
+         if (expr)
+         {
+            if (control_block_)
+            {
+               if (0 == --control_block_->ref_count)
+               {
+                  delete control_block_;
+               }
+            }
+
+            control_block_ = control_block::create(expr);
+         }
+      }
+
+      inline void register_local_var(expression_ptr expr)
+      {
+         if (expr)
+         {
+            if (control_block_)
+            {
+               control_block_->
+                  local_data_list.push_back(
+                     typename expression<T>::control_block::
+                        data_pack(reinterpret_cast<void*>(expr),
+                                  control_block::e_expr));
+            }
+         }
+      }
+
+      inline void register_local_var(vector_holder_ptr vec_holder)
+      {
+         if (vec_holder)
+         {
+            if (control_block_)
+            {
+               control_block_->
+                  local_data_list.push_back(
+                     typename expression<T>::control_block::
+                        data_pack(reinterpret_cast<void*>(vec_holder),
+                                  control_block::e_vecholder));
+            }
+         }
+      }
+
+      inline void register_local_data(void* data, const std::size_t& size = 0, const std::size_t data_mode = 0)
+      {
+         if (data)
+         {
+            if (control_block_)
+            {
+               typename control_block::data_type dt = control_block::e_data;
+
+               switch (data_mode)
+               {
+                  case 0 : dt = control_block::e_data;    break;
+                  case 1 : dt = control_block::e_vecdata; break;
+                  case 2 : dt = control_block::e_string;  break;
+               }
+
+               control_block_->
+                  local_data_list.push_back(
+                     typename expression<T>::control_block::
+                        data_pack(reinterpret_cast<void*>(data), dt, size));
+            }
+         }
+      }
+
+      inline const typename control_block::local_data_list_t& local_data_list()
+      {
+         if (control_block_)
+         {
+            return control_block_->local_data_list;
+         }
+         else
+         {
+            static typename control_block::local_data_list_t null_local_data_list;
+            return null_local_data_list;
+         }
+      }
+
+      inline void register_return_results(results_context_t* rc)
+      {
+         if (control_block_ && rc)
+         {
+            control_block_->results = rc;
+         }
+      }
+
+      inline void set_retinvk(bool* retinvk_ptr)
+      {
+         if (control_block_)
+         {
+            control_block_->return_invoked = retinvk_ptr;
+         }
+      }
+
+      control_block* control_block_;
+      symtab_list_t  symbol_table_list_;
+
+      friend class parser<T>;
+      friend class expression_helper<T>;
+      friend class function_compositor<T>;
+   };
+
+   template <typename T>
+   class expression_helper
+   {
+   public:
+
+      static inline bool is_constant(const expression<T>& expr)
+      {
+         return details::is_constant_node(expr.control_block_->expr);
+      }
+
+      static inline bool is_variable(const expression<T>& expr)
+      {
+         return details::is_variable_node(expr.control_block_->expr);
+      }
+
+      static inline bool is_unary(const expression<T>& expr)
+      {
+         return details::is_unary_node(expr.control_block_->expr);
+      }
+
+      static inline bool is_binary(const expression<T>& expr)
+      {
+         return details::is_binary_node(expr.control_block_->expr);
+      }
+
+      static inline bool is_function(const expression<T>& expr)
+      {
+         return details::is_function(expr.control_block_->expr);
+      }
+
+      static inline bool is_null(const expression<T>& expr)
+      {
+         return details::is_null_node(expr.control_block_->expr);
+      }
+   };
+
+   template <typename T>
+   inline bool is_valid(const expression<T>& expr)
+   {
+      return !expression_helper<T>::is_null(expr);
+   }
+
+   namespace parser_error
+   {
+      enum error_mode
+      {
+         e_unknown = 0,
+         e_syntax  = 1,
+         e_token   = 2,
+         e_numeric = 4,
+         e_symtab  = 5,
+         e_lexer   = 6,
+         e_helper  = 7
+      };
+
+      struct type
+      {
+         type()
+         : mode(parser_error::e_unknown),
+           line_no  (0),
+           column_no(0)
+         {}
+
+         lexer::token token;
+         error_mode mode;
+         std::string diagnostic;
+         std::string src_location;
+         std::string error_line;
+         std::size_t line_no;
+         std::size_t column_no;
+      };
+
+      inline type make_error(const error_mode mode,
+                             const std::string& diagnostic   = "",
+                             const std::string& src_location = "")
+      {
+         type t;
+         t.mode         = mode;
+         t.token.type   = lexer::token::e_error;
+         t.diagnostic   = diagnostic;
+         t.src_location = src_location;
+         exprtk_debug(("%s\n",diagnostic .c_str()));
+         return t;
+      }
+
+      inline type make_error(const error_mode mode,
+                             const lexer::token& tk,
+                             const std::string& diagnostic   = "",
+                             const std::string& src_location = "")
+      {
+         type t;
+         t.mode       = mode;
+         t.token      = tk;
+         t.diagnostic = diagnostic;
+         t.src_location = src_location;
+         exprtk_debug(("%s\n",diagnostic .c_str()));
+         return t;
+      }
+
+      inline std::string to_str(error_mode mode)
+      {
+         switch (mode)
+         {
+            case e_unknown : return std::string("Unknown Error");
+            case e_syntax  : return std::string("Syntax Error" );
+            case e_token   : return std::string("Token Error"  );
+            case e_numeric : return std::string("Numeric Error");
+            case e_symtab  : return std::string("Symbol Error" );
+            case e_lexer   : return std::string("Lexer Error"  );
+            case e_helper  : return std::string("Helper Error" );
+            default        : return std::string("Unknown Error");
+         }
+      }
+
+      inline bool update_error(type& error, const std::string& expression)
+      {
+         if (
+              expression.empty()                         ||
+              (error.token.position > expression.size()) ||
+              (std::numeric_limits<std::size_t>::max() == error.token.position)
+            )
+         {
+            return false;
+         }
+
+         std::size_t error_line_start = 0;
+
+         for (std::size_t i = error.token.position; i > 0; --i)
+         {
+            const details::char_t c = expression[i];
+
+            if (('\n' == c) || ('\r' == c))
+            {
+               error_line_start = i + 1;
+               break;
+            }
+         }
+
+         std::size_t next_nl_position = std::min(expression.size(),
+                                                 expression.find_first_of('\n',error.token.position + 1));
+
+         error.column_no  = error.token.position - error_line_start;
+         error.error_line = expression.substr(error_line_start,
+                                              next_nl_position - error_line_start);
+
+         error.line_no = 0;
+
+         for (std::size_t i = 0; i < next_nl_position; ++i)
+         {
+            if ('\n' == expression[i])
+               ++error.line_no;
+         }
+
+         return true;
+      }
+
+      inline void dump_error(const type& error)
+      {
+         printf("Position: %02d   Type: [%s]   Msg: %s\n",
+                static_cast<int>(error.token.position),
+                exprtk::parser_error::to_str(error.mode).c_str(),
+                error.diagnostic.c_str());
+      }
+   }
+
+   namespace details
+   {
+      template <typename Parser>
+      inline void disable_type_checking(Parser& p)
+      {
+         p.state_.type_check_enabled = false;
+      }
+   }
+
+   template <typename T>
+   class parser : public lexer::parser_helper
+   {
+   private:
+
+      enum precedence_level
+      {
+         e_level00,
+         e_level01,
+         e_level02,
+         e_level03,
+         e_level04,
+         e_level05,
+         e_level06,
+         e_level07,
+         e_level08,
+         e_level09,
+         e_level10,
+         e_level11,
+         e_level12,
+         e_level13,
+         e_level14
+      };
+
+      typedef const T&                                               cref_t;
+      typedef const T                                               const_t;
+      typedef ifunction                <T>                                F;
+      typedef ivararg_function         <T>                              VAF;
+      typedef igeneric_function        <T>                               GF;
+      typedef ifunction                <T>                      ifunction_t;
+      typedef ivararg_function         <T>               ivararg_function_t;
+      typedef igeneric_function        <T>              igeneric_function_t;
+      typedef details::expression_node <T>                expression_node_t;
+      typedef details::literal_node    <T>                   literal_node_t;
+      typedef details::unary_node      <T>                     unary_node_t;
+      typedef details::binary_node     <T>                    binary_node_t;
+      typedef details::trinary_node    <T>                   trinary_node_t;
+      typedef details::quaternary_node <T>                quaternary_node_t;
+      typedef details::conditional_node<T>               conditional_node_t;
+      typedef details::cons_conditional_node<T>     cons_conditional_node_t;
+      typedef details::while_loop_node <T>                while_loop_node_t;
+      typedef details::repeat_until_loop_node<T>   repeat_until_loop_node_t;
+      typedef details::for_loop_node   <T>                  for_loop_node_t;
+      #ifndef exprtk_disable_break_continue
+      typedef details::while_loop_bc_node <T>          while_loop_bc_node_t;
+      typedef details::repeat_until_loop_bc_node<T> repeat_until_loop_bc_node_t;
+      typedef details::for_loop_bc_node<T>               for_loop_bc_node_t;
+      #endif
+      typedef details::switch_node     <T>                    switch_node_t;
+      typedef details::variable_node   <T>                  variable_node_t;
+      typedef details::vector_elem_node<T>               vector_elem_node_t;
+      typedef details::rebasevector_elem_node<T>   rebasevector_elem_node_t;
+      typedef details::rebasevector_celem_node<T> rebasevector_celem_node_t;
+      typedef details::vector_node     <T>                    vector_node_t;
+      typedef details::range_pack      <T>                          range_t;
+      #ifndef exprtk_disable_string_capabilities
+      typedef details::stringvar_node     <T>              stringvar_node_t;
+      typedef details::string_literal_node<T>         string_literal_node_t;
+      typedef details::string_range_node  <T>           string_range_node_t;
+      typedef details::const_string_range_node<T> const_string_range_node_t;
+      typedef details::generic_string_range_node<T> generic_string_range_node_t;
+      typedef details::string_concat_node <T>          string_concat_node_t;
+      typedef details::assignment_string_node<T>   assignment_string_node_t;
+      typedef details::assignment_string_range_node<T> assignment_string_range_node_t;
+      typedef details::conditional_string_node<T>  conditional_string_node_t;
+      typedef details::cons_conditional_str_node<T> cons_conditional_str_node_t;
+      #endif
+      typedef details::assignment_node<T>                 assignment_node_t;
+      typedef details::assignment_vec_elem_node       <T> assignment_vec_elem_node_t;
+      typedef details::assignment_rebasevec_elem_node <T> assignment_rebasevec_elem_node_t;
+      typedef details::assignment_rebasevec_celem_node<T> assignment_rebasevec_celem_node_t;
+      typedef details::assignment_vec_node     <T>    assignment_vec_node_t;
+      typedef details::assignment_vecvec_node  <T> assignment_vecvec_node_t;
+      typedef details::scand_node<T>                           scand_node_t;
+      typedef details::scor_node<T>                             scor_node_t;
+      typedef lexer::token                                          token_t;
+      typedef expression_node_t*                        expression_node_ptr;
+      typedef expression<T>                                    expression_t;
+      typedef symbol_table<T>                                symbol_table_t;
+      typedef typename expression<T>::symtab_list_t     symbol_table_list_t;
+      typedef details::vector_holder<T>*                  vector_holder_ptr;
+
+      typedef typename details::functor_t<T>            functor_t;
+      typedef typename functor_t::qfunc_t    quaternary_functor_t;
+      typedef typename functor_t::tfunc_t       trinary_functor_t;
+      typedef typename functor_t::bfunc_t        binary_functor_t;
+      typedef typename functor_t::ufunc_t         unary_functor_t;
+
+      typedef details::operator_type operator_t;
+
+      typedef std::map<operator_t,  unary_functor_t>   unary_op_map_t;
+      typedef std::map<operator_t, binary_functor_t>  binary_op_map_t;
+      typedef std::map<operator_t,trinary_functor_t> trinary_op_map_t;
+
+      typedef std::map<std::string,std::pair<trinary_functor_t   ,operator_t> > sf3_map_t;
+      typedef std::map<std::string,std::pair<quaternary_functor_t,operator_t> > sf4_map_t;
+
+      typedef std::map<binary_functor_t,operator_t> inv_binary_op_map_t;
+      typedef std::multimap<std::string,details::base_operation_t,details::ilesscompare> base_ops_map_t;
+      typedef std::set<std::string,details::ilesscompare> disabled_func_set_t;
+
+      typedef details::T0oT1_define<T,  cref_t,  cref_t> vov_t;
+      typedef details::T0oT1_define<T, const_t,  cref_t> cov_t;
+      typedef details::T0oT1_define<T,  cref_t, const_t> voc_t;
+
+      typedef details::T0oT1oT2_define<T,  cref_t,  cref_t,  cref_t> vovov_t;
+      typedef details::T0oT1oT2_define<T,  cref_t,  cref_t, const_t> vovoc_t;
+      typedef details::T0oT1oT2_define<T,  cref_t, const_t,  cref_t> vocov_t;
+      typedef details::T0oT1oT2_define<T, const_t,  cref_t,  cref_t> covov_t;
+      typedef details::T0oT1oT2_define<T, const_t,  cref_t, const_t> covoc_t;
+      typedef details::T0oT1oT2_define<T, const_t, const_t,  cref_t> cocov_t;
+      typedef details::T0oT1oT2_define<T,  cref_t, const_t, const_t> vococ_t;
+
+      typedef details::T0oT1oT2oT3_define<T,  cref_t,  cref_t,  cref_t,  cref_t> vovovov_t;
+      typedef details::T0oT1oT2oT3_define<T,  cref_t,  cref_t,  cref_t, const_t> vovovoc_t;
+      typedef details::T0oT1oT2oT3_define<T,  cref_t,  cref_t, const_t,  cref_t> vovocov_t;
+      typedef details::T0oT1oT2oT3_define<T,  cref_t, const_t,  cref_t,  cref_t> vocovov_t;
+      typedef details::T0oT1oT2oT3_define<T, const_t,  cref_t,  cref_t,  cref_t> covovov_t;
+
+      typedef details::T0oT1oT2oT3_define<T, const_t,  cref_t, const_t,  cref_t> covocov_t;
+      typedef details::T0oT1oT2oT3_define<T,  cref_t, const_t,  cref_t, const_t> vocovoc_t;
+      typedef details::T0oT1oT2oT3_define<T, const_t,  cref_t,  cref_t, const_t> covovoc_t;
+      typedef details::T0oT1oT2oT3_define<T,  cref_t, const_t, const_t,  cref_t> vococov_t;
+
+      typedef results_context<T> results_context_t;
+
+      typedef parser_helper prsrhlpr_t;
+
+      struct scope_element
+      {
+         enum element_type
+         {
+            e_none    ,
+            e_variable,
+            e_vector  ,
+            e_vecelem ,
+            e_string
+         };
+
+         typedef details::vector_holder<T> vector_holder_t;
+         typedef variable_node_t*        variable_node_ptr;
+         typedef vector_holder_t*        vector_holder_ptr;
+         typedef expression_node_t*    expression_node_ptr;
+         #ifndef exprtk_disable_string_capabilities
+         typedef stringvar_node_t*      stringvar_node_ptr;
+         #endif
+
+         scope_element()
+         : name("???"),
+           size (std::numeric_limits<std::size_t>::max()),
+           index(std::numeric_limits<std::size_t>::max()),
+           depth(std::numeric_limits<std::size_t>::max()),
+           ref_count(0),
+           ip_index (0),
+           type (e_none),
+           active(false),
+           data    (0),
+           var_node(0),
+           vec_node(0)
+           #ifndef exprtk_disable_string_capabilities
+           ,str_node(0)
+           #endif
+         {}
+
+         bool operator < (const scope_element& se) const
+         {
+            if (ip_index < se.ip_index)
+               return true;
+            else if (ip_index > se.ip_index)
+               return false;
+            else if (depth < se.depth)
+               return true;
+            else if (depth > se.depth)
+               return false;
+            else if (index < se.index)
+               return true;
+            else if (index > se.index)
+               return false;
+            else
+               return (name < se.name);
+         }
+
+         void clear()
+         {
+            name   = "???";
+            size   = std::numeric_limits<std::size_t>::max();
+            index  = std::numeric_limits<std::size_t>::max();
+            depth  = std::numeric_limits<std::size_t>::max();
+            type   = e_none;
+            active = false;
+            ref_count = 0;
+            ip_index  = 0;
+            data      = 0;
+            var_node  = 0;
+            vec_node  = 0;
+            #ifndef exprtk_disable_string_capabilities
+            str_node  = 0;
+            #endif
+         }
+
+         std::string  name;
+         std::size_t  size;
+         std::size_t  index;
+         std::size_t  depth;
+         std::size_t  ref_count;
+         std::size_t  ip_index;
+         element_type type;
+         bool         active;
+         void*        data;
+         expression_node_ptr var_node;
+         vector_holder_ptr   vec_node;
+         #ifndef exprtk_disable_string_capabilities
+         stringvar_node_ptr str_node;
+         #endif
+      };
+
+      class scope_element_manager
+      {
+      public:
+
+         typedef expression_node_t* expression_node_ptr;
+         typedef variable_node_t*     variable_node_ptr;
+         typedef parser<T>                     parser_t;
+
+         explicit scope_element_manager(parser<T>& p)
+         : parser_(p),
+           input_param_cnt_(0)
+         {}
+
+         inline std::size_t size() const
+         {
+            return element_.size();
+         }
+
+         inline bool empty() const
+         {
+            return element_.empty();
+         }
+
+         inline scope_element& get_element(const std::size_t& index)
+         {
+            if (index < element_.size())
+               return element_[index];
+            else
+               return null_element_;
+         }
+
+         inline scope_element& get_element(const std::string& var_name,
+                                           const std::size_t index = std::numeric_limits<std::size_t>::max())
+         {
+            const std::size_t current_depth = parser_.state_.scope_depth;
+
+            for (std::size_t i = 0; i < element_.size(); ++i)
+            {
+               scope_element& se = element_[i];
+
+               if (se.depth > current_depth)
+                  continue;
+               else if (
+                         details::imatch(se.name, var_name) &&
+                         (se.index == index)
+                       )
+                  return se;
+            }
+
+            return null_element_;
+         }
+
+         inline scope_element& get_active_element(const std::string& var_name,
+                                                  const std::size_t index = std::numeric_limits<std::size_t>::max())
+         {
+            const std::size_t current_depth = parser_.state_.scope_depth;
+
+            for (std::size_t i = 0; i < element_.size(); ++i)
+            {
+               scope_element& se = element_[i];
+
+               if (se.depth > current_depth)
+                  continue;
+               else if (
+                         details::imatch(se.name, var_name) &&
+                         (se.index == index)                &&
+                         (se.active)
+                       )
+                  return se;
+            }
+
+            return null_element_;
+         }
+
+         inline bool add_element(const scope_element& se)
+         {
+            for (std::size_t i = 0; i < element_.size(); ++i)
+            {
+               scope_element& cse = element_[i];
+
+               if (
+                    details::imatch(cse.name, se.name) &&
+                    (cse.depth <= se.depth)            &&
+                    (cse.index == se.index)            &&
+                    (cse.size  == se.size )            &&
+                    (cse.type  == se.type )            &&
+                    (cse.active)
+                  )
+                  return false;
+            }
+
+            element_.push_back(se);
+            std::sort(element_.begin(),element_.end());
+
+            return true;
+         }
+
+         inline void deactivate(const std::size_t& scope_depth)
+         {
+            exprtk_debug(("deactivate() - Scope depth: %d\n",
+                          static_cast<int>(parser_.state_.scope_depth)));
+
+            for (std::size_t i = 0; i < element_.size(); ++i)
+            {
+               scope_element& se = element_[i];
+
+               if (se.active && (se.depth >= scope_depth))
+               {
+                  exprtk_debug(("deactivate() - element[%02d] '%s'\n",
+                                static_cast<int>(i),
+                                se.name.c_str()));
+
+                  se.active = false;
+               }
+            }
+         }
+
+         inline void free_element(scope_element& se)
+         {
+            #ifdef exprtk_enable_debugging
+            exprtk_debug(("free_element() - se[%s]\n", se.name.c_str()));
+            #endif
+
+            switch (se.type)
+            {
+               case scope_element::e_variable   : if (se.data    ) delete (T*) se.data;
+                                                  if (se.var_node) delete se.var_node;
+                                                  break;
+
+               case scope_element::e_vector     : if (se.data    ) delete[] (T*) se.data;
+                                                  if (se.vec_node) delete se.vec_node;
+                                                  break;
+
+               case scope_element::e_vecelem    : if (se.var_node) delete se.var_node;
+                                                  break;
+
+               #ifndef exprtk_disable_string_capabilities
+               case scope_element::e_string     : if (se.data    ) delete (std::string*) se.data;
+                                                  if (se.str_node) delete se.str_node;
+                                                  break;
+               #endif
+
+               default                          : return;
+            }
+
+            se.clear();
+         }
+
+         inline void cleanup()
+         {
+            for (std::size_t i = 0; i < element_.size(); ++i)
+            {
+               free_element(element_[i]);
+            }
+
+            element_.clear();
+
+            input_param_cnt_ = 0;
+         }
+
+         inline std::size_t next_ip_index()
+         {
+            return ++input_param_cnt_;
+         }
+
+         inline expression_node_ptr get_variable(const T& v)
+         {
+            for (std::size_t i = 0; i < element_.size(); ++i)
+            {
+               scope_element& se = element_[i];
+
+               if (
+                    se.active   &&
+                    se.var_node &&
+                    details::is_variable_node(se.var_node)
+                  )
+               {
+                  variable_node_ptr vn = reinterpret_cast<variable_node_ptr>(se.var_node);
+
+                  if (&(vn->ref()) == (&v))
+                  {
+                     return se.var_node;
+                  }
+               }
+            }
+
+            return expression_node_ptr(0);
+         }
+
+      private:
+
+         scope_element_manager& operator=(const scope_element_manager&);
+
+         parser_t& parser_;
+         std::vector<scope_element> element_;
+         scope_element null_element_;
+         std::size_t input_param_cnt_;
+      };
+
+      class scope_handler
+      {
+      public:
+
+         typedef parser<T> parser_t;
+
+         explicit scope_handler(parser<T>& p)
+         : parser_(p)
+         {
+            parser_.state_.scope_depth++;
+            #ifdef exprtk_enable_debugging
+            const std::string depth(2 * parser_.state_.scope_depth,'-');
+            exprtk_debug(("%s> Scope Depth: %02d\n",
+                          depth.c_str(),
+                          static_cast<int>(parser_.state_.scope_depth)));
+            #endif
+         }
+
+        ~scope_handler()
+         {
+            parser_.sem_.deactivate(parser_.state_.scope_depth);
+            parser_.state_.scope_depth--;
+            #ifdef exprtk_enable_debugging
+            const std::string depth(2 * parser_.state_.scope_depth,'-');
+            exprtk_debug(("<%s Scope Depth: %02d\n",
+                          depth.c_str(),
+                          static_cast<int>(parser_.state_.scope_depth)));
+            #endif
+         }
+
+      private:
+
+         scope_handler& operator=(const scope_handler&);
+
+         parser_t& parser_;
+      };
+
+      struct symtab_store
+      {
+         symbol_table_list_t symtab_list_;
+
+         typedef typename symbol_table_t::local_data_t   local_data_t;
+         typedef typename symbol_table_t::variable_ptr   variable_ptr;
+         typedef typename symbol_table_t::function_ptr   function_ptr;
+         #ifndef exprtk_disable_string_capabilities
+         typedef typename symbol_table_t::stringvar_ptr stringvar_ptr;
+         #endif
+         typedef typename symbol_table_t::vector_holder_ptr       vector_holder_ptr;
+         typedef typename symbol_table_t::vararg_function_ptr   vararg_function_ptr;
+         typedef typename symbol_table_t::generic_function_ptr generic_function_ptr;
+
+         inline bool empty() const
+         {
+            return symtab_list_.empty();
+         }
+
+         inline void clear()
+         {
+            symtab_list_.clear();
+         }
+
+         inline bool valid() const
+         {
+            if (!empty())
+            {
+               for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+               {
+                  if (symtab_list_[i].valid())
+                     return true;
+               }
+            }
+
+            return false;
+         }
+
+         inline bool valid_symbol(const std::string& symbol) const
+         {
+            if (!symtab_list_.empty())
+               return symtab_list_[0].valid_symbol(symbol);
+            else
+               return false;
+         }
+
+         inline bool valid_function_name(const std::string& symbol) const
+         {
+            if (!symtab_list_.empty())
+               return symtab_list_[0].valid_function(symbol);
+            else
+               return false;
+         }
+
+         inline variable_ptr get_variable(const std::string& variable_name) const
+         {
+            if (!valid_symbol(variable_name))
+               return reinterpret_cast<variable_ptr>(0);
+
+            variable_ptr result = reinterpret_cast<variable_ptr>(0);
+
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else
+                  result = local_data(i)
+                              .variable_store.get(variable_name);
+
+               if (result) break;
+            }
+
+            return result;
+         }
+
+         inline variable_ptr get_variable(const T& var_ref) const
+         {
+            variable_ptr result = reinterpret_cast<variable_ptr>(0);
+
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else
+                  result = local_data(i).variable_store
+                              .get_from_varptr(reinterpret_cast<const void*>(&var_ref));
+
+               if (result) break;
+            }
+
+            return result;
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         inline stringvar_ptr get_stringvar(const std::string& string_name) const
+         {
+            if (!valid_symbol(string_name))
+               return reinterpret_cast<stringvar_ptr>(0);
+
+            stringvar_ptr result = reinterpret_cast<stringvar_ptr>(0);
+
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else
+                  result = local_data(i)
+                              .stringvar_store.get(string_name);
+
+               if (result) break;
+            }
+
+            return result;
+         }
+         #endif
+
+         inline function_ptr get_function(const std::string& function_name) const
+         {
+            if (!valid_function_name(function_name))
+               return reinterpret_cast<function_ptr>(0);
+
+            function_ptr result = reinterpret_cast<function_ptr>(0);
+
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else
+                  result = local_data(i)
+                              .function_store.get(function_name);
+
+               if (result) break;
+            }
+
+            return result;
+         }
+
+         inline vararg_function_ptr get_vararg_function(const std::string& vararg_function_name) const
+         {
+            if (!valid_function_name(vararg_function_name))
+               return reinterpret_cast<vararg_function_ptr>(0);
+
+            vararg_function_ptr result = reinterpret_cast<vararg_function_ptr>(0);
+
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else
+                  result = local_data(i)
+                              .vararg_function_store.get(vararg_function_name);
+
+               if (result) break;
+            }
+
+            return result;
+         }
+
+         inline generic_function_ptr get_generic_function(const std::string& function_name) const
+         {
+            if (!valid_function_name(function_name))
+               return reinterpret_cast<generic_function_ptr>(0);
+
+            generic_function_ptr result = reinterpret_cast<generic_function_ptr>(0);
+
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else
+                  result = local_data(i)
+                              .generic_function_store.get(function_name);
+
+               if (result) break;
+            }
+
+            return result;
+         }
+
+         inline generic_function_ptr get_string_function(const std::string& function_name) const
+         {
+            if (!valid_function_name(function_name))
+               return reinterpret_cast<generic_function_ptr>(0);
+
+            generic_function_ptr result = reinterpret_cast<generic_function_ptr>(0);
+
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else
+                  result =
+                     local_data(i).string_function_store.get(function_name);
+
+               if (result) break;
+            }
+
+            return result;
+         }
+
+         inline generic_function_ptr get_overload_function(const std::string& function_name) const
+         {
+            if (!valid_function_name(function_name))
+               return reinterpret_cast<generic_function_ptr>(0);
+
+            generic_function_ptr result = reinterpret_cast<generic_function_ptr>(0);
+
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else
+                  result =
+                     local_data(i).overload_function_store.get(function_name);
+
+               if (result) break;
+            }
+
+            return result;
+         }
+
+         inline vector_holder_ptr get_vector(const std::string& vector_name) const
+         {
+            if (!valid_symbol(vector_name))
+               return reinterpret_cast<vector_holder_ptr>(0);
+
+            vector_holder_ptr result = reinterpret_cast<vector_holder_ptr>(0);
+
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else
+                  result =
+                     local_data(i).vector_store.get(vector_name);
+
+               if (result) break;
+            }
+
+            return result;
+         }
+
+         inline bool is_constant_node(const std::string& symbol_name) const
+         {
+            if (!valid_symbol(symbol_name))
+               return false;
+
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else if (local_data(i).variable_store.is_constant(symbol_name))
+                  return true;
+            }
+
+            return false;
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         inline bool is_constant_string(const std::string& symbol_name) const
+         {
+            if (!valid_symbol(symbol_name))
+               return false;
+
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else if (!local_data(i).stringvar_store.symbol_exists(symbol_name))
+                  continue;
+               else if ( local_data(i).stringvar_store.is_constant(symbol_name))
+                  return true;
+            }
+
+            return false;
+         }
+         #endif
+
+         inline bool symbol_exists(const std::string& symbol) const
+         {
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else if (symtab_list_[i].symbol_exists(symbol))
+                  return true;
+            }
+
+            return false;
+         }
+
+         inline bool is_variable(const std::string& variable_name) const
+         {
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else if (
+                         symtab_list_[i].local_data().variable_store
+                           .symbol_exists(variable_name)
+                       )
+                  return true;
+            }
+
+            return false;
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         inline bool is_stringvar(const std::string& stringvar_name) const
+         {
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else if (
+                         symtab_list_[i].local_data().stringvar_store
+                           .symbol_exists(stringvar_name)
+                       )
+                  return true;
+            }
+
+            return false;
+         }
+
+         inline bool is_conststr_stringvar(const std::string& symbol_name) const
+         {
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else if (
+                         symtab_list_[i].local_data().stringvar_store
+                           .symbol_exists(symbol_name)
+                       )
+               {
+                  return (
+                           local_data(i).stringvar_store.symbol_exists(symbol_name) ||
+                           local_data(i).stringvar_store.is_constant  (symbol_name)
+                         );
+
+               }
+            }
+
+            return false;
+         }
+         #endif
+
+         inline bool is_function(const std::string& function_name) const
+         {
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else if (
+                         local_data(i).vararg_function_store
+                           .symbol_exists(function_name)
+                       )
+                  return true;
+            }
+
+            return false;
+         }
+
+         inline bool is_vararg_function(const std::string& vararg_function_name) const
+         {
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else if (
+                         local_data(i).vararg_function_store
+                           .symbol_exists(vararg_function_name)
+                       )
+                  return true;
+            }
+
+            return false;
+         }
+
+         inline bool is_vector(const std::string& vector_name) const
+         {
+            for (std::size_t i = 0; i < symtab_list_.size(); ++i)
+            {
+               if (!symtab_list_[i].valid())
+                  continue;
+               else if (
+                         local_data(i).vector_store
+                           .symbol_exists(vector_name)
+                       )
+                  return true;
+            }
+
+            return false;
+         }
+
+         inline std::string get_variable_name(const expression_node_ptr& ptr) const
+         {
+            return local_data().variable_store.entity_name(ptr);
+         }
+
+         inline std::string get_vector_name(const vector_holder_ptr& ptr) const
+         {
+            return local_data().vector_store.entity_name(ptr);
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         inline std::string get_stringvar_name(const expression_node_ptr& ptr) const
+         {
+            return local_data().stringvar_store.entity_name(ptr);
+         }
+
+         inline std::string get_conststr_stringvar_name(const expression_node_ptr& ptr) const
+         {
+            return local_data().stringvar_store.entity_name(ptr);
+         }
+         #endif
+
+         inline local_data_t& local_data(const std::size_t& index = 0)
+         {
+            return symtab_list_[index].local_data();
+         }
+
+         inline const local_data_t& local_data(const std::size_t& index = 0) const
+         {
+            return symtab_list_[index].local_data();
+         }
+
+         inline symbol_table_t& get_symbol_table(const std::size_t& index = 0)
+         {
+            return symtab_list_[index];
+         }
+      };
+
+      struct parser_state
+      {
+         parser_state()
+         : type_check_enabled(true)
+         {
+            reset();
+         }
+
+         void reset()
+         {
+            parsing_return_stmt = false;
+            parsing_break_stmt  = false;
+            return_stmt_present = false;
+            side_effect_present = false;
+            scope_depth         = 0;
+         }
+
+         #ifndef exprtk_enable_debugging
+         void activate_side_effect(const std::string&)
+         #else
+         void activate_side_effect(const std::string& source)
+         #endif
+         {
+            if (!side_effect_present)
+            {
+               side_effect_present = true;
+
+               exprtk_debug(("activate_side_effect() - caller: %s\n",source.c_str()));
+            }
+         }
+
+         bool parsing_return_stmt;
+         bool parsing_break_stmt;
+         bool return_stmt_present;
+         bool side_effect_present;
+         bool type_check_enabled;
+         std::size_t scope_depth;
+      };
+
+   public:
+
+      struct unknown_symbol_resolver
+      {
+
+         enum usr_symbol_type
+         {
+            e_usr_unknown_type  = 0,
+            e_usr_variable_type = 1,
+            e_usr_constant_type = 2
+         };
+
+         enum usr_mode
+         {
+            e_usrmode_default  = 0,
+            e_usrmode_extended = 1
+         };
+
+         usr_mode mode;
+
+         unknown_symbol_resolver(const usr_mode m = e_usrmode_default)
+         : mode(m)
+         {}
+
+         virtual ~unknown_symbol_resolver()
+         {}
+
+         virtual bool process(const std::string& /*unknown_symbol*/,
+                              usr_symbol_type&   st,
+                              T&                 default_value,
+                              std::string&       error_message)
+         {
+            if (e_usrmode_default != mode)
+               return false;
+
+            st = e_usr_variable_type;
+            default_value = T(0);
+            error_message.clear();
+
+            return true;
+         }
+
+         virtual bool process(const std::string& /* unknown_symbol */,
+                              symbol_table_t&    /* symbol_table   */,
+                              std::string&       /* error_message  */)
+         {
+            return false;
+         }
+      };
+
+      enum collect_type
+      {
+         e_ct_none        = 0,
+         e_ct_variables   = 1,
+         e_ct_functions   = 2,
+         e_ct_assignments = 4
+      };
+
+      enum symbol_type
+      {
+         e_st_unknown        = 0,
+         e_st_variable       = 1,
+         e_st_vector         = 2,
+         e_st_vecelem        = 3,
+         e_st_string         = 4,
+         e_st_function       = 5,
+         e_st_local_variable = 6,
+         e_st_local_vector   = 7,
+         e_st_local_string   = 8
+      };
+
+      class dependent_entity_collector
+      {
+      public:
+
+         typedef std::pair<std::string,symbol_type> symbol_t;
+         typedef std::vector<symbol_t> symbol_list_t;
+
+         dependent_entity_collector(const std::size_t options = e_ct_none)
+         : options_(options),
+           collect_variables_  ((options_ & e_ct_variables  ) == e_ct_variables  ),
+           collect_functions_  ((options_ & e_ct_functions  ) == e_ct_functions  ),
+           collect_assignments_((options_ & e_ct_assignments) == e_ct_assignments),
+           return_present_   (false),
+           final_stmt_return_(false)
+         {}
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline std::size_t symbols(Sequence<symbol_t,Allocator>& symbols_list)
+         {
+            if (!collect_variables_ && !collect_functions_)
+               return 0;
+            else if (symbol_name_list_.empty())
+               return 0;
+
+            for (std::size_t i = 0; i < symbol_name_list_.size(); ++i)
+            {
+               details::case_normalise(symbol_name_list_[i].first);
+            }
+
+            std::sort(symbol_name_list_.begin(),symbol_name_list_.end());
+
+            std::unique_copy(symbol_name_list_.begin(),
+                             symbol_name_list_.end  (),
+                             std::back_inserter(symbols_list));
+
+            return symbols_list.size();
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline std::size_t assignment_symbols(Sequence<symbol_t,Allocator>& assignment_list)
+         {
+            if (!collect_assignments_)
+               return 0;
+            else if (assignment_name_list_.empty())
+               return 0;
+
+            for (std::size_t i = 0; i < assignment_name_list_.size(); ++i)
+            {
+               details::case_normalise(assignment_name_list_[i].first);
+            }
+
+            std::sort(assignment_name_list_.begin(),assignment_name_list_.end());
+
+            std::unique_copy(assignment_name_list_.begin(),
+                             assignment_name_list_.end  (),
+                             std::back_inserter(assignment_list));
+
+            return assignment_list.size();
+         }
+
+         void clear()
+         {
+            symbol_name_list_    .clear();
+            assignment_name_list_.clear();
+            retparam_list_       .clear();
+            return_present_    = false;
+            final_stmt_return_ = false;
+         }
+
+         bool& collect_variables()
+         {
+            return collect_variables_;
+         }
+
+         bool& collect_functions()
+         {
+            return collect_functions_;
+         }
+
+         bool& collect_assignments()
+         {
+            return collect_assignments_;
+         }
+
+         bool return_present() const
+         {
+            return return_present_;
+         }
+
+         bool final_stmt_return() const
+         {
+            return final_stmt_return_;
+         }
+
+         typedef std::vector<std::string> retparam_list_t;
+
+         retparam_list_t return_param_type_list() const
+         {
+            return retparam_list_;
+         }
+
+      private:
+
+         inline void add_symbol(const std::string& symbol, const symbol_type st)
+         {
+            switch (st)
+            {
+               case e_st_variable       :
+               case e_st_vector         :
+               case e_st_string         :
+               case e_st_local_variable :
+               case e_st_local_vector   :
+               case e_st_local_string   : if (collect_variables_)
+                                             symbol_name_list_
+                                                .push_back(std::make_pair(symbol, st));
+                                          break;
+
+               case e_st_function       : if (collect_functions_)
+                                             symbol_name_list_
+                                                .push_back(std::make_pair(symbol, st));
+                                          break;
+
+               default                  : return;
+            }
+         }
+
+         inline void add_assignment(const std::string& symbol, const symbol_type st)
+         {
+            switch (st)
+            {
+               case e_st_variable       :
+               case e_st_vector         :
+               case e_st_string         : if (collect_assignments_)
+                                             assignment_name_list_
+                                                .push_back(std::make_pair(symbol, st));
+                                          break;
+
+               default                  : return;
+            }
+         }
+
+         std::size_t options_;
+         bool collect_variables_;
+         bool collect_functions_;
+         bool collect_assignments_;
+         bool return_present_;
+         bool final_stmt_return_;
+         symbol_list_t symbol_name_list_;
+         symbol_list_t assignment_name_list_;
+         retparam_list_t retparam_list_;
+
+         friend class parser<T>;
+      };
+
+      class settings_store
+      {
+      private:
+
+         typedef std::set<std::string,details::ilesscompare> disabled_entity_set_t;
+         typedef disabled_entity_set_t::iterator des_itr_t;
+
+      public:
+
+         enum settings_compilation_options
+         {
+            e_unknown              =    0,
+            e_replacer             =    1,
+            e_joiner               =    2,
+            e_numeric_check        =    4,
+            e_bracket_check        =    8,
+            e_sequence_check       =   16,
+            e_commutative_check    =   32,
+            e_strength_reduction   =   64,
+            e_disable_vardef       =  128,
+            e_collect_vars         =  256,
+            e_collect_funcs        =  512,
+            e_collect_assings      = 1024,
+            e_disable_usr_on_rsrvd = 2048,
+            e_disable_zero_return  = 4096
+         };
+
+         enum settings_base_funcs
+         {
+            e_bf_unknown = 0,
+            e_bf_abs       , e_bf_acos     , e_bf_acosh    , e_bf_asin   ,
+            e_bf_asinh     , e_bf_atan     , e_bf_atan2    , e_bf_atanh  ,
+            e_bf_avg       , e_bf_ceil     , e_bf_clamp    , e_bf_cos    ,
+            e_bf_cosh      , e_bf_cot      , e_bf_csc      , e_bf_equal  ,
+            e_bf_erf       , e_bf_erfc     , e_bf_exp      , e_bf_expm1  ,
+            e_bf_floor     , e_bf_frac     , e_bf_hypot    , e_bf_iclamp ,
+            e_bf_like      , e_bf_log      , e_bf_log10    , e_bf_log1p  ,
+            e_bf_log2      , e_bf_logn     , e_bf_mand     , e_bf_max    ,
+            e_bf_min       , e_bf_mod      , e_bf_mor      , e_bf_mul    ,
+            e_bf_ncdf      , e_bf_pow      , e_bf_root     , e_bf_round  ,
+            e_bf_roundn    , e_bf_sec      , e_bf_sgn      , e_bf_sin    ,
+            e_bf_sinc      , e_bf_sinh     , e_bf_sqrt     , e_bf_sum    ,
+            e_bf_swap      , e_bf_tan      , e_bf_tanh     , e_bf_trunc  ,
+            e_bf_not_equal , e_bf_inrange  , e_bf_deg2grad , e_bf_deg2rad,
+            e_bf_rad2deg   , e_bf_grad2deg
+         };
+
+         enum settings_control_structs
+         {
+            e_ctrl_unknown = 0,
+            e_ctrl_ifelse,
+            e_ctrl_switch,
+            e_ctrl_for_loop,
+            e_ctrl_while_loop,
+            e_ctrl_repeat_loop,
+            e_ctrl_return
+         };
+
+         enum settings_logic_opr
+         {
+            e_logic_unknown = 0,
+            e_logic_and, e_logic_nand,  e_logic_nor,
+            e_logic_not, e_logic_or,    e_logic_xnor,
+            e_logic_xor, e_logic_scand, e_logic_scor
+         };
+
+         enum settings_arithmetic_opr
+         {
+            e_arith_unknown = 0,
+            e_arith_add, e_arith_sub, e_arith_mul,
+            e_arith_div, e_arith_mod, e_arith_pow
+         };
+
+         enum settings_assignment_opr
+         {
+            e_assign_unknown = 0,
+            e_assign_assign, e_assign_addass, e_assign_subass,
+            e_assign_mulass, e_assign_divass, e_assign_modass
+         };
+
+         enum settings_inequality_opr
+         {
+            e_ineq_unknown = 0,
+            e_ineq_lt,    e_ineq_lte, e_ineq_eq,
+            e_ineq_equal, e_ineq_ne,  e_ineq_nequal,
+            e_ineq_gte,   e_ineq_gt
+         };
+
+         static const std::size_t compile_all_opts = e_replacer          +
+                                                     e_joiner            +
+                                                     e_numeric_check     +
+                                                     e_bracket_check     +
+                                                     e_sequence_check    +
+                                                     e_commutative_check +
+                                                     e_strength_reduction;
+
+         settings_store(const std::size_t compile_options = compile_all_opts)
+         {
+           load_compile_options(compile_options);
+         }
+
+         settings_store& enable_all_base_functions()
+         {
+            disabled_func_set_.clear();
+            return (*this);
+         }
+
+         settings_store& enable_all_control_structures()
+         {
+            disabled_ctrl_set_.clear();
+            return (*this);
+         }
+
+         settings_store& enable_all_logic_ops()
+         {
+            disabled_logic_set_.clear();
+            return (*this);
+         }
+
+         settings_store& enable_all_arithmetic_ops()
+         {
+            disabled_arithmetic_set_.clear();
+            return (*this);
+         }
+
+         settings_store& enable_all_assignment_ops()
+         {
+            disabled_assignment_set_.clear();
+            return (*this);
+         }
+
+         settings_store& enable_all_inequality_ops()
+         {
+            disabled_inequality_set_.clear();
+            return (*this);
+         }
+
+         settings_store& enable_local_vardef()
+         {
+            disable_vardef_ = false;
+            return (*this);
+         }
+
+         settings_store& disable_all_base_functions()
+         {
+            std::copy(details::base_function_list,
+                      details::base_function_list + details::base_function_list_size,
+                      std::insert_iterator<disabled_entity_set_t>
+                        (disabled_func_set_, disabled_func_set_.begin()));
+            return (*this);
+         }
+
+         settings_store& disable_all_control_structures()
+         {
+            std::copy(details::cntrl_struct_list,
+                      details::cntrl_struct_list + details::cntrl_struct_list_size,
+                      std::insert_iterator<disabled_entity_set_t>
+                        (disabled_ctrl_set_, disabled_ctrl_set_.begin()));
+            return (*this);
+         }
+
+         settings_store& disable_all_logic_ops()
+         {
+            std::copy(details::logic_ops_list,
+                      details::logic_ops_list + details::logic_ops_list_size,
+                      std::insert_iterator<disabled_entity_set_t>
+                        (disabled_logic_set_, disabled_logic_set_.begin()));
+            return (*this);
+         }
+
+         settings_store& disable_all_arithmetic_ops()
+         {
+            std::copy(details::arithmetic_ops_list,
+                      details::arithmetic_ops_list + details::arithmetic_ops_list_size,
+                      std::insert_iterator<disabled_entity_set_t>
+                        (disabled_arithmetic_set_, disabled_arithmetic_set_.begin()));
+            return (*this);
+         }
+
+         settings_store& disable_all_assignment_ops()
+         {
+            std::copy(details::assignment_ops_list,
+                      details::assignment_ops_list + details::assignment_ops_list_size,
+                      std::insert_iterator<disabled_entity_set_t>
+                        (disabled_assignment_set_, disabled_assignment_set_.begin()));
+            return (*this);
+         }
+
+         settings_store& disable_all_inequality_ops()
+         {
+            std::copy(details::inequality_ops_list,
+                      details::inequality_ops_list + details::inequality_ops_list_size,
+                      std::insert_iterator<disabled_entity_set_t>
+                        (disabled_inequality_set_, disabled_inequality_set_.begin()));
+            return (*this);
+         }
+
+         settings_store& disable_local_vardef()
+         {
+            disable_vardef_ = true;
+            return (*this);
+         }
+
+         bool replacer_enabled           () const { return enable_replacer_;           }
+         bool commutative_check_enabled  () const { return enable_commutative_check_;  }
+         bool joiner_enabled             () const { return enable_joiner_;             }
+         bool numeric_check_enabled      () const { return enable_numeric_check_;      }
+         bool bracket_check_enabled      () const { return enable_bracket_check_;      }
+         bool sequence_check_enabled     () const { return enable_sequence_check_;     }
+         bool strength_reduction_enabled () const { return enable_strength_reduction_; }
+         bool collect_variables_enabled  () const { return enable_collect_vars_;       }
+         bool collect_functions_enabled  () const { return enable_collect_funcs_;      }
+         bool collect_assignments_enabled() const { return enable_collect_assings_;    }
+         bool vardef_disabled            () const { return disable_vardef_;            }
+         bool rsrvd_sym_usr_disabled     () const { return disable_rsrvd_sym_usr_;     }
+         bool zero_return_disabled       () const { return disable_zero_return_;       }
+
+         bool function_enabled(const std::string& function_name) const
+         {
+            if (disabled_func_set_.empty())
+               return true;
+            else
+               return (disabled_func_set_.end() == disabled_func_set_.find(function_name));
+         }
+
+         bool control_struct_enabled(const std::string& control_struct) const
+         {
+            if (disabled_ctrl_set_.empty())
+               return true;
+            else
+               return (disabled_ctrl_set_.end() == disabled_ctrl_set_.find(control_struct));
+         }
+
+         bool logic_enabled(const std::string& logic_operation) const
+         {
+            if (disabled_logic_set_.empty())
+               return true;
+            else
+               return (disabled_logic_set_.end() == disabled_logic_set_.find(logic_operation));
+         }
+
+         bool arithmetic_enabled(const details::operator_type& arithmetic_operation) const
+         {
+            if (disabled_logic_set_.empty())
+               return true;
+            else
+               return disabled_arithmetic_set_.end() == disabled_arithmetic_set_
+                                                            .find(arith_opr_to_string(arithmetic_operation));
+         }
+
+         bool assignment_enabled(const details::operator_type& assignment) const
+         {
+            if (disabled_assignment_set_.empty())
+               return true;
+            else
+               return disabled_assignment_set_.end() == disabled_assignment_set_
+                                                           .find(assign_opr_to_string(assignment));
+         }
+
+         bool inequality_enabled(const details::operator_type& inequality) const
+         {
+            if (disabled_inequality_set_.empty())
+               return true;
+            else
+               return disabled_inequality_set_.end() == disabled_inequality_set_
+                                                           .find(inequality_opr_to_string(inequality));
+         }
+
+         bool function_disabled(const std::string& function_name) const
+         {
+            if (disabled_func_set_.empty())
+               return false;
+            else
+               return (disabled_func_set_.end() != disabled_func_set_.find(function_name));
+         }
+
+         bool control_struct_disabled(const std::string& control_struct) const
+         {
+            if (disabled_ctrl_set_.empty())
+               return false;
+            else
+               return (disabled_ctrl_set_.end() != disabled_ctrl_set_.find(control_struct));
+         }
+
+         bool logic_disabled(const std::string& logic_operation) const
+         {
+            if (disabled_logic_set_.empty())
+               return false;
+            else
+               return (disabled_logic_set_.end() != disabled_logic_set_.find(logic_operation));
+         }
+
+         bool assignment_disabled(const details::operator_type assignment_operation) const
+         {
+            if (disabled_assignment_set_.empty())
+               return false;
+            else
+               return disabled_assignment_set_.end() != disabled_assignment_set_
+                                                           .find(assign_opr_to_string(assignment_operation));
+         }
+
+         bool logic_disabled(const details::operator_type logic_operation) const
+         {
+            if (disabled_logic_set_.empty())
+               return false;
+            else
+               return disabled_logic_set_.end() != disabled_logic_set_
+                                                           .find(logic_opr_to_string(logic_operation));
+         }
+
+         bool arithmetic_disabled(const details::operator_type arithmetic_operation) const
+         {
+            if (disabled_arithmetic_set_.empty())
+               return false;
+            else
+               return disabled_arithmetic_set_.end() != disabled_arithmetic_set_
+                                                           .find(arith_opr_to_string(arithmetic_operation));
+         }
+
+         bool inequality_disabled(const details::operator_type& inequality) const
+         {
+            if (disabled_inequality_set_.empty())
+               return false;
+            else
+               return disabled_inequality_set_.end() != disabled_inequality_set_
+                                                           .find(inequality_opr_to_string(inequality));
+         }
+
+         settings_store& disable_base_function(settings_base_funcs bf)
+         {
+            if (
+                 (e_bf_unknown != bf) &&
+                 (static_cast<std::size_t>(bf) < (details::base_function_list_size + 1))
+               )
+            {
+               disabled_func_set_.insert(details::base_function_list[bf - 1]);
+            }
+
+            return (*this);
+         }
+
+         settings_store& disable_control_structure(settings_control_structs ctrl_struct)
+         {
+            if (
+                 (e_ctrl_unknown != ctrl_struct) &&
+                 (static_cast<std::size_t>(ctrl_struct) < (details::cntrl_struct_list_size + 1))
+               )
+            {
+               disabled_ctrl_set_.insert(details::cntrl_struct_list[ctrl_struct - 1]);
+            }
+
+            return (*this);
+         }
+
+         settings_store& disable_logic_operation(settings_logic_opr logic)
+         {
+            if (
+                 (e_logic_unknown != logic) &&
+                 (static_cast<std::size_t>(logic) < (details::logic_ops_list_size + 1))
+               )
+            {
+               disabled_logic_set_.insert(details::logic_ops_list[logic - 1]);
+            }
+
+            return (*this);
+         }
+
+         settings_store& disable_arithmetic_operation(settings_arithmetic_opr arithmetic)
+         {
+            if (
+                 (e_arith_unknown != arithmetic) &&
+                 (static_cast<std::size_t>(arithmetic) < (details::arithmetic_ops_list_size + 1))
+               )
+            {
+               disabled_arithmetic_set_.insert(details::arithmetic_ops_list[arithmetic - 1]);
+            }
+
+            return (*this);
+         }
+
+         settings_store& disable_assignment_operation(settings_assignment_opr assignment)
+         {
+            if (
+                 (e_assign_unknown != assignment) &&
+                 (static_cast<std::size_t>(assignment) < (details::assignment_ops_list_size + 1))
+               )
+            {
+               disabled_assignment_set_.insert(details::assignment_ops_list[assignment - 1]);
+            }
+
+            return (*this);
+         }
+
+         settings_store& disable_inequality_operation(settings_inequality_opr inequality)
+         {
+            if (
+                 (e_ineq_unknown != inequality) &&
+                 (static_cast<std::size_t>(inequality) < (details::inequality_ops_list_size + 1))
+               )
+            {
+               disabled_inequality_set_.insert(details::inequality_ops_list[inequality - 1]);
+            }
+
+            return (*this);
+         }
+
+         settings_store& enable_base_function(settings_base_funcs bf)
+         {
+            if (
+                 (e_bf_unknown != bf) &&
+                 (static_cast<std::size_t>(bf) < (details::base_function_list_size + 1))
+               )
+            {
+               const des_itr_t itr = disabled_func_set_.find(details::base_function_list[bf - 1]);
+
+               if (disabled_func_set_.end() != itr)
+               {
+                  disabled_func_set_.erase(itr);
+               }
+            }
+
+            return (*this);
+         }
+
+         settings_store& enable_control_structure(settings_control_structs ctrl_struct)
+         {
+            if (
+                 (e_ctrl_unknown != ctrl_struct) &&
+                 (static_cast<std::size_t>(ctrl_struct) < (details::cntrl_struct_list_size + 1))
+               )
+            {
+               const des_itr_t itr = disabled_ctrl_set_.find(details::cntrl_struct_list[ctrl_struct - 1]);
+
+               if (disabled_ctrl_set_.end() != itr)
+               {
+                  disabled_ctrl_set_.erase(itr);
+               }
+            }
+
+            return (*this);
+         }
+
+         settings_store& enable_logic_operation(settings_logic_opr logic)
+         {
+            if (
+                 (e_logic_unknown != logic) &&
+                 (static_cast<std::size_t>(logic) < (details::logic_ops_list_size + 1))
+               )
+            {
+               const des_itr_t itr = disabled_logic_set_.find(details::logic_ops_list[logic - 1]);
+
+               if (disabled_logic_set_.end() != itr)
+               {
+                  disabled_logic_set_.erase(itr);
+               }
+            }
+
+            return (*this);
+         }
+
+         settings_store& enable_arithmetic_operation(settings_arithmetic_opr arithmetic)
+         {
+            if (
+                 (e_arith_unknown != arithmetic) &&
+                 (static_cast<std::size_t>(arithmetic) < (details::arithmetic_ops_list_size + 1))
+               )
+            {
+               const des_itr_t itr = disabled_arithmetic_set_.find(details::arithmetic_ops_list[arithmetic - 1]);
+
+               if (disabled_arithmetic_set_.end() != itr)
+               {
+                  disabled_arithmetic_set_.erase(itr);
+               }
+            }
+
+            return (*this);
+         }
+
+         settings_store& enable_assignment_operation(settings_assignment_opr assignment)
+         {
+            if (
+                 (e_assign_unknown != assignment) &&
+                 (static_cast<std::size_t>(assignment) < (details::assignment_ops_list_size + 1))
+               )
+            {
+               const des_itr_t itr = disabled_assignment_set_.find(details::assignment_ops_list[assignment - 1]);
+
+               if (disabled_assignment_set_.end() != itr)
+               {
+                  disabled_assignment_set_.erase(itr);
+               }
+            }
+
+            return (*this);
+         }
+
+         settings_store& enable_inequality_operation(settings_inequality_opr inequality)
+         {
+            if (
+                 (e_ineq_unknown != inequality) &&
+                 (static_cast<std::size_t>(inequality) < (details::inequality_ops_list_size + 1))
+               )
+            {
+               const des_itr_t itr = disabled_inequality_set_.find(details::inequality_ops_list[inequality - 1]);
+
+               if (disabled_inequality_set_.end() != itr)
+               {
+                  disabled_inequality_set_.erase(itr);
+               }
+            }
+
+            return (*this);
+         }
+
+      private:
+
+         void load_compile_options(const std::size_t compile_options)
+         {
+            enable_replacer_           = (compile_options & e_replacer            ) == e_replacer;
+            enable_joiner_             = (compile_options & e_joiner              ) == e_joiner;
+            enable_numeric_check_      = (compile_options & e_numeric_check       ) == e_numeric_check;
+            enable_bracket_check_      = (compile_options & e_bracket_check       ) == e_bracket_check;
+            enable_sequence_check_     = (compile_options & e_sequence_check      ) == e_sequence_check;
+            enable_commutative_check_  = (compile_options & e_commutative_check   ) == e_commutative_check;
+            enable_strength_reduction_ = (compile_options & e_strength_reduction  ) == e_strength_reduction;
+            enable_collect_vars_       = (compile_options & e_collect_vars        ) == e_collect_vars;
+            enable_collect_funcs_      = (compile_options & e_collect_funcs       ) == e_collect_funcs;
+            enable_collect_assings_    = (compile_options & e_collect_assings     ) == e_collect_assings;
+            disable_vardef_            = (compile_options & e_disable_vardef      ) == e_disable_vardef;
+            disable_rsrvd_sym_usr_     = (compile_options & e_disable_usr_on_rsrvd) == e_disable_usr_on_rsrvd;
+            disable_zero_return_       = (compile_options & e_disable_zero_return ) == e_disable_zero_return;
+         }
+
+         std::string assign_opr_to_string(details::operator_type opr) const
+         {
+            switch (opr)
+            {
+               case details::e_assign : return ":=";
+               case details::e_addass : return "+=";
+               case details::e_subass : return "-=";
+               case details::e_mulass : return "*=";
+               case details::e_divass : return "/=";
+               case details::e_modass : return "%=";
+               default                : return   "";
+            }
+         }
+
+         std::string arith_opr_to_string(details::operator_type opr) const
+         {
+            switch (opr)
+            {
+               case details::e_add : return "+";
+               case details::e_sub : return "-";
+               case details::e_mul : return "*";
+               case details::e_div : return "/";
+               case details::e_mod : return "%";
+               default             : return  "";
+            }
+         }
+
+         std::string inequality_opr_to_string(details::operator_type opr) const
+         {
+            switch (opr)
+            {
+               case details::e_lt    : return  "<";
+               case details::e_lte   : return "<=";
+               case details::e_eq    : return "==";
+               case details::e_equal : return  "=";
+               case details::e_ne    : return "!=";
+               case details::e_nequal: return "<>";
+               case details::e_gte   : return ">=";
+               case details::e_gt    : return  ">";
+               default               : return   "";
+            }
+         }
+
+         std::string logic_opr_to_string(details::operator_type opr) const
+         {
+            switch (opr)
+            {
+               case details::e_and  : return "and" ;
+               case details::e_or   : return "or"  ;
+               case details::e_xor  : return "xor" ;
+               case details::e_nand : return "nand";
+               case details::e_nor  : return "nor" ;
+               case details::e_xnor : return "xnor";
+               case details::e_notl : return "not" ;
+               default              : return ""    ;
+            }
+         }
+
+         bool enable_replacer_;
+         bool enable_joiner_;
+         bool enable_numeric_check_;
+         bool enable_bracket_check_;
+         bool enable_sequence_check_;
+         bool enable_commutative_check_;
+         bool enable_strength_reduction_;
+         bool enable_collect_vars_;
+         bool enable_collect_funcs_;
+         bool enable_collect_assings_;
+         bool disable_vardef_;
+         bool disable_rsrvd_sym_usr_;
+         bool disable_zero_return_;
+
+         disabled_entity_set_t disabled_func_set_ ;
+         disabled_entity_set_t disabled_ctrl_set_ ;
+         disabled_entity_set_t disabled_logic_set_;
+         disabled_entity_set_t disabled_arithmetic_set_;
+         disabled_entity_set_t disabled_assignment_set_;
+         disabled_entity_set_t disabled_inequality_set_;
+
+         friend class parser<T>;
+      };
+
+      typedef settings_store settings_t;
+
+      parser(const settings_t& settings = settings_t())
+      : settings_(settings),
+        resolve_unknown_symbol_(false),
+        results_context_(0),
+        unknown_symbol_resolver_(reinterpret_cast<unknown_symbol_resolver*>(0)),
+        #ifdef _MSC_VER
+        #pragma warning(push)
+        #pragma warning (disable:4355)
+        #endif
+        sem_(*this),
+        #ifdef _MSC_VER
+        #pragma warning(pop)
+        #endif
+        operator_joiner_2_(2),
+        operator_joiner_3_(3)
+      {
+         init_precompilation();
+
+         load_operations_map           (base_ops_map_     );
+         load_unary_operations_map     (unary_op_map_     );
+         load_binary_operations_map    (binary_op_map_    );
+         load_inv_binary_operations_map(inv_binary_op_map_);
+         load_sf3_map                  (sf3_map_          );
+         load_sf4_map                  (sf4_map_          );
+
+         expression_generator_.init_synthesize_map();
+         expression_generator_.set_parser(*this);
+         expression_generator_.set_uom(unary_op_map_);
+         expression_generator_.set_bom(binary_op_map_);
+         expression_generator_.set_ibom(inv_binary_op_map_);
+         expression_generator_.set_sf3m(sf3_map_);
+         expression_generator_.set_sf4m(sf4_map_);
+         expression_generator_.set_strength_reduction_state(settings_.strength_reduction_enabled());
+      }
+
+     ~parser()
+      {}
+
+      inline void init_precompilation()
+      {
+         if (settings_.collect_variables_enabled())
+            dec_.collect_variables() = true;
+
+         if (settings_.collect_functions_enabled())
+            dec_.collect_functions() = true;
+
+         if (settings_.collect_assignments_enabled())
+            dec_.collect_assignments() = true;
+
+         if (settings_.replacer_enabled())
+         {
+            symbol_replacer_.clear();
+            symbol_replacer_.add_replace("true" , "1", lexer::token::e_number);
+            symbol_replacer_.add_replace("false", "0", lexer::token::e_number);
+            helper_assembly_.token_modifier_list.clear();
+            helper_assembly_.register_modifier(&symbol_replacer_);
+         }
+
+         if (settings_.commutative_check_enabled())
+         {
+            for (std::size_t i = 0; i < details::reserved_words_size; ++i)
+            {
+               commutative_inserter_.ignore_symbol(details::reserved_words[i]);
+            }
+
+            helper_assembly_.token_inserter_list.clear();
+            helper_assembly_.register_inserter(&commutative_inserter_);
+         }
+
+         if (settings_.joiner_enabled())
+         {
+            helper_assembly_.token_joiner_list.clear();
+            helper_assembly_.register_joiner(&operator_joiner_2_);
+            helper_assembly_.register_joiner(&operator_joiner_3_);
+         }
+
+         if (
+              settings_.numeric_check_enabled () ||
+              settings_.bracket_check_enabled () ||
+              settings_.sequence_check_enabled()
+            )
+         {
+            helper_assembly_.token_scanner_list.clear();
+
+            if (settings_.numeric_check_enabled())
+            {
+               helper_assembly_.register_scanner(&numeric_checker_);
+            }
+
+            if (settings_.bracket_check_enabled())
+            {
+               helper_assembly_.register_scanner(&bracket_checker_);
+            }
+
+            if (settings_.sequence_check_enabled())
+            {
+               helper_assembly_.register_scanner(&sequence_validator_      );
+               helper_assembly_.register_scanner(&sequence_validator_3tkns_);
+            }
+         }
+      }
+
+      inline bool compile(const std::string& expression_string, expression<T>& expr)
+      {
+         state_          .reset();
+         error_list_     .clear();
+         brkcnt_list_    .clear();
+         synthesis_error_.clear();
+         sem_            .cleanup();
+
+         return_cleanup();
+
+         expression_generator_.set_allocator(node_allocator_);
+
+         if (expression_string.empty())
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          "ERR000 - Empty expression!",
+                          exprtk_error_location));
+
+            return false;
+         }
+
+         if (!init(expression_string))
+         {
+            process_lexer_errors();
+            return false;
+         }
+
+         if (lexer().empty())
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          "ERR001 - Empty expression!",
+                          exprtk_error_location));
+
+            return false;
+         }
+
+         if (!run_assemblies())
+         {
+            return false;
+         }
+
+         symtab_store_.symtab_list_ = expr.get_symbol_table_list();
+         dec_.clear();
+
+         lexer().begin();
+
+         next_token();
+
+         expression_node_ptr e = parse_corpus();
+
+         if ((0 != e) && (token_t::e_eof == current_token().type))
+         {
+            bool* retinvk_ptr = 0;
+
+            if (state_.return_stmt_present)
+            {
+               dec_.return_present_ = true;
+
+               e = expression_generator_
+                     .return_envelope(e, results_context_, retinvk_ptr);
+            }
+
+            expr.set_expression(e);
+            expr.set_retinvk(retinvk_ptr);
+
+            register_local_vars(expr);
+            register_return_results(expr);
+
+            return !(!expr);
+         }
+         else
+         {
+            if (error_list_.empty())
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR002 - Invalid expression encountered",
+                             exprtk_error_location));
+            }
+
+            if ((0 != e) && branch_deletable(e))
+            {
+               destroy_node(e);
+            }
+
+            dec_.clear    ();
+            sem_.cleanup  ();
+            return_cleanup();
+
+            return false;
+         }
+      }
+
+      inline expression_t compile(const std::string& expression_string, symbol_table_t& symtab)
+      {
+         expression_t expr;
+
+         expr.register_symbol_table(symtab);
+
+         compile(expression_string,expr);
+
+         return expr;
+      }
+
+      void process_lexer_errors()
+      {
+         for (std::size_t i = 0; i < lexer().size(); ++i)
+         {
+            if (lexer()[i].is_error())
+            {
+               std::string diagnostic = "ERR003 - ";
+
+               switch (lexer()[i].type)
+               {
+                  case lexer::token::e_error      : diagnostic += "General token error";
+                                                    break;
+
+                  case lexer::token::e_err_symbol : diagnostic += "Symbol error";
+                                                    break;
+
+                  case lexer::token::e_err_number : diagnostic += "Invalid numeric token";
+                                                    break;
+
+                  case lexer::token::e_err_string : diagnostic += "Invalid string token";
+                                                    break;
+
+                  case lexer::token::e_err_sfunc  : diagnostic += "Invalid special function token";
+                                                    break;
+
+                  default                         : diagnostic += "Unknown compiler error";
+               }
+
+               set_error(
+                  make_error(parser_error::e_lexer,
+                             lexer()[i],
+                             diagnostic + ": " + lexer()[i].value,
+                             exprtk_error_location));
+            }
+         }
+      }
+
+      inline bool run_assemblies()
+      {
+         if (settings_.commutative_check_enabled())
+         {
+            helper_assembly_.run_inserters(lexer());
+         }
+
+         if (settings_.joiner_enabled())
+         {
+            helper_assembly_.run_joiners(lexer());
+         }
+
+         if (settings_.replacer_enabled())
+         {
+            helper_assembly_.run_modifiers(lexer());
+         }
+
+         if (
+              settings_.numeric_check_enabled () ||
+              settings_.bracket_check_enabled () ||
+              settings_.sequence_check_enabled()
+            )
+         {
+            if (!helper_assembly_.run_scanners(lexer()))
+            {
+               if (helper_assembly_.error_token_scanner)
+               {
+                  lexer::helper::bracket_checker*            bracket_checker_ptr     = 0;
+                  lexer::helper::numeric_checker*            numeric_checker_ptr     = 0;
+                  lexer::helper::sequence_validator*         sequence_validator_ptr  = 0;
+                  lexer::helper::sequence_validator_3tokens* sequence_validator3_ptr = 0;
+
+                  if (0 != (bracket_checker_ptr = dynamic_cast<lexer::helper::bracket_checker*>(helper_assembly_.error_token_scanner)))
+                  {
+                     set_error(
+                        make_error(parser_error::e_token,
+                                   bracket_checker_ptr->error_token(),
+                                   "ERR004 - Mismatched brackets: '" + bracket_checker_ptr->error_token().value + "'",
+                                   exprtk_error_location));
+                  }
+                  else if (0 != (numeric_checker_ptr = dynamic_cast<lexer::helper::numeric_checker*>(helper_assembly_.error_token_scanner)))
+                  {
+                     for (std::size_t i = 0; i < numeric_checker_ptr->error_count(); ++i)
+                     {
+                        lexer::token error_token = lexer()[numeric_checker_ptr->error_index(i)];
+
+                        set_error(
+                           make_error(parser_error::e_token,
+                                      error_token,
+                                      "ERR005 - Invalid numeric token: '" + error_token.value + "'",
+                                      exprtk_error_location));
+                     }
+
+                     if (numeric_checker_ptr->error_count())
+                     {
+                        numeric_checker_ptr->clear_errors();
+                     }
+                  }
+                  else if (0 != (sequence_validator_ptr = dynamic_cast<lexer::helper::sequence_validator*>(helper_assembly_.error_token_scanner)))
+                  {
+                     for (std::size_t i = 0; i < sequence_validator_ptr->error_count(); ++i)
+                     {
+                        std::pair<lexer::token,lexer::token> error_token = sequence_validator_ptr->error(i);
+
+                        set_error(
+                           make_error(parser_error::e_token,
+                                      error_token.first,
+                                      "ERR006 - Invalid token sequence: '" +
+                                      error_token.first.value  + "' and '" +
+                                      error_token.second.value + "'",
+                                      exprtk_error_location));
+                     }
+
+                     if (sequence_validator_ptr->error_count())
+                     {
+                        sequence_validator_ptr->clear_errors();
+                     }
+                  }
+                  else if (0 != (sequence_validator3_ptr = dynamic_cast<lexer::helper::sequence_validator_3tokens*>(helper_assembly_.error_token_scanner)))
+                  {
+                     for (std::size_t i = 0; i < sequence_validator3_ptr->error_count(); ++i)
+                     {
+                        std::pair<lexer::token,lexer::token> error_token = sequence_validator3_ptr->error(i);
+
+                        set_error(
+                           make_error(parser_error::e_token,
+                                      error_token.first,
+                                      "ERR007 - Invalid token sequence: '" +
+                                      error_token.first.value  + "' and '" +
+                                      error_token.second.value + "'",
+                                      exprtk_error_location));
+                     }
+
+                     if (sequence_validator3_ptr->error_count())
+                     {
+                        sequence_validator3_ptr->clear_errors();
+                     }
+                  }
+               }
+
+               return false;
+            }
+         }
+
+         return true;
+      }
+
+      inline settings_store& settings()
+      {
+         return settings_;
+      }
+
+      inline parser_error::type get_error(const std::size_t& index) const
+      {
+         if (index < error_list_.size())
+            return error_list_[index];
+         else
+            throw std::invalid_argument("parser::get_error() - Invalid error index specificed");
+      }
+
+      inline std::string error() const
+      {
+         if (!error_list_.empty())
+         {
+            return error_list_[0].diagnostic;
+         }
+         else
+            return std::string("No Error");
+      }
+
+      inline std::size_t error_count() const
+      {
+         return error_list_.size();
+      }
+
+      inline dependent_entity_collector& dec()
+      {
+         return dec_;
+      }
+
+      inline bool replace_symbol(const std::string& old_symbol, const std::string& new_symbol)
+      {
+         if (!settings_.replacer_enabled())
+            return false;
+         else if (details::is_reserved_word(old_symbol))
+            return false;
+         else
+            return symbol_replacer_.add_replace(old_symbol,new_symbol,lexer::token::e_symbol);
+      }
+
+      inline bool remove_replace_symbol(const std::string& symbol)
+      {
+         if (!settings_.replacer_enabled())
+            return false;
+         else if (details::is_reserved_word(symbol))
+            return false;
+         else
+            return symbol_replacer_.remove(symbol);
+      }
+
+      inline void enable_unknown_symbol_resolver(unknown_symbol_resolver* usr = reinterpret_cast<unknown_symbol_resolver*>(0))
+      {
+         resolve_unknown_symbol_ = true;
+
+         if (usr)
+            unknown_symbol_resolver_ = usr;
+         else
+            unknown_symbol_resolver_ = &default_usr_;
+      }
+
+      inline void enable_unknown_symbol_resolver(unknown_symbol_resolver& usr)
+      {
+         enable_unknown_symbol_resolver(&usr);
+      }
+
+      inline void disable_unknown_symbol_resolver()
+      {
+         resolve_unknown_symbol_  = false;
+         unknown_symbol_resolver_ = &default_usr_;
+      }
+
+   private:
+
+      inline bool valid_base_operation(const std::string& symbol) const
+      {
+         const std::size_t length = symbol.size();
+
+         if (
+              (length < 3) || // Shortest base op symbol length
+              (length > 9)    // Longest base op symbol length
+            )
+            return false;
+         else
+            return settings_.function_enabled(symbol) &&
+                   (base_ops_map_.end() != base_ops_map_.find(symbol));
+      }
+
+      inline bool valid_vararg_operation(const std::string& symbol) const
+      {
+         static const std::string s_sum     = "sum" ;
+         static const std::string s_mul     = "mul" ;
+         static const std::string s_avg     = "avg" ;
+         static const std::string s_min     = "min" ;
+         static const std::string s_max     = "max" ;
+         static const std::string s_mand    = "mand";
+         static const std::string s_mor     = "mor" ;
+         static const std::string s_multi   = "~"   ;
+         static const std::string s_mswitch = "[*]" ;
+
+         return
+               (
+                  details::imatch(symbol,s_sum    ) ||
+                  details::imatch(symbol,s_mul    ) ||
+                  details::imatch(symbol,s_avg    ) ||
+                  details::imatch(symbol,s_min    ) ||
+                  details::imatch(symbol,s_max    ) ||
+                  details::imatch(symbol,s_mand   ) ||
+                  details::imatch(symbol,s_mor    ) ||
+                  details::imatch(symbol,s_multi  ) ||
+                  details::imatch(symbol,s_mswitch)
+               ) &&
+               settings_.function_enabled(symbol);
+      }
+
+      bool is_invalid_logic_operation(const details::operator_type operation) const
+      {
+         return settings_.logic_disabled(operation);
+      }
+
+      bool is_invalid_arithmetic_operation(const details::operator_type operation) const
+      {
+         return settings_.arithmetic_disabled(operation);
+      }
+
+      bool is_invalid_assignment_operation(const details::operator_type operation) const
+      {
+         return settings_.assignment_disabled(operation);
+      }
+
+      bool is_invalid_inequality_operation(const details::operator_type operation) const
+      {
+         return settings_.inequality_disabled(operation);
+      }
+
+      #ifdef exprtk_enable_debugging
+      inline void next_token()
+      {
+         const std::string ct_str = current_token().value;
+         parser_helper::next_token();
+         const std::string depth(2 * state_.scope_depth,' ');
+         exprtk_debug(("%s"
+                       "prev[%s] --> curr[%s]\n",
+                       depth.c_str(),
+                       ct_str.c_str(),
+                       current_token().value.c_str()));
+      }
+      #endif
+
+      inline expression_node_ptr parse_corpus()
+      {
+         std::vector<expression_node_ptr> arg_list;
+         std::vector<bool> side_effect_list;
+
+         scoped_vec_delete<expression_node_t> sdd((*this),arg_list);
+
+         lexer::token begin_token;
+         lexer::token   end_token;
+
+         for ( ; ; )
+         {
+            state_.side_effect_present = false;
+
+            begin_token = current_token();
+
+            expression_node_ptr arg = parse_expression();
+
+            if (0 == arg)
+            {
+               if (error_list_.empty())
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR008 - Invalid expression encountered",
+                                exprtk_error_location));
+               }
+
+               return error_node();
+            }
+            else
+            {
+               arg_list.push_back(arg);
+
+               side_effect_list.push_back(state_.side_effect_present);
+
+               end_token = current_token();
+
+               const std::string sub_expr = construct_subexpr(begin_token, end_token);
+
+               exprtk_debug(("parse_corpus(%02d) Subexpr: %s\n",
+                             static_cast<int>(arg_list.size() - 1),
+                             sub_expr.c_str()));
+
+               exprtk_debug(("parse_corpus(%02d) - Side effect present: %s\n",
+                             static_cast<int>(arg_list.size() - 1),
+                             state_.side_effect_present ? "true" : "false"));
+
+               exprtk_debug(("-------------------------------------------------\n"));
+            }
+
+            if (lexer().finished())
+               break;
+            else if (token_is(token_t::e_eof,prsrhlpr_t::e_hold))
+            {
+               if (lexer().finished())
+                  break;
+               else
+                  next_token();
+            }
+         }
+
+         if (
+              !arg_list.empty() &&
+              is_return_node(arg_list.back())
+            )
+         {
+            dec_.final_stmt_return_ = true;
+         }
+
+         const expression_node_ptr result = simplify(arg_list,side_effect_list);
+
+         sdd.delete_ptr = (0 == result);
+
+         return result;
+      }
+
+      std::string construct_subexpr(lexer::token& begin_token, lexer::token& end_token)
+      {
+         std::string result = lexer().substr(begin_token.position,end_token.position);
+
+         for (std::size_t i = 0; i < result.size(); ++i)
+         {
+            if (details::is_whitespace(result[i])) result[i] = ' ';
+         }
+
+         return result;
+      }
+
+      static const precedence_level default_precedence = e_level00;
+
+      struct state_t
+      {
+         inline void set(const precedence_level& l,
+                         const precedence_level& r,
+                         const details::operator_type& o)
+         {
+            left  = l;
+            right = r;
+            operation = o;
+         }
+
+         inline void reset()
+         {
+            left      = e_level00;
+            right     = e_level00;
+            operation = details::e_default;
+         }
+
+         precedence_level left;
+         precedence_level right;
+         details::operator_type operation;
+      };
+
+      inline expression_node_ptr parse_expression(precedence_level precedence = e_level00)
+      {
+         expression_node_ptr expression = parse_branch(precedence);
+
+         if (0 == expression)
+         {
+            return error_node();
+         }
+
+         bool break_loop = false;
+
+         state_t current_state;
+
+         for ( ; ; )
+         {
+            current_state.reset();
+
+            switch (current_token().type)
+            {
+               case token_t::e_assign : current_state.set(e_level00,e_level00, details::e_assign); break;
+               case token_t::e_addass : current_state.set(e_level00,e_level00, details::e_addass); break;
+               case token_t::e_subass : current_state.set(e_level00,e_level00, details::e_subass); break;
+               case token_t::e_mulass : current_state.set(e_level00,e_level00, details::e_mulass); break;
+               case token_t::e_divass : current_state.set(e_level00,e_level00, details::e_divass); break;
+               case token_t::e_modass : current_state.set(e_level00,e_level00, details::e_modass); break;
+               case token_t::e_swap   : current_state.set(e_level00,e_level00, details::e_swap  ); break;
+               case token_t::e_lt     : current_state.set(e_level05,e_level06, details::    e_lt); break;
+               case token_t::e_lte    : current_state.set(e_level05,e_level06, details::   e_lte); break;
+               case token_t::e_eq     : current_state.set(e_level05,e_level06, details::    e_eq); break;
+               case token_t::e_ne     : current_state.set(e_level05,e_level06, details::    e_ne); break;
+               case token_t::e_gte    : current_state.set(e_level05,e_level06, details::   e_gte); break;
+               case token_t::e_gt     : current_state.set(e_level05,e_level06, details::    e_gt); break;
+               case token_t::e_add    : current_state.set(e_level07,e_level08, details::   e_add); break;
+               case token_t::e_sub    : current_state.set(e_level07,e_level08, details::   e_sub); break;
+               case token_t::e_div    : current_state.set(e_level10,e_level11, details::   e_div); break;
+               case token_t::e_mul    : current_state.set(e_level10,e_level11, details::   e_mul); break;
+               case token_t::e_mod    : current_state.set(e_level10,e_level11, details::   e_mod); break;
+               case token_t::e_pow    : current_state.set(e_level12,e_level12, details::   e_pow); break;
+               default                : if (token_t::e_symbol == current_token().type)
+                                        {
+                                           static const std::string s_and   =   "and";
+                                           static const std::string s_nand  =  "nand";
+                                           static const std::string s_or    =    "or";
+                                           static const std::string s_nor   =   "nor";
+                                           static const std::string s_xor   =   "xor";
+                                           static const std::string s_xnor  =  "xnor";
+                                           static const std::string s_in    =    "in";
+                                           static const std::string s_like  =  "like";
+                                           static const std::string s_ilike = "ilike";
+                                           static const std::string s_and1  =     "&";
+                                           static const std::string s_or1   =     "|";
+                                           static const std::string s_not   =   "not";
+
+                                           if (details::imatch(current_token().value,s_and))
+                                           {
+                                              current_state.set(e_level03, e_level04, details::e_and);
+                                              break;
+                                           }
+                                           else if (details::imatch(current_token().value,s_and1))
+                                           {
+                                              #ifndef exprtk_disable_sc_andor
+                                              current_state.set(e_level03, e_level04, details::e_scand);
+                                              #else
+                                              current_state.set(e_level03, e_level04, details::e_and);
+                                              #endif
+                                              break;
+                                           }
+                                           else if (details::imatch(current_token().value,s_nand))
+                                           {
+                                              current_state.set(e_level03, e_level04, details::e_nand);
+                                              break;
+                                           }
+                                           else if (details::imatch(current_token().value,s_or))
+                                           {
+                                              current_state.set(e_level01, e_level02, details::e_or);
+                                              break;
+                                           }
+                                           else if (details::imatch(current_token().value,s_or1))
+                                           {
+                                              #ifndef exprtk_disable_sc_andor
+                                              current_state.set(e_level01, e_level02, details::e_scor);
+                                              #else
+                                              current_state.set(e_level01, e_level02, details::e_or);
+                                              #endif
+                                              break;
+                                           }
+                                           else if (details::imatch(current_token().value,s_nor))
+                                           {
+                                              current_state.set(e_level01, e_level02, details::e_nor);
+                                              break;
+                                           }
+                                           else if (details::imatch(current_token().value,s_xor))
+                                           {
+                                              current_state.set(e_level01, e_level02, details::e_xor);
+                                              break;
+                                           }
+                                           else if (details::imatch(current_token().value,s_xnor))
+                                           {
+                                              current_state.set(e_level01, e_level02, details::e_xnor);
+                                              break;
+                                           }
+                                           else if (details::imatch(current_token().value,s_in))
+                                           {
+                                              current_state.set(e_level04, e_level04, details::e_in);
+                                              break;
+                                           }
+                                           else if (details::imatch(current_token().value,s_like))
+                                           {
+                                              current_state.set(e_level04, e_level04, details::e_like);
+                                              break;
+                                           }
+                                           else if (details::imatch(current_token().value,s_ilike))
+                                           {
+                                              current_state.set(e_level04, e_level04, details::e_ilike);
+                                              break;
+                                           }
+                                           else if (details::imatch(current_token().value,s_not))
+                                           {
+                                              break;
+                                           }
+                                        }
+
+                                        break_loop = true;
+            }
+
+            if (break_loop)
+            {
+               parse_pending_string_rangesize(expression);
+               break;
+            }
+            else if (current_state.left < precedence)
+               break;
+
+            const lexer::token prev_token = current_token();
+
+            next_token();
+
+            expression_node_ptr right_branch   = error_node();
+            expression_node_ptr new_expression = error_node();
+
+            if (is_invalid_logic_operation(current_state.operation))
+            {
+               free_node(node_allocator_,expression);
+
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             prev_token,
+                             "ERR009 - Invalid or disabled logic operation '" + details::to_str(current_state.operation) + "'",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+            else if (is_invalid_arithmetic_operation(current_state.operation))
+            {
+               free_node(node_allocator_,expression);
+
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             prev_token,
+                             "ERR010 - Invalid or disabled arithmetic operation '" + details::to_str(current_state.operation) + "'",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+            else if (is_invalid_inequality_operation(current_state.operation))
+            {
+               free_node(node_allocator_,expression);
+
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             prev_token,
+                             "ERR011 - Invalid inequality operation '" + details::to_str(current_state.operation) + "'",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+            else if (is_invalid_assignment_operation(current_state.operation))
+            {
+               free_node(node_allocator_,expression);
+
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             prev_token,
+                             "ERR012 - Invalid or disabled assignment operation '" + details::to_str(current_state.operation) + "'",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+
+            if (0 != (right_branch = parse_expression(current_state.right)))
+            {
+               if (
+                    details::is_return_node(  expression) ||
+                    details::is_return_node(right_branch)
+                  )
+               {
+                  free_node(node_allocator_,   expression);
+                  free_node(node_allocator_, right_branch);
+
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                prev_token,
+                                "ERR013 - Return statements cannot be part of sub-expressions",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+
+               new_expression = expression_generator_
+                                  (
+                                    current_state.operation,
+                                    expression,
+                                    right_branch
+                                  );
+            }
+
+            if (0 == new_expression)
+            {
+               if (error_list_.empty())
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                prev_token,
+                                !synthesis_error_.empty() ?
+                                synthesis_error_ :
+                                "ERR014 - General parsing error at token: '" + prev_token.value + "'",
+                                exprtk_error_location));
+               }
+
+               free_node(node_allocator_,   expression);
+               free_node(node_allocator_, right_branch);
+
+               return error_node();
+            }
+            else
+            {
+               if (
+                    token_is(token_t::e_ternary,prsrhlpr_t::e_hold) &&
+                    (precedence == e_level00)
+                  )
+               {
+                  expression = parse_ternary_conditional_statement(new_expression);
+               }
+               else
+                  expression = new_expression;
+
+               parse_pending_string_rangesize(expression);
+            }
+         }
+
+         return expression;
+      }
+
+      bool simplify_unary_negation_branch(expression_node_ptr& node)
+      {
+         {
+            typedef details::unary_branch_node<T,details::neg_op<T> > ubn_t;
+            ubn_t* n = dynamic_cast<ubn_t*>(node);
+
+            if (n)
+            {
+               expression_node_ptr un_r = n->branch(0);
+               n->release();
+               free_node(node_allocator_,node);
+               node = un_r;
+
+               return true;
+            }
+         }
+
+         {
+            typedef details::unary_variable_node<T,details::neg_op<T> > uvn_t;
+
+            uvn_t* n = dynamic_cast<uvn_t*>(node);
+
+            if (n)
+            {
+               const T& v = n->v();
+               expression_node_ptr return_node = error_node();
+
+               if (
+                    (0 != (return_node = symtab_store_.get_variable(v))) ||
+                    (0 != (return_node = sem_         .get_variable(v)))
+                  )
+               {
+                  free_node(node_allocator_,node);
+                  node = return_node;
+
+                  return true;
+               }
+               else
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR015 - Failed to find variable node in symbol table",
+                                exprtk_error_location));
+
+                  free_node(node_allocator_,node);
+
+                  return false;
+               }
+            }
+         }
+
+         return false;
+      }
+
+      static inline expression_node_ptr error_node()
+      {
+         return reinterpret_cast<expression_node_ptr>(0);
+      }
+
+      template <typename Type, std::size_t N>
+      struct scoped_delete
+      {
+         typedef Type* ptr_t;
+
+         scoped_delete(parser<T>& pr, ptr_t& p)
+         : delete_ptr(true),
+           parser_(pr),
+           p_(&p)
+         {}
+
+         scoped_delete(parser<T>& pr, ptr_t (&p)[N])
+         : delete_ptr(true),
+           parser_(pr),
+           p_(&p[0])
+         {}
+
+        ~scoped_delete()
+         {
+            if (delete_ptr)
+            {
+               for (std::size_t i = 0; i < N; ++i)
+               {
+                  free_node(parser_.node_allocator_,p_[i]);
+               }
+            }
+         }
+
+         bool delete_ptr;
+         parser<T>& parser_;
+         ptr_t* p_;
+
+      private:
+
+         scoped_delete<Type,N>& operator=(const scoped_delete<Type,N>&);
+      };
+
+      template <typename Type>
+      struct scoped_deq_delete
+      {
+         typedef Type* ptr_t;
+
+         scoped_deq_delete(parser<T>& pr, std::deque<ptr_t>& deq)
+         : delete_ptr(true),
+           parser_(pr),
+           deq_(deq)
+         {}
+
+        ~scoped_deq_delete()
+         {
+            if (delete_ptr && !deq_.empty())
+            {
+               for (std::size_t i = 0; i < deq_.size(); ++i)
+               {
+                  free_node(parser_.node_allocator_,deq_[i]);
+               }
+
+               deq_.clear();
+            }
+         }
+
+         bool delete_ptr;
+         parser<T>& parser_;
+         std::deque<ptr_t>& deq_;
+
+      private:
+
+         scoped_deq_delete<Type>& operator=(const scoped_deq_delete<Type>&);
+      };
+
+      template <typename Type>
+      struct scoped_vec_delete
+      {
+         typedef Type* ptr_t;
+
+         scoped_vec_delete(parser<T>& pr, std::vector<ptr_t>& vec)
+         : delete_ptr(true),
+           parser_(pr),
+           vec_(vec)
+         {}
+
+        ~scoped_vec_delete()
+         {
+            if (delete_ptr && !vec_.empty())
+            {
+               for (std::size_t i = 0; i < vec_.size(); ++i)
+               {
+                  free_node(parser_.node_allocator_,vec_[i]);
+               }
+
+               vec_.clear();
+            }
+         }
+
+         bool delete_ptr;
+         parser<T>& parser_;
+         std::vector<ptr_t>& vec_;
+
+      private:
+
+         scoped_vec_delete<Type>& operator=(const scoped_vec_delete<Type>&);
+      };
+
+      struct scoped_bool_negator
+      {
+         explicit scoped_bool_negator(bool& bb)
+         : b(bb)
+         { b = !b; }
+
+        ~scoped_bool_negator()
+         { b = !b; }
+
+         bool& b;
+      };
+
+      struct scoped_bool_or_restorer
+      {
+         explicit scoped_bool_or_restorer(bool& bb)
+         : b(bb),
+           original_value_(bb)
+         {}
+
+        ~scoped_bool_or_restorer()
+         {
+            b = b || original_value_;
+         }
+
+         bool& b;
+         bool original_value_;
+      };
+
+      inline expression_node_ptr parse_function_invocation(ifunction<T>* function, const std::string& function_name)
+      {
+         expression_node_ptr func_node = reinterpret_cast<expression_node_ptr>(0);
+
+         switch (function->param_count)
+         {
+            case  0 : func_node = parse_function_call_0  (function,function_name); break;
+            case  1 : func_node = parse_function_call< 1>(function,function_name); break;
+            case  2 : func_node = parse_function_call< 2>(function,function_name); break;
+            case  3 : func_node = parse_function_call< 3>(function,function_name); break;
+            case  4 : func_node = parse_function_call< 4>(function,function_name); break;
+            case  5 : func_node = parse_function_call< 5>(function,function_name); break;
+            case  6 : func_node = parse_function_call< 6>(function,function_name); break;
+            case  7 : func_node = parse_function_call< 7>(function,function_name); break;
+            case  8 : func_node = parse_function_call< 8>(function,function_name); break;
+            case  9 : func_node = parse_function_call< 9>(function,function_name); break;
+            case 10 : func_node = parse_function_call<10>(function,function_name); break;
+            case 11 : func_node = parse_function_call<11>(function,function_name); break;
+            case 12 : func_node = parse_function_call<12>(function,function_name); break;
+            case 13 : func_node = parse_function_call<13>(function,function_name); break;
+            case 14 : func_node = parse_function_call<14>(function,function_name); break;
+            case 15 : func_node = parse_function_call<15>(function,function_name); break;
+            case 16 : func_node = parse_function_call<16>(function,function_name); break;
+            case 17 : func_node = parse_function_call<17>(function,function_name); break;
+            case 18 : func_node = parse_function_call<18>(function,function_name); break;
+            case 19 : func_node = parse_function_call<19>(function,function_name); break;
+            case 20 : func_node = parse_function_call<20>(function,function_name); break;
+            default : {
+                         set_error(
+                            make_error(parser_error::e_syntax,
+                                       current_token(),
+                                       "ERR016 - Invalid number of parameters for function: '" + function_name + "'",
+                                       exprtk_error_location));
+
+                         return error_node();
+                      }
+         }
+
+         if (func_node)
+            return func_node;
+         else
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR017 - Failed to generate call to function: '" + function_name + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+      }
+
+      template <std::size_t NumberofParameters>
+      inline expression_node_ptr parse_function_call(ifunction<T>* function, const std::string& function_name)
+      {
+         #ifdef _MSC_VER
+            #pragma warning(push)
+            #pragma warning(disable: 4127)
+         #endif
+         if (0 == NumberofParameters)
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR018 - Expecting ifunction '" + function_name + "' to have non-zero parameter count",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         #ifdef _MSC_VER
+            #pragma warning(pop)
+         #endif
+
+         expression_node_ptr branch[NumberofParameters];
+         expression_node_ptr result  = error_node();
+
+         std::fill_n(branch, NumberofParameters, reinterpret_cast<expression_node_ptr>(0));
+
+         scoped_delete<expression_node_t,NumberofParameters> sd((*this),branch);
+
+         next_token();
+
+         if (!token_is(token_t::e_lbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR019 - Expecting argument list for function: '" + function_name + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         for (int i = 0; i < static_cast<int>(NumberofParameters); ++i)
+         {
+            branch[i] = parse_expression();
+
+            if (0 == branch[i])
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR020 - Failed to parse argument " + details::to_str(i) + " for function: '" + function_name + "'",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+            else if (i < static_cast<int>(NumberofParameters - 1))
+            {
+               if (!token_is(token_t::e_comma))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR021 - Invalid number of arguments for function: '" + function_name + "'",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+            }
+         }
+
+         if (!token_is(token_t::e_rbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR022 - Invalid number of arguments for function: '" + function_name + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else
+            result = expression_generator_.function(function,branch);
+
+         sd.delete_ptr = false;
+
+         return result;
+      }
+
+      inline expression_node_ptr parse_function_call_0(ifunction<T>* function, const std::string& function_name)
+      {
+         expression_node_ptr result = expression_generator_.function(function);
+
+         state_.side_effect_present = function->has_side_effects();
+
+         next_token();
+
+         if (
+               token_is(token_t::e_lbracket) &&
+              !token_is(token_t::e_rbracket)
+            )
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR023 - Expecting '()' to proceed call to function: '" + function_name + "'",
+                          exprtk_error_location));
+
+            free_node(node_allocator_,result);
+
+            return error_node();
+         }
+         else
+            return result;
+      }
+
+      template <std::size_t MaxNumberofParameters>
+      inline std::size_t parse_base_function_call(expression_node_ptr (&param_list)[MaxNumberofParameters], const std::string& function_name = "")
+      {
+         std::fill_n(param_list, MaxNumberofParameters, reinterpret_cast<expression_node_ptr>(0));
+
+         scoped_delete<expression_node_t,MaxNumberofParameters> sd((*this),param_list);
+
+         next_token();
+
+         if (!token_is(token_t::e_lbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR024 - Expected a '(' at start of function call to '" + function_name  +
+                          "', instead got: '" + current_token().value + "'",
+                          exprtk_error_location));
+
+            return 0;
+         }
+
+         if (token_is(token_t::e_rbracket, e_hold))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR025 - Expected at least one input parameter for function call '" + function_name + "'",
+                          exprtk_error_location));
+
+            return 0;
+         }
+
+         std::size_t param_index = 0;
+
+         for (; param_index < MaxNumberofParameters; ++param_index)
+         {
+            param_list[param_index] = parse_expression();
+
+            if (0 == param_list[param_index])
+               return 0;
+            else if (token_is(token_t::e_rbracket))
+            {
+               sd.delete_ptr = false;
+               break;
+            }
+            else if (token_is(token_t::e_comma))
+               continue;
+            else
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR026 - Expected a ',' between function input parameters, instead got: '" + current_token().value + "'",
+                             exprtk_error_location));
+
+               return 0;
+            }
+         }
+
+         if (sd.delete_ptr)
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR027 - Invalid number of input parameters passed to function '" + function_name  + "'",
+                          exprtk_error_location));
+
+            return 0;
+         }
+
+         return (param_index + 1);
+      }
+
+      inline expression_node_ptr parse_base_operation()
+      {
+         typedef std::pair<base_ops_map_t::iterator,base_ops_map_t::iterator> map_range_t;
+
+         const std::string operation_name   = current_token().value;
+         const token_t     diagnostic_token = current_token();
+
+         map_range_t itr_range = base_ops_map_.equal_range(operation_name);
+
+         if (0 == std::distance(itr_range.first,itr_range.second))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          diagnostic_token,
+                          "ERR028 - No entry found for base operation: " + operation_name,
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         static const std::size_t MaxNumberofParameters = 4;
+         expression_node_ptr param_list[MaxNumberofParameters] = {0};
+
+         const std::size_t parameter_count = parse_base_function_call(param_list, operation_name);
+
+         if ((parameter_count > 0) && (parameter_count <= MaxNumberofParameters))
+         {
+            for (base_ops_map_t::iterator itr = itr_range.first; itr != itr_range.second; ++itr)
+            {
+               const details::base_operation_t& operation = itr->second;
+
+               if (operation.num_params == parameter_count)
+               {
+                  switch (parameter_count)
+                  {
+                     #define base_opr_case(N)                                         \
+                     case N : {                                                       \
+                                 expression_node_ptr pl##N[N] = {0};                  \
+                                 std::copy(param_list, param_list + N, pl##N);        \
+                                 lodge_symbol(operation_name, e_st_function);         \
+                                 return expression_generator_(operation.type, pl##N); \
+                              }                                                       \
+
+                     base_opr_case(1)
+                     base_opr_case(2)
+                     base_opr_case(3)
+                     base_opr_case(4)
+                     #undef base_opr_case
+                  }
+               }
+            }
+         }
+
+         for (std::size_t i = 0; i < MaxNumberofParameters; ++i)
+         {
+            free_node(node_allocator_, param_list[i]);
+         }
+
+         set_error(
+            make_error(parser_error::e_syntax,
+                       diagnostic_token,
+                       "ERR029 - Invalid number of input parameters for call to function: '" + operation_name + "'",
+                       exprtk_error_location));
+
+         return error_node();
+      }
+
+      inline expression_node_ptr parse_conditional_statement_01(expression_node_ptr condition)
+      {
+         // Parse: [if][(][condition][,][consequent][,][alternative][)]
+
+         expression_node_ptr consequent  = error_node();
+         expression_node_ptr alternative = error_node();
+
+         bool result = true;
+
+         if (!token_is(token_t::e_comma))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR030 - Expected ',' between if-statement condition and consequent",
+                          exprtk_error_location));
+            result = false;
+         }
+         else if (0 == (consequent = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR031 - Failed to parse consequent for if-statement",
+                          exprtk_error_location));
+            result = false;
+         }
+         else if (!token_is(token_t::e_comma))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR032 - Expected ',' between if-statement consequent and alternative",
+                          exprtk_error_location));
+            result = false;
+         }
+         else if (0 == (alternative = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR033 - Failed to parse alternative for if-statement",
+                          exprtk_error_location));
+            result = false;
+         }
+         else if (!token_is(token_t::e_rbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR034 - Expected ')' at the end of if-statement",
+                          exprtk_error_location));
+            result = false;
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         if (result)
+         {
+            const bool consq_is_str = is_generally_string_node( consequent);
+            const bool alter_is_str = is_generally_string_node(alternative);
+
+            if (consq_is_str || alter_is_str)
+            {
+               if (consq_is_str && alter_is_str)
+               {
+                  return expression_generator_
+                           .conditional_string(condition, consequent, alternative);
+               }
+
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR035 - Return types of ternary if-statement differ",
+                             exprtk_error_location));
+
+               result = false;
+            }
+         }
+         #endif
+
+         if (!result)
+         {
+            free_node(node_allocator_,  condition);
+            free_node(node_allocator_, consequent);
+            free_node(node_allocator_,alternative);
+
+            return error_node();
+         }
+         else
+            return expression_generator_
+                      .conditional(condition, consequent, alternative);
+      }
+
+      inline expression_node_ptr parse_conditional_statement_02(expression_node_ptr condition)
+      {
+         expression_node_ptr consequent  = error_node();
+         expression_node_ptr alternative = error_node();
+
+         bool result = true;
+
+         if (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold))
+         {
+            if (0 == (consequent = parse_multi_sequence("if-statement-01")))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR036 - Failed to parse body of consequent for if-statement",
+                             exprtk_error_location));
+
+               result = false;
+            }
+         }
+         else
+         {
+            if (
+                 settings_.commutative_check_enabled() &&
+                 token_is(token_t::e_mul,prsrhlpr_t::e_hold)
+               )
+            {
+               next_token();
+            }
+
+            if (0 != (consequent = parse_expression()))
+            {
+               if (!token_is(token_t::e_eof))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR037 - Expected ';' at the end of the consequent for if-statement",
+                                exprtk_error_location));
+
+                  result = false;
+               }
+            }
+            else
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR038 - Failed to parse body of consequent for if-statement",
+                             exprtk_error_location));
+
+               result = false;
+            }
+         }
+
+         if (result)
+         {
+            if (details::imatch(current_token().value,"else"))
+            {
+               next_token();
+
+               if (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold))
+               {
+                  if (0 == (alternative = parse_multi_sequence("else-statement-01")))
+                  {
+                     set_error(
+                        make_error(parser_error::e_syntax,
+                                   current_token(),
+                                   "ERR039 - Failed to parse body of the 'else' for if-statement",
+                                   exprtk_error_location));
+
+                     result = false;
+                  }
+               }
+               else if (details::imatch(current_token().value,"if"))
+               {
+                  if (0 == (alternative = parse_conditional_statement()))
+                  {
+                     set_error(
+                        make_error(parser_error::e_syntax,
+                                   current_token(),
+                                   "ERR040 - Failed to parse body of if-else statement",
+                                   exprtk_error_location));
+
+                     result = false;
+                  }
+               }
+               else if (0 != (alternative = parse_expression()))
+               {
+                  if (!token_is(token_t::e_eof))
+                  {
+                     set_error(
+                        make_error(parser_error::e_syntax,
+                                   current_token(),
+                                   "ERR041 - Expected ';' at the end of the 'else-if' for the if-statement",
+                                   exprtk_error_location));
+
+                     result = false;
+                  }
+               }
+               else
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR042 - Failed to parse body of the 'else' for if-statement",
+                                exprtk_error_location));
+
+                  result = false;
+               }
+            }
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         if (result)
+         {
+            const bool consq_is_str = is_generally_string_node( consequent);
+            const bool alter_is_str = is_generally_string_node(alternative);
+
+            if (consq_is_str || alter_is_str)
+            {
+               if (consq_is_str && alter_is_str)
+               {
+                  return expression_generator_
+                           .conditional_string(condition, consequent, alternative);
+               }
+
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR043 - Return types of ternary if-statement differ",
+                             exprtk_error_location));
+
+               result = false;
+            }
+         }
+         #endif
+
+         if (!result)
+         {
+            free_node(node_allocator_,   condition);
+            free_node(node_allocator_,  consequent);
+            free_node(node_allocator_, alternative);
+
+            return error_node();
+         }
+         else
+            return expression_generator_
+                      .conditional(condition, consequent, alternative);
+      }
+
+      inline expression_node_ptr parse_conditional_statement()
+      {
+         expression_node_ptr condition = error_node();
+
+         next_token();
+
+         if (!token_is(token_t::e_lbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR044 - Expected '(' at start of if-statement, instead got: '" + current_token().value + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (0 == (condition = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR045 - Failed to parse condition for if-statement",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (token_is(token_t::e_comma,prsrhlpr_t::e_hold))
+         {
+            // if (x,y,z)
+            return parse_conditional_statement_01(condition);
+         }
+         else if (token_is(token_t::e_rbracket))
+         {
+            // 00. if (x) y;
+            // 01. if (x) y; else z;
+            // 02. if (x) y; else {z0; ... zn;}
+            // 03. if (x) y; else if (z) w;
+            // 04. if (x) y; else if (z) w; else u;
+            // 05. if (x) y; else if (z) w; else {u0; ... un;}
+            // 06. if (x) y; else if (z) {w0; ... wn;}
+            // 07. if (x) {y0; ... yn;}
+            // 08. if (x) {y0; ... yn;} else z;
+            // 09. if (x) {y0; ... yn;} else {z0; ... zn;};
+            // 10. if (x) {y0; ... yn;} else if (z) w;
+            // 11. if (x) {y0; ... yn;} else if (z) w; else u;
+            // 12. if (x) {y0; ... nex;} else if (z) w; else {u0 ... un;}
+            // 13. if (x) {y0; ... yn;} else if (z) {w0; ... wn;}
+            return parse_conditional_statement_02(condition);
+         }
+
+         set_error(
+            make_error(parser_error::e_syntax,
+                       current_token(),
+                       "ERR046 - Invalid if-statement",
+                       exprtk_error_location));
+
+         free_node(node_allocator_,condition);
+
+         return error_node();
+      }
+
+      inline expression_node_ptr parse_ternary_conditional_statement(expression_node_ptr condition)
+      {
+         // Parse: [condition][?][consequent][:][alternative]
+         expression_node_ptr consequent  = error_node();
+         expression_node_ptr alternative = error_node();
+
+         bool result = true;
+
+         if (0 == condition)
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR047 - Encountered invalid condition branch for ternary if-statement",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!token_is(token_t::e_ternary))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR048 - Expected '?' after condition of ternary if-statement",
+                          exprtk_error_location));
+
+            result = false;
+         }
+         else if (0 == (consequent = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR049 - Failed to parse consequent for ternary if-statement",
+                          exprtk_error_location));
+
+            result = false;
+         }
+         else if (!token_is(token_t::e_colon))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR050 - Expected ':' between ternary if-statement consequent and alternative",
+                          exprtk_error_location));
+
+            result = false;
+         }
+         else if (0 == (alternative = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR051 - Failed to parse alternative for ternary if-statement",
+                          exprtk_error_location));
+
+            result = false;
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         if (result)
+         {
+            const bool consq_is_str = is_generally_string_node( consequent);
+            const bool alter_is_str = is_generally_string_node(alternative);
+
+            if (consq_is_str || alter_is_str)
+            {
+               if (consq_is_str && alter_is_str)
+               {
+                  return expression_generator_
+                           .conditional_string(condition, consequent, alternative);
+               }
+
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR052 - Return types of ternary if-statement differ",
+                             exprtk_error_location));
+
+               result = false;
+            }
+         }
+         #endif
+
+         if (!result)
+         {
+            free_node(node_allocator_,   condition);
+            free_node(node_allocator_,  consequent);
+            free_node(node_allocator_, alternative);
+
+            return error_node();
+         }
+         else
+            return expression_generator_
+                      .conditional(condition, consequent, alternative);
+      }
+
+      inline expression_node_ptr parse_not_statement()
+      {
+         if (settings_.logic_disabled("not"))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR053 - Invalid or disabled logic operation 'not'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         return parse_base_operation();
+      }
+
+      inline expression_node_ptr parse_while_loop()
+      {
+         // Parse: [while][(][test expr][)][{][expression][}]
+         expression_node_ptr condition   = error_node();
+         expression_node_ptr branch      = error_node();
+         expression_node_ptr result_node = error_node();
+
+         bool result = true;
+
+         next_token();
+
+         if (!token_is(token_t::e_lbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR054 - Expected '(' at start of while-loop condition statement",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (0 == (condition = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR055 - Failed to parse condition for while-loop",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!token_is(token_t::e_rbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR056 - Expected ')' at end of while-loop condition statement",
+                          exprtk_error_location));
+
+            result = false;
+         }
+
+         brkcnt_list_.push_front(false);
+
+         if (result)
+         {
+            if (0 == (branch = parse_multi_sequence("while-loop")))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR057 - Failed to parse body of while-loop"));
+               result = false;
+            }
+            else if (0 == (result_node = expression_generator_.while_loop(condition,
+                                                                          branch,
+                                                                          brkcnt_list_.front())))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR058 - Failed to synthesize while-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+         }
+
+         if (!result)
+         {
+            free_node(node_allocator_,      branch);
+            free_node(node_allocator_,   condition);
+            free_node(node_allocator_, result_node);
+
+            brkcnt_list_.pop_front();
+
+            return error_node();
+         }
+         else
+            return result_node;
+      }
+
+      inline expression_node_ptr parse_repeat_until_loop()
+      {
+         // Parse: [repeat][{][expression][}][until][(][test expr][)]
+         expression_node_ptr condition = error_node();
+         expression_node_ptr branch    = error_node();
+         next_token();
+
+         std::vector<expression_node_ptr> arg_list;
+         std::vector<bool> side_effect_list;
+
+         scoped_vec_delete<expression_node_t> sdd((*this),arg_list);
+
+         brkcnt_list_.push_front(false);
+
+         if (details::imatch(current_token().value,"until"))
+         {
+            next_token();
+            branch = node_allocator_.allocate<details::null_node<T> >();
+         }
+         else
+         {
+            const token_t::token_type seperator = token_t::e_eof;
+
+            scope_handler sh(*this);
+
+            scoped_bool_or_restorer sbr(state_.side_effect_present);
+
+            for ( ; ; )
+            {
+               state_.side_effect_present = false;
+
+               expression_node_ptr arg = parse_expression();
+
+               if (0 == arg)
+                  return error_node();
+               else
+               {
+                  arg_list.push_back(arg);
+                  side_effect_list.push_back(state_.side_effect_present);
+               }
+
+               if (details::imatch(current_token().value,"until"))
+               {
+                  next_token();
+                  break;
+               }
+
+               const bool is_next_until = peek_token_is(token_t::e_symbol) &&
+                                          peek_token_is("until");
+
+               if (!token_is(seperator) && is_next_until)
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR059 - Expected '" + token_t::to_str(seperator) + "' in body of repeat until loop",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+
+               if (details::imatch(current_token().value,"until"))
+               {
+                  next_token();
+                  break;
+               }
+            }
+
+            branch = simplify(arg_list,side_effect_list);
+
+            sdd.delete_ptr = (0 == branch);
+
+            if (sdd.delete_ptr)
+            {
+               brkcnt_list_.pop_front();
+
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR060 - Failed to parse body of repeat until loop",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+         }
+
+         if (!token_is(token_t::e_lbracket))
+         {
+            brkcnt_list_.pop_front();
+
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR061 - Expected '(' before condition statement of repeat until loop",
+                          exprtk_error_location));
+
+            free_node(node_allocator_,branch);
+
+            return error_node();
+         }
+         else if (0 == (condition = parse_expression()))
+         {
+            brkcnt_list_.pop_front();
+
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR062 - Failed to parse condition for repeat until loop",
+                          exprtk_error_location));
+
+            free_node(node_allocator_,branch);
+
+            return error_node();
+         }
+         else if (!token_is(token_t::e_rbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR063 - Expected ')' after condition of repeat until loop",
+                          exprtk_error_location));
+
+            free_node(node_allocator_,    branch);
+            free_node(node_allocator_, condition);
+
+            brkcnt_list_.pop_front();
+
+            return error_node();
+         }
+
+         expression_node_ptr result;
+
+         result = expression_generator_
+                     .repeat_until_loop(condition, branch, brkcnt_list_.front());
+
+         if (0 == result)
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR064 - Failed to synthesize repeat until loop",
+                          exprtk_error_location));
+
+            free_node(node_allocator_,condition);
+
+            brkcnt_list_.pop_front();
+
+            return error_node();
+         }
+         else
+         {
+            brkcnt_list_.pop_front();
+            return result;
+         }
+      }
+
+      inline expression_node_ptr parse_for_loop()
+      {
+         expression_node_ptr initialiser = error_node();
+         expression_node_ptr condition   = error_node();
+         expression_node_ptr incrementor = error_node();
+         expression_node_ptr loop_body   = error_node();
+
+         scope_element* se = 0;
+         bool result       = true;
+
+         next_token();
+
+         scope_handler sh(*this);
+
+         if (!token_is(token_t::e_lbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR065 - Expected '(' at start of for-loop",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         if (!token_is(token_t::e_eof))
+         {
+            if (
+                 !token_is(token_t::e_symbol,prsrhlpr_t::e_hold) &&
+                 details::imatch(current_token().value,"var")
+               )
+            {
+               next_token();
+
+               if (!token_is(token_t::e_symbol,prsrhlpr_t::e_hold))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR066 - Expected a variable at the start of initialiser section of for-loop",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+               else if (!peek_token_is(token_t::e_assign))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR067 - Expected variable assignment of initialiser section of for-loop",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+
+               const std::string loop_counter_symbol = current_token().value;
+
+               se = &sem_.get_element(loop_counter_symbol);
+
+               if ((se->name == loop_counter_symbol) && se->active)
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR068 - For-loop variable '" + loop_counter_symbol+ "' is being shadowed by a previous declaration",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+               else if (!symtab_store_.is_variable(loop_counter_symbol))
+               {
+                  if (
+                       !se->active &&
+                       (se->name == loop_counter_symbol) &&
+                       (se->type ==  scope_element::e_variable)
+                     )
+                  {
+                     se->active = true;
+                     se->ref_count++;
+                  }
+                  else
+                  {
+                     scope_element nse;
+                     nse.name      = loop_counter_symbol;
+                     nse.active    = true;
+                     nse.ref_count = 1;
+                     nse.type      = scope_element::e_variable;
+                     nse.depth     = state_.scope_depth;
+                     nse.data      = new T(T(0));
+                     nse.var_node  = node_allocator_.allocate<variable_node_t>(*(T*)(nse.data));
+
+                     if (!sem_.add_element(nse))
+                     {
+                        set_error(
+                           make_error(parser_error::e_syntax,
+                                      current_token(),
+                                      "ERR069 - Failed to add new local variable '" + loop_counter_symbol + "' to SEM",
+                                      exprtk_error_location));
+
+                        sem_.free_element(nse);
+
+                        result = false;
+                     }
+                     else
+                     {
+                        exprtk_debug(("parse_for_loop() - INFO - Added new local variable: %s\n",nse.name.c_str()));
+
+                        state_.activate_side_effect("parse_for_loop()");
+                     }
+                  }
+               }
+            }
+
+            if (0 == (initialiser = parse_expression()))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR070 - Failed to parse initialiser of for-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+            else if (!token_is(token_t::e_eof))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR071 - Expected ';' after initialiser of for-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+         }
+
+         if (!token_is(token_t::e_eof))
+         {
+            if (0 == (condition = parse_expression()))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR072 - Failed to parse condition of for-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+            else if (!token_is(token_t::e_eof))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR073 - Expected ';' after condition section of for-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+         }
+
+         if (!token_is(token_t::e_rbracket))
+         {
+            if (0 == (incrementor = parse_expression()))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR074 - Failed to parse incrementor of for-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+            else if (!token_is(token_t::e_rbracket))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR075 - Expected ')' after incrementor section of for-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+         }
+
+         if (result)
+         {
+            brkcnt_list_.push_front(false);
+
+            if (0 == (loop_body = parse_multi_sequence("for-loop")))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR076 - Failed to parse body of for-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+         }
+
+         if (!result)
+         {
+            if (se)
+            {
+               se->ref_count--;
+            }
+
+            free_node(node_allocator_, initialiser);
+            free_node(node_allocator_,   condition);
+            free_node(node_allocator_, incrementor);
+            free_node(node_allocator_,   loop_body);
+
+            if (!brkcnt_list_.empty())
+            {
+               brkcnt_list_.pop_front();
+            }
+
+            return error_node();
+         }
+         else
+         {
+            expression_node_ptr result_node =
+               expression_generator_.for_loop(initialiser,
+                                              condition,
+                                              incrementor,
+                                              loop_body,
+                                              brkcnt_list_.front());
+            brkcnt_list_.pop_front();
+
+            return result_node;
+         }
+      }
+
+      inline expression_node_ptr parse_switch_statement()
+      {
+         std::vector<expression_node_ptr> arg_list;
+         expression_node_ptr result = error_node();
+
+         if (!details::imatch(current_token().value,"switch"))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR077 - Expected keyword 'switch'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         scoped_vec_delete<expression_node_t> svd((*this),arg_list);
+
+         next_token();
+
+         if (!token_is(token_t::e_lcrlbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR078 - Expected '{' for call to switch statement",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         for ( ; ; )
+         {
+            if (!details::imatch("case",current_token().value))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR079 - Expected either a 'case' or 'default' statement",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+
+            next_token();
+
+            expression_node_ptr condition = parse_expression();
+
+            if (0 == condition)
+               return error_node();
+            else if (!token_is(token_t::e_colon))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR080 - Expected ':' for case of switch statement",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+
+            expression_node_ptr consequent = parse_expression();
+
+            if (0 == consequent)
+               return error_node();
+            else if (!token_is(token_t::e_eof))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR081 - Expected ';' at end of case for switch statement",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+
+            // Can we optimise away the case statement?
+            if (is_constant_node(condition) && is_false(condition))
+            {
+               free_node(node_allocator_,  condition);
+               free_node(node_allocator_, consequent);
+            }
+            else
+            {
+               arg_list.push_back( condition);
+               arg_list.push_back(consequent);
+            }
+
+            if (details::imatch("default",current_token().value))
+            {
+               next_token();
+               if (!token_is(token_t::e_colon))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR082 - Expected ':' for default of switch statement",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+
+               expression_node_ptr default_statement = error_node();
+
+               if (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold))
+                  default_statement = parse_multi_sequence("switch-default");
+               else
+                  default_statement = parse_expression();
+
+               if (0 == default_statement)
+                  return error_node();
+               else if (!token_is(token_t::e_eof))
+               {
+                  free_node(node_allocator_,default_statement);
+
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR083 - Expected ';' at end of default for switch statement",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+
+               arg_list.push_back(default_statement);
+               break;
+            }
+         }
+
+         if (!token_is(token_t::e_rcrlbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR084 - Expected '}' at end of switch statement",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         result = expression_generator_.switch_statement(arg_list);
+
+         svd.delete_ptr = (0 == result);
+
+         return result;
+      }
+
+      inline expression_node_ptr parse_multi_switch_statement()
+      {
+         std::vector<expression_node_ptr> arg_list;
+
+         if (!details::imatch(current_token().value,"[*]"))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR085 - Expected token '[*]'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         scoped_vec_delete<expression_node_t> svd((*this),arg_list);
+
+         next_token();
+
+         if (!token_is(token_t::e_lcrlbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR086 - Expected '{' for call to [*] statement",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         for ( ; ; )
+         {
+            if (!details::imatch("case",current_token().value))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR087 - Expected a 'case' statement for multi-switch",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+
+            next_token();
+
+            expression_node_ptr condition = parse_expression();
+
+            if (0 == condition)
+               return error_node();
+
+            if (!token_is(token_t::e_colon))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR088 - Expected ':' for case of [*] statement",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+
+            expression_node_ptr consequent = parse_expression();
+
+            if (0 == consequent)
+               return error_node();
+
+            if (!token_is(token_t::e_eof))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR089 - Expected ';' at end of case for [*] statement",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+
+            // Can we optimise away the case statement?
+            if (is_constant_node(condition) && is_false(condition))
+            {
+               free_node(node_allocator_,  condition);
+               free_node(node_allocator_, consequent);
+            }
+            else
+            {
+               arg_list.push_back( condition);
+               arg_list.push_back(consequent);
+            }
+
+            if (token_is(token_t::e_rcrlbracket,prsrhlpr_t::e_hold))
+            {
+               break;
+            }
+         }
+
+         if (!token_is(token_t::e_rcrlbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR090 - Expected '}' at end of [*] statement",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         const expression_node_ptr result = expression_generator_.multi_switch_statement(arg_list);
+
+         svd.delete_ptr = (0 == result);
+
+         return result;
+      }
+
+      inline expression_node_ptr parse_vararg_function()
+      {
+         std::vector<expression_node_ptr> arg_list;
+
+         details::operator_type opt_type = details::e_default;
+         const std::string symbol = current_token().value;
+
+         if (details::imatch(symbol,"~"))
+         {
+            next_token();
+            return parse_multi_sequence();
+         }
+         else if (details::imatch(symbol,"[*]"))
+         {
+            return parse_multi_switch_statement();
+         }
+         else if (details::imatch(symbol, "avg" )) opt_type = details::e_avg ;
+         else if (details::imatch(symbol, "mand")) opt_type = details::e_mand;
+         else if (details::imatch(symbol, "max" )) opt_type = details::e_max ;
+         else if (details::imatch(symbol, "min" )) opt_type = details::e_min ;
+         else if (details::imatch(symbol, "mor" )) opt_type = details::e_mor ;
+         else if (details::imatch(symbol, "mul" )) opt_type = details::e_prod;
+         else if (details::imatch(symbol, "sum" )) opt_type = details::e_sum ;
+         else
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR091 - Unsupported vararg function: " + symbol,
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         scoped_vec_delete<expression_node_t> sdd((*this),arg_list);
+
+         lodge_symbol(symbol, e_st_function);
+
+         next_token();
+
+         if (!token_is(token_t::e_lbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR092 - Expected '(' for call to vararg function: " + symbol,
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         for ( ; ; )
+         {
+            expression_node_ptr arg = parse_expression();
+
+            if (0 == arg)
+               return error_node();
+            else
+               arg_list.push_back(arg);
+
+            if (token_is(token_t::e_rbracket))
+               break;
+            else if (!token_is(token_t::e_comma))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR093 - Expected ',' for call to vararg function: " + symbol,
+                             exprtk_error_location));
+
+               return error_node();
+            }
+         }
+
+         const expression_node_ptr result = expression_generator_.vararg_function(opt_type,arg_list);
+
+         sdd.delete_ptr = (0 == result);
+         return result;
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      inline expression_node_ptr parse_string_range_statement(expression_node_ptr& expression)
+      {
+         if (!token_is(token_t::e_lsqrbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR094 - Expected '[' as start of string range definition",
+                          exprtk_error_location));
+
+            free_node(node_allocator_,expression);
+
+            return error_node();
+         }
+         else if (token_is(token_t::e_rsqrbracket))
+         {
+            return node_allocator_.allocate<details::string_size_node<T> >(expression);
+         }
+
+         range_t rp;
+
+         if (!parse_range(rp,true))
+         {
+            free_node(node_allocator_,expression);
+
+            return error_node();
+         }
+
+         expression_node_ptr result = expression_generator_(expression,rp);
+
+         if (0 == result)
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR095 - Failed to generate string range node",
+                          exprtk_error_location));
+
+            free_node(node_allocator_,expression);
+         }
+
+         rp.clear();
+
+         return result;
+      }
+      #else
+      inline expression_node_ptr parse_string_range_statement(expression_node_ptr&)
+      {
+         return error_node();
+      }
+      #endif
+
+      inline void parse_pending_string_rangesize(expression_node_ptr& expression)
+      {
+         // Allow no more than 100 range calls, eg: s[][][]...[][]
+         const std::size_t max_rangesize_parses = 100;
+
+         std::size_t i = 0;
+
+         while
+            (
+              (0 != expression)                     &&
+              (i++ < max_rangesize_parses)          &&
+              error_list_.empty()                   &&
+              is_generally_string_node(expression)  &&
+              token_is(token_t::e_lsqrbracket,prsrhlpr_t::e_hold)
+            )
+         {
+            expression = parse_string_range_statement(expression);
+         }
+      }
+
+      template <typename Allocator1,
+                typename Allocator2,
+                template <typename, typename> class Sequence>
+      inline expression_node_ptr simplify(Sequence<expression_node_ptr,Allocator1>& expression_list,
+                                          Sequence<bool,Allocator2>& side_effect_list,
+                                          const bool specialise_on_final_type = false)
+      {
+         if (expression_list.empty())
+            return error_node();
+         else if (1 == expression_list.size())
+            return expression_list[0];
+
+         Sequence<expression_node_ptr,Allocator1> tmp_expression_list;
+
+         bool return_node_present = false;
+
+         for (std::size_t i = 0; i < (expression_list.size() - 1); ++i)
+         {
+            if (is_variable_node(expression_list[i]))
+               continue;
+            else if (
+                      is_return_node  (expression_list[i]) ||
+                      is_break_node   (expression_list[i]) ||
+                      is_continue_node(expression_list[i])
+                    )
+            {
+               tmp_expression_list.push_back(expression_list[i]);
+
+               // Remove all subexpressions after first short-circuit
+               // node has been encountered.
+
+               for (std::size_t j = i + 1; j < expression_list.size(); ++j)
+               {
+                  free_node(node_allocator_,expression_list[j]);
+               }
+
+               return_node_present = true;
+
+               break;
+            }
+            else if (
+                      is_constant_node(expression_list[i]) ||
+                      is_null_node    (expression_list[i]) ||
+                      !side_effect_list[i]
+                    )
+            {
+               free_node(node_allocator_,expression_list[i]);
+               continue;
+            }
+            else
+               tmp_expression_list.push_back(expression_list[i]);
+         }
+
+         if (!return_node_present)
+         {
+            tmp_expression_list.push_back(expression_list.back());
+         }
+
+         expression_list.swap(tmp_expression_list);
+
+         if (tmp_expression_list.size() > expression_list.size())
+         {
+            exprtk_debug(("simplify() - Reduced subexpressions from %d to %d\n",
+                          static_cast<int>(tmp_expression_list.size()),
+                          static_cast<int>(expression_list    .size())));
+         }
+
+         if (
+              return_node_present          ||
+              side_effect_list.back()      ||
+              (expression_list.size() > 1)
+            )
+            state_.activate_side_effect("simplify()");
+
+         if (1 == expression_list.size())
+            return expression_list[0];
+         else if (specialise_on_final_type && is_generally_string_node(expression_list.back()))
+            return expression_generator_.vararg_function(details::e_smulti,expression_list);
+         else
+            return expression_generator_.vararg_function(details::e_multi,expression_list);
+      }
+
+      inline expression_node_ptr parse_multi_sequence(const std::string& source = "")
+      {
+         token_t::token_type close_bracket = token_t::e_rcrlbracket;
+         token_t::token_type seperator     = token_t::e_eof;
+
+         if (!token_is(token_t::e_lcrlbracket))
+         {
+            if (token_is(token_t::e_lbracket))
+            {
+               close_bracket = token_t::e_rbracket;
+               seperator     = token_t::e_comma;
+            }
+            else
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR096 - Expected '" + token_t::to_str(close_bracket) + "' for call to multi-sequence" +
+                             ((!source.empty()) ? std::string(" section of " + source): ""),
+                             exprtk_error_location));
+
+               return error_node();
+            }
+         }
+         else if (token_is(token_t::e_rcrlbracket))
+         {
+            return node_allocator_.allocate<details::null_node<T> >();
+         }
+
+         std::vector<expression_node_ptr> arg_list;
+         std::vector<bool> side_effect_list;
+
+         expression_node_ptr result = error_node();
+
+         scoped_vec_delete<expression_node_t> sdd((*this),arg_list);
+
+         scope_handler sh(*this);
+
+         scoped_bool_or_restorer sbr(state_.side_effect_present);
+
+         for ( ; ; )
+         {
+            state_.side_effect_present = false;
+
+            expression_node_ptr arg = parse_expression();
+
+            if (0 == arg)
+               return error_node();
+            else
+            {
+               arg_list.push_back(arg);
+               side_effect_list.push_back(state_.side_effect_present);
+            }
+
+            if (token_is(close_bracket))
+               break;
+
+            const bool is_next_close = peek_token_is(close_bracket);
+
+            if (!token_is(seperator) && is_next_close)
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR097 - Expected '" + details::to_str(seperator) + "' for call to multi-sequence section of " + source,
+                             exprtk_error_location));
+
+               return error_node();
+            }
+
+            if (token_is(close_bracket))
+               break;
+         }
+
+         result = simplify(arg_list,side_effect_list,source.empty());
+
+         sdd.delete_ptr = (0 == result);
+         return result;
+      }
+
+      inline bool parse_range(range_t& rp, const bool skip_lsqr = false)
+      {
+         // Examples of valid ranges:
+         // 1. [1:5]     -> 1..5
+         // 2. [ :5]     -> 0..5
+         // 3. [1: ]     -> 1..end
+         // 4. [x:y]     -> x..y where x <= y
+         // 5. [x+1:y/2] -> x+1..y/2 where x+1 <= y/2
+         // 6. [ :y]     -> 0..y where 0 <= y
+         // 7. [x: ]     -> x..end where x <= end
+
+         rp.clear();
+
+         if (!skip_lsqr && !token_is(token_t::e_lsqrbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR098 - Expected '[' for start of range",
+                          exprtk_error_location));
+
+            return false;
+         }
+
+         if (token_is(token_t::e_colon))
+         {
+            rp.n0_c.first  = true;
+            rp.n0_c.second = 0;
+            rp.cache.first = 0;
+         }
+         else
+         {
+            expression_node_ptr r0 = parse_expression();
+
+            if (0 == r0)
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR099 - Failed parse begin section of range",
+                             exprtk_error_location));
+
+               return false;
+            }
+            else if (is_constant_node(r0))
+            {
+               const T r0_value = r0->value();
+
+               if (r0_value >= T(0))
+               {
+                  rp.n0_c.first  = true;
+                  rp.n0_c.second = static_cast<std::size_t>(details::numeric::to_int64(r0_value));
+                  rp.cache.first = rp.n0_c.second;
+               }
+
+               free_node(node_allocator_,r0);
+
+               if (r0_value < T(0))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR100 - Range lower bound less than zero! Constraint: r0 >= 0",
+                                exprtk_error_location));
+
+                  return false;
+               }
+            }
+            else
+            {
+               rp.n0_e.first  = true;
+               rp.n0_e.second = r0;
+            }
+
+            if (!token_is(token_t::e_colon))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR101 - Expected ':' for break  in range",
+                             exprtk_error_location));
+
+               rp.free();
+
+               return false;
+            }
+         }
+
+         if (token_is(token_t::e_rsqrbracket))
+         {
+            rp.n1_c.first  = true;
+            rp.n1_c.second = std::numeric_limits<std::size_t>::max();
+         }
+         else
+         {
+            expression_node_ptr r1 = parse_expression();
+
+            if (0 == r1)
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR102 - Failed parse end section of range",
+                             exprtk_error_location));
+
+               rp.free();
+
+               return false;
+            }
+            else if (is_constant_node(r1))
+            {
+               const T r1_value = r1->value();
+
+               if (r1_value >= T(0))
+               {
+                  rp.n1_c.first   = true;
+                  rp.n1_c.second  = static_cast<std::size_t>(details::numeric::to_int64(r1_value));
+                  rp.cache.second = rp.n1_c.second;
+               }
+
+               free_node(node_allocator_,r1);
+
+               if (r1_value < T(0))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR103 - Range upper bound less than zero! Constraint: r1 >= 0",
+                                exprtk_error_location));
+
+                  return false;
+               }
+            }
+            else
+            {
+               rp.n1_e.first  = true;
+               rp.n1_e.second = r1;
+            }
+
+            if (!token_is(token_t::e_rsqrbracket))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR104 - Expected ']' for start of range",
+                             exprtk_error_location));
+
+               rp.free();
+
+               return false;
+            }
+         }
+
+         if (rp.const_range())
+         {
+            std::size_t r0 = 0;
+            std::size_t r1 = 0;
+
+            const bool rp_result = rp(r0,r1);
+
+            if (!rp_result || (r0 > r1))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR105 - Invalid range, Constraint: r0 <= r1",
+                             exprtk_error_location));
+
+               return false;
+            }
+         }
+
+         return true;
+      }
+
+      inline void lodge_symbol(const std::string& symbol,
+                               const symbol_type st)
+      {
+         dec_.add_symbol(symbol,st);
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      inline expression_node_ptr parse_string()
+      {
+         const std::string symbol = current_token().value;
+
+         typedef details::stringvar_node<T>* strvar_node_t;
+
+         expression_node_ptr result   = error_node();
+         strvar_node_t const_str_node = static_cast<strvar_node_t>(0);
+
+         scope_element& se = sem_.get_active_element(symbol);
+
+         if (scope_element::e_string == se.type)
+         {
+            se.active = true;
+            result    = se.str_node;
+            lodge_symbol(symbol, e_st_local_string);
+         }
+         else
+         {
+            if (!symtab_store_.is_conststr_stringvar(symbol))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR106 - Unknown string symbol",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+
+            result = symtab_store_.get_stringvar(symbol);
+
+            if (symtab_store_.is_constant_string(symbol))
+            {
+               const_str_node = static_cast<strvar_node_t>(result);
+               result = expression_generator_(const_str_node->str());
+            }
+
+            lodge_symbol(symbol, e_st_string);
+         }
+
+         if (peek_token_is(token_t::e_lsqrbracket))
+         {
+            next_token();
+
+            if (peek_token_is(token_t::e_rsqrbracket))
+            {
+               next_token();
+               next_token();
+
+               if (const_str_node)
+               {
+                  free_node(node_allocator_,result);
+
+                  return expression_generator_(T(const_str_node->size()));
+               }
+               else
+                  return node_allocator_.allocate<details::stringvar_size_node<T> >
+                            (static_cast<details::stringvar_node<T>*>(result)->ref());
+            }
+
+            range_t rp;
+
+            if (!parse_range(rp))
+            {
+               free_node(node_allocator_,result);
+
+               return error_node();
+            }
+            else if (const_str_node)
+            {
+               free_node(node_allocator_,result);
+               result = expression_generator_(const_str_node->ref(),rp);
+            }
+            else
+               result = expression_generator_(static_cast<details::stringvar_node<T>*>
+                           (result)->ref(), rp);
+
+            if (result)
+               rp.clear();
+         }
+         else
+            next_token();
+
+         return result;
+      }
+      #else
+      inline expression_node_ptr parse_string()
+      {
+         return error_node();
+      }
+      #endif
+
+      #ifndef exprtk_disable_string_capabilities
+      inline expression_node_ptr parse_const_string()
+      {
+         const std::string   const_str = current_token().value;
+         expression_node_ptr result    = expression_generator_(const_str);
+
+         if (peek_token_is(token_t::e_lsqrbracket))
+         {
+            next_token();
+
+            if (peek_token_is(token_t::e_rsqrbracket))
+            {
+               next_token();
+               next_token();
+
+               free_node(node_allocator_,result);
+
+               return expression_generator_(T(const_str.size()));
+            }
+
+            range_t rp;
+
+            if (!parse_range(rp))
+            {
+               free_node(node_allocator_,result);
+
+               return error_node();
+            }
+
+            free_node(node_allocator_,result);
+
+            if (rp.n1_c.first && (rp.n1_c.second == std::numeric_limits<std::size_t>::max()))
+            {
+               rp.n1_c.second  = const_str.size() - 1;
+               rp.cache.second = rp.n1_c.second;
+            }
+
+            if (
+                 (rp.n0_c.first && (rp.n0_c.second >= const_str.size())) ||
+                 (rp.n1_c.first && (rp.n1_c.second >= const_str.size()))
+               )
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR107 - Overflow in range for string: '" + const_str + "'[" +
+                             (rp.n0_c.first ? details::to_str(static_cast<int>(rp.n0_c.second)) : "?") + ":" +
+                             (rp.n1_c.first ? details::to_str(static_cast<int>(rp.n1_c.second)) : "?") + "]",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+
+            result = expression_generator_(const_str,rp);
+
+            if (result)
+               rp.clear();
+         }
+         else
+            next_token();
+
+         return result;
+      }
+      #else
+      inline expression_node_ptr parse_const_string()
+      {
+         return error_node();
+      }
+      #endif
+
+      inline expression_node_ptr parse_vector()
+      {
+         const std::string symbol = current_token().value;
+
+         vector_holder_ptr vec = vector_holder_ptr(0);
+
+         const scope_element& se = sem_.get_active_element(symbol);
+
+         if (
+              !details::imatch(se.name, symbol) ||
+              (se.depth > state_.scope_depth)   ||
+              (scope_element::e_vector != se.type)
+            )
+         {
+            if (0 == (vec = symtab_store_.get_vector(symbol)))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR108 - Symbol '" + symbol+ " not a vector",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+         }
+         else
+            vec = se.vec_node;
+
+         expression_node_ptr index_expr = error_node();
+
+         next_token();
+
+         if (!token_is(token_t::e_lsqrbracket))
+         {
+            return node_allocator_.allocate<vector_node_t>(vec);
+         }
+         else if (token_is(token_t::e_rsqrbracket))
+         {
+            return expression_generator_(T(vec->size()));
+         }
+         else if (0 == (index_expr = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR109 - Failed to parse index for vector: '" + symbol + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!token_is(token_t::e_rsqrbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR110 - Expected ']' for index of vector: '" + symbol + "'",
+                          exprtk_error_location));
+
+            free_node(node_allocator_,index_expr);
+
+            return error_node();
+         }
+
+         // Perform compile-time range check
+         if (details::is_constant_node(index_expr))
+         {
+            const std::size_t index    = static_cast<std::size_t>(details::numeric::to_int32(index_expr->value()));
+            const std::size_t vec_size = vec->size();
+
+            if (index >= vec_size)
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR111 - Index of " + details::to_str(index) + " out of range for "
+                             "vector '" + symbol + "' of size " + details::to_str(vec_size),
+                             exprtk_error_location));
+
+               free_node(node_allocator_,index_expr);
+
+               return error_node();
+            }
+         }
+
+         return expression_generator_.vector_element(symbol, vec, index_expr);
+      }
+
+      inline expression_node_ptr parse_vararg_function_call(ivararg_function<T>* vararg_function, const std::string& vararg_function_name)
+      {
+         std::vector<expression_node_ptr> arg_list;
+
+         expression_node_ptr result = error_node();
+
+         scoped_vec_delete<expression_node_t> sdd((*this),arg_list);
+
+         next_token();
+
+         if (token_is(token_t::e_lbracket))
+         {
+            if (token_is(token_t::e_rbracket))
+            {
+               if (!vararg_function->allow_zero_parameters())
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR112 - Zero parameter call to vararg function: "
+                                + vararg_function_name + " not allowed",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+            }
+            else
+            {
+               for ( ; ; )
+               {
+                  expression_node_ptr arg = parse_expression();
+
+                  if (0 == arg)
+                     return error_node();
+                  else
+                     arg_list.push_back(arg);
+
+                  if (token_is(token_t::e_rbracket))
+                     break;
+                  else if (!token_is(token_t::e_comma))
+                  {
+                     set_error(
+                        make_error(parser_error::e_syntax,
+                                   current_token(),
+                                   "ERR113 - Expected ',' for call to vararg function: "
+                                   + vararg_function_name,
+                                   exprtk_error_location));
+
+                     return error_node();
+                  }
+               }
+            }
+         }
+         else if (!vararg_function->allow_zero_parameters())
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR114 - Zero parameter call to vararg function: "
+                          + vararg_function_name + " not allowed",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         if (arg_list.size() < vararg_function->min_num_args())
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR115 - Invalid number of parameters to call to vararg function: "
+                          + vararg_function_name + ", require at least "
+                          + details::to_str(static_cast<int>(vararg_function->min_num_args())) + " parameters",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (arg_list.size() > vararg_function->max_num_args())
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR116 - Invalid number of parameters to call to vararg function: "
+                          + vararg_function_name + ", require no more than "
+                          + details::to_str(static_cast<int>(vararg_function->max_num_args())) + " parameters",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         result = expression_generator_.vararg_function_call(vararg_function,arg_list);
+
+         sdd.delete_ptr = (0 == result);
+
+         return result;
+      }
+
+      class type_checker
+      {
+      public:
+
+         enum return_type_t
+         {
+            e_overload = ' ',
+            e_numeric  = 'T',
+            e_string   = 'S'
+         };
+
+         struct function_prototype_t
+         {
+             return_type_t return_type;
+             std::string   param_seq;
+         };
+
+         typedef parser<T> parser_t;
+         typedef std::vector<function_prototype_t> function_definition_list_t;
+
+         type_checker(parser_t& p,
+                      const std::string& func_name,
+                      const std::string& func_prototypes,
+                      const return_type_t default_return_type)
+         : invalid_state_(true),
+           parser_(p),
+           function_name_(func_name),
+           default_return_type_(default_return_type)
+         {
+            parse_function_prototypes(func_prototypes);
+         }
+
+         void set_default_return_type(const std::string& return_type)
+         {
+            default_return_type_ = return_type;
+         }
+
+         bool verify(const std::string& param_seq, std::size_t& pseq_index)
+         {
+            if (function_definition_list_.empty())
+               return true;
+
+            std::vector<std::pair<std::size_t,char> > error_list;
+
+            for (std::size_t i = 0; i < function_definition_list_.size(); ++i)
+            {
+               details::char_t diff_value = 0;
+               std::size_t     diff_index = 0;
+
+               const bool result = details::sequence_match(function_definition_list_[i].param_seq,
+                                                           param_seq,
+                                                           diff_index, diff_value);
+
+              if (result)
+              {
+                 pseq_index = i;
+                 return true;
+              }
+              else
+                 error_list.push_back(std::make_pair(diff_index, diff_value));
+            }
+
+            if (1 == error_list.size())
+            {
+               parser_.
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                parser_.current_token(),
+                                "ERR117 - Failed parameter type check for function '" + function_name_ + "', "
+                                "Expected '" + function_definition_list_[0].param_seq +
+                                "'  call set: '" + param_seq + "'",
+                                exprtk_error_location));
+            }
+            else
+            {
+               // find first with largest diff_index;
+               std::size_t max_diff_index = 0;
+
+               for (std::size_t i = 1; i < error_list.size(); ++i)
+               {
+                  if (error_list[i].first > error_list[max_diff_index].first)
+                  {
+                     max_diff_index = i;
+                  }
+               }
+
+               parser_.
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                parser_.current_token(),
+                                "ERR118 - Failed parameter type check for function '" + function_name_ + "', "
+                                "Best match: '" + function_definition_list_[max_diff_index].param_seq +
+                                "'  call set: '" + param_seq + "'",
+                                exprtk_error_location));
+            }
+
+            return false;
+         }
+
+         std::size_t paramseq_count() const
+         {
+            return function_definition_list_.size();
+         }
+
+         std::string paramseq(const std::size_t& index) const
+         {
+            return function_definition_list_[index].param_seq;
+         }
+
+         return_type_t return_type(const std::size_t& index) const
+         {
+            return function_definition_list_[index].return_type;
+         }
+
+         bool invalid() const
+         {
+            return !invalid_state_;
+         }
+
+         bool allow_zero_parameters() const
+         {
+
+            for (std::size_t i = 0; i < function_definition_list_.size(); ++i)
+            {
+               if (std::string::npos != function_definition_list_[i].param_seq.find("Z"))
+               {
+                  return true;
+               }
+            }
+
+            return false;
+         }
+
+      private:
+
+         std::vector<std::string> split_param_seq(const std::string& param_seq, const details::char_t delimiter = '|') const
+         {
+             std::string::const_iterator current_begin = param_seq.begin();
+             std::string::const_iterator iter          = param_seq.begin();
+
+             std::vector<std::string> result;
+
+             while (iter != param_seq.end())
+             {
+                 if (*iter == delimiter)
+                 {
+                     result.push_back(std::string(current_begin, iter));
+                     current_begin = ++iter;
+                 }
+                 else
+                     ++iter;
+             }
+
+             if (current_begin != iter)
+             {
+                 result.push_back(std::string(current_begin, iter));
+             }
+
+             return result;
+         }
+
+         inline bool is_valid_token(std::string param_seq,
+                                    function_prototype_t& funcproto) const
+         {
+            // Determine return type
+            funcproto.return_type = default_return_type_;
+
+            if (param_seq.size() > 2)
+            {
+               if (':' == param_seq[1])
+               {
+                  // Note: Only overloaded igeneric functions can have return
+                  // type definitions.
+                  if (type_checker::e_overload != default_return_type_)
+                     return false;
+
+                  switch (param_seq[0])
+                  {
+                     case 'T' : funcproto.return_type = type_checker::e_numeric;
+                                break;
+
+                     case 'S' : funcproto.return_type = type_checker::e_string;
+                                break;
+
+                     default  : return false;
+                  }
+
+                  param_seq.erase(0,2);
+               }
+            }
+
+            if (
+                 (std::string::npos != param_seq.find("?*")) ||
+                 (std::string::npos != param_seq.find("**"))
+               )
+            {
+               return false;
+            }
+            else if (
+                      (std::string::npos == param_seq.find_first_not_of("STV*?|")) ||
+                      ("Z" == param_seq)
+                    )
+            {
+               funcproto.param_seq = param_seq;
+               return true;
+            }
+
+            return false;
+         }
+
+         void parse_function_prototypes(const std::string& func_prototypes)
+         {
+            if (func_prototypes.empty())
+               return;
+
+            std::vector<std::string> param_seq_list = split_param_seq(func_prototypes);
+
+            typedef std::map<std::string,std::size_t> param_seq_map_t;
+            param_seq_map_t param_seq_map;
+
+            for (std::size_t i = 0; i < param_seq_list.size(); ++i)
+            {
+               function_prototype_t func_proto;
+
+               if (!is_valid_token(param_seq_list[i], func_proto))
+               {
+                  invalid_state_ = false;
+
+                  parser_.
+                     set_error(
+                        make_error(parser_error::e_syntax,
+                                   parser_.current_token(),
+                                   "ERR119 - Invalid parameter sequence of '" + param_seq_list[i] +
+                                   "' for function: " + function_name_,
+                                   exprtk_error_location));
+                  return;
+               }
+
+               param_seq_map_t::const_iterator seq_itr = param_seq_map.find(param_seq_list[i]);
+
+               if (param_seq_map.end() != seq_itr)
+               {
+                  invalid_state_ = false;
+
+                  parser_.
+                     set_error(
+                        make_error(parser_error::e_syntax,
+                                   parser_.current_token(),
+                                   "ERR120 - Function '" + function_name_ + "' has a parameter sequence conflict between " +
+                                   "pseq_idx[" + details::to_str(seq_itr->second) + "] and" +
+                                   "pseq_idx[" + details::to_str(i) + "] " +
+                                   "param seq: " + param_seq_list[i],
+                                   exprtk_error_location));
+                  return;
+               }
+
+               function_definition_list_.push_back(func_proto);
+            }
+         }
+
+         type_checker(const type_checker&);
+         type_checker& operator=(const type_checker&);
+
+         bool invalid_state_;
+         parser_t& parser_;
+         std::string function_name_;
+         const return_type_t default_return_type_;
+         function_definition_list_t function_definition_list_;
+      };
+
+      inline expression_node_ptr parse_generic_function_call(igeneric_function<T>* function, const std::string& function_name)
+      {
+         std::vector<expression_node_ptr> arg_list;
+
+         scoped_vec_delete<expression_node_t> sdd((*this),arg_list);
+
+         next_token();
+
+         std::string param_type_list;
+
+         type_checker tc((*this), function_name, function->parameter_sequence, type_checker::e_string);
+
+         if (tc.invalid())
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR121 - Type checker instantiation failure for generic function: " + function_name,
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         if (token_is(token_t::e_lbracket))
+         {
+            if (token_is(token_t::e_rbracket))
+            {
+               if (
+                    !function->allow_zero_parameters() &&
+                    !tc       .allow_zero_parameters()
+                  )
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR122 - Zero parameter call to generic function: "
+                                + function_name + " not allowed",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+            }
+            else
+            {
+               for ( ; ; )
+               {
+                  expression_node_ptr arg = parse_expression();
+
+                  if (0 == arg)
+                     return error_node();
+
+                  if (is_ivector_node(arg))
+                     param_type_list += 'V';
+                  else if (is_generally_string_node(arg))
+                     param_type_list += 'S';
+                  else // Everything else is assumed to be a scalar returning expression
+                     param_type_list += 'T';
+
+                  arg_list.push_back(arg);
+
+                  if (token_is(token_t::e_rbracket))
+                     break;
+                  else if (!token_is(token_t::e_comma))
+                  {
+                     set_error(
+                        make_error(parser_error::e_syntax,
+                                   current_token(),
+                                   "ERR123 - Expected ',' for call to generic function: " + function_name,
+                                   exprtk_error_location));
+
+                     return error_node();
+                  }
+               }
+            }
+         }
+         else if (
+                   !function->parameter_sequence.empty() &&
+                   function->allow_zero_parameters    () &&
+                   !tc      .allow_zero_parameters    ()
+                 )
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR124 - Zero parameter call to generic function: "
+                          + function_name + " not allowed",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         std::size_t param_seq_index = 0;
+
+         if (
+              state_.type_check_enabled &&
+              !tc.verify(param_type_list, param_seq_index)
+            )
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR125 - Invalid input parameter sequence for call to generic function: " + function_name,
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         expression_node_ptr result = error_node();
+
+         if (tc.paramseq_count() <= 1)
+            result = expression_generator_
+                       .generic_function_call(function, arg_list);
+         else
+            result = expression_generator_
+                       .generic_function_call(function, arg_list, param_seq_index);
+
+         sdd.delete_ptr = (0 == result);
+
+         return result;
+      }
+
+      inline bool parse_igeneric_function_params(std::string& param_type_list,
+                                                 std::vector<expression_node_ptr>& arg_list,
+                                                 const std::string& function_name,
+                                                 igeneric_function<T>* function,
+                                                 const type_checker& tc)
+      {
+         if (token_is(token_t::e_lbracket))
+         {
+            if (token_is(token_t::e_rbracket))
+            {
+               if (
+                    !function->allow_zero_parameters() &&
+                    !tc       .allow_zero_parameters()
+                  )
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR126 - Zero parameter call to generic function: "
+                                + function_name + " not allowed",
+                                exprtk_error_location));
+
+                  return false;
+               }
+            }
+            else
+            {
+               for ( ; ; )
+               {
+                  expression_node_ptr arg = parse_expression();
+
+                  if (0 == arg)
+                     return false;
+
+                  if (is_ivector_node(arg))
+                     param_type_list += 'V';
+                  else if (is_generally_string_node(arg))
+                     param_type_list += 'S';
+                  else // Everything else is a scalar returning expression
+                     param_type_list += 'T';
+
+                  arg_list.push_back(arg);
+
+                  if (token_is(token_t::e_rbracket))
+                     break;
+                  else if (!token_is(token_t::e_comma))
+                  {
+                     set_error(
+                        make_error(parser_error::e_syntax,
+                                   current_token(),
+                                   "ERR127 - Expected ',' for call to string function: " + function_name,
+                                   exprtk_error_location));
+
+                     return false;
+                  }
+               }
+            }
+
+            return true;
+         }
+         else
+            return false;
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      inline expression_node_ptr parse_string_function_call(igeneric_function<T>* function, const std::string& function_name)
+      {
+         // Move pass the function name
+         next_token();
+
+         std::string param_type_list;
+
+         type_checker tc((*this), function_name, function->parameter_sequence, type_checker::e_string);
+
+         if (
+              (!function->parameter_sequence.empty()) &&
+              (0 == tc.paramseq_count())
+            )
+         {
+            return error_node();
+         }
+
+         std::vector<expression_node_ptr> arg_list;
+         scoped_vec_delete<expression_node_t> sdd((*this),arg_list);
+
+         if (!parse_igeneric_function_params(param_type_list, arg_list, function_name, function, tc))
+         {
+            return error_node();
+         }
+
+         std::size_t param_seq_index = 0;
+
+         if (!tc.verify(param_type_list, param_seq_index))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR128 - Invalid input parameter sequence for call to string function: " + function_name,
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         expression_node_ptr result = error_node();
+
+         if (tc.paramseq_count() <= 1)
+            result = expression_generator_
+                       .string_function_call(function, arg_list);
+         else
+            result = expression_generator_
+                       .string_function_call(function, arg_list, param_seq_index);
+
+         sdd.delete_ptr = (0 == result);
+
+         return result;
+      }
+
+      inline expression_node_ptr parse_overload_function_call(igeneric_function<T>* function, const std::string& function_name)
+      {
+         // Move pass the function name
+         next_token();
+
+         std::string param_type_list;
+
+         type_checker tc((*this), function_name, function->parameter_sequence, type_checker::e_overload);
+
+         if (
+              (!function->parameter_sequence.empty()) &&
+              (0 == tc.paramseq_count())
+            )
+         {
+            return error_node();
+         }
+
+         std::vector<expression_node_ptr> arg_list;
+         scoped_vec_delete<expression_node_t> sdd((*this),arg_list);
+
+         if (!parse_igeneric_function_params(param_type_list, arg_list, function_name, function, tc))
+         {
+            return error_node();
+         }
+
+         std::size_t param_seq_index = 0;
+
+         if (!tc.verify(param_type_list, param_seq_index))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR129 - Invalid input parameter sequence for call to overloaded function: " + function_name,
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         expression_node_ptr result = error_node();
+
+         if (type_checker::e_numeric == tc.return_type(param_seq_index))
+         {
+            if (tc.paramseq_count() <= 1)
+               result = expression_generator_
+                          .generic_function_call(function, arg_list);
+            else
+               result = expression_generator_
+                          .generic_function_call(function, arg_list, param_seq_index);
+         }
+         else if (type_checker::e_string == tc.return_type(param_seq_index))
+         {
+            if (tc.paramseq_count() <= 1)
+               result = expression_generator_
+                          .string_function_call(function, arg_list);
+            else
+               result = expression_generator_
+                          .string_function_call(function, arg_list, param_seq_index);
+         }
+         else
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR130 - Invalid return type for call to overloaded function: " + function_name,
+                          exprtk_error_location));
+         }
+
+         sdd.delete_ptr = (0 == result);
+         return result;
+      }
+      #endif
+
+      template <typename Type, std::size_t NumberOfParameters>
+      struct parse_special_function_impl
+      {
+         static inline expression_node_ptr process(parser<Type>& p, const details::operator_type opt_type, const std::string& sf_name)
+         {
+            expression_node_ptr branch[NumberOfParameters];
+            expression_node_ptr result = error_node();
+
+            std::fill_n(branch,NumberOfParameters,reinterpret_cast<expression_node_ptr>(0));
+
+            scoped_delete<expression_node_t,NumberOfParameters> sd(p,branch);
+
+            p.next_token();
+
+            if (!p.token_is(token_t::e_lbracket))
+            {
+               p.set_error(
+                    make_error(parser_error::e_syntax,
+                               p.current_token(),
+                               "ERR131 - Expected '(' for special function '" + sf_name + "'",
+                               exprtk_error_location));
+
+               return error_node();
+            }
+
+            for (std::size_t i = 0; i < NumberOfParameters; ++i)
+            {
+               branch[i] = p.parse_expression();
+
+               if (0 == branch[i])
+               {
+                  return p.error_node();
+               }
+               else if (i < (NumberOfParameters - 1))
+               {
+                  if (!p.token_is(token_t::e_comma))
+                  {
+                     p.set_error(
+                          make_error(parser_error::e_syntax,
+                                     p.current_token(),
+                                     "ERR132 - Expected ',' before next parameter of special function '" + sf_name + "'",
+                                     exprtk_error_location));
+
+                     return p.error_node();
+                  }
+               }
+            }
+
+            if (!p.token_is(token_t::e_rbracket))
+            {
+               p.set_error(
+                    make_error(parser_error::e_syntax,
+                               p.current_token(),
+                               "ERR133 - Invalid number of parameters for special function '" + sf_name + "'",
+                               exprtk_error_location));
+
+               return p.error_node();
+            }
+            else
+               result = p.expression_generator_.special_function(opt_type,branch);
+
+            sd.delete_ptr = (0 == result);
+
+            return result;
+         }
+      };
+
+      inline expression_node_ptr parse_special_function()
+      {
+         const std::string sf_name = current_token().value;
+
+         // Expect: $fDD(expr0,expr1,expr2) or $fDD(expr0,expr1,expr2,expr3)
+         if (
+              !details::is_digit(sf_name[2]) ||
+              !details::is_digit(sf_name[3])
+            )
+         {
+            set_error(
+               make_error(parser_error::e_token,
+                          current_token(),
+                          "ERR134 - Invalid special function[1]: " + sf_name,
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         const int id = (sf_name[2] - '0') * 10 +
+                        (sf_name[3] - '0');
+
+         if (id >= details::e_sffinal)
+         {
+            set_error(
+               make_error(parser_error::e_token,
+                          current_token(),
+                          "ERR135 - Invalid special function[2]: " + sf_name,
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         const int sf_3_to_4                   = details::e_sf48;
+         const details::operator_type opt_type = details::operator_type(id + 1000);
+         const std::size_t NumberOfParameters  = (id < (sf_3_to_4 - 1000)) ? 3U : 4U;
+
+         switch (NumberOfParameters)
+         {
+            case 3  : return parse_special_function_impl<T,3>::process((*this), opt_type, sf_name);
+            case 4  : return parse_special_function_impl<T,4>::process((*this), opt_type, sf_name);
+            default : return error_node();
+         }
+      }
+
+      inline expression_node_ptr parse_null_statement()
+      {
+         next_token();
+         return node_allocator_.allocate<details::null_node<T> >();
+      }
+
+      #ifndef exprtk_disable_break_continue
+      inline expression_node_ptr parse_break_statement()
+      {
+         if (state_.parsing_break_stmt)
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR136 - Break call within a break call is not allowed",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         scoped_bool_negator sbn(state_.parsing_break_stmt);
+
+         if (!brkcnt_list_.empty())
+         {
+            next_token();
+
+            brkcnt_list_.front() = true;
+
+            expression_node_ptr return_expr = error_node();
+
+            if (token_is(token_t::e_lsqrbracket))
+            {
+               if (0 == (return_expr = parse_expression()))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR137 - Failed to parse return expression for 'break' statement",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+               else if (!token_is(token_t::e_rsqrbracket))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR138 - Expected ']' at the completion of break's return expression",
+                                exprtk_error_location));
+
+                  free_node(node_allocator_,return_expr);
+
+                  return error_node();
+               }
+            }
+
+            state_.activate_side_effect("parse_break_statement()");
+
+            return node_allocator_.allocate<details::break_node<T> >(return_expr);
+         }
+         else
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR139 - Invalid use of 'break', allowed only in the scope of a loop",
+                          exprtk_error_location));
+         }
+
+         return error_node();
+      }
+
+      inline expression_node_ptr parse_continue_statement()
+      {
+         if (!brkcnt_list_.empty())
+         {
+            next_token();
+
+            brkcnt_list_.front() = true;
+            state_.activate_side_effect("parse_continue_statement()");
+
+            return node_allocator_.allocate<details::continue_node<T> >();
+         }
+         else
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR140 - Invalid use of 'continue', allowed only in the scope of a loop",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+      }
+      #endif
+
+      inline expression_node_ptr parse_define_vector_statement(const std::string& vec_name)
+      {
+         expression_node_ptr size_expr = error_node();
+
+         if (!token_is(token_t::e_lsqrbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR141 - Expected '[' as part of vector size definition",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (0 == (size_expr = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR142 - Failed to determine size of vector '" + vec_name + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!is_constant_node(size_expr))
+         {
+            free_node(node_allocator_,size_expr);
+
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR143 - Expected a literal number as size of vector '" + vec_name + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         const T vector_size = size_expr->value();
+
+         free_node(node_allocator_,size_expr);
+
+         const T max_vector_size = T(2000000000.0);
+
+         if (
+              (vector_size <= T(0)) ||
+              std::not_equal_to<T>()
+              (T(0),vector_size - details::numeric::trunc(vector_size)) ||
+              (vector_size > max_vector_size)
+            )
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR144 - Invalid vector size. Must be an integer in the range [0,2e9], size: " +
+                          details::to_str(details::numeric::to_int32(vector_size)),
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         std::vector<expression_node_ptr> vec_initilizer_list;
+
+         scoped_vec_delete<expression_node_t> svd((*this),vec_initilizer_list);
+
+         bool single_value_initialiser = false;
+         bool vec_to_vec_initialiser   = false;
+         bool null_initialisation      = false;
+
+         if (!token_is(token_t::e_rsqrbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR145 - Expected ']' as part of vector size definition",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!token_is(token_t::e_eof))
+         {
+            if (!token_is(token_t::e_assign))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR146 - Expected ':=' as part of vector definition",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+            else if (token_is(token_t::e_lsqrbracket))
+            {
+               expression_node_ptr initialiser = parse_expression();
+
+               if (0 == initialiser)
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR147 - Failed to parse single vector initialiser",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+
+               vec_initilizer_list.push_back(initialiser);
+
+               if (!token_is(token_t::e_rsqrbracket))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR148 - Expected ']' to close single value vector initialiser",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+
+               single_value_initialiser = true;
+            }
+            else if (!token_is(token_t::e_lcrlbracket))
+            {
+               expression_node_ptr initialiser = error_node();
+
+               // Is this a vector to vector assignment and initialisation?
+               if (token_t::e_symbol == current_token().type)
+               {
+                  // Is it a locally defined vector?
+                  scope_element& se = sem_.get_active_element(current_token().value);
+
+                  if (scope_element::e_vector == se.type)
+                  {
+                     if (0 != (initialiser = parse_expression()))
+                        vec_initilizer_list.push_back(initialiser);
+                     else
+                        return error_node();
+                  }
+                  // Are we dealing with a user defined vector?
+                  else if (symtab_store_.is_vector(current_token().value))
+                  {
+                     lodge_symbol(current_token().value, e_st_vector);
+
+                     if (0 != (initialiser = parse_expression()))
+                        vec_initilizer_list.push_back(initialiser);
+                     else
+                        return error_node();
+                  }
+                  // Are we dealing with a null initialisation vector definition?
+                  else if (token_is(token_t::e_symbol,"null"))
+                     null_initialisation = true;
+               }
+
+               if (!null_initialisation)
+               {
+                  if (0 == initialiser)
+                  {
+                     set_error(
+                        make_error(parser_error::e_syntax,
+                                   current_token(),
+                                   "ERR149 - Expected '{' as part of vector initialiser list",
+                                   exprtk_error_location));
+
+                     return error_node();
+                  }
+                  else
+                     vec_to_vec_initialiser = true;
+               }
+            }
+            else if (!token_is(token_t::e_rcrlbracket))
+            {
+               for ( ; ; )
+               {
+                  expression_node_ptr initialiser = parse_expression();
+
+                  if (0 == initialiser)
+                  {
+                     set_error(
+                        make_error(parser_error::e_syntax,
+                                   current_token(),
+                                   "ERR150 - Expected '{' as part of vector initialiser list",
+                                   exprtk_error_location));
+
+                     return error_node();
+                  }
+                  else
+                     vec_initilizer_list.push_back(initialiser);
+
+                  if (token_is(token_t::e_rcrlbracket))
+                     break;
+
+                  const bool is_next_close = peek_token_is(token_t::e_rcrlbracket);
+
+                  if (!token_is(token_t::e_comma) && is_next_close)
+                  {
+                     set_error(
+                        make_error(parser_error::e_syntax,
+                                   current_token(),
+                                   "ERR151 - Expected ',' between vector initialisers",
+                                   exprtk_error_location));
+
+                     return error_node();
+                  }
+
+                  if (token_is(token_t::e_rcrlbracket))
+                     break;
+               }
+            }
+
+            if (
+                 !token_is(token_t::e_rbracket   , prsrhlpr_t::e_hold) &&
+                 !token_is(token_t::e_rcrlbracket, prsrhlpr_t::e_hold) &&
+                 !token_is(token_t::e_rsqrbracket, prsrhlpr_t::e_hold)
+               )
+            {
+               if (!token_is(token_t::e_eof))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR152 - Expected ';' at end of vector definition",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+            }
+
+            if (vec_initilizer_list.size() > vector_size)
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR153 - Initialiser list larger than the number of elements in the vector: '" + vec_name + "'",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+         }
+
+         typename symbol_table_t::vector_holder_ptr vec_holder = typename symbol_table_t::vector_holder_ptr(0);
+
+         const std::size_t vec_size = static_cast<std::size_t>(details::numeric::to_int32(vector_size));
+
+         scope_element& se = sem_.get_element(vec_name);
+
+         if (se.name == vec_name)
+         {
+            if (se.active)
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR154 - Illegal redefinition of local vector: '" + vec_name + "'",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+            else if (
+                      (se.size == vec_size) &&
+                      (scope_element::e_vector == se.type)
+                    )
+            {
+               vec_holder = se.vec_node;
+               se.active  = true;
+               se.depth   = state_.scope_depth;
+               se.ref_count++;
+            }
+         }
+
+         if (0 == vec_holder)
+         {
+            scope_element nse;
+            nse.name      = vec_name;
+            nse.active    = true;
+            nse.ref_count = 1;
+            nse.type      = scope_element::e_vector;
+            nse.depth     = state_.scope_depth;
+            nse.size      = vec_size;
+            nse.data      = new T[vec_size];
+            nse.vec_node  = new typename scope_element::vector_holder_t((T*)(nse.data),nse.size);
+
+            if (!sem_.add_element(nse))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR155 - Failed to add new local vector '" + vec_name + "' to SEM",
+                             exprtk_error_location));
+
+               sem_.free_element(nse);
+
+               return error_node();
+            }
+
+            vec_holder = nse.vec_node;
+
+            exprtk_debug(("parse_define_vector_statement() - INFO - Added new local vector: %s[%d]\n",
+                          nse.name.c_str(),
+                          static_cast<int>(nse.size)));
+         }
+
+         state_.activate_side_effect("parse_define_vector_statement()");
+
+         lodge_symbol(vec_name, e_st_local_vector);
+
+         expression_node_ptr result = error_node();
+
+         if (null_initialisation)
+            result = expression_generator_(T(0.0));
+         else if (vec_to_vec_initialiser)
+         {
+            expression_node_ptr vec_node = node_allocator_.allocate<vector_node_t>(vec_holder);
+
+            result = expression_generator_(
+                        details::e_assign,
+                        vec_node,
+                        vec_initilizer_list[0]);
+         }
+         else
+            result = node_allocator_
+                        .allocate<details::vector_assignment_node<T> >(
+                           (*vec_holder)[0],
+                           vec_size,
+                           vec_initilizer_list,
+                           single_value_initialiser);
+
+         svd.delete_ptr = (0 == result);
+
+         return result;
+      }
+
+      #ifndef exprtk_disable_string_capabilities
+      inline expression_node_ptr parse_define_string_statement(const std::string& str_name, expression_node_ptr initialisation_expression)
+      {
+         stringvar_node_t* str_node = reinterpret_cast<stringvar_node_t*>(0);
+
+         scope_element& se = sem_.get_element(str_name);
+
+         if (se.name == str_name)
+         {
+            if (se.active)
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR156 - Illegal redefinition of local variable: '" + str_name + "'",
+                             exprtk_error_location));
+
+               free_node(node_allocator_,initialisation_expression);
+
+               return error_node();
+            }
+            else if (scope_element::e_string == se.type)
+            {
+               str_node  = se.str_node;
+               se.active = true;
+               se.depth  = state_.scope_depth;
+               se.ref_count++;
+            }
+         }
+
+         if (0 == str_node)
+         {
+            scope_element nse;
+            nse.name      = str_name;
+            nse.active    = true;
+            nse.ref_count = 1;
+            nse.type      = scope_element::e_string;
+            nse.depth     = state_.scope_depth;
+            nse.data      = new std::string;
+            nse.str_node  = new stringvar_node_t(*(std::string*)(nse.data));
+
+            if (!sem_.add_element(nse))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR157 - Failed to add new local string variable '" + str_name + "' to SEM",
+                             exprtk_error_location));
+
+               free_node(node_allocator_,initialisation_expression);
+
+               sem_.free_element(nse);
+
+               return error_node();
+            }
+
+            str_node = nse.str_node;
+
+            exprtk_debug(("parse_define_string_statement() - INFO - Added new local string variable: %s\n",nse.name.c_str()));
+         }
+
+         lodge_symbol(str_name, e_st_local_string);
+
+         state_.activate_side_effect("parse_define_string_statement()");
+
+         expression_node_ptr branch[2] = {0};
+
+         branch[0] = str_node;
+         branch[1] = initialisation_expression;
+
+         return expression_generator_(details::e_assign,branch);
+      }
+      #else
+      inline expression_node_ptr parse_define_string_statement(const std::string&, expression_node_ptr)
+      {
+         return error_node();
+      }
+      #endif
+
+      inline bool local_variable_is_shadowed(const std::string& symbol)
+      {
+         const scope_element& se = sem_.get_element(symbol);
+         return (se.name == symbol) && se.active;
+      }
+
+      inline expression_node_ptr parse_define_var_statement()
+      {
+         if (settings_.vardef_disabled())
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR158 - Illegal variable definition",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!details::imatch(current_token().value,"var"))
+         {
+            return error_node();
+         }
+         else
+            next_token();
+
+         const std::string var_name = current_token().value;
+
+         expression_node_ptr initialisation_expression = error_node();
+
+         if (!token_is(token_t::e_symbol))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR159 - Expected a symbol for variable definition",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (details::is_reserved_symbol(var_name))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR160 - Illegal redefinition of reserved keyword: '" + var_name + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (symtab_store_.symbol_exists(var_name))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR161 - Illegal redefinition of variable '" + var_name + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (local_variable_is_shadowed(var_name))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR162 - Illegal redefinition of local variable: '" + var_name + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (token_is(token_t::e_lsqrbracket,prsrhlpr_t::e_hold))
+         {
+            return parse_define_vector_statement(var_name);
+         }
+         else if (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold))
+         {
+            return parse_uninitialised_var_statement(var_name);
+         }
+         else if (token_is(token_t::e_assign))
+         {
+            if (0 == (initialisation_expression = parse_expression()))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR163 - Failed to parse initialisation expression",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+         }
+
+         if (
+              !token_is(token_t::e_rbracket   , prsrhlpr_t::e_hold) &&
+              !token_is(token_t::e_rcrlbracket, prsrhlpr_t::e_hold) &&
+              !token_is(token_t::e_rsqrbracket, prsrhlpr_t::e_hold)
+            )
+         {
+            if (!token_is(token_t::e_eof,prsrhlpr_t::e_hold))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR164 - Expected ';' after variable definition",
+                             exprtk_error_location));
+
+               free_node(node_allocator_,initialisation_expression);
+
+               return error_node();
+            }
+         }
+
+         if (
+              (0 != initialisation_expression) &&
+              details::is_generally_string_node(initialisation_expression)
+            )
+         {
+            return parse_define_string_statement(var_name,initialisation_expression);
+         }
+
+         expression_node_ptr var_node = reinterpret_cast<expression_node_ptr>(0);
+
+         scope_element& se = sem_.get_element(var_name);
+
+         if (se.name == var_name)
+         {
+            if (se.active)
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR165 - Illegal redefinition of local variable: '" + var_name + "'",
+                             exprtk_error_location));
+
+               free_node(node_allocator_, initialisation_expression);
+
+               return error_node();
+            }
+            else if (scope_element::e_variable == se.type)
+            {
+               var_node  = se.var_node;
+               se.active = true;
+               se.depth  = state_.scope_depth;
+               se.ref_count++;
+            }
+         }
+
+         if (0 == var_node)
+         {
+            scope_element nse;
+            nse.name      = var_name;
+            nse.active    = true;
+            nse.ref_count = 1;
+            nse.type      = scope_element::e_variable;
+            nse.depth     = state_.scope_depth;
+            nse.data      = new T(T(0));
+            nse.var_node  = node_allocator_.allocate<variable_node_t>(*(T*)(nse.data));
+
+            if (!sem_.add_element(nse))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR166 - Failed to add new local variable '" + var_name + "' to SEM",
+                             exprtk_error_location));
+
+               free_node(node_allocator_, initialisation_expression);
+
+               sem_.free_element(nse);
+
+               return error_node();
+            }
+
+            var_node = nse.var_node;
+
+            exprtk_debug(("parse_define_var_statement() - INFO - Added new local variable: %s\n",nse.name.c_str()));
+         }
+
+         state_.activate_side_effect("parse_define_var_statement()");
+
+         lodge_symbol(var_name, e_st_local_variable);
+
+         expression_node_ptr branch[2] = {0};
+
+         branch[0] = var_node;
+         branch[1] = initialisation_expression ? initialisation_expression : expression_generator_(T(0));
+
+         return expression_generator_(details::e_assign,branch);
+      }
+
+      inline expression_node_ptr parse_uninitialised_var_statement(const std::string& var_name)
+      {
+         if (
+              !token_is(token_t::e_lcrlbracket) ||
+              !token_is(token_t::e_rcrlbracket)
+            )
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR167 - Expected a '{}' for uninitialised var definition",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!token_is(token_t::e_eof,prsrhlpr_t::e_hold))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR168 - Expected ';' after uninitialised variable definition",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         expression_node_ptr var_node = reinterpret_cast<expression_node_ptr>(0);
+
+         scope_element& se = sem_.get_element(var_name);
+
+         if (se.name == var_name)
+         {
+            if (se.active)
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR169 - Illegal redefinition of local variable: '" + var_name + "'",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+            else if (scope_element::e_variable == se.type)
+            {
+               var_node  = se.var_node;
+               se.active = true;
+               se.ref_count++;
+            }
+         }
+
+         if (0 == var_node)
+         {
+            scope_element nse;
+            nse.name      = var_name;
+            nse.active    = true;
+            nse.ref_count = 1;
+            nse.type      = scope_element::e_variable;
+            nse.depth     = state_.scope_depth;
+            nse.ip_index  = sem_.next_ip_index();
+            nse.data      = new T(T(0));
+            nse.var_node  = node_allocator_.allocate<variable_node_t>(*(T*)(nse.data));
+
+            if (!sem_.add_element(nse))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR170 - Failed to add new local variable '" + var_name + "' to SEM",
+                             exprtk_error_location));
+
+               sem_.free_element(nse);
+
+               return error_node();
+            }
+
+            exprtk_debug(("parse_uninitialised_var_statement() - INFO - Added new local variable: %s\n",
+                          nse.name.c_str()));
+         }
+
+         lodge_symbol(var_name, e_st_local_variable);
+
+         state_.activate_side_effect("parse_uninitialised_var_statement()");
+
+         return expression_generator_(T(0));
+      }
+
+      inline expression_node_ptr parse_swap_statement()
+      {
+         if (!details::imatch(current_token().value,"swap"))
+         {
+            return error_node();
+         }
+         else
+            next_token();
+
+         if (!token_is(token_t::e_lbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR171 - Expected '(' at start of swap statement",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         expression_node_ptr variable0 = error_node();
+         expression_node_ptr variable1 = error_node();
+
+         bool variable0_generated = false;
+         bool variable1_generated = false;
+
+         const std::string var0_name = current_token().value;
+
+         if (!token_is(token_t::e_symbol,prsrhlpr_t::e_hold))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR172 - Expected a symbol for variable or vector element definition",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (peek_token_is(token_t::e_lsqrbracket))
+         {
+            if (0 == (variable0 = parse_vector()))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR173 - First parameter to swap is an invalid vector element: '" + var0_name + "'",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+
+            variable0_generated = true;
+         }
+         else
+         {
+            if (symtab_store_.is_variable(var0_name))
+            {
+               variable0 = symtab_store_.get_variable(var0_name);
+            }
+
+            scope_element& se = sem_.get_element(var0_name);
+
+            if (
+                 (se.active)            &&
+                 (se.name == var0_name) &&
+                 (scope_element::e_variable == se.type)
+               )
+            {
+               variable0 = se.var_node;
+            }
+
+            lodge_symbol(var0_name, e_st_variable);
+
+            if (0 == variable0)
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR174 - First parameter to swap is an invalid variable: '" + var0_name + "'",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+            else
+               next_token();
+         }
+
+         if (!token_is(token_t::e_comma))
+         {
+            set_error(
+                make_error(parser_error::e_syntax,
+                           current_token(),
+                           "ERR175 - Expected ',' between parameters to swap",
+                           exprtk_error_location));
+
+            if (variable0_generated)
+            {
+               free_node(node_allocator_,variable0);
+            }
+
+            return error_node();
+         }
+
+         const std::string var1_name = current_token().value;
+
+         if (!token_is(token_t::e_symbol,prsrhlpr_t::e_hold))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR176 - Expected a symbol for variable or vector element definition",
+                          exprtk_error_location));
+
+            if (variable0_generated)
+            {
+               free_node(node_allocator_,variable0);
+            }
+
+            return error_node();
+         }
+         else if (peek_token_is(token_t::e_lsqrbracket))
+         {
+            if (0 == (variable1 = parse_vector()))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR177 - Second parameter to swap is an invalid vector element: '" + var1_name + "'",
+                             exprtk_error_location));
+
+               if (variable0_generated)
+               {
+                  free_node(node_allocator_,variable0);
+               }
+
+               return error_node();
+            }
+
+            variable1_generated = true;
+         }
+         else
+         {
+            if (symtab_store_.is_variable(var1_name))
+            {
+               variable1 = symtab_store_.get_variable(var1_name);
+            }
+
+            scope_element& se = sem_.get_element(var1_name);
+
+            if (
+                 (se.active) &&
+                 (se.name == var1_name) &&
+                 (scope_element::e_variable == se.type)
+               )
+            {
+               variable1 = se.var_node;
+            }
+
+            lodge_symbol(var1_name, e_st_variable);
+
+            if (0 == variable1)
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR178 - Second parameter to swap is an invalid variable: '" + var1_name + "'",
+                             exprtk_error_location));
+
+               if (variable0_generated)
+               {
+                  free_node(node_allocator_,variable0);
+               }
+
+               return error_node();
+            }
+            else
+               next_token();
+         }
+
+         if (!token_is(token_t::e_rbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR179 - Expected ')' at end of swap statement",
+                          exprtk_error_location));
+
+            if (variable0_generated)
+            {
+               free_node(node_allocator_,variable0);
+            }
+
+            if (variable1_generated)
+            {
+               free_node(node_allocator_,variable1);
+            }
+
+            return error_node();
+         }
+
+         typedef details::variable_node<T>* variable_node_ptr;
+
+         variable_node_ptr v0 = variable_node_ptr(0);
+         variable_node_ptr v1 = variable_node_ptr(0);
+
+         expression_node_ptr result = error_node();
+
+         if (
+              (0 != (v0 = dynamic_cast<variable_node_ptr>(variable0))) &&
+              (0 != (v1 = dynamic_cast<variable_node_ptr>(variable1)))
+            )
+         {
+            result = node_allocator_.allocate<details::swap_node<T> >(v0, v1);
+
+            if (variable0_generated)
+            {
+               free_node(node_allocator_,variable0);
+            }
+
+            if (variable1_generated)
+            {
+               free_node(node_allocator_,variable1);
+            }
+         }
+         else
+            result = node_allocator_.allocate<details::swap_generic_node<T> >
+                        (variable0, variable1);
+
+         state_.activate_side_effect("parse_swap_statement()");
+
+         return result;
+      }
+
+      #ifndef exprtk_disable_return_statement
+      inline expression_node_ptr parse_return_statement()
+      {
+         if (state_.parsing_return_stmt)
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR180 - Return call within a return call is not allowed",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         scoped_bool_negator sbn(state_.parsing_return_stmt);
+
+         std::vector<expression_node_ptr> arg_list;
+
+         scoped_vec_delete<expression_node_t> sdd((*this),arg_list);
+
+         if (!details::imatch(current_token().value,"return"))
+         {
+            return error_node();
+         }
+         else
+            next_token();
+
+         if (!token_is(token_t::e_lsqrbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR181 - Expected '[' at start of return statement",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!token_is(token_t::e_rsqrbracket))
+         {
+            for ( ; ; )
+            {
+               expression_node_ptr arg = parse_expression();
+
+               if (0 == arg)
+                  return error_node();
+
+               arg_list.push_back(arg);
+
+               if (token_is(token_t::e_rsqrbracket))
+                  break;
+               else if (!token_is(token_t::e_comma))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR182 - Expected ',' between values during call to return",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+            }
+         }
+         else if (settings_.zero_return_disabled())
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR183 - Zero parameter return statement not allowed",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         const lexer::token prev_token = current_token();
+
+         if (token_is(token_t::e_rsqrbracket))
+         {
+            if (!arg_list.empty())
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             prev_token,
+                             "ERR184 - Invalid ']' found during return call",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+         }
+
+         std::string ret_param_type_list;
+
+         for (std::size_t i = 0; i < arg_list.size(); ++i)
+         {
+            if (0 == arg_list[i])
+               return error_node();
+            else if (is_ivector_node(arg_list[i]))
+               ret_param_type_list += 'V';
+            else if (is_generally_string_node(arg_list[i]))
+               ret_param_type_list += 'S';
+            else
+               ret_param_type_list += 'T';
+         }
+
+         dec_.retparam_list_.push_back(ret_param_type_list);
+
+         expression_node_ptr result = expression_generator_.return_call(arg_list);
+
+         sdd.delete_ptr = (0 == result);
+
+         state_.return_stmt_present = true;
+
+         state_.activate_side_effect("parse_return_statement()");
+
+         return result;
+      }
+      #else
+      inline expression_node_ptr parse_return_statement()
+      {
+         return error_node();
+      }
+      #endif
+
+      inline bool post_variable_process(const std::string& symbol)
+      {
+         if (
+              peek_token_is(token_t::e_lbracket   ) ||
+              peek_token_is(token_t::e_lcrlbracket) ||
+              peek_token_is(token_t::e_lsqrbracket)
+            )
+         {
+            if (!settings_.commutative_check_enabled())
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR185 - Invalid sequence of variable '"+ symbol + "' and bracket",
+                             exprtk_error_location));
+
+               return false;
+            }
+
+            lexer().insert_front(token_t::e_mul);
+         }
+
+         return true;
+      }
+
+      inline bool post_bracket_process(const typename token_t::token_type& token, expression_node_ptr& branch)
+      {
+         bool implied_mul = false;
+
+         if (is_generally_string_node(branch))
+            return true;
+
+         const lexer::parser_helper::token_advance_mode hold = prsrhlpr_t::e_hold;
+
+         switch (token)
+         {
+            case token_t::e_lcrlbracket : implied_mul = token_is(token_t::e_lbracket   ,hold) ||
+                                                        token_is(token_t::e_lcrlbracket,hold) ||
+                                                        token_is(token_t::e_lsqrbracket,hold) ;
+                                          break;
+
+            case token_t::e_lbracket    : implied_mul = token_is(token_t::e_lbracket   ,hold) ||
+                                                        token_is(token_t::e_lcrlbracket,hold) ||
+                                                        token_is(token_t::e_lsqrbracket,hold) ;
+                                          break;
+
+            case token_t::e_lsqrbracket : implied_mul = token_is(token_t::e_lbracket   ,hold) ||
+                                                        token_is(token_t::e_lcrlbracket,hold) ||
+                                                        token_is(token_t::e_lsqrbracket,hold) ;
+                                          break;
+
+            default                     : return true;
+         }
+
+         if (implied_mul)
+         {
+            if (!settings_.commutative_check_enabled())
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR186 - Invalid sequence of brackets",
+                             exprtk_error_location));
+
+               return false;
+            }
+            else if (token_t::e_eof != current_token().type)
+            {
+               lexer().insert_front(current_token().type);
+               lexer().insert_front(token_t::e_mul);
+               next_token();
+            }
+         }
+
+         return true;
+      }
+
+      inline expression_node_ptr parse_symtab_symbol()
+      {
+         const std::string symbol = current_token().value;
+
+         // Are we dealing with a variable or a special constant?
+         expression_node_ptr variable = symtab_store_.get_variable(symbol);
+
+         if (variable)
+         {
+            if (symtab_store_.is_constant_node(symbol))
+            {
+               variable = expression_generator_(variable->value());
+            }
+
+            if (!post_variable_process(symbol))
+               return error_node();
+
+            lodge_symbol(symbol, e_st_variable);
+            next_token();
+
+            return variable;
+         }
+
+         // Are we dealing with a locally defined variable, vector or string?
+         if (!sem_.empty())
+         {
+            scope_element& se = sem_.get_active_element(symbol);
+
+            if (se.active && details::imatch(se.name, symbol))
+            {
+               if (scope_element::e_variable == se.type)
+               {
+                  se.active = true;
+                  lodge_symbol(symbol, e_st_local_variable);
+
+                  if (!post_variable_process(symbol))
+                     return error_node();
+
+                  next_token();
+
+                  return se.var_node;
+               }
+               else if (scope_element::e_vector == se.type)
+               {
+                  return parse_vector();
+               }
+               #ifndef exprtk_disable_string_capabilities
+               else if (scope_element::e_string == se.type)
+               {
+                  return parse_string();
+               }
+               #endif
+            }
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         // Are we dealing with a string variable?
+         if (symtab_store_.is_stringvar(symbol))
+         {
+            return parse_string();
+         }
+         #endif
+
+         {
+            // Are we dealing with a function?
+            ifunction<T>* function = symtab_store_.get_function(symbol);
+
+            if (function)
+            {
+               lodge_symbol(symbol, e_st_function);
+
+               expression_node_ptr func_node =
+                                      parse_function_invocation(function,symbol);
+
+               if (func_node)
+                  return func_node;
+               else
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR187 - Failed to generate node for function: '" + symbol + "'",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+            }
+         }
+
+         {
+            // Are we dealing with a vararg function?
+            ivararg_function<T>* vararg_function = symtab_store_.get_vararg_function(symbol);
+
+            if (vararg_function)
+            {
+               lodge_symbol(symbol, e_st_function);
+
+               expression_node_ptr vararg_func_node =
+                                      parse_vararg_function_call(vararg_function, symbol);
+
+               if (vararg_func_node)
+                  return vararg_func_node;
+               else
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR188 - Failed to generate node for vararg function: '" + symbol + "'",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+            }
+         }
+
+         {
+            // Are we dealing with a vararg generic function?
+            igeneric_function<T>* generic_function = symtab_store_.get_generic_function(symbol);
+
+            if (generic_function)
+            {
+               lodge_symbol(symbol, e_st_function);
+
+               expression_node_ptr genericfunc_node =
+                                      parse_generic_function_call(generic_function, symbol);
+
+               if (genericfunc_node)
+                  return genericfunc_node;
+               else
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR189 - Failed to generate node for generic function: '" + symbol + "'",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+            }
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         {
+            // Are we dealing with a vararg string returning function?
+            igeneric_function<T>* string_function = symtab_store_.get_string_function(symbol);
+
+            if (string_function)
+            {
+               lodge_symbol(symbol, e_st_function);
+
+               expression_node_ptr stringfunc_node =
+                                      parse_string_function_call(string_function, symbol);
+
+               if (stringfunc_node)
+                  return stringfunc_node;
+               else
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR190 - Failed to generate node for string function: '" + symbol + "'",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+            }
+         }
+
+         {
+            // Are we dealing with a vararg overloaded scalar/string returning function?
+            igeneric_function<T>* overload_function = symtab_store_.get_overload_function(symbol);
+
+            if (overload_function)
+            {
+               lodge_symbol(symbol, e_st_function);
+
+               expression_node_ptr overloadfunc_node =
+                                      parse_overload_function_call(overload_function, symbol);
+
+               if (overloadfunc_node)
+                  return overloadfunc_node;
+               else
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR191 - Failed to generate node for overload function: '" + symbol + "'",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+            }
+         }
+         #endif
+
+         // Are we dealing with a vector?
+         if (symtab_store_.is_vector(symbol))
+         {
+            lodge_symbol(symbol, e_st_vector);
+            return parse_vector();
+         }
+
+         if (details::is_reserved_symbol(symbol))
+         {
+               if (
+                    settings_.function_enabled(symbol) ||
+                    !details::is_base_function(symbol)
+                  )
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR192 - Invalid use of reserved symbol '" + symbol + "'",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+         }
+
+         // Should we handle unknown symbols?
+         if (resolve_unknown_symbol_ && unknown_symbol_resolver_)
+         {
+            if (!(settings_.rsrvd_sym_usr_disabled() && details::is_reserved_symbol(symbol)))
+            {
+               symbol_table_t& symtab = symtab_store_.get_symbol_table();
+
+               std::string error_message;
+
+               if (unknown_symbol_resolver::e_usrmode_default == unknown_symbol_resolver_->mode)
+               {
+                  T default_value = T(0);
+
+                  typename unknown_symbol_resolver::usr_symbol_type usr_symbol_type = unknown_symbol_resolver::e_usr_unknown_type;
+
+                  if (unknown_symbol_resolver_->process(symbol, usr_symbol_type, default_value, error_message))
+                  {
+                     bool create_result = false;
+
+                     switch (usr_symbol_type)
+                     {
+                        case unknown_symbol_resolver::e_usr_variable_type : create_result = symtab.create_variable(symbol, default_value);
+                                                                            break;
+
+                        case unknown_symbol_resolver::e_usr_constant_type : create_result = symtab.add_constant(symbol, default_value);
+                                                                            break;
+
+                        default                                           : create_result = false;
+                     }
+
+                     if (create_result)
+                     {
+                        expression_node_ptr var = symtab_store_.get_variable(symbol);
+
+                        if (var)
+                        {
+                           if (symtab_store_.is_constant_node(symbol))
+                           {
+                              var = expression_generator_(var->value());
+                           }
+
+                           lodge_symbol(symbol, e_st_variable);
+
+                           if (!post_variable_process(symbol))
+                              return error_node();
+
+                           next_token();
+
+                           return var;
+                        }
+                     }
+                  }
+
+                  set_error(
+                     make_error(parser_error::e_symtab,
+                                current_token(),
+                                "ERR193 - Failed to create variable: '" + symbol + "'" +
+                                (error_message.empty() ? "" : " - " + error_message),
+                                exprtk_error_location));
+
+               }
+               else if (unknown_symbol_resolver::e_usrmode_extended == unknown_symbol_resolver_->mode)
+               {
+                  if (unknown_symbol_resolver_->process(symbol, symtab, error_message))
+                  {
+                     expression_node_ptr result = parse_symtab_symbol();
+
+                     if (result)
+                     {
+                        return result;
+                     }
+                  }
+
+                  set_error(
+                     make_error(parser_error::e_symtab,
+                                current_token(),
+                                "ERR194 - Failed to resolve symbol: '" + symbol + "'" +
+                                (error_message.empty() ? "" : " - " + error_message),
+                                exprtk_error_location));
+               }
+
+               return error_node();
+            }
+         }
+
+         set_error(
+            make_error(parser_error::e_syntax,
+                       current_token(),
+                       "ERR195 - Undefined symbol: '" + symbol + "'",
+                       exprtk_error_location));
+
+         return error_node();
+      }
+
+      inline expression_node_ptr parse_symbol()
+      {
+         static const std::string symbol_if       = "if"      ;
+         static const std::string symbol_while    = "while"   ;
+         static const std::string symbol_repeat   = "repeat"  ;
+         static const std::string symbol_for      = "for"     ;
+         static const std::string symbol_switch   = "switch"  ;
+         static const std::string symbol_null     = "null"    ;
+         static const std::string symbol_break    = "break"   ;
+         static const std::string symbol_continue = "continue";
+         static const std::string symbol_var      = "var"     ;
+         static const std::string symbol_swap     = "swap"    ;
+         static const std::string symbol_return   = "return"  ;
+         static const std::string symbol_not      = "not"     ;
+
+         if (valid_vararg_operation(current_token().value))
+         {
+            return parse_vararg_function();
+         }
+         else if (details::imatch(current_token().value, symbol_not))
+         {
+            return parse_not_statement();
+         }
+         else if (valid_base_operation(current_token().value))
+         {
+            return parse_base_operation();
+         }
+         else if (
+                   details::imatch(current_token().value, symbol_if) &&
+                   settings_.control_struct_enabled(current_token().value)
+                 )
+         {
+            return parse_conditional_statement();
+         }
+         else if (
+                   details::imatch(current_token().value, symbol_while) &&
+                   settings_.control_struct_enabled(current_token().value)
+                 )
+         {
+            return parse_while_loop();
+         }
+         else if (
+                   details::imatch(current_token().value, symbol_repeat) &&
+                   settings_.control_struct_enabled(current_token().value)
+                 )
+         {
+            return parse_repeat_until_loop();
+         }
+         else if (
+                   details::imatch(current_token().value, symbol_for) &&
+                   settings_.control_struct_enabled(current_token().value)
+                 )
+         {
+            return parse_for_loop();
+         }
+         else if (
+                   details::imatch(current_token().value, symbol_switch) &&
+                   settings_.control_struct_enabled(current_token().value)
+                 )
+         {
+            return parse_switch_statement();
+         }
+         else if (details::is_valid_sf_symbol(current_token().value))
+         {
+            return parse_special_function();
+         }
+         else if (details::imatch(current_token().value, symbol_null))
+         {
+            return parse_null_statement();
+         }
+         #ifndef exprtk_disable_break_continue
+         else if (details::imatch(current_token().value, symbol_break))
+         {
+            return parse_break_statement();
+         }
+         else if (details::imatch(current_token().value, symbol_continue))
+         {
+            return parse_continue_statement();
+         }
+         #endif
+         else if (details::imatch(current_token().value, symbol_var))
+         {
+            return parse_define_var_statement();
+         }
+         else if (details::imatch(current_token().value, symbol_swap))
+         {
+            return parse_swap_statement();
+         }
+         #ifndef exprtk_disable_return_statement
+         else if (
+                   details::imatch(current_token().value, symbol_return) &&
+                   settings_.control_struct_enabled(current_token().value)
+                 )
+         {
+            return parse_return_statement();
+         }
+         #endif
+         else if (symtab_store_.valid() || !sem_.empty())
+         {
+            return parse_symtab_symbol();
+         }
+         else
+         {
+            set_error(
+               make_error(parser_error::e_symtab,
+                          current_token(),
+                          "ERR196 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token().value,
+                          exprtk_error_location));
+
+            return error_node();
+         }
+      }
+
+      inline expression_node_ptr parse_branch(precedence_level precedence = e_level00)
+      {
+         expression_node_ptr branch = error_node();
+
+         if (token_t::e_number == current_token().type)
+         {
+            T numeric_value = T(0);
+
+            if (details::string_to_real(current_token().value, numeric_value))
+            {
+               expression_node_ptr literal_exp = expression_generator_(numeric_value);
+
+               if (0 == literal_exp)
+               {
+                  set_error(
+                     make_error(parser_error::e_numeric,
+                                current_token(),
+                                "ERR197 - Failed generate node for scalar: '" + current_token().value + "'",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+
+               next_token();
+               branch = literal_exp;
+            }
+            else
+            {
+               set_error(
+                  make_error(parser_error::e_numeric,
+                             current_token(),
+                             "ERR198 - Failed to convert '" + current_token().value + "' to a number",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+         }
+         else if (token_t::e_symbol == current_token().type)
+         {
+            branch = parse_symbol();
+         }
+         #ifndef exprtk_disable_string_capabilities
+         else if (token_t::e_string == current_token().type)
+         {
+            branch = parse_const_string();
+         }
+         #endif
+         else if (token_t::e_lbracket == current_token().type)
+         {
+            next_token();
+
+            if (0 == (branch = parse_expression()))
+               return error_node();
+            else if (!token_is(token_t::e_rbracket))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR199 - Expected ')' instead of: '" + current_token().value + "'",
+                             exprtk_error_location));
+
+               free_node(node_allocator_,branch);
+
+               return error_node();
+            }
+            else if (!post_bracket_process(token_t::e_lbracket,branch))
+            {
+               free_node(node_allocator_,branch);
+
+               return error_node();
+            }
+         }
+         else if (token_t::e_lsqrbracket == current_token().type)
+         {
+            next_token();
+
+            if (0 == (branch = parse_expression()))
+               return error_node();
+            else if (!token_is(token_t::e_rsqrbracket))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR200 - Expected ']' instead of: '" + current_token().value + "'",
+                             exprtk_error_location));
+
+               free_node(node_allocator_,branch);
+
+               return error_node();
+            }
+            else if (!post_bracket_process(token_t::e_lsqrbracket,branch))
+            {
+               free_node(node_allocator_,branch);
+
+               return error_node();
+            }
+         }
+         else if (token_t::e_lcrlbracket == current_token().type)
+         {
+            next_token();
+
+            if (0 == (branch = parse_expression()))
+               return error_node();
+            else if (!token_is(token_t::e_rcrlbracket))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR201 - Expected '}' instead of: '" + current_token().value + "'",
+                             exprtk_error_location));
+
+               free_node(node_allocator_,branch);
+
+               return error_node();
+            }
+            else if (!post_bracket_process(token_t::e_lcrlbracket,branch))
+            {
+               free_node(node_allocator_,branch);
+
+               return error_node();
+            }
+         }
+         else if (token_t::e_sub == current_token().type)
+         {
+            next_token();
+            branch = parse_expression(e_level11);
+
+            if (
+                 branch &&
+                 !(
+                    details::is_neg_unary_node    (branch) &&
+                    simplify_unary_negation_branch(branch)
+                  )
+               )
+            {
+               branch = expression_generator_(details::e_neg,branch);
+            }
+         }
+         else if (token_t::e_add == current_token().type)
+         {
+            next_token();
+            branch = parse_expression(e_level13);
+         }
+         else if (token_t::e_eof == current_token().type)
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR202 - Premature end of expression[1]",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR203 - Premature end of expression[2]",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         if (
+              branch                    &&
+              (e_level00 == precedence) &&
+              token_is(token_t::e_ternary,prsrhlpr_t::e_hold)
+            )
+         {
+            branch = parse_ternary_conditional_statement(branch);
+         }
+
+         parse_pending_string_rangesize(branch);
+
+         return branch;
+      }
+
+      template <typename Type>
+      class expression_generator
+      {
+      public:
+
+         typedef details::expression_node<Type>* expression_node_ptr;
+         typedef expression_node_ptr (*synthesize_functor_t)(expression_generator<T>&, const details::operator_type& operation, expression_node_ptr (&branch)[2]);
+         typedef std::map<std::string,synthesize_functor_t> synthesize_map_t;
+         typedef typename exprtk::parser<Type> parser_t;
+         typedef const Type& vtype;
+         typedef const Type  ctype;
+
+         inline void init_synthesize_map()
+         {
+            #ifndef exprtk_disable_enhanced_features
+            synthesize_map_["(v)o(v)"] = synthesize_vov_expression::process;
+            synthesize_map_["(c)o(v)"] = synthesize_cov_expression::process;
+            synthesize_map_["(v)o(c)"] = synthesize_voc_expression::process;
+
+            #define register_synthezier(S)                      \
+            synthesize_map_[S ::node_type::id()] = S ::process; \
+
+            register_synthezier(synthesize_vovov_expression0)
+            register_synthezier(synthesize_vovov_expression1)
+            register_synthezier(synthesize_vovoc_expression0)
+            register_synthezier(synthesize_vovoc_expression1)
+            register_synthezier(synthesize_vocov_expression0)
+            register_synthezier(synthesize_vocov_expression1)
+            register_synthezier(synthesize_covov_expression0)
+            register_synthezier(synthesize_covov_expression1)
+            register_synthezier(synthesize_covoc_expression0)
+            register_synthezier(synthesize_covoc_expression1)
+            register_synthezier(synthesize_cocov_expression1)
+            register_synthezier(synthesize_vococ_expression0)
+
+            register_synthezier(synthesize_vovovov_expression0)
+            register_synthezier(synthesize_vovovoc_expression0)
+            register_synthezier(synthesize_vovocov_expression0)
+            register_synthezier(synthesize_vocovov_expression0)
+            register_synthezier(synthesize_covovov_expression0)
+            register_synthezier(synthesize_covocov_expression0)
+            register_synthezier(synthesize_vocovoc_expression0)
+            register_synthezier(synthesize_covovoc_expression0)
+            register_synthezier(synthesize_vococov_expression0)
+
+            register_synthezier(synthesize_vovovov_expression1)
+            register_synthezier(synthesize_vovovoc_expression1)
+            register_synthezier(synthesize_vovocov_expression1)
+            register_synthezier(synthesize_vocovov_expression1)
+            register_synthezier(synthesize_covovov_expression1)
+            register_synthezier(synthesize_covocov_expression1)
+            register_synthezier(synthesize_vocovoc_expression1)
+            register_synthezier(synthesize_covovoc_expression1)
+            register_synthezier(synthesize_vococov_expression1)
+
+            register_synthezier(synthesize_vovovov_expression2)
+            register_synthezier(synthesize_vovovoc_expression2)
+            register_synthezier(synthesize_vovocov_expression2)
+            register_synthezier(synthesize_vocovov_expression2)
+            register_synthezier(synthesize_covovov_expression2)
+            register_synthezier(synthesize_covocov_expression2)
+            register_synthezier(synthesize_vocovoc_expression2)
+            register_synthezier(synthesize_covovoc_expression2)
+
+            register_synthezier(synthesize_vovovov_expression3)
+            register_synthezier(synthesize_vovovoc_expression3)
+            register_synthezier(synthesize_vovocov_expression3)
+            register_synthezier(synthesize_vocovov_expression3)
+            register_synthezier(synthesize_covovov_expression3)
+            register_synthezier(synthesize_covocov_expression3)
+            register_synthezier(synthesize_vocovoc_expression3)
+            register_synthezier(synthesize_covovoc_expression3)
+            register_synthezier(synthesize_vococov_expression3)
+
+            register_synthezier(synthesize_vovovov_expression4)
+            register_synthezier(synthesize_vovovoc_expression4)
+            register_synthezier(synthesize_vovocov_expression4)
+            register_synthezier(synthesize_vocovov_expression4)
+            register_synthezier(synthesize_covovov_expression4)
+            register_synthezier(synthesize_covocov_expression4)
+            register_synthezier(synthesize_vocovoc_expression4)
+            register_synthezier(synthesize_covovoc_expression4)
+            #endif
+         }
+
+         inline void set_parser(parser_t& p)
+         {
+            parser_ = &p;
+         }
+
+         inline void set_uom(unary_op_map_t& unary_op_map)
+         {
+            unary_op_map_ = &unary_op_map;
+         }
+
+         inline void set_bom(binary_op_map_t& binary_op_map)
+         {
+            binary_op_map_ = &binary_op_map;
+         }
+
+         inline void set_ibom(inv_binary_op_map_t& inv_binary_op_map)
+         {
+            inv_binary_op_map_ = &inv_binary_op_map;
+         }
+
+         inline void set_sf3m(sf3_map_t& sf3_map)
+         {
+            sf3_map_ = &sf3_map;
+         }
+
+         inline void set_sf4m(sf4_map_t& sf4_map)
+         {
+            sf4_map_ = &sf4_map;
+         }
+
+         inline void set_allocator(details::node_allocator& na)
+         {
+            node_allocator_ = &na;
+         }
+
+         inline void set_strength_reduction_state(const bool enabled)
+         {
+            strength_reduction_enabled_ = enabled;
+         }
+
+         inline bool strength_reduction_enabled() const
+         {
+            return strength_reduction_enabled_;
+         }
+
+         inline bool valid_operator(const details::operator_type& operation, binary_functor_t& bop)
+         {
+            typename binary_op_map_t::iterator bop_itr = binary_op_map_->find(operation);
+
+            if ((*binary_op_map_).end() == bop_itr)
+               return false;
+
+            bop = bop_itr->second;
+
+            return true;
+         }
+
+         inline bool valid_operator(const details::operator_type& operation, unary_functor_t& uop)
+         {
+            typename unary_op_map_t::iterator uop_itr = unary_op_map_->find(operation);
+
+            if ((*unary_op_map_).end() == uop_itr)
+               return false;
+
+            uop = uop_itr->second;
+
+            return true;
+         }
+
+         inline details::operator_type get_operator(const binary_functor_t& bop) const
+         {
+            return (*inv_binary_op_map_).find(bop)->second;
+         }
+
+         inline expression_node_ptr operator() (const Type& v) const
+         {
+            return node_allocator_->allocate<literal_node_t>(v);
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         inline expression_node_ptr operator() (const std::string& s) const
+         {
+            return node_allocator_->allocate<string_literal_node_t>(s);
+         }
+
+         inline expression_node_ptr operator() (std::string& s, range_t& rp) const
+         {
+            return node_allocator_->allocate_rr<string_range_node_t>(s,rp);
+         }
+
+         inline expression_node_ptr operator() (const std::string& s, range_t& rp) const
+         {
+            return node_allocator_->allocate_tt<const_string_range_node_t>(s,rp);
+         }
+
+         inline expression_node_ptr operator() (expression_node_ptr branch, range_t& rp) const
+         {
+            if (is_generally_string_node(branch))
+               return node_allocator_->allocate_tt<generic_string_range_node_t>(branch,rp);
+            else
+               return error_node();
+         }
+         #endif
+
+         inline bool unary_optimisable(const details::operator_type& operation) const
+         {
+            return (details::e_abs   == operation) || (details::e_acos  == operation) ||
+                   (details::e_acosh == operation) || (details::e_asin  == operation) ||
+                   (details::e_asinh == operation) || (details::e_atan  == operation) ||
+                   (details::e_atanh == operation) || (details::e_ceil  == operation) ||
+                   (details::e_cos   == operation) || (details::e_cosh  == operation) ||
+                   (details::e_exp   == operation) || (details::e_expm1 == operation) ||
+                   (details::e_floor == operation) || (details::e_log   == operation) ||
+                   (details::e_log10 == operation) || (details::e_log2  == operation) ||
+                   (details::e_log1p == operation) || (details::e_neg   == operation) ||
+                   (details::e_pos   == operation) || (details::e_round == operation) ||
+                   (details::e_sin   == operation) || (details::e_sinc  == operation) ||
+                   (details::e_sinh  == operation) || (details::e_sqrt  == operation) ||
+                   (details::e_tan   == operation) || (details::e_tanh  == operation) ||
+                   (details::e_cot   == operation) || (details::e_sec   == operation) ||
+                   (details::e_csc   == operation) || (details::e_r2d   == operation) ||
+                   (details::e_d2r   == operation) || (details::e_d2g   == operation) ||
+                   (details::e_g2d   == operation) || (details::e_notl  == operation) ||
+                   (details::e_sgn   == operation) || (details::e_erf   == operation) ||
+                   (details::e_erfc  == operation) || (details::e_ncdf  == operation) ||
+                   (details::e_frac  == operation) || (details::e_trunc == operation) ;
+         }
+
+         inline bool sf3_optimisable(const std::string& sf3id, trinary_functor_t& tfunc) const
+         {
+            typename sf3_map_t::const_iterator itr = sf3_map_->find(sf3id);
+
+            if (sf3_map_->end() == itr)
+               return false;
+            else
+               tfunc = itr->second.first;
+
+            return true;
+         }
+
+         inline bool sf4_optimisable(const std::string& sf4id, quaternary_functor_t& qfunc) const
+         {
+            typename sf4_map_t::const_iterator itr = sf4_map_->find(sf4id);
+
+            if (sf4_map_->end() == itr)
+               return false;
+            else
+               qfunc = itr->second.first;
+
+            return true;
+         }
+
+         inline bool sf3_optimisable(const std::string& sf3id, details::operator_type& operation) const
+         {
+            typename sf3_map_t::const_iterator itr = sf3_map_->find(sf3id);
+
+            if (sf3_map_->end() == itr)
+               return false;
+            else
+               operation = itr->second.second;
+
+            return true;
+         }
+
+         inline bool sf4_optimisable(const std::string& sf4id, details::operator_type& operation) const
+         {
+            typename sf4_map_t::const_iterator itr = sf4_map_->find(sf4id);
+
+            if (sf4_map_->end() == itr)
+               return false;
+            else
+               operation = itr->second.second;
+
+            return true;
+         }
+
+         inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr (&branch)[1])
+         {
+            if (0 == branch[0])
+            {
+               return error_node();
+            }
+            else if (details::is_null_node(branch[0]))
+            {
+               return branch[0];
+            }
+            else if (details::is_break_node(branch[0]))
+            {
+               return error_node();
+            }
+            else if (details::is_continue_node(branch[0]))
+            {
+               return error_node();
+            }
+            else if (details::is_constant_node(branch[0]))
+            {
+               return synthesize_expression<unary_node_t,1>(operation,branch);
+            }
+            else if (unary_optimisable(operation) && details::is_variable_node(branch[0]))
+            {
+               return synthesize_uv_expression(operation,branch);
+            }
+            else if (unary_optimisable(operation) && details::is_ivector_node(branch[0]))
+            {
+               return synthesize_uvec_expression(operation,branch);
+            }
+            else
+               return synthesize_unary_expression(operation,branch);
+         }
+
+         inline bool is_assignment_operation(const details::operator_type& operation) const
+         {
+            return (
+                     (details::e_addass == operation) ||
+                     (details::e_subass == operation) ||
+                     (details::e_mulass == operation) ||
+                     (details::e_divass == operation) ||
+                     (details::e_modass == operation)
+                   ) &&
+                   parser_->settings_.assignment_enabled(operation);
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         inline bool valid_string_operation(const details::operator_type& operation) const
+         {
+            return (details::e_add    == operation) ||
+                   (details::e_lt     == operation) ||
+                   (details::e_lte    == operation) ||
+                   (details::e_gt     == operation) ||
+                   (details::e_gte    == operation) ||
+                   (details::e_eq     == operation) ||
+                   (details::e_ne     == operation) ||
+                   (details::e_in     == operation) ||
+                   (details::e_like   == operation) ||
+                   (details::e_ilike  == operation) ||
+                   (details::e_assign == operation) ||
+                   (details::e_addass == operation) ||
+                   (details::e_swap   == operation) ;
+         }
+         #else
+         inline bool valid_string_operation(const details::operator_type&) const
+         {
+            return false;
+         }
+         #endif
+
+         inline std::string to_str(const details::operator_type& operation) const
+         {
+            switch (operation)
+            {
+               case details::e_add  : return "+"      ;
+               case details::e_sub  : return "-"      ;
+               case details::e_mul  : return "*"      ;
+               case details::e_div  : return "/"      ;
+               case details::e_mod  : return "%"      ;
+               case details::e_pow  : return "^"      ;
+               case details::e_lt   : return "<"      ;
+               case details::e_lte  : return "<="     ;
+               case details::e_gt   : return ">"      ;
+               case details::e_gte  : return ">="     ;
+               case details::e_eq   : return "=="     ;
+               case details::e_ne   : return "!="     ;
+               case details::e_and  : return "and"    ;
+               case details::e_nand : return "nand"   ;
+               case details::e_or   : return "or"     ;
+               case details::e_nor  : return "nor"    ;
+               case details::e_xor  : return "xor"    ;
+               case details::e_xnor : return "xnor"   ;
+               default              : return "UNKNOWN";
+            }
+         }
+
+         inline bool operation_optimisable(const details::operator_type& operation) const
+         {
+            return (details::e_add  == operation) ||
+                   (details::e_sub  == operation) ||
+                   (details::e_mul  == operation) ||
+                   (details::e_div  == operation) ||
+                   (details::e_mod  == operation) ||
+                   (details::e_pow  == operation) ||
+                   (details::e_lt   == operation) ||
+                   (details::e_lte  == operation) ||
+                   (details::e_gt   == operation) ||
+                   (details::e_gte  == operation) ||
+                   (details::e_eq   == operation) ||
+                   (details::e_ne   == operation) ||
+                   (details::e_and  == operation) ||
+                   (details::e_nand == operation) ||
+                   (details::e_or   == operation) ||
+                   (details::e_nor  == operation) ||
+                   (details::e_xor  == operation) ||
+                   (details::e_xnor == operation) ;
+         }
+
+         inline std::string branch_to_id(expression_node_ptr branch) const
+         {
+            static const std::string null_str   ("(null)" );
+            static const std::string const_str  ("(c)"    );
+            static const std::string var_str    ("(v)"    );
+            static const std::string vov_str    ("(vov)"  );
+            static const std::string cov_str    ("(cov)"  );
+            static const std::string voc_str    ("(voc)"  );
+            static const std::string str_str    ("(s)"    );
+            static const std::string strrng_str ("(rngs)" );
+            static const std::string cs_str     ("(cs)"   );
+            static const std::string cstrrng_str("(crngs)");
+
+            if (details::is_null_node(branch))
+               return null_str;
+            else if (details::is_constant_node(branch))
+               return const_str;
+            else if (details::is_variable_node(branch))
+               return var_str;
+            else if (details::is_vov_node(branch))
+               return vov_str;
+            else if (details::is_cov_node(branch))
+               return cov_str;
+            else if (details::is_voc_node(branch))
+               return voc_str;
+            else if (details::is_string_node(branch))
+               return str_str;
+            else if (details::is_const_string_node(branch))
+               return cs_str;
+            else if (details::is_string_range_node(branch))
+               return strrng_str;
+            else if (details::is_const_string_range_node(branch))
+               return cstrrng_str;
+            else if (details::is_t0ot1ot2_node(branch))
+               return "(" + dynamic_cast<details::T0oT1oT2_base_node<T>*>(branch)->type_id() + ")";
+            else if (details::is_t0ot1ot2ot3_node(branch))
+               return "(" + dynamic_cast<details::T0oT1oT2oT3_base_node<T>*>(branch)->type_id() + ")";
+            else
+               return "ERROR";
+         }
+
+         inline std::string branch_to_id(expression_node_ptr (&branch)[2]) const
+         {
+            return branch_to_id(branch[0]) + std::string("o") + branch_to_id(branch[1]);
+         }
+
+         inline bool cov_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (!operation_optimisable(operation))
+               return false;
+            else
+               return details::is_constant_node(branch[0]) &&
+                      details::is_variable_node(branch[1]) ;
+         }
+
+         inline bool voc_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (!operation_optimisable(operation))
+               return false;
+            else
+               return details::is_variable_node(branch[0]) &&
+                      details::is_constant_node(branch[1]) ;
+         }
+
+         inline bool vov_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (!operation_optimisable(operation))
+               return false;
+            else
+               return details::is_variable_node(branch[0]) &&
+                      details::is_variable_node(branch[1]) ;
+         }
+
+         inline bool cob_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (!operation_optimisable(operation))
+               return false;
+            else
+               return details::is_constant_node(branch[0]) &&
+                     !details::is_constant_node(branch[1]) ;
+         }
+
+         inline bool boc_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (!operation_optimisable(operation))
+               return false;
+            else
+               return !details::is_constant_node(branch[0]) &&
+                       details::is_constant_node(branch[1]) ;
+         }
+
+         inline bool cocob_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (
+                 (details::e_add == operation) ||
+                 (details::e_sub == operation) ||
+                 (details::e_mul == operation) ||
+                 (details::e_div == operation)
+               )
+            {
+               return (details::is_constant_node(branch[0]) && details::is_cob_node(branch[1])) ||
+                      (details::is_constant_node(branch[1]) && details::is_cob_node(branch[0])) ;
+            }
+            else
+               return false;
+         }
+
+         inline bool coboc_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (
+                 (details::e_add == operation) ||
+                 (details::e_sub == operation) ||
+                 (details::e_mul == operation) ||
+                 (details::e_div == operation)
+               )
+            {
+               return (details::is_constant_node(branch[0]) && details::is_boc_node(branch[1])) ||
+                      (details::is_constant_node(branch[1]) && details::is_boc_node(branch[0])) ;
+            }
+            else
+               return false;
+         }
+
+         inline bool uvouv_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (!operation_optimisable(operation))
+               return false;
+            else
+               return details::is_uv_node(branch[0]) &&
+                      details::is_uv_node(branch[1]) ;
+         }
+
+         inline bool vob_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (!operation_optimisable(operation))
+               return false;
+            else
+               return details::is_variable_node(branch[0]) &&
+                     !details::is_variable_node(branch[1]) ;
+         }
+
+         inline bool bov_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (!operation_optimisable(operation))
+               return false;
+            else
+               return !details::is_variable_node(branch[0]) &&
+                       details::is_variable_node(branch[1]) ;
+         }
+
+         inline bool binext_optimisable(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (!operation_optimisable(operation))
+               return false;
+            else
+               return !details::is_constant_node(branch[0]) ||
+                      !details::is_constant_node(branch[1]) ;
+         }
+
+         inline bool is_invalid_assignment_op(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (is_assignment_operation(operation))
+            {
+               const bool b1_is_genstring = details::is_generally_string_node(branch[1]);
+
+               if (details::is_string_node(branch[0]))
+                  return !b1_is_genstring;
+               else
+                  return (
+                           !details::is_variable_node          (branch[0]) &&
+                           !details::is_vector_elem_node       (branch[0]) &&
+                           !details::is_rebasevector_elem_node (branch[0]) &&
+                           !details::is_rebasevector_celem_node(branch[0]) &&
+                           !details::is_vector_node            (branch[0])
+                         )
+                         || b1_is_genstring;
+            }
+            else
+               return false;
+         }
+
+         inline bool is_constpow_operation(const details::operator_type& operation, expression_node_ptr(&branch)[2]) const
+         {
+            if (
+                 !details::is_constant_node(branch[1]) ||
+                  details::is_constant_node(branch[0]) ||
+                  details::is_variable_node(branch[0]) ||
+                  details::is_vector_node  (branch[0]) ||
+                  details::is_generally_string_node(branch[0])
+               )
+               return false;
+
+            const Type c = static_cast<details::literal_node<Type>*>(branch[1])->value();
+
+            return cardinal_pow_optimisable(operation, c);
+         }
+
+         inline bool is_invalid_break_continue_op(expression_node_ptr (&branch)[2]) const
+         {
+            return (
+                     details::is_break_node   (branch[0]) ||
+                     details::is_break_node   (branch[1]) ||
+                     details::is_continue_node(branch[0]) ||
+                     details::is_continue_node(branch[1])
+                   );
+         }
+
+         inline bool is_invalid_string_op(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            const bool b0_string = is_generally_string_node(branch[0]);
+            const bool b1_string = is_generally_string_node(branch[1]);
+
+            bool result = false;
+
+            if (b0_string != b1_string)
+               result = true;
+            else if (!valid_string_operation(operation) && b0_string && b1_string)
+               result = true;
+
+            if (result)
+            {
+               parser_->set_synthesis_error("Invalid string operation");
+            }
+
+            return result;
+         }
+
+         inline bool is_invalid_string_op(const details::operator_type& operation, expression_node_ptr (&branch)[3]) const
+         {
+            const bool b0_string = is_generally_string_node(branch[0]);
+            const bool b1_string = is_generally_string_node(branch[1]);
+            const bool b2_string = is_generally_string_node(branch[2]);
+
+            bool result = false;
+
+            if ((b0_string != b1_string) || (b1_string != b2_string))
+               result = true;
+            else if ((details::e_inrange != operation) && b0_string && b1_string && b2_string)
+               result = true;
+
+            if (result)
+            {
+               parser_->set_synthesis_error("Invalid string operation");
+            }
+
+            return result;
+         }
+
+         inline bool is_string_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            const bool b0_string = is_generally_string_node(branch[0]);
+            const bool b1_string = is_generally_string_node(branch[1]);
+
+            return (b0_string && b1_string && valid_string_operation(operation));
+         }
+
+         inline bool is_string_operation(const details::operator_type& operation, expression_node_ptr (&branch)[3]) const
+         {
+            const bool b0_string = is_generally_string_node(branch[0]);
+            const bool b1_string = is_generally_string_node(branch[1]);
+            const bool b2_string = is_generally_string_node(branch[2]);
+
+            return (b0_string && b1_string && b2_string && (details::e_inrange == operation));
+         }
+
+         #ifndef exprtk_disable_sc_andor
+         inline bool is_shortcircuit_expression(const details::operator_type& operation) const
+         {
+            return (
+                     (details::e_scand == operation) ||
+                     (details::e_scor  == operation)
+                   );
+         }
+         #else
+         inline bool is_shortcircuit_expression(const details::operator_type&) const
+         {
+            return false;
+         }
+         #endif
+
+         inline bool is_null_present(expression_node_ptr (&branch)[2]) const
+         {
+            return (
+                     details::is_null_node(branch[0]) ||
+                     details::is_null_node(branch[1])
+                   );
+         }
+
+         inline bool is_vector_eqineq_logic_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (!is_ivector_node(branch[0]) && !is_ivector_node(branch[1]))
+               return false;
+            else
+               return (
+                        (details::e_lt    == operation) ||
+                        (details::e_lte   == operation) ||
+                        (details::e_gt    == operation) ||
+                        (details::e_gte   == operation) ||
+                        (details::e_eq    == operation) ||
+                        (details::e_ne    == operation) ||
+                        (details::e_equal == operation) ||
+                        (details::e_and   == operation) ||
+                        (details::e_nand  == operation) ||
+                        (details::  e_or  == operation) ||
+                        (details:: e_nor  == operation) ||
+                        (details:: e_xor  == operation) ||
+                        (details::e_xnor  == operation)
+                      );
+         }
+
+         inline bool is_vector_arithmetic_operation(const details::operator_type& operation, expression_node_ptr (&branch)[2]) const
+         {
+            if (!is_ivector_node(branch[0]) && !is_ivector_node(branch[1]))
+               return false;
+            else
+               return (
+                        (details::e_add == operation) ||
+                        (details::e_sub == operation) ||
+                        (details::e_mul == operation) ||
+                        (details::e_div == operation) ||
+                        (details::e_pow == operation)
+                      );
+         }
+
+         inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr (&branch)[2])
+         {
+            if ((0 == branch[0]) || (0 == branch[1]))
+            {
+               return error_node();
+            }
+            else if (is_invalid_string_op(operation,branch))
+            {
+               return error_node();
+            }
+            else if (is_invalid_assignment_op(operation,branch))
+            {
+               return error_node();
+            }
+            else if (is_invalid_break_continue_op(branch))
+            {
+               return error_node();
+            }
+            else if (details::e_assign == operation)
+            {
+               return synthesize_assignment_expression(operation, branch);
+            }
+            else if (details::e_swap == operation)
+            {
+               return synthesize_swap_expression(branch);
+            }
+            else if (is_assignment_operation(operation))
+            {
+               return synthesize_assignment_operation_expression(operation, branch);
+            }
+            else if (is_vector_eqineq_logic_operation(operation, branch))
+            {
+               return synthesize_veceqineqlogic_operation_expression(operation, branch);
+            }
+            else if (is_vector_arithmetic_operation(operation, branch))
+            {
+               return synthesize_vecarithmetic_operation_expression(operation, branch);
+            }
+            else if (is_shortcircuit_expression(operation))
+            {
+               return synthesize_shortcircuit_expression(operation, branch);
+            }
+            else if (is_string_operation(operation, branch))
+            {
+               return synthesize_string_expression(operation, branch);
+            }
+            else if (is_null_present(branch))
+            {
+               return synthesize_null_expression(operation, branch);
+            }
+            #ifndef exprtk_disable_cardinal_pow_optimisation
+            else if (is_constpow_operation(operation, branch))
+            {
+               return cardinal_pow_optimisation(branch);
+            }
+            #endif
+
+            expression_node_ptr result = error_node();
+
+            #ifndef exprtk_disable_enhanced_features
+            if (synthesize_expression(operation, branch, result))
+            {
+               return result;
+            }
+            else
+            #endif
+
+            {
+               /*
+                  Possible reductions:
+                  1. c o cob -> cob
+                  2. cob o c -> cob
+                  3. c o boc -> boc
+                  4. boc o c -> boc
+               */
+               result = error_node();
+
+               if (cocob_optimisable(operation, branch))
+               {
+                  result = synthesize_cocob_expression::process((*this), operation, branch);
+               }
+               else if (coboc_optimisable(operation, branch) && (0 == result))
+               {
+                  result = synthesize_coboc_expression::process((*this), operation, branch);
+               }
+
+               if (result)
+                  return result;
+            }
+
+            if (uvouv_optimisable(operation, branch))
+            {
+               return synthesize_uvouv_expression(operation, branch);
+            }
+            else if (vob_optimisable(operation, branch))
+            {
+               return synthesize_vob_expression::process((*this), operation, branch);
+            }
+            else if (bov_optimisable(operation, branch))
+            {
+               return synthesize_bov_expression::process((*this), operation, branch);
+            }
+            else if (cob_optimisable(operation, branch))
+            {
+               return synthesize_cob_expression::process((*this), operation, branch);
+            }
+            else if (boc_optimisable(operation, branch))
+            {
+               return synthesize_boc_expression::process((*this), operation, branch);
+            }
+            #ifndef exprtk_disable_enhanced_features
+            else if (cov_optimisable(operation, branch))
+            {
+               return synthesize_cov_expression::process((*this), operation, branch);
+            }
+            #endif
+            else if (binext_optimisable(operation, branch))
+            {
+               return synthesize_binary_ext_expression::process((*this), operation, branch);
+            }
+            else
+               return synthesize_expression<binary_node_t,2>(operation, branch);
+         }
+
+         inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr (&branch)[3])
+         {
+            if (
+                 (0 == branch[0]) ||
+                 (0 == branch[1]) ||
+                 (0 == branch[2])
+               )
+            {
+               details::free_all_nodes(*node_allocator_,branch);
+
+               return error_node();
+            }
+            else if (is_invalid_string_op(operation, branch))
+            {
+               return error_node();
+            }
+            else if (is_string_operation(operation, branch))
+            {
+               return synthesize_string_expression(operation, branch);
+            }
+            else
+               return synthesize_expression<trinary_node_t,3>(operation, branch);
+         }
+
+         inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr (&branch)[4])
+         {
+            return synthesize_expression<quaternary_node_t,4>(operation,branch);
+         }
+
+         inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr b0)
+         {
+            expression_node_ptr branch[1] = { b0 };
+            return (*this)(operation,branch);
+         }
+
+         inline expression_node_ptr operator() (const details::operator_type& operation, expression_node_ptr& b0, expression_node_ptr& b1)
+         {
+            expression_node_ptr result = error_node();
+
+            if ((0 != b0) && (0 != b1))
+            {
+               expression_node_ptr branch[2] = { b0, b1 };
+               result = expression_generator<Type>::operator()(operation, branch);
+               b0 = branch[0];
+               b1 = branch[1];
+            }
+
+            return result;
+         }
+
+         inline expression_node_ptr conditional(expression_node_ptr condition,
+                                                expression_node_ptr consequent,
+                                                expression_node_ptr alternative) const
+         {
+            if ((0 == condition) || (0 == consequent))
+            {
+               free_node(*node_allocator_,   condition);
+               free_node(*node_allocator_,  consequent);
+               free_node(*node_allocator_, alternative);
+
+               return error_node();
+            }
+            // Can the condition be immediately evaluated? if so optimise.
+            else if (details::is_constant_node(condition))
+            {
+               // True branch
+               if (details::is_true(condition))
+               {
+                  free_node(*node_allocator_,   condition);
+                  free_node(*node_allocator_, alternative);
+
+                  return consequent;
+               }
+               // False branch
+               else
+               {
+                  free_node(*node_allocator_,  condition);
+                  free_node(*node_allocator_, consequent);
+
+                  if (alternative)
+                     return alternative;
+                  else
+                     return node_allocator_->allocate<details::null_node<T> >();
+               }
+            }
+            else if ((0 != consequent) && (0 != alternative))
+            {
+               return node_allocator_->
+                        allocate<conditional_node_t>(condition, consequent, alternative);
+            }
+            else
+               return node_allocator_->
+                        allocate<cons_conditional_node_t>(condition, consequent);
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         inline expression_node_ptr conditional_string(expression_node_ptr condition,
+                                                       expression_node_ptr consequent,
+                                                       expression_node_ptr alternative) const
+         {
+            if ((0 == condition) || (0 == consequent))
+            {
+               free_node(*node_allocator_,   condition);
+               free_node(*node_allocator_,  consequent);
+               free_node(*node_allocator_, alternative);
+
+               return error_node();
+            }
+            // Can the condition be immediately evaluated? if so optimise.
+            else if (details::is_constant_node(condition))
+            {
+               // True branch
+               if (details::is_true(condition))
+               {
+                  free_node(*node_allocator_,   condition);
+                  free_node(*node_allocator_, alternative);
+
+                  return consequent;
+               }
+               // False branch
+               else
+               {
+                  free_node(*node_allocator_,  condition);
+                  free_node(*node_allocator_, consequent);
+
+                  if (alternative)
+                     return alternative;
+                  else
+                     return node_allocator_->
+                              allocate_c<details::string_literal_node<Type> >("");
+               }
+            }
+            else if ((0 != consequent) && (0 != alternative))
+               return node_allocator_->
+                        allocate<conditional_string_node_t>(condition, consequent, alternative);
+            else
+               return error_node();
+         }
+         #else
+         inline expression_node_ptr conditional_string(expression_node_ptr,
+                                                       expression_node_ptr,
+                                                       expression_node_ptr) const
+         {
+            return error_node();
+         }
+         #endif
+
+         inline expression_node_ptr while_loop(expression_node_ptr& condition,
+                                               expression_node_ptr& branch,
+                                               const bool brkcont = false) const
+         {
+            if (!brkcont && details::is_constant_node(condition))
+            {
+               expression_node_ptr result = error_node();
+               if (details::is_true(condition))
+                  // Infinite loops are not allowed.
+                  result = error_node();
+               else
+                  result = node_allocator_->allocate<details::null_node<Type> >();
+
+               free_node(*node_allocator_, condition);
+               free_node(*node_allocator_,    branch);
+
+               return result;
+            }
+            else if (details::is_null_node(condition))
+            {
+               free_node(*node_allocator_,condition);
+
+               return branch;
+            }
+            else if (!brkcont)
+               return node_allocator_->allocate<while_loop_node_t>(condition,branch);
+            #ifndef exprtk_disable_break_continue
+            else
+               return node_allocator_->allocate<while_loop_bc_node_t>(condition,branch);
+            #else
+               return error_node();
+            #endif
+         }
+
+         inline expression_node_ptr repeat_until_loop(expression_node_ptr& condition,
+                                                      expression_node_ptr& branch,
+                                                      const bool brkcont = false) const
+         {
+            if (!brkcont && details::is_constant_node(condition))
+            {
+               if (
+                    details::is_true(condition) &&
+                    details::is_constant_node(branch)
+                  )
+               {
+                  free_node(*node_allocator_,condition);
+
+                  return branch;
+               }
+
+               free_node(*node_allocator_, condition);
+               free_node(*node_allocator_,    branch);
+
+               return error_node();
+            }
+            else if (details::is_null_node(condition))
+            {
+               free_node(*node_allocator_,condition);
+
+               return branch;
+            }
+            else if (!brkcont)
+               return node_allocator_->allocate<repeat_until_loop_node_t>(condition,branch);
+            #ifndef exprtk_disable_break_continue
+            else
+               return node_allocator_->allocate<repeat_until_loop_bc_node_t>(condition,branch);
+            #else
+               return error_node();
+            #endif
+         }
+
+         inline expression_node_ptr for_loop(expression_node_ptr& initialiser,
+                                             expression_node_ptr& condition,
+                                             expression_node_ptr& incrementor,
+                                             expression_node_ptr& loop_body,
+                                             bool brkcont = false) const
+         {
+            if (!brkcont && details::is_constant_node(condition))
+            {
+               expression_node_ptr result = error_node();
+
+               if (details::is_true(condition))
+                  // Infinite loops are not allowed.
+                  result = error_node();
+               else
+                  result = node_allocator_->allocate<details::null_node<Type> >();
+
+               free_node(*node_allocator_, initialiser);
+               free_node(*node_allocator_,   condition);
+               free_node(*node_allocator_, incrementor);
+               free_node(*node_allocator_,   loop_body);
+
+               return result;
+            }
+            else if (details::is_null_node(condition) || (0 == condition))
+            {
+               free_node(*node_allocator_, initialiser);
+               free_node(*node_allocator_,   condition);
+               free_node(*node_allocator_, incrementor);
+
+               return loop_body;
+            }
+            else if (!brkcont)
+               return node_allocator_->allocate<for_loop_node_t>
+                                       (
+                                         initialiser,
+                                         condition,
+                                         incrementor,
+                                         loop_body
+                                       );
+
+            #ifndef exprtk_disable_break_continue
+            else
+               return node_allocator_->allocate<for_loop_bc_node_t>
+                                       (
+                                         initialiser,
+                                         condition,
+                                         incrementor,
+                                         loop_body
+                                       );
+            #else
+            return error_node();
+            #endif
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline expression_node_ptr const_optimise_switch(Sequence<expression_node_ptr,Allocator>& arg_list)
+         {
+            expression_node_ptr result = error_node();
+
+            for (std::size_t i = 0; i < (arg_list.size() / 2); ++i)
+            {
+               expression_node_ptr condition  = arg_list[(2 * i)    ];
+               expression_node_ptr consequent = arg_list[(2 * i) + 1];
+
+               if ((0 == result) && details::is_true(condition))
+               {
+                  result = consequent;
+                  break;
+               }
+            }
+
+            if (0 == result)
+            {
+               result = arg_list.back();
+            }
+
+            for (std::size_t i = 0; i < arg_list.size(); ++i)
+            {
+               expression_node_ptr current_expr = arg_list[i];
+
+               if (current_expr && (current_expr != result))
+               {
+                  free_node(*node_allocator_,current_expr);
+               }
+            }
+
+            return result;
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline expression_node_ptr const_optimise_mswitch(Sequence<expression_node_ptr,Allocator>& arg_list)
+         {
+            expression_node_ptr result = error_node();
+
+            for (std::size_t i = 0; i < (arg_list.size() / 2); ++i)
+            {
+               expression_node_ptr condition  = arg_list[(2 * i)    ];
+               expression_node_ptr consequent = arg_list[(2 * i) + 1];
+
+               if (details::is_true(condition))
+               {
+                  result = consequent;
+               }
+            }
+
+            if (0 == result)
+            {
+               T zero = T(0);
+               result = node_allocator_->allocate<literal_node_t>(zero);
+            }
+
+            for (std::size_t i = 0; i < arg_list.size(); ++i)
+            {
+               expression_node_ptr& current_expr = arg_list[i];
+
+               if (current_expr && (current_expr != result))
+               {
+                  free_node(*node_allocator_,current_expr);
+               }
+            }
+
+            return result;
+         }
+
+         struct switch_nodes
+         {
+            typedef std::vector<expression_node_ptr> arg_list_t;
+
+            #define case_stmt(N)                                             \
+            if (is_true(arg[(2 * N)])) { return arg[(2 * N) + 1]->value(); } \
+
+            struct switch_1
+            {
+               static inline T process(const arg_list_t& arg)
+               {
+                  case_stmt(0)
+
+                  return arg.back()->value();
+               }
+            };
+
+            struct switch_2
+            {
+               static inline T process(const arg_list_t& arg)
+               {
+                  case_stmt(0) case_stmt(1)
+
+                  return arg.back()->value();
+               }
+            };
+
+            struct switch_3
+            {
+               static inline T process(const arg_list_t& arg)
+               {
+                  case_stmt(0) case_stmt(1)
+                  case_stmt(2)
+
+                  return arg.back()->value();
+               }
+            };
+
+            struct switch_4
+            {
+               static inline T process(const arg_list_t& arg)
+               {
+                  case_stmt(0) case_stmt(1)
+                  case_stmt(2) case_stmt(3)
+
+                  return arg.back()->value();
+               }
+            };
+
+            struct switch_5
+            {
+               static inline T process(const arg_list_t& arg)
+               {
+                  case_stmt(0) case_stmt(1)
+                  case_stmt(2) case_stmt(3)
+                  case_stmt(4)
+
+                  return arg.back()->value();
+               }
+            };
+
+            struct switch_6
+            {
+               static inline T process(const arg_list_t& arg)
+               {
+                  case_stmt(0) case_stmt(1)
+                  case_stmt(2) case_stmt(3)
+                  case_stmt(4) case_stmt(5)
+
+                  return arg.back()->value();
+               }
+            };
+
+            struct switch_7
+            {
+               static inline T process(const arg_list_t& arg)
+               {
+                  case_stmt(0) case_stmt(1)
+                  case_stmt(2) case_stmt(3)
+                  case_stmt(4) case_stmt(5)
+                  case_stmt(6)
+
+                  return arg.back()->value();
+               }
+            };
+
+            #undef case_stmt
+         };
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline expression_node_ptr switch_statement(Sequence<expression_node_ptr,Allocator>& arg_list)
+         {
+            if (arg_list.empty())
+               return error_node();
+            else if (
+                      !all_nodes_valid(arg_list)   ||
+                      (arg_list.size() < 3)        ||
+                      ((arg_list.size() % 2) != 1)
+                    )
+            {
+               details::free_all_nodes(*node_allocator_,arg_list);
+
+               return error_node();
+            }
+            else if (is_constant_foldable(arg_list))
+               return const_optimise_switch(arg_list);
+
+            switch ((arg_list.size() - 1) / 2)
+            {
+               #define case_stmt(N)                                                 \
+               case N :                                                             \
+                  return node_allocator_->                                          \
+                            allocate<details::switch_n_node                         \
+                              <Type,typename switch_nodes::switch_##N> >(arg_list); \
+
+               case_stmt(1)
+               case_stmt(2)
+               case_stmt(3)
+               case_stmt(4)
+               case_stmt(5)
+               case_stmt(6)
+               case_stmt(7)
+               #undef case_stmt
+
+               default : return node_allocator_->allocate<details::switch_node<Type> >(arg_list);
+            }
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline expression_node_ptr multi_switch_statement(Sequence<expression_node_ptr,Allocator>& arg_list)
+         {
+            if (!all_nodes_valid(arg_list))
+            {
+               details::free_all_nodes(*node_allocator_,arg_list);
+
+               return error_node();
+            }
+            else if (is_constant_foldable(arg_list))
+               return const_optimise_mswitch(arg_list);
+            else
+               return node_allocator_->allocate<details::multi_switch_node<Type> >(arg_list);
+         }
+
+         #define unary_opr_switch_statements            \
+         case_stmt(details::  e_abs, details::  abs_op) \
+         case_stmt(details:: e_acos, details:: acos_op) \
+         case_stmt(details::e_acosh, details::acosh_op) \
+         case_stmt(details:: e_asin, details:: asin_op) \
+         case_stmt(details::e_asinh, details::asinh_op) \
+         case_stmt(details:: e_atan, details:: atan_op) \
+         case_stmt(details::e_atanh, details::atanh_op) \
+         case_stmt(details:: e_ceil, details:: ceil_op) \
+         case_stmt(details::  e_cos, details::  cos_op) \
+         case_stmt(details:: e_cosh, details:: cosh_op) \
+         case_stmt(details::  e_exp, details::  exp_op) \
+         case_stmt(details::e_expm1, details::expm1_op) \
+         case_stmt(details::e_floor, details::floor_op) \
+         case_stmt(details::  e_log, details::  log_op) \
+         case_stmt(details::e_log10, details::log10_op) \
+         case_stmt(details:: e_log2, details:: log2_op) \
+         case_stmt(details::e_log1p, details::log1p_op) \
+         case_stmt(details::  e_neg, details::  neg_op) \
+         case_stmt(details::  e_pos, details::  pos_op) \
+         case_stmt(details::e_round, details::round_op) \
+         case_stmt(details::  e_sin, details::  sin_op) \
+         case_stmt(details:: e_sinc, details:: sinc_op) \
+         case_stmt(details:: e_sinh, details:: sinh_op) \
+         case_stmt(details:: e_sqrt, details:: sqrt_op) \
+         case_stmt(details::  e_tan, details::  tan_op) \
+         case_stmt(details:: e_tanh, details:: tanh_op) \
+         case_stmt(details::  e_cot, details::  cot_op) \
+         case_stmt(details::  e_sec, details::  sec_op) \
+         case_stmt(details::  e_csc, details::  csc_op) \
+         case_stmt(details::  e_r2d, details::  r2d_op) \
+         case_stmt(details::  e_d2r, details::  d2r_op) \
+         case_stmt(details::  e_d2g, details::  d2g_op) \
+         case_stmt(details::  e_g2d, details::  g2d_op) \
+         case_stmt(details:: e_notl, details:: notl_op) \
+         case_stmt(details::  e_sgn, details::  sgn_op) \
+         case_stmt(details::  e_erf, details::  erf_op) \
+         case_stmt(details:: e_erfc, details:: erfc_op) \
+         case_stmt(details:: e_ncdf, details:: ncdf_op) \
+         case_stmt(details:: e_frac, details:: frac_op) \
+         case_stmt(details::e_trunc, details::trunc_op) \
+
+         inline expression_node_ptr synthesize_uv_expression(const details::operator_type& operation,
+                                                             expression_node_ptr (&branch)[1])
+         {
+            T& v = static_cast<details::variable_node<T>*>(branch[0])->ref();
+
+            switch (operation)
+            {
+               #define case_stmt(op0,op1)                                                          \
+               case op0 : return node_allocator_->                                                 \
+                             allocate<typename details::unary_variable_node<Type,op1<Type> > >(v); \
+
+               unary_opr_switch_statements
+               #undef case_stmt
+               default : return error_node();
+            }
+         }
+
+         inline expression_node_ptr synthesize_uvec_expression(const details::operator_type& operation,
+                                                               expression_node_ptr (&branch)[1])
+         {
+            switch (operation)
+            {
+               #define case_stmt(op0,op1)                                                    \
+               case op0 : return node_allocator_->                                           \
+                             allocate<typename details::unary_vector_node<Type,op1<Type> > > \
+                                (operation, branch[0]);                                      \
+
+               unary_opr_switch_statements
+               #undef case_stmt
+               default : return error_node();
+            }
+         }
+
+         inline expression_node_ptr synthesize_unary_expression(const details::operator_type& operation,
+                                                                expression_node_ptr (&branch)[1])
+         {
+            switch (operation)
+            {
+               #define case_stmt(op0,op1)                                                                \
+               case op0 : return node_allocator_->                                                       \
+                             allocate<typename details::unary_branch_node<Type,op1<Type> > >(branch[0]); \
+
+               unary_opr_switch_statements
+               #undef case_stmt
+               default : return error_node();
+            }
+         }
+
+         inline expression_node_ptr const_optimise_sf3(const details::operator_type& operation,
+                                                       expression_node_ptr (&branch)[3])
+         {
+            expression_node_ptr temp_node = error_node();
+
+            switch (operation)
+            {
+               #define case_stmt(op)                                                        \
+               case details::e_sf##op : temp_node = node_allocator_->                       \
+                             allocate<details::sf3_node<Type,details::sf##op##_op<Type> > > \
+                                (operation, branch);                                        \
+                             break;                                                         \
+
+               case_stmt(00) case_stmt(01) case_stmt(02) case_stmt(03)
+               case_stmt(04) case_stmt(05) case_stmt(06) case_stmt(07)
+               case_stmt(08) case_stmt(09) case_stmt(10) case_stmt(11)
+               case_stmt(12) case_stmt(13) case_stmt(14) case_stmt(15)
+               case_stmt(16) case_stmt(17) case_stmt(18) case_stmt(19)
+               case_stmt(20) case_stmt(21) case_stmt(22) case_stmt(23)
+               case_stmt(24) case_stmt(25) case_stmt(26) case_stmt(27)
+               case_stmt(28) case_stmt(29) case_stmt(30) case_stmt(31)
+               case_stmt(32) case_stmt(33) case_stmt(34) case_stmt(35)
+               case_stmt(36) case_stmt(37) case_stmt(38) case_stmt(39)
+               case_stmt(40) case_stmt(41) case_stmt(42) case_stmt(43)
+               case_stmt(44) case_stmt(45) case_stmt(46) case_stmt(47)
+               #undef case_stmt
+               default : return error_node();
+            }
+
+            const T v = temp_node->value();
+
+            details::free_node(*node_allocator_,temp_node);
+
+            return node_allocator_->allocate<literal_node_t>(v);
+         }
+
+         inline expression_node_ptr varnode_optimise_sf3(const details::operator_type& operation, expression_node_ptr (&branch)[3])
+         {
+            typedef details::variable_node<Type>* variable_ptr;
+
+            const Type& v0 = static_cast<variable_ptr>(branch[0])->ref();
+            const Type& v1 = static_cast<variable_ptr>(branch[1])->ref();
+            const Type& v2 = static_cast<variable_ptr>(branch[2])->ref();
+
+            switch (operation)
+            {
+               #define case_stmt(op)                                                                \
+               case details::e_sf##op : return node_allocator_->                                    \
+                             allocate_rrr<details::sf3_var_node<Type,details::sf##op##_op<Type> > > \
+                                (v0, v1, v2);                                                       \
+
+               case_stmt(00) case_stmt(01) case_stmt(02) case_stmt(03)
+               case_stmt(04) case_stmt(05) case_stmt(06) case_stmt(07)
+               case_stmt(08) case_stmt(09) case_stmt(10) case_stmt(11)
+               case_stmt(12) case_stmt(13) case_stmt(14) case_stmt(15)
+               case_stmt(16) case_stmt(17) case_stmt(18) case_stmt(19)
+               case_stmt(20) case_stmt(21) case_stmt(22) case_stmt(23)
+               case_stmt(24) case_stmt(25) case_stmt(26) case_stmt(27)
+               case_stmt(28) case_stmt(29) case_stmt(30) case_stmt(31)
+               case_stmt(32) case_stmt(33) case_stmt(34) case_stmt(35)
+               case_stmt(36) case_stmt(37) case_stmt(38) case_stmt(39)
+               case_stmt(40) case_stmt(41) case_stmt(42) case_stmt(43)
+               case_stmt(44) case_stmt(45) case_stmt(46) case_stmt(47)
+               #undef case_stmt
+               default : return error_node();
+            }
+         }
+
+         inline expression_node_ptr special_function(const details::operator_type& operation, expression_node_ptr (&branch)[3])
+         {
+            if (!all_nodes_valid(branch))
+               return error_node();
+            else if (is_constant_foldable(branch))
+               return const_optimise_sf3(operation,branch);
+            else if (all_nodes_variables(branch))
+               return varnode_optimise_sf3(operation,branch);
+            else
+            {
+               switch (operation)
+               {
+                  #define case_stmt(op)                                                        \
+                  case details::e_sf##op : return node_allocator_->                            \
+                                allocate<details::sf3_node<Type,details::sf##op##_op<Type> > > \
+                                   (operation, branch);                                        \
+
+                  case_stmt(00) case_stmt(01) case_stmt(02) case_stmt(03)
+                  case_stmt(04) case_stmt(05) case_stmt(06) case_stmt(07)
+                  case_stmt(08) case_stmt(09) case_stmt(10) case_stmt(11)
+                  case_stmt(12) case_stmt(13) case_stmt(14) case_stmt(15)
+                  case_stmt(16) case_stmt(17) case_stmt(18) case_stmt(19)
+                  case_stmt(20) case_stmt(21) case_stmt(22) case_stmt(23)
+                  case_stmt(24) case_stmt(25) case_stmt(26) case_stmt(27)
+                  case_stmt(28) case_stmt(29) case_stmt(30) case_stmt(31)
+                  case_stmt(32) case_stmt(33) case_stmt(34) case_stmt(35)
+                  case_stmt(36) case_stmt(37) case_stmt(38) case_stmt(39)
+                  case_stmt(40) case_stmt(41) case_stmt(42) case_stmt(43)
+                  case_stmt(44) case_stmt(45) case_stmt(46) case_stmt(47)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+         }
+
+         inline expression_node_ptr const_optimise_sf4(const details::operator_type& operation, expression_node_ptr (&branch)[4])
+         {
+            expression_node_ptr temp_node = error_node();
+
+            switch (operation)
+            {
+               #define case_stmt(op)                                                                    \
+               case details::e_sf##op : temp_node = node_allocator_->                                   \
+                                         allocate<details::sf4_node<Type,details::sf##op##_op<Type> > > \
+                                            (operation, branch);                                        \
+                                        break;                                                          \
+
+               case_stmt(48) case_stmt(49) case_stmt(50) case_stmt(51)
+               case_stmt(52) case_stmt(53) case_stmt(54) case_stmt(55)
+               case_stmt(56) case_stmt(57) case_stmt(58) case_stmt(59)
+               case_stmt(60) case_stmt(61) case_stmt(62) case_stmt(63)
+               case_stmt(64) case_stmt(65) case_stmt(66) case_stmt(67)
+               case_stmt(68) case_stmt(69) case_stmt(70) case_stmt(71)
+               case_stmt(72) case_stmt(73) case_stmt(74) case_stmt(75)
+               case_stmt(76) case_stmt(77) case_stmt(78) case_stmt(79)
+               case_stmt(80) case_stmt(81) case_stmt(82) case_stmt(83)
+               case_stmt(84) case_stmt(85) case_stmt(86) case_stmt(87)
+               case_stmt(88) case_stmt(89) case_stmt(90) case_stmt(91)
+               case_stmt(92) case_stmt(93) case_stmt(94) case_stmt(95)
+               case_stmt(96) case_stmt(97) case_stmt(98) case_stmt(99)
+               #undef case_stmt
+               default : return error_node();
+            }
+
+            const T v = temp_node->value();
+
+            details::free_node(*node_allocator_,temp_node);
+
+            return node_allocator_->allocate<literal_node_t>(v);
+         }
+
+         inline expression_node_ptr varnode_optimise_sf4(const details::operator_type& operation, expression_node_ptr (&branch)[4])
+         {
+            typedef details::variable_node<Type>* variable_ptr;
+
+            const Type& v0 = static_cast<variable_ptr>(branch[0])->ref();
+            const Type& v1 = static_cast<variable_ptr>(branch[1])->ref();
+            const Type& v2 = static_cast<variable_ptr>(branch[2])->ref();
+            const Type& v3 = static_cast<variable_ptr>(branch[3])->ref();
+
+            switch (operation)
+            {
+               #define case_stmt(op)                                                                 \
+               case details::e_sf##op : return node_allocator_->                                     \
+                             allocate_rrrr<details::sf4_var_node<Type,details::sf##op##_op<Type> > > \
+                                (v0, v1, v2, v3);                                                    \
+
+               case_stmt(48) case_stmt(49) case_stmt(50) case_stmt(51)
+               case_stmt(52) case_stmt(53) case_stmt(54) case_stmt(55)
+               case_stmt(56) case_stmt(57) case_stmt(58) case_stmt(59)
+               case_stmt(60) case_stmt(61) case_stmt(62) case_stmt(63)
+               case_stmt(64) case_stmt(65) case_stmt(66) case_stmt(67)
+               case_stmt(68) case_stmt(69) case_stmt(70) case_stmt(71)
+               case_stmt(72) case_stmt(73) case_stmt(74) case_stmt(75)
+               case_stmt(76) case_stmt(77) case_stmt(78) case_stmt(79)
+               case_stmt(80) case_stmt(81) case_stmt(82) case_stmt(83)
+               case_stmt(84) case_stmt(85) case_stmt(86) case_stmt(87)
+               case_stmt(88) case_stmt(89) case_stmt(90) case_stmt(91)
+               case_stmt(92) case_stmt(93) case_stmt(94) case_stmt(95)
+               case_stmt(96) case_stmt(97) case_stmt(98) case_stmt(99)
+               #undef case_stmt
+               default : return error_node();
+            }
+         }
+
+         inline expression_node_ptr special_function(const details::operator_type& operation, expression_node_ptr (&branch)[4])
+         {
+            if (!all_nodes_valid(branch))
+               return error_node();
+            else if (is_constant_foldable(branch))
+               return const_optimise_sf4(operation,branch);
+            else if (all_nodes_variables(branch))
+               return varnode_optimise_sf4(operation,branch);
+            switch (operation)
+            {
+               #define case_stmt(op)                                                        \
+               case details::e_sf##op : return node_allocator_->                            \
+                             allocate<details::sf4_node<Type,details::sf##op##_op<Type> > > \
+                                (operation, branch);                                        \
+
+               case_stmt(48) case_stmt(49) case_stmt(50) case_stmt(51)
+               case_stmt(52) case_stmt(53) case_stmt(54) case_stmt(55)
+               case_stmt(56) case_stmt(57) case_stmt(58) case_stmt(59)
+               case_stmt(60) case_stmt(61) case_stmt(62) case_stmt(63)
+               case_stmt(64) case_stmt(65) case_stmt(66) case_stmt(67)
+               case_stmt(68) case_stmt(69) case_stmt(70) case_stmt(71)
+               case_stmt(72) case_stmt(73) case_stmt(74) case_stmt(75)
+               case_stmt(76) case_stmt(77) case_stmt(78) case_stmt(79)
+               case_stmt(80) case_stmt(81) case_stmt(82) case_stmt(83)
+               case_stmt(84) case_stmt(85) case_stmt(86) case_stmt(87)
+               case_stmt(88) case_stmt(89) case_stmt(90) case_stmt(91)
+               case_stmt(92) case_stmt(93) case_stmt(94) case_stmt(95)
+               case_stmt(96) case_stmt(97) case_stmt(98) case_stmt(99)
+               #undef case_stmt
+               default : return error_node();
+            }
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline expression_node_ptr const_optimise_varargfunc(const details::operator_type& operation, Sequence<expression_node_ptr,Allocator>& arg_list)
+         {
+            expression_node_ptr temp_node = error_node();
+
+            switch (operation)
+            {
+               #define case_stmt(op0,op1)                                                 \
+               case op0 : temp_node = node_allocator_->                                   \
+                                         allocate<details::vararg_node<Type,op1<Type> > > \
+                                            (arg_list);                                   \
+                          break;                                                          \
+
+               case_stmt(details::e_sum   , details::vararg_add_op  )
+               case_stmt(details::e_prod  , details::vararg_mul_op  )
+               case_stmt(details::e_avg   , details::vararg_avg_op  )
+               case_stmt(details::e_min   , details::vararg_min_op  )
+               case_stmt(details::e_max   , details::vararg_max_op  )
+               case_stmt(details::e_mand  , details::vararg_mand_op )
+               case_stmt(details::e_mor   , details::vararg_mor_op  )
+               case_stmt(details::e_multi , details::vararg_multi_op)
+               #undef case_stmt
+               default : return error_node();
+            }
+
+            const T v = temp_node->value();
+
+            details::free_node(*node_allocator_,temp_node);
+
+            return node_allocator_->allocate<literal_node_t>(v);
+         }
+
+         inline bool special_one_parameter_vararg(const details::operator_type& operation) const
+         {
+            return (
+                     (details::e_sum  == operation) ||
+                     (details::e_prod == operation) ||
+                     (details::e_avg  == operation) ||
+                     (details::e_min  == operation) ||
+                     (details::e_max  == operation)
+                   );
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline expression_node_ptr varnode_optimise_varargfunc(const details::operator_type& operation, Sequence<expression_node_ptr,Allocator>& arg_list)
+         {
+            switch (operation)
+            {
+               #define case_stmt(op0,op1)                                                   \
+               case op0 : return node_allocator_->                                          \
+                             allocate<details::vararg_varnode<Type,op1<Type> > >(arg_list); \
+
+               case_stmt(details::e_sum   , details::vararg_add_op  )
+               case_stmt(details::e_prod  , details::vararg_mul_op  )
+               case_stmt(details::e_avg   , details::vararg_avg_op  )
+               case_stmt(details::e_min   , details::vararg_min_op  )
+               case_stmt(details::e_max   , details::vararg_max_op  )
+               case_stmt(details::e_mand  , details::vararg_mand_op )
+               case_stmt(details::e_mor   , details::vararg_mor_op  )
+               case_stmt(details::e_multi , details::vararg_multi_op)
+               #undef case_stmt
+               default : return error_node();
+            }
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline expression_node_ptr vectorize_func(const details::operator_type& operation, Sequence<expression_node_ptr,Allocator>& arg_list)
+         {
+            if (1 == arg_list.size())
+            {
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                      \
+                  case op0 : return node_allocator_->                                             \
+                                allocate<details::vectorize_node<Type,op1<Type> > >(arg_list[0]); \
+
+                  case_stmt(details::e_sum  , details::vec_add_op)
+                  case_stmt(details::e_prod , details::vec_mul_op)
+                  case_stmt(details::e_avg  , details::vec_avg_op)
+                  case_stmt(details::e_min  , details::vec_min_op)
+                  case_stmt(details::e_max  , details::vec_max_op)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else
+               return error_node();
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline expression_node_ptr vararg_function(const details::operator_type& operation, Sequence<expression_node_ptr,Allocator>& arg_list)
+         {
+            if (!all_nodes_valid(arg_list))
+            {
+               details::free_all_nodes(*node_allocator_,arg_list);
+
+               return error_node();
+            }
+            else if (is_constant_foldable(arg_list))
+               return const_optimise_varargfunc(operation,arg_list);
+            else if ((arg_list.size() == 1) && details::is_ivector_node(arg_list[0]))
+               return vectorize_func(operation,arg_list);
+            else if ((arg_list.size() == 1) && special_one_parameter_vararg(operation))
+               return arg_list[0];
+            else if (all_nodes_variables(arg_list))
+               return varnode_optimise_varargfunc(operation,arg_list);
+
+            #ifndef exprtk_disable_string_capabilities
+            if (details::e_smulti == operation)
+            {
+               return node_allocator_->
+                 allocate<details::str_vararg_node<Type,details::vararg_multi_op<Type> > >(arg_list);
+            }
+            else
+            #endif
+            {
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                \
+                  case op0 : return node_allocator_->                                       \
+                                allocate<details::vararg_node<Type,op1<Type> > >(arg_list); \
+
+                  case_stmt(details::e_sum   , details::vararg_add_op  )
+                  case_stmt(details::e_prod  , details::vararg_mul_op  )
+                  case_stmt(details::e_avg   , details::vararg_avg_op  )
+                  case_stmt(details::e_min   , details::vararg_min_op  )
+                  case_stmt(details::e_max   , details::vararg_max_op  )
+                  case_stmt(details::e_mand  , details::vararg_mand_op )
+                  case_stmt(details::e_mor   , details::vararg_mor_op  )
+                  case_stmt(details::e_multi , details::vararg_multi_op)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+         }
+
+         template <std::size_t N>
+         inline expression_node_ptr function(ifunction_t* f, expression_node_ptr (&b)[N])
+         {
+            typedef typename details::function_N_node<T,ifunction_t,N> function_N_node_t;
+            expression_node_ptr result = synthesize_expression<function_N_node_t,N>(f,b);
+
+            if (0 == result)
+               return error_node();
+            else
+            {
+               // Can the function call be completely optimised?
+               if (details::is_constant_node(result))
+                  return result;
+               else if (!all_nodes_valid(b))
+                  return error_node();
+               else if (N != f->param_count)
+               {
+                  details::free_all_nodes(*node_allocator_,b);
+
+                  return error_node();
+               }
+
+               function_N_node_t* func_node_ptr = static_cast<function_N_node_t*>(result);
+
+               if (func_node_ptr->init_branches(b))
+                  return result;
+               else
+               {
+                  details::free_all_nodes(*node_allocator_,b);
+
+                  return error_node();
+               }
+            }
+         }
+
+         inline expression_node_ptr function(ifunction_t* f)
+         {
+            typedef typename details::function_N_node<Type,ifunction_t,0> function_N_node_t;
+            return node_allocator_->allocate<function_N_node_t>(f);
+         }
+
+         inline expression_node_ptr vararg_function_call(ivararg_function_t* vaf,
+                                                         std::vector<expression_node_ptr>& arg_list)
+         {
+            if (!all_nodes_valid(arg_list))
+            {
+               details::free_all_nodes(*node_allocator_,arg_list);
+
+               return error_node();
+            }
+
+            typedef details::vararg_function_node<Type,ivararg_function_t> alloc_type;
+
+            expression_node_ptr result = node_allocator_->allocate<alloc_type>(vaf,arg_list);
+
+            if (
+                 !arg_list.empty()        &&
+                 !vaf->has_side_effects() &&
+                 is_constant_foldable(arg_list)
+               )
+            {
+               const Type v = result->value();
+               details::free_node(*node_allocator_,result);
+               result = node_allocator_->allocate<literal_node_t>(v);
+            }
+
+            parser_->state_.activate_side_effect("vararg_function_call()");
+
+            return result;
+         }
+
+         inline expression_node_ptr generic_function_call(igeneric_function_t* gf,
+                                                          std::vector<expression_node_ptr>& arg_list,
+                                                          const std::size_t& param_seq_index = std::numeric_limits<std::size_t>::max())
+         {
+            if (!all_nodes_valid(arg_list))
+            {
+               details::free_all_nodes(*node_allocator_,arg_list);
+               return error_node();
+            }
+
+            typedef details::generic_function_node     <Type,igeneric_function_t> alloc_type1;
+            typedef details::multimode_genfunction_node<Type,igeneric_function_t> alloc_type2;
+
+            const std::size_t no_psi = std::numeric_limits<std::size_t>::max();
+
+            expression_node_ptr result = error_node();
+
+            if (no_psi == param_seq_index)
+               result = node_allocator_->allocate<alloc_type1>(arg_list,gf);
+            else
+               result = node_allocator_->allocate<alloc_type2>(gf, param_seq_index, arg_list);
+
+            alloc_type1* genfunc_node_ptr = static_cast<alloc_type1*>(result);
+
+            if (
+                 !arg_list.empty()                  &&
+                 !gf->has_side_effects()            &&
+                 parser_->state_.type_check_enabled &&
+                 is_constant_foldable(arg_list)
+               )
+            {
+               genfunc_node_ptr->init_branches();
+
+               const Type v = result->value();
+
+               details::free_node(*node_allocator_,result);
+
+               return node_allocator_->allocate<literal_node_t>(v);
+            }
+            else if (genfunc_node_ptr->init_branches())
+            {
+               parser_->state_.activate_side_effect("generic_function_call()");
+
+               return result;
+            }
+            else
+            {
+               details::free_node(*node_allocator_, result);
+               details::free_all_nodes(*node_allocator_, arg_list);
+
+               return error_node();
+            }
+         }
+
+         #ifndef exprtk_disable_string_capabilities
+         inline expression_node_ptr string_function_call(igeneric_function_t* gf,
+                                                         std::vector<expression_node_ptr>& arg_list,
+                                                         const std::size_t& param_seq_index = std::numeric_limits<std::size_t>::max())
+         {
+            if (!all_nodes_valid(arg_list))
+            {
+               details::free_all_nodes(*node_allocator_,arg_list);
+               return error_node();
+            }
+
+            typedef details::string_function_node      <Type,igeneric_function_t> alloc_type1;
+            typedef details::multimode_strfunction_node<Type,igeneric_function_t> alloc_type2;
+
+            const std::size_t no_psi = std::numeric_limits<std::size_t>::max();
+
+            expression_node_ptr result = error_node();
+
+            if (no_psi == param_seq_index)
+               result = node_allocator_->allocate<alloc_type1>(gf,arg_list);
+            else
+               result = node_allocator_->allocate<alloc_type2>(gf, param_seq_index, arg_list);
+
+            alloc_type1* strfunc_node_ptr = static_cast<alloc_type1*>(result);
+
+            if (
+                 !arg_list.empty()       &&
+                 !gf->has_side_effects() &&
+                 is_constant_foldable(arg_list)
+               )
+            {
+               strfunc_node_ptr->init_branches();
+
+               const Type v = result->value();
+
+               details::free_node(*node_allocator_,result);
+
+               return node_allocator_->allocate<literal_node_t>(v);
+            }
+            else if (strfunc_node_ptr->init_branches())
+            {
+               parser_->state_.activate_side_effect("string_function_call()");
+
+               return result;
+            }
+            else
+            {
+               details::free_node     (*node_allocator_,result  );
+               details::free_all_nodes(*node_allocator_,arg_list);
+
+               return error_node();
+            }
+         }
+         #endif
+
+         #ifndef exprtk_disable_return_statement
+         inline expression_node_ptr return_call(std::vector<expression_node_ptr>& arg_list)
+         {
+            if (!all_nodes_valid(arg_list))
+            {
+               details::free_all_nodes(*node_allocator_,arg_list);
+               return error_node();
+            }
+
+            typedef details::return_node<Type> alloc_type;
+
+            expression_node_ptr result = node_allocator_->
+                                            allocate_rr<alloc_type>(arg_list,parser_->results_ctx());
+
+            alloc_type* return_node_ptr = static_cast<alloc_type*>(result);
+
+            if (return_node_ptr->init_branches())
+            {
+               parser_->state_.activate_side_effect("return_call()");
+
+               return result;
+            }
+            else
+            {
+               details::free_node     (*node_allocator_,result  );
+               details::free_all_nodes(*node_allocator_,arg_list);
+
+               return error_node();
+            }
+         }
+
+         inline expression_node_ptr return_envelope(expression_node_ptr body,
+                                                    results_context_t* rc,
+                                                    bool*& return_invoked)
+         {
+            typedef details::return_envelope_node<Type> alloc_type;
+
+            expression_node_ptr result = node_allocator_->
+                                            allocate_cr<alloc_type>(body,(*rc));
+
+            return_invoked = static_cast<alloc_type*>(result)->retinvk_ptr();
+
+            return result;
+         }
+         #else
+         inline expression_node_ptr return_call(std::vector<expression_node_ptr>&)
+         {
+            return error_node();
+         }
+
+         inline expression_node_ptr return_envelope(expression_node_ptr,
+                                                    results_context_t*,
+                                                    bool*&)
+         {
+            return error_node();
+         }
+         #endif
+
+         inline expression_node_ptr vector_element(const std::string& symbol,
+                                                   vector_holder_ptr vector_base,
+                                                   expression_node_ptr index)
+         {
+            expression_node_ptr result = error_node();
+
+            if (details::is_constant_node(index))
+            {
+               std::size_t i = static_cast<std::size_t>(details::numeric::to_int64(index->value()));
+
+               details::free_node(*node_allocator_,index);
+
+               if (vector_base->rebaseable())
+               {
+                  return node_allocator_->allocate<rebasevector_celem_node_t>(i,vector_base);
+               }
+
+               scope_element& se = parser_->sem_.get_element(symbol,i);
+
+               if (se.index == i)
+               {
+                  result = se.var_node;
+               }
+               else
+               {
+                  scope_element nse;
+                  nse.name      = symbol;
+                  nse.active    = true;
+                  nse.ref_count = 1;
+                  nse.type      = scope_element::e_vecelem;
+                  nse.index     = i;
+                  nse.depth     = parser_->state_.scope_depth;
+                  nse.data      = 0;
+                  nse.var_node  = node_allocator_->allocate<variable_node_t>((*(*vector_base)[i]));
+
+                  if (!parser_->sem_.add_element(nse))
+                  {
+                     parser_->set_synthesis_error("Failed to add new local vector element to SEM [1]");
+
+                     parser_->sem_.free_element(nse);
+
+                     result = error_node();
+                  }
+
+                  exprtk_debug(("vector_element() - INFO - Added new local vector element: %s\n",nse.name.c_str()));
+
+                  parser_->state_.activate_side_effect("vector_element()");
+
+                  result = nse.var_node;
+               }
+            }
+            else if (vector_base->rebaseable())
+               result = node_allocator_->allocate<rebasevector_elem_node_t>(index,vector_base);
+            else
+               result = node_allocator_->allocate<vector_elem_node_t>(index,vector_base);
+
+            return result;
+         }
+
+      private:
+
+         template <std::size_t N, typename NodePtr>
+         inline bool is_constant_foldable(NodePtr (&b)[N]) const
+         {
+            for (std::size_t i = 0; i < N; ++i)
+            {
+               if (0 == b[i])
+                  return false;
+               else if (!details::is_constant_node(b[i]))
+                  return false;
+            }
+
+            return true;
+         }
+
+         template <typename NodePtr,
+                   typename Allocator,
+                   template <typename, typename> class Sequence>
+         inline bool is_constant_foldable(const Sequence<NodePtr,Allocator>& b) const
+         {
+            for (std::size_t i = 0; i < b.size(); ++i)
+            {
+               if (0 == b[i])
+                  return false;
+               else if (!details::is_constant_node(b[i]))
+                  return false;
+            }
+
+            return true;
+         }
+
+         void lodge_assignment(symbol_type cst, expression_node_ptr node)
+         {
+            parser_->state_.activate_side_effect("lodge_assignment()");
+
+            if (!parser_->dec_.collect_assignments())
+               return;
+
+            std::string symbol_name;
+
+            switch (cst)
+            {
+               case e_st_variable : symbol_name = parser_->symtab_store_
+                                                     .get_variable_name(node);
+                                    break;
+
+               #ifndef exprtk_disable_string_capabilities
+               case e_st_string   : symbol_name = parser_->symtab_store_
+                                                     .get_stringvar_name(node);
+                                    break;
+               #endif
+
+               case e_st_vector   : {
+                                       typedef details::vector_holder<T> vector_holder_t;
+
+                                       vector_holder_t& vh = static_cast<vector_node_t*>(node)->vec_holder();
+
+                                       symbol_name = parser_->symtab_store_.get_vector_name(&vh);
+                                    }
+                                    break;
+
+               case e_st_vecelem  : {
+                                       typedef details::vector_holder<T> vector_holder_t;
+
+                                       vector_holder_t& vh = static_cast<vector_elem_node_t*>(node)->vec_holder();
+
+                                       symbol_name = parser_->symtab_store_.get_vector_name(&vh);
+
+                                       cst = e_st_vector;
+                                    }
+                                    break;
+
+               default            : return;
+            }
+
+            if (!symbol_name.empty())
+            {
+               parser_->dec_.add_assignment(symbol_name,cst);
+            }
+         }
+
+         inline expression_node_ptr synthesize_assignment_expression(const details::operator_type& operation, expression_node_ptr (&branch)[2])
+         {
+            if (details::is_variable_node(branch[0]))
+            {
+               lodge_assignment(e_st_variable,branch[0]);
+
+               return synthesize_expression<assignment_node_t,2>(operation,branch);
+            }
+            else if (details::is_vector_elem_node(branch[0]))
+            {
+               lodge_assignment(e_st_vecelem,branch[0]);
+
+               return synthesize_expression<assignment_vec_elem_node_t, 2>(operation, branch);
+            }
+            else if (details::is_rebasevector_elem_node(branch[0]))
+            {
+               lodge_assignment(e_st_vecelem,branch[0]);
+
+               return synthesize_expression<assignment_rebasevec_elem_node_t, 2>(operation, branch);
+            }
+            else if (details::is_rebasevector_celem_node(branch[0]))
+            {
+               lodge_assignment(e_st_vecelem,branch[0]);
+
+               return synthesize_expression<assignment_rebasevec_celem_node_t, 2>(operation, branch);
+            }
+            #ifndef exprtk_disable_string_capabilities
+            else if (details::is_string_node(branch[0]))
+            {
+               lodge_assignment(e_st_string,branch[0]);
+
+               return synthesize_expression<assignment_string_node_t,2>(operation, branch);
+            }
+            else if (details::is_string_range_node(branch[0]))
+            {
+               lodge_assignment(e_st_string,branch[0]);
+
+               return synthesize_expression<assignment_string_range_node_t,2>(operation, branch);
+            }
+            #endif
+            else if (details::is_vector_node(branch[0]))
+            {
+               lodge_assignment(e_st_vector,branch[0]);
+
+               if (details::is_ivector_node(branch[1]))
+                  return synthesize_expression<assignment_vecvec_node_t,2>(operation, branch);
+              else
+                  return synthesize_expression<assignment_vec_node_t,2>(operation, branch);
+            }
+            else
+            {
+               parser_->set_synthesis_error("Invalid assignment operation.[1]");
+
+               return error_node();
+            }
+         }
+
+         inline expression_node_ptr synthesize_assignment_operation_expression(const details::operator_type& operation,
+                                                                               expression_node_ptr (&branch)[2])
+         {
+            if (details::is_variable_node(branch[0]))
+            {
+               lodge_assignment(e_st_variable,branch[0]);
+
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                                  \
+                  case op0 : return node_allocator_->                                                         \
+                                template allocate_rrr<typename details::assignment_op_node<Type,op1<Type> > > \
+                                   (operation, branch[0], branch[1]);                                         \
+
+                  case_stmt(details::e_addass,details::add_op)
+                  case_stmt(details::e_subass,details::sub_op)
+                  case_stmt(details::e_mulass,details::mul_op)
+                  case_stmt(details::e_divass,details::div_op)
+                  case_stmt(details::e_modass,details::mod_op)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else if (details::is_vector_elem_node(branch[0]))
+            {
+               lodge_assignment(e_st_vecelem,branch[0]);
+
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                                            \
+                  case op0 : return node_allocator_->                                                                   \
+                                 template allocate_rrr<typename details::assignment_vec_elem_op_node<Type,op1<Type> > > \
+                                    (operation, branch[0], branch[1]);                                                  \
+
+                  case_stmt(details::e_addass,details::add_op)
+                  case_stmt(details::e_subass,details::sub_op)
+                  case_stmt(details::e_mulass,details::mul_op)
+                  case_stmt(details::e_divass,details::div_op)
+                  case_stmt(details::e_modass,details::mod_op)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else if (details::is_rebasevector_elem_node(branch[0]))
+            {
+               lodge_assignment(e_st_vecelem,branch[0]);
+
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                                                  \
+                  case op0 : return node_allocator_->                                                                         \
+                                 template allocate_rrr<typename details::assignment_rebasevec_elem_op_node<Type,op1<Type> > > \
+                                    (operation, branch[0], branch[1]);                                                        \
+
+                  case_stmt(details::e_addass,details::add_op)
+                  case_stmt(details::e_subass,details::sub_op)
+                  case_stmt(details::e_mulass,details::mul_op)
+                  case_stmt(details::e_divass,details::div_op)
+                  case_stmt(details::e_modass,details::mod_op)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else if (details::is_rebasevector_celem_node(branch[0]))
+            {
+               lodge_assignment(e_st_vecelem,branch[0]);
+
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                                                   \
+                  case op0 : return node_allocator_->                                                                          \
+                                 template allocate_rrr<typename details::assignment_rebasevec_celem_op_node<Type,op1<Type> > > \
+                                    (operation, branch[0], branch[1]);                                                         \
+
+                  case_stmt(details::e_addass,details::add_op)
+                  case_stmt(details::e_subass,details::sub_op)
+                  case_stmt(details::e_mulass,details::mul_op)
+                  case_stmt(details::e_divass,details::div_op)
+                  case_stmt(details::e_modass,details::mod_op)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else if (details::is_vector_node(branch[0]))
+            {
+               lodge_assignment(e_st_vector,branch[0]);
+
+               if (details::is_ivector_node(branch[1]))
+               {
+                  switch (operation)
+                  {
+                     #define case_stmt(op0,op1)                                                                         \
+                     case op0 : return node_allocator_->                                                                \
+                                   template allocate_rrr<typename details::assignment_vecvec_op_node<Type,op1<Type> > > \
+                                      (operation, branch[0], branch[1]);                                                \
+
+                     case_stmt(details::e_addass,details::add_op)
+                     case_stmt(details::e_subass,details::sub_op)
+                     case_stmt(details::e_mulass,details::mul_op)
+                     case_stmt(details::e_divass,details::div_op)
+                     case_stmt(details::e_modass,details::mod_op)
+                     #undef case_stmt
+                     default : return error_node();
+                  }
+               }
+               else
+               {
+                  switch (operation)
+                  {
+                     #define case_stmt(op0,op1)                                                                      \
+                     case op0 : return node_allocator_->                                                             \
+                                   template allocate_rrr<typename details::assignment_vec_op_node<Type,op1<Type> > > \
+                                      (operation, branch[0], branch[1]);                                             \
+
+                     case_stmt(details::e_addass,details::add_op)
+                     case_stmt(details::e_subass,details::sub_op)
+                     case_stmt(details::e_mulass,details::mul_op)
+                     case_stmt(details::e_divass,details::div_op)
+                     case_stmt(details::e_modass,details::mod_op)
+                     #undef case_stmt
+                     default : return error_node();
+                  }
+               }
+            }
+            #ifndef exprtk_disable_string_capabilities
+            else if (
+                      (details::e_addass == operation) &&
+                      details::is_string_node(branch[0])
+                    )
+            {
+               typedef details::assignment_string_node<T,details::asn_addassignment> addass_t;
+
+               lodge_assignment(e_st_string,branch[0]);
+
+               return synthesize_expression<addass_t,2>(operation,branch);
+            }
+            #endif
+            else
+            {
+               parser_->set_synthesis_error("Invalid assignment operation[2]");
+
+               return error_node();
+            }
+         }
+
+         inline expression_node_ptr synthesize_veceqineqlogic_operation_expression(const details::operator_type& operation,
+                                                                                   expression_node_ptr (&branch)[2])
+         {
+            const bool is_b0_ivec = details::is_ivector_node(branch[0]);
+            const bool is_b1_ivec = details::is_ivector_node(branch[1]);
+
+            #define batch_eqineq_logic_case                \
+            case_stmt(details::   e_lt, details::   lt_op) \
+            case_stmt(details::  e_lte, details::  lte_op) \
+            case_stmt(details::   e_gt, details::   gt_op) \
+            case_stmt(details::  e_gte, details::  gte_op) \
+            case_stmt(details::   e_eq, details::   eq_op) \
+            case_stmt(details::   e_ne, details::   ne_op) \
+            case_stmt(details::e_equal, details::equal_op) \
+            case_stmt(details::  e_and, details::  and_op) \
+            case_stmt(details:: e_nand, details:: nand_op) \
+            case_stmt(details::   e_or, details::   or_op) \
+            case_stmt(details::  e_nor, details::  nor_op) \
+            case_stmt(details::  e_xor, details::  xor_op) \
+            case_stmt(details:: e_xnor, details:: xnor_op) \
+
+            if (is_b0_ivec && is_b1_ivec)
+            {
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                                     \
+                  case op0 : return node_allocator_->                                                            \
+                                template allocate_rrr<typename details::vec_binop_vecvec_node<Type,op1<Type> > > \
+                                   (operation, branch[0], branch[1]);                                            \
+
+                  batch_eqineq_logic_case
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else if (is_b0_ivec && !is_b1_ivec)
+            {
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                                     \
+                  case op0 : return node_allocator_->                                                            \
+                                template allocate_rrr<typename details::vec_binop_vecval_node<Type,op1<Type> > > \
+                                   (operation, branch[0], branch[1]);                                            \
+
+                  batch_eqineq_logic_case
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else if (!is_b0_ivec && is_b1_ivec)
+            {
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                                     \
+                  case op0 : return node_allocator_->                                                            \
+                                template allocate_rrr<typename details::vec_binop_valvec_node<Type,op1<Type> > > \
+                                   (operation, branch[0], branch[1]);                                            \
+
+                  batch_eqineq_logic_case
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else
+               return error_node();
+
+            #undef batch_eqineq_logic_case
+         }
+
+         inline expression_node_ptr synthesize_vecarithmetic_operation_expression(const details::operator_type& operation,
+                                                                                  expression_node_ptr (&branch)[2])
+         {
+            const bool is_b0_ivec = details::is_ivector_node(branch[0]);
+            const bool is_b1_ivec = details::is_ivector_node(branch[1]);
+
+            #define vector_ops                        \
+            case_stmt(details::e_add,details::add_op) \
+            case_stmt(details::e_sub,details::sub_op) \
+            case_stmt(details::e_mul,details::mul_op) \
+            case_stmt(details::e_div,details::div_op) \
+            case_stmt(details::e_mod,details::mod_op) \
+
+            if (is_b0_ivec && is_b1_ivec)
+            {
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                                     \
+                  case op0 : return node_allocator_->                                                            \
+                                template allocate_rrr<typename details::vec_binop_vecvec_node<Type,op1<Type> > > \
+                                   (operation, branch[0], branch[1]);                                            \
+
+                  vector_ops
+                  case_stmt(details::e_pow,details:: pow_op)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else if (is_b0_ivec && !is_b1_ivec)
+            {
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                                     \
+                  case op0 : return node_allocator_->                                                            \
+                                template allocate_rrr<typename details::vec_binop_vecval_node<Type,op1<Type> > > \
+                                   (operation, branch[0], branch[1]);                                            \
+
+                  vector_ops
+                  case_stmt(details::e_pow,details:: pow_op)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else if (!is_b0_ivec && is_b1_ivec)
+            {
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                                     \
+                  case op0 : return node_allocator_->                                                            \
+                                template allocate_rrr<typename details::vec_binop_valvec_node<Type,op1<Type> > > \
+                                   (operation, branch[0], branch[1]);                                            \
+
+                  vector_ops
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+            else
+               return error_node();
+
+            #undef vector_ops
+         }
+
+         inline expression_node_ptr synthesize_swap_expression(expression_node_ptr (&branch)[2])
+         {
+            const bool v0_is_ivar = details::is_ivariable_node(branch[0]);
+            const bool v1_is_ivar = details::is_ivariable_node(branch[1]);
+
+            const bool v0_is_ivec = details::is_ivector_node  (branch[0]);
+            const bool v1_is_ivec = details::is_ivector_node  (branch[1]);
+
+            #ifndef exprtk_disable_string_capabilities
+            const bool v0_is_str = details::is_generally_string_node(branch[0]);
+            const bool v1_is_str = details::is_generally_string_node(branch[1]);
+            #endif
+
+            expression_node_ptr result = error_node();
+
+            if (v0_is_ivar && v1_is_ivar)
+            {
+               typedef details::variable_node<T>* variable_node_ptr;
+
+               variable_node_ptr v0 = variable_node_ptr(0);
+               variable_node_ptr v1 = variable_node_ptr(0);
+
+               if (
+                    (0 != (v0 = dynamic_cast<variable_node_ptr>(branch[0]))) &&
+                    (0 != (v1 = dynamic_cast<variable_node_ptr>(branch[1])))
+                  )
+               {
+                  result = node_allocator_->allocate<details::swap_node<T> >(v0,v1);
+               }
+               else
+                  result = node_allocator_->allocate<details::swap_generic_node<T> >(branch[0],branch[1]);
+            }
+            else if (v0_is_ivec && v1_is_ivec)
+            {
+               result = node_allocator_->allocate<details::swap_vecvec_node<T> >(branch[0],branch[1]);
+            }
+            #ifndef exprtk_disable_string_capabilities
+            else if (v0_is_str && v1_is_str)
+            {
+               if (is_string_node(branch[0]) && is_string_node(branch[1]))
+                  result = node_allocator_->allocate<details::swap_string_node<T> >
+                                               (branch[0], branch[1]);
+               else
+                  result = node_allocator_->allocate<details::swap_genstrings_node<T> >
+                                               (branch[0], branch[1]);
+            }
+            #endif
+            else
+            {
+               parser_->set_synthesis_error("Only variables, strings, vectors or vector elements can be swapped");
+
+               return error_node();
+            }
+
+            parser_->state_.activate_side_effect("synthesize_swap_expression()");
+
+            return result;
+         }
+
+         #ifndef exprtk_disable_sc_andor
+         inline expression_node_ptr synthesize_shortcircuit_expression(const details::operator_type& operation, expression_node_ptr (&branch)[2])
+         {
+            expression_node_ptr result = error_node();
+
+            if (details::is_constant_node(branch[0]))
+            {
+               if (
+                    (details::e_scand == operation) &&
+                    std::equal_to<T>()(T(0),branch[0]->value())
+                  )
+                  result = node_allocator_->allocate_c<literal_node_t>(T(0));
+               else if (
+                         (details::e_scor == operation) &&
+                         std::not_equal_to<T>()(T(0),branch[0]->value())
+                       )
+                  result = node_allocator_->allocate_c<literal_node_t>(T(1));
+            }
+
+            if (details::is_constant_node(branch[1]) && (0 == result))
+            {
+               if (
+                    (details::e_scand == operation) &&
+                    std::equal_to<T>()(T(0),branch[1]->value())
+                  )
+                  result = node_allocator_->allocate_c<literal_node_t>(T(0));
+               else if (
+                         (details::e_scor == operation) &&
+                         std::not_equal_to<T>()(T(0),branch[1]->value())
+                       )
+                  result = node_allocator_->allocate_c<literal_node_t>(T(1));
+            }
+
+            if (result)
+            {
+               free_node(*node_allocator_, branch[0]);
+               free_node(*node_allocator_, branch[1]);
+
+               return result;
+            }
+            else if (details::e_scand == operation)
+            {
+               return synthesize_expression<scand_node_t,2>(operation, branch);
+            }
+            else if (details::e_scor == operation)
+            {
+               return synthesize_expression<scor_node_t,2>(operation, branch);
+            }
+            else
+               return error_node();
+         }
+         #else
+         inline expression_node_ptr synthesize_shortcircuit_expression(const details::operator_type&, expression_node_ptr (&)[2])
+         {
+            return error_node();
+         }
+         #endif
+
+         #define basic_opr_switch_statements        \
+         case_stmt(details::e_add, details::add_op) \
+         case_stmt(details::e_sub, details::sub_op) \
+         case_stmt(details::e_mul, details::mul_op) \
+         case_stmt(details::e_div, details::div_op) \
+         case_stmt(details::e_mod, details::mod_op) \
+         case_stmt(details::e_pow, details::pow_op) \
+
+         #define extended_opr_switch_statements       \
+         case_stmt(details::  e_lt, details::  lt_op) \
+         case_stmt(details:: e_lte, details:: lte_op) \
+         case_stmt(details::  e_gt, details::  gt_op) \
+         case_stmt(details:: e_gte, details:: gte_op) \
+         case_stmt(details::  e_eq, details::  eq_op) \
+         case_stmt(details::  e_ne, details::  ne_op) \
+         case_stmt(details:: e_and, details:: and_op) \
+         case_stmt(details::e_nand, details::nand_op) \
+         case_stmt(details::  e_or, details::  or_op) \
+         case_stmt(details:: e_nor, details:: nor_op) \
+         case_stmt(details:: e_xor, details:: xor_op) \
+         case_stmt(details::e_xnor, details::xnor_op) \
+
+         #ifndef exprtk_disable_cardinal_pow_optimisation
+         template <typename TType, template <typename, typename> class IPowNode>
+         inline expression_node_ptr cardinal_pow_optimisation_impl(const TType& v, const unsigned int& p)
+         {
+            switch (p)
+            {
+               #define case_stmt(cp)                                                     \
+               case cp : return node_allocator_->                                        \
+                            allocate<IPowNode<T,details::numeric::fast_exp<T,cp> > >(v); \
+
+               case_stmt( 1) case_stmt( 2) case_stmt( 3) case_stmt( 4)
+               case_stmt( 5) case_stmt( 6) case_stmt( 7) case_stmt( 8)
+               case_stmt( 9) case_stmt(10) case_stmt(11) case_stmt(12)
+               case_stmt(13) case_stmt(14) case_stmt(15) case_stmt(16)
+               case_stmt(17) case_stmt(18) case_stmt(19) case_stmt(20)
+               case_stmt(21) case_stmt(22) case_stmt(23) case_stmt(24)
+               case_stmt(25) case_stmt(26) case_stmt(27) case_stmt(28)
+               case_stmt(29) case_stmt(30) case_stmt(31) case_stmt(32)
+               case_stmt(33) case_stmt(34) case_stmt(35) case_stmt(36)
+               case_stmt(37) case_stmt(38) case_stmt(39) case_stmt(40)
+               case_stmt(41) case_stmt(42) case_stmt(43) case_stmt(44)
+               case_stmt(45) case_stmt(46) case_stmt(47) case_stmt(48)
+               case_stmt(49) case_stmt(50) case_stmt(51) case_stmt(52)
+               case_stmt(53) case_stmt(54) case_stmt(55) case_stmt(56)
+               case_stmt(57) case_stmt(58) case_stmt(59) case_stmt(60)
+               #undef case_stmt
+               default : return error_node();
+            }
+         }
+
+         inline expression_node_ptr cardinal_pow_optimisation(const T& v, const T& c)
+         {
+            const bool not_recipricol = (c >= T(0));
+            const unsigned int p = static_cast<unsigned int>(details::numeric::to_int32(details::numeric::abs(c)));
+
+            if (0 == p)
+               return node_allocator_->allocate_c<literal_node_t>(T(1));
+            else if (std::equal_to<T>()(T(2),c))
+            {
+               return node_allocator_->
+                  template allocate_rr<typename details::vov_node<Type,details::mul_op<Type> > >(v,v);
+            }
+            else
+            {
+               if (not_recipricol)
+                  return cardinal_pow_optimisation_impl<T,details::ipow_node>(v,p);
+               else
+                  return cardinal_pow_optimisation_impl<T,details::ipowinv_node>(v,p);
+            }
+         }
+
+         inline bool cardinal_pow_optimisable(const details::operator_type& operation, const T& c) const
+         {
+            return (details::e_pow == operation) && (details::numeric::abs(c) <= T(60)) && details::numeric::is_integer(c);
+         }
+
+         inline expression_node_ptr cardinal_pow_optimisation(expression_node_ptr (&branch)[2])
+         {
+            const Type c = static_cast<details::literal_node<Type>*>(branch[1])->value();
+            const bool not_recipricol = (c >= T(0));
+            const unsigned int p = static_cast<unsigned int>(details::numeric::to_int32(details::numeric::abs(c)));
+
+            node_allocator_->free(branch[1]);
+
+            if (0 == p)
+            {
+               details::free_all_nodes(*node_allocator_, branch);
+
+               return node_allocator_->allocate_c<literal_node_t>(T(1));
+            }
+            else if (not_recipricol)
+               return cardinal_pow_optimisation_impl<expression_node_ptr,details::bipow_node>(branch[0],p);
+            else
+               return cardinal_pow_optimisation_impl<expression_node_ptr,details::bipowninv_node>(branch[0],p);
+         }
+         #else
+         inline expression_node_ptr cardinal_pow_optimisation(T&, const T&)
+         {
+            return error_node();
+         }
+
+         inline bool cardinal_pow_optimisable(const details::operator_type&, const T&)
+         {
+            return false;
+         }
+
+         inline expression_node_ptr cardinal_pow_optimisation(expression_node_ptr(&)[2])
+         {
+            return error_node();
+         }
+         #endif
+
+         struct synthesize_binary_ext_expression
+         {
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               const bool left_neg  = is_neg_unary_node(branch[0]);
+               const bool right_neg = is_neg_unary_node(branch[1]);
+
+               if (left_neg && right_neg)
+               {
+                  if (
+                       (details::e_add == operation) ||
+                       (details::e_sub == operation) ||
+                       (details::e_mul == operation) ||
+                       (details::e_div == operation)
+                     )
+                  {
+                     if (
+                          !expr_gen.parser_->simplify_unary_negation_branch(branch[0]) ||
+                          !expr_gen.parser_->simplify_unary_negation_branch(branch[1])
+                        )
+                     {
+                        details::free_all_nodes(*expr_gen.node_allocator_,branch);
+
+                        return error_node();
+                     }
+                  }
+
+                  switch (operation)
+                  {
+                                           // -f(x + 1) + -g(y + 1) --> -(f(x + 1) + g(y + 1))
+                     case details::e_add : return expr_gen(details::e_neg,
+                                              expr_gen.node_allocator_->
+                                                 template allocate<typename details::binary_ext_node<Type,details::add_op<Type> > >
+                                                    (branch[0],branch[1]));
+
+                                           // -f(x + 1) - -g(y + 1) --> g(y + 1) - f(x + 1)
+                     case details::e_sub : return expr_gen.node_allocator_->
+                                              template allocate<typename details::binary_ext_node<Type,details::sub_op<Type> > >
+                                                 (branch[1],branch[0]);
+
+                     default             : break;
+                  }
+               }
+               else if (left_neg && !right_neg)
+               {
+                  if (
+                       (details::e_add == operation) ||
+                       (details::e_sub == operation) ||
+                       (details::e_mul == operation) ||
+                       (details::e_div == operation)
+                     )
+                  {
+                     if (!expr_gen.parser_->simplify_unary_negation_branch(branch[0]))
+                     {
+                        details::free_all_nodes(*expr_gen.node_allocator_,branch);
+
+                        return error_node();
+                     }
+
+                     switch (operation)
+                     {
+                                              // -f(x + 1) + g(y + 1) --> g(y + 1) - f(x + 1)
+                        case details::e_add : return expr_gen.node_allocator_->
+                                                 template allocate<typename details::binary_ext_node<Type,details::sub_op<Type> > >
+                                                   (branch[1], branch[0]);
+
+                                              // -f(x + 1) - g(y + 1) --> -(f(x + 1) + g(y + 1))
+                        case details::e_sub : return expr_gen(details::e_neg,
+                                                 expr_gen.node_allocator_->
+                                                    template allocate<typename details::binary_ext_node<Type,details::add_op<Type> > >
+                                                       (branch[0], branch[1]));
+
+                                              // -f(x + 1) * g(y + 1) --> -(f(x + 1) * g(y + 1))
+                        case details::e_mul : return expr_gen(details::e_neg,
+                                                 expr_gen.node_allocator_->
+                                                    template allocate<typename details::binary_ext_node<Type,details::mul_op<Type> > >
+                                                       (branch[0], branch[1]));
+
+                                              // -f(x + 1) / g(y + 1) --> -(f(x + 1) / g(y + 1))
+                        case details::e_div : return expr_gen(details::e_neg,
+                                                 expr_gen.node_allocator_->
+                                                    template allocate<typename details::binary_ext_node<Type,details::div_op<Type> > >
+                                                       (branch[0], branch[1]));
+
+                        default             : return error_node();
+                     }
+                  }
+               }
+               else if (!left_neg && right_neg)
+               {
+                  if (
+                       (details::e_add == operation) ||
+                       (details::e_sub == operation) ||
+                       (details::e_mul == operation) ||
+                       (details::e_div == operation)
+                     )
+                  {
+                     if (!expr_gen.parser_->simplify_unary_negation_branch(branch[1]))
+                     {
+                        details::free_all_nodes(*expr_gen.node_allocator_,branch);
+
+                        return error_node();
+                     }
+
+                     switch (operation)
+                     {
+                                              // f(x + 1) + -g(y + 1) --> f(x + 1) - g(y + 1)
+                        case details::e_add : return expr_gen.node_allocator_->
+                                                 template allocate<typename details::binary_ext_node<Type,details::sub_op<Type> > >
+                                                   (branch[0], branch[1]);
+
+                                              // f(x + 1) - - g(y + 1) --> f(x + 1) + g(y + 1)
+                        case details::e_sub : return expr_gen.node_allocator_->
+                                                 template allocate<typename details::binary_ext_node<Type,details::add_op<Type> > >
+                                                   (branch[0], branch[1]);
+
+                                              // f(x + 1) * -g(y + 1) --> -(f(x + 1) * g(y + 1))
+                        case details::e_mul : return expr_gen(details::e_neg,
+                                                 expr_gen.node_allocator_->
+                                                    template allocate<typename details::binary_ext_node<Type,details::mul_op<Type> > >
+                                                       (branch[0], branch[1]));
+
+                                              // f(x + 1) / -g(y + 1) --> -(f(x + 1) / g(y + 1))
+                        case details::e_div : return expr_gen(details::e_neg,
+                                                 expr_gen.node_allocator_->
+                                                    template allocate<typename details::binary_ext_node<Type,details::div_op<Type> > >
+                                                       (branch[0], branch[1]));
+
+                        default             : return error_node();
+                     }
+                  }
+               }
+
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                           \
+                  case op0 : return expr_gen.node_allocator_->                                         \
+                                template allocate<typename details::binary_ext_node<Type,op1<Type> > > \
+                                   (branch[0], branch[1]);                                             \
+
+                  basic_opr_switch_statements
+                  extended_opr_switch_statements
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+         };
+
+         struct synthesize_vob_expression
+         {
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               const Type& v = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+
+               #ifndef exprtk_disable_enhanced_features
+               if (details::is_sf3ext_node(branch[1]))
+               {
+                  expression_node_ptr result = error_node();
+
+                  const bool synthesis_result =
+                     synthesize_sf4ext_expression::template compile_right<vtype>
+                        (expr_gen, v, operation, branch[1], result);
+
+                  if (synthesis_result)
+                  {
+                     free_node(*expr_gen.node_allocator_,branch[1]);
+                     return result;
+                  }
+               }
+               #endif
+
+               if (
+                    (details::e_mul == operation) ||
+                    (details::e_div == operation)
+                  )
+               {
+                  if (details::is_uv_node(branch[1]))
+                  {
+                     typedef details::uv_base_node<Type>* uvbn_ptr_t;
+
+                     details::operator_type o = static_cast<uvbn_ptr_t>(branch[1])->operation();
+
+                     if (details::e_neg == o)
+                     {
+                        const Type& v1 = static_cast<uvbn_ptr_t>(branch[1])->v();
+
+                        free_node(*expr_gen.node_allocator_,branch[1]);
+
+                        switch (operation)
+                        {
+                           case details::e_mul : return expr_gen(details::e_neg,
+                                                    expr_gen.node_allocator_->
+                                                       template allocate_rr<typename details::
+                                                          vov_node<Type,details::mul_op<Type> > >(v,v1));
+
+                           case details::e_div : return expr_gen(details::e_neg,
+                                                    expr_gen.node_allocator_->
+                                                       template allocate_rr<typename details::
+                                                          vov_node<Type,details::div_op<Type> > >(v,v1));
+
+                           default             : break;
+                        }
+                     }
+                  }
+               }
+
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                       \
+                  case op0 : return expr_gen.node_allocator_->                                     \
+                                template allocate_rc<typename details::vob_node<Type,op1<Type> > > \
+                                   (v, branch[1]);                                                 \
+
+                  basic_opr_switch_statements
+                  extended_opr_switch_statements
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+         };
+
+         struct synthesize_bov_expression
+         {
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               const Type& v = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+
+               #ifndef exprtk_disable_enhanced_features
+               if (details::is_sf3ext_node(branch[0]))
+               {
+                  expression_node_ptr result = error_node();
+
+                  const bool synthesis_result =
+                     synthesize_sf4ext_expression::template compile_left<vtype>
+                        (expr_gen, v, operation, branch[0], result);
+
+                  if (synthesis_result)
+                  {
+                     free_node(*expr_gen.node_allocator_, branch[0]);
+
+                     return result;
+                  }
+               }
+               #endif
+
+               if (
+                    (details::e_add == operation) ||
+                    (details::e_sub == operation) ||
+                    (details::e_mul == operation) ||
+                    (details::e_div == operation)
+                  )
+               {
+                  if (details::is_uv_node(branch[0]))
+                  {
+                     typedef details::uv_base_node<Type>* uvbn_ptr_t;
+
+                     details::operator_type o = static_cast<uvbn_ptr_t>(branch[0])->operation();
+
+                     if (details::e_neg == o)
+                     {
+                        const Type& v0 = static_cast<uvbn_ptr_t>(branch[0])->v();
+
+                        free_node(*expr_gen.node_allocator_,branch[0]);
+
+                        switch (operation)
+                        {
+                           case details::e_add : return expr_gen.node_allocator_->
+                                                    template allocate_rr<typename details::
+                                                       vov_node<Type,details::sub_op<Type> > >(v,v0);
+
+                           case details::e_sub : return expr_gen(details::e_neg,
+                                                    expr_gen.node_allocator_->
+                                                       template allocate_rr<typename details::
+                                                          vov_node<Type,details::add_op<Type> > >(v0,v));
+
+                           case details::e_mul : return expr_gen(details::e_neg,
+                                                    expr_gen.node_allocator_->
+                                                       template allocate_rr<typename details::
+                                                          vov_node<Type,details::mul_op<Type> > >(v0,v));
+
+                           case details::e_div : return expr_gen(details::e_neg,
+                                                    expr_gen.node_allocator_->
+                                                       template allocate_rr<typename details::
+                                                          vov_node<Type,details::div_op<Type> > >(v0,v));
+                           default : break;
+                        }
+                     }
+                  }
+               }
+
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                       \
+                  case op0 : return expr_gen.node_allocator_->                                     \
+                                template allocate_cr<typename details::bov_node<Type,op1<Type> > > \
+                                   (branch[0], v);                                                 \
+
+                  basic_opr_switch_statements
+                  extended_opr_switch_statements
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+         };
+
+         struct synthesize_cob_expression
+         {
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               const Type c = static_cast<details::literal_node<Type>*>(branch[0])->value();
+
+               free_node(*expr_gen.node_allocator_,branch[0]);
+
+               if (std::equal_to<T>()(T(0),c) && (details::e_mul == operation))
+               {
+                  free_node(*expr_gen.node_allocator_,branch[1]);
+
+                  return expr_gen(T(0));
+               }
+               else if (std::equal_to<T>()(T(0),c) && (details::e_div == operation))
+               {
+                  free_node(*expr_gen.node_allocator_, branch[1]);
+
+                  return expr_gen(T(0));
+               }
+               else if (std::equal_to<T>()(T(0),c) && (details::e_add == operation))
+                  return branch[1];
+               else if (std::equal_to<T>()(T(1),c) && (details::e_mul == operation))
+                  return branch[1];
+
+               if (details::is_cob_node(branch[1]))
+               {
+                  // Simplify expressions of the form:
+                  // 1. (1 * (2 * (3 * (4 * (5 * (6 * (7 * (8 * (9 + x))))))))) --> 40320 * (9 + x)
+                  // 2. (1 + (2 + (3 + (4 + (5 + (6 + (7 + (8 + (9 + x))))))))) --> 45 + x
+                  if (
+                       (operation == details::e_mul) ||
+                       (operation == details::e_add)
+                     )
+                  {
+                     details::cob_base_node<Type>* cobnode = static_cast<details::cob_base_node<Type>*>(branch[1]);
+
+                     if (operation == cobnode->operation())
+                     {
+                        switch (operation)
+                        {
+                           case details::e_add : cobnode->set_c(c + cobnode->c()); break;
+                           case details::e_mul : cobnode->set_c(c * cobnode->c()); break;
+                           default             : return error_node();
+                        }
+
+                        return cobnode;
+                     }
+                  }
+
+                  if (operation == details::e_mul)
+                  {
+                     details::cob_base_node<Type>* cobnode = static_cast<details::cob_base_node<Type>*>(branch[1]);
+                     details::operator_type cob_opr = cobnode->operation();
+
+                     if (
+                          (details::e_div == cob_opr) ||
+                          (details::e_mul == cob_opr)
+                        )
+                     {
+                        switch (cob_opr)
+                        {
+                           case details::e_div : cobnode->set_c(c * cobnode->c()); break;
+                           case details::e_mul : cobnode->set_c(cobnode->c() / c); break;
+                           default             : return error_node();
+                        }
+
+                        return cobnode;
+                     }
+                  }
+                  else if (operation == details::e_div)
+                  {
+                     details::cob_base_node<Type>* cobnode = static_cast<details::cob_base_node<Type>*>(branch[1]);
+                     details::operator_type cob_opr = cobnode->operation();
+
+                     if (
+                          (details::e_div == cob_opr) ||
+                          (details::e_mul == cob_opr)
+                        )
+                     {
+                        details::expression_node<Type>* new_cobnode = error_node();
+
+                        switch (cob_opr)
+                        {
+                           case details::e_div : new_cobnode = expr_gen.node_allocator_->
+                                                    template allocate_tt<typename details::cob_node<Type,details::mul_op<Type> > >
+                                                       (c / cobnode->c(), cobnode->move_branch(0));
+                                                 break;
+
+                           case details::e_mul : new_cobnode = expr_gen.node_allocator_->
+                                                    template allocate_tt<typename details::cob_node<Type,details::div_op<Type> > >
+                                                       (c / cobnode->c(), cobnode->move_branch(0));
+                                                 break;
+
+                           default             : return error_node();
+                        }
+
+                        free_node(*expr_gen.node_allocator_,branch[1]);
+
+                        return new_cobnode;
+                     }
+                  }
+               }
+               #ifndef exprtk_disable_enhanced_features
+               else if (details::is_sf3ext_node(branch[1]))
+               {
+                  expression_node_ptr result = error_node();
+
+                  const bool synthesis_result =
+                     synthesize_sf4ext_expression::template compile_right<ctype>
+                        (expr_gen, c, operation, branch[1], result);
+
+                  if (synthesis_result)
+                  {
+                     free_node(*expr_gen.node_allocator_,branch[1]);
+
+                     return result;
+                  }
+               }
+               #endif
+
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                       \
+                  case op0 : return expr_gen.node_allocator_->                                     \
+                                template allocate_tt<typename details::cob_node<Type,op1<Type> > > \
+                                   (c,  branch[1]);                                                \
+
+                  basic_opr_switch_statements
+                  extended_opr_switch_statements
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+         };
+
+         struct synthesize_boc_expression
+         {
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               const Type c = static_cast<details::literal_node<Type>*>(branch[1])->value();
+
+               details::free_node(*(expr_gen.node_allocator_), branch[1]);
+
+               if (std::equal_to<T>()(T(0),c) && (details::e_mul == operation))
+               {
+                  free_node(*expr_gen.node_allocator_, branch[0]);
+
+                  return expr_gen(T(0));
+               }
+               else if (std::equal_to<T>()(T(0),c) && (details::e_div == operation))
+               {
+                  free_node(*expr_gen.node_allocator_, branch[0]);
+
+                  return expr_gen(std::numeric_limits<T>::quiet_NaN());
+               }
+               else if (std::equal_to<T>()(T(0),c) && (details::e_add == operation))
+                  return branch[0];
+               else if (std::equal_to<T>()(T(1),c) && (details::e_mul == operation))
+                  return branch[0];
+
+               if (details::is_boc_node(branch[0]))
+               {
+                  // Simplify expressions of the form:
+                  // 1. (((((((((x + 9) * 8) * 7) * 6) * 5) * 4) * 3) * 2) * 1) --> (x + 9) * 40320
+                  // 2. (((((((((x + 9) + 8) + 7) + 6) + 5) + 4) + 3) + 2) + 1) --> x + 45
+                  if (
+                       (operation == details::e_mul) ||
+                       (operation == details::e_add)
+                     )
+                  {
+                     details::boc_base_node<Type>* bocnode = static_cast<details::boc_base_node<Type>*>(branch[0]);
+
+                     if (operation == bocnode->operation())
+                     {
+                        switch (operation)
+                        {
+                           case details::e_add : bocnode->set_c(c + bocnode->c()); break;
+                           case details::e_mul : bocnode->set_c(c * bocnode->c()); break;
+                           default             : return error_node();
+                        }
+
+                        return bocnode;
+                     }
+                  }
+                  else if (operation == details::e_div)
+                  {
+                     details::boc_base_node<Type>* bocnode = static_cast<details::boc_base_node<Type>*>(branch[0]);
+                     details::operator_type        boc_opr = bocnode->operation();
+
+                     if (
+                          (details::e_div == boc_opr) ||
+                          (details::e_mul == boc_opr)
+                        )
+                     {
+                        switch (boc_opr)
+                        {
+                           case details::e_div : bocnode->set_c(c * bocnode->c()); break;
+                           case details::e_mul : bocnode->set_c(bocnode->c() / c); break;
+                           default             : return error_node();
+                        }
+
+                        return bocnode;
+                     }
+                  }
+                  else if (operation == details::e_pow)
+                  {
+                     // (v ^ c0) ^ c1 --> v ^(c0 * c1)
+                     details::boc_base_node<Type>* bocnode = static_cast<details::boc_base_node<Type>*>(branch[0]);
+                     details::operator_type        boc_opr = bocnode->operation();
+
+                     if (details::e_pow == boc_opr)
+                     {
+                        bocnode->set_c(bocnode->c() * c);
+
+                        return bocnode;
+                     }
+                  }
+               }
+
+               #ifndef exprtk_disable_enhanced_features
+               if (details::is_sf3ext_node(branch[0]))
+               {
+                  expression_node_ptr result = error_node();
+
+                  const bool synthesis_result =
+                     synthesize_sf4ext_expression::template compile_left<ctype>
+                        (expr_gen, c, operation, branch[0], result);
+
+                  if (synthesis_result)
+                  {
+                     free_node(*expr_gen.node_allocator_, branch[0]);
+
+                     return result;
+                  }
+               }
+               #endif
+
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                       \
+                  case op0 : return expr_gen.node_allocator_->                                     \
+                                template allocate_cr<typename details::boc_node<Type,op1<Type> > > \
+                                   (branch[0], c);                                                 \
+
+                  basic_opr_switch_statements
+                  extended_opr_switch_statements
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+         };
+
+         struct synthesize_cocob_expression
+         {
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               expression_node_ptr result = error_node();
+
+               // (cob) o c --> cob
+               if (details::is_cob_node(branch[0]))
+               {
+                  details::cob_base_node<Type>* cobnode = static_cast<details::cob_base_node<Type>*>(branch[0]);
+
+                  const Type c = static_cast<details::literal_node<Type>*>(branch[1])->value();
+
+                  if (std::equal_to<T>()(T(0),c) && (details::e_mul == operation))
+                  {
+                     free_node(*expr_gen.node_allocator_, branch[0]);
+                     free_node(*expr_gen.node_allocator_, branch[1]);
+
+                     return expr_gen(T(0));
+                  }
+                  else if (std::equal_to<T>()(T(0),c) && (details::e_div == operation))
+                  {
+                     free_node(*expr_gen.node_allocator_, branch[0]);
+                     free_node(*expr_gen.node_allocator_, branch[1]);
+
+                     return expr_gen(T(std::numeric_limits<T>::quiet_NaN()));
+                  }
+                  else if (std::equal_to<T>()(T(0),c) && (details::e_add == operation))
+                  {
+                     free_node(*expr_gen.node_allocator_, branch[1]);
+
+                     return branch[0];
+                  }
+                  else if (std::equal_to<T>()(T(1),c) && (details::e_mul == operation))
+                  {
+                     free_node(*expr_gen.node_allocator_, branch[1]);
+
+                     return branch[0];
+                  }
+                  else if (std::equal_to<T>()(T(1),c) && (details::e_div == operation))
+                  {
+                     free_node(*expr_gen.node_allocator_, branch[1]);
+
+                     return branch[0];
+                  }
+
+                  const bool op_addsub = (details::e_add == cobnode->operation()) ||
+                                         (details::e_sub == cobnode->operation()) ;
+
+                  if (op_addsub)
+                  {
+                     switch (operation)
+                     {
+                        case details::e_add : cobnode->set_c(cobnode->c() + c); break;
+                        case details::e_sub : cobnode->set_c(cobnode->c() - c); break;
+                        default             : return error_node();
+                     }
+
+                     result = cobnode;
+                  }
+                  else if (details::e_mul == cobnode->operation())
+                  {
+                     switch (operation)
+                     {
+                        case details::e_mul : cobnode->set_c(cobnode->c() * c); break;
+                        case details::e_div : cobnode->set_c(cobnode->c() / c); break;
+                        default             : return error_node();
+                     }
+
+                     result = cobnode;
+                  }
+                  else if (details::e_div == cobnode->operation())
+                  {
+                     if (details::e_mul == operation)
+                     {
+                        cobnode->set_c(cobnode->c() * c);
+                        result = cobnode;
+                     }
+                     else if (details::e_div == operation)
+                     {
+                        result = expr_gen.node_allocator_->
+                                    template allocate_tt<typename details::cob_node<Type,details::div_op<Type> > >
+                                       (cobnode->c() / c, cobnode->move_branch(0));
+
+                        free_node(*expr_gen.node_allocator_, branch[0]);
+                     }
+                  }
+
+                  if (result)
+                  {
+                     free_node(*expr_gen.node_allocator_,branch[1]);
+                  }
+               }
+
+               // c o (cob) --> cob
+               else if (details::is_cob_node(branch[1]))
+               {
+                  details::cob_base_node<Type>* cobnode = static_cast<details::cob_base_node<Type>*>(branch[1]);
+
+                  const Type c = static_cast<details::literal_node<Type>*>(branch[0])->value();
+
+                  if (std::equal_to<T>()(T(0),c) && (details::e_mul == operation))
+                  {
+                     free_node(*expr_gen.node_allocator_, branch[0]);
+                     free_node(*expr_gen.node_allocator_, branch[1]);
+
+                     return expr_gen(T(0));
+                  }
+                  else if (std::equal_to<T>()(T(0),c) && (details::e_div == operation))
+                  {
+                     free_node(*expr_gen.node_allocator_, branch[0]);
+                     free_node(*expr_gen.node_allocator_, branch[1]);
+
+                     return expr_gen(T(0));
+                  }
+                  else if (std::equal_to<T>()(T(0),c) && (details::e_add == operation))
+                  {
+                     free_node(*expr_gen.node_allocator_, branch[0]);
+
+                     return branch[1];
+                  }
+                  else if (std::equal_to<T>()(T(1),c) && (details::e_mul == operation))
+                  {
+                     free_node(*expr_gen.node_allocator_, branch[0]);
+
+                     return branch[1];
+                  }
+
+                  if (details::e_add == cobnode->operation())
+                  {
+                     if (details::e_add == operation)
+                     {
+                        cobnode->set_c(c + cobnode->c());
+                        result = cobnode;
+                     }
+                     else if (details::e_sub == operation)
+                     {
+                        result = expr_gen.node_allocator_->
+                                    template allocate_tt<typename details::cob_node<Type,details::sub_op<Type> > >
+                                       (c - cobnode->c(), cobnode->move_branch(0));
+
+                        free_node(*expr_gen.node_allocator_,branch[1]);
+                     }
+                  }
+                  else if (details::e_sub == cobnode->operation())
+                  {
+                     if (details::e_add == operation)
+                     {
+                        cobnode->set_c(c + cobnode->c());
+                        result = cobnode;
+                     }
+                     else if (details::e_sub == operation)
+                     {
+                        result = expr_gen.node_allocator_->
+                                    template allocate_tt<typename details::cob_node<Type,details::add_op<Type> > >
+                                       (c - cobnode->c(), cobnode->move_branch(0));
+
+                        free_node(*expr_gen.node_allocator_,branch[1]);
+                     }
+                  }
+                  else if (details::e_mul == cobnode->operation())
+                  {
+                     if (details::e_mul == operation)
+                     {
+                        cobnode->set_c(c * cobnode->c());
+                        result = cobnode;
+                     }
+                     else if (details::e_div == operation)
+                     {
+                        result = expr_gen.node_allocator_->
+                                    template allocate_tt<typename details::cob_node<Type,details::div_op<Type> > >
+                                       (c / cobnode->c(), cobnode->move_branch(0));
+
+                        free_node(*expr_gen.node_allocator_,branch[1]);
+                     }
+                  }
+                  else if (details::e_div == cobnode->operation())
+                  {
+                     if (details::e_mul == operation)
+                     {
+                        cobnode->set_c(c * cobnode->c());
+                        result = cobnode;
+                     }
+                     else if (details::e_div == operation)
+                     {
+                        result = expr_gen.node_allocator_->
+                                    template allocate_tt<typename details::cob_node<Type,details::mul_op<Type> > >
+                                       (c / cobnode->c(), cobnode->move_branch(0));
+
+                        free_node(*expr_gen.node_allocator_,branch[1]);
+                     }
+                  }
+
+                  if (result)
+                  {
+                     free_node(*expr_gen.node_allocator_,branch[0]);
+                  }
+               }
+
+               return result;
+            }
+         };
+
+         struct synthesize_coboc_expression
+         {
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               expression_node_ptr result = error_node();
+
+               // (boc) o c --> boc
+               if (details::is_boc_node(branch[0]))
+               {
+                  details::boc_base_node<Type>* bocnode = static_cast<details::boc_base_node<Type>*>(branch[0]);
+
+                  const Type c = static_cast<details::literal_node<Type>*>(branch[1])->value();
+
+                  if (details::e_add == bocnode->operation())
+                  {
+                     switch (operation)
+                     {
+                        case details::e_add : bocnode->set_c(bocnode->c() + c); break;
+                        case details::e_sub : bocnode->set_c(bocnode->c() - c); break;
+                        default             : return error_node();
+                     }
+
+                     result = bocnode;
+                  }
+                  else if (details::e_mul == bocnode->operation())
+                  {
+                     switch (operation)
+                     {
+                        case details::e_mul : bocnode->set_c(bocnode->c() * c); break;
+                        case details::e_div : bocnode->set_c(bocnode->c() / c); break;
+                        default             : return error_node();
+                     }
+
+                     result = bocnode;
+                  }
+                  else if (details::e_sub == bocnode->operation())
+                  {
+                     if (details::e_add == operation)
+                     {
+                        result = expr_gen.node_allocator_->
+                                    template allocate_tt<typename details::boc_node<Type,details::add_op<Type> > >
+                                       (bocnode->move_branch(0), c - bocnode->c());
+
+                        free_node(*expr_gen.node_allocator_,branch[0]);
+                     }
+                     else if (details::e_sub == operation)
+                     {
+                        bocnode->set_c(bocnode->c() + c);
+                        result = bocnode;
+                     }
+                  }
+                  else if (details::e_div == bocnode->operation())
+                  {
+                     switch (operation)
+                     {
+                        case details::e_div : bocnode->set_c(bocnode->c() * c); break;
+                        case details::e_mul : bocnode->set_c(bocnode->c() / c); break;
+                        default             : return error_node();
+                     }
+
+                     result = bocnode;
+                  }
+
+                  if (result)
+                  {
+                     free_node(*expr_gen.node_allocator_, branch[1]);
+                  }
+               }
+
+               // c o (boc) --> boc
+               else if (details::is_boc_node(branch[1]))
+               {
+                  details::boc_base_node<Type>* bocnode = static_cast<details::boc_base_node<Type>*>(branch[1]);
+
+                  const Type c = static_cast<details::literal_node<Type>*>(branch[0])->value();
+
+                  if (details::e_add == bocnode->operation())
+                  {
+                     if (details::e_add == operation)
+                     {
+                        bocnode->set_c(c + bocnode->c());
+                        result = bocnode;
+                     }
+                     else if (details::e_sub == operation)
+                     {
+                        result = expr_gen.node_allocator_->
+                                    template allocate_tt<typename details::cob_node<Type,details::sub_op<Type> > >
+                                       (c - bocnode->c(), bocnode->move_branch(0));
+
+                        free_node(*expr_gen.node_allocator_,branch[1]);
+                     }
+                  }
+                  else if (details::e_sub == bocnode->operation())
+                  {
+                     if (details::e_add == operation)
+                     {
+                        result = expr_gen.node_allocator_->
+                                    template allocate_tt<typename details::boc_node<Type,details::add_op<Type> > >
+                                       (bocnode->move_branch(0), c - bocnode->c());
+
+                        free_node(*expr_gen.node_allocator_,branch[1]);
+                     }
+                     else if (details::e_sub == operation)
+                     {
+                        result = expr_gen.node_allocator_->
+                                    template allocate_tt<typename details::cob_node<Type,details::sub_op<Type> > >
+                                       (c + bocnode->c(), bocnode->move_branch(0));
+
+                        free_node(*expr_gen.node_allocator_,branch[1]);
+                     }
+                  }
+                  else if (details::e_mul == bocnode->operation())
+                  {
+                     if (details::e_mul == operation)
+                     {
+                        bocnode->set_c(c * bocnode->c());
+                        result = bocnode;
+                     }
+                     else if (details::e_div == operation)
+                     {
+                        result = expr_gen.node_allocator_->
+                                    template allocate_tt<typename details::cob_node<Type,details::div_op<Type> > >
+                                       (c / bocnode->c(), bocnode->move_branch(0));
+
+                        free_node(*expr_gen.node_allocator_,branch[1]);
+                     }
+                  }
+                  else if (details::e_div == bocnode->operation())
+                  {
+                     if (details::e_mul == operation)
+                     {
+                        bocnode->set_c(bocnode->c() / c);
+                        result = bocnode;
+                     }
+                     else if (details::e_div == operation)
+                     {
+                        result = expr_gen.node_allocator_->
+                                    template allocate_tt<typename details::cob_node<Type,details::div_op<Type> > >
+                                       (c * bocnode->c(), bocnode->move_branch(0));
+
+                        free_node(*expr_gen.node_allocator_,branch[1]);
+                     }
+                  }
+
+                  if (result)
+                  {
+                     free_node(*expr_gen.node_allocator_,branch[0]);
+                  }
+               }
+
+               return result;
+            }
+         };
+
+         #ifndef exprtk_disable_enhanced_features
+         inline bool synthesize_expression(const details::operator_type& operation,
+                                           expression_node_ptr (&branch)[2],
+                                           expression_node_ptr& result)
+         {
+            result = error_node();
+
+            if (!operation_optimisable(operation))
+               return false;
+
+            const std::string node_id = branch_to_id(branch);
+
+            const typename synthesize_map_t::iterator itr = synthesize_map_.find(node_id);
+
+            if (synthesize_map_.end() != itr)
+            {
+               result = itr->second((*this), operation, branch);
+
+               return true;
+            }
+            else
+               return false;
+         }
+
+         struct synthesize_vov_expression
+         {
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               const Type& v1 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type& v2 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                       \
+                  case op0 : return expr_gen.node_allocator_->                                     \
+                                template allocate_rr<typename details::vov_node<Type,op1<Type> > > \
+                                   (v1, v2);                                                       \
+
+                  basic_opr_switch_statements
+                  extended_opr_switch_statements
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+         };
+
+         struct synthesize_cov_expression
+         {
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               const Type  c = static_cast<details::literal_node<Type>*> (branch[0])->value();
+               const Type& v = static_cast<details::variable_node<Type>*>(branch[1])->ref  ();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               if (std::equal_to<T>()(T(0),c) && (details::e_mul == operation))
+                  return expr_gen(T(0));
+               else if (std::equal_to<T>()(T(0),c) && (details::e_div == operation))
+                  return expr_gen(T(0));
+               else if (std::equal_to<T>()(T(0),c) && (details::e_add == operation))
+                  return static_cast<details::variable_node<Type>*>(branch[1]);
+               else if (std::equal_to<T>()(T(1),c) && (details::e_mul == operation))
+                  return static_cast<details::variable_node<Type>*>(branch[1]);
+
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                       \
+                  case op0 : return expr_gen.node_allocator_->                                     \
+                                template allocate_cr<typename details::cov_node<Type,op1<Type> > > \
+                                   (c, v);                                                         \
+
+                  basic_opr_switch_statements
+                  extended_opr_switch_statements
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+         };
+
+         struct synthesize_voc_expression
+         {
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               const Type& v = static_cast<details::variable_node<Type>*>(branch[0])->ref  ();
+               const Type  c = static_cast<details::literal_node<Type>*> (branch[1])->value();
+
+               details::free_node(*(expr_gen.node_allocator_), branch[1]);
+
+               if (expr_gen.cardinal_pow_optimisable(operation,c))
+               {
+                  if (std::equal_to<T>()(T(1),c))
+                     return branch[0];
+                  else
+                     return expr_gen.cardinal_pow_optimisation(v,c);
+               }
+               else if (std::equal_to<T>()(T(0),c) && (details::e_mul == operation))
+                  return expr_gen(T(0));
+               else if (std::equal_to<T>()(T(0),c) && (details::e_div == operation))
+                  return expr_gen(std::numeric_limits<T>::quiet_NaN());
+               else if (std::equal_to<T>()(T(0),c) && (details::e_add == operation))
+                  return static_cast<details::variable_node<Type>*>(branch[0]);
+               else if (std::equal_to<T>()(T(1),c) && (details::e_mul == operation))
+                  return static_cast<details::variable_node<Type>*>(branch[0]);
+               else if (std::equal_to<T>()(T(1),c) && (details::e_div == operation))
+                  return static_cast<details::variable_node<Type>*>(branch[0]);
+
+               switch (operation)
+               {
+                  #define case_stmt(op0,op1)                                                       \
+                  case op0 : return expr_gen.node_allocator_->                                     \
+                                template allocate_rc<typename details::voc_node<Type,op1<Type> > > \
+                                   (v, c);                                                         \
+
+                  basic_opr_switch_statements
+                  extended_opr_switch_statements
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+         };
+
+         struct synthesize_sf3ext_expression
+         {
+            template <typename T0, typename T1, typename T2>
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& sf3opr,
+                                                      T0 t0, T1 t1, T2 t2)
+            {
+               switch (sf3opr)
+               {
+                  #define case_stmt(op)                                                                              \
+                  case details::e_sf##op : return details::T0oT1oT2_sf3ext<T,T0,T1,T2,details::sf##op##_op<Type> >:: \
+                                allocate(*(expr_gen.node_allocator_), t0, t1, t2);                                   \
+
+                  case_stmt(00) case_stmt(01) case_stmt(02) case_stmt(03)
+                  case_stmt(04) case_stmt(05) case_stmt(06) case_stmt(07)
+                  case_stmt(08) case_stmt(09) case_stmt(10) case_stmt(11)
+                  case_stmt(12) case_stmt(13) case_stmt(14) case_stmt(15)
+                  case_stmt(16) case_stmt(17) case_stmt(18) case_stmt(19)
+                  case_stmt(20) case_stmt(21) case_stmt(22) case_stmt(23)
+                  case_stmt(24) case_stmt(25) case_stmt(26) case_stmt(27)
+                  case_stmt(28) case_stmt(29) case_stmt(30)
+                  #undef case_stmt
+                  default : return error_node();
+               }
+            }
+
+            template <typename T0, typename T1, typename T2>
+            static inline bool compile(expression_generator<Type>& expr_gen, const std::string& id,
+                                       T0 t0, T1 t1, T2 t2,
+                                       expression_node_ptr& result)
+            {
+               details::operator_type sf3opr;
+
+               if (!expr_gen.sf3_optimisable(id,sf3opr))
+                  return false;
+               else
+                  result = synthesize_sf3ext_expression::template process<T0, T1, T2>
+                              (expr_gen, sf3opr, t0, t1, t2);
+
+               return true;
+            }
+         };
+
+         struct synthesize_sf4ext_expression
+         {
+            template <typename T0, typename T1, typename T2, typename T3>
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& sf4opr,
+                                                      T0 t0, T1 t1, T2 t2, T3 t3)
+            {
+               switch (sf4opr)
+               {
+                  #define case_stmt0(op)                                                                                      \
+                  case details::e_sf##op : return details::T0oT1oT2oT3_sf4ext<Type,T0,T1,T2,T3,details::sf##op##_op<Type> >:: \
+                                allocate(*(expr_gen.node_allocator_), t0, t1, t2, t3);                                        \
+
+
+                  #define case_stmt1(op)                                                                                             \
+                  case details::e_sf4ext##op : return details::T0oT1oT2oT3_sf4ext<Type,T0,T1,T2,T3,details::sfext##op##_op<Type> >:: \
+                                allocate(*(expr_gen.node_allocator_), t0, t1, t2, t3);                                               \
+
+                  case_stmt0(48) case_stmt0(49) case_stmt0(50) case_stmt0(51)
+                  case_stmt0(52) case_stmt0(53) case_stmt0(54) case_stmt0(55)
+                  case_stmt0(56) case_stmt0(57) case_stmt0(58) case_stmt0(59)
+                  case_stmt0(60) case_stmt0(61) case_stmt0(62) case_stmt0(63)
+                  case_stmt0(64) case_stmt0(65) case_stmt0(66) case_stmt0(67)
+                  case_stmt0(68) case_stmt0(69) case_stmt0(70) case_stmt0(71)
+                  case_stmt0(72) case_stmt0(73) case_stmt0(74) case_stmt0(75)
+                  case_stmt0(76) case_stmt0(77) case_stmt0(78) case_stmt0(79)
+                  case_stmt0(80) case_stmt0(81) case_stmt0(82) case_stmt0(83)
+
+                  case_stmt1(00) case_stmt1(01) case_stmt1(02) case_stmt1(03)
+                  case_stmt1(04) case_stmt1(05) case_stmt1(06) case_stmt1(07)
+                  case_stmt1(08) case_stmt1(09) case_stmt1(10) case_stmt1(11)
+                  case_stmt1(12) case_stmt1(13) case_stmt1(14) case_stmt1(15)
+                  case_stmt1(16) case_stmt1(17) case_stmt1(18) case_stmt1(19)
+                  case_stmt1(20) case_stmt1(21) case_stmt1(22) case_stmt1(23)
+                  case_stmt1(24) case_stmt1(25) case_stmt1(26) case_stmt1(27)
+                  case_stmt1(28) case_stmt1(29) case_stmt1(30) case_stmt1(31)
+                  case_stmt1(32) case_stmt1(33) case_stmt1(34) case_stmt1(35)
+                  case_stmt1(36) case_stmt1(37) case_stmt1(38) case_stmt1(39)
+                  case_stmt1(40) case_stmt1(41) case_stmt1(42) case_stmt1(43)
+                  case_stmt1(44) case_stmt1(45) case_stmt1(46) case_stmt1(47)
+                  case_stmt1(48) case_stmt1(49) case_stmt1(50) case_stmt1(51)
+                  case_stmt1(52) case_stmt1(53) case_stmt1(54) case_stmt1(55)
+                  case_stmt1(56) case_stmt1(57) case_stmt1(58) case_stmt1(59)
+                  case_stmt1(60) case_stmt1(61)
+
+                  #undef case_stmt0
+                  #undef case_stmt1
+                  default : return error_node();
+               }
+            }
+
+            template <typename T0, typename T1, typename T2, typename T3>
+            static inline bool compile(expression_generator<Type>& expr_gen, const std::string& id,
+                                       T0 t0, T1 t1, T2 t2, T3 t3,
+                                       expression_node_ptr& result)
+            {
+               details::operator_type sf4opr;
+
+               if (!expr_gen.sf4_optimisable(id,sf4opr))
+                  return false;
+               else
+                  result = synthesize_sf4ext_expression::template process<T0, T1, T2, T3>
+                              (expr_gen, sf4opr, t0, t1, t2, t3);
+
+               return true;
+            }
+
+            // T o (sf3ext)
+            template <typename ExternalType>
+            static inline bool compile_right(expression_generator<Type>& expr_gen,
+                                             ExternalType t,
+                                             const details::operator_type& operation,
+                                             expression_node_ptr& sf3node,
+                                             expression_node_ptr& result)
+            {
+               if (!details::is_sf3ext_node(sf3node))
+                  return false;
+
+               typedef details::T0oT1oT2_base_node<Type>* sf3ext_base_ptr;
+
+               sf3ext_base_ptr n = static_cast<sf3ext_base_ptr>(sf3node);
+               const std::string id = "t" + expr_gen.to_str(operation) + "(" + n->type_id() + ")";
+
+               switch (n->type())
+               {
+                  case details::expression_node<Type>::e_covoc : return compile_right_impl
+                                                                    <typename covoc_t::sf3_type_node,ExternalType, ctype, vtype, ctype>
+                                                                       (expr_gen, id, t, sf3node, result);
+
+                  case details::expression_node<Type>::e_covov : return compile_right_impl
+                                                                    <typename covov_t::sf3_type_node,ExternalType, ctype, vtype, vtype>
+                                                                       (expr_gen, id, t, sf3node, result);
+
+                  case details::expression_node<Type>::e_vocov : return compile_right_impl
+                                                                    <typename vocov_t::sf3_type_node,ExternalType, vtype, ctype, vtype>
+                                                                       (expr_gen, id, t, sf3node, result);
+
+                  case details::expression_node<Type>::e_vovoc : return compile_right_impl
+                                                                    <typename vovoc_t::sf3_type_node,ExternalType, vtype, vtype, ctype>
+                                                                       (expr_gen, id, t, sf3node, result);
+
+                  case details::expression_node<Type>::e_vovov : return compile_right_impl
+                                                                    <typename vovov_t::sf3_type_node,ExternalType, vtype, vtype, vtype>
+                                                                       (expr_gen, id, t, sf3node, result);
+
+                  default                                      : return false;
+               }
+            }
+
+            // (sf3ext) o T
+            template <typename ExternalType>
+            static inline bool compile_left(expression_generator<Type>& expr_gen,
+                                            ExternalType t,
+                                            const details::operator_type& operation,
+                                            expression_node_ptr& sf3node,
+                                            expression_node_ptr& result)
+            {
+               if (!details::is_sf3ext_node(sf3node))
+                  return false;
+
+               typedef details::T0oT1oT2_base_node<Type>* sf3ext_base_ptr;
+
+               sf3ext_base_ptr n = static_cast<sf3ext_base_ptr>(sf3node);
+
+               const std::string id = "(" + n->type_id() + ")" + expr_gen.to_str(operation) + "t";
+
+               switch (n->type())
+               {
+                  case details::expression_node<Type>::e_covoc : return compile_left_impl
+                                                                    <typename covoc_t::sf3_type_node,ExternalType, ctype, vtype, ctype>
+                                                                       (expr_gen, id, t, sf3node, result);
+
+                  case details::expression_node<Type>::e_covov : return compile_left_impl
+                                                                    <typename covov_t::sf3_type_node,ExternalType, ctype, vtype, vtype>
+                                                                       (expr_gen, id, t, sf3node, result);
+
+                  case details::expression_node<Type>::e_vocov : return compile_left_impl
+                                                                    <typename vocov_t::sf3_type_node,ExternalType, vtype, ctype, vtype>
+                                                                       (expr_gen, id, t, sf3node, result);
+
+                  case details::expression_node<Type>::e_vovoc : return compile_left_impl
+                                                                    <typename vovoc_t::sf3_type_node,ExternalType, vtype, vtype, ctype>
+                                                                       (expr_gen, id, t, sf3node, result);
+
+                  case details::expression_node<Type>::e_vovov : return compile_left_impl
+                                                                    <typename vovov_t::sf3_type_node,ExternalType, vtype, vtype, vtype>
+                                                                       (expr_gen, id, t, sf3node, result);
+
+                  default                                      : return false;
+               }
+            }
+
+            template <typename SF3TypeNode, typename ExternalType, typename T0, typename T1, typename T2>
+            static inline bool compile_right_impl(expression_generator<Type>& expr_gen,
+                                                  const std::string& id,
+                                                  ExternalType t,
+                                                  expression_node_ptr& node,
+                                                  expression_node_ptr& result)
+            {
+               SF3TypeNode* n = dynamic_cast<SF3TypeNode*>(node);
+
+               if (n)
+               {
+                  T0 t0 = n->t0();
+                  T1 t1 = n->t1();
+                  T2 t2 = n->t2();
+
+                  return synthesize_sf4ext_expression::template compile<ExternalType, T0, T1, T2>
+                            (expr_gen, id, t, t0, t1, t2, result);
+               }
+               else
+                  return false;
+            }
+
+            template <typename SF3TypeNode, typename ExternalType, typename T0, typename T1, typename T2>
+            static inline bool compile_left_impl(expression_generator<Type>& expr_gen,
+                                                 const std::string& id,
+                                                 ExternalType t,
+                                                 expression_node_ptr& node,
+                                                 expression_node_ptr& result)
+            {
+               SF3TypeNode* n = dynamic_cast<SF3TypeNode*>(node);
+
+               if (n)
+               {
+                  T0 t0 = n->t0();
+                  T1 t1 = n->t1();
+                  T2 t2 = n->t2();
+
+                  return synthesize_sf4ext_expression::template compile<T0, T1, T2, ExternalType>
+                            (expr_gen, id, t0, t1, t2, t, result);
+               }
+               else
+                  return false;
+            }
+         };
+
+         struct synthesize_vovov_expression0
+         {
+            typedef typename vovov_t::type0 node_type;
+            typedef typename vovov_t::sf3_type sf3_type;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v0 o0 v1) o1 (v2)
+               const details::vov_base_node<Type>* vov = static_cast<details::vov_base_node<Type>*>(branch[0]);
+               const Type& v0 = vov->v0();
+               const Type& v1 = vov->v1();
+               const Type& v2 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = vov->operation();
+               const details::operator_type o1 = operation;
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (v0 / v1) / v2 --> (vovov) v0 / (v1 * v2)
+                  if ((details::e_div == o0) && (details::e_div == o1))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<vtype,vtype,vtype>(expr_gen, "t/(t*t)", v0, v1, v2, result);
+
+                     exprtk_debug(("(v0 / v1) / v2 --> (vovov) v0 / (v1 * v2)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf3ext_expression::template compile<vtype, vtype, vtype>
+                     (expr_gen, id(expr_gen, o0, o1), v0, v1, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, f0, f1);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0, const details::operator_type o1)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vovov_expression1
+         {
+            typedef typename vovov_t::type1 node_type;
+            typedef typename vovov_t::sf3_type sf3_type;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v0) o0 (v1 o1 v2)
+               const details::vov_base_node<Type>* vov = static_cast<details::vov_base_node<Type>*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type& v1 = vov->v0();
+               const Type& v2 = vov->v1();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = vov->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // v0 / (v1 / v2) --> (vovov) (v0 * v2) / v1
+                  if ((details::e_div == o0) && (details::e_div == o1))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<vtype,vtype,vtype>(expr_gen, "(t*t)/t", v0, v2, v1, result);
+
+                     exprtk_debug(("v0 / (v1 / v2) --> (vovov) (v0 * v2) / v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf3ext_expression::template compile<vtype, vtype, vtype>
+                     (expr_gen, id(expr_gen, o0, o1), v0, v1, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, f0, f1);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0, const details::operator_type o1)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vovoc_expression0
+         {
+            typedef typename vovoc_t::type0 node_type;
+            typedef typename vovoc_t::sf3_type sf3_type;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v0 o0 v1) o1 (c)
+               const details::vov_base_node<Type>* vov = static_cast<details::vov_base_node<Type>*>(branch[0]);
+               const Type& v0 = vov->v0();
+               const Type& v1 = vov->v1();
+               const Type   c = static_cast<details::literal_node<Type>*>(branch[1])->value();
+               const details::operator_type o0 = vov->operation();
+               const details::operator_type o1 = operation;
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (v0 / v1) / c --> (vovoc) v0 / (v1 * c)
+                  if ((details::e_div == o0) && (details::e_div == o1))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<vtype,vtype,ctype>(expr_gen, "t/(t*t)", v0, v1, c, result);
+
+                     exprtk_debug(("(v0 / v1) / c --> (vovoc) v0 / (v1 * c)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf3ext_expression::template compile<vtype, vtype, ctype>
+                     (expr_gen, id(expr_gen, o0, o1), v0, v1, c, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, f0, f1);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0, const details::operator_type o1)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vovoc_expression1
+         {
+            typedef typename vovoc_t::type1 node_type;
+            typedef typename vovoc_t::sf3_type sf3_type;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v0) o0 (v1 o1 c)
+               const details::voc_base_node<Type>* voc = static_cast<const details::voc_base_node<Type>*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type& v1 = voc->v();
+               const Type   c = voc->c();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = voc->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // v0 / (v1 / c) --> (vocov) (v0 * c) / v1
+                  if ((details::e_div == o0) && (details::e_div == o1))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<vtype,ctype,vtype>(expr_gen, "(t*t)/t", v0, c, v1, result);
+
+                     exprtk_debug(("v0 / (v1 / c) --> (vocov) (v0 * c) / v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf3ext_expression::template compile<vtype, vtype, ctype>
+                     (expr_gen, id(expr_gen, o0, o1), v0, v1, c, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, f0, f1);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0, const details::operator_type o1)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vocov_expression0
+         {
+            typedef typename vocov_t::type0 node_type;
+            typedef typename vocov_t::sf3_type sf3_type;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v0 o0 c) o1 (v1)
+               const details::voc_base_node<Type>* voc = static_cast<details::voc_base_node<Type>*>(branch[0]);
+               const Type& v0 = voc->v();
+               const Type   c = voc->c();
+               const Type& v1 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = voc->operation();
+               const details::operator_type o1 = operation;
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (v0 / c) / v1 --> (vovoc) v0 / (v1 * c)
+                  if ((details::e_div == o0) && (details::e_div == o1))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<vtype,vtype,ctype>(expr_gen, "t/(t*t)", v0, v1, c, result);
+
+                     exprtk_debug(("(v0 / c) / v1 --> (vovoc) v0 / (v1 * c)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf3ext_expression::template compile<vtype, ctype, vtype>
+                     (expr_gen, id(expr_gen, o0, o1), v0, c, v1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, f0, f1);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0, const details::operator_type o1)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vocov_expression1
+         {
+            typedef typename vocov_t::type1 node_type;
+            typedef typename vocov_t::sf3_type sf3_type;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v0) o0 (c o1 v1)
+               const details::cov_base_node<Type>* cov = static_cast<details::cov_base_node<Type>*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type   c = cov->c();
+               const Type& v1 = cov->v();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = cov->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // v0 / (c / v1) --> (vovoc) (v0 * v1) / c
+                  if ((details::e_div == o0) && (details::e_div == o1))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<vtype, vtype, ctype>(expr_gen, "(t*t)/t", v0, v1, c, result);
+
+                     exprtk_debug(("v0 / (c / v1) --> (vovoc) (v0 * v1) / c\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf3ext_expression::template compile<vtype, ctype, vtype>
+                     (expr_gen, id(expr_gen, o0, o1), v0, c, v1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, f0, f1);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0, const details::operator_type o1)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_covov_expression0
+         {
+            typedef typename covov_t::type0 node_type;
+            typedef typename covov_t::sf3_type sf3_type;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (c o0 v0) o1 (v1)
+               const details::cov_base_node<Type>* cov = static_cast<details::cov_base_node<Type>*>(branch[0]);
+               const Type   c = cov->c();
+               const Type& v0 = cov->v();
+               const Type& v1 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = cov->operation();
+               const details::operator_type o1 = operation;
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (c / v0) / v1 --> (covov) c / (v0 * v1)
+                  if ((details::e_div == o0) && (details::e_div == o1))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype, vtype, vtype>(expr_gen, "t/(t*t)", c, v0, v1, result);
+
+                     exprtk_debug(("(c / v0) / v1 --> (covov) c / (v0 * v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf3ext_expression::template compile<ctype, vtype, vtype>
+                     (expr_gen, id(expr_gen, o0, o1), c, v0, v1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, f0, f1);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0, const details::operator_type o1)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "t";
+            }
+         };
+
+         struct synthesize_covov_expression1
+         {
+            typedef typename covov_t::type1 node_type;
+            typedef typename covov_t::sf3_type sf3_type;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (c) o0 (v0 o1 v1)
+               const details::vov_base_node<Type>* vov = static_cast<details::vov_base_node<Type>*>(branch[1]);
+               const Type   c = static_cast<details::literal_node<Type>*>(branch[0])->value();
+               const Type& v0 = vov->v0();
+               const Type& v1 = vov->v1();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = vov->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // c / (v0 / v1) --> (covov) (c * v1) / v0
+                  if ((details::e_div == o0) && (details::e_div == o1))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype, vtype, vtype>(expr_gen, "(t*t)/t", c, v1, v0, result);
+
+                     exprtk_debug(("c / (v0 / v1) --> (covov) (c * v1) / v0\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf3ext_expression::template compile<ctype, vtype, vtype>
+                     (expr_gen, id(expr_gen, o0, o1), c, v0, v1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, f0, f1);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_covoc_expression0
+         {
+            typedef typename covoc_t::type0 node_type;
+            typedef typename covoc_t::sf3_type sf3_type;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (c0 o0 v) o1 (c1)
+               const details::cov_base_node<Type>* cov = static_cast<details::cov_base_node<Type>*>(branch[0]);
+               const Type  c0 = cov->c();
+               const Type&  v = cov->v();
+               const Type  c1 = static_cast<details::literal_node<Type>*>(branch[1])->value();
+               const details::operator_type o0 = cov->operation();
+               const details::operator_type o1 = operation;
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (c0 + v) + c1 --> (cov) (c0 + c1) + v
+                  if ((details::e_add == o0) && (details::e_add == o1))
+                  {
+                     exprtk_debug(("(c0 + v) + c1 --> (cov) (c0 + c1) + v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::add_op<Type> > >(c0 + c1, v);
+                  }
+                  // (c0 + v) - c1 --> (cov) (c0 - c1) + v
+                  else if ((details::e_add == o0) && (details::e_sub == o1))
+                  {
+                     exprtk_debug(("(c0 + v) - c1 --> (cov) (c0 - c1) + v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::add_op<Type> > >(c0 - c1, v);
+                  }
+                  // (c0 - v) + c1 --> (cov) (c0 + c1) - v
+                  else if ((details::e_sub == o0) && (details::e_add == o1))
+                  {
+                     exprtk_debug(("(c0 - v) + c1 --> (cov) (c0 + c1) - v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::sub_op<Type> > >(c0 + c1, v);
+                  }
+                  // (c0 - v) - c1 --> (cov) (c0 - c1) - v
+                  else if ((details::e_sub == o0) && (details::e_sub == o1))
+                  {
+                     exprtk_debug(("(c0 - v) - c1 --> (cov) (c0 - c1) - v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::sub_op<Type> > >(c0 - c1, v);
+                  }
+                  // (c0 * v) * c1 --> (cov) (c0 * c1) * v
+                  else if ((details::e_mul == o0) && (details::e_mul == o1))
+                  {
+                     exprtk_debug(("(c0 * v) * c1 --> (cov) (c0 * c1) * v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::mul_op<Type> > >(c0 * c1, v);
+                  }
+                  // (c0 * v) / c1 --> (cov) (c0 / c1) * v
+                  else if ((details::e_mul == o0) && (details::e_div == o1))
+                  {
+                     exprtk_debug(("(c0 * v) / c1 --> (cov) (c0 / c1) * v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::mul_op<Type> > >(c0 / c1, v);
+                  }
+                  // (c0 / v) * c1 --> (cov) (c0 * c1) / v
+                  else if ((details::e_div == o0) && (details::e_mul == o1))
+                  {
+                     exprtk_debug(("(c0 / v) * c1 --> (cov) (c0 * c1) / v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::div_op<Type> > >(c0 * c1, v);
+                  }
+                  // (c0 / v) / c1 --> (cov) (c0 / c1) / v
+                  else if ((details::e_div == o0) && (details::e_div == o1))
+                  {
+                     exprtk_debug(("(c0 / v) / c1 --> (cov) (c0 / c1) / v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::div_op<Type> > >(c0 / c1, v);
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf3ext_expression::template compile<ctype, vtype, ctype>
+                     (expr_gen, id(expr_gen, o0, o1), c0, v, c1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), c0, v, c1, f0, f1);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0, const details::operator_type o1)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "t";
+            }
+         };
+
+         struct synthesize_covoc_expression1
+         {
+            typedef typename covoc_t::type1 node_type;
+            typedef typename covoc_t::sf3_type sf3_type;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (c0) o0 (v o1 c1)
+               const details::voc_base_node<Type>* voc = static_cast<details::voc_base_node<Type>*>(branch[1]);
+               const Type  c0 = static_cast<details::literal_node<Type>*>(branch[0])->value();
+               const Type&  v = voc->v();
+               const Type  c1 = voc->c();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = voc->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (c0) + (v + c1) --> (cov) (c0 + c1) + v
+                  if ((details::e_add == o0) && (details::e_add == o1))
+                  {
+                     exprtk_debug(("(c0) + (v + c1) --> (cov) (c0 + c1) + v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::add_op<Type> > >(c0 + c1, v);
+                  }
+                  // (c0) + (v - c1) --> (cov) (c0 - c1) + v
+                  else if ((details::e_add == o0) && (details::e_sub == o1))
+                  {
+                     exprtk_debug(("(c0) + (v - c1) --> (cov) (c0 - c1) + v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::add_op<Type> > >(c0 - c1, v);
+                  }
+                  // (c0) - (v + c1) --> (cov) (c0 - c1) - v
+                  else if ((details::e_sub == o0) && (details::e_add == o1))
+                  {
+                     exprtk_debug(("(c0) - (v + c1) --> (cov) (c0 - c1) - v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::sub_op<Type> > >(c0 - c1, v);
+                  }
+                  // (c0) - (v - c1) --> (cov) (c0 + c1) - v
+                  else if ((details::e_sub == o0) && (details::e_sub == o1))
+                  {
+                     exprtk_debug(("(c0) - (v - c1) --> (cov) (c0 + c1) - v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::sub_op<Type> > >(c0 + c1, v);
+                  }
+                  // (c0) * (v * c1) --> (voc) v * (c0 * c1)
+                  else if ((details::e_mul == o0) && (details::e_mul == o1))
+                  {
+                     exprtk_debug(("(c0) * (v * c1) --> (voc) v * (c0 * c1)\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::mul_op<Type> > >(c0 * c1, v);
+                  }
+                  // (c0) * (v / c1) --> (cov) (c0 / c1) * v
+                  else if ((details::e_mul == o0) && (details::e_div == o1))
+                  {
+                     exprtk_debug(("(c0) * (v / c1) --> (cov) (c0 / c1) * v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::mul_op<Type> > >(c0 / c1, v);
+                  }
+                  // (c0) / (v * c1) --> (cov) (c0 / c1) / v
+                  else if ((details::e_div == o0) && (details::e_mul == o1))
+                  {
+                     exprtk_debug(("(c0) / (v * c1) --> (cov) (c0 / c1) / v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::div_op<Type> > >(c0 / c1, v);
+                  }
+                  // (c0) / (v / c1) --> (cov) (c0 * c1) / v
+                  else if ((details::e_div == o0) && (details::e_div == o1))
+                  {
+                     exprtk_debug(("(c0) / (v / c1) --> (cov) (c0 * c1) / v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::div_op<Type> > >(c0 * c1, v);
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf3ext_expression::template compile<ctype, vtype, ctype>
+                     (expr_gen, id(expr_gen, o0, o1), c0, v, c1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), c0, v, c1, f0, f1);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0, const details::operator_type o1)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_cocov_expression0
+         {
+            typedef typename cocov_t::type0 node_type;
+            static inline expression_node_ptr process(expression_generator<Type>&, const details::operator_type&, expression_node_ptr (&)[2])
+            {
+               // (c0 o0 c1) o1 (v) - Not possible.
+               return error_node();
+            }
+         };
+
+         struct synthesize_cocov_expression1
+         {
+            typedef typename cocov_t::type1 node_type;
+            typedef typename cocov_t::sf3_type sf3_type;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (c0) o0 (c1 o1 v)
+               const details::cov_base_node<Type>* cov = static_cast<details::cov_base_node<Type>*>(branch[1]);
+               const Type  c0 = static_cast<details::literal_node<Type>*>(branch[0])->value();
+               const Type  c1 = cov->c();
+               const Type&  v = cov->v();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = cov->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (c0) + (c1 + v) --> (cov) (c0 + c1) + v
+                  if ((details::e_add == o0) && (details::e_add == o1))
+                  {
+                     exprtk_debug(("(c0) + (c1 + v) --> (cov) (c0 + c1) + v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::add_op<Type> > >(c0 + c1, v);
+                  }
+                  // (c0) + (c1 - v) --> (cov) (c0 + c1) - v
+                  else if ((details::e_add == o0) && (details::e_sub == o1))
+                  {
+                     exprtk_debug(("(c0) + (c1 - v) --> (cov) (c0 + c1) - v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::sub_op<Type> > >(c0 + c1, v);
+                  }
+                  // (c0) - (c1 + v) --> (cov) (c0 - c1) - v
+                  else if ((details::e_sub == o0) && (details::e_add == o1))
+                  {
+                     exprtk_debug(("(c0) - (c1 + v) --> (cov) (c0 - c1) - v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::sub_op<Type> > >(c0 - c1, v);
+                  }
+                  // (c0) - (c1 - v) --> (cov) (c0 - c1) + v
+                  else if ((details::e_sub == o0) && (details::e_sub == o1))
+                  {
+                     exprtk_debug(("(c0) - (c1 - v) --> (cov) (c0 - c1) + v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::add_op<Type> > >(c0 - c1, v);
+                  }
+                  // (c0) * (c1 * v) --> (cov) (c0 * c1) * v
+                  else if ((details::e_mul == o0) && (details::e_mul == o1))
+                  {
+                     exprtk_debug(("(c0) * (c1 * v) --> (cov) (c0 * c1) * v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::mul_op<Type> > >(c0 * c1, v);
+                  }
+                  // (c0) * (c1 / v) --> (cov) (c0 * c1) / v
+                  else if ((details::e_mul == o0) && (details::e_div == o1))
+                  {
+                     exprtk_debug(("(c0) * (c1 / v) --> (cov) (c0 * c1) / v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::div_op<Type> > >(c0 * c1, v);
+                  }
+                  // (c0) / (c1 * v) --> (cov) (c0 / c1) / v
+                  else if ((details::e_div == o0) && (details::e_mul == o1))
+                  {
+                     exprtk_debug(("(c0) / (c1 * v) --> (cov) (c0 / c1) / v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::div_op<Type> > >(c0 / c1, v);
+                  }
+                  // (c0) / (c1 / v) --> (cov) (c0 / c1) * v
+                  else if ((details::e_div == o0) && (details::e_div == o1))
+                  {
+                     exprtk_debug(("(c0) / (c1 / v) --> (cov) (c0 / c1) * v\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_cr<typename details::cov_node<Type,details::mul_op<Type> > >(c0 / c1, v);
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf3ext_expression::template compile<ctype, ctype, vtype>
+                     (expr_gen, id(expr_gen, o0, o1), c0, c1, v, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), c0, c1, v, f0, f1);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen, const details::operator_type o0, const details::operator_type o1)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vococ_expression0
+         {
+            typedef typename vococ_t::type0 node_type;
+            typedef typename vococ_t::sf3_type sf3_type;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v o0 c0) o1 (c1)
+               const details::voc_base_node<Type>* voc = static_cast<details::voc_base_node<Type>*>(branch[0]);
+               const Type&  v = voc->v();
+               const Type& c0 = voc->c();
+               const Type& c1 = static_cast<details::literal_node<Type>*>(branch[1])->value();
+               const details::operator_type o0 = voc->operation();
+               const details::operator_type o1 = operation;
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (v + c0) + c1 --> (voc) v + (c0 + c1)
+                  if ((details::e_add == o0) && (details::e_add == o1))
+                  {
+                     exprtk_debug(("(v + c0) + c1 --> (voc) v + (c0 + c1)\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_rc<typename details::voc_node<Type,details::add_op<Type> > >(v, c0 + c1);
+                  }
+                  // (v + c0) - c1 --> (voc) v + (c0 - c1)
+                  else if ((details::e_add == o0) && (details::e_sub == o1))
+                  {
+                     exprtk_debug(("(v + c0) - c1 --> (voc) v + (c0 - c1)\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_rc<typename details::voc_node<Type,details::add_op<Type> > >(v, c0 - c1);
+                  }
+                  // (v - c0) + c1 --> (voc) v - (c0 + c1)
+                  else if ((details::e_sub == o0) && (details::e_add == o1))
+                  {
+                     exprtk_debug(("(v - c0) + c1 --> (voc) v - (c0 + c1)\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_rc<typename details::voc_node<Type,details::add_op<Type> > >(v, c1 - c0);
+                  }
+                  // (v - c0) - c1 --> (voc) v - (c0 + c1)
+                  else if ((details::e_sub == o0) && (details::e_sub == o1))
+                  {
+                     exprtk_debug(("(v - c0) - c1 --> (voc) v - (c0 + c1)\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_rc<typename details::voc_node<Type,details::sub_op<Type> > >(v, c0 + c1);
+                  }
+                  // (v * c0) * c1 --> (voc) v * (c0 * c1)
+                  else if ((details::e_mul == o0) && (details::e_mul == o1))
+                  {
+                     exprtk_debug(("(v * c0) * c1 --> (voc) v * (c0 * c1)\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_rc<typename details::voc_node<Type,details::mul_op<Type> > >(v, c0 * c1);
+                  }
+                  // (v * c0) / c1 --> (voc) v * (c0 / c1)
+                  else if ((details::e_mul == o0) && (details::e_div == o1))
+                  {
+                     exprtk_debug(("(v * c0) / c1 --> (voc) v * (c0 / c1)\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_rc<typename details::voc_node<Type,details::mul_op<Type> > >(v, c0 / c1);
+                  }
+                  // (v / c0) * c1 --> (voc) v * (c1 / c0)
+                  else if ((details::e_div == o0) && (details::e_mul == o1))
+                  {
+                     exprtk_debug(("(v / c0) * c1 --> (voc) v * (c1 / c0)\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_rc<typename details::voc_node<Type,details::mul_op<Type> > >(v, c1 / c0);
+                  }
+                  // (v / c0) / c1 --> (voc) v / (c0 * c1)
+                  else if ((details::e_div == o0) && (details::e_div == o1))
+                  {
+                     exprtk_debug(("(v / c0) / c1 --> (voc) v / (c0 * c1)\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_rc<typename details::voc_node<Type,details::div_op<Type> > >(v, c0 * c1);
+                  }
+                  // (v ^ c0) ^ c1 --> (voc) v ^ (c0 * c1)
+                  else if ((details::e_pow == o0) && (details::e_pow == o1))
+                  {
+                     exprtk_debug(("(v ^ c0) ^ c1 --> (voc) v ^ (c0 * c1)\n"));
+
+                     return expr_gen.node_allocator_->
+                               template allocate_rc<typename details::voc_node<Type,details::pow_op<Type> > >(v, c0 * c1);
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf3ext_expression::template compile<vtype, ctype, ctype>
+                     (expr_gen, id(expr_gen, o0, o1), v, c0, c1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), v, c0, c1, f0, f1);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0, const details::operator_type o1)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vococ_expression1
+         {
+            typedef typename vococ_t::type0 node_type;
+
+            static inline expression_node_ptr process(expression_generator<Type>&, const details::operator_type&, expression_node_ptr (&)[2])
+            {
+               // (v) o0 (c0 o1 c1) - Not possible.
+               exprtk_debug(("(v) o0 (c0 o1 c1) - Not possible.\n"));
+               return error_node();
+            }
+         };
+
+         struct synthesize_vovovov_expression0
+         {
+            typedef typename vovovov_t::type0 node_type;
+            typedef typename vovovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v0 o0 v1) o1 (v2 o2 v3)
+               const details::vov_base_node<Type>* vov0 = static_cast<details::vov_base_node<Type>*>(branch[0]);
+               const details::vov_base_node<Type>* vov1 = static_cast<details::vov_base_node<Type>*>(branch[1]);
+               const Type& v0 = vov0->v0();
+               const Type& v1 = vov0->v1();
+               const Type& v2 = vov1->v0();
+               const Type& v3 = vov1->v1();
+               const details::operator_type o0 = vov0->operation();
+               const details::operator_type o1 = operation;
+               const details::operator_type o2 = vov1->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (v0 / v1) * (v2 / v3) --> (vovovov) (v0 * v2) / (v1 * v3)
+                  if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<vtype,vtype,vtype,vtype>(expr_gen, "(t*t)/(t*t)", v0, v2, v1, v3, result);
+
+                     exprtk_debug(("(v0 / v1) * (v2 / v3) --> (vovovov) (v0 * v2) / (v1 * v3)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 / v1) / (v2 / v3) --> (vovovov) (v0 * v3) / (v1 * v2)
+                  else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<vtype,vtype,vtype,vtype>(expr_gen, "(t*t)/(t*t)", v0, v3, v1, v2, result);
+
+                     exprtk_debug(("(v0 / v1) / (v2 / v3) --> (vovovov) (v0 * v3) / (v1 * v2)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 + v1) / (v2 / v3) --> (vovovov) (v0 + v1) * (v3 / v2)
+                  else if ((details::e_add == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<vtype,vtype,vtype,vtype>(expr_gen, "(t+t)*(t/t)", v0, v1, v3, v2, result);
+
+                     exprtk_debug(("(v0 + v1) / (v2 / v3) --> (vovovov) (v0 + v1) * (v3 / v2)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 - v1) / (v2 / v3) --> (vovovov) (v0 + v1) * (v3 / v2)
+                  else if ((details::e_sub == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<vtype,vtype,vtype,vtype>(expr_gen, "(t-t)*(t/t)", v0, v1, v3, v2, result);
+
+                     exprtk_debug(("(v0 - v1) / (v2 / v3) --> (vovovov) (v0 - v1) * (v3 / v2)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 * v1) / (v2 / v3) --> (vovovov) ((v0 * v1) * v3) / v2
+                  else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<vtype,vtype,vtype,vtype>(expr_gen, "((t*t)*t)/t", v0, v1, v3, v2, result);
+
+                     exprtk_debug(("(v0 * v1) / (v2 / v3) --> (vovovov) ((v0 * v1) * v3) / v2\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, v3, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, v3, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vovovoc_expression0
+         {
+            typedef typename vovovoc_t::type0 node_type;
+            typedef typename vovovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v0 o0 v1) o1 (v2 o2 c)
+               const details::vov_base_node<Type>* vov = static_cast<details::vov_base_node<Type>*>(branch[0]);
+               const details::voc_base_node<Type>* voc = static_cast<details::voc_base_node<Type>*>(branch[1]);
+               const Type& v0 = vov->v0();
+               const Type& v1 = vov->v1();
+               const Type& v2 = voc->v ();
+               const Type   c = voc->c ();
+               const details::operator_type o0 = vov->operation();
+               const details::operator_type o1 = operation;
+               const details::operator_type o2 = voc->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (v0 / v1) * (v2 / c) --> (vovovoc) (v0 * v2) / (v1 * c)
+                  if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<vtype,vtype,vtype,ctype>(expr_gen, "(t*t)/(t*t)", v0, v2, v1, c, result);
+
+                     exprtk_debug(("(v0 / v1) * (v2 / c) --> (vovovoc) (v0 * v2) / (v1 * c)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 / v1) / (v2 / c) --> (vocovov) (v0 * c) / (v1 * v2)
+                  if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<vtype,ctype,vtype,vtype>(expr_gen, "(t*t)/(t*t)", v0, c, v1, v2, result);
+
+                     exprtk_debug(("(v0 / v1) / (v2 / c) --> (vocovov) (v0 * c) / (v1 * v2)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, c, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, c, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vovocov_expression0
+         {
+            typedef typename vovocov_t::type0 node_type;
+            typedef typename vovocov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v0 o0 v1) o1 (c o2 v2)
+               const details::vov_base_node<Type>* vov = static_cast<details::vov_base_node<Type>*>(branch[0]);
+               const details::cov_base_node<Type>* cov = static_cast<details::cov_base_node<Type>*>(branch[1]);
+               const Type& v0 = vov->v0();
+               const Type& v1 = vov->v1();
+               const Type& v2 = cov->v ();
+               const Type   c = cov->c ();
+               const details::operator_type o0 = vov->operation();
+               const details::operator_type o1 = operation;
+               const details::operator_type o2 = cov->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (v0 / v1) * (c / v2) --> (vocovov) (v0 * c) / (v1 * v2)
+                  if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<vtype,ctype,vtype,vtype>(expr_gen, "(t*t)/(t*t)", v0, c, v1, v2, result);
+
+                     exprtk_debug(("(v0 / v1) * (c / v2) --> (vocovov) (v0 * c) / (v1 * v2)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 / v1) / (c / v2) --> (vovovoc) (v0 * v2) / (v1 * c)
+                  if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<vtype,vtype,vtype,ctype>(expr_gen, "(t*t)/(t*t)", v0, v2, v1, c, result);
+
+                     exprtk_debug(("(v0 / v1) / (c / v2) --> (vovovoc) (v0 * v2) / (v1 * c)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, c, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vocovov_expression0
+         {
+            typedef typename vocovov_t::type0 node_type;
+            typedef typename vocovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v0 o0 c) o1 (v1 o2 v2)
+               const details::voc_base_node<Type>* voc = static_cast<details::voc_base_node<Type>*>(branch[0]);
+               const details::vov_base_node<Type>* vov = static_cast<details::vov_base_node<Type>*>(branch[1]);
+               const Type   c = voc->c ();
+               const Type& v0 = voc->v ();
+               const Type& v1 = vov->v0();
+               const Type& v2 = vov->v1();
+               const details::operator_type o0 = voc->operation();
+               const details::operator_type o1 = operation;
+               const details::operator_type o2 = vov->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (v0 / c) * (v1 / v2) --> (vovocov) (v0 * v1) / (c * v2)
+                  if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<vtype,vtype,ctype,vtype>(expr_gen, "(t*t)/(t*t)", v0, v1, c, v2, result);
+
+                     exprtk_debug(("(v0 / c) * (v1 / v2) --> (vovocov) (v0 * v1) / (c * v2)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 / c) / (v1 / v2) --> (vovocov) (v0 * v2) / (c * v1)
+                  if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<vtype,vtype,ctype,vtype>(expr_gen, "(t*t)/(t*t)", v0, v2, c, v1, result);
+
+                     exprtk_debug(("(v0 / c) / (v1 / v2) --> (vovocov) (v0 * v2) / (c * v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, c, v1, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_covovov_expression0
+         {
+            typedef typename covovov_t::type0 node_type;
+            typedef typename covovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (c o0 v0) o1 (v1 o2 v2)
+               const details::cov_base_node<Type>* cov = static_cast<details::cov_base_node<Type>*>(branch[0]);
+               const details::vov_base_node<Type>* vov = static_cast<details::vov_base_node<Type>*>(branch[1]);
+               const Type   c = cov->c ();
+               const Type& v0 = cov->v ();
+               const Type& v1 = vov->v0();
+               const Type& v2 = vov->v1();
+               const details::operator_type o0 = cov->operation();
+               const details::operator_type o1 = operation;
+               const details::operator_type o2 = vov->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (c / v0) * (v1 / v2) --> (covovov) (c * v1) / (v0 * v2)
+                  if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<ctype,vtype,vtype,vtype>(expr_gen, "(t*t)/(t*t)", c, v1, v0, v2, result);
+
+                     exprtk_debug(("(c / v0) * (v1 / v2) --> (covovov) (c * v1) / (v0 * v2)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c / v0) / (v1 / v2) --> (covovov) (c * v2) / (v0 * v1)
+                  if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<ctype,vtype,vtype,vtype>(expr_gen, "(t*t)/(t*t)", c, v2, v0, v1, result);
+
+                     exprtk_debug(("(c / v0) / (v1 / v2) --> (covovov) (c * v2) / (v0 * v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c, v0, v1, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_covocov_expression0
+         {
+            typedef typename covocov_t::type0 node_type;
+            typedef typename covocov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (c0 o0 v0) o1 (c1 o2 v1)
+               const details::cov_base_node<Type>* cov0 = static_cast<details::cov_base_node<Type>*>(branch[0]);
+               const details::cov_base_node<Type>* cov1 = static_cast<details::cov_base_node<Type>*>(branch[1]);
+               const Type  c0 = cov0->c();
+               const Type& v0 = cov0->v();
+               const Type  c1 = cov1->c();
+               const Type& v1 = cov1->v();
+               const details::operator_type o0 = cov0->operation();
+               const details::operator_type o1 = operation;
+               const details::operator_type o2 = cov1->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (c0 + v0) + (c1 + v1) --> (covov) (c0 + c1) + v0 + v1
+                  if ((details::e_add == o0) && (details::e_add == o1) && (details::e_add == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 + v0) + (c1 + v1) --> (covov) (c0 + c1) + v0 + v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 + v0) - (c1 + v1) --> (covov) (c0 - c1) + v0 - v1
+                  else if ((details::e_add == o0) && (details::e_sub == o1) && (details::e_add == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 + v0) - (c1 + v1) --> (covov) (c0 - c1) + v0 - v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 - v0) - (c1 - v1) --> (covov) (c0 - c1) - v0 + v1
+                  else if ((details::e_sub == o0) && (details::e_sub == o1) && (details::e_sub == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t-t)+t", (c0 - c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 - v0) - (c1 - v1) --> (covov) (c0 - c1) - v0 + v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 * v0) * (c1 * v1) --> (covov) (c0 * c1) * v0 * v1
+                  else if ((details::e_mul == o0) && (details::e_mul == o1) && (details::e_mul == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 * v0) * (c1 * v1) --> (covov) (c0 * c1) * v0 * v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 * v0) / (c1 * v1) --> (covov) (c0 / c1) * (v0 / v1)
+                  else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_mul == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 * v0) / (c1 * v1) --> (covov) (c0 / c1) * (v0 / v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 / v0) * (c1 / v1) --> (covov) (c0 * c1) / (v0 * v1)
+                  else if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "t/(t*t)", (c0 * c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 / v0) * (c1 / v1) --> (covov) (c0 * c1) / (v0 * v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 / v0) / (c1 / v1) --> (covov) ((c0 / c1) * v1) / v0
+                  else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c0 / c1), v1, v0, result);
+
+                     exprtk_debug(("(c0 / v0) / (c1 / v1) --> (covov) ((c0 / c1) * v1) / v0\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 * v0) / (c1 / v1) --> (covov) (c0 / c1) * (v0 * v1)
+                  else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "t*(t*t)", (c0 / c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 * v0) / (c1 / v1) --> (covov) (c0 / c1) * (v0 * v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 / v0) / (c1 * v1) --> (covov) (c0 / c1) / (v0 * v1)
+                  else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_mul == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "t/(t*t)", (c0 / c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 / v0) / (c1 * v1) --> (covov) (c0 / c1) / (v0 * v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c * v0) +/- (c * v1) --> (covov) c * (v0 +/- v1)
+                  else if (
+                            (std::equal_to<T>()(c0,c1)) &&
+                            (details::e_mul == o0)      &&
+                            (details::e_mul == o2)      &&
+                            (
+                              (details::e_add == o1) ||
+                              (details::e_sub == o1)
+                            )
+                          )
+                  {
+                     std::string specfunc;
+
+                     switch (o1)
+                     {
+                        case details::e_add : specfunc = "t*(t+t)"; break;
+                        case details::e_sub : specfunc = "t*(t-t)"; break;
+                        default             : return error_node();
+                     }
+
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype, vtype, vtype>(expr_gen, specfunc, c0, v0, v1, result);
+
+                     exprtk_debug(("(c * v0) +/- (c * v1) --> (covov) c * (v0 +/- v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, c1, v1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, c1, v1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vocovoc_expression0
+         {
+            typedef typename vocovoc_t::type0 node_type;
+            typedef typename vocovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v0 o0 c0) o1 (v1 o2 c1)
+               const details::voc_base_node<Type>* voc0 = static_cast<details::voc_base_node<Type>*>(branch[0]);
+               const details::voc_base_node<Type>* voc1 = static_cast<details::voc_base_node<Type>*>(branch[1]);
+               const Type  c0 = voc0->c();
+               const Type& v0 = voc0->v();
+               const Type  c1 = voc1->c();
+               const Type& v1 = voc1->v();
+               const details::operator_type o0 = voc0->operation();
+               const details::operator_type o1 = operation;
+               const details::operator_type o2 = voc1->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (v0 + c0) + (v1 + c1) --> (covov) (c0 + c1) + v0 + v1
+                  if ((details::e_add == o0) && (details::e_add == o1) && (details::e_add == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result);
+
+                     exprtk_debug(("(v0 + c0) + (v1 + c1) --> (covov) (c0 + c1) + v0 + v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 + c0) - (v1 + c1) --> (covov) (c0 - c1) + v0 - v1
+                  else if ((details::e_add == o0) && (details::e_sub == o1) && (details::e_add == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result);
+
+                     exprtk_debug(("(v0 + c0) - (v1 + c1) --> (covov) (c0 - c1) + v0 - v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 - c0) - (v1 - c1) --> (covov) (c1 - c0) + v0 - v1
+                  else if ((details::e_sub == o0) && (details::e_sub == o1) && (details::e_sub == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)-t", (c1 - c0), v0, v1, result);
+
+                     exprtk_debug(("(v0 - c0) - (v1 - c1) --> (covov) (c1 - c0) + v0 - v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 * c0) * (v1 * c1) --> (covov) (c0 * c1) * v0 * v1
+                  else if ((details::e_mul == o0) && (details::e_mul == o1) && (details::e_mul == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result);
+
+                     exprtk_debug(("(v0 * c0) * (v1 * c1) --> (covov) (c0 * c1) * v0 * v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 * c0) / (v1 * c1) --> (covov) (c0 / c1) * (v0 / v1)
+                  else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_mul == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result);
+
+                     exprtk_debug(("(v0 * c0) / (v1 * c1) --> (covov) (c0 / c1) * (v0 / v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 / c0) * (v1 / c1) --> (covov) (1 / (c0 * c1)) * v0 * v1
+                  else if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)*t", Type(1) / (c0 * c1), v0, v1, result);
+
+                     exprtk_debug(("(v0 / c0) * (v1 / c1) --> (covov) (1 / (c0 * c1)) * v0 * v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 / c0) / (v1 / c1) --> (covov) ((c1 / c0) * v0) / v1
+                  else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c1 / c0), v0, v1, result);
+
+                     exprtk_debug(("(v0 / c0) / (v1 / c1) --> (covov) ((c1 / c0) * v0) / v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 * c0) / (v1 / c1) --> (covov) (c0 * c1) * (v0 / v1)
+                  else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "t*(t/t)", (c0 * c1), v0, v1, result);
+
+                     exprtk_debug(("(v0 * c0) / (v1 / c1) --> (covov) (c0 * c1) * (v0 / v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 / c0) / (v1 * c1) --> (covov) (1 / (c0 * c1)) * v0 / v1
+                  else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_mul == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "t*(t/t)", Type(1) / (c0 * c1), v0, v1, result);
+
+                     exprtk_debug(("(v0 / c0) / (v1 * c1) --> (covov) (1 / (c0 * c1)) * v0 / v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 / c0) * (v1 + c1) --> (vocovoc) (v0 * (1 / c0)) * (v1 + c1)
+                  else if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_add == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<vtype,ctype,vtype,ctype>(expr_gen, "(t*t)*(t+t)", v0, T(1) / c0, v1, c1, result);
+
+                     exprtk_debug(("(v0 / c0) * (v1 + c1) --> (vocovoc) (v0 * (1 / c0)) * (v1 + c1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 / c0) * (v1 - c1) --> (vocovoc) (v0 * (1 / c0)) * (v1 - c1)
+                  else if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_sub == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf4ext_expression::
+                           template compile<vtype,ctype,vtype,ctype>(expr_gen, "(t*t)*(t-t)", v0, T(1) / c0, v1, c1, result);
+
+                     exprtk_debug(("(v0 / c0) * (v1 - c1) --> (vocovoc) (v0 * (1 / c0)) * (v1 - c1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 * c) +/- (v1 * c) --> (covov) c * (v0 +/- v1)
+                  else if (
+                            (std::equal_to<T>()(c0,c1)) &&
+                            (details::e_mul == o0)      &&
+                            (details::e_mul == o2)      &&
+                            (
+                              (details::e_add == o1) ||
+                              (details::e_sub == o1)
+                            )
+                          )
+                  {
+                     std::string specfunc;
+
+                     switch (o1)
+                     {
+                        case details::e_add : specfunc = "t*(t+t)"; break;
+                        case details::e_sub : specfunc = "t*(t-t)"; break;
+                        default             : return error_node();
+                     }
+
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, specfunc, c0, v0, v1, result);
+
+                     exprtk_debug(("(v0 * c) +/- (v1 * c) --> (covov) c * (v0 +/- v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 / c) +/- (v1 / c) --> (vovoc) (v0 +/- v1) / c
+                  else if (
+                            (std::equal_to<T>()(c0,c1)) &&
+                            (details::e_div == o0)      &&
+                            (details::e_div == o2)      &&
+                            (
+                              (details::e_add == o1) ||
+                              (details::e_sub == o1)
+                            )
+                          )
+                  {
+                     std::string specfunc;
+
+                     switch (o1)
+                     {
+                        case details::e_add : specfunc = "(t+t)/t"; break;
+                        case details::e_sub : specfunc = "(t-t)/t"; break;
+                        default             : return error_node();
+                     }
+
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<vtype,vtype,ctype>(expr_gen, specfunc, v0, v1, c0, result);
+
+                     exprtk_debug(("(v0 / c) +/- (v1 / c) --> (vovoc) (v0 +/- v1) / c\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, v1, c1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, v1, c1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_covovoc_expression0
+         {
+            typedef typename covovoc_t::type0 node_type;
+            typedef typename covovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (c0 o0 v0) o1 (v1 o2 c1)
+               const details::cov_base_node<Type>* cov = static_cast<details::cov_base_node<Type>*>(branch[0]);
+               const details::voc_base_node<Type>* voc = static_cast<details::voc_base_node<Type>*>(branch[1]);
+               const Type  c0 = cov->c();
+               const Type& v0 = cov->v();
+               const Type  c1 = voc->c();
+               const Type& v1 = voc->v();
+               const details::operator_type o0 = cov->operation();
+               const details::operator_type o1 = operation;
+               const details::operator_type o2 = voc->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (c0 + v0) + (v1 + c1) --> (covov) (c0 + c1) + v0 + v1
+                  if ((details::e_add == o0) && (details::e_add == o1) && (details::e_add == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 + v0) + (v1 + c1) --> (covov) (c0 + c1) + v0 + v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 + v0) - (v1 + c1) --> (covov) (c0 - c1) + v0 - v1
+                  else if ((details::e_add == o0) && (details::e_sub == o1) && (details::e_add == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 + v0) - (v1 + c1) --> (covov) (c0 - c1) + v0 - v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 - v0) - (v1 - c1) --> (covov) (c0 + c1) - v0 - v1
+                  else if ((details::e_sub == o0) && (details::e_sub == o1) && (details::e_sub == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "t-(t+t)", (c0 + c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 - v0) - (v1 - c1) --> (covov) (c0 + c1) - v0 - v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 * v0) * (v1 * c1) --> (covov) (c0 * c1) * v0 * v1
+                  else if ((details::e_mul == o0) && (details::e_mul == o1) && (details::e_mul == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 * v0) * (v1 * c1) --> (covov) (c0 * c1) * v0 * v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 * v0) / (v1 * c1) --> (covov) (c0 / c1) * (v0 / v1)
+                  else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_mul == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 * v0) / (v1 * c1) --> (covov) (c0 / c1) * (v0 / v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 / v0) * (v1 / c1) --> (covov) (c0 / c1) * (v1 / v0)
+                  else if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "t*(t/t)", (c0 / c1), v1, v0, result);
+
+                     exprtk_debug(("(c0 / v0) * (v1 / c1) --> (covov) (c0 / c1) * (v1 / v0)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 / v0) / (v1 / c1) --> (covov) (c0 * c1) / (v0 * v1)
+                  else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "t/(t*t)", (c0 * c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 / v0) / (v1 / c1) --> (covov) (c0 * c1) / (v0 * v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 * v0) / (v1 / c1) --> (covov) (c0 * c1) * (v0 / v1)
+                  else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c0 * c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 * v0) / (v1 / c1) --> (covov) (c0 * c1) * (v0 / v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c0 / v0) / (v1 * c1) --> (covov) (c0 / c1) / (v0 * v1)
+                  else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_mul == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "t/(t*t)", (c0 / c1), v0, v1, result);
+
+                     exprtk_debug(("(c0 / v0) / (v1 * c1) --> (covov) (c0 / c1) / (v0 * v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (c * v0) +/- (v1 * c) --> (covov) c * (v0 +/- v1)
+                  else if (
+                            (std::equal_to<T>()(c0,c1)) &&
+                            (details::e_mul == o0)      &&
+                            (details::e_mul == o2)      &&
+                            (
+                              (details::e_add == o1) ||
+                              (details::e_sub == o1)
+                            )
+                          )
+                  {
+                     std::string specfunc;
+
+                     switch (o1)
+                     {
+                        case details::e_add : specfunc = "t*(t+t)"; break;
+                        case details::e_sub : specfunc = "t*(t-t)"; break;
+                        default             : return error_node();
+                     }
+
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen,specfunc, c0, v0, v1, result);
+
+                     exprtk_debug(("(c * v0) +/- (v1 * c) --> (covov) c * (v0 +/- v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, v1, c1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, v1, c1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vococov_expression0
+         {
+            typedef typename vococov_t::type0 node_type;
+            typedef typename vococov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v0 o0 c0) o1 (c1 o2 v1)
+               const details::voc_base_node<Type>* voc = static_cast<details::voc_base_node<Type>*>(branch[0]);
+               const details::cov_base_node<Type>* cov = static_cast<details::cov_base_node<Type>*>(branch[1]);
+               const Type  c0 = voc->c();
+               const Type& v0 = voc->v();
+               const Type  c1 = cov->c();
+               const Type& v1 = cov->v();
+               const details::operator_type o0 = voc->operation();
+               const details::operator_type o1 = operation;
+               const details::operator_type o2 = cov->operation();
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (expr_gen.parser_->settings_.strength_reduction_enabled())
+               {
+                  // (v0 + c0) + (c1 + v1) --> (covov) (c0 + c1) + v0 + v1
+                  if ((details::e_add == o0) && (details::e_add == o1) && (details::e_add == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)+t", (c0 + c1), v0, v1, result);
+
+                     exprtk_debug(("(v0 + c0) + (c1 + v1) --> (covov) (c0 + c1) + v0 + v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 + c0) - (c1 + v1) --> (covov) (c0 - c1) + v0 - v1
+                  else if ((details::e_add == o0) && (details::e_sub == o1) && (details::e_add == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t+t)-t", (c0 - c1), v0, v1, result);
+
+                     exprtk_debug(("(v0 + c0) - (c1 + v1) --> (covov) (c0 - c1) + v0 - v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 - c0) - (c1 - v1) --> (vovoc) v0 + v1 - (c1 + c0)
+                  else if ((details::e_sub == o0) && (details::e_sub == o1) && (details::e_sub == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<vtype,vtype,ctype>(expr_gen, "(t+t)-t", v0, v1, (c1 + c0), result);
+
+                     exprtk_debug(("(v0 - c0) - (c1 - v1) --> (vovoc) v0 + v1 - (c1 + c0)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 * c0) * (c1 * v1) --> (covov) (c0 * c1) * v0 * v1
+                  else if ((details::e_mul == o0) && (details::e_mul == o1) && (details::e_mul == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)*t", (c0 * c1), v0, v1, result);
+
+                     exprtk_debug(("(v0 * c0) * (c1 * v1) --> (covov) (c0 * c1) * v0 * v1\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 * c0) / (c1 * v1) --> (covov) (c0 / c1) * (v0 * v1)
+                  else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_mul == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c0 / c1), v0, v1, result);
+
+                     exprtk_debug(("(v0 * c0) / (c1 * v1) --> (covov) (c0 / c1) * (v0 * v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 / c0) * (c1 / v1) --> (covov) (c1 / c0) * (v0 / v1)
+                  else if ((details::e_div == o0) && (details::e_mul == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", (c1 / c0), v0, v1, result);
+
+                     exprtk_debug(("(v0 / c0) * (c1 / v1) --> (covov) (c1 / c0) * (v0 / v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 * c0) / (c1 / v1) --> (covov) (c0 / c1) * (v0 * v1)
+                  else if ((details::e_mul == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)*t", (c0 / c1), v0, v1, result);
+
+                     exprtk_debug(("(v0 * c0) / (c1 / v1) --> (covov) (c0 / c1) * (v0 * v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 / c0) / (c1 * v1) --> (covov) (1 / (c0 * c1)) * (v0 / v1)
+                  else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_mul == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, "(t*t)/t", Type(1) / (c0 * c1), v0, v1, result);
+
+                     exprtk_debug(("(v0 / c0) / (c1 * v1) --> (covov) (1 / (c0 * c1)) * (v0 / v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 / c0) / (c1 / v1) --> (vovoc) (v0 * v1) * (1 / (c0 * c1))
+                  else if ((details::e_div == o0) && (details::e_div == o1) && (details::e_div == o2))
+                  {
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<vtype,vtype,ctype>(expr_gen, "(t*t)*t", v0, v1, Type(1) / (c0 * c1), result);
+
+                     exprtk_debug(("(v0 / c0) / (c1 / v1) --> (vovoc) (v0 * v1) * (1 / (c0 * c1))\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+                  // (v0 * c) +/- (c * v1) --> (covov) c * (v0 +/- v1)
+                  else if (
+                            (std::equal_to<T>()(c0,c1)) &&
+                            (details::e_mul == o0)      &&
+                            (details::e_mul == o2)      &&
+                            (
+                              (details::e_add == o1) || (details::e_sub == o1)
+                            )
+                          )
+                  {
+                     std::string specfunc;
+
+                     switch (o1)
+                     {
+                        case details::e_add : specfunc = "t*(t+t)"; break;
+                        case details::e_sub : specfunc = "t*(t-t)"; break;
+                        default             : return error_node();
+                     }
+
+                     const bool synthesis_result =
+                        synthesize_sf3ext_expression::
+                           template compile<ctype,vtype,vtype>(expr_gen, specfunc, c0, v0, v1, result);
+
+                     exprtk_debug(("(v0 * c) +/- (c * v1) --> (covov) c * (v0 +/- v1)\n"));
+
+                     return (synthesis_result) ? result : error_node();
+                  }
+               }
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, c1, v1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o1,f1))
+                  return error_node();
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+               else
+                  return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, c1, v1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "t)" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vovovov_expression1
+         {
+            typedef typename vovovov_t::type1 node_type;
+            typedef typename vovovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // v0 o0 (v1 o1 (v2 o2 v3))
+               typedef typename synthesize_vovov_expression1::node_type lcl_vovov_t;
+
+               const lcl_vovov_t* vovov = static_cast<const lcl_vovov_t*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type& v1 = vovov->t0();
+               const Type& v2 = vovov->t1();
+               const Type& v3 = vovov->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(vovov->f0());
+               const details::operator_type o2 = expr_gen.get_operator(vovov->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = vovov->f0();
+               binary_functor_t f2 = vovov->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               if (synthesize_sf4ext_expression::template compile<T0,T1,T2,T3>(expr_gen,id(expr_gen,o0,o1,o2),v0,v1,v2,v3,result))
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("v0 o0 (v1 o1 (v2 o2 v3))\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_),v0,v1,v2,v3,f0,f1,f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t))";
+            }
+         };
+
+         struct synthesize_vovovoc_expression1
+         {
+            typedef typename vovovoc_t::type1 node_type;
+            typedef typename vovovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // v0 o0 (v1 o1 (v2 o2 c))
+               typedef typename synthesize_vovoc_expression1::node_type lcl_vovoc_t;
+
+               const lcl_vovoc_t* vovoc = static_cast<const lcl_vovoc_t*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type& v1 = vovoc->t0();
+               const Type& v2 = vovoc->t1();
+               const Type   c = vovoc->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(vovoc->f0());
+               const details::operator_type o2 = expr_gen.get_operator(vovoc->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = vovoc->f0();
+               binary_functor_t f2 = vovoc->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, c, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("v0 o0 (v1 o1 (v2 o2 c))\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, c, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t))";
+            }
+         };
+
+         struct synthesize_vovocov_expression1
+         {
+            typedef typename vovocov_t::type1 node_type;
+            typedef typename vovocov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // v0 o0 (v1 o1 (c o2 v2))
+               typedef typename synthesize_vocov_expression1::node_type lcl_vocov_t;
+
+               const lcl_vocov_t* vocov = static_cast<const lcl_vocov_t*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type& v1 = vocov->t0();
+               const Type   c = vocov->t1();
+               const Type& v2 = vocov->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(vocov->f0());
+               const details::operator_type o2 = expr_gen.get_operator(vocov->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = vocov->f0();
+               binary_functor_t f2 = vocov->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, c, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("v0 o0 (v1 o1 (c o2 v2))\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t))";
+            }
+         };
+
+         struct synthesize_vocovov_expression1
+         {
+            typedef typename vocovov_t::type1 node_type;
+            typedef typename vocovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // v0 o0 (c o1 (v1 o2 v2))
+               typedef typename synthesize_covov_expression1::node_type lcl_covov_t;
+
+               const lcl_covov_t* covov = static_cast<const lcl_covov_t*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type   c = covov->t0();
+               const Type& v1 = covov->t1();
+               const Type& v2 = covov->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(covov->f0());
+               const details::operator_type o2 = expr_gen.get_operator(covov->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = covov->f0();
+               binary_functor_t f2 = covov->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, c, v1, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("v0 o0 (c o1 (v1 o2 v2))\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t))";
+            }
+         };
+
+         struct synthesize_covovov_expression1
+         {
+            typedef typename covovov_t::type1 node_type;
+            typedef typename covovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // c o0 (v0 o1 (v1 o2 v2))
+               typedef typename synthesize_vovov_expression1::node_type lcl_vovov_t;
+
+               const lcl_vovov_t* vovov = static_cast<const lcl_vovov_t*>(branch[1]);
+               const Type   c = static_cast<details::literal_node<Type>*>(branch[0])->value();
+               const Type& v0 = vovov->t0();
+               const Type& v1 = vovov->t1();
+               const Type& v2 = vovov->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(vovov->f0());
+               const details::operator_type o2 = expr_gen.get_operator(vovov->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = vovov->f0();
+               binary_functor_t f2 = vovov->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c, v0, v1, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("c o0 (v0 o1 (v1 o2 v2))\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t))";
+            }
+         };
+
+         struct synthesize_covocov_expression1
+         {
+            typedef typename covocov_t::type1 node_type;
+            typedef typename covocov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // c0 o0 (v0 o1 (c1 o2 v1))
+               typedef typename synthesize_vocov_expression1::node_type lcl_vocov_t;
+
+               const lcl_vocov_t* vocov = static_cast<const lcl_vocov_t*>(branch[1]);
+               const Type  c0 = static_cast<details::literal_node<Type>*>(branch[0])->value();
+               const Type& v0 = vocov->t0();
+               const Type  c1 = vocov->t1();
+               const Type& v1 = vocov->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(vocov->f0());
+               const details::operator_type o2 = expr_gen.get_operator(vocov->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = vocov->f0();
+               binary_functor_t f2 = vocov->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, c1, v1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("c0 o0 (v0 o1 (c1 o2 v1))\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, c1, v1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t))";
+            }
+         };
+
+         struct synthesize_vocovoc_expression1
+         {
+            typedef typename vocovoc_t::type1 node_type;
+            typedef typename vocovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // v0 o0 (c0 o1 (v1 o2 c2))
+               typedef typename synthesize_covoc_expression1::node_type lcl_covoc_t;
+
+               const lcl_covoc_t* covoc = static_cast<const lcl_covoc_t*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type  c0 = covoc->t0();
+               const Type& v1 = covoc->t1();
+               const Type  c1 = covoc->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(covoc->f0());
+               const details::operator_type o2 = expr_gen.get_operator(covoc->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = covoc->f0();
+               binary_functor_t f2 = covoc->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, v1, c1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("v0 o0 (c0 o1 (v1 o2 c2))\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, v1, c1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t))";
+            }
+         };
+
+         struct synthesize_covovoc_expression1
+         {
+            typedef typename covovoc_t::type1 node_type;
+            typedef typename covovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // c0 o0 (v0 o1 (v1 o2 c1))
+               typedef typename synthesize_vovoc_expression1::node_type lcl_vovoc_t;
+
+               const lcl_vovoc_t* vovoc = static_cast<const lcl_vovoc_t*>(branch[1]);
+               const Type  c0 = static_cast<details::literal_node<Type>*>(branch[0])->value();
+               const Type& v0 = vovoc->t0();
+               const Type& v1 = vovoc->t1();
+               const Type  c1 = vovoc->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(vovoc->f0());
+               const details::operator_type o2 = expr_gen.get_operator(vovoc->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = vovoc->f0();
+               binary_functor_t f2 = vovoc->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, v1, c1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("c0 o0 (v0 o1 (v1 o2 c1))\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, v1, c1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t))";
+            }
+         };
+
+         struct synthesize_vococov_expression1
+         {
+            typedef typename vococov_t::type1 node_type;
+            typedef typename vococov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // v0 o0 (c0 o1 (c1 o2 v1))
+               typedef typename synthesize_cocov_expression1::node_type lcl_cocov_t;
+
+               const lcl_cocov_t* cocov = static_cast<const lcl_cocov_t*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type  c0 = cocov->t0();
+               const Type  c1 = cocov->t1();
+               const Type& v1 = cocov->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(cocov->f0());
+               const details::operator_type o2 = expr_gen.get_operator(cocov->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = cocov->f0();
+               binary_functor_t f2 = cocov->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, c1, v1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("v0 o0 (c0 o1 (c1 o2 v1))\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, c1, v1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"  << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "(t" << expr_gen.to_str(o2)
+                         << "t))";
+            }
+         };
+
+         struct synthesize_vovovov_expression2
+         {
+            typedef typename vovovov_t::type2 node_type;
+            typedef typename vovovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // v0 o0 ((v1 o1 v2) o2 v3)
+               typedef typename synthesize_vovov_expression0::node_type lcl_vovov_t;
+
+               const lcl_vovov_t* vovov = static_cast<const lcl_vovov_t*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type& v1 = vovov->t0();
+               const Type& v2 = vovov->t1();
+               const Type& v3 = vovov->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(vovov->f0());
+               const details::operator_type o2 = expr_gen.get_operator(vovov->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = vovov->f0();
+               binary_functor_t f2 = vovov->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, v3, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("v0 o0 ((v1 o1 v2) o2 v3)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, v3, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"   << expr_gen.to_str(o0)
+                         << "((t" << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vovovoc_expression2
+         {
+            typedef typename vovovoc_t::type2 node_type;
+            typedef typename vovovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // v0 o0 ((v1 o1 v2) o2 c)
+               typedef typename synthesize_vovoc_expression0::node_type lcl_vovoc_t;
+
+               const lcl_vovoc_t* vovoc = static_cast<const lcl_vovoc_t*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type& v1 = vovoc->t0();
+               const Type& v2 = vovoc->t1();
+               const Type   c = vovoc->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(vovoc->f0());
+               const details::operator_type o2 = expr_gen.get_operator(vovoc->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = vovoc->f0();
+               binary_functor_t f2 = vovoc->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, c, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("v0 o0 ((v1 o1 v2) o2 c)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, c, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"   << expr_gen.to_str(o0)
+                         << "((t" << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vovocov_expression2
+         {
+            typedef typename vovocov_t::type2 node_type;
+            typedef typename vovocov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // v0 o0 ((v1 o1 c) o2 v2)
+               typedef typename synthesize_vocov_expression0::node_type lcl_vocov_t;
+
+               const lcl_vocov_t* vocov = static_cast<const lcl_vocov_t*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type& v1 = vocov->t0();
+               const Type   c = vocov->t1();
+               const Type& v2 = vocov->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(vocov->f0());
+               const details::operator_type o2 = expr_gen.get_operator(vocov->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = vocov->f0();
+               binary_functor_t f2 = vocov->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, c, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("v0 o0 ((v1 o1 c) o2 v2)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"   << expr_gen.to_str(o0)
+                         << "((t" << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vocovov_expression2
+         {
+            typedef typename vocovov_t::type2 node_type;
+            typedef typename vocovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // v0 o0 ((c o1 v1) o2 v2)
+               typedef typename synthesize_covov_expression0::node_type lcl_covov_t;
+
+               const lcl_covov_t* covov = static_cast<const lcl_covov_t*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type   c = covov->t0();
+               const Type& v1 = covov->t1();
+               const Type& v2 = covov->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(covov->f0());
+               const details::operator_type o2 = expr_gen.get_operator(covov->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = covov->f0();
+               binary_functor_t f2 = covov->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, c, v1, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("v0 o0 ((c o1 v1) o2 v2)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"   << expr_gen.to_str(o0)
+                         << "((t" << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_covovov_expression2
+         {
+            typedef typename covovov_t::type2 node_type;
+            typedef typename covovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // c o0 ((v1 o1 v2) o2 v3)
+               typedef typename synthesize_vovov_expression0::node_type lcl_vovov_t;
+
+               const lcl_vovov_t* vovov = static_cast<const lcl_vovov_t*>(branch[1]);
+               const Type   c = static_cast<details::literal_node<Type>*>(branch[0])->value();
+               const Type& v0 = vovov->t0();
+               const Type& v1 = vovov->t1();
+               const Type& v2 = vovov->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(vovov->f0());
+               const details::operator_type o2 = expr_gen.get_operator(vovov->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = vovov->f0();
+               binary_functor_t f2 = vovov->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c, v0, v1, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("c o0 ((v1 o1 v2) o2 v3)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"   << expr_gen.to_str(o0)
+                         << "((t" << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t)";
+            }
+        };
+
+         struct synthesize_covocov_expression2
+         {
+            typedef typename covocov_t::type2 node_type;
+            typedef typename covocov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // c0 o0 ((v0 o1 c1) o2 v1)
+               typedef typename synthesize_vocov_expression0::node_type lcl_vocov_t;
+
+               const lcl_vocov_t* vocov = static_cast<const lcl_vocov_t*>(branch[1]);
+               const Type  c0 = static_cast<details::literal_node<Type>*>(branch[0])->value();
+               const Type& v0 = vocov->t0();
+               const Type  c1 = vocov->t1();
+               const Type& v1 = vocov->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(vocov->f0());
+               const details::operator_type o2 = expr_gen.get_operator(vocov->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = vocov->f0();
+               binary_functor_t f2 = vocov->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, c1, v1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("c0 o0 ((v0 o1 c1) o2 v1)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, c1, v1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"   << expr_gen.to_str(o0)
+                         << "((t" << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vocovoc_expression2
+         {
+            typedef typename vocovoc_t::type2 node_type;
+            typedef typename vocovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // v0 o0 ((c0 o1 v1) o2 c1)
+               typedef typename synthesize_covoc_expression0::node_type lcl_covoc_t;
+
+               const lcl_covoc_t* covoc = static_cast<const lcl_covoc_t*>(branch[1]);
+               const Type& v0 = static_cast<details::variable_node<Type>*>(branch[0])->ref();
+               const Type  c0 = covoc->t0();
+               const Type& v1 = covoc->t1();
+               const Type  c1 = covoc->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(covoc->f0());
+               const details::operator_type o2 = expr_gen.get_operator(covoc->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = covoc->f0();
+               binary_functor_t f2 = covoc->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, v1, c1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("v0 o0 ((c0 o1 v1) o2 c1)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, v1, c1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"   << expr_gen.to_str(o0)
+                         << "((t" << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_covovoc_expression2
+         {
+            typedef typename covovoc_t::type2 node_type;
+            typedef typename covovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // c0 o0 ((v0 o1 v1) o2 c1)
+               typedef typename synthesize_vovoc_expression0::node_type lcl_vovoc_t;
+
+               const lcl_vovoc_t* vovoc = static_cast<const lcl_vovoc_t*>(branch[1]);
+               const Type  c0 = static_cast<details::literal_node<Type>*>(branch[0])->value();
+               const Type& v0 = vovoc->t0();
+               const Type& v1 = vovoc->t1();
+               const Type  c1 = vovoc->t2();
+               const details::operator_type o0 = operation;
+               const details::operator_type o1 = expr_gen.get_operator(vovoc->f0());
+               const details::operator_type o2 = expr_gen.get_operator(vovoc->f1());
+
+               binary_functor_t f0 = reinterpret_cast<binary_functor_t>(0);
+               binary_functor_t f1 = vovoc->f0();
+               binary_functor_t f2 = vovoc->f1();
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, v1, c1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o0,f0))
+                  return error_node();
+
+               exprtk_debug(("c0 o0 ((v0 o1 v1) o2 c1)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, v1, c1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "t"   << expr_gen.to_str(o0)
+                         << "((t" << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t)";
+            }
+         };
+
+         struct synthesize_vococov_expression2
+         {
+            typedef typename vococov_t::type2 node_type;
+            static inline expression_node_ptr process(expression_generator<Type>&, const details::operator_type&, expression_node_ptr (&)[2])
+            {
+               // v0 o0 ((c0 o1 c1) o2 v1) - Not possible
+               exprtk_debug(("v0 o0 ((c0 o1 c1) o2 v1) - Not possible\n"));
+               return error_node();
+            }
+
+            static inline std::string id(expression_generator<Type>&,
+                                         const details::operator_type, const details::operator_type, const details::operator_type)
+            {
+               return "INVALID";
+            }
+         };
+
+         struct synthesize_vovovov_expression3
+         {
+            typedef typename vovovov_t::type3 node_type;
+            typedef typename vovovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((v0 o0 v1) o1 v2) o2 v3
+               typedef typename synthesize_vovov_expression0::node_type lcl_vovov_t;
+
+               const lcl_vovov_t* vovov = static_cast<const lcl_vovov_t*>(branch[0]);
+               const Type& v0 = vovov->t0();
+               const Type& v1 = vovov->t1();
+               const Type& v2 = vovov->t2();
+               const Type& v3 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = expr_gen.get_operator(vovov->f0());
+               const details::operator_type o1 = expr_gen.get_operator(vovov->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = vovov->f0();
+               binary_functor_t f1 = vovov->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, v3, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((v0 o0 v1) o1 v2) o2 v3\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, v3, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "((t" << expr_gen.to_str(o0)
+                         << "t)"  << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vovovoc_expression3
+         {
+            typedef typename vovovoc_t::type3 node_type;
+            typedef typename vovovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((v0 o0 v1) o1 v2) o2 c
+               typedef typename synthesize_vovov_expression0::node_type lcl_vovov_t;
+
+               const lcl_vovov_t* vovov = static_cast<const lcl_vovov_t*>(branch[0]);
+               const Type& v0 = vovov->t0();
+               const Type& v1 = vovov->t1();
+               const Type& v2 = vovov->t2();
+               const Type   c = static_cast<details::literal_node<Type>*>(branch[1])->value();
+               const details::operator_type o0 = expr_gen.get_operator(vovov->f0());
+               const details::operator_type o1 = expr_gen.get_operator(vovov->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = vovov->f0();
+               binary_functor_t f1 = vovov->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, c, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((v0 o0 v1) o1 v2) o2 c\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, c, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "((t" << expr_gen.to_str(o0)
+                         << "t)"  << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vovocov_expression3
+         {
+            typedef typename vovocov_t::type3 node_type;
+            typedef typename vovocov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((v0 o0 v1) o1 c) o2 v2
+               typedef typename synthesize_vovoc_expression0::node_type lcl_vovoc_t;
+
+               const lcl_vovoc_t* vovoc = static_cast<const lcl_vovoc_t*>(branch[0]);
+               const Type& v0 = vovoc->t0();
+               const Type& v1 = vovoc->t1();
+               const Type   c = vovoc->t2();
+               const Type& v2 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = expr_gen.get_operator(vovoc->f0());
+               const details::operator_type o1 = expr_gen.get_operator(vovoc->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = vovoc->f0();
+               binary_functor_t f1 = vovoc->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, c, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((v0 o0 v1) o1 c) o2 v2\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "((t" << expr_gen.to_str(o0)
+                         << "t)"  << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vocovov_expression3
+         {
+            typedef typename vocovov_t::type3 node_type;
+            typedef typename vocovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((v0 o0 c) o1 v1) o2 v2
+               typedef typename synthesize_vocov_expression0::node_type lcl_vocov_t;
+
+               const lcl_vocov_t* vocov = static_cast<const lcl_vocov_t*>(branch[0]);
+               const Type& v0 = vocov->t0();
+               const Type   c = vocov->t1();
+               const Type& v1 = vocov->t2();
+               const Type& v2 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = expr_gen.get_operator(vocov->f0());
+               const details::operator_type o1 = expr_gen.get_operator(vocov->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = vocov->f0();
+               binary_functor_t f1 = vocov->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, c, v1, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((v0 o0 c) o1 v1) o2 v2\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "((t" << expr_gen.to_str(o0)
+                         << "t)"  << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_covovov_expression3
+         {
+            typedef typename covovov_t::type3 node_type;
+            typedef typename covovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((c o0 v0) o1 v1) o2 v2
+               typedef typename synthesize_covov_expression0::node_type lcl_covov_t;
+
+               const lcl_covov_t* covov = static_cast<const lcl_covov_t*>(branch[0]);
+               const Type   c = covov->t0();
+               const Type& v0 = covov->t1();
+               const Type& v1 = covov->t2();
+               const Type& v2 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = expr_gen.get_operator(covov->f0());
+               const details::operator_type o1 = expr_gen.get_operator(covov->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = covov->f0();
+               binary_functor_t f1 = covov->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c, v0, v1, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((c o0 v0) o1 v1) o2 v2\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "((t" << expr_gen.to_str(o0)
+                         << "t)"  << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_covocov_expression3
+         {
+            typedef typename covocov_t::type3 node_type;
+            typedef typename covocov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((c0 o0 v0) o1 c1) o2 v1
+               typedef typename synthesize_covoc_expression0::node_type lcl_covoc_t;
+
+               const lcl_covoc_t* covoc = static_cast<const lcl_covoc_t*>(branch[0]);
+               const Type  c0 = covoc->t0();
+               const Type& v0 = covoc->t1();
+               const Type  c1 = covoc->t2();
+               const Type& v1 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = expr_gen.get_operator(covoc->f0());
+               const details::operator_type o1 = expr_gen.get_operator(covoc->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = covoc->f0();
+               binary_functor_t f1 = covoc->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, c1, v1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((c0 o0 v0) o1 c1) o2 v1\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, c1, v1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "((t" << expr_gen.to_str(o0)
+                         << "t)"  << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vocovoc_expression3
+         {
+            typedef typename vocovoc_t::type3 node_type;
+            typedef typename vocovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((v0 o0 c0) o1 v1) o2 c1
+               typedef typename synthesize_vocov_expression0::node_type lcl_vocov_t;
+
+               const lcl_vocov_t* vocov = static_cast<const lcl_vocov_t*>(branch[0]);
+               const Type& v0 = vocov->t0();
+               const Type  c0 = vocov->t1();
+               const Type& v1 = vocov->t2();
+               const Type  c1 = static_cast<details::literal_node<Type>*>(branch[1])->value();
+               const details::operator_type o0 = expr_gen.get_operator(vocov->f0());
+               const details::operator_type o1 = expr_gen.get_operator(vocov->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = vocov->f0();
+               binary_functor_t f1 = vocov->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, v1, c1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((v0 o0 c0) o1 v1) o2 c1\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, v1, c1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "((t" << expr_gen.to_str(o0)
+                         << "t)"  << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_covovoc_expression3
+         {
+            typedef typename covovoc_t::type3 node_type;
+            typedef typename covovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((c0 o0 v0) o1 v1) o2 c1
+               typedef typename synthesize_covov_expression0::node_type lcl_covov_t;
+
+               const lcl_covov_t* covov = static_cast<const lcl_covov_t*>(branch[0]);
+               const Type  c0 = covov->t0();
+               const Type& v0 = covov->t1();
+               const Type& v1 = covov->t2();
+               const Type  c1 = static_cast<details::literal_node<Type>*>(branch[1])->value();
+               const details::operator_type o0 = expr_gen.get_operator(covov->f0());
+               const details::operator_type o1 = expr_gen.get_operator(covov->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = covov->f0();
+               binary_functor_t f1 = covov->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, v1, c1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((c0 o0 v0) o1 v1) o2 c1\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, v1, c1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "((t" << expr_gen.to_str(o0)
+                         << "t)"  << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vococov_expression3
+         {
+            typedef typename vococov_t::type3 node_type;
+            typedef typename vococov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((v0 o0 c0) o1 c1) o2 v1
+               typedef typename synthesize_vococ_expression0::node_type lcl_vococ_t;
+
+               const lcl_vococ_t* vococ = static_cast<const lcl_vococ_t*>(branch[0]);
+               const Type& v0 = vococ->t0();
+               const Type  c0 = vococ->t1();
+               const Type  c1 = vococ->t2();
+               const Type& v1 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = expr_gen.get_operator(vococ->f0());
+               const details::operator_type o1 = expr_gen.get_operator(vococ->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = vococ->f0();
+               binary_functor_t f1 = vococ->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, c1, v1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((v0 o0 c0) o1 c1) o2 v1\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, c1, v1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "((t" << expr_gen.to_str(o0)
+                         << "t)"  << expr_gen.to_str(o1)
+                         << "t)"  << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vovovov_expression4
+         {
+            typedef typename vovovov_t::type4 node_type;
+            typedef typename vovovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // (v0 o0 (v1 o1 v2)) o2 v3
+               typedef typename synthesize_vovov_expression1::node_type lcl_vovov_t;
+
+               const lcl_vovov_t* vovov = static_cast<const lcl_vovov_t*>(branch[0]);
+               const Type& v0 = vovov->t0();
+               const Type& v1 = vovov->t1();
+               const Type& v2 = vovov->t2();
+               const Type& v3 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = expr_gen.get_operator(vovov->f0());
+               const details::operator_type o1 = expr_gen.get_operator(vovov->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = vovov->f0();
+               binary_functor_t f1 = vovov->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, v3, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("(v0 o0 (v1 o1 v2)) o2 v3\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, v3, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)" << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vovovoc_expression4
+         {
+            typedef typename vovovoc_t::type4 node_type;
+            typedef typename vovovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((v0 o0 (v1 o1 v2)) o2 c)
+               typedef typename synthesize_vovov_expression1::node_type lcl_vovov_t;
+
+               const lcl_vovov_t* vovov = static_cast<const lcl_vovov_t*>(branch[0]);
+               const Type& v0 = vovov->t0();
+               const Type& v1 = vovov->t1();
+               const Type& v2 = vovov->t2();
+               const Type   c = static_cast<details::literal_node<Type>*>(branch[1])->value();
+               const details::operator_type o0 = expr_gen.get_operator(vovov->f0());
+               const details::operator_type o1 = expr_gen.get_operator(vovov->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = vovov->f0();
+               binary_functor_t f1 = vovov->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, c, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((v0 o0 (v1 o1 v2)) o2 c)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, v2, c, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)" << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vovocov_expression4
+         {
+            typedef typename vovocov_t::type4 node_type;
+            typedef typename vovocov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((v0 o0 (v1 o1 c)) o2 v1)
+               typedef typename synthesize_vovoc_expression1::node_type lcl_vovoc_t;
+
+               const lcl_vovoc_t* vovoc = static_cast<const lcl_vovoc_t*>(branch[0]);
+               const Type& v0 = vovoc->t0();
+               const Type& v1 = vovoc->t1();
+               const Type   c = vovoc->t2();
+               const Type& v2 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = expr_gen.get_operator(vovoc->f0());
+               const details::operator_type o1 = expr_gen.get_operator(vovoc->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = vovoc->f0();
+               binary_functor_t f1 = vovoc->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, c, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((v0 o0 (v1 o1 c)) o2 v1)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, v1, c, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)" << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vocovov_expression4
+         {
+            typedef typename vocovov_t::type4 node_type;
+            typedef typename vocovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((v0 o0 (c o1 v1)) o2 v2)
+               typedef typename synthesize_vocov_expression1::node_type lcl_vocov_t;
+
+               const lcl_vocov_t* vocov = static_cast<const lcl_vocov_t*>(branch[0]);
+               const Type& v0 = vocov->t0();
+               const Type   c = vocov->t1();
+               const Type& v1 = vocov->t2();
+               const Type& v2 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = expr_gen.get_operator(vocov->f0());
+               const details::operator_type o1 = expr_gen.get_operator(vocov->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = vocov->f0();
+               binary_functor_t f1 = vocov->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, c, v1, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((v0 o0 (c o1 v1)) o2 v2)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, c, v1, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)" << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_covovov_expression4
+         {
+            typedef typename covovov_t::type4 node_type;
+            typedef typename covovov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((c o0 (v0 o1 v1)) o2 v2)
+               typedef typename synthesize_covov_expression1::node_type lcl_covov_t;
+
+               const lcl_covov_t* covov = static_cast<const lcl_covov_t*>(branch[0]);
+               const Type   c = covov->t0();
+               const Type& v0 = covov->t1();
+               const Type& v1 = covov->t2();
+               const Type& v2 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = expr_gen.get_operator(covov->f0());
+               const details::operator_type o1 = expr_gen.get_operator(covov->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = covov->f0();
+               binary_functor_t f1 = covov->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c, v0, v1, v2, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((c o0 (v0 o1 v1)) o2 v2)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), c, v0, v1, v2, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)" << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_covocov_expression4
+         {
+            typedef typename covocov_t::type4 node_type;
+            typedef typename covocov_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((c0 o0 (v0 o1 c1)) o2 v1)
+               typedef typename synthesize_covoc_expression1::node_type lcl_covoc_t;
+
+               const lcl_covoc_t* covoc = static_cast<const lcl_covoc_t*>(branch[0]);
+               const Type  c0 = covoc->t0();
+               const Type& v0 = covoc->t1();
+               const Type  c1 = covoc->t2();
+               const Type& v1 = static_cast<details::variable_node<Type>*>(branch[1])->ref();
+               const details::operator_type o0 = expr_gen.get_operator(covoc->f0());
+               const details::operator_type o1 = expr_gen.get_operator(covoc->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = covoc->f0();
+               binary_functor_t f1 = covoc->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, c1, v1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((c0 o0 (v0 o1 c1)) o2 v1)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, c1, v1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)" << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vocovoc_expression4
+         {
+            typedef typename vocovoc_t::type4 node_type;
+            typedef typename vocovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((v0 o0 (c0 o1 v1)) o2 c1)
+               typedef typename synthesize_vocov_expression1::node_type lcl_vocov_t;
+
+               const lcl_vocov_t* vocov = static_cast<const lcl_vocov_t*>(branch[0]);
+               const Type& v0 = vocov->t0();
+               const Type  c0 = vocov->t1();
+               const Type& v1 = vocov->t2();
+               const Type  c1 = static_cast<details::literal_node<Type>*>(branch[1])->value();
+               const details::operator_type o0 = expr_gen.get_operator(vocov->f0());
+               const details::operator_type o1 = expr_gen.get_operator(vocov->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = vocov->f0();
+               binary_functor_t f1 = vocov->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, c0, v1, c1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((v0 o0 (c0 o1 v1)) o2 c1)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), v0, c0, v1, c1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)" << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_covovoc_expression4
+         {
+            typedef typename covovoc_t::type4 node_type;
+            typedef typename covovoc_t::sf4_type sf4_type;
+            typedef typename node_type::T0 T0;
+            typedef typename node_type::T1 T1;
+            typedef typename node_type::T2 T2;
+            typedef typename node_type::T3 T3;
+
+            static inline expression_node_ptr process(expression_generator<Type>& expr_gen,
+                                                      const details::operator_type& operation,
+                                                      expression_node_ptr (&branch)[2])
+            {
+               // ((c0 o0 (v0 o1 v1)) o2 c1)
+               typedef typename synthesize_covov_expression1::node_type lcl_covov_t;
+
+               const lcl_covov_t* covov = static_cast<const lcl_covov_t*>(branch[0]);
+               const Type  c0 = covov->t0();
+               const Type& v0 = covov->t1();
+               const Type& v1 = covov->t2();
+               const Type  c1 = static_cast<details::literal_node<Type>*>(branch[1])->value();
+               const details::operator_type o0 = expr_gen.get_operator(covov->f0());
+               const details::operator_type o1 = expr_gen.get_operator(covov->f1());
+               const details::operator_type o2 = operation;
+
+               binary_functor_t f0 = covov->f0();
+               binary_functor_t f1 = covov->f1();
+               binary_functor_t f2 = reinterpret_cast<binary_functor_t>(0);
+
+               details::free_node(*(expr_gen.node_allocator_),branch[0]);
+               details::free_node(*(expr_gen.node_allocator_),branch[1]);
+
+               expression_node_ptr result = error_node();
+
+               const bool synthesis_result =
+                  synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
+                     (expr_gen, id(expr_gen, o0, o1, o2), c0, v0, v1, c1, result);
+
+               if (synthesis_result)
+                  return result;
+               else if (!expr_gen.valid_operator(o2,f2))
+                  return error_node();
+
+               exprtk_debug(("((c0 o0 (v0 o1 v1)) o2 c1)\n"));
+
+               return node_type::allocate(*(expr_gen.node_allocator_), c0, v0, v1, c1, f0, f1, f2);
+            }
+
+            static inline std::string id(expression_generator<Type>& expr_gen,
+                                         const details::operator_type o0,
+                                         const details::operator_type o1,
+                                         const details::operator_type o2)
+            {
+               return details::build_string()
+                         << "(t" << expr_gen.to_str(o0)
+                         << "(t" << expr_gen.to_str(o1)
+                         << "t)" << expr_gen.to_str(o2)
+                         << "t";
+            }
+         };
+
+         struct synthesize_vococov_expression4
+         {
+            typedef typename vococov_t::type4 node_type;
+            static inline expression_node_ptr process(expression_generator<Type>&, const details::operator_type&, expression_node_ptr (&)[2])
+            {
+               // ((v0 o0 (c0 o1 c1)) o2 v1) - Not possible
+               exprtk_debug(("((v0 o0 (c0 o1 c1)) o2 v1) - Not possible\n"));
+               return error_node();
+            }
+
+            static inline std::string id(expression_generator<Type>&,
+                                         const details::operator_type, const details::operator_type, const details::operator_type)
+            {
+               return "INVALID";
+            }
+         };
+         #endif
+
+         inline expression_node_ptr synthesize_uvouv_expression(const details::operator_type& operation, expression_node_ptr (&branch)[2])
+         {
+            // Definition: uv o uv
+            details::operator_type o0 = static_cast<details::uv_base_node<Type>*>(branch[0])->operation();
+            details::operator_type o1 = static_cast<details::uv_base_node<Type>*>(branch[1])->operation();
+            const Type& v0 = static_cast<details::uv_base_node<Type>*>(branch[0])->v();
+            const Type& v1 = static_cast<details::uv_base_node<Type>*>(branch[1])->v();
+            unary_functor_t u0 = reinterpret_cast<unary_functor_t> (0);
+            unary_functor_t u1 = reinterpret_cast<unary_functor_t> (0);
+            binary_functor_t f = reinterpret_cast<binary_functor_t>(0);
+
+            if (!valid_operator(o0,u0))
+               return error_node();
+            else if (!valid_operator(o1,u1))
+               return error_node();
+            else if (!valid_operator(operation,f))
+               return error_node();
+
+            expression_node_ptr result = error_node();
+
+            if (
+                 (details::e_neg == o0) &&
+                 (details::e_neg == o1)
+               )
+            {
+               switch (operation)
+               {
+                  // (-v0 + -v1) --> -(v0 + v1)
+                  case details::e_add : result = (*this)(details::e_neg,
+                                                    node_allocator_->
+                                                       allocate_rr<typename details::
+                                                          vov_node<Type,details::add_op<Type> > >(v0, v1));
+                                        exprtk_debug(("(-v0 + -v1) --> -(v0 + v1)\n"));
+                                        break;
+
+                  // (-v0 - -v1) --> (v1 - v0)
+                  case details::e_sub : result = node_allocator_->
+                                                    allocate_rr<typename details::
+                                                       vov_node<Type,details::sub_op<Type> > >(v1, v0);
+                                        exprtk_debug(("(-v0 - -v1) --> (v1 - v0)\n"));
+                                        break;
+
+                  // (-v0 * -v1) --> (v0 * v1)
+                  case details::e_mul : result = node_allocator_->
+                                                    allocate_rr<typename details::
+                                                       vov_node<Type,details::mul_op<Type> > >(v0, v1);
+                                        exprtk_debug(("(-v0 * -v1) --> (v0 * v1)\n"));
+                                        break;
+
+                  // (-v0 / -v1) --> (v0 / v1)
+                  case details::e_div : result = node_allocator_->
+                                                    allocate_rr<typename details::
+                                                       vov_node<Type,details::div_op<Type> > >(v0, v1);
+                                        exprtk_debug(("(-v0 / -v1) --> (v0 / v1)\n"));
+                                        break;
+
+                  default             : break;
+               }
+            }
+
+            if (0 == result)
+            {
+               result = node_allocator_->
+                            allocate_rrrrr<typename details::uvouv_node<Type> >(v0, v1, u0, u1, f);
+            }
+
+            details::free_all_nodes(*node_allocator_,branch);
+            return result;
+         }
+
+         #undef basic_opr_switch_statements
+         #undef extended_opr_switch_statements
+         #undef unary_opr_switch_statements
+
+         #ifndef exprtk_disable_string_capabilities
+
+         #define string_opr_switch_statements          \
+         case_stmt(details::  e_lt ,details::   lt_op) \
+         case_stmt(details:: e_lte ,details::  lte_op) \
+         case_stmt(details::  e_gt ,details::   gt_op) \
+         case_stmt(details:: e_gte ,details::  gte_op) \
+         case_stmt(details::  e_eq ,details::   eq_op) \
+         case_stmt(details::  e_ne ,details::   ne_op) \
+         case_stmt(details::e_in   ,details::   in_op) \
+         case_stmt(details::e_like ,details:: like_op) \
+         case_stmt(details::e_ilike,details::ilike_op) \
+
+         template <typename T0, typename T1>
+         inline expression_node_ptr synthesize_str_xrox_expression_impl(const details::operator_type& opr,
+                                                                        T0 s0, T1 s1,
+                                                                        range_t rp0)
+         {
+            switch (opr)
+            {
+               #define case_stmt(op0,op1)                                                                       \
+               case op0 : return node_allocator_->                                                              \
+                             allocate_ttt<typename details::str_xrox_node<Type,T0,T1,range_t,op1<Type> >,T0,T1> \
+                                (s0, s1, rp0);                                                                  \
+
+               string_opr_switch_statements
+               #undef case_stmt
+               default : return error_node();
+            }
+         }
+
+         template <typename T0, typename T1>
+         inline expression_node_ptr synthesize_str_xoxr_expression_impl(const details::operator_type& opr,
+                                                                        T0 s0, T1 s1,
+                                                                        range_t rp1)
+         {
+            switch (opr)
+            {
+               #define case_stmt(op0,op1)                                                                       \
+               case op0 : return node_allocator_->                                                              \
+                             allocate_ttt<typename details::str_xoxr_node<Type,T0,T1,range_t,op1<Type> >,T0,T1> \
+                                (s0, s1, rp1);                                                                  \
+
+               string_opr_switch_statements
+               #undef case_stmt
+               default : return error_node();
+            }
+         }
+
+         template <typename T0, typename T1>
+         inline expression_node_ptr synthesize_str_xroxr_expression_impl(const details::operator_type& opr,
+                                                                         T0 s0, T1 s1,
+                                                                         range_t rp0, range_t rp1)
+         {
+            switch (opr)
+            {
+               #define case_stmt(op0,op1)                                                                         \
+               case op0 : return node_allocator_->                                                                \
+                             allocate_tttt<typename details::str_xroxr_node<Type,T0,T1,range_t,op1<Type> >,T0,T1> \
+                                (s0, s1, rp0, rp1);                                                               \
+
+               string_opr_switch_statements
+               #undef case_stmt
+               default : return error_node();
+            }
+         }
+
+         template <typename T0, typename T1>
+         inline expression_node_ptr synthesize_sos_expression_impl(const details::operator_type& opr, T0 s0, T1 s1)
+         {
+            switch (opr)
+            {
+               #define case_stmt(op0,op1)                                                                  \
+               case op0 : return node_allocator_->                                                         \
+                             allocate_tt<typename details::sos_node<Type,T0,T1,op1<Type> >,T0,T1>(s0, s1); \
+
+               string_opr_switch_statements
+               #undef case_stmt
+               default : return error_node();
+            }
+         }
+
+         inline expression_node_ptr synthesize_sos_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            std::string& s0 = static_cast<details::stringvar_node<Type>*>(branch[0])->ref();
+            std::string& s1 = static_cast<details::stringvar_node<Type>*>(branch[1])->ref();
+
+            return synthesize_sos_expression_impl<std::string&,std::string&>(opr, s0, s1);
+         }
+
+         inline expression_node_ptr synthesize_sros_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            std::string&  s0 = static_cast<details::string_range_node<Type>*>(branch[0])->ref  ();
+            std::string&  s1 = static_cast<details::stringvar_node<Type>*>   (branch[1])->ref  ();
+            range_t      rp0 = static_cast<details::string_range_node<Type>*>(branch[0])->range();
+
+            static_cast<details::string_range_node<Type>*>(branch[0])->range_ref().clear();
+
+            free_node(*node_allocator_,branch[0]);
+
+            return synthesize_str_xrox_expression_impl<std::string&,std::string&>(opr, s0, s1, rp0);
+         }
+
+         inline expression_node_ptr synthesize_sosr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            std::string&  s0 = static_cast<details::stringvar_node<Type>*>   (branch[0])->ref  ();
+            std::string&  s1 = static_cast<details::string_range_node<Type>*>(branch[1])->ref  ();
+            range_t      rp1 = static_cast<details::string_range_node<Type>*>(branch[1])->range();
+
+            static_cast<details::string_range_node<Type>*>(branch[1])->range_ref().clear();
+
+            free_node(*node_allocator_,branch[1]);
+
+            return synthesize_str_xoxr_expression_impl<std::string&,std::string&>(opr, s0, s1, rp1);
+         }
+
+         inline expression_node_ptr synthesize_socsr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            std::string&  s0 = static_cast<details::stringvar_node<Type>*>         (branch[0])->ref  ();
+            std::string   s1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->str  ();
+            range_t      rp1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->range();
+
+            static_cast<details::const_string_range_node<Type>*>(branch[1])->range_ref().clear();
+
+            free_node(*node_allocator_,branch[1]);
+
+            return synthesize_str_xoxr_expression_impl<std::string&, const std::string>(opr, s0, s1, rp1);
+         }
+
+         inline expression_node_ptr synthesize_srosr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            std::string&  s0 = static_cast<details::string_range_node<Type>*>(branch[0])->ref  ();
+            std::string&  s1 = static_cast<details::string_range_node<Type>*>(branch[1])->ref  ();
+            range_t      rp0 = static_cast<details::string_range_node<Type>*>(branch[0])->range();
+            range_t      rp1 = static_cast<details::string_range_node<Type>*>(branch[1])->range();
+
+            static_cast<details::string_range_node<Type>*>(branch[0])->range_ref().clear();
+            static_cast<details::string_range_node<Type>*>(branch[1])->range_ref().clear();
+
+            details::free_node(*node_allocator_,branch[0]);
+            details::free_node(*node_allocator_,branch[1]);
+
+            return synthesize_str_xroxr_expression_impl<std::string&,std::string&>(opr, s0, s1, rp0, rp1);
+         }
+
+         inline expression_node_ptr synthesize_socs_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            std::string& s0 = static_cast<     details::stringvar_node<Type>*>(branch[0])->ref();
+            std::string  s1 = static_cast<details::string_literal_node<Type>*>(branch[1])->str();
+
+            details::free_node(*node_allocator_,branch[1]);
+
+            return synthesize_sos_expression_impl<std::string&, const std::string>(opr, s0, s1);
+         }
+
+         inline expression_node_ptr synthesize_csos_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            std::string  s0 = static_cast<details::string_literal_node<Type>*>(branch[0])->str();
+            std::string& s1 = static_cast<     details::stringvar_node<Type>*>(branch[1])->ref();
+
+            details::free_node(*node_allocator_,branch[0]);
+
+            return synthesize_sos_expression_impl<const std::string,std::string&>(opr, s0, s1);
+         }
+
+         inline expression_node_ptr synthesize_csosr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            std::string   s0 = static_cast<details::string_literal_node<Type>*>(branch[0])->str  ();
+            std::string&  s1 = static_cast<details::string_range_node<Type>*>  (branch[1])->ref  ();
+            range_t      rp1 = static_cast<details::string_range_node<Type>*>  (branch[1])->range();
+
+            static_cast<details::string_range_node<Type>*>(branch[1])->range_ref().clear();
+
+            details::free_node(*node_allocator_,branch[0]);
+            details::free_node(*node_allocator_,branch[1]);
+
+            return synthesize_str_xoxr_expression_impl<const std::string,std::string&>(opr, s0, s1, rp1);
+         }
+
+         inline expression_node_ptr synthesize_srocs_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            std::string&  s0 = static_cast<details::string_range_node<Type>*>  (branch[0])->ref  ();
+            std::string   s1 = static_cast<details::string_literal_node<Type>*>(branch[1])->str  ();
+            range_t      rp0 = static_cast<details::string_range_node<Type>*>  (branch[0])->range();
+
+            static_cast<details::string_range_node<Type>*>(branch[0])->range_ref().clear();
+
+            details::free_node(*node_allocator_,branch[0]);
+            details::free_node(*node_allocator_,branch[1]);
+
+            return synthesize_str_xrox_expression_impl<std::string&, const std::string>(opr, s0, s1, rp0);
+         }
+
+         inline expression_node_ptr synthesize_srocsr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            std::string&  s0 = static_cast<details::string_range_node<Type>*>      (branch[0])->ref  ();
+            std::string   s1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->str  ();
+            range_t      rp0 = static_cast<details::string_range_node<Type>*>      (branch[0])->range();
+            range_t      rp1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->range();
+
+            static_cast<details::string_range_node<Type>*>      (branch[0])->range_ref().clear();
+            static_cast<details::const_string_range_node<Type>*>(branch[1])->range_ref().clear();
+
+            details::free_node(*node_allocator_,branch[0]);
+            details::free_node(*node_allocator_,branch[1]);
+
+            return synthesize_str_xroxr_expression_impl<std::string&, const std::string>(opr, s0, s1, rp0, rp1);
+         }
+
+         inline expression_node_ptr synthesize_csocs_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            const std::string s0 = static_cast<details::string_literal_node<Type>*>(branch[0])->str();
+            const std::string s1 = static_cast<details::string_literal_node<Type>*>(branch[1])->str();
+
+            expression_node_ptr result = error_node();
+
+            if (details::e_add == opr)
+               result = node_allocator_->allocate_c<details::string_literal_node<Type> >(s0 + s1);
+            else if (details::e_in == opr)
+               result = node_allocator_->allocate_c<details::literal_node<Type> >(details::in_op   <Type>::process(s0,s1));
+            else if (details::e_like == opr)
+               result = node_allocator_->allocate_c<details::literal_node<Type> >(details::like_op <Type>::process(s0,s1));
+            else if (details::e_ilike == opr)
+               result = node_allocator_->allocate_c<details::literal_node<Type> >(details::ilike_op<Type>::process(s0,s1));
+            else
+            {
+               expression_node_ptr temp = synthesize_sos_expression_impl<const std::string, const std::string>(opr, s0, s1);
+
+               const Type v = temp->value();
+
+               details::free_node(*node_allocator_,temp);
+
+               result = node_allocator_->allocate<literal_node_t>(v);
+            }
+
+            details::free_all_nodes(*node_allocator_,branch);
+
+            return result;
+         }
+
+         inline expression_node_ptr synthesize_csocsr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            const std::string s0 = static_cast<details::string_literal_node<Type>*>    (branch[0])->str  ();
+                  std::string s1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->str  ();
+            range_t          rp1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->range();
+
+            static_cast<details::const_string_range_node<Type>*>(branch[1])->range_ref().clear();
+
+            free_node(*node_allocator_,branch[0]);
+            free_node(*node_allocator_,branch[1]);
+
+            return synthesize_str_xoxr_expression_impl<const std::string, const std::string>(opr, s0, s1, rp1);
+         }
+
+         inline expression_node_ptr synthesize_csros_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            std::string   s0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->str  ();
+            std::string&  s1 = static_cast<details::stringvar_node<Type>*>         (branch[1])->ref  ();
+            range_t      rp0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->range();
+
+            static_cast<details::const_string_range_node<Type>*>(branch[0])->range_ref().clear();
+
+            free_node(*node_allocator_,branch[0]);
+
+            return synthesize_str_xrox_expression_impl<const std::string,std::string&>(opr, s0, s1, rp0);
+         }
+
+         inline expression_node_ptr synthesize_csrosr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            const std::string  s0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->str  ();
+                  std::string& s1 = static_cast<details::string_range_node<Type>*>      (branch[1])->ref  ();
+            const range_t     rp0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->range();
+            const range_t     rp1 = static_cast<details::string_range_node<Type>*>      (branch[1])->range();
+
+            static_cast<details::const_string_range_node<Type>*>(branch[0])->range_ref().clear();
+            static_cast<details::string_range_node<Type>*>      (branch[1])->range_ref().clear();
+
+            free_node(*node_allocator_,branch[0]);
+            free_node(*node_allocator_,branch[1]);
+
+            return synthesize_str_xroxr_expression_impl<const std::string,std::string&>(opr, s0, s1, rp0, rp1);
+         }
+
+         inline expression_node_ptr synthesize_csrocs_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            const std::string s0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->str  ();
+            const std::string s1 = static_cast<details::string_literal_node<Type>*>    (branch[1])->str  ();
+            const range_t    rp0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->range();
+
+            static_cast<details::const_string_range_node<Type>*>(branch[0])->range_ref().clear();
+
+            details::free_all_nodes(*node_allocator_,branch);
+
+            return synthesize_str_xrox_expression_impl<const std::string,std::string>(opr, s0, s1, rp0);
+         }
+
+         inline expression_node_ptr synthesize_csrocsr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            const std::string s0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->str  ();
+            const std::string s1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->str  ();
+            const range_t    rp0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->range();
+            const range_t    rp1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->range();
+
+            static_cast<details::const_string_range_node<Type>*>(branch[0])->range_ref().clear();
+            static_cast<details::const_string_range_node<Type>*>(branch[1])->range_ref().clear();
+
+            details::free_all_nodes(*node_allocator_,branch);
+
+            return synthesize_str_xroxr_expression_impl<const std::string, const std::string>(opr, s0, s1, rp0, rp1);
+         }
+
+         inline expression_node_ptr synthesize_strogen_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            switch (opr)
+            {
+               #define case_stmt(op0,op1)                                                       \
+               case op0 : return node_allocator_->                                              \
+                             allocate_ttt<typename details::str_sogens_node<Type,op1<Type> > >  \
+                                (opr, branch[0], branch[1]);                                    \
+
+               string_opr_switch_statements
+               #undef case_stmt
+               default : return error_node();
+            }
+         }
+         #endif
+
+         #ifndef exprtk_disable_string_capabilities
+         inline expression_node_ptr synthesize_string_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
+         {
+            if ((0 == branch[0]) || (0 == branch[1]))
+            {
+               details::free_all_nodes(*node_allocator_,branch);
+
+               return error_node();
+            }
+
+            const bool b0_is_s   = details::is_string_node            (branch[0]);
+            const bool b0_is_cs  = details::is_const_string_node      (branch[0]);
+            const bool b0_is_sr  = details::is_string_range_node      (branch[0]);
+            const bool b0_is_csr = details::is_const_string_range_node(branch[0]);
+
+            const bool b1_is_s   = details::is_string_node            (branch[1]);
+            const bool b1_is_cs  = details::is_const_string_node      (branch[1]);
+            const bool b1_is_sr  = details::is_string_range_node      (branch[1]);
+            const bool b1_is_csr = details::is_const_string_range_node(branch[1]);
+
+            const bool b0_is_gen = details::is_string_assignment_node (branch[0]) ||
+                                   details::is_genricstring_range_node(branch[0]) ||
+                                   details::is_string_concat_node     (branch[0]) ||
+                                   details::is_string_function_node   (branch[0]) ||
+                                   details::is_string_condition_node  (branch[0]) ||
+                                   details::is_string_ccondition_node (branch[0]) ||
+                                   details::is_string_vararg_node     (branch[0]) ;
+
+            const bool b1_is_gen = details::is_string_assignment_node (branch[1]) ||
+                                   details::is_genricstring_range_node(branch[1]) ||
+                                   details::is_string_concat_node     (branch[1]) ||
+                                   details::is_string_function_node   (branch[1]) ||
+                                   details::is_string_condition_node  (branch[1]) ||
+                                   details::is_string_ccondition_node (branch[1]) ||
+                                   details::is_string_vararg_node     (branch[1]) ;
+
+            if (details::e_add == opr)
+            {
+               if (!b0_is_cs || !b1_is_cs)
+               {
+                  return synthesize_expression<string_concat_node_t,2>(opr,branch);
+               }
+            }
+
+            if (b0_is_gen || b1_is_gen)
+            {
+               return synthesize_strogen_expression(opr,branch);
+            }
+            else if (b0_is_s)
+            {
+                    if (b1_is_s  ) return synthesize_sos_expression   (opr,branch);
+               else if (b1_is_cs ) return synthesize_socs_expression  (opr,branch);
+               else if (b1_is_sr ) return synthesize_sosr_expression  (opr,branch);
+               else if (b1_is_csr) return synthesize_socsr_expression (opr,branch);
+            }
+            else if (b0_is_cs)
+            {
+                    if (b1_is_s  ) return synthesize_csos_expression  (opr,branch);
+               else if (b1_is_cs ) return synthesize_csocs_expression (opr,branch);
+               else if (b1_is_sr ) return synthesize_csosr_expression (opr,branch);
+               else if (b1_is_csr) return synthesize_csocsr_expression(opr,branch);
+            }
+            else if (b0_is_sr)
+            {
+                    if (b1_is_s  ) return synthesize_sros_expression  (opr,branch);
+               else if (b1_is_sr ) return synthesize_srosr_expression (opr,branch);
+               else if (b1_is_cs ) return synthesize_srocs_expression (opr,branch);
+               else if (b1_is_csr) return synthesize_srocsr_expression(opr,branch);
+            }
+            else if (b0_is_csr)
+            {
+                    if (b1_is_s  ) return synthesize_csros_expression  (opr,branch);
+               else if (b1_is_sr ) return synthesize_csrosr_expression (opr,branch);
+               else if (b1_is_cs ) return synthesize_csrocs_expression (opr,branch);
+               else if (b1_is_csr) return synthesize_csrocsr_expression(opr,branch);
+            }
+
+            return error_node();
+         }
+         #else
+         inline expression_node_ptr synthesize_string_expression(const details::operator_type&, expression_node_ptr (&branch)[2])
+         {
+            details::free_all_nodes(*node_allocator_,branch);
+            return error_node();
+         }
+         #endif
+
+         #ifndef exprtk_disable_string_capabilities
+         inline expression_node_ptr synthesize_string_expression(const details::operator_type& opr, expression_node_ptr (&branch)[3])
+         {
+            if (details::e_inrange != opr)
+               return error_node();
+            else if ((0 == branch[0]) || (0 == branch[1]) || (0 == branch[2]))
+            {
+               details::free_all_nodes(*node_allocator_,branch);
+
+               return error_node();
+            }
+            else if (
+                      details::is_const_string_node(branch[0]) &&
+                      details::is_const_string_node(branch[1]) &&
+                      details::is_const_string_node(branch[2])
+                    )
+            {
+               const std::string s0 = static_cast<details::string_literal_node<Type>*>(branch[0])->str();
+               const std::string s1 = static_cast<details::string_literal_node<Type>*>(branch[1])->str();
+               const std::string s2 = static_cast<details::string_literal_node<Type>*>(branch[2])->str();
+
+               const Type v = (((s0 <= s1) && (s1 <= s2)) ? Type(1) : Type(0));
+
+               details::free_all_nodes(*node_allocator_,branch);
+
+               return node_allocator_->allocate_c<details::literal_node<Type> >(v);
+            }
+            else if (
+                      details::is_string_node(branch[0]) &&
+                      details::is_string_node(branch[1]) &&
+                      details::is_string_node(branch[2])
+                    )
+            {
+               std::string& s0 = static_cast<details::stringvar_node<Type>*>(branch[0])->ref();
+               std::string& s1 = static_cast<details::stringvar_node<Type>*>(branch[1])->ref();
+               std::string& s2 = static_cast<details::stringvar_node<Type>*>(branch[2])->ref();
+
+               typedef typename details::sosos_node<Type, std::string&, std::string&, std::string&, details::inrange_op<Type> > inrange_t;
+
+               return node_allocator_->allocate_type<inrange_t, std::string&, std::string&, std::string&>(s0, s1, s2);
+            }
+            else if (
+                      details::is_const_string_node(branch[0]) &&
+                            details::is_string_node(branch[1]) &&
+                      details::is_const_string_node(branch[2])
+                    )
+            {
+               std::string  s0 = static_cast<details::string_literal_node<Type>*>(branch[0])->str();
+               std::string& s1 = static_cast<     details::stringvar_node<Type>*>(branch[1])->ref();
+               std::string  s2 = static_cast<details::string_literal_node<Type>*>(branch[2])->str();
+
+               typedef typename details::sosos_node<Type, std::string, std::string&, std::string, details::inrange_op<Type> > inrange_t;
+
+               details::free_node(*node_allocator_,branch[0]);
+               details::free_node(*node_allocator_,branch[2]);
+
+               return node_allocator_->allocate_type<inrange_t, std::string, std::string&, std::string>(s0, s1, s2);
+            }
+            else if (
+                            details::is_string_node(branch[0]) &&
+                      details::is_const_string_node(branch[1]) &&
+                            details::is_string_node(branch[2])
+                    )
+            {
+               std::string&  s0 = static_cast<     details::stringvar_node<Type>*>(branch[0])->ref();
+               std::string   s1 = static_cast<details::string_literal_node<Type>*>(branch[1])->str();
+               std::string&  s2 = static_cast<     details::stringvar_node<Type>*>(branch[2])->ref();
+
+               typedef typename details::sosos_node<Type, std::string&, std::string, std::string&, details::inrange_op<Type> > inrange_t;
+
+               details::free_node(*node_allocator_,branch[1]);
+
+               return node_allocator_->allocate_type<inrange_t, std::string&, std::string, std::string&>(s0, s1, s2);
+            }
+            else if (
+                      details::is_string_node(branch[0]) &&
+                      details::is_string_node(branch[1]) &&
+                      details::is_const_string_node(branch[2])
+                    )
+            {
+               std::string& s0 = static_cast<     details::stringvar_node<Type>*>(branch[0])->ref();
+               std::string& s1 = static_cast<     details::stringvar_node<Type>*>(branch[1])->ref();
+               std::string  s2 = static_cast<details::string_literal_node<Type>*>(branch[2])->str();
+
+               typedef typename details::sosos_node<Type, std::string&, std::string&, std::string, details::inrange_op<Type> > inrange_t;
+
+               details::free_node(*node_allocator_,branch[2]);
+
+               return node_allocator_->allocate_type<inrange_t, std::string&, std::string&, std::string>(s0, s1, s2);
+            }
+            else if (
+                      details::is_const_string_node(branch[0]) &&
+                      details::      is_string_node(branch[1]) &&
+                      details::      is_string_node(branch[2])
+                    )
+            {
+               std::string  s0 = static_cast<details::string_literal_node<Type>*>(branch[0])->str();
+               std::string& s1 = static_cast<     details::stringvar_node<Type>*>(branch[1])->ref();
+               std::string& s2 = static_cast<     details::stringvar_node<Type>*>(branch[2])->ref();
+
+               typedef typename details::sosos_node<Type, std::string, std::string&, std::string&, details::inrange_op<Type> > inrange_t;
+
+               details::free_node(*node_allocator_,branch[0]);
+
+               return node_allocator_->allocate_type<inrange_t, std::string, std::string&, std::string&>(s0, s1, s2);
+            }
+            else
+               return error_node();
+         }
+         #else
+         inline expression_node_ptr synthesize_string_expression(const details::operator_type&, expression_node_ptr (&branch)[3])
+         {
+            details::free_all_nodes(*node_allocator_,branch);
+            return error_node();
+         }
+         #endif
+
+         inline expression_node_ptr synthesize_null_expression(const details::operator_type& operation, expression_node_ptr (&branch)[2])
+         {
+            /*
+             Note: The following are the type promotion rules
+             that relate to operations that include 'null':
+             0. null ==/!=     null --> true false
+             1. null operation null --> null
+             2. x    ==/!=     null --> true/false
+             3. null ==/!=     x    --> true/false
+             4. x   operation  null --> x
+             5. null operation x    --> x
+            */
+
+            typedef typename details::null_eq_node<T> nulleq_node_t;
+
+            const bool b0_null = details::is_null_node(branch[0]);
+            const bool b1_null = details::is_null_node(branch[1]);
+
+            if (b0_null && b1_null)
+            {
+               expression_node_ptr result = error_node();
+
+               if (details::e_eq == operation)
+                  result = node_allocator_->allocate_c<literal_node_t>(T(1));
+               else if (details::e_ne == operation)
+                  result = node_allocator_->allocate_c<literal_node_t>(T(0));
+
+               if (result)
+               {
+                  details::free_node(*node_allocator_,branch[0]);
+                  details::free_node(*node_allocator_,branch[1]);
+
+                  return result;
+               }
+
+               details::free_node(*node_allocator_,branch[1]);
+
+               return branch[0];
+            }
+            else if (details::e_eq == operation)
+            {
+               expression_node_ptr result = node_allocator_->
+                                                allocate_rc<nulleq_node_t>(branch[b0_null ? 0 : 1],true);
+
+               details::free_node(*node_allocator_,branch[b0_null ? 1 : 0]);
+
+               return result;
+            }
+            else if (details::e_ne == operation)
+            {
+               expression_node_ptr result = node_allocator_->
+                                                allocate_rc<nulleq_node_t>(branch[b0_null ? 0 : 1],false);
+
+               details::free_node(*node_allocator_,branch[b0_null ? 1 : 0]);
+
+               return result;
+            }
+            else if (b0_null)
+            {
+               details::free_node(*node_allocator_,branch[0]);
+               branch[0] = branch[1];
+               branch[1] = error_node();
+            }
+            else if (b1_null)
+            {
+               details::free_node(*node_allocator_,branch[1]);
+               branch[1] = error_node();
+            }
+
+            if (
+                 (details::e_add == operation) || (details::e_sub == operation) ||
+                 (details::e_mul == operation) || (details::e_div == operation) ||
+                 (details::e_mod == operation) || (details::e_pow == operation)
+               )
+            {
+               return branch[0];
+            }
+            else if (
+                      (details::e_lt    == operation) || (details::e_lte  == operation) ||
+                      (details::e_gt    == operation) || (details::e_gte  == operation) ||
+                      (details::e_and   == operation) || (details::e_nand == operation) ||
+                      (details::e_or    == operation) || (details::e_nor  == operation) ||
+                      (details::e_xor   == operation) || (details::e_xnor == operation) ||
+                      (details::e_in    == operation) || (details::e_like == operation) ||
+                      (details::e_ilike == operation)
+                    )
+            {
+               return node_allocator_->allocate_c<literal_node_t>(T(0));
+            }
+
+            details::free_node(*node_allocator_,branch[0]);
+
+            return node_allocator_->allocate<details::null_node<Type> >();
+         }
+
+         template <typename NodeType, std::size_t N>
+         inline expression_node_ptr synthesize_expression(const details::operator_type& operation, expression_node_ptr (&branch)[N])
+         {
+            if (
+                 (details::e_in    == operation) ||
+                 (details::e_like  == operation) ||
+                 (details::e_ilike == operation)
+               )
+            {
+               free_all_nodes(*node_allocator_,branch);
+
+               return error_node();
+            }
+            else if (!details::all_nodes_valid<N>(branch))
+            {
+               free_all_nodes(*node_allocator_,branch);
+
+               return error_node();
+            }
+            else if ((details::e_default != operation))
+            {
+               // Attempt simple constant folding optimisation.
+               expression_node_ptr expression_point = node_allocator_->allocate<NodeType>(operation,branch);
+
+               if (is_constant_foldable<N>(branch))
+               {
+                  const Type v = expression_point->value();
+                  details::free_node(*node_allocator_,expression_point);
+
+                  return node_allocator_->allocate<literal_node_t>(v);
+               }
+               else
+                  return expression_point;
+            }
+            else
+               return error_node();
+         }
+
+         template <typename NodeType, std::size_t N>
+         inline expression_node_ptr synthesize_expression(F* f, expression_node_ptr (&branch)[N])
+         {
+            if (!details::all_nodes_valid<N>(branch))
+            {
+               free_all_nodes(*node_allocator_,branch);
+
+               return error_node();
+            }
+
+            typedef typename details::function_N_node<T,ifunction_t,N> function_N_node_t;
+
+            // Attempt simple constant folding optimisation.
+
+            expression_node_ptr expression_point = node_allocator_->allocate<NodeType>(f);
+            function_N_node_t* func_node_ptr = dynamic_cast<function_N_node_t*>(expression_point);
+
+            if (0 == func_node_ptr)
+            {
+               free_all_nodes(*node_allocator_,branch);
+
+               return error_node();
+            }
+            else
+               func_node_ptr->init_branches(branch);
+
+            if (is_constant_foldable<N>(branch) && !f->has_side_effects())
+            {
+               Type v = expression_point->value();
+               details::free_node(*node_allocator_,expression_point);
+
+               return node_allocator_->allocate<literal_node_t>(v);
+            }
+
+            parser_->state_.activate_side_effect("synthesize_expression(function<NT,N>)");
+
+            return expression_point;
+         }
+
+         bool                     strength_reduction_enabled_;
+         details::node_allocator* node_allocator_;
+         synthesize_map_t         synthesize_map_;
+         unary_op_map_t*          unary_op_map_;
+         binary_op_map_t*         binary_op_map_;
+         inv_binary_op_map_t*     inv_binary_op_map_;
+         sf3_map_t*               sf3_map_;
+         sf4_map_t*               sf4_map_;
+         parser_t*                parser_;
+      };
+
+      inline void set_error(const parser_error::type& error_type)
+      {
+         error_list_.push_back(error_type);
+      }
+
+      inline void remove_last_error()
+      {
+         if (!error_list_.empty())
+         {
+            error_list_.pop_back();
+         }
+      }
+
+      inline void set_synthesis_error(const std::string& synthesis_error_message)
+      {
+         if (synthesis_error_.empty())
+         {
+            synthesis_error_ = synthesis_error_message;
+         }
+      }
+
+      inline void register_local_vars(expression<T>& e)
+      {
+         for (std::size_t i = 0; i < sem_.size(); ++i)
+         {
+            scope_element& se = sem_.get_element(i);
+
+            if (
+                 (scope_element::e_variable == se.type) ||
+                 (scope_element::e_vecelem  == se.type)
+               )
+            {
+               if (se.var_node)
+               {
+                  e.register_local_var(se.var_node);
+               }
+
+               if (se.data)
+               {
+                  e.register_local_data(se.data, 1, 0);
+               }
+            }
+            else if (scope_element::e_vector == se.type)
+            {
+               if (se.vec_node)
+               {
+                  e.register_local_var(se.vec_node);
+               }
+
+               if (se.data)
+               {
+                  e.register_local_data(se.data, se.size, 1);
+               }
+            }
+            #ifndef exprtk_disable_string_capabilities
+            else if (scope_element::e_string == se.type)
+            {
+               if (se.str_node)
+               {
+                  e.register_local_var(se.str_node);
+               }
+
+               if (se.data)
+               {
+                  e.register_local_data(se.data, se.size, 2);
+               }
+            }
+            #endif
+
+            se.var_node  = 0;
+            se.vec_node  = 0;
+            #ifndef exprtk_disable_string_capabilities
+            se.str_node  = 0;
+            #endif
+            se.data      = 0;
+            se.ref_count = 0;
+            se.active    = false;
+         }
+      }
+
+      inline void register_return_results(expression<T>& e)
+      {
+         e.register_return_results(results_context_);
+         results_context_ = 0;
+      }
+
+      inline void load_unary_operations_map(unary_op_map_t& m)
+      {
+         #define register_unary_op(Op,UnaryFunctor)             \
+         m.insert(std::make_pair(Op,UnaryFunctor<T>::process)); \
+
+         register_unary_op(details::  e_abs, details::  abs_op)
+         register_unary_op(details:: e_acos, details:: acos_op)
+         register_unary_op(details::e_acosh, details::acosh_op)
+         register_unary_op(details:: e_asin, details:: asin_op)
+         register_unary_op(details::e_asinh, details::asinh_op)
+         register_unary_op(details::e_atanh, details::atanh_op)
+         register_unary_op(details:: e_ceil, details:: ceil_op)
+         register_unary_op(details::  e_cos, details::  cos_op)
+         register_unary_op(details:: e_cosh, details:: cosh_op)
+         register_unary_op(details::  e_exp, details::  exp_op)
+         register_unary_op(details::e_expm1, details::expm1_op)
+         register_unary_op(details::e_floor, details::floor_op)
+         register_unary_op(details::  e_log, details::  log_op)
+         register_unary_op(details::e_log10, details::log10_op)
+         register_unary_op(details:: e_log2, details:: log2_op)
+         register_unary_op(details::e_log1p, details::log1p_op)
+         register_unary_op(details::  e_neg, details::  neg_op)
+         register_unary_op(details::  e_pos, details::  pos_op)
+         register_unary_op(details::e_round, details::round_op)
+         register_unary_op(details::  e_sin, details::  sin_op)
+         register_unary_op(details:: e_sinc, details:: sinc_op)
+         register_unary_op(details:: e_sinh, details:: sinh_op)
+         register_unary_op(details:: e_sqrt, details:: sqrt_op)
+         register_unary_op(details::  e_tan, details::  tan_op)
+         register_unary_op(details:: e_tanh, details:: tanh_op)
+         register_unary_op(details::  e_cot, details::  cot_op)
+         register_unary_op(details::  e_sec, details::  sec_op)
+         register_unary_op(details::  e_csc, details::  csc_op)
+         register_unary_op(details::  e_r2d, details::  r2d_op)
+         register_unary_op(details::  e_d2r, details::  d2r_op)
+         register_unary_op(details::  e_d2g, details::  d2g_op)
+         register_unary_op(details::  e_g2d, details::  g2d_op)
+         register_unary_op(details:: e_notl, details:: notl_op)
+         register_unary_op(details::  e_sgn, details::  sgn_op)
+         register_unary_op(details::  e_erf, details::  erf_op)
+         register_unary_op(details:: e_erfc, details:: erfc_op)
+         register_unary_op(details:: e_ncdf, details:: ncdf_op)
+         register_unary_op(details:: e_frac, details:: frac_op)
+         register_unary_op(details::e_trunc, details::trunc_op)
+         #undef register_unary_op
+      }
+
+      inline void load_binary_operations_map(binary_op_map_t& m)
+      {
+         typedef typename binary_op_map_t::value_type value_type;
+
+         #define register_binary_op(Op,BinaryFunctor)        \
+         m.insert(value_type(Op,BinaryFunctor<T>::process)); \
+
+         register_binary_op(details:: e_add, details:: add_op)
+         register_binary_op(details:: e_sub, details:: sub_op)
+         register_binary_op(details:: e_mul, details:: mul_op)
+         register_binary_op(details:: e_div, details:: div_op)
+         register_binary_op(details:: e_mod, details:: mod_op)
+         register_binary_op(details:: e_pow, details:: pow_op)
+         register_binary_op(details::  e_lt, details::  lt_op)
+         register_binary_op(details:: e_lte, details:: lte_op)
+         register_binary_op(details::  e_gt, details::  gt_op)
+         register_binary_op(details:: e_gte, details:: gte_op)
+         register_binary_op(details::  e_eq, details::  eq_op)
+         register_binary_op(details::  e_ne, details::  ne_op)
+         register_binary_op(details:: e_and, details:: and_op)
+         register_binary_op(details::e_nand, details::nand_op)
+         register_binary_op(details::  e_or, details::  or_op)
+         register_binary_op(details:: e_nor, details:: nor_op)
+         register_binary_op(details:: e_xor, details:: xor_op)
+         register_binary_op(details::e_xnor, details::xnor_op)
+         #undef register_binary_op
+      }
+
+      inline void load_inv_binary_operations_map(inv_binary_op_map_t& m)
+      {
+         typedef typename inv_binary_op_map_t::value_type value_type;
+
+         #define register_binary_op(Op,BinaryFunctor)        \
+         m.insert(value_type(BinaryFunctor<T>::process,Op)); \
+
+         register_binary_op(details:: e_add, details:: add_op)
+         register_binary_op(details:: e_sub, details:: sub_op)
+         register_binary_op(details:: e_mul, details:: mul_op)
+         register_binary_op(details:: e_div, details:: div_op)
+         register_binary_op(details:: e_mod, details:: mod_op)
+         register_binary_op(details:: e_pow, details:: pow_op)
+         register_binary_op(details::  e_lt, details::  lt_op)
+         register_binary_op(details:: e_lte, details:: lte_op)
+         register_binary_op(details::  e_gt, details::  gt_op)
+         register_binary_op(details:: e_gte, details:: gte_op)
+         register_binary_op(details::  e_eq, details::  eq_op)
+         register_binary_op(details::  e_ne, details::  ne_op)
+         register_binary_op(details:: e_and, details:: and_op)
+         register_binary_op(details::e_nand, details::nand_op)
+         register_binary_op(details::  e_or, details::  or_op)
+         register_binary_op(details:: e_nor, details:: nor_op)
+         register_binary_op(details:: e_xor, details:: xor_op)
+         register_binary_op(details::e_xnor, details::xnor_op)
+         #undef register_binary_op
+      }
+
+      inline void load_sf3_map(sf3_map_t& sf3_map)
+      {
+         typedef std::pair<trinary_functor_t,details::operator_type> pair_t;
+
+         #define register_sf3(Op)                                                                             \
+         sf3_map[details::sf##Op##_op<T>::id()] = pair_t(details::sf##Op##_op<T>::process,details::e_sf##Op); \
+
+         register_sf3(00) register_sf3(01) register_sf3(02) register_sf3(03)
+         register_sf3(04) register_sf3(05) register_sf3(06) register_sf3(07)
+         register_sf3(08) register_sf3(09) register_sf3(10) register_sf3(11)
+         register_sf3(12) register_sf3(13) register_sf3(14) register_sf3(15)
+         register_sf3(16) register_sf3(17) register_sf3(18) register_sf3(19)
+         register_sf3(20) register_sf3(21) register_sf3(22) register_sf3(23)
+         register_sf3(24) register_sf3(25) register_sf3(26) register_sf3(27)
+         register_sf3(28) register_sf3(29) register_sf3(30)
+         #undef register_sf3
+
+         #define register_sf3_extid(Id, Op)                                        \
+         sf3_map[Id] = pair_t(details::sf##Op##_op<T>::process,details::e_sf##Op); \
+
+         register_sf3_extid("(t-t)-t",23)  // (t-t)-t --> t-(t+t)
+         #undef register_sf3_extid
+      }
+
+      inline void load_sf4_map(sf4_map_t& sf4_map)
+      {
+         typedef std::pair<quaternary_functor_t,details::operator_type> pair_t;
+
+         #define register_sf4(Op)                                                                             \
+         sf4_map[details::sf##Op##_op<T>::id()] = pair_t(details::sf##Op##_op<T>::process,details::e_sf##Op); \
+
+         register_sf4(48) register_sf4(49) register_sf4(50) register_sf4(51)
+         register_sf4(52) register_sf4(53) register_sf4(54) register_sf4(55)
+         register_sf4(56) register_sf4(57) register_sf4(58) register_sf4(59)
+         register_sf4(60) register_sf4(61) register_sf4(62) register_sf4(63)
+         register_sf4(64) register_sf4(65) register_sf4(66) register_sf4(67)
+         register_sf4(68) register_sf4(69) register_sf4(70) register_sf4(71)
+         register_sf4(72) register_sf4(73) register_sf4(74) register_sf4(75)
+         register_sf4(76) register_sf4(77) register_sf4(78) register_sf4(79)
+         register_sf4(80) register_sf4(81) register_sf4(82) register_sf4(83)
+         #undef register_sf4
+
+         #define register_sf4ext(Op)                                                                                    \
+         sf4_map[details::sfext##Op##_op<T>::id()] = pair_t(details::sfext##Op##_op<T>::process,details::e_sf4ext##Op); \
+
+         register_sf4ext(00) register_sf4ext(01) register_sf4ext(02) register_sf4ext(03)
+         register_sf4ext(04) register_sf4ext(05) register_sf4ext(06) register_sf4ext(07)
+         register_sf4ext(08) register_sf4ext(09) register_sf4ext(10) register_sf4ext(11)
+         register_sf4ext(12) register_sf4ext(13) register_sf4ext(14) register_sf4ext(15)
+         register_sf4ext(16) register_sf4ext(17) register_sf4ext(18) register_sf4ext(19)
+         register_sf4ext(20) register_sf4ext(21) register_sf4ext(22) register_sf4ext(23)
+         register_sf4ext(24) register_sf4ext(25) register_sf4ext(26) register_sf4ext(27)
+         register_sf4ext(28) register_sf4ext(29) register_sf4ext(30) register_sf4ext(31)
+         register_sf4ext(32) register_sf4ext(33) register_sf4ext(34) register_sf4ext(35)
+         register_sf4ext(36) register_sf4ext(36) register_sf4ext(38) register_sf4ext(39)
+         register_sf4ext(40) register_sf4ext(41) register_sf4ext(42) register_sf4ext(43)
+         register_sf4ext(44) register_sf4ext(45) register_sf4ext(46) register_sf4ext(47)
+         register_sf4ext(48) register_sf4ext(49) register_sf4ext(50) register_sf4ext(51)
+         register_sf4ext(52) register_sf4ext(53) register_sf4ext(54) register_sf4ext(55)
+         register_sf4ext(56) register_sf4ext(57) register_sf4ext(58) register_sf4ext(59)
+         register_sf4ext(60) register_sf4ext(61)
+         #undef register_sf4ext
+      }
+
+      inline results_context_t& results_ctx()
+      {
+         if (0 == results_context_)
+         {
+            results_context_ = new results_context_t();
+         }
+
+         return (*results_context_);
+      }
+
+      inline void return_cleanup()
+      {
+         #ifndef exprtk_disable_return_statement
+         if (results_context_)
+         {
+            delete results_context_;
+            results_context_ = 0;
+         }
+
+         state_.return_stmt_present = false;
+         #endif
+      }
+
+   private:
+
+      parser(const parser<T>&);
+      parser<T>& operator=(const parser<T>&);
+
+      settings_store settings_;
+      expression_generator<T> expression_generator_;
+      details::node_allocator node_allocator_;
+      symtab_store symtab_store_;
+      dependent_entity_collector dec_;
+      std::deque<parser_error::type> error_list_;
+      std::deque<bool> brkcnt_list_;
+      parser_state state_;
+      bool resolve_unknown_symbol_;
+      results_context_t* results_context_;
+      unknown_symbol_resolver* unknown_symbol_resolver_;
+      unknown_symbol_resolver default_usr_;
+      base_ops_map_t base_ops_map_;
+      unary_op_map_t unary_op_map_;
+      binary_op_map_t binary_op_map_;
+      inv_binary_op_map_t inv_binary_op_map_;
+      sf3_map_t sf3_map_;
+      sf4_map_t sf4_map_;
+      std::string synthesis_error_;
+      scope_element_manager sem_;
+
+      lexer::helper::helper_assembly helper_assembly_;
+
+      lexer::helper::commutative_inserter       commutative_inserter_;
+      lexer::helper::operator_joiner            operator_joiner_2_;
+      lexer::helper::operator_joiner            operator_joiner_3_;
+      lexer::helper::symbol_replacer            symbol_replacer_;
+      lexer::helper::bracket_checker            bracket_checker_;
+      lexer::helper::numeric_checker            numeric_checker_;
+      lexer::helper::sequence_validator         sequence_validator_;
+      lexer::helper::sequence_validator_3tokens sequence_validator_3tkns_;
+
+      template <typename ParserType>
+      friend void details::disable_type_checking(ParserType& p);
+   };
+
+   namespace details
+   {
+      template <typename T>
+      struct collector_helper
+      {
+         typedef exprtk::symbol_table<T> symbol_table_t;
+         typedef exprtk::expression<T>     expression_t;
+         typedef exprtk::parser<T>             parser_t;
+         typedef typename parser_t::dependent_entity_collector::symbol_t symbol_t;
+         typedef typename parser_t::unknown_symbol_resolver usr_t;
+
+         struct resolve_as_vector : public parser_t::unknown_symbol_resolver
+         {
+            typedef exprtk::parser<T> parser_t;
+
+            resolve_as_vector()
+            : usr_t(usr_t::e_usrmode_extended)
+            {}
+
+            virtual bool process(const std::string& unknown_symbol,
+                                 symbol_table_t& symbol_table,
+                                 std::string&)
+            {
+               static T v[1];
+               symbol_table.add_vector(unknown_symbol,v);
+               return true;
+            }
+         };
+
+         static inline bool collection_pass(const std::string& expression_string,
+                                            std::set<std::string>& symbol_set,
+                                            const bool collect_variables,
+                                            const bool collect_functions,
+                                            const bool vector_pass,
+                                            symbol_table_t& ext_symbol_table)
+         {
+            symbol_table_t symbol_table;
+            expression_t   expression;
+            parser_t       parser;
+
+            resolve_as_vector vect_resolver;
+
+            expression.register_symbol_table(symbol_table    );
+            expression.register_symbol_table(ext_symbol_table);
+
+            if (vector_pass)
+               parser.enable_unknown_symbol_resolver(&vect_resolver);
+            else
+               parser.enable_unknown_symbol_resolver();
+
+            if (collect_variables)
+               parser.dec().collect_variables() = true;
+
+            if (collect_functions)
+               parser.dec().collect_functions() = true;
+
+            bool pass_result = false;
+
+            details::disable_type_checking(parser);
+
+            if (parser.compile(expression_string, expression))
+            {
+               pass_result = true;
+
+               std::deque<symbol_t> symb_list;
+               parser.dec().symbols(symb_list);
+
+               for (std::size_t i = 0; i < symb_list.size(); ++i)
+               {
+                  symbol_set.insert(symb_list[i].first);
+               }
+            }
+
+            return pass_result;
+         }
+      };
+   }
+
+   template <typename Allocator,
+             template <typename, typename> class Sequence>
+   inline bool collect_variables(const std::string& expression,
+                                 Sequence<std::string, Allocator>& symbol_list)
+   {
+      typedef double T;
+      typedef details::collector_helper<T> collect_t;
+
+      collect_t::symbol_table_t null_symbol_table;
+
+      std::set<std::string> symbol_set;
+
+      const bool variable_pass = collect_t::collection_pass
+                                    (expression, symbol_set, true, false, false, null_symbol_table);
+      const bool vector_pass   = collect_t::collection_pass
+                                    (expression, symbol_set, true, false,  true, null_symbol_table);
+
+      if (!variable_pass && !vector_pass)
+         return false;
+
+      std::set<std::string>::iterator itr = symbol_set.begin();
+
+      while (symbol_set.end() != itr)
+      {
+         symbol_list.push_back(*itr);
+         ++itr;
+      }
+
+      return true;
+   }
+
+   template <typename T,
+             typename Allocator,
+             template <typename, typename> class Sequence>
+   inline bool collect_variables(const std::string& expression,
+                                 exprtk::symbol_table<T>& extrnl_symbol_table,
+                                 Sequence<std::string, Allocator>& symbol_list)
+   {
+      typedef details::collector_helper<T> collect_t;
+
+      std::set<std::string> symbol_set;
+
+      const bool variable_pass = collect_t::collection_pass
+                                    (expression, symbol_set, true, false, false, extrnl_symbol_table);
+      const bool vector_pass   = collect_t::collection_pass
+                                    (expression, symbol_set, true, false,  true, extrnl_symbol_table);
+
+      if (!variable_pass && !vector_pass)
+         return false;
+
+      std::set<std::string>::iterator itr = symbol_set.begin();
+
+      while (symbol_set.end() != itr)
+      {
+         symbol_list.push_back(*itr);
+         ++itr;
+      }
+
+      return true;
+   }
+
+   template <typename Allocator,
+             template <typename, typename> class Sequence>
+   inline bool collect_functions(const std::string& expression,
+                                 Sequence<std::string, Allocator>& symbol_list)
+   {
+      typedef double T;
+      typedef details::collector_helper<T> collect_t;
+
+      collect_t::symbol_table_t null_symbol_table;
+
+      std::set<std::string> symbol_set;
+
+      const bool variable_pass = collect_t::collection_pass
+                                    (expression, symbol_set, false, true, false, null_symbol_table);
+      const bool vector_pass   = collect_t::collection_pass
+                                    (expression, symbol_set, false, true,  true, null_symbol_table);
+
+      if (!variable_pass && !vector_pass)
+         return false;
+
+      std::set<std::string>::iterator itr = symbol_set.begin();
+
+      while (symbol_set.end() != itr)
+      {
+         symbol_list.push_back(*itr);
+         ++itr;
+      }
+
+      return true;
+   }
+
+   template <typename T,
+             typename Allocator,
+             template <typename, typename> class Sequence>
+   inline bool collect_functions(const std::string& expression,
+                                 exprtk::symbol_table<T>& extrnl_symbol_table,
+                                 Sequence<std::string, Allocator>& symbol_list)
+   {
+      typedef details::collector_helper<T> collect_t;
+
+      std::set<std::string> symbol_set;
+
+      const bool variable_pass = collect_t::collection_pass
+                                    (expression, symbol_set, false, true, false, extrnl_symbol_table);
+      const bool vector_pass   = collect_t::collection_pass
+                                    (expression, symbol_set, false, true,  true, extrnl_symbol_table);
+
+      if (!variable_pass && !vector_pass)
+         return false;
+
+      std::set<std::string>::iterator itr = symbol_set.begin();
+
+      while (symbol_set.end() != itr)
+      {
+         symbol_list.push_back(*itr);
+         ++itr;
+      }
+
+      return true;
+   }
+
+   template <typename T>
+   inline T integrate(const expression<T>& e,
+                      T& x,
+                      const T& r0, const T& r1,
+                      const std::size_t number_of_intervals = 1000000)
+   {
+      if (r0 > r1)
+         return T(0);
+
+      const T h = (r1 - r0) / (T(2) * number_of_intervals);
+      T total_area = T(0);
+
+      for (std::size_t i = 0; i < number_of_intervals; ++i)
+      {
+         x = r0 + T(2) * i * h;
+         const T y0 = e.value(); x += h;
+         const T y1 = e.value(); x += h;
+         const T y2 = e.value(); x += h;
+         total_area += h * (y0 + T(4) * y1 + y2) / T(3);
+      }
+
+      return total_area;
+   }
+
+   template <typename T>
+   inline T integrate(const expression<T>& e,
+                      const std::string& variable_name,
+                      const T& r0, const T& r1,
+                      const std::size_t number_of_intervals = 1000000)
+   {
+      const symbol_table<T>& sym_table = e.get_symbol_table();
+
+      if (!sym_table.valid())
+         return std::numeric_limits<T>::quiet_NaN();
+
+      details::variable_node<T>* var = sym_table.get_variable(variable_name);
+
+      if (var)
+      {
+         T& x = var->ref();
+         T  x_original = x;
+         T result = integrate(e, x, r0, r1, number_of_intervals);
+         x = x_original;
+
+         return result;
+      }
+      else
+         return std::numeric_limits<T>::quiet_NaN();
+   }
+
+   template <typename T>
+   inline T derivative(const expression<T>& e,
+                       T& x,
+                       const T& h = T(0.00000001))
+   {
+      const T x_init = x;
+      const T _2h    = T(2) * h;
+
+      x = x_init + _2h;
+      const T y0 = e.value();
+      x = x_init +   h;
+      const T y1 = e.value();
+      x = x_init -   h;
+      const T y2 = e.value();
+      x = x_init - _2h;
+      const T y3 = e.value();
+      x = x_init;
+
+      return (-y0 + T(8) * (y1 - y2) + y3) / (T(12) * h);
+   }
+
+   template <typename T>
+   inline T second_derivative(const expression<T>& e,
+                              T& x,
+                              const T& h = T(0.00001))
+   {
+      const T x_init = x;
+      const T _2h    = T(2) * h;
+
+      const T y = e.value();
+      x = x_init + _2h;
+      const T y0 = e.value();
+      x = x_init +   h;
+      const T y1 = e.value();
+      x = x_init -   h;
+      const T y2 = e.value();
+      x = x_init - _2h;
+      const T y3 = e.value();
+      x = x_init;
+
+      return (-y0 + T(16) * (y1 + y2) - T(30) * y - y3) / (T(12) * h * h);
+   }
+
+   template <typename T>
+   inline T third_derivative(const expression<T>& e,
+                             T& x,
+                             const T& h = T(0.0001))
+   {
+      const T x_init = x;
+      const T _2h    = T(2) * h;
+
+      x = x_init + _2h;
+      const T y0 = e.value();
+      x = x_init +   h;
+      const T y1 = e.value();
+      x = x_init -   h;
+      const T y2 = e.value();
+      x = x_init - _2h;
+      const T y3 = e.value();
+      x = x_init;
+
+      return (y0 + T(2) * (y2 - y1) - y3) / (T(2) * h * h * h);
+   }
+
+   template <typename T>
+   inline T derivative(const expression<T>& e,
+                       const std::string& variable_name,
+                       const T& h = T(0.00000001))
+   {
+      const symbol_table<T>& sym_table = e.get_symbol_table();
+
+      if (!sym_table.valid())
+      {
+         return std::numeric_limits<T>::quiet_NaN();
+      }
+
+      details::variable_node<T>* var = sym_table.get_variable(variable_name);
+
+      if (var)
+      {
+         T& x = var->ref();
+         T x_original = x;
+         T result = derivative(e, x, h);
+         x = x_original;
+
+         return result;
+      }
+      else
+         return std::numeric_limits<T>::quiet_NaN();
+   }
+
+   template <typename T>
+   inline T second_derivative(const expression<T>& e,
+                              const std::string& variable_name,
+                              const T& h = T(0.00001))
+   {
+      const symbol_table<T>& sym_table = e.get_symbol_table();
+
+      if (!sym_table.valid())
+      {
+         return std::numeric_limits<T>::quiet_NaN();
+      }
+
+      details::variable_node<T>* var = sym_table.get_variable(variable_name);
+
+      if (var)
+      {
+         T& x = var->ref();
+         const T x_original = x;
+         const T result = second_derivative(e, x, h);
+         x = x_original;
+
+         return result;
+      }
+      else
+         return std::numeric_limits<T>::quiet_NaN();
+   }
+
+   template <typename T>
+   inline T third_derivative(const expression<T>& e,
+                             const std::string& variable_name,
+                             const T& h = T(0.0001))
+   {
+      const symbol_table<T>& sym_table = e.get_symbol_table();
+
+      if (!sym_table.valid())
+      {
+         return std::numeric_limits<T>::quiet_NaN();
+      }
+
+      details::variable_node<T>* var = sym_table.get_variable(variable_name);
+
+      if (var)
+      {
+         T& x = var->ref();
+         const T x_original = x;
+         const T result = third_derivative(e, x, h);
+         x = x_original;
+
+         return result;
+      }
+      else
+         return std::numeric_limits<T>::quiet_NaN();
+   }
+
+   /*
+      Note: The following 'compute' routines are simple helpers,
+      for quickly setting up the required pieces of code in order
+      to evaluate an expression. By virtue of how they operate
+      there will be an overhead with regards to their setup and
+      teardown and hence should not be used in time critical
+      sections of code.
+      Furthermore they only assume a small sub set of variables,
+      no string variables or user defined functions.
+   */
+   template <typename T>
+   inline bool compute(const std::string& expression_string, T& result)
+   {
+      // No variables
+      symbol_table<T> symbol_table;
+      symbol_table.add_constants();
+
+      expression<T> expression;
+      expression.register_symbol_table(symbol_table);
+
+      parser<T> parser;
+
+      if (parser.compile(expression_string,expression))
+      {
+         result = expression.value();
+
+         return true;
+      }
+      else
+         return false;
+   }
+
+   template <typename T>
+   inline bool compute(const std::string& expression_string,
+                       const T& x,
+                       T& result)
+   {
+      // Only 'x'
+      static const std::string x_var("x");
+
+      symbol_table<T> symbol_table;
+      symbol_table.add_constants();
+      symbol_table.add_constant(x_var,x);
+
+      expression<T> expression;
+      expression.register_symbol_table(symbol_table);
+
+      parser<T> parser;
+
+      if (parser.compile(expression_string,expression))
+      {
+         result = expression.value();
+
+         return true;
+      }
+      else
+         return false;
+   }
+
+   template <typename T>
+   inline bool compute(const std::string& expression_string,
+                       const T&x, const T& y,
+                       T& result)
+   {
+      // Only 'x' and 'y'
+      static const std::string x_var("x");
+      static const std::string y_var("y");
+
+      symbol_table<T> symbol_table;
+      symbol_table.add_constants();
+      symbol_table.add_constant(x_var,x);
+      symbol_table.add_constant(y_var,y);
+
+      expression<T> expression;
+      expression.register_symbol_table(symbol_table);
+
+      parser<T> parser;
+
+      if (parser.compile(expression_string,expression))
+      {
+         result = expression.value();
+
+         return true;
+      }
+      else
+         return false;
+   }
+
+   template <typename T>
+   inline bool compute(const std::string& expression_string,
+                       const T& x, const T& y, const T& z,
+                       T& result)
+   {
+      // Only 'x', 'y' or 'z'
+      static const std::string x_var("x");
+      static const std::string y_var("y");
+      static const std::string z_var("z");
+
+      symbol_table<T> symbol_table;
+      symbol_table.add_constants();
+      symbol_table.add_constant(x_var,x);
+      symbol_table.add_constant(y_var,y);
+      symbol_table.add_constant(z_var,z);
+
+      expression<T> expression;
+      expression.register_symbol_table(symbol_table);
+
+      parser<T> parser;
+
+      if (parser.compile(expression_string,expression))
+      {
+         result = expression.value();
+
+         return true;
+      }
+      else
+         return false;
+   }
+
+   template <typename T, std::size_t N>
+   class polynomial : public ifunction<T>
+   {
+   private:
+
+      template <typename Type, std::size_t NumberOfCoefficients>
+      struct poly_impl { };
+
+      template <typename Type>
+      struct poly_impl <Type,12>
+      {
+         static inline T evaluate(const Type x,
+                                  const Type c12, const Type c11, const Type c10, const Type c9, const Type c8,
+                                  const Type  c7, const Type  c6, const Type  c5, const Type c4, const Type c3,
+                                  const Type  c2, const Type  c1, const Type  c0)
+         {
+            // p(x) = c_12x^12 + c_11x^11 + c_10x^10 + c_9x^9 + c_8x^8 + c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0
+            return ((((((((((((c12 * x + c11) * x + c10) * x + c9) * x + c8) * x + c7) * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0);
+         }
+      };
+
+      template <typename Type>
+      struct poly_impl <Type,11>
+      {
+         static inline T evaluate(const Type x,
+                                  const Type c11, const Type c10, const Type c9, const Type c8, const Type c7,
+                                  const Type c6,  const Type  c5, const Type c4, const Type c3, const Type c2,
+                                  const Type c1,  const Type  c0)
+         {
+            // p(x) = c_11x^11 + c_10x^10 + c_9x^9 + c_8x^8 + c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0
+            return (((((((((((c11 * x + c10) * x + c9) * x + c8) * x + c7) * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0);
+         }
+      };
+
+      template <typename Type>
+      struct poly_impl <Type,10>
+      {
+         static inline T evaluate(const Type x,
+                                  const Type c10, const Type c9, const Type c8, const Type c7, const Type c6,
+                                  const Type c5,  const Type c4, const Type c3, const Type c2, const Type c1,
+                                  const Type c0)
+         {
+            // p(x) = c_10x^10 + c_9x^9 + c_8x^8 + c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0
+            return ((((((((((c10 * x + c9) * x + c8) * x + c7) * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0);
+         }
+      };
+
+      template <typename Type>
+      struct poly_impl <Type,9>
+      {
+         static inline T evaluate(const Type x,
+                                  const Type c9, const Type c8, const Type c7, const Type c6, const Type c5,
+                                  const Type c4, const Type c3, const Type c2, const Type c1, const Type c0)
+         {
+            // p(x) = c_9x^9 + c_8x^8 + c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0
+            return (((((((((c9 * x + c8) * x + c7) * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0);
+         }
+      };
+
+      template <typename Type>
+      struct poly_impl <Type,8>
+      {
+         static inline T evaluate(const Type x,
+                                  const Type c8, const Type c7, const Type c6, const Type c5, const Type c4,
+                                  const Type c3, const Type c2, const Type c1, const Type c0)
+         {
+            // p(x) = c_8x^8 + c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0
+            return ((((((((c8 * x + c7) * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0);
+         }
+      };
+
+      template <typename Type>
+      struct poly_impl <Type,7>
+      {
+         static inline T evaluate(const Type x,
+                                  const Type c7, const Type c6, const Type c5, const Type c4, const Type c3,
+                                  const Type c2, const Type c1, const Type c0)
+         {
+            // p(x) = c_7x^7 + c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0
+            return (((((((c7 * x + c6) * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0);
+         }
+      };
+
+      template <typename Type>
+      struct poly_impl <Type,6>
+      {
+         static inline T evaluate(const Type x,
+                                  const Type c6, const Type c5, const Type c4, const Type c3, const Type c2,
+                                  const Type c1, const Type c0)
+         {
+            // p(x) = c_6x^6 + c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0
+            return ((((((c6 * x + c5) * x + c4) * x + c3) * x + c2) * x + c1) * x + c0);
+         }
+      };
+
+      template <typename Type>
+      struct poly_impl <Type,5>
+      {
+         static inline T evaluate(const Type x,
+                                  const Type c5, const Type c4, const Type c3, const Type c2,
+                                  const Type c1, const Type c0)
+         {
+            // p(x) = c_5x^5 + c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0
+            return (((((c5 * x + c4) * x + c3) * x + c2) * x + c1) * x + c0);
+         }
+      };
+
+      template <typename Type>
+      struct poly_impl <Type,4>
+      {
+         static inline T evaluate(const Type x, const Type c4, const Type c3, const Type c2, const Type c1, const Type c0)
+         {
+            // p(x) = c_4x^4 + c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0
+            return ((((c4 * x + c3) * x + c2) * x + c1) * x + c0);
+         }
+      };
+
+      template <typename Type>
+      struct poly_impl <Type,3>
+      {
+         static inline T evaluate(const Type x, const Type c3, const Type c2, const Type c1, const Type c0)
+         {
+            // p(x) = c_3x^3 + c_2x^2 + c_1x^1 + c_0x^0
+            return (((c3 * x + c2) * x + c1) * x + c0);
+         }
+      };
+
+      template <typename Type>
+      struct poly_impl <Type,2>
+      {
+         static inline T evaluate(const Type x, const Type c2, const Type c1, const Type c0)
+         {
+            // p(x) = c_2x^2 + c_1x^1 + c_0x^0
+            return ((c2 * x + c1) * x + c0);
+         }
+      };
+
+      template <typename Type>
+      struct poly_impl <Type,1>
+      {
+         static inline T evaluate(const Type x, const Type c1, const Type c0)
+         {
+            // p(x) = c_1x^1 + c_0x^0
+            return (c1 * x + c0);
+         }
+      };
+
+   public:
+
+      using ifunction<T>::operator();
+
+      polynomial()
+      : ifunction<T>((N+2 <= 20) ? (N + 2) : std::numeric_limits<std::size_t>::max())
+      {
+         disable_has_side_effects(*this);
+      }
+
+      virtual ~polynomial()
+      {}
+
+      #define poly_rtrn(NN) \
+      return (NN != N) ? std::numeric_limits<T>::quiet_NaN() :
+
+      inline virtual T operator() (const T& x, const T& c1, const T& c0)
+      {
+         poly_rtrn(1) poly_impl<T,1>::evaluate(x, c1, c0);
+      }
+
+      inline virtual T operator() (const T& x, const T& c2, const T& c1, const T& c0)
+      {
+         poly_rtrn(2) poly_impl<T,2>::evaluate(x, c2, c1, c0);
+      }
+
+      inline virtual T operator() (const T& x, const T& c3, const T& c2, const T& c1, const T& c0)
+      {
+         poly_rtrn(3) poly_impl<T,3>::evaluate(x, c3, c2, c1, c0);
+      }
+
+      inline virtual T operator() (const T& x, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      {
+         poly_rtrn(4) poly_impl<T,4>::evaluate(x, c4, c3, c2, c1, c0);
+      }
+
+      inline virtual T operator() (const T& x, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      {
+         poly_rtrn(5) poly_impl<T,5>::evaluate(x, c5, c4, c3, c2, c1, c0);
+      }
+
+      inline virtual T operator() (const T& x, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      {
+         poly_rtrn(6) poly_impl<T,6>::evaluate(x, c6, c5, c4, c3, c2, c1, c0);
+      }
+
+      inline virtual T operator() (const T& x, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      {
+         poly_rtrn(7) poly_impl<T,7>::evaluate(x, c7, c6, c5, c4, c3, c2, c1, c0);
+      }
+
+      inline virtual T operator() (const T& x, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      {
+         poly_rtrn(8) poly_impl<T,8>::evaluate(x, c8, c7, c6, c5, c4, c3, c2, c1, c0);
+      }
+
+      inline virtual T operator() (const T& x, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      {
+         poly_rtrn(9) poly_impl<T,9>::evaluate(x, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0);
+      }
+
+      inline virtual T operator() (const T& x, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      {
+         poly_rtrn(10) poly_impl<T,10>::evaluate(x, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0);
+      }
+
+      inline virtual T operator() (const T& x, const T& c11, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      {
+         poly_rtrn(11) poly_impl<T,11>::evaluate(x, c11, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0);
+      }
+
+      inline virtual T operator() (const T& x, const T& c12, const T& c11, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
+      {
+         poly_rtrn(12) poly_impl<T,12>::evaluate(x, c12, c11, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0);
+      }
+
+      #undef poly_rtrn
+
+      inline virtual T operator() ()
+      {
+         return std::numeric_limits<T>::quiet_NaN();
+      }
+
+      inline virtual T operator() (const T&)
+      {
+         return std::numeric_limits<T>::quiet_NaN();
+      }
+
+      inline virtual T operator() (const T&, const T&)
+      {
+         return std::numeric_limits<T>::quiet_NaN();
+      }
+   };
+
+   template <typename T>
+   class function_compositor
+   {
+   public:
+
+      typedef exprtk::expression<T>             expression_t;
+      typedef exprtk::symbol_table<T>           symbol_table_t;
+      typedef exprtk::parser<T>                 parser_t;
+      typedef typename parser_t::settings_store settings_t;
+
+      struct function
+      {
+         function()
+         {}
+
+         function(const std::string& n)
+         : name_(n)
+         {}
+
+         function(const std::string& name,
+                  const std::string& expression)
+         : name_(name),
+           expression_(expression)
+         {}
+
+         function(const std::string& name,
+                  const std::string& expression,
+                  const std::string& v0)
+         : name_(name),
+           expression_(expression)
+         {
+            v_.push_back(v0);
+         }
+
+         function(const std::string& name,
+                  const std::string& expression,
+                  const std::string& v0, const std::string& v1)
+         : name_(name),
+           expression_(expression)
+         {
+            v_.push_back(v0); v_.push_back(v1);
+         }
+
+         function(const std::string& name,
+                  const std::string& expression,
+                  const std::string& v0, const std::string& v1,
+                  const std::string& v2)
+         : name_(name),
+           expression_(expression)
+         {
+            v_.push_back(v0); v_.push_back(v1);
+            v_.push_back(v2);
+         }
+
+         function(const std::string& name,
+                  const std::string& expression,
+                  const std::string& v0, const std::string& v1,
+                  const std::string& v2, const std::string& v3)
+         : name_(name),
+           expression_(expression)
+         {
+            v_.push_back(v0); v_.push_back(v1);
+            v_.push_back(v2); v_.push_back(v3);
+         }
+
+         function(const std::string& name,
+                  const std::string& expression,
+                  const std::string& v0, const std::string& v1,
+                  const std::string& v2, const std::string& v3,
+                  const std::string& v4)
+         : name_(name),
+           expression_(expression)
+         {
+            v_.push_back(v0); v_.push_back(v1);
+            v_.push_back(v2); v_.push_back(v3);
+            v_.push_back(v4);
+         }
+
+         inline function& name(const std::string& n)
+         {
+            name_ = n;
+            return (*this);
+         }
+
+         inline function& expression(const std::string& e)
+         {
+            expression_ = e;
+            return (*this);
+         }
+
+         inline function& var(const std::string& v)
+         {
+            v_.push_back(v);
+            return (*this);
+         }
+
+         std::string name_;
+         std::string expression_;
+         std::deque<std::string> v_;
+      };
+
+   private:
+
+      struct base_func : public exprtk::ifunction<T>
+      {
+         typedef const T&                       type;
+         typedef exprtk::ifunction<T>     function_t;
+         typedef std::vector<T*>            varref_t;
+         typedef std::vector<T>                var_t;
+         typedef std::pair<T*,std::size_t> lvarref_t;
+         typedef std::vector<lvarref_t>    lvr_vec_t;
+
+         using exprtk::ifunction<T>::operator();
+
+         base_func(const std::size_t& pc = 0)
+         : exprtk::ifunction<T>(pc),
+           local_var_stack_size(0),
+           stack_depth(0)
+         {
+            v.resize(pc);
+         }
+
+         virtual ~base_func()
+         {}
+
+         inline void update(const T& v0)
+         {
+            (*v[0]) = v0;
+         }
+
+         inline void update(const T& v0, const T& v1)
+         {
+            (*v[0]) = v0; (*v[1]) = v1;
+         }
+
+         inline void update(const T& v0, const T& v1, const T& v2)
+         {
+            (*v[0]) = v0; (*v[1]) = v1;
+            (*v[2]) = v2;
+         }
+
+         inline void update(const T& v0, const T& v1, const T& v2, const T& v3)
+         {
+            (*v[0]) = v0; (*v[1]) = v1;
+            (*v[2]) = v2; (*v[3]) = v3;
+         }
+
+         inline void update(const T& v0, const T& v1, const T& v2, const T& v3, const T& v4)
+         {
+            (*v[0]) = v0; (*v[1]) = v1;
+            (*v[2]) = v2; (*v[3]) = v3;
+            (*v[4]) = v4;
+         }
+
+         inline void update(const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, const T& v5)
+         {
+            (*v[0]) = v0; (*v[1]) = v1;
+            (*v[2]) = v2; (*v[3]) = v3;
+            (*v[4]) = v4; (*v[5]) = v5;
+         }
+
+         inline function_t& setup(expression_t& expr)
+         {
+            expression = expr;
+
+            typedef typename expression_t::control_block::local_data_list_t ldl_t;
+
+            ldl_t ldl = expr.local_data_list();
+
+            std::vector<std::size_t> index_list;
+
+            for (std::size_t i = 0; i < ldl.size(); ++i)
+            {
+               if (ldl[i].size)
+               {
+                  index_list.push_back(i);
+               }
+            }
+
+            std::size_t input_param_count = 0;
+
+            for (std::size_t i = 0; i < index_list.size(); ++i)
+            {
+               const std::size_t index = index_list[i];
+
+               if (i < (index_list.size() - v.size()))
+               {
+                  lv.push_back(
+                        std::make_pair(
+                           reinterpret_cast<T*>(ldl[index].pointer),
+                           ldl[index].size));
+
+                  local_var_stack_size += ldl[index].size;
+               }
+               else
+                  v[input_param_count++] = reinterpret_cast<T*>(ldl[index].pointer);
+            }
+
+            clear_stack();
+
+            return (*this);
+         }
+
+         inline void pre()
+         {
+            if (stack_depth++)
+            {
+               if (!v.empty())
+               {
+                  var_t var_stack(v.size(),T(0));
+                  copy(v,var_stack);
+                  param_stack.push_back(var_stack);
+               }
+
+               if (!lv.empty())
+               {
+                  var_t local_var_stack(local_var_stack_size,T(0));
+                  copy(lv,local_var_stack);
+                  local_stack.push_back(local_var_stack);
+               }
+            }
+         }
+
+         inline void post()
+         {
+            if (--stack_depth)
+            {
+               if (!v.empty())
+               {
+                  copy(param_stack.back(),v);
+                  param_stack.pop_back();
+               }
+
+               if (!lv.empty())
+               {
+                  copy(local_stack.back(),lv);
+                  local_stack.pop_back();
+               }
+            }
+         }
+
+         void copy(const varref_t& src_v, var_t& dest_v)
+         {
+            for (std::size_t i = 0; i < src_v.size(); ++i)
+            {
+               dest_v[i] = (*src_v[i]);
+            }
+         }
+
+         void copy(const var_t& src_v, varref_t& dest_v)
+         {
+            for (std::size_t i = 0; i < src_v.size(); ++i)
+            {
+               (*dest_v[i]) = src_v[i];
+            }
+         }
+
+         void copy(const lvr_vec_t& src_v, var_t& dest_v)
+         {
+            typename var_t::iterator itr = dest_v.begin();
+            typedef  typename std::iterator_traits<typename var_t::iterator>::difference_type diff_t;
+
+            for (std::size_t i = 0; i < src_v.size(); ++i)
+            {
+               lvarref_t vr = src_v[i];
+
+               if (1 == vr.second)
+                  *itr++ = (*vr.first);
+               else
+               {
+                  std::copy(vr.first, vr.first + vr.second, itr);
+                  itr += static_cast<diff_t>(vr.second);
+               }
+            }
+         }
+
+         void copy(const var_t& src_v, lvr_vec_t& dest_v)
+         {
+            typename var_t::const_iterator itr = src_v.begin();
+            typedef  typename std::iterator_traits<typename var_t::iterator>::difference_type diff_t;
+
+            for (std::size_t i = 0; i < src_v.size(); ++i)
+            {
+               lvarref_t vr = dest_v[i];
+
+               if (1 == vr.second)
+                  (*vr.first) = *itr++;
+               else
+               {
+                  std::copy(itr, itr + static_cast<diff_t>(vr.second), vr.first);
+                  itr += static_cast<diff_t>(vr.second);
+               }
+            }
+         }
+
+         inline void clear_stack()
+         {
+            for (std::size_t i = 0; i < v.size(); ++i)
+            {
+               (*v[i]) = 0;
+            }
+         }
+
+         inline virtual T value(expression_t& e)
+         {
+            return e.value();
+         }
+
+         expression_t expression;
+         varref_t v;
+         lvr_vec_t lv;
+         std::size_t local_var_stack_size;
+         std::size_t stack_depth;
+         std::deque<var_t> param_stack;
+         std::deque<var_t> local_stack;
+      };
+
+      typedef std::map<std::string,base_func*> funcparam_t;
+
+      struct func_0param : public base_func
+      {
+         using exprtk::ifunction<T>::operator();
+
+         func_0param() : base_func(0) {}
+
+         inline T operator() ()
+         {
+            return this->value(base_func::expression);
+         }
+      };
+
+      typedef const T& type;
+
+      template <typename BaseFuncType>
+      struct scoped_bft
+      {
+         scoped_bft(BaseFuncType& bft) : bft_(bft) { bft_.pre (); }
+        ~scoped_bft()                              { bft_.post(); }
+
+         BaseFuncType& bft_;
+
+      private:
+
+         scoped_bft(scoped_bft&);
+         scoped_bft& operator=(scoped_bft&);
+      };
+
+      struct func_1param : public base_func
+      {
+         using exprtk::ifunction<T>::operator();
+
+         func_1param() : base_func(1) {}
+
+         inline T operator() (type v0)
+         {
+            scoped_bft<func_1param> sb(*this);
+            base_func::update(v0);
+            return this->value(base_func::expression);
+         }
+      };
+
+      struct func_2param : public base_func
+      {
+         using exprtk::ifunction<T>::operator();
+
+         func_2param() : base_func(2) {}
+
+         inline T operator() (type v0, type v1)
+         {
+            scoped_bft<func_2param> sb(*this);
+            base_func::update(v0, v1);
+            return this->value(base_func::expression);
+         }
+      };
+
+      struct func_3param : public base_func
+      {
+         using exprtk::ifunction<T>::operator();
+
+         func_3param() : base_func(3) {}
+
+         inline T operator() (type v0, type v1, type v2)
+         {
+            scoped_bft<func_3param> sb(*this);
+            base_func::update(v0, v1, v2);
+            return this->value(base_func::expression);
+         }
+      };
+
+      struct func_4param : public base_func
+      {
+         using exprtk::ifunction<T>::operator();
+
+         func_4param() : base_func(4) {}
+
+         inline T operator() (type v0, type v1, type v2, type v3)
+         {
+            scoped_bft<func_4param> sb(*this);
+            base_func::update(v0, v1, v2, v3);
+            return this->value(base_func::expression);
+         }
+      };
+
+      struct func_5param : public base_func
+      {
+         using exprtk::ifunction<T>::operator();
+
+         func_5param() : base_func(5) {}
+
+         inline T operator() (type v0, type v1, type v2, type v3, type v4)
+         {
+            scoped_bft<func_5param> sb(*this);
+            base_func::update(v0, v1, v2, v3, v4);
+            return this->value(base_func::expression);
+         }
+      };
+
+      struct func_6param : public base_func
+      {
+         using exprtk::ifunction<T>::operator();
+
+         func_6param() : base_func(6) {}
+
+         inline T operator() (type v0, type v1, type v2, type v3, type v4, type v5)
+         {
+            scoped_bft<func_6param> sb(*this);
+            base_func::update(v0, v1, v2, v3, v4, v5);
+            return this->value(base_func::expression);
+         }
+      };
+
+      static T return_value(expression_t& e)
+      {
+         typedef exprtk::results_context<T> results_context_t;
+         typedef typename results_context_t::type_store_t type_t;
+         typedef typename type_t::scalar_view scalar_t;
+
+         T result = e.value();
+
+         if (e.return_invoked())
+         {
+            // Due to the post compilation checks, it can be safely
+            // assumed that there will be at least one parameter
+            // and that the first parameter will always be scalar.
+            return scalar_t(e.results()[0])();
+         }
+
+         return result;
+      }
+
+      #define def_fp_retval(N)                               \
+      struct func_##N##param_retval : public func_##N##param \
+      {                                                      \
+         inline T value(expression_t& e)                     \
+         {                                                   \
+            return return_value(e);                          \
+         }                                                   \
+      };                                                     \
+
+      def_fp_retval(0)
+      def_fp_retval(1)
+      def_fp_retval(2)
+      def_fp_retval(3)
+      def_fp_retval(4)
+      def_fp_retval(5)
+      def_fp_retval(6)
+
+      template <typename Allocator,
+                template <typename, typename> class Sequence>
+      inline bool add(const std::string& name,
+                      const std::string& expression,
+                      const Sequence<std::string,Allocator>& var_list,
+                      const bool override = false)
+      {
+         const typename std::map<std::string,expression_t>::iterator itr = expr_map_.find(name);
+
+         if (expr_map_.end() != itr)
+         {
+            if (!override)
+            {
+               exprtk_debug(("Compositor error(add): function '%s' already defined\n",
+                             name.c_str()));
+
+               return false;
+            }
+
+            remove(name, var_list.size());
+         }
+
+         if (compile_expression(name,expression,var_list))
+         {
+            const std::size_t n = var_list.size();
+
+            fp_map_[n][name]->setup(expr_map_[name]);
+
+            return true;
+         }
+         else
+         {
+            exprtk_debug(("Compositor error(add): Failed to compile function '%s'\n",
+                          name.c_str()));
+
+            return false;
+         }
+      }
+
+   public:
+
+      function_compositor()
+      : parser_(settings_t::compile_all_opts +
+                settings_t::e_disable_zero_return),
+        fp_map_(7)
+      {}
+
+      function_compositor(const symbol_table_t& st)
+      : symbol_table_(st),
+        parser_(settings_t::compile_all_opts +
+                settings_t::e_disable_zero_return),
+        fp_map_(7)
+      {}
+
+     ~function_compositor()
+      {
+         clear();
+      }
+
+      inline symbol_table_t& symbol_table()
+      {
+         return symbol_table_;
+      }
+
+      inline void add_auxiliary_symtab(symbol_table_t& symtab)
+      {
+         auxiliary_symtab_list_.push_back(&symtab);
+      }
+
+      void clear()
+      {
+         symbol_table_.clear();
+         expr_map_    .clear();
+
+         for (std::size_t i = 0; i < fp_map_.size(); ++i)
+         {
+            typename funcparam_t::iterator itr = fp_map_[i].begin();
+            typename funcparam_t::iterator end = fp_map_[i].end  ();
+
+            while (itr != end)
+            {
+               delete itr->second;
+               ++itr;
+            }
+
+            fp_map_[i].clear();
+         }
+      }
+
+      inline bool add(const function& f, const bool override = false)
+      {
+         return add(f.name_, f.expression_, f.v_,override);
+      }
+
+   private:
+
+      template <typename Allocator,
+                template <typename, typename> class Sequence>
+      bool compile_expression(const std::string& name,
+                              const std::string& expression,
+                              const Sequence<std::string,Allocator>& input_var_list,
+                              bool  return_present = false)
+      {
+         expression_t compiled_expression;
+         symbol_table_t local_symbol_table;
+
+         local_symbol_table.load_from(symbol_table_);
+         local_symbol_table.add_constants();
+
+         if (!valid(name,input_var_list.size()))
+            return false;
+
+         if (!forward(name,
+                      input_var_list.size(),
+                      local_symbol_table,
+                      return_present))
+            return false;
+
+         compiled_expression.register_symbol_table(local_symbol_table);
+
+         for (std::size_t i = 0; i < auxiliary_symtab_list_.size(); ++i)
+         {
+            compiled_expression.register_symbol_table((*auxiliary_symtab_list_[i]));
+         }
+
+         std::string mod_expression;
+
+         for (std::size_t i = 0; i < input_var_list.size(); ++i)
+         {
+            mod_expression += " var " + input_var_list[i] + "{};\n";
+         }
+
+         if (
+              ('{' == details::front(expression)) &&
+              ('}' == details::back (expression))
+            )
+            mod_expression += "~" + expression + ";";
+         else
+            mod_expression += "~{" + expression + "};";
+
+         if (!parser_.compile(mod_expression,compiled_expression))
+         {
+            exprtk_debug(("Compositor Error: %s\n",parser_.error().c_str()));
+            exprtk_debug(("Compositor modified expression: \n%s\n",mod_expression.c_str()));
+
+            remove(name,input_var_list.size());
+
+            return false;
+         }
+
+         if (!return_present && parser_.dec().return_present())
+         {
+            remove(name,input_var_list.size());
+
+            return compile_expression(name, expression, input_var_list, true);
+         }
+
+         // Make sure every return point has a scalar as its first parameter
+         if (parser_.dec().return_present())
+         {
+            typedef std::vector<std::string> str_list_t;
+
+            str_list_t ret_param_list = parser_.dec().return_param_type_list();
+
+            for (std::size_t i = 0; i < ret_param_list.size(); ++i)
+            {
+               const std::string& params = ret_param_list[i];
+
+               if (params.empty() || ('T' != params[0]))
+               {
+                  exprtk_debug(("Compositor Error: Return statement in function '%s' is invalid\n",
+                                name.c_str()));
+
+                  remove(name,input_var_list.size());
+
+                  return false;
+               }
+            }
+         }
+
+         expr_map_[name] = compiled_expression;
+
+         exprtk::ifunction<T>& ifunc = (*(fp_map_[input_var_list.size()])[name]);
+
+         if (symbol_table_.add_function(name,ifunc))
+            return true;
+         else
+         {
+            exprtk_debug(("Compositor Error: Failed to add function '%s' to symbol table\n",
+                          name.c_str()));
+            return false;
+         }
+      }
+
+      inline bool symbol_used(const std::string& symbol) const
+      {
+         return (
+                  symbol_table_.is_variable       (symbol) ||
+                  symbol_table_.is_stringvar      (symbol) ||
+                  symbol_table_.is_function       (symbol) ||
+                  symbol_table_.is_vector         (symbol) ||
+                  symbol_table_.is_vararg_function(symbol)
+                );
+      }
+
+      inline bool valid(const std::string& name,
+                        const std::size_t& arg_count) const
+      {
+         if (arg_count > 6)
+            return false;
+         else if (symbol_used(name))
+            return false;
+         else if (fp_map_[arg_count].end() != fp_map_[arg_count].find(name))
+            return false;
+         else
+            return true;
+      }
+
+      inline bool forward(const std::string& name,
+                          const std::size_t& arg_count,
+                          symbol_table_t& sym_table,
+                          const bool ret_present = false)
+      {
+         switch (arg_count)
+         {
+            #define case_stmt(N)                                     \
+            case N : (fp_map_[arg_count])[name] =                    \
+                     (!ret_present) ? static_cast<base_func*>        \
+                                      (new func_##N##param) :        \
+                                      static_cast<base_func*>        \
+                                      (new func_##N##param_retval) ; \
+                     break;                                          \
+
+            case_stmt(0) case_stmt(1) case_stmt(2)
+            case_stmt(3) case_stmt(4) case_stmt(5)
+            case_stmt(6)
+            #undef case_stmt
+         }
+
+         exprtk::ifunction<T>& ifunc = (*(fp_map_[arg_count])[name]);
+
+         return sym_table.add_function(name,ifunc);
+      }
+
+      inline void remove(const std::string& name, const std::size_t& arg_count)
+      {
+         if (arg_count > 6)
+            return;
+
+         const typename std::map<std::string,expression_t>::iterator em_itr = expr_map_.find(name);
+
+         if (expr_map_.end() != em_itr)
+         {
+            expr_map_.erase(em_itr);
+         }
+
+         const typename funcparam_t::iterator fp_itr = fp_map_[arg_count].find(name);
+
+         if (fp_map_[arg_count].end() != fp_itr)
+         {
+            delete fp_itr->second;
+            fp_map_[arg_count].erase(fp_itr);
+         }
+
+         symbol_table_.remove_function(name);
+      }
+
+   private:
+
+      symbol_table_t symbol_table_;
+      parser_t parser_;
+      std::map<std::string,expression_t> expr_map_;
+      std::vector<funcparam_t> fp_map_;
+      std::vector<symbol_table_t*> auxiliary_symtab_list_;
+   };
+
+   template <typename T>
+   inline bool pgo_primer()
+   {
+      static const std::string expression_list[] =
+             {
+                "(y + x)",
+                "2 * (y + x)",
+                "(2 * y + 2 * x)",
+                "(y + x / y) * (x - y / x)",
+                "x / ((x + y) * (x - y)) / y",
+                "1 - ((x * y) + (y / x)) - 3",
+                "sin(2 * x) + cos(pi / y)",
+                "1 - sin(2 * x) + cos(pi / y)",
+                "sqrt(1 - sin(2 * x) + cos(pi / y) / 3)",
+                "(x^2 / sin(2 * pi / y)) -x / 2",
+                "x + (cos(y - sin(2 / x * pi)) - sin(x - cos(2 * y / pi))) - y",
+                "clamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0)",
+                "iclamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0)",
+                "max(3.33, min(sqrt(1 - sin(2 * x) + cos(pi / y) / 3), 1.11))",
+                "if(avg(x,y) <= x + y, x - y, x * y) + 2 * pi / x",
+                "1.1x^1 + 2.2y^2 - 3.3x^3 + 4.4y^4 - 5.5x^5 + 6.6y^6 - 7.7x^27 + 8.8y^55",
+                "(yy + xx)",
+                "2 * (yy + xx)",
+                "(2 * yy + 2 * xx)",
+                "(yy + xx / yy) * (xx - yy / xx)",
+                "xx / ((xx + yy) * (xx - yy)) / yy",
+                "1 - ((xx * yy) + (yy / xx)) - 3",
+                "sin(2 * xx) + cos(pi / yy)",
+                "1 - sin(2 * xx) + cos(pi / yy)",
+                "sqrt(1 - sin(2 * xx) + cos(pi / yy) / 3)",
+                "(xx^2 / sin(2 * pi / yy)) -xx / 2",
+                "xx + (cos(yy - sin(2 / xx * pi)) - sin(xx - cos(2 * yy / pi))) - yy",
+                "clamp(-1.0, sin(2 * pi * xx) + cos(yy / 2 * pi), +1.0)",
+                "max(3.33, min(sqrt(1 - sin(2 * xx) + cos(pi / yy) / 3), 1.11))",
+                "if(avg(xx,yy) <= xx + yy, xx - yy, xx * yy) + 2 * pi / xx",
+                "1.1xx^1 + 2.2yy^2 - 3.3xx^3 + 4.4yy^4 - 5.5xx^5 + 6.6yy^6 - 7.7xx^27 + 8.8yy^55",
+                "(1.1*(2.2*(3.3*(4.4*(5.5*(6.6*(7.7*(8.8*(9.9+x)))))))))",
+                "(((((((((x+9.9)*8.8)*7.7)*6.6)*5.5)*4.4)*3.3)*2.2)*1.1)",
+                "(x + y) * z", "x + (y * z)", "(x + y) * 7", "x + (y * 7)",
+                "(x + 7) * y", "x + (7 * y)", "(7 + x) * y", "7 + (x * y)",
+                "(2 + x) * 3", "2 + (x * 3)", "(2 + 3) * x", "2 + (3 * x)",
+                "(x + 2) * 3", "x + (2 * 3)",
+                "(x + y) * (z / w)", "(x + y) * (z / 7)", "(x + y) * (7 / z)", "(x + 7) * (y / z)",
+                "(7 + x) * (y / z)", "(2 + x) * (y / z)", "(x + 2) * (y / 3)", "(2 + x) * (y / 3)",
+                "(x + 2) * (3 / y)", "x + (y * (z / w))", "x + (y * (z / 7))", "x + (y * (7 / z))",
+                "x + (7 * (y / z))", "7 + (x * (y / z))", "2 + (x * (3 / y))", "x + (2 * (y / 4))",
+                "2 + (x * (y / 3))", "x + (2 * (3 / y))",
+                "x + ((y * z) / w)", "x + ((y * z) / 7)", "x + ((y * 7) / z)", "x + ((7 * y) / z)",
+                "7 + ((y * z) / w)", "2 + ((x * 3) / y)", "x + ((2 * y) / 3)", "2 + ((x * y) / 3)",
+                "x + ((2 * 3) / y)", "(((x + y) * z) / w)",
+                "(((x + y) * z) / 7)", "(((x + y) * 7) / z)", "(((x + 7) * y) / z)", "(((7 + x) * y) / z)",
+                "(((2 + x) * 3) / y)", "(((x + 2) * y) / 3)", "(((2 + x) * y) / 3)", "(((x + 2) * 3) / y)",
+                "((x + (y * z)) / w)", "((x + (y * z)) / 7)", "((x + (y * 7)) / y)", "((x + (7 * y)) / z)",
+                "((7 + (x * y)) / z)", "((2 + (x * 3)) / y)", "((x + (2 * y)) / 3)", "((2 + (x * y)) / 3)",
+                "((x + (2 * 3)) / y)",
+                "(xx + yy) * zz", "xx + (yy * zz)",
+                "(xx + yy) * 7", "xx + (yy * 7)",
+                "(xx + 7) * yy", "xx + (7 * yy)",
+                "(7 + xx) * yy", "7 + (xx * yy)",
+                "(2 + x) * 3", "2 + (x * 3)",
+                "(2 + 3) * x", "2 + (3 * x)",
+                "(x + 2) * 3", "x + (2 * 3)",
+                "(xx + yy) * (zz / ww)", "(xx + yy) * (zz / 7)",
+                "(xx + yy) * (7 / zz)", "(xx + 7) * (yy / zz)",
+                "(7 + xx) * (yy / zz)", "(2 + xx) * (yy / zz)",
+                "(xx + 2) * (yy / 3)", "(2 + xx) * (yy / 3)",
+                "(xx + 2) * (3 / yy)", "xx + (yy * (zz / ww))",
+                "xx + (yy * (zz / 7))", "xx + (yy * (7 / zz))",
+                "xx + (7 * (yy / zz))", "7 + (xx * (yy / zz))",
+                "2 + (xx * (3 / yy))", "xx + (2 * (yy / 4))",
+                "2 + (xx * (yy / 3))", "xx + (2 * (3 / yy))",
+                "xx + ((yy * zz) / ww)", "xx + ((yy * zz) / 7)",
+                "xx + ((yy * 7) / zz)", "xx + ((7 * yy) / zz)",
+                "7 + ((yy * zz) / ww)", "2 + ((xx * 3) / yy)",
+                "xx + ((2 * yy) / 3)", "2 + ((xx * yy) / 3)",
+                "xx + ((2 * 3) / yy)", "(((xx + yy) * zz) / ww)",
+                "(((xx + yy) * zz) / 7)", "(((xx + yy) * 7) / zz)",
+                "(((xx + 7) * yy) / zz)", "(((7 + xx) * yy) / zz)",
+                "(((2 + xx) * 3) / yy)", "(((xx + 2) * yy) / 3)",
+                "(((2 + xx) * yy) / 3)", "(((xx + 2) * 3) / yy)",
+                "((xx + (yy * zz)) / ww)", "((xx + (yy * zz)) / 7)",
+                "((xx + (yy * 7)) / yy)", "((xx + (7 * yy)) / zz)",
+                "((7 + (xx * yy)) / zz)", "((2 + (xx * 3)) / yy)",
+                "((xx + (2 * yy)) / 3)", "((2 + (xx * yy)) / 3)",
+                "((xx + (2 * 3)) / yy)"
+             };
+
+      static const std::size_t expression_list_size = sizeof(expression_list) / sizeof(std::string);
+
+      T  x = T(0);
+      T  y = T(0);
+      T  z = T(0);
+      T  w = T(0);
+      T xx = T(0);
+      T yy = T(0);
+      T zz = T(0);
+      T ww = T(0);
+
+      exprtk::symbol_table<T> symbol_table;
+      symbol_table.add_constants();
+      symbol_table.add_variable( "x", x);
+      symbol_table.add_variable( "y", y);
+      symbol_table.add_variable( "z", z);
+      symbol_table.add_variable( "w", w);
+      symbol_table.add_variable("xx",xx);
+      symbol_table.add_variable("yy",yy);
+      symbol_table.add_variable("zz",zz);
+      symbol_table.add_variable("ww",ww);
+
+      typedef typename std::deque<exprtk::expression<T> > expr_list_t;
+      expr_list_t expr_list;
+
+      const std::size_t rounds = 50;
+
+      {
+         for (std::size_t r = 0; r < rounds; ++r)
+         {
+            expr_list.clear();
+            exprtk::parser<T> parser;
+
+            for (std::size_t i = 0; i < expression_list_size; ++i)
+            {
+               exprtk::expression<T> expression;
+               expression.register_symbol_table(symbol_table);
+
+               if (!parser.compile(expression_list[i],expression))
+               {
+                  return false;
+               }
+
+               expr_list.push_back(expression);
+            }
+         }
+      }
+
+      struct execute
+      {
+         static inline T process(T& x, T& y, expression<T>& expression)
+         {
+            static const T lower_bound = T(-20);
+            static const T upper_bound = T(+20);
+            static const T delta       = T(0.1);
+
+            T total = T(0);
+
+            for (x = lower_bound; x <= upper_bound; x += delta)
+            {
+               for (y = lower_bound; y <= upper_bound; y += delta)
+               {
+                  total += expression.value();
+               }
+            }
+
+            return total;
+         }
+      };
+
+      for (std::size_t i = 0; i < expr_list.size(); ++i)
+      {
+         execute::process( x,  y, expr_list[i]);
+         execute::process(xx, yy, expr_list[i]);
+      }
+
+      {
+         for (std::size_t i = 0; i < 10000; ++i)
+         {
+            const T v = T(123.456 + i);
+
+            if (details::is_true(details::numeric::nequal(details::numeric::fast_exp<T, 1>::result(v),details::numeric::pow(v,T( 1)))))
+               return false;
+
+            #define else_stmt(N)                                                                                                           \
+            else if (details::is_true(details::numeric::nequal(details::numeric::fast_exp<T,N>::result(v),details::numeric::pow(v,T(N))))) \
+               return false;                                                                                                               \
+
+            else_stmt( 2) else_stmt( 3) else_stmt( 4) else_stmt( 5)
+            else_stmt( 6) else_stmt( 7) else_stmt( 8) else_stmt( 9)
+            else_stmt(10) else_stmt(11) else_stmt(12) else_stmt(13)
+            else_stmt(14) else_stmt(15) else_stmt(16) else_stmt(17)
+            else_stmt(18) else_stmt(19) else_stmt(20) else_stmt(21)
+            else_stmt(22) else_stmt(23) else_stmt(24) else_stmt(25)
+            else_stmt(26) else_stmt(27) else_stmt(28) else_stmt(29)
+            else_stmt(30) else_stmt(31) else_stmt(32) else_stmt(33)
+            else_stmt(34) else_stmt(35) else_stmt(36) else_stmt(37)
+            else_stmt(38) else_stmt(39) else_stmt(40) else_stmt(41)
+            else_stmt(42) else_stmt(43) else_stmt(44) else_stmt(45)
+            else_stmt(46) else_stmt(47) else_stmt(48) else_stmt(49)
+            else_stmt(50) else_stmt(51) else_stmt(52) else_stmt(53)
+            else_stmt(54) else_stmt(55) else_stmt(56) else_stmt(57)
+            else_stmt(58) else_stmt(59) else_stmt(60) else_stmt(61)
+         }
+      }
+
+      return true;
+   }
+}
+
+#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+#   ifndef NOMINMAX
+#      define NOMINMAX
+#   endif
+#   ifndef WIN32_LEAN_AND_MEAN
+#      define WIN32_LEAN_AND_MEAN
+#   endif
+#   define NOGDI
+#   define NORESOURCE
+#   include <windows.h>
+#   include <ctime>
+#else
+#   include <ctime>
+#   include <sys/time.h>
+#   include <sys/types.h>
+#endif
+
+namespace exprtk
+{
+   class timer
+   {
+   public:
+
+      #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+      timer()
+      : in_use_(false)
+      {
+         QueryPerformanceFrequency(&clock_frequency_);
+      }
+
+      inline void start()
+      {
+         in_use_ = true;
+         QueryPerformanceCounter(&start_time_);
+      }
+
+      inline void stop()
+      {
+         QueryPerformanceCounter(&stop_time_);
+         in_use_ = false;
+      }
+
+      inline double time() const
+      {
+         return (1.0 * (stop_time_.QuadPart - start_time_.QuadPart)) / (1.0 * clock_frequency_.QuadPart);
+      }
+
+      #else
+
+      timer()
+      : in_use_(false)
+      {
+         start_time_.tv_sec  = 0;
+         start_time_.tv_usec = 0;
+         stop_time_.tv_sec   = 0;
+         stop_time_.tv_usec  = 0;
+      }
+
+      inline void start()
+      {
+         in_use_ = true;
+         gettimeofday(&start_time_,0);
+      }
+
+      inline void stop()
+      {
+         gettimeofday(&stop_time_, 0);
+         in_use_ = false;
+      }
+
+      inline unsigned long long int usec_time() const
+      {
+         if (!in_use_)
+         {
+            if (stop_time_.tv_sec >= start_time_.tv_sec)
+            {
+               return 1000000LLU * static_cast<details::_uint64_t>(stop_time_.tv_sec  - start_time_.tv_sec ) +
+                                   static_cast<details::_uint64_t>(stop_time_.tv_usec - start_time_.tv_usec) ;
+            }
+            else
+               return std::numeric_limits<details::_uint64_t>::max();
+         }
+         else
+            return std::numeric_limits<details::_uint64_t>::max();
+      }
+
+      inline double time() const
+      {
+         return usec_time() * 0.000001;
+      }
+
+      #endif
+
+      inline bool in_use() const
+      {
+         return in_use_;
+      }
+
+   private:
+
+      bool in_use_;
+
+      #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
+         LARGE_INTEGER start_time_;
+         LARGE_INTEGER stop_time_;
+         LARGE_INTEGER clock_frequency_;
+      #else
+         struct timeval start_time_;
+         struct timeval stop_time_;
+      #endif
+   };
+
+} // namespace exprtk
+
+#ifndef exprtk_disable_rtl_io
+namespace exprtk
+{
+   namespace rtl { namespace io { namespace details
+   {
+      template <typename T>
+      inline void print_type(const std::string& fmt,
+                             const T v,
+                             exprtk::details::numeric::details::real_type_tag)
+      {
+         printf(fmt.c_str(),v);
+      }
+
+      template <typename T>
+      struct print_impl
+      {
+         typedef typename igeneric_function<T>::generic_type generic_type;
+         typedef typename igeneric_function<T>::parameter_list_t parameter_list_t;
+         typedef typename generic_type::scalar_view scalar_t;
+         typedef typename generic_type::vector_view vector_t;
+         typedef typename generic_type::string_view string_t;
+         typedef typename exprtk::details::numeric::details::number_type<T>::type num_type;
+
+         static void process(const std::string& scalar_format, parameter_list_t parameters)
+         {
+            for (std::size_t i = 0; i < parameters.size(); ++i)
+            {
+               generic_type& gt = parameters[i];
+
+               switch (gt.type)
+               {
+                  case generic_type::e_scalar : print(scalar_format,scalar_t(gt));
+                                                break;
+
+                  case generic_type::e_vector : print(scalar_format,vector_t(gt));
+                                                break;
+
+                  case generic_type::e_string : print(string_t(gt));
+                                                break;
+
+                  default                     : continue;
+               }
+            }
+         }
+
+         static inline void print(const std::string& scalar_format, const scalar_t& s)
+         {
+            print_type(scalar_format,s(),num_type());
+         }
+
+         static inline void print(const std::string& scalar_format, const vector_t& v)
+         {
+            for (std::size_t i = 0; i < v.size(); ++i)
+            {
+               print_type(scalar_format,v[i],num_type());
+
+               if ((i + 1) < v.size())
+                  printf(" ");
+            }
+         }
+
+         static inline void print(const string_t& s)
+         {
+            printf("%s",to_str(s).c_str());
+         }
+      };
+
+   } // namespace exprtk::rtl::io::details
+
+   template <typename T>
+   struct print : public exprtk::igeneric_function<T>
+   {
+      typedef typename igeneric_function<T>::parameter_list_t parameter_list_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      print(const std::string& scalar_format = "%10.5f")
+      : scalar_format_(scalar_format)
+      {
+         exprtk::enable_zero_parameters(*this);
+      }
+
+      inline T operator() (parameter_list_t parameters)
+      {
+         details::print_impl<T>::process(scalar_format_,parameters);
+         return T(0);
+      }
+
+      std::string scalar_format_;
+   };
+
+   template <typename T>
+   struct println : public exprtk::igeneric_function<T>
+   {
+      typedef typename igeneric_function<T>::parameter_list_t parameter_list_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      println(const std::string& scalar_format = "%10.5f")
+      : scalar_format_(scalar_format)
+      {
+         exprtk::enable_zero_parameters(*this);
+      }
+
+      inline T operator() (parameter_list_t parameters)
+      {
+         details::print_impl<T>::process(scalar_format_,parameters);
+         printf("\n");
+         return T(0);
+      }
+
+      std::string scalar_format_;
+   };
+
+   template <typename T>
+   struct package
+   {
+      print  <T> p;
+      println<T> pl;
+
+      bool register_package(exprtk::symbol_table<T>& symtab)
+      {
+         #define exprtk_register_function(FunctionName,FunctionType)              \
+         if (!symtab.add_function(FunctionName,FunctionType))                     \
+         {                                                                        \
+            exprtk_debug((                                                        \
+              "exprtk::rtl::io::register_package - Failed to add function: %s\n", \
+              FunctionName));                                                     \
+            return false;                                                         \
+         }                                                                        \
+
+         exprtk_register_function("print"  ,  p)
+         exprtk_register_function("println", pl)
+         #undef exprtk_register_function
+
+         return true;
+      }
+   };
+
+   } // namespace exprtk::rtl::io
+   } // namespace exprtk::rtl
+}    // namespace exprtk
+#endif
+
+#ifndef exprtk_disable_rtl_io_file
+#include <fstream>
+namespace exprtk
+{
+   namespace rtl { namespace io { namespace file { namespace details
+   {
+      enum file_mode
+      {
+         e_error = 0,
+         e_read  = 1,
+         e_write = 2,
+         e_rdwrt = 4
+      };
+
+      struct file_descriptor
+      {
+         file_descriptor(const std::string& fname, const std::string& access)
+         : stream_ptr(0),
+           mode(get_file_mode(access)),
+           file_name(fname)
+         {}
+
+         void*       stream_ptr;
+         file_mode   mode;
+         std::string file_name;
+
+         bool open()
+         {
+            if (e_read == mode)
+            {
+               std::ifstream* stream = new std::ifstream(file_name.c_str(),std::ios::binary);
+
+               if (!(*stream))
+               {
+                  file_name.clear();
+                  delete stream;
+
+                  return false;
+               }
+               else
+                  stream_ptr = stream;
+
+               return true;
+            }
+            else if (e_write == mode)
+            {
+               std::ofstream* stream = new std::ofstream(file_name.c_str(),std::ios::binary);
+
+               if (!(*stream))
+               {
+                  file_name.clear();
+                  delete stream;
+
+                  return false;
+               }
+               else
+                  stream_ptr = stream;
+
+               return true;
+            }
+            else if (e_rdwrt == mode)
+            {
+               std::fstream* stream = new std::fstream(file_name.c_str(),std::ios::binary);
+
+               if (!(*stream))
+               {
+                  file_name.clear();
+                  delete stream;
+
+                  return false;
+               }
+               else
+                  stream_ptr = stream;
+
+               return true;
+            }
+            else
+               return false;
+         }
+
+         template <typename Stream, typename Ptr>
+         void close(Ptr& p)
+         {
+            Stream* stream = reinterpret_cast<Stream*>(p);
+            stream->close();
+            delete stream;
+            p = reinterpret_cast<Ptr>(0);
+         }
+
+         bool close()
+         {
+            switch (mode)
+            {
+               case e_read  : close<std::ifstream>(stream_ptr);
+                              break;
+
+               case e_write : close<std::ofstream>(stream_ptr);
+                              break;
+
+               case e_rdwrt : close<std::fstream> (stream_ptr);
+                              break;
+
+               default      : return false;
+            }
+
+            return true;
+         }
+
+         template <typename View>
+         bool write(const View& view, const std::size_t amount, const std::size_t offset = 0)
+         {
+            switch (mode)
+            {
+               case e_write : reinterpret_cast<std::ofstream*>(stream_ptr)->
+                                 write(reinterpret_cast<const char*>(view.begin() + offset), amount * sizeof(typename View::value_t));
+                              break;
+
+               case e_rdwrt : reinterpret_cast<std::fstream*>(stream_ptr)->
+                                 write(reinterpret_cast<const char*>(view.begin() + offset) , amount * sizeof(typename View::value_t));
+                              break;
+
+               default      : return false;
+            }
+
+            return true;
+         }
+
+         template <typename View>
+         bool read(View& view, const std::size_t amount, const std::size_t offset = 0)
+         {
+            switch (mode)
+            {
+               case e_read  : reinterpret_cast<std::ifstream*>(stream_ptr)->
+                                 read(reinterpret_cast<char*>(view.begin() + offset), amount * sizeof(typename View::value_t));
+                              break;
+
+               case e_rdwrt : reinterpret_cast<std::fstream*>(stream_ptr)->
+                                 read(reinterpret_cast<char*>(view.begin() + offset) , amount * sizeof(typename View::value_t));
+                              break;
+
+               default      : return false;
+            }
+
+            return true;
+         }
+
+         bool getline(std::string& s)
+         {
+            switch (mode)
+            {
+               case e_read  : return (!!std::getline(*reinterpret_cast<std::ifstream*>(stream_ptr),s));
+               case e_rdwrt : return (!!std::getline(*reinterpret_cast<std::fstream* >(stream_ptr),s));
+               default      : return false;
+            }
+         }
+
+         bool eof() const
+         {
+            switch (mode)
+            {
+               case e_read  : return reinterpret_cast<std::ifstream*>(stream_ptr)->eof();
+               case e_write : return reinterpret_cast<std::ofstream*>(stream_ptr)->eof();
+               case e_rdwrt : return reinterpret_cast<std::fstream* >(stream_ptr)->eof();
+               default      : return true;
+            }
+         }
+
+         file_mode get_file_mode(const std::string& access) const
+         {
+            if (access.empty() || access.size() > 2)
+               return e_error;
+
+            std::size_t w_cnt = 0;
+            std::size_t r_cnt = 0;
+
+            for (std::size_t i = 0; i < access.size(); ++i)
+            {
+               switch (std::tolower(access[i]))
+               {
+                  case 'r' : r_cnt++; break;
+                  case 'w' : w_cnt++; break;
+                  default  : return e_error;
+               }
+            }
+
+            if ((0 == r_cnt) && (0 == w_cnt))
+               return e_error;
+            else if ((r_cnt > 1) || (w_cnt > 1))
+               return e_error;
+            else if ((1 == r_cnt) && (1 == w_cnt))
+               return e_rdwrt;
+            else if (1 == r_cnt)
+               return e_read;
+            else
+               return e_write;
+         }
+      };
+
+      template <typename T>
+      file_descriptor* make_handle(T v)
+      {
+         file_descriptor* fd = reinterpret_cast<file_descriptor*>(0);
+
+         std::memcpy(reinterpret_cast<char*>(&fd),
+                     reinterpret_cast<const char*>(&v),
+                     sizeof(fd));
+         return fd;
+      }
+
+      template <typename T>
+      void perform_check()
+      {
+         #ifdef _MSC_VER
+         #pragma warning(push)
+         #pragma warning(disable: 4127)
+         #endif
+         if (sizeof(T) < sizeof(void*))
+         {
+            throw std::runtime_error("exprtk::rtl::io::file - Error - pointer size larger than holder.");
+         }
+         #ifdef _MSC_VER
+         #pragma warning(pop)
+         #endif
+      }
+
+   } // namespace exprtk::rtl::io::file::details
+
+   template <typename T>
+   class open : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::string_view    string_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      open()
+      : exprtk::igeneric_function<T>("S|SS")
+      { details::perform_check<T>(); }
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         std::string file_name = to_str(string_t(parameters[0]));
+         std::string access;
+
+         if (file_name.empty())
+            return T(0);
+
+         if (0 == ps_index)
+            access = "r";
+         else if (0 == string_t(parameters[1]).size())
+            return T(0);
+         else
+            access = to_str(string_t(parameters[1]));
+
+         details::file_descriptor* fd = new details::file_descriptor(file_name,access);
+
+         if (fd->open())
+         {
+            T t = T(0);
+
+            std::memcpy(reinterpret_cast<char*>(&t ),
+                        reinterpret_cast<char*>(&fd),
+                        sizeof(fd));
+            return t;
+         }
+         else
+         {
+            delete fd;
+            return T(0);
+         }
+      }
+   };
+
+   template <typename T>
+   struct close : public exprtk::ifunction<T>
+   {
+      using exprtk::ifunction<T>::operator();
+
+      close()
+      : exprtk::ifunction<T>(1)
+      { details::perform_check<T>(); }
+
+      inline T operator() (const T& v)
+      {
+         details::file_descriptor* fd = details::make_handle(v);
+
+         if (!fd->close())
+            return T(0);
+
+         delete fd;
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class write : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::string_view    string_t;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      write()
+      : igfun_t("TS|TST|TV|TVT")
+      { details::perform_check<T>(); }
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         details::file_descriptor* fd = details::make_handle(scalar_t(parameters[0])());
+
+         std::size_t amount = 0;
+
+         switch (ps_index)
+         {
+            case 0  : {
+                         const string_t buffer(parameters[1]);
+                         amount = buffer.size();
+                         return T(fd->write(buffer, amount) ? 1 : 0);
+                      }
+
+            case 1  : {
+                         const string_t buffer(parameters[1]);
+                         amount = std::min(buffer.size(),
+                                           static_cast<std::size_t>(scalar_t(parameters[2])()));
+                         return T(fd->write(buffer, amount) ? 1 : 0);
+                      }
+
+            case 2  : {
+                         const vector_t vec(parameters[1]);
+                         amount = vec.size();
+                         return T(fd->write(vec, amount) ? 1 : 0);
+                      }
+
+            case 3  : {
+                         const vector_t vec(parameters[1]);
+                         amount = std::min(vec.size(),
+                                           static_cast<std::size_t>(scalar_t(parameters[2])()));
+                         return T(fd->write(vec, amount) ? 1 : 0);
+                      }
+         }
+
+         return T(0);
+      }
+   };
+
+   template <typename T>
+   class read : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::string_view    string_t;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      read()
+      : igfun_t("TS|TST|TV|TVT")
+      { details::perform_check<T>(); }
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         details::file_descriptor* fd = details::make_handle(scalar_t(parameters[0])());
+
+         std::size_t amount = 0;
+
+         switch (ps_index)
+         {
+            case 0  : {
+                         string_t buffer(parameters[1]);
+                         amount = buffer.size();
+                         return T(fd->read(buffer,amount) ? 1 : 0);
+                      }
+
+            case 1  : {
+                         string_t buffer(parameters[1]);
+                         amount = std::min(buffer.size(),
+                                           static_cast<std::size_t>(scalar_t(parameters[2])()));
+                         return T(fd->read(buffer,amount) ? 1 : 0);
+                      }
+
+            case 2  : {
+                         vector_t vec(parameters[1]);
+                         amount = vec.size();
+                         return T(fd->read(vec,amount) ? 1 : 0);
+                      }
+
+            case 3  : {
+                         vector_t vec(parameters[1]);
+                         amount = std::min(vec.size(),
+                                           static_cast<std::size_t>(scalar_t(parameters[2])()));
+                         return T(fd->read(vec,amount) ? 1 : 0);
+                      }
+         }
+
+         return T(0);
+      }
+   };
+
+   template <typename T>
+   class getline : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::string_view    string_t;
+      typedef typename generic_type::scalar_view    scalar_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      getline()
+      : igfun_t("T",igfun_t::e_rtrn_string)
+      { details::perform_check<T>(); }
+
+      inline T operator() (std::string& result,
+                           parameter_list_t parameters)
+      {
+         details::file_descriptor* fd = details::make_handle(scalar_t(parameters[0])());
+         return T(fd->getline(result) ? 1 : 0);
+      }
+   };
+
+   template <typename T>
+   struct eof : public exprtk::ifunction<T>
+   {
+      using exprtk::ifunction<T>::operator();
+
+      eof()
+      : exprtk::ifunction<T>(1)
+      { details::perform_check<T>(); }
+
+      inline T operator() (const T& v)
+      {
+         details::file_descriptor* fd = details::make_handle(v);
+
+         return (fd->eof() ? T(1) : T(0));
+      }
+   };
+
+   template <typename T>
+   struct package
+   {
+      open   <T> o;
+      close  <T> c;
+      write  <T> w;
+      read   <T> r;
+      getline<T> g;
+      eof    <T> e;
+
+      bool register_package(exprtk::symbol_table<T>& symtab)
+      {
+         #define exprtk_register_function(FunctionName,FunctionType)                    \
+         if (!symtab.add_function(FunctionName,FunctionType))                           \
+         {                                                                              \
+            exprtk_debug((                                                              \
+              "exprtk::rtl::io::file::register_package - Failed to add function: %s\n", \
+              FunctionName));                                                           \
+            return false;                                                               \
+         }                                                                              \
+
+         exprtk_register_function("open"   ,o)
+         exprtk_register_function("close"  ,c)
+         exprtk_register_function("write"  ,w)
+         exprtk_register_function("read"   ,r)
+         exprtk_register_function("getline",g)
+         exprtk_register_function("eof"    ,e)
+         #undef exprtk_register_function
+
+         return true;
+      }
+   };
+
+   } // namespace exprtk::rtl::io::file
+   } // namespace exprtk::rtl::io
+   } // namespace exprtk::rtl
+}    // namespace exprtk
+#endif
+
+#ifndef exprtk_disable_rtl_vecops
+namespace exprtk
+{
+   namespace rtl { namespace vecops {
+
+   namespace helper
+   {
+      template <typename Vector>
+      inline bool invalid_range(const Vector& v, const std::size_t r0, const std::size_t r1)
+      {
+         if (r0 > (v.size() - 1))
+            return true;
+         else if (r1 > (v.size() - 1))
+            return true;
+         else if (r1 < r0)
+            return true;
+         else
+            return false;
+      }
+
+      template <typename T>
+      struct load_vector_range
+      {
+         typedef typename exprtk::igeneric_function<T> igfun_t;
+         typedef typename igfun_t::parameter_list_t    parameter_list_t;
+         typedef typename igfun_t::generic_type        generic_type;
+         typedef typename generic_type::scalar_view    scalar_t;
+         typedef typename generic_type::vector_view    vector_t;
+
+         static inline bool process(parameter_list_t& parameters,
+                                    std::size_t& r0, std::size_t& r1,
+                                    const std::size_t& r0_prmidx,
+                                    const std::size_t& r1_prmidx,
+                                    const std::size_t vec_idx = 0)
+         {
+            if (r0_prmidx >= parameters.size())
+               return false;
+
+            if (r1_prmidx >= parameters.size())
+               return false;
+
+            if (!scalar_t(parameters[r0_prmidx]).to_uint(r0))
+               return false;
+
+            if (!scalar_t(parameters[r1_prmidx]).to_uint(r1))
+               return false;
+
+            return !invalid_range(vector_t(parameters[vec_idx]), r0, r1);
+         }
+      };
+   }
+
+   namespace details
+   {
+      template <typename T>
+      inline void kahan_sum(T& sum, T& error, const T v)
+      {
+         const T x = v - error;
+         const T y = sum + x;
+         error = (y - sum) - x;
+         sum = y;
+      }
+
+   } // namespace exprtk::rtl::details
+
+   template <typename T>
+   class all_true : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      all_true()
+      : exprtk::igeneric_function<T>("V|VTT")
+        /*
+           Overloads:
+           0. V   - vector
+           1. VTT - vector, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t vec(parameters[0]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (
+              (1 == ps_index) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+            )
+            return std::numeric_limits<T>::quiet_NaN();
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            if (vec[i] == T(0))
+            {
+               return T(0);
+            }
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class all_false : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      all_false()
+      : exprtk::igeneric_function<T>("V|VTT")
+        /*
+           Overloads:
+           0. V   - vector
+           1. VTT - vector, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t vec(parameters[0]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (
+              (1 == ps_index) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+            )
+            return std::numeric_limits<T>::quiet_NaN();
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            if (vec[i] != T(0))
+            {
+               return T(0);
+            }
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class any_true : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      any_true()
+      : exprtk::igeneric_function<T>("V|VTT")
+        /*
+           Overloads:
+           0. V   - vector
+           1. VTT - vector, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t vec(parameters[0]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (
+              (1 == ps_index) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+            )
+            return std::numeric_limits<T>::quiet_NaN();
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            if (vec[i] != T(0))
+            {
+               return T(1);
+            }
+         }
+
+         return T(0);
+      }
+   };
+
+   template <typename T>
+   class any_false : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      any_false()
+      : exprtk::igeneric_function<T>("V|VTT")
+        /*
+           Overloads:
+           0. V   - vector
+           1. VTT - vector, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t vec(parameters[0]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (
+              (1 == ps_index) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+            )
+            return std::numeric_limits<T>::quiet_NaN();
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            if (vec[i] == T(0))
+            {
+               return T(1);
+            }
+         }
+
+         return T(0);
+      }
+   };
+
+   template <typename T>
+   class count : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      count()
+      : exprtk::igeneric_function<T>("V|VTT")
+        /*
+           Overloads:
+           0. V   - vector
+           1. VTT - vector, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t vec(parameters[0]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (
+              (1 == ps_index) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0)
+            )
+            return std::numeric_limits<T>::quiet_NaN();
+
+         std::size_t cnt = 0;
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            if (vec[i] != T(0)) ++cnt;
+         }
+
+         return T(cnt);
+      }
+   };
+
+   template <typename T>
+   class copy : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      copy()
+      : exprtk::igeneric_function<T>("VV|VTTVTT")
+        /*
+           Overloads:
+           0. VV     - x(vector), y(vector)
+           1. VTTVTT - x(vector), xr0, xr1, y(vector), yr0, yr1,
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t x(parameters[0]);
+               vector_t y(parameters[(0 == ps_index) ? 1 : 3]);
+
+         std::size_t xr0 = 0;
+         std::size_t xr1 = x.size() - 1;
+
+         std::size_t yr0 = 0;
+         std::size_t yr1 = y.size() - 1;
+
+         if (1 == ps_index)
+         {
+            if (
+                 !helper::load_vector_range<T>::process(parameters, xr0, xr1, 1, 2, 0) ||
+                 !helper::load_vector_range<T>::process(parameters, yr0, yr1, 4, 5, 3)
+               )
+               return T(0);
+         }
+
+         const std::size_t n = std::min(xr1 - xr0 + 1, yr1 - yr0 + 1);
+
+         std::copy(x.begin() + xr0, x.begin() + xr0 + n, y.begin() + yr0);
+
+         return T(n);
+      }
+   };
+
+   template <typename T>
+   class rol : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      rol()
+      : exprtk::igeneric_function<T>("VT|VTTT")
+        /*
+           Overloads:
+           0. VT   - vector, N
+           1. VTTT - vector, N, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         vector_t vec(parameters[0]);
+
+         std::size_t n  = 0;
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (!scalar_t(parameters[1]).to_uint(n))
+            return T(0);
+
+         if (
+              (1 == ps_index) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0)
+            )
+            return T(0);
+
+         std::size_t dist  = r1 - r0 + 1;
+         std::size_t shift = n % dist;
+
+         std::rotate(vec.begin() + r0, vec.begin() + r0 + shift, vec.begin() + r1 + 1);
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class ror : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      ror()
+      : exprtk::igeneric_function<T>("VT|VTTT")
+        /*
+           Overloads:
+           0. VT   - vector, N
+           1. VTTT - vector, N, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         vector_t vec(parameters[0]);
+
+         std::size_t n  = 0;
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (!scalar_t(parameters[1]).to_uint(n))
+            return T(0);
+
+         if (
+              (1 == ps_index) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0)
+            )
+            return T(0);
+
+         std::size_t dist  = r1 - r0 + 1;
+         std::size_t shift = (dist - (n % dist)) % dist;
+
+         std::rotate(vec.begin() + r0, vec.begin() + r0 + shift, vec.begin() + r1 + 1);
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class shift_left : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      shift_left()
+      : exprtk::igeneric_function<T>("VT|VTTT")
+        /*
+           Overloads:
+           0. VT   - vector, N
+           1. VTTT - vector, N, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         vector_t vec(parameters[0]);
+
+         std::size_t n  = 0;
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (!scalar_t(parameters[1]).to_uint(n))
+            return T(0);
+
+         if (
+              (1 == ps_index) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0)
+            )
+            return T(0);
+
+         std::size_t dist  = r1 - r0 + 1;
+
+         if (n > dist)
+            return T(0);
+
+         std::rotate(vec.begin() + r0, vec.begin() + r0 + n, vec.begin() + r1 + 1);
+
+         for (std::size_t i = r1 - n + 1; i <= r1; ++i)
+         {
+            vec[i] = T(0);
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class shift_right : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      shift_right()
+      : exprtk::igeneric_function<T>("VT|VTTT")
+        /*
+           Overloads:
+           0. VT   - vector, N
+           1. VTTT - vector, N, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         vector_t vec(parameters[0]);
+
+         std::size_t n  = 0;
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (!scalar_t(parameters[1]).to_uint(n))
+            return T(0);
+
+         if (
+              (1 == ps_index) &&
+              !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0)
+            )
+            return T(0);
+
+         std::size_t dist  = r1 - r0 + 1;
+
+         if (n > dist)
+            return T(0);
+
+         std::size_t shift = (dist - (n % dist)) % dist;
+
+         std::rotate(vec.begin() + r0, vec.begin() + r0 + shift, vec.begin() + r1 + 1);
+
+         for (std::size_t i = r0; i < r0 + n; ++i)
+         {
+            vec[i] = T(0);
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class sort : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::string_view    string_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      sort()
+      : exprtk::igeneric_function<T>("V|VTT|VS|VSTT")
+        /*
+           Overloads:
+           0. V    - vector
+           1. VTT  - vector, r0, r1
+           2. VS   - vector, string
+           3. VSTT - vector, string, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         vector_t vec(parameters[0]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0))
+            return T(0);
+         if ((3 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0))
+            return T(0);
+
+         bool ascending = true;
+
+         if ((2 == ps_index) || (3 == ps_index))
+         {
+            if (exprtk::details::imatch(to_str(string_t(parameters[1])),"ascending"))
+               ascending = true;
+            else if (exprtk::details::imatch(to_str(string_t(parameters[1])),"descending"))
+               ascending = false;
+            else
+               return T(0);
+         }
+
+         if (ascending)
+            std::sort(vec.begin() + r0, vec.begin() + r1 + 1, std::less<T>   ());
+         else
+            std::sort(vec.begin() + r0, vec.begin() + r1 + 1, std::greater<T>());
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class nthelement : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      nthelement()
+      : exprtk::igeneric_function<T>("VT|VTTT")
+        /*
+           Overloads:
+           0. VT   - vector, nth-element
+           1. VTTT - vector, nth-element, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         vector_t vec(parameters[0]);
+
+         std::size_t n  = 0;
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if (!scalar_t(parameters[1]).to_uint(n))
+            return T(0);
+
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0))
+            return std::numeric_limits<T>::quiet_NaN();
+
+         std::nth_element(vec.begin() + r0, vec.begin() + r0 + n , vec.begin() + r1 + 1);
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class iota : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      iota()
+      : exprtk::igeneric_function<T>("VT|VTT|VTTT|VTTTT")
+        /*
+           Overloads:
+           0. VT    - vector, increment
+           1. VTT   - vector, increment, base
+           2. VTTTT - vector, increment, r0, r1
+           3. VTTTT - vector, increment, base, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         vector_t vec(parameters[0]);
+
+         T increment = scalar_t(parameters[1])();
+         T base      = ((1 == ps_index) || (3 == ps_index)) ? scalar_t(parameters[2])() : T(0);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if ((2 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0))
+            return std::numeric_limits<T>::quiet_NaN();
+         else if ((3 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 3, 4, 0))
+            return std::numeric_limits<T>::quiet_NaN();
+         else
+         {
+            long long j = 0;
+
+            for (std::size_t i = r0; i <= r1; ++i, ++j)
+            {
+               vec[i] = base + (increment * j);
+            }
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class sumk : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      sumk()
+      : exprtk::igeneric_function<T>("V|VTT")
+        /*
+           Overloads:
+           0. V   - vector
+           1. VTT - vector, r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t vec(parameters[0]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = vec.size() - 1;
+
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 1, 2, 0))
+            return std::numeric_limits<T>::quiet_NaN();
+
+         T result = T(0);
+         T error  = T(0);
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            details::kahan_sum(result, error, vec[i]);
+         }
+
+         return result;
+      }
+   };
+
+   template <typename T>
+   class axpy : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      axpy()
+      : exprtk::igeneric_function<T>("TVV|TVVTT")
+        /*
+           y <- ax + y
+           Overloads:
+           0. TVV   - a, x(vector), y(vector)
+           1. TVVTT - a, x(vector), y(vector), r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t x(parameters[1]);
+               vector_t y(parameters[2]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = std::min(x.size(),y.size()) - 1;
+
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 3, 4, 1))
+            return std::numeric_limits<T>::quiet_NaN();
+         else if (helper::invalid_range(y, r0, r1))
+            return std::numeric_limits<T>::quiet_NaN();
+
+         const T a = scalar_t(parameters[0])();
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            y[i] = (a * x[i]) + y[i];
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class axpby : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      axpby()
+      : exprtk::igeneric_function<T>("TVTV|TVTVTT")
+        /*
+           y <- ax + by
+           Overloads:
+           0. TVTV   - a, x(vector), b, y(vector)
+           1. TVTVTT - a, x(vector), b, y(vector), r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t x(parameters[1]);
+               vector_t y(parameters[3]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = std::min(x.size(),y.size()) - 1;
+
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 4, 5, 1))
+            return std::numeric_limits<T>::quiet_NaN();
+         else if (helper::invalid_range(y, r0, r1))
+            return std::numeric_limits<T>::quiet_NaN();
+
+         const T a = scalar_t(parameters[0])();
+         const T b = scalar_t(parameters[2])();
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            y[i] = (a * x[i]) + (b * y[i]);
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class axpyz : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      axpyz()
+      : exprtk::igeneric_function<T>("TVVV|TVVVTT")
+        /*
+           z <- ax + y
+           Overloads:
+           0. TVVV   - a, x(vector), y(vector), z(vector)
+           1. TVVVTT - a, x(vector), y(vector), z(vector), r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t x(parameters[1]);
+         const vector_t y(parameters[2]);
+               vector_t z(parameters[3]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = std::min(x.size(),y.size()) - 1;
+
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 3, 4, 1))
+            return std::numeric_limits<T>::quiet_NaN();
+         else if (helper::invalid_range(y, r0, r1))
+            return std::numeric_limits<T>::quiet_NaN();
+         else if (helper::invalid_range(z, r0, r1))
+            return std::numeric_limits<T>::quiet_NaN();
+
+         const T a = scalar_t(parameters[0])();
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            z[i] = (a * x[i]) + y[i];
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class axpbyz : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      axpbyz()
+      : exprtk::igeneric_function<T>("TVTVV|TVTVVTT")
+        /*
+           z <- ax + by
+           Overloads:
+           0. TVTVV   - a, x(vector), b, y(vector), z(vector)
+           1. TVTVVTT - a, x(vector), b, y(vector), z(vector), r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t x(parameters[1]);
+         const vector_t y(parameters[3]);
+               vector_t z(parameters[4]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = std::min(x.size(),y.size()) - 1;
+
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 4, 5, 1))
+            return std::numeric_limits<T>::quiet_NaN();
+         else if (helper::invalid_range(y, r0, r1))
+            return std::numeric_limits<T>::quiet_NaN();
+         else if (helper::invalid_range(z, r0, r1))
+            return std::numeric_limits<T>::quiet_NaN();
+
+         const T a = scalar_t(parameters[0])();
+         const T b = scalar_t(parameters[2])();
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            z[i] = (a * x[i]) + (b * y[i]);
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class axpbz : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      axpbz()
+      : exprtk::igeneric_function<T>("TVTV|TVTVTT")
+        /*
+           z <- ax + b
+           Overloads:
+           0. TVTV   - a, x(vector), b, z(vector)
+           1. TVTVTT - a, x(vector), b, z(vector), r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t x(parameters[1]);
+               vector_t z(parameters[3]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = x.size() - 1;
+
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 4, 5, 1))
+            return std::numeric_limits<T>::quiet_NaN();
+         else if (helper::invalid_range(z, r0, r1))
+            return std::numeric_limits<T>::quiet_NaN();
+
+         const T a = scalar_t(parameters[0])();
+         const T b = scalar_t(parameters[2])();
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            z[i] = (a * x[i]) + b;
+         }
+
+         return T(1);
+      }
+   };
+
+   template <typename T>
+   class dot : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      dot()
+      : exprtk::igeneric_function<T>("VV|VVTT")
+        /*
+           Overloads:
+           0. VV   - x(vector), y(vector)
+           1. VVTT - x(vector), y(vector), r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t x(parameters[0]);
+         const vector_t y(parameters[1]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = std::min(x.size(),y.size()) - 1;
+
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0))
+            return std::numeric_limits<T>::quiet_NaN();
+         else if (helper::invalid_range(y, r0, r1))
+            return std::numeric_limits<T>::quiet_NaN();
+
+         T result = T(0);
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            result += (x[i] * y[i]);
+         }
+
+         return result;
+      }
+   };
+
+   template <typename T>
+   class dotk : public exprtk::igeneric_function<T>
+   {
+   public:
+
+      typedef typename exprtk::igeneric_function<T> igfun_t;
+      typedef typename igfun_t::parameter_list_t    parameter_list_t;
+      typedef typename igfun_t::generic_type        generic_type;
+      typedef typename generic_type::scalar_view    scalar_t;
+      typedef typename generic_type::vector_view    vector_t;
+
+      using exprtk::igeneric_function<T>::operator();
+
+      dotk()
+      : exprtk::igeneric_function<T>("VV|VVTT")
+        /*
+           Overloads:
+           0. VV   - x(vector), y(vector)
+           1. VVTT - x(vector), y(vector), r0, r1
+        */
+      {}
+
+      inline T operator() (const std::size_t& ps_index, parameter_list_t parameters)
+      {
+         const vector_t x(parameters[0]);
+         const vector_t y(parameters[1]);
+
+         std::size_t r0 = 0;
+         std::size_t r1 = std::min(x.size(),y.size()) - 1;
+
+         if ((1 == ps_index) && !helper::load_vector_range<T>::process(parameters, r0, r1, 2, 3, 0))
+            return std::numeric_limits<T>::quiet_NaN();
+         else if (helper::invalid_range(y, r0, r1))
+            return std::numeric_limits<T>::quiet_NaN();
+
+         T result = T(0);
+         T error  = T(0);
+
+         for (std::size_t i = r0; i <= r1; ++i)
+         {
+            details::kahan_sum(result, error, (x[i] * y[i]));
+         }
+
+         return result;
+      }
+   };
+
+   template <typename T>
+   struct package
+   {
+      all_true   <T> at;
+      all_false  <T> af;
+      any_true   <T> nt;
+      any_false  <T> nf;
+      count      <T>  c;
+      copy       <T> cp;
+      rol        <T> rl;
+      ror        <T> rr;
+      shift_left <T> sl;
+      shift_right<T> sr;
+      sort       <T> st;
+      nthelement <T> ne;
+      iota       <T> ia;
+      sumk       <T> sk;
+      axpy       <T> b1_axpy;
+      axpby      <T> b1_axpby;
+      axpyz      <T> b1_axpyz;
+      axpbyz     <T> b1_axpbyz;
+      axpbz      <T> b1_axpbz;
+      dot        <T> dt;
+      dotk       <T> dtk;
+
+      bool register_package(exprtk::symbol_table<T>& symtab)
+      {
+         #define exprtk_register_function(FunctionName,FunctionType)                  \
+         if (!symtab.add_function(FunctionName,FunctionType))                         \
+         {                                                                            \
+            exprtk_debug((                                                            \
+              "exprtk::rtl::vecops::register_package - Failed to add function: %s\n", \
+              FunctionName));                                                         \
+            return false;                                                             \
+         }                                                                            \
+
+         exprtk_register_function("all_true"     ,at)
+         exprtk_register_function("all_false"    ,af)
+         exprtk_register_function("any_true"     ,nt)
+         exprtk_register_function("any_false"    ,nf)
+         exprtk_register_function("count"        , c)
+         exprtk_register_function("copy"         ,cp)
+         exprtk_register_function("rotate_left"  ,rl)
+         exprtk_register_function("rol"          ,rl)
+         exprtk_register_function("rotate_right" ,rr)
+         exprtk_register_function("ror"          ,rr)
+         exprtk_register_function("shftl"        ,sl)
+         exprtk_register_function("shftr"        ,sr)
+         exprtk_register_function("sort"         ,st)
+         exprtk_register_function("nth_element"  ,ne)
+         exprtk_register_function("iota"         ,ia)
+         exprtk_register_function("sumk"         ,sk)
+         exprtk_register_function("axpy"    ,b1_axpy)
+         exprtk_register_function("axpby"  ,b1_axpby)
+         exprtk_register_function("axpyz"  ,b1_axpyz)
+         exprtk_register_function("axpbyz",b1_axpbyz)
+         exprtk_register_function("axpbz"  ,b1_axpbz)
+         exprtk_register_function("dot"          ,dt)
+         exprtk_register_function("dotk"        ,dtk)
+         #undef exprtk_register_function
+
+         return true;
+      }
+   };
+
+   } // namespace exprtk::rtl::vecops
+   } // namespace exprtk::rtl
+}    // namespace exprtk
+#endif
+
+namespace exprtk
+{
+   namespace information
+   {
+      static const char* library = "Mathematical Expression Toolkit";
+      static const char* version = "2.7182818284590452353602874713526624977572470936999595749"
+                                   "669676277240766303535475945713821785251664274274663919320";
+      static const char* date    = "20200101";
+
+      static inline std::string data()
+      {
+         static const std::string info_str = std::string(library) +
+                                             std::string(" v") + std::string(version) +
+                                             std::string(" (") + date + std::string(")");
+         return info_str;
+      }
+
+   } // namespace information
+
+   #ifdef exprtk_debug
+   #undef exprtk_debug
+   #endif
+
+   #ifdef exprtk_error_location
+   #undef exprtk_error_location
+   #endif
+
+   #ifdef exprtk_disable_fallthrough_begin
+   #undef exprtk_disable_fallthrough_begin
+   #endif
+
+   #ifdef exprtk_disable_fallthrough_end
+   #undef exprtk_disable_fallthrough_end
+   #endif
+
+} // namespace exprtk
+
+#endif
index 614b983c40a45418fb6b986c367fd2fad2227ee9..ecca21d5216ed534ef433f0b95740cb14cd34f0b 100644 (file)
@@ -17,8 +17,9 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "globalsettings.hpp"
-#include "application.hpp"
+#include <boost/archive/text_iarchive.hpp>
+#include <boost/archive/text_oarchive.hpp>
+#include <boost/serialization/serialization.hpp>
 
 #include <QApplication>
 #include <QColor>
 #include <QStyle>
 #include <QtGlobal>
 
+#include "globalsettings.hpp"
+#include "application.hpp"
+
 using std::map;
 using std::pair;
 using std::string;
+using std::stringstream;
 using std::vector;
 
 namespace pv {
@@ -47,11 +52,13 @@ const QString GlobalSettings::Key_General_Language = "General_Language";
 const QString GlobalSettings::Key_General_Theme = "General_Theme";
 const QString GlobalSettings::Key_General_Style = "General_Style";
 const QString GlobalSettings::Key_General_SaveWithSetup = "General_SaveWithSetup";
+const QString GlobalSettings::Key_General_StartAllSessions = "General_StartAllSessions";
 const QString GlobalSettings::Key_View_ZoomToFitDuringAcq = "View_ZoomToFitDuringAcq";
 const QString GlobalSettings::Key_View_ZoomToFitAfterAcq = "View_ZoomToFitAfterAcq";
 const QString GlobalSettings::Key_View_TriggerIsZeroTime = "View_TriggerIsZeroTime";
 const QString GlobalSettings::Key_View_ColoredBG = "View_ColoredBG";
 const QString GlobalSettings::Key_View_StickyScrolling = "View_StickyScrolling";
+const QString GlobalSettings::Key_View_AllowVerticalDragging = "View_AllowVerticalDragging";
 const QString GlobalSettings::Key_View_ShowSamplingPoints = "View_ShowSamplingPoints";
 const QString GlobalSettings::Key_View_FillSignalHighAreas = "View_FillSignalHighAreas";
 const QString GlobalSettings::Key_View_FillSignalHighAreaColor = "View_FillSignalHighAreaColor";
@@ -60,6 +67,7 @@ const QString GlobalSettings::Key_View_ConversionThresholdDispMode = "View_Conve
 const QString GlobalSettings::Key_View_DefaultDivHeight = "View_DefaultDivHeight";
 const QString GlobalSettings::Key_View_DefaultLogicHeight = "View_DefaultLogicHeight";
 const QString GlobalSettings::Key_View_ShowHoverMarker = "View_ShowHoverMarker";
+const QString GlobalSettings::Key_View_KeepRulerItemSelected = "View_KeepRulerItemSelected";
 const QString GlobalSettings::Key_View_SnapDistance = "View_SnapDistance";
 const QString GlobalSettings::Key_View_CursorFillColor = "View_CursorFillColor";
 const QString GlobalSettings::Key_View_CursorShowFrequency = "View_CursorShowFrequency";
@@ -97,10 +105,11 @@ void GlobalSettings::set_defaults_where_needed()
 {
        if (!contains(Key_General_Language)) {
                // Determine and set default UI language
-               QString language = QLocale().uiLanguages().first();  // May return e.g. en-Latn-US
+               QString language = QLocale().uiLanguages().first();  // May return e.g. en-Latn-US  // clazy:exclude=detaching-temporary
                language = language.split("-").first();
 
                setValue(Key_General_Language, language);
+               apply_language();
        }
 
        // Use no theme by default
@@ -117,6 +126,10 @@ void GlobalSettings::set_defaults_where_needed()
        if (!contains(Key_View_ZoomToFitAfterAcq))
                setValue(Key_View_ZoomToFitAfterAcq, true);
 
+       // Allow vertical dragging by default
+       if (!contains(Key_View_AllowVerticalDragging))
+               setValue(Key_View_AllowVerticalDragging, true);
+
        // Enable colored trace backgrounds by default
        if (!contains(Key_View_ColoredBG))
                setValue(Key_View_ColoredBG, true);
@@ -140,6 +153,9 @@ void GlobalSettings::set_defaults_where_needed()
        if (!contains(Key_View_ShowHoverMarker))
                setValue(Key_View_ShowHoverMarker, true);
 
+       if (!contains(Key_View_KeepRulerItemSelected))
+               setValue(Key_View_KeepRulerItemSelected, false);
+
        if (!contains(Key_View_SnapDistance))
                setValue(Key_View_SnapDistance, 15);
 
@@ -318,7 +334,11 @@ void GlobalSettings::store_gvariant(QSettings &settings, GVariant *v)
                g_variant_get_size(v));
 
        settings.setValue("value", var_data);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       settings.setValue("type", (const char *)var_type_str);
+#else
        settings.setValue("type", var_type_str);
+#endif
 
        g_free(var_type_str);
 }
@@ -330,8 +350,11 @@ GVariant* GlobalSettings::restore_gvariant(QSettings &settings)
 
        QByteArray data = settings.value("value").toByteArray();
 
-       gpointer var_data = g_memdup((gconstpointer)data.constData(),
-               (guint)data.size());
+#if GLIB_CHECK_VERSION(2, 67, 3)  // See https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538
+       gpointer var_data = g_memdup2((gconstpointer)data.constData(), (gsize)data.size());
+#else
+       gpointer var_data = g_memdup((gconstpointer)data.constData(), (guint)data.size());
+#endif
 
        GVariant *value = g_variant_new_from_data(var_type, var_data,
                data.size(), false, g_free, var_data);
@@ -356,8 +379,11 @@ Glib::VariantBase GlobalSettings::restore_variantbase(QSettings &settings)
 
        QByteArray data = settings.value("value").toByteArray();
 
-       gpointer var_data = g_memdup((gconstpointer)data.constData(),
-               (guint)data.size());
+#if GLIB_CHECK_VERSION(2, 67, 3)  // See https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538
+       gpointer var_data = g_memdup2((gconstpointer)data.constData(), (gsize)data.size());
+#else
+       gpointer var_data = g_memdup((gconstpointer)data.constData(), (guint)data.size());
+#endif
 
        GVariant *value = g_variant_new_from_data(var_type, var_data,
                data.size(), false, g_free, var_data);
@@ -370,4 +396,28 @@ Glib::VariantBase GlobalSettings::restore_variantbase(QSettings &settings)
        return ret_val;
 }
 
+void GlobalSettings::store_timestamp(QSettings &settings, const char *name, const pv::util::Timestamp &ts)
+{
+       stringstream ss;
+       boost::archive::text_oarchive oa(ss);
+       oa << boost::serialization::make_nvp(name, ts);
+       settings.setValue(name, QString::fromStdString(ss.str()));
+}
+
+pv::util::Timestamp GlobalSettings::restore_timestamp(QSettings &settings, const char *name)
+{
+       util::Timestamp result;
+       stringstream ss;
+       ss << settings.value(name).toString().toStdString();
+
+       try {
+               boost::archive::text_iarchive ia(ss);
+               ia >> boost::serialization::make_nvp(name, result);
+       } catch (boost::archive::archive_exception&) {
+               qDebug() << "Could not restore setting" << name;
+       }
+
+       return result;
+}
+
 } // namespace pv
index adb9168a1fbc3be027db6f7ee16df0930b501de4..f6239a6b3d089f726cea54afd1bffaf3f211b14a 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_GLOBALSETTINGS_HPP
-#define PULSEVIEW_GLOBALSETTINGS_HPP
+#ifndef PULSEVIEW_PV_GLOBALSETTINGS_HPP
+#define PULSEVIEW_PV_GLOBALSETTINGS_HPP
 
 #include <map>
 
@@ -30,6 +30,8 @@
 #include <QString>
 #include <QVariant>
 
+#include "util.hpp"
+
 using std::map;
 using std::pair;
 using std::vector;
@@ -55,11 +57,13 @@ public:
        static const QString Key_General_Theme;
        static const QString Key_General_Style;
        static const QString Key_General_SaveWithSetup;
+       static const QString Key_General_StartAllSessions;
        static const QString Key_View_ZoomToFitDuringAcq;
        static const QString Key_View_ZoomToFitAfterAcq;
        static const QString Key_View_TriggerIsZeroTime;
        static const QString Key_View_ColoredBG;
        static const QString Key_View_StickyScrolling;
+       static const QString Key_View_AllowVerticalDragging;
        static const QString Key_View_ShowSamplingPoints;
        static const QString Key_View_FillSignalHighAreas;
        static const QString Key_View_FillSignalHighAreaColor;
@@ -68,6 +72,7 @@ public:
        static const QString Key_View_DefaultDivHeight;
        static const QString Key_View_DefaultLogicHeight;
        static const QString Key_View_ShowHoverMarker;
+       static const QString Key_View_KeepRulerItemSelected;
        static const QString Key_View_SnapDistance;
        static const QString Key_View_CursorFillColor;
        static const QString Key_View_CursorShowInterval;
@@ -122,13 +127,14 @@ public:
        void undo_tracked_changes();
 
        static void store_gvariant(QSettings &settings, GVariant *v);
-
        static GVariant* restore_gvariant(QSettings &settings);
 
        static void store_variantbase(QSettings &settings, Glib::VariantBase v);
-
        static Glib::VariantBase restore_variantbase(QSettings &settings);
 
+       static void store_timestamp(QSettings &settings, const char *name, const pv::util::Timestamp &ts);
+       static pv::util::Timestamp restore_timestamp(QSettings &settings, const char *name);
+
 private:
        static vector<GlobalSettingsInterface*> callbacks_;
 
@@ -143,4 +149,4 @@ private:
 
 } // namespace pv
 
-#endif // PULSEVIEW_GLOBALSETTINGS_HPP
+#endif // PULSEVIEW_PV_GLOBALSETTINGS_HPP
index e42a9e54e94add49cb46b34f3a5ab216650e6c3f..7dab545de93cd3989af8d72a69867e470a5b0753 100644 (file)
@@ -191,8 +191,13 @@ int Logging::log_srd(void *cb_data, int loglevel, const char *format, va_list ar
        char *text = g_strdup_vprintf(format, args);
 
        QString s = QString::fromUtf8(text);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+       for (QString& substring : s.split("\n", Qt::SkipEmptyParts))
+                       logging.log(substring, LogSource_srd);
+#else
        for (QString& substring : s.split("\n", QString::SkipEmptyParts))
                        logging.log(substring, LogSource_srd);
+#endif
        g_free(text);
 
        return SR_OK;
index ceace369f982158b26668ed38d4e836655e84f2d..8646175e2b32f7a6b7f0a7ec7233f07d136d77fb 100644 (file)
@@ -52,7 +52,8 @@
 
 #ifdef ENABLE_DECODE
 #include "subwindows/decoder_selector/subwindow.hpp"
-#include "views/decoder_output/view.hpp"
+#include "views/decoder_binary/view.hpp"
+#include "views/tabular_decoder/view.hpp"
 #endif
 
 #include <libsigrokcxx/libsigrokcxx.hpp>
@@ -78,6 +79,8 @@ MainWindow::MainWindow(DeviceManager &device_manager, QWidget *parent) :
 {
        setup_ui();
        restore_ui_settings();
+       connect(this, SIGNAL(session_error_raised(const QString, const QString)),
+               this, SLOT(on_session_error_raised(const QString, const QString)));
 }
 
 MainWindow::~MainWindow()
@@ -94,7 +97,7 @@ MainWindow::~MainWindow()
 void MainWindow::show_session_error(const QString text, const QString info_text)
 {
        // TODO Emulate noquote()
-       qDebug() << "Notifying user of session error:" << info_text;
+       qDebug() << "Notifying user of session error: " << text << "; " << info_text;
 
        QMessageBox msg;
        msg.setText(text + "\n\n" + info_text);
@@ -162,8 +165,10 @@ shared_ptr<views::ViewBase> MainWindow::add_view(views::ViewType type,
                // This view will be the main view if there's no main bar yet
                v = make_shared<views::trace::View>(session, (main_bar ? false : true), dock_main);
 #ifdef ENABLE_DECODE
-       if (type == views::ViewTypeDecoderOutput)
-               v = make_shared<views::decoder_output::View>(session, false, dock_main);
+       if (type == views::ViewTypeDecoderBinary)
+               v = make_shared<views::decoder_binary::View>(session, false, dock_main);
+       if (type == views::ViewTypeTabularDecoder)
+               v = make_shared<views::tabular_decoder::View>(session, false, dock_main);
 #endif
 
        if (!v)
@@ -190,6 +195,9 @@ shared_ptr<views::ViewBase> MainWindow::add_view(views::ViewType type,
                qobject_cast<views::ViewBase*>(v.get()),
                SLOT(trigger_event(int, util::Timestamp)));
 
+       connect(&session, SIGNAL(session_error_raised(const QString, const QString)),
+               this, SLOT(on_session_error_raised(const QString, const QString)));
+
        if (type == views::ViewTypeTrace) {
                views::trace::View *tv =
                        qobject_cast<views::trace::View*>(v.get());
@@ -320,8 +328,6 @@ shared_ptr<subwindows::SubWindowBase> MainWindow::add_subwindow(
        if (w->minimum_width() > 0)
                dock->setMinimumSize(w->minimum_width(), 0);
 
-       w->setFocus();
-
        return w;
 }
 
@@ -332,8 +338,8 @@ shared_ptr<Session> MainWindow::add_session()
 
        shared_ptr<Session> session = make_shared<Session>(device_manager_, name);
 
-       connect(session.get(), SIGNAL(add_view(views::ViewType, Session*)),
-               this, SLOT(on_add_view(views::ViewType, Session*)));
+       connect(session.get(), SIGNAL(add_view(ViewType, Session*)),
+               this, SLOT(on_add_view(ViewType, Session*)));
        connect(session.get(), SIGNAL(name_changed()),
                this, SLOT(on_session_name_changed()));
        connect(session.get(), SIGNAL(device_changed()),
@@ -546,10 +552,14 @@ void MainWindow::setup_ui()
        session_selector_.setCornerWidget(static_tab_widget_, Qt::TopLeftCorner);
        session_selector_.setTabsClosable(true);
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       close_application_shortcut_ = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this, SLOT(close()));
+       close_current_tab_shortcut_ = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_W), this, SLOT(on_close_current_tab()));
+#else
        close_application_shortcut_ = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this, SLOT(close()));
-       close_application_shortcut_->setAutoRepeat(false);
-
        close_current_tab_shortcut_ = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), this, SLOT(on_close_current_tab()));
+#endif
+       close_application_shortcut_->setAutoRepeat(false);
 
        connect(new_session_button_, SIGNAL(clicked(bool)),
                this, SLOT(on_new_session_clicked()));
@@ -571,10 +581,16 @@ void MainWindow::setup_ui()
 
 void MainWindow::update_acq_button(Session *session)
 {
-       int state = session->get_capture_state();
+       int state;
+       QString run_caption;
 
-       const QString run_caption =
-               session->using_file_device() ? tr("Reload") : tr("Run");
+       if (session) {
+               state = session->get_capture_state();
+               run_caption = session->using_file_device() ? tr("Reload") : tr("Run");
+       } else {
+               state = Session::Stopped;
+               run_caption = tr("Run");
+       }
 
        const QIcon *icons[] = {&icon_grey_, &icon_red_, &icon_green_};
        run_stop_button_->setIcon(*icons[state]);
@@ -652,6 +668,54 @@ bool MainWindow::restoreState(const QByteArray &state, int version)
        return false;
 }
 
+void MainWindow::on_run_stop_clicked()
+{
+       GlobalSettings settings;
+       bool all_sessions = settings.value(GlobalSettings::Key_General_StartAllSessions).toBool();
+
+       if (all_sessions)
+       {
+               vector< shared_ptr<Session> > hw_sessions;
+
+               // Make a list of all sessions where a hardware device is used
+               for (const shared_ptr<Session>& s : sessions_) {
+                       shared_ptr<devices::HardwareDevice> hw_device =
+                                       dynamic_pointer_cast< devices::HardwareDevice >(s->device());
+                       if (!hw_device)
+                               continue;
+                       hw_sessions.push_back(s);
+               }
+
+               // Stop all acquisitions if there are any running ones, start all otherwise
+               bool any_running = any_of(hw_sessions.begin(), hw_sessions.end(),
+                               [](const shared_ptr<Session> &s)
+                               { return (s->get_capture_state() == Session::AwaitingTrigger) ||
+                                               (s->get_capture_state() == Session::Running); });
+
+               for (shared_ptr<Session> s : hw_sessions)
+                       if (any_running)
+                               s->stop_capture();
+                       else
+                               s->start_capture([&](QString message) {Q_EMIT session_error_raised("Capture failed", message);});
+       } else {
+
+               shared_ptr<Session> session = last_focused_session_;
+
+               if (!session)
+                       return;
+
+               switch (session->get_capture_state()) {
+               case Session::Stopped:
+                       session->start_capture([&](QString message) {Q_EMIT session_error_raised("Capture failed", message);});
+                       break;
+               case Session::AwaitingTrigger:
+               case Session::Running:
+                       session->stop_capture();
+                       break;
+               }
+       }
+}
+
 void MainWindow::on_add_view(views::ViewType type, Session *session)
 {
        // We get a pointer and need a reference
@@ -702,25 +766,6 @@ void MainWindow::on_new_session_clicked()
        add_session();
 }
 
-void MainWindow::on_run_stop_clicked()
-{
-       shared_ptr<Session> session = last_focused_session_;
-
-       if (!session)
-               return;
-
-       switch (session->get_capture_state()) {
-       case Session::Stopped:
-               session->start_capture([&](QString message) {
-                       show_session_error("Capture failed", message); });
-               break;
-       case Session::AwaitingTrigger:
-       case Session::Running:
-               session->stop_capture();
-               break;
-       }
-}
-
 void MainWindow::on_settings_clicked()
 {
        dialogs::Settings dlg(device_manager_);
@@ -849,6 +894,9 @@ void MainWindow::on_tab_close_requested(int index)
                tr("This session contains unsaved data. Close it anyway?"),
                QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes))
                remove_session(session);
+
+       if (sessions_.empty())
+               update_acq_button(nullptr);
 }
 
 void MainWindow::on_show_decoder_selector(Session *session)
@@ -871,6 +919,8 @@ void MainWindow::on_show_decoder_selector(Session *session)
        for (shared_ptr<Session>& s : sessions_)
                if (s.get() == session)
                        add_subwindow(subwindows::SubWindowTypeDecoderSelector, *s);
+#else
+       (void)session;
 #endif
 }
 
@@ -934,4 +984,8 @@ void MainWindow::on_close_current_tab()
        on_tab_close_requested(tab);
 }
 
+void MainWindow::on_session_error_raised(const QString text, const QString info_text) {
+       MainWindow::show_session_error(text, info_text);
+}
+
 } // namespace pv
index 522ab1c0478ea7d1def85a90ada942733735f182..493e3a55cc0dec32b3ca6e1471138dd0a0bdeac7 100644 (file)
@@ -31,7 +31,6 @@
 
 #include "session.hpp"
 #include "subwindows/subwindowbase.hpp"
-#include "views/viewbase.hpp"
 
 using std::list;
 using std::map;
@@ -53,6 +52,7 @@ class MainBar;
 
 namespace view {
 class View;
+class ViewBase;
 }
 
 namespace widgets {
@@ -61,6 +61,9 @@ class DecoderMenu;
 #endif
 }
 
+using pv::views::ViewBase;
+using pv::views::ViewType;
+
 class MainWindow : public QMainWindow
 {
        Q_OBJECT
@@ -78,9 +81,9 @@ public:
 
        shared_ptr<views::ViewBase> get_active_view() const;
 
-       shared_ptr<views::ViewBase> add_view(views::ViewType type, Session &session);
+       shared_ptr<views::ViewBase> add_view(ViewType type, Session &session);
 
-       void remove_view(shared_ptr<views::ViewBase> view);
+       void remove_view(shared_ptr<ViewBase> view);
 
        shared_ptr<subwindows::SubWindowBase> add_subwindow(
                subwindows::SubWindowType type, Session &session);
@@ -112,14 +115,20 @@ private:
 
        virtual bool restoreState(const QByteArray &state, int version = 0);
 
+Q_SIGNALS:
+       void session_error_raised(const QString text, const QString info_text);
+
+public Q_SLOTS:
+       void on_run_stop_clicked();
+       void on_session_error_raised(const QString text, const QString info_text);
+
 private Q_SLOTS:
-       void on_add_view(views::ViewType type, Session *session);
+       void on_add_view(ViewType type, Session *session);
 
        void on_focus_changed();
        void on_focused_session_changed(shared_ptr<Session> session);
 
        void on_new_session_clicked();
-       void on_run_stop_clicked();
        void on_settings_clicked();
 
        void on_session_name_changed();
diff --git a/pv/metadata_obj.cpp b/pv/metadata_obj.cpp
new file mode 100644 (file)
index 0000000..3788dbc
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "metadata_obj.hpp"
+
+
+namespace pv {
+
+const char* MetadataObjectNames[MetadataObjectTypeCount] = {
+       "main_view_range",
+       "selection",
+       "time_marker",
+       "mouse_pos"
+};
+
+const char* MetadataValueNames[MetadataValueTypeCount] = {
+       "start_sample",
+       "end_sample"
+       "text"
+};
+
+
+void MetadataObjObserverInterface::on_metadata_object_created(MetadataObject* obj)
+{
+       (void)obj;
+}
+
+void MetadataObjObserverInterface::on_metadata_object_deleted(MetadataObject* obj)
+{
+       (void)obj;
+}
+
+void MetadataObjObserverInterface::on_metadata_object_changed(MetadataObject* obj,
+       MetadataValueType value_type)
+{
+       (void)obj;
+       (void)value_type;
+}
+
+
+MetadataObject::MetadataObject(MetadataObjManager* obj_manager, uint32_t obj_id,
+       MetadataObjectType obj_type) :
+       obj_manager_(obj_manager),
+       id_(obj_id),
+       type_(obj_type)
+{
+       // Make sure we accept all value type indices
+       values_.resize(MetadataValueTypeCount);
+}
+
+uint32_t MetadataObject::id() const
+{
+       return id_;
+}
+
+MetadataObjectType MetadataObject::type() const
+{
+       return type_;
+}
+
+void MetadataObject::set_value(MetadataValueType value_type, const QVariant& value)
+{
+       values_.at((uint8_t)value_type) = value;
+       obj_manager_->notify_observers(this, value_type);
+}
+
+QVariant MetadataObject::value(MetadataValueType value_type) const
+{
+       return values_.at((uint8_t)value_type);
+}
+
+
+MetadataObject* MetadataObjManager::create_object(MetadataObjectType obj_type)
+{
+       // Note: This function is not reentrant as race conditions between
+       // emplace_back() and back() may occur
+
+       objects_.emplace_back(this, objects_.size(), obj_type);
+       MetadataObject* obj = &(objects_.back());
+
+       for (MetadataObjObserverInterface *cb : callbacks_)
+               cb->on_metadata_object_created(obj);
+
+       return obj;
+}
+
+void MetadataObjManager::delete_object(uint32_t obj_id)
+{
+       for (MetadataObjObserverInterface *cb : callbacks_)
+               cb->on_metadata_object_deleted(&(objects_.at(obj_id)));
+
+       objects_.erase(std::remove_if(objects_.begin(), objects_.end(),
+               [&](MetadataObject obj) { return obj.id() == obj_id; }),
+               objects_.end());
+}
+
+MetadataObject* MetadataObjManager::find_object_by_type(MetadataObjectType obj_type)
+{
+       for (MetadataObject& obj : objects_)
+               if (obj.type() == obj_type)
+                       return &obj;
+
+       return nullptr;
+}
+
+MetadataObject* MetadataObjManager::object(uint32_t obj_id)
+{
+       return &(objects_.at(obj_id));
+}
+
+void MetadataObjManager::add_observer(MetadataObjObserverInterface *cb)
+{
+       callbacks_.emplace_back(cb);
+}
+
+void MetadataObjManager::remove_observer(MetadataObjObserverInterface *cb)
+{
+       for (auto cb_it = callbacks_.begin(); cb_it != callbacks_.end(); cb_it++)
+               if (*cb_it == cb) {
+                       callbacks_.erase(cb_it);
+                       break;
+               }
+}
+
+void MetadataObjManager::save_objects(QSettings &settings) const
+{
+       (void)settings;
+}
+
+void MetadataObjManager::restore_objects(QSettings &settings)
+{
+       (void)settings;
+}
+
+void MetadataObjManager::notify_observers(MetadataObject* obj,
+       MetadataValueType changed_value)
+{
+       for (MetadataObjObserverInterface *cb : callbacks_)
+               cb->on_metadata_object_changed(obj, changed_value);
+}
+
+} // namespace pv
diff --git a/pv/metadata_obj.hpp b/pv/metadata_obj.hpp
new file mode 100644 (file)
index 0000000..6837fef
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_METADATA_OBJ_HPP
+#define PULSEVIEW_PV_METADATA_OBJ_HPP
+
+#include <deque>
+#include <vector>
+
+#include <QObject>
+#include <QSettings>
+#include <QString>
+#include <QVariant>
+
+using std::deque;
+using std::vector;
+
+namespace pv {
+
+
+// When adding an entry here, don't forget to update MetadataObjectNames as well
+enum MetadataObjectType {
+       MetadataObjMainViewRange,
+       MetadataObjSelection,
+       MetadataObjTimeMarker,
+    // --- Types below will not be saved/restored ---
+       MetadataObjMousePos,
+       MetadataObjectTypeCount  // Indicates how many metadata object types there are, must always be last
+};
+
+// When adding an entry here, don't forget to update MetadataValueNames as well
+enum MetadataValueType {
+       MetadataValueStartSample,  // int64_t / qlonglong
+       MetadataValueEndSample,    // int64_t / qlonglong
+       MetadataValueText,
+       MetadataValueTypeCount  // Indicates how many metadata value types there are, must always be last
+};
+
+extern const char* MetadataObjectNames[MetadataObjectTypeCount];
+extern const char* MetadataValueNames[MetadataValueTypeCount];
+
+
+class MetadataObjManager;
+class MetadataObject;
+
+
+class MetadataObjObserverInterface
+{
+public:
+       virtual void on_metadata_object_created(MetadataObject* obj);
+       virtual void on_metadata_object_deleted(MetadataObject* obj);
+       virtual void on_metadata_object_changed(MetadataObject* obj,
+               MetadataValueType value_type);
+};
+
+
+class MetadataObject
+{
+public:
+       MetadataObject(MetadataObjManager* obj_manager, uint32_t obj_id, MetadataObjectType obj_type);
+       virtual ~MetadataObject() = default;
+
+       virtual uint32_t id() const;
+       virtual MetadataObjectType type() const;
+
+       virtual void set_value(MetadataValueType value_type, const QVariant& value);
+       virtual QVariant value(MetadataValueType value_type) const;
+private:
+       MetadataObjManager* obj_manager_;
+       uint32_t id_;
+       MetadataObjectType type_;
+       vector<QVariant> values_;
+};
+
+
+class MetadataObjManager : public QObject
+{
+       Q_OBJECT
+
+public:
+       MetadataObject* create_object(MetadataObjectType obj_type);
+       void delete_object(uint32_t obj_id);
+       MetadataObject* find_object_by_type(MetadataObjectType obj_type);
+       MetadataObject* object(uint32_t obj_id);
+
+       void add_observer(MetadataObjObserverInterface *cb);
+       void remove_observer(MetadataObjObserverInterface *cb);
+
+       void save_objects(QSettings &settings) const;
+       void restore_objects(QSettings &settings);
+
+       void notify_observers(MetadataObject* obj, MetadataValueType changed_value);
+
+private:
+       vector<MetadataObjObserverInterface*> callbacks_;
+       deque<MetadataObject> objects_;
+};
+
+
+} // namespace pv
+
+#endif // PULSEVIEW_PV_METADATA_OBJ_HPP
index b7fcad4584bdd1a5f82042c4955afa84648f1d3d..ce2e8e98e08e0f7f3e88356b68ddce4aad1d4c66 100644 (file)
@@ -158,8 +158,13 @@ Channels::Channels(Session &session, QWidget *parent) :
        layout_.addRow(&filter_buttons_bar_);
 
        // Connect the check-box signal mapper
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(&check_box_mapper_, SIGNAL(mappedObject(QObject*)),
+               this, SLOT(on_channel_checked(QObject*)));
+#else
        connect(&check_box_mapper_, SIGNAL(mapped(QWidget*)),
                this, SLOT(on_channel_checked(QWidget*)));
+#endif
 }
 
 void Channels::set_all_channels(bool set)
@@ -272,14 +277,14 @@ void Channels::populate_group(shared_ptr<ChannelGroup> group,
                        if (sigs.size() > 8) {
                                QPushButton *row_enable_button = new QPushButton(tr("All"), this);
                                grid->addWidget(row_enable_button, row, 8);
-                               connect(row_enable_button, &QPushButton::clicked,
-                                               [this_row]() {
-                                       for (QCheckBox *box : this_row)
-                                               box->setChecked(true);
+                               connect(row_enable_button, &QPushButton::clicked, row_enable_button,
+                                       [this_row]() {
+                                               for (QCheckBox *box : this_row)
+                                                       box->setChecked(true);
                                        });
 
                                QPushButton *row_disable_button = new QPushButton(tr("None"), this);
-                               connect(row_disable_button, &QPushButton::clicked,
+                               connect(row_disable_button, &QPushButton::clicked, row_disable_button,
                                                [this_row]() {
                                        for (QCheckBox *box : this_row)
                                                box->setChecked(false);
@@ -302,14 +307,16 @@ void Channels::populate_group(shared_ptr<ChannelGroup> group,
                group_layout->addWidget(btn_enable_all);
                group_layout->addWidget(btn_disable_all);
 
-               connect(btn_enable_all, &QPushButton::clicked, [group_checkboxes](){
-                       for (QCheckBox *box: group_checkboxes)
-                               box->setChecked(true);
+               connect(btn_enable_all, &QPushButton::clicked, btn_enable_all,
+                       [group_checkboxes](){
+                               for (QCheckBox *box: group_checkboxes)
+                                       box->setChecked(true);
                        });
 
-               connect(btn_disable_all, &QPushButton::clicked, [group_checkboxes](){
-                       for (QCheckBox *box: group_checkboxes)
-                               box->setChecked(false);
+               connect(btn_disable_all, &QPushButton::clicked, btn_disable_all,
+                       [group_checkboxes](){
+                               for (QCheckBox *box: group_checkboxes)
+                                       box->setChecked(false);
                        });
        }
 
@@ -331,11 +338,9 @@ void Channels::showEvent(QShowEvent *event)
        for (auto& entry : device->channel_groups()) {
                const shared_ptr<ChannelGroup> group = entry.second;
 
-               try {
-                       QLabel* label = group_label_map_.at(group);
+               if (group_label_map_.count(group) > 0) {
+                       QLabel* label = group_label_map_[group];
                        label->setText(QString("<h3>%1</h3>").arg(group->name().c_str()));
-               } catch (out_of_range&) {
-                       // Do nothing
                }
        }
 
@@ -354,7 +359,11 @@ void Channels::showEvent(QShowEvent *event)
        updating_channels_ = false;
 }
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+void Channels::on_channel_checked(QObject *widget)
+#else
 void Channels::on_channel_checked(QWidget *widget)
+#endif
 {
        if (updating_channels_)
                return;
index 972549a8d1c9fd2f65b63e997eb37b1d6440459f..66d284b00dfcefcadd003a1b7bbd73b021b6f274 100644 (file)
@@ -82,7 +82,11 @@ private:
        void showEvent(QShowEvent *event);
 
 private Q_SLOTS:
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       void on_channel_checked(QObject *widget);
+#else
        void on_channel_checked(QWidget *widget);
+#endif
 
        void enable_all_channels();
        void disable_all_channels();
@@ -103,8 +107,7 @@ private:
        bool updating_channels_;
 
        vector< shared_ptr<pv::binding::Device> > group_bindings_;
-       map< QCheckBox*, shared_ptr<pv::data::SignalBase> >
-               check_box_signal_map_;
+       map< QCheckBox*, shared_ptr<pv::data::SignalBase> > check_box_signal_map_;
        map< shared_ptr<sigrok::ChannelGroup>, QLabel*> group_label_map_;
 
        QGridLayout filter_buttons_bar_;
index fd5744fe3830f0d56779b981afca4e85aae2e4ab..310eec6f06444af6905141568e1027b90b864931 100644 (file)
@@ -29,7 +29,7 @@ namespace prop {
 
 class Bool : public Property
 {
-       Q_OBJECT;
+       Q_OBJECT
 
 public:
        Bool(QString name, QString desc, Getter getter, Setter setter);
index d62c901cf4c70d2c3848b3a3875d9d088596b794..8243f32d0a1b63512d364ef4aea227c05d0bce35 100644 (file)
@@ -153,7 +153,7 @@ QWidget* Enum::get_widget(QWidget *parent, bool auto_commit)
                selector_ = new QComboBox(parent);
                for (unsigned int i = 0; i < values_.size(); i++) {
                        const pair<Glib::VariantBase, QString> &v = values_[i];
-                       selector_->addItem(v.second, qVariantFromValue(v.first));
+                       selector_->addItem(v.second, QVariant::fromValue(v.first));
                }
 
                update_widget();
index 1434e7e9e0419aa301b03b9d21febcd2f0f66a7a..8050bcdf0f0897acea109125ad36d3a8651e2365 100644 (file)
@@ -41,7 +41,7 @@ namespace prop {
 
 class Enum : public Property
 {
-       Q_OBJECT;
+       Q_OBJECT
 
 public:
        Enum(QString name, QString desc,
index 3f29951b3d997b7b5b8a299fd34c3a0bf0d6db40..c1b2d22a6be97b4692d8254c2185e1cd076c72ea 100644 (file)
@@ -35,14 +35,12 @@ using std::pair;
 namespace pv {
 namespace prop {
 
-Int::Int(QString name,
-       QString desc,
-       QString suffix,
-       optional< pair<int64_t, int64_t> > range,
-       Getter getter,
-       Setter setter) :
+Int::Int(QString name, QString desc, QString suffix,
+       optional< pair<int64_t, int64_t> > range, Getter getter, Setter setter,
+       QString special_value_text) :
        Property(name, desc, getter, setter),
        suffix_(suffix),
+       special_value_text_(special_value_text),
        range_(range),
        spin_box_(nullptr)
 {
@@ -72,6 +70,7 @@ QWidget* Int::get_widget(QWidget *parent, bool auto_commit)
 
        spin_box_ = new QSpinBox(parent);
        spin_box_->setSuffix(suffix_);
+       spin_box_->setSpecialValueText(special_value_text_);
 
        const GVariantType *const type = g_variant_get_type(value);
        assert(type);
index f6b7c2d1f1642d64a55a575fbcd7100618cd3c46..1f6a64ca503fddf069090f9c9e03e2eaa44b2bc6 100644 (file)
@@ -35,12 +35,12 @@ namespace prop {
 
 class Int : public Property
 {
-       Q_OBJECT;
+       Q_OBJECT
 
 public:
        Int(QString name, QString desc, QString suffix,
                boost::optional< pair<int64_t, int64_t> > range,
-               Getter getter, Setter setter);
+               Getter getter, Setter setter, QString special_value_text = "");
 
        virtual ~Int() = default;
 
@@ -53,7 +53,7 @@ private Q_SLOTS:
        void on_value_changed(int);
 
 private:
-       const QString suffix_;
+       const QString suffix_, special_value_text_;
        const boost::optional< pair<int64_t, int64_t> > range_;
 
        Glib::VariantBase value_;
index 4ce352e11e5dacb2aaacfe4e726e3e94875daaef..da1c260e81302185e6c4570a8dc7fcf8e1bbde31 100644 (file)
@@ -40,7 +40,7 @@ namespace prop {
 
 class Property : public QObject
 {
-       Q_OBJECT;
+       Q_OBJECT
 
 public:
        typedef function<Glib::VariantBase ()> Getter;
index 8282cb7f5928616fb2b64da1b5420c6439948458..3e303568309e2449f1b1c564fb325770d9c60465 100644 (file)
@@ -29,7 +29,7 @@ namespace prop {
 
 class String : public Property
 {
-       Q_OBJECT;
+       Q_OBJECT
 
 public:
        String(QString name, QString desc, Getter getter, Setter setter);
index 589ec333a24c35bfbe02f47b8e393970f4370f42..b4ecc6a87c2f4886c3a812df1191cea08542e4f8 100644 (file)
@@ -17,9 +17,6 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <QDebug>
-#include <QFileInfo>
-
 #include <cassert>
 #include <memory>
 #include <mutex>
 
 #include <sys/stat.h>
 
+#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+
 #include "devicemanager.hpp"
 #include "mainwindow.hpp"
 #include "session.hpp"
+#include "util.hpp"
 
 #include "data/analog.hpp"
 #include "data/analogsegment.hpp"
 #include "data/decode/decoder.hpp"
 #include "data/logic.hpp"
 #include "data/logicsegment.hpp"
+#include "data/mathsignal.hpp"
 #include "data/signalbase.hpp"
 
 #include "devices/hardwaredevice.hpp"
@@ -66,8 +69,8 @@ using std::bad_alloc;
 using std::dynamic_pointer_cast;
 using std::find_if;
 using std::function;
-using std::lock_guard;
 using std::list;
+using std::lock_guard;
 using std::make_pair;
 using std::make_shared;
 using std::map;
@@ -83,7 +86,6 @@ using std::string;
 using std::unique_lock;
 #endif
 using std::unique_ptr;
-using std::unordered_set;
 using std::vector;
 
 using sigrok::Analog;
@@ -105,6 +107,8 @@ using Gst::ElementFactory;
 using Gst::Pipeline;
 #endif
 
+using pv::data::SignalGroup;
+using pv::util::Timestamp;
 using pv::views::trace::Signal;
 using pv::views::trace::AnalogSignal;
 using pv::views::trace::LogicSignal;
@@ -114,6 +118,7 @@ namespace pv {
 shared_ptr<sigrok::Context> Session::sr_context;
 
 Session::Session(DeviceManager &device_manager, QString name) :
+       shutting_down_(false),
        device_manager_(device_manager),
        default_name_(name),
        name_(name),
@@ -121,12 +126,21 @@ Session::Session(DeviceManager &device_manager, QString name) :
        cur_samplerate_(0),
        data_saved_(true)
 {
+       // Use this name also for the QObject instance
+       setObjectName(name_);
 }
 
 Session::~Session()
 {
+       shutting_down_ = true;
+
        // Stop and join to the thread
        stop_capture();
+
+       for (SignalGroup* group : signal_groups_) {
+               group->clear();
+               delete group;
+       }
 }
 
 DeviceManager& Session::device_manager()
@@ -163,10 +177,23 @@ void Session::set_name(QString name)
 
        name_ = name;
 
+       // Use this name also for the QObject instance
+       setObjectName(name_);
+
        name_changed();
 }
 
-const list< shared_ptr<views::ViewBase> > Session::views() const
+QString Session::save_path() const
+{
+       return save_path_;
+}
+
+void Session::set_save_path(QString path)
+{
+       save_path_ = path;
+}
+
+const vector< shared_ptr<views::ViewBase> > Session::views() const
 {
        return views_;
 }
@@ -193,42 +220,89 @@ bool Session::data_saved() const
 
 void Session::save_setup(QSettings &settings) const
 {
-       int decode_signals = 0, views = 0;
+       int i;
+       int decode_signal_count = 0;
+       int gen_signal_count = 0;
 
        // Save channels and decoders
        for (const shared_ptr<data::SignalBase>& base : signalbases_) {
 #ifdef ENABLE_DECODE
                if (base->is_decode_signal()) {
-                       settings.beginGroup("decode_signal" + QString::number(decode_signals++));
+                       settings.beginGroup("decode_signal" + QString::number(decode_signal_count++));
                        base->save_settings(settings);
                        settings.endGroup();
                } else
 #endif
-               {
+               if (base->is_generated()) {
+                       settings.beginGroup("generated_signal" + QString::number(gen_signal_count++));
+                       settings.setValue("type", base->type());
+                       base->save_settings(settings);
+                       settings.endGroup();
+               } else {
                        settings.beginGroup(base->internal_name());
                        base->save_settings(settings);
                        settings.endGroup();
                }
        }
 
-       settings.setValue("decode_signals", decode_signals);
+       settings.setValue("decode_signals", decode_signal_count);
+       settings.setValue("generated_signals", gen_signal_count);
 
        // Save view states and their signal settings
        // Note: main_view must be saved as view0
-       settings.beginGroup("view" + QString::number(views++));
+       i = 0;
+       settings.beginGroup("view" + QString::number(i++));
        main_view_->save_settings(settings);
        settings.endGroup();
 
        for (const shared_ptr<views::ViewBase>& view : views_) {
                if (view != main_view_) {
-                       settings.beginGroup("view" + QString::number(views++));
+                       settings.beginGroup("view" + QString::number(i++));
                        settings.setValue("type", view->get_type());
                        view->save_settings(settings);
                        settings.endGroup();
                }
        }
 
-       settings.setValue("views", views);
+       settings.setValue("views", i);
+
+       int view_id = 0;
+       i = 0;
+       for (const shared_ptr<views::ViewBase>& vb : views_) {
+               shared_ptr<views::trace::View> tv = dynamic_pointer_cast<views::trace::View>(vb);
+               if (tv) {
+                       for (const shared_ptr<views::trace::TimeItem>& time_item : tv->time_items()) {
+
+                               const shared_ptr<views::trace::Flag> flag =
+                                       dynamic_pointer_cast<views::trace::Flag>(time_item);
+                               if (flag) {
+                                       if (!flag->enabled())
+                                               continue;
+
+                                       settings.beginGroup("meta_obj" + QString::number(i++));
+                                       settings.setValue("type", "time_marker");
+                                       settings.setValue("assoc_view", view_id);
+                                       GlobalSettings::store_timestamp(settings, "time", flag->time());
+                                       settings.setValue("text", flag->get_text());
+                                       settings.endGroup();
+                               }
+                       }
+
+                       if (tv->cursors_shown()) {
+                               settings.beginGroup("meta_obj" + QString::number(i++));
+                               settings.setValue("type", "selection");
+                               settings.setValue("assoc_view", view_id);
+                               const shared_ptr<views::trace::CursorPair> cp = tv->cursors();
+                               GlobalSettings::store_timestamp(settings, "start_time", cp->first()->time());
+                               GlobalSettings::store_timestamp(settings, "end_time", cp->second()->time());
+                               settings.endGroup();
+                       }
+               }
+
+               view_id++;
+       }
+
+       settings.setValue("meta_objs", i);
 }
 
 void Session::save_settings(QSettings &settings) const
@@ -263,25 +337,36 @@ void Session::save_settings(QSettings &settings) const
                        settings.endGroup();
                }
 
-               shared_ptr<devices::SessionFile> sessionfile_device =
-                       dynamic_pointer_cast<devices::SessionFile>(device_);
-
-               if (sessionfile_device) {
+               // Having saved the data to srzip overrides the current device. This is
+               // a crappy hack around the fact that saving e.g. an imported file to
+               // srzip would require changing the underlying libsigrok device
+               if (!save_path_.isEmpty()) {
+                       QFileInfo fi = QFileInfo(QDir(save_path_), name_);
                        settings.setValue("device_type", "sessionfile");
                        settings.beginGroup("device");
-                       settings.setValue("filename", QString::fromStdString(
-                               sessionfile_device->full_name()));
+                       settings.setValue("filename", fi.absoluteFilePath());
                        settings.endGroup();
-               }
+               } else {
+                       shared_ptr<devices::SessionFile> sessionfile_device =
+                               dynamic_pointer_cast<devices::SessionFile>(device_);
+
+                       if (sessionfile_device) {
+                               settings.setValue("device_type", "sessionfile");
+                               settings.beginGroup("device");
+                               settings.setValue("filename", QString::fromStdString(
+                                       sessionfile_device->full_name()));
+                               settings.endGroup();
+                       }
 
-               shared_ptr<devices::InputFile> inputfile_device =
-                       dynamic_pointer_cast<devices::InputFile>(device_);
+                       shared_ptr<devices::InputFile> inputfile_device =
+                               dynamic_pointer_cast<devices::InputFile>(device_);
 
-               if (inputfile_device) {
-                       settings.setValue("device_type", "inputfile");
-                       settings.beginGroup("device");
-                       inputfile_device->save_meta_to_settings(settings);
-                       settings.endGroup();
+                       if (inputfile_device) {
+                               settings.setValue("device_type", "inputfile");
+                               settings.beginGroup("device");
+                               inputfile_device->save_meta_to_settings(settings);
+                               settings.endGroup();
+                       }
                }
 
                save_setup(settings);
@@ -297,11 +382,34 @@ void Session::restore_setup(QSettings &settings)
                settings.endGroup();
        }
 
+       // Restore generated signals
+       int gen_signal_count = settings.value("generated_signals").toInt();
+
+       for (int i = 0; i < gen_signal_count; i++) {
+               settings.beginGroup("generated_signal" + QString::number(i));
+               SignalBase::ChannelType type = (SignalBase::ChannelType)settings.value("type").toInt();
+               shared_ptr<data::SignalBase> signal;
+
+               if (type == SignalBase::MathChannel)
+                       signal = make_shared<data::MathSignal>(*this);
+               else
+                       qWarning() << tr("Can't restore generated signal of unknown type %1 (%2)") \
+                               .arg((int)type) \
+                               .arg(settings.value("name").toString());
+
+               if (signal) {
+                       add_generated_signal(signal);
+                       signal->restore_settings(settings);
+               }
+
+               settings.endGroup();
+       }
+
        // Restore decoders
 #ifdef ENABLE_DECODE
-       int decode_signals = settings.value("decode_signals").toInt();
+       int decode_signal_count = settings.value("decode_signals").toInt();
 
-       for (int i = 0; i < decode_signals; i++) {
+       for (int i = 0; i < decode_signal_count; i++) {
                settings.beginGroup("decode_signal" + QString::number(i));
                shared_ptr<data::DecodeSignal> signal = add_decode_signal();
                signal->restore_settings(settings);
@@ -324,13 +432,45 @@ void Session::restore_setup(QSettings &settings)
 
                settings.endGroup();
        }
+
+       // Restore meta objects like markers and cursors
+       int meta_objs = settings.value("meta_objs").toInt();
+
+       for (int i = 0; i < meta_objs; i++) {
+               settings.beginGroup("meta_obj" + QString::number(i));
+
+               shared_ptr<views::ViewBase> vb;
+               shared_ptr<views::trace::View> tv;
+               if (settings.contains("assoc_view"))
+                       vb = views_.at(settings.value("assoc_view").toInt());
+
+               if (vb)
+                       tv = dynamic_pointer_cast<views::trace::View>(vb);
+
+               const QString type = settings.value("type").toString();
+
+               if ((type == "time_marker") && tv) {
+                       Timestamp ts = GlobalSettings::restore_timestamp(settings, "time");
+                       shared_ptr<views::trace::Flag> flag = tv->add_flag(ts);
+                       flag->set_text(settings.value("text").toString());
+               }
+
+               if ((type == "selection") && tv) {
+                       Timestamp start = GlobalSettings::restore_timestamp(settings, "start_time");
+                       Timestamp end = GlobalSettings::restore_timestamp(settings, "end_time");
+                       tv->set_cursors(start, end);
+                       tv->show_cursors();
+               }
+
+               settings.endGroup();
+       }
 }
 
 void Session::restore_settings(QSettings &settings)
 {
        shared_ptr<devices::Device> device;
 
-       QString device_type = settings.value("device_type").toString();
+       const QString device_type = settings.value("device_type").toString();
 
        if (device_type == "hardware") {
                map<string, string> dev_info;
@@ -361,18 +501,21 @@ void Session::restore_settings(QSettings &settings)
                        set_device(device);
 
                settings.endGroup();
+
+               if (device)
+                       restore_setup(settings);
        }
 
+       QString filename;
        if ((device_type == "sessionfile") || (device_type == "inputfile")) {
                if (device_type == "sessionfile") {
                        settings.beginGroup("device");
-                       QString filename = settings.value("filename").toString();
+                       filename = settings.value("filename").toString();
                        settings.endGroup();
 
-                       if (QFileInfo(filename).isReadable()) {
+                       if (QFileInfo(filename).isReadable())
                                device = make_shared<devices::SessionFile>(device_manager_.context(),
                                        filename.toStdString());
-                       }
                }
 
                if (device_type == "inputfile") {
@@ -382,8 +525,10 @@ void Session::restore_settings(QSettings &settings)
                        settings.endGroup();
                }
 
+
                if (device) {
                        set_device(device);
+                       restore_setup(settings);
 
                        start_capture([](QString infoMessage) {
                                // TODO Emulate noquote()
@@ -391,11 +536,16 @@ void Session::restore_settings(QSettings &settings)
 
                        set_name(QString::fromStdString(
                                dynamic_pointer_cast<devices::File>(device)->display_name(device_manager_)));
+
+                       if (!filename.isEmpty()) {
+                               // Only set the save path if we load an srzip file
+                               if (device_type == "sessionfile")
+                                       set_save_path(QFileInfo(filename).absolutePath());
+
+                               set_name(QFileInfo(filename).fileName());
+                       }
                }
        }
-
-       if (device)
-               restore_setup(settings);
 }
 
 void Session::select_device(shared_ptr<devices::Device> device)
@@ -428,14 +578,22 @@ void Session::set_device(shared_ptr<devices::Device> device)
 
        // Remove all stored data and reset all views
        for (shared_ptr<views::ViewBase> view : views_) {
-               view->clear_signals();
+               view->clear_signalbases();
 #ifdef ENABLE_DECODE
                view->clear_decode_signals();
 #endif
                view->reset_view_state();
        }
+
+       for (SignalGroup* group : signal_groups_) {
+               group->clear();
+               delete group;
+       }
+       signal_groups_.clear();
+
        for (const shared_ptr<data::SignalData>& d : all_signal_data_)
                d->clear();
+
        all_signal_data_.clear();
        signalbases_.clear();
        cur_logic_segment_.reset();
@@ -456,6 +614,9 @@ void Session::set_device(shared_ptr<devices::Device> device)
        } catch (const QString &e) {
                device_.reset();
                MainWindow::show_session_error(tr("Failed to open device"), e);
+       } catch (const sigrok::Error &e) {
+               device_.reset();
+               MainWindow::show_session_error(tr("Failed to open device"), QString(e.what()));
        }
 
        if (device_) {
@@ -532,8 +693,12 @@ Session::input_format_options(vector<string> user_spec,
                 * data type.
                 */
                auto found = fmt_opts.find(key);
-               if (found == fmt_opts.end())
+               if (found == fmt_opts.end()) {
+                       qCritical() << "Supplied input option" << QString::fromStdString(key) <<
+                               "is not a valid option for this input module, it will be ignored!";
                        continue;
+               }
+
                shared_ptr<Option> opt = found->second;
                result[key] = opt->parse_string(val);
        }
@@ -558,7 +723,7 @@ void Session::load_init_file(const string &file_name,
                                return f.first == user_name; });
                if (iter == formats.end()) {
                        MainWindow::show_session_error(tr("Error"),
-                               tr("Unexpected input format: %s").arg(QString::fromStdString(format)));
+                               tr("Unexpected input format: %1").arg(QString::fromStdString(format)));
                        return;
                }
                input_format = (*iter).second;
@@ -594,8 +759,11 @@ void Session::load_file(QString file_name, QString setup_file_name,
                                        file_name.toStdString())));
        } catch (Error& e) {
                MainWindow::show_session_error(tr("Failed to load %1").arg(file_name), e.what());
-               set_default_device();
-               main_bar_->update_device_list();
+               return;
+       }
+
+       if (!device_) {
+               MainWindow::show_session_error(errorMessage, "");
                return;
        }
 
@@ -614,7 +782,11 @@ void Session::load_file(QString file_name, QString setup_file_name,
        main_bar_->update_device_list();
 
        start_capture([&, errorMessage](QString infoMessage) {
-               MainWindow::show_session_error(errorMessage, infoMessage); });
+               Q_EMIT session_error_raised(errorMessage, infoMessage); });
+
+       // Only set save path if we loaded an srzip file
+       if (dynamic_pointer_cast<devices::SessionFile>(device_))
+               set_save_path(QFileInfo(file_name).absolutePath());
 
        set_name(QFileInfo(file_name).fileName());
 }
@@ -651,6 +823,7 @@ void Session::start_capture(function<void (const QString)> error_handler)
                d->clear();
 
        trigger_list_.clear();
+       segment_sample_count_.clear();
 
        // Revert name back to default name (e.g. "Session 1") for real devices
        // as the (possibly saved) data is gone. File devices keep their name.
@@ -662,9 +835,10 @@ void Session::start_capture(function<void (const QString)> error_handler)
                name_changed();
        }
 
+       acq_start_time_ = Glib::DateTime::create_now_local();
+
        // Begin the session
-       sampling_thread_ = std::thread(
-               &Session::sample_thread_proc, this, error_handler);
+       sampling_thread_ = std::thread(&Session::sample_thread_proc, this, error_handler);
 }
 
 void Session::stop_capture()
@@ -688,7 +862,7 @@ void Session::register_view(shared_ptr<views::ViewBase> view)
        update_signals();
 
        // Add all other signals
-       unordered_set< shared_ptr<data::SignalBase> > view_signalbases = view->signalbases();
+       vector< shared_ptr<data::SignalBase> > view_signalbases = view->signalbases();
 
        for (const shared_ptr<data::SignalBase>& signalbase : signalbases_) {
                const int sb_exists = count_if(
@@ -718,7 +892,9 @@ void Session::register_view(shared_ptr<views::ViewBase> view)
 
 void Session::deregister_view(shared_ptr<views::ViewBase> view)
 {
-       views_.remove_if([&](shared_ptr<views::ViewBase> v) { return v == view; });
+       views_.erase(std::remove_if(views_.begin(), views_.end(),
+               [&](shared_ptr<views::ViewBase> v) { return v == view; }),
+               views_.end());
 
        if (views_.empty()) {
                main_view_.reset();
@@ -755,16 +931,22 @@ double Session::get_samplerate() const
        return samplerate;
 }
 
-uint32_t Session::get_segment_count() const
+Glib::DateTime Session::get_acquisition_start_time() const
 {
-       uint32_t value = 0;
+       return acq_start_time_;
+}
 
-       // Find the highest number of segments
-       for (const shared_ptr<data::SignalData>& data : all_signal_data_)
-               if (data->get_segment_count() > value)
-                       value = data->get_segment_count();
+uint32_t Session::get_highest_segment_id() const
+{
+       return highest_segment_id_;
+}
 
-       return value;
+uint64_t Session::get_segment_sample_count(uint32_t segment_id) const
+{
+       if (segment_id < segment_sample_count_.size())
+               return segment_sample_count_[segment_id];
+       else
+               return 0;
 }
 
 vector<util::Timestamp> Session::get_triggers(uint32_t segment_id) const
@@ -778,20 +960,46 @@ vector<util::Timestamp> Session::get_triggers(uint32_t segment_id) const
        return result;
 }
 
-const unordered_set< shared_ptr<data::SignalBase> > Session::signalbases() const
+const vector< shared_ptr<data::SignalBase> > Session::signalbases() const
 {
        return signalbases_;
 }
 
-bool Session::all_segments_complete(uint32_t segment_id) const
+uint32_t Session::get_signal_count(data::SignalBase::ChannelType type) const
 {
-       bool all_complete = true;
+       return count_if(signalbases_.begin(), signalbases_.end(),
+               [&] (shared_ptr<SignalBase> sb) { return sb->type() == type; });
+}
 
-       for (const shared_ptr<data::SignalBase>& base : signalbases_)
-               if (!base->segment_is_complete(segment_id))
-                       all_complete = false;
+uint32_t Session::get_next_signal_index(data::SignalBase::ChannelType type)
+{
+       next_index_list_[type]++;
+       return next_index_list_[type];
+}
 
-       return all_complete;
+void Session::add_generated_signal(shared_ptr<data::SignalBase> signal)
+{
+       signalbases_.push_back(signal);
+
+       for (shared_ptr<views::ViewBase>& view : views_)
+               view->add_signalbase(signal);
+
+       update_signals();
+}
+
+void Session::remove_generated_signal(shared_ptr<data::SignalBase> signal)
+{
+       if (shutting_down_)
+               return;
+
+       signalbases_.erase(std::remove_if(signalbases_.begin(), signalbases_.end(),
+               [&](shared_ptr<data::SignalBase> s) { return s == signal; }),
+               signalbases_.end());
+
+       for (shared_ptr<views::ViewBase>& view : views_)
+               view->remove_signalbase(signal);
+
+       update_signals();
 }
 
 #ifdef ENABLE_DECODE
@@ -803,7 +1011,7 @@ shared_ptr<data::DecodeSignal> Session::add_decode_signal()
                // Create the decode signal
                signal = make_shared<data::DecodeSignal>(*this);
 
-               signalbases_.insert(signal);
+               signalbases_.push_back(signal);
 
                // Add the decode signal to all views
                for (shared_ptr<views::ViewBase>& view : views_)
@@ -820,7 +1028,12 @@ shared_ptr<data::DecodeSignal> Session::add_decode_signal()
 
 void Session::remove_decode_signal(shared_ptr<data::DecodeSignal> signal)
 {
-       signalbases_.erase(signal);
+       if (shutting_down_)
+               return;
+
+       signalbases_.erase(std::remove_if(signalbases_.begin(), signalbases_.end(),
+               [&](shared_ptr<data::SignalBase> s) { return s == signal; }),
+               signalbases_.end());
 
        for (shared_ptr<views::ViewBase>& view : views_)
                view->remove_decode_signal(signal);
@@ -829,9 +1042,26 @@ void Session::remove_decode_signal(shared_ptr<data::DecodeSignal> signal)
 }
 #endif
 
+bool Session::all_segments_complete(uint32_t segment_id) const
+{
+       bool all_complete = true;
+
+       for (const shared_ptr<data::SignalBase>& base : signalbases_)
+               if (!base->segment_is_complete(segment_id))
+                       all_complete = false;
+
+       return all_complete;
+}
+
+MetadataObjManager* Session::metadata_obj_manager()
+{
+       return &metadata_obj_manager_;
+}
+
 void Session::set_capture_state(capture_state state)
 {
-       bool changed;
+       if (state == capture_state_)
+               return;
 
        if (state == Running)
                acq_time_.restart();
@@ -840,12 +1070,10 @@ void Session::set_capture_state(capture_state state)
 
        {
                lock_guard<mutex> lock(sampling_mutex_);
-               changed = capture_state_ != state;
                capture_state_ = state;
        }
 
-       if (changed)
-               capture_state_changed(state);
+       capture_state_changed(state);
 }
 
 void Session::update_signals()
@@ -854,7 +1082,7 @@ void Session::update_signals()
                signalbases_.clear();
                logic_data_.reset();
                for (shared_ptr<views::ViewBase>& view : views_) {
-                       view->clear_signals();
+                       view->clear_signalbases();
 #ifdef ENABLE_DECODE
                        view->clear_decode_signals();
 #endif
@@ -869,7 +1097,7 @@ void Session::update_signals()
                signalbases_.clear();
                logic_data_.reset();
                for (shared_ptr<views::ViewBase>& view : views_) {
-                       view->clear_signals();
+                       view->clear_signalbases();
 #ifdef ENABLE_DECODE
                        view->clear_decode_signals();
 #endif
@@ -884,7 +1112,7 @@ void Session::update_signals()
                [] (shared_ptr<Channel> channel) {
                        return channel->type() == sigrok::ChannelType::LOGIC; });
 
-       // Create data containers for the logic data segments
+       // Create a common data container for the logic signalbases
        {
                lock_guard<recursive_mutex> data_lock(data_mutex_);
 
@@ -892,97 +1120,96 @@ void Session::update_signals()
                        logic_data_.reset();
                } else if (!logic_data_ ||
                        logic_data_->num_channels() != logic_channel_count) {
-                       logic_data_.reset(new data::Logic(
-                               logic_channel_count));
+                       logic_data_.reset(new data::Logic(logic_channel_count));
                        assert(logic_data_);
                }
        }
 
-       // Make the signals list
-       for (shared_ptr<views::ViewBase>& viewbase : views_) {
-               views::trace::View *trace_view =
-                       qobject_cast<views::trace::View*>(viewbase.get());
-
-               if (trace_view) {
-                       unordered_set< shared_ptr<Signal> > prev_sigs(trace_view->signals());
-                       trace_view->clear_signals();
-
-                       for (auto channel : sr_dev->channels()) {
-                               shared_ptr<data::SignalBase> signalbase;
-                               shared_ptr<Signal> signal;
-
-                               // Find the channel in the old signals
-                               const auto iter = find_if(
-                                       prev_sigs.cbegin(), prev_sigs.cend(),
-                                       [&](const shared_ptr<Signal> &s) {
-                                               return s->base()->channel() == channel;
-                                       });
-                               if (iter != prev_sigs.end()) {
-                                       // Copy the signal from the old set to the new
-                                       signal = *iter;
-                                       trace_view->add_signal(signal);
-                               } else {
-                                       // Find the signalbase for this channel if possible
-                                       signalbase.reset();
-                                       for (const shared_ptr<data::SignalBase>& b : signalbases_)
-                                               if (b->channel() == channel)
-                                                       signalbase = b;
-
-                                       shared_ptr<Signal> signal;
-
-                                       switch(channel->type()->id()) {
-                                       case SR_CHANNEL_LOGIC:
-                                               if (!signalbase) {
-                                                       signalbase = make_shared<data::SignalBase>(channel,
-                                                               data::SignalBase::LogicChannel);
-                                                       signalbases_.insert(signalbase);
-
-                                                       all_signal_data_.insert(logic_data_);
-                                                       signalbase->set_data(logic_data_);
-
-                                                       connect(this, SIGNAL(capture_state_changed(int)),
-                                                               signalbase.get(), SLOT(on_capture_state_changed(int)));
-                                               }
-
-                                               signal = shared_ptr<Signal>(new LogicSignal(*this, device_, signalbase));
-                                               break;
+       // Create signalbases if necessary
+       for (auto channel : sr_dev->channels()) {
 
-                                       case SR_CHANNEL_ANALOG:
-                                       {
-                                               if (!signalbase) {
-                                                       signalbase = make_shared<data::SignalBase>(channel,
-                                                               data::SignalBase::AnalogChannel);
-                                                       signalbases_.insert(signalbase);
+               // Try to find the channel in the list of existing signalbases
+               const auto iter = find_if(signalbases_.cbegin(), signalbases_.cend(),
+                       [&](const shared_ptr<SignalBase> &sb) { return sb->channel() == channel; });
 
-                                                       shared_ptr<data::Analog> data(new data::Analog());
-                                                       all_signal_data_.insert(data);
-                                                       signalbase->set_data(data);
+               // Not found, let's make a signalbase for it
+               if (iter == signalbases_.cend()) {
+                       shared_ptr<SignalBase> signalbase;
+                       switch(channel->type()->id()) {
+                       case SR_CHANNEL_LOGIC:
+                               signalbase = make_shared<data::SignalBase>(channel, data::SignalBase::LogicChannel);
+                               signalbases_.push_back(signalbase);
 
-                                                       connect(this, SIGNAL(capture_state_changed(int)),
-                                                               signalbase.get(), SLOT(on_capture_state_changed(int)));
-                                               }
+                               all_signal_data_.insert(logic_data_);
+                               signalbase->set_data(logic_data_);
 
-                                               signal = shared_ptr<Signal>(new AnalogSignal(*this, signalbase));
-                                               break;
-                                       }
+                               connect(this, SIGNAL(capture_state_changed(int)),
+                                       signalbase.get(), SLOT(on_capture_state_changed(int)));
+                               break;
 
-                                       default:
-                                               assert(false);
-                                               break;
-                                       }
+                       case SR_CHANNEL_ANALOG:
+                               signalbase = make_shared<data::SignalBase>(channel, data::SignalBase::AnalogChannel);
+                               signalbases_.push_back(signalbase);
 
-                                       // New views take their signal settings from the main view
-                                       if (!viewbase->is_main_view()) {
-                                               shared_ptr<pv::views::trace::View> main_tv =
-                                                       dynamic_pointer_cast<pv::views::trace::View>(main_view_);
-                                               shared_ptr<Signal> main_signal =
-                                                       main_tv->get_signal_by_signalbase(signalbase);
-                                               signal->restore_settings(main_signal->save_settings());
-                                       }
+                               shared_ptr<data::Analog> data(new data::Analog());
+                               all_signal_data_.insert(data);
+                               signalbase->set_data(data);
+
+                               connect(this, SIGNAL(capture_state_changed(int)),
+                                       signalbase.get(), SLOT(on_capture_state_changed(int)));
+                               break;
+                       }
+               }
+       }
+
+       // Create and assign default signal groups if needed
+       if (signal_groups_.empty()) {
+               for (auto& entry : sr_dev->channel_groups()) {
+                       const shared_ptr<sigrok::ChannelGroup>& group = entry.second;
 
-                                       trace_view->add_signal(signal);
+                       if (group->channels().size() <= 1)
+                               continue;
+
+                       SignalGroup* sg = new SignalGroup(QString::fromStdString(entry.first));
+                       for (const shared_ptr<sigrok::Channel>& channel : group->channels()) {
+                               for (shared_ptr<data::SignalBase> s : signalbases_) {
+                                       if (s->channel() == channel) {
+                                               sg->append_signal(s);
+                                               break;
+                                       }
                                }
                        }
+                       signal_groups_.emplace_back(sg);
+               }
+       }
+
+       // Update all views
+       for (shared_ptr<views::ViewBase>& viewbase : views_) {
+               vector< shared_ptr<SignalBase> > view_signalbases =
+                               viewbase->signalbases();
+
+               // Add all non-decode signalbases that don't yet exist in the view
+               for (shared_ptr<SignalBase>& session_sb : signalbases_) {
+                       if (session_sb->type() == SignalBase::DecodeChannel)
+                               continue;
+
+                       const auto iter = find_if(view_signalbases.cbegin(), view_signalbases.cend(),
+                               [&](const shared_ptr<SignalBase> &sb) { return sb == session_sb; });
+
+                       if (iter == view_signalbases.cend())
+                               viewbase->add_signalbase(session_sb);
+               }
+
+               // Remove all non-decode signalbases that no longer exist
+               for (shared_ptr<SignalBase>& view_sb : view_signalbases) {
+                       if (view_sb->type() == SignalBase::DecodeChannel)
+                               continue;
+
+                       const auto iter = find_if(signalbases_.cbegin(), signalbases_.cend(),
+                               [&](const shared_ptr<SignalBase> &sb) { return sb == view_sb; });
+
+                       if (iter == signalbases_.cend())
+                               viewbase->remove_signalbase(view_sb);
                }
        }
 
@@ -1048,6 +1275,8 @@ void Session::sample_thread_proc(function<void (const QString)> error_handler)
                lock_guard<recursive_mutex> lock(data_mutex_);
                cur_logic_segment_.reset();
                cur_analog_segments_.clear();
+               for (shared_ptr<data::SignalBase> sb : signalbases_)
+                       sb->clear_sample_data();
        }
        highest_segment_id_ = -1;
        frame_began_ = false;
@@ -1131,6 +1360,7 @@ void Session::signal_new_segment()
 
        if (new_segment_id > highest_segment_id_) {
                highest_segment_id_ = new_segment_id;
+               segment_sample_count_.emplace_back(0);
                new_segment(highest_segment_id_);
        }
 }
@@ -1290,7 +1520,7 @@ void Session::feed_in_frame_end()
        signal_segment_completed();
 }
 
-void Session::feed_in_logic(shared_ptr<Logic> logic)
+void Session::feed_in_logic(shared_ptr<sigrok::Logic> logic)
 {
        if (logic->data_length() == 0) {
                qDebug() << "WARNING: Received logic packet with 0 samples.";
@@ -1331,10 +1561,13 @@ void Session::feed_in_logic(shared_ptr<Logic> logic)
 
        cur_logic_segment_->append_payload(logic);
 
+       segment_sample_count_[highest_segment_id_] =
+               max(segment_sample_count_[highest_segment_id_], cur_logic_segment_->get_sample_count());
+
        data_received();
 }
 
-void Session::feed_in_analog(shared_ptr<Analog> analog)
+void Session::feed_in_analog(shared_ptr<sigrok::Analog> analog)
 {
        if (analog->num_samples() == 0) {
                qDebug() << "WARNING: Received analog packet with 0 samples.";
@@ -1397,6 +1630,9 @@ void Session::feed_in_analog(shared_ptr<Analog> analog)
                // Append the samples in the segment
                segment->append_interleaved_samples(channel_data++, analog->num_samples(),
                        channels.size());
+
+               segment_sample_count_[highest_segment_id_] =
+                       max(segment_sample_count_[highest_segment_id_], segment->get_sample_count());
        }
 
        if (sweep_beginning) {
index f4d150dbe0031133afbd2497db3c4947a047c01b..94338c20e259144a7bb9ae3bad96edc9ef4c44f5 100644 (file)
@@ -25,6 +25,7 @@
 #include <condition_variable>
 #endif
 
+#include <deque>
 #include <functional>
 #include <map>
 #include <memory>
 #include <set>
 #include <string>
 #include <thread>
-#include <unordered_set>
 #include <vector>
 
+#include <glibmm/datetime.h>
+
 #include <QObject>
 #include <QSettings>
 #include <QString>
 #include <libsigrokflow/libsigrokflow.hpp>
 #endif
 
+#include "metadata_obj.hpp"
 #include "util.hpp"
 #include "views/viewbase.hpp"
 
-
+using std::deque;
 using std::function;
-using std::list;
 using std::map;
 using std::mutex;
 using std::recursive_mutex;
@@ -95,6 +97,7 @@ class Logic;
 class LogicSegment;
 class SignalBase;
 class SignalData;
+class SignalGroup;
 }
 
 namespace devices {
@@ -109,6 +112,8 @@ namespace views {
 class ViewBase;
 }
 
+using pv::views::ViewType;
+
 class Session : public QObject
 {
        Q_OBJECT
@@ -136,15 +141,15 @@ public:
        shared_ptr<devices::Device> device() const;
 
        QString name() const;
-
        void set_name(QString name);
 
-       const list< shared_ptr<views::ViewBase> > views() const;
+       QString save_path() const;
+       void set_save_path(QString path);
 
-       shared_ptr<views::ViewBase> main_view() const;
+       const vector< shared_ptr<views::ViewBase> > views() const;
 
+       shared_ptr<views::ViewBase> main_view() const;
        shared_ptr<pv::toolbars::MainBar> main_bar() const;
-
        void set_main_bar(shared_ptr<pv::toolbars::MainBar> main_bar);
 
        /**
@@ -153,11 +158,8 @@ public:
        bool data_saved() const;
 
        void save_setup(QSettings &settings) const;
-
        void save_settings(QSettings &settings) const;
-
        void restore_setup(QSettings &settings);
-
        void restore_settings(QSettings &settings);
 
        /**
@@ -169,9 +171,7 @@ public:
         * Sets device instance that will be used in the next capture session.
         */
        void set_device(shared_ptr<devices::Device> device);
-
        void set_default_device();
-
        bool using_file_device() const;
 
        void load_init_file(const string &file_name, const string &format,
@@ -183,26 +183,27 @@ public:
                        map<string, Glib::VariantBase>());
 
        capture_state get_capture_state() const;
-
        void start_capture(function<void (const QString)> error_handler);
-
        void stop_capture();
 
        double get_samplerate() const;
+       Glib::DateTime get_acquisition_start_time() const;
 
-       uint32_t get_segment_count() const;
+       uint32_t get_highest_segment_id() const;
+       uint64_t get_segment_sample_count(uint32_t segment_id) const;
 
        vector<util::Timestamp> get_triggers(uint32_t segment_id) const;
 
        void register_view(shared_ptr<views::ViewBase> view);
-
        void deregister_view(shared_ptr<views::ViewBase> view);
-
        bool has_view(shared_ptr<views::ViewBase> view);
 
-       const unordered_set< shared_ptr<data::SignalBase> > signalbases() const;
+       const vector< shared_ptr<data::SignalBase> > signalbases() const;
+       uint32_t get_signal_count(data::SignalBase::ChannelType type) const;
+       uint32_t get_next_signal_index(data::SignalBase::ChannelType type);
 
-       bool all_segments_complete(uint32_t segment_id) const;
+       void add_generated_signal(shared_ptr<data::SignalBase> signal);
+       void remove_generated_signal(shared_ptr<data::SignalBase> signal);
 
 #ifdef ENABLE_DECODE
        shared_ptr<data::DecodeSignal> add_decode_signal();
@@ -210,6 +211,10 @@ public:
        void remove_decode_signal(shared_ptr<data::DecodeSignal> signal);
 #endif
 
+       bool all_segments_complete(uint32_t segment_id) const;
+
+       MetadataObjManager* metadata_obj_manager();
+
 private:
        void set_capture_state(capture_state state);
 
@@ -236,16 +241,11 @@ private:
 #endif
 
        void feed_in_header();
-
        void feed_in_meta(shared_ptr<sigrok::Meta> meta);
-
        void feed_in_trigger();
-
        void feed_in_frame_begin();
        void feed_in_frame_end();
-
        void feed_in_logic(shared_ptr<sigrok::Logic> logic);
-
        void feed_in_analog(shared_ptr<sigrok::Analog> analog);
 
        void data_feed_in(shared_ptr<sigrok::Device> device,
@@ -266,7 +266,8 @@ Q_SIGNALS:
 
        void data_received();
 
-       void add_view(views::ViewType type, Session *session);
+       void add_view(ViewType type, Session *session);
+       void session_error_raised(const QString text, const QString info_text);
 
 public Q_SLOTS:
        void on_data_saved();
@@ -276,22 +277,26 @@ public Q_SLOTS:
 #endif
 
 private:
+       bool shutting_down_;
+
        DeviceManager &device_manager_;
        shared_ptr<devices::Device> device_;
-       QString default_name_, name_;
+       QString default_name_, name_, save_path_;
 
-       list< shared_ptr<views::ViewBase> > views_;
+       vector< shared_ptr<views::ViewBase> > views_;
        shared_ptr<pv::views::ViewBase> main_view_;
 
        shared_ptr<pv::toolbars::MainBar> main_bar_;
 
-       mutable mutex sampling_mutex_; //!< Protects access to capture_state_.
+       mutable mutex sampling_mutex_; //!< Protects access to capture_state_
        capture_state capture_state_;
 
-       unordered_set< shared_ptr<data::SignalBase> > signalbases_;
+       vector< shared_ptr<data::SignalBase> > signalbases_;
        unordered_set< shared_ptr<data::SignalData> > all_signal_data_;
+       deque<data::SignalGroup*> signal_groups_;
+       map<uint8_t, uint32_t> next_index_list_; // signal type -> index counter
 
-       /// trigger_list_ contains pairs of <segment_id, timestamp> values.
+       /// trigger_list_ contains pairs of <segment_id, timestamp> values
        vector< std::pair<uint32_t, util::Timestamp> > trigger_list_;
 
        mutable recursive_mutex data_mutex_;
@@ -301,6 +306,7 @@ private:
        map< shared_ptr<sigrok::Channel>, shared_ptr<data::AnalogSegment> >
                cur_analog_segments_;
        int32_t highest_segment_id_;
+       vector<uint64_t> segment_sample_count_;
 
        std::thread sampling_thread_;
 
@@ -309,6 +315,9 @@ private:
        bool frame_began_;
 
        QElapsedTimer acq_time_;
+       Glib::DateTime acq_start_time_;
+
+       MetadataObjManager metadata_obj_manager_;
 
 #ifdef ENABLE_FLOW
        RefPtr<Pipeline> pipeline_;
index f024a36cb8a8c49792eb5d8bd14d424429f42d2c..5eb6a8ad25aacbec2085500ad95a6ec9363a0ad1 100644 (file)
@@ -45,7 +45,6 @@ using std::mutex;
 using std::pair;
 using std::shared_ptr;
 using std::string;
-using std::unordered_set;
 using std::vector;
 
 using Glib::VariantBase;
@@ -93,7 +92,7 @@ const QString& StoreSession::error() const
 
 bool StoreSession::start()
 {
-       const unordered_set< shared_ptr<data::SignalBase> > sigs(session_.signalbases());
+       const vector< shared_ptr<data::SignalBase> > sigs(session_.signalbases());
 
        shared_ptr<data::Segment> any_segment;
        shared_ptr<data::LogicSegment> lsegment;
@@ -185,6 +184,9 @@ bool StoreSession::start()
                        {{ConfigKey::SAMPLERATE, Glib::Variant<guint64>::create(
                                any_segment->samplerate())}});
                output_->receive(meta);
+
+               auto header = context->create_header_packet(session_.get_acquisition_start_time());
+               output_->receive(header);
        } catch (Error& error) {
                error_ = tr("Error while saving: ") + error.what();
                return false;
@@ -302,10 +304,14 @@ void StoreSession::store_proc(vector< shared_ptr<data::SignalBase> > achannel_li
                units_stored_ = unit_count_ - (sample_count_ >> progress_scale);
        }
 
-       auto dfend = context->create_end_packet();
-       const string ldata_str = output_->receive(dfend);
-       if (output_stream_.is_open())
-               output_stream_ << ldata_str;
+       try {
+               auto dfend = context->create_end_packet();
+               const string ldata_str = output_->receive(dfend);
+               if (output_stream_.is_open())
+                       output_stream_ << ldata_str;
+       } catch (Error& error) {
+               error_ = tr("Error while saving: ") + error.what();
+       }
 
        // Zeroing the progress variables indicates completion
        units_stored_ = unit_count_ = 0;
index ebdfd9d7d387635c442be8cabedaa6cebc5016e3..0c4dab59c3811d1b83ea922d1eb3098f80802070 100644 (file)
@@ -134,7 +134,7 @@ QVariant DecoderCollectionModel::data(const QModelIndex& index, int role) const
 Qt::ItemFlags DecoderCollectionModel::flags(const QModelIndex& index) const
 {
        if (!index.isValid())
-               return nullptr;
+               return Qt::NoItemFlags;
 
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
 }
@@ -142,7 +142,7 @@ Qt::ItemFlags DecoderCollectionModel::flags(const QModelIndex& index) const
 QVariant DecoderCollectionModel::headerData(int section, Qt::Orientation orientation,
        int role) const
 {
-       if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+       if ((orientation == Qt::Horizontal) && (role == Qt::DisplayRole))
                return root_->data(section);
 
        return QVariant();
index 94ed6f4b6da970e02a3191ba2a87dc7d951bd054..0c4b76bb8c14aca9fcb58688f037579c205dd13e 100644 (file)
@@ -74,7 +74,8 @@ void QCustomTreeView::currentChanged(const QModelIndex& current,
        const QModelIndex& previous)
 {
        QTreeView::currentChanged(current, previous);
-       currentChanged(current);
+
+       current_changed(current);
 }
 
 
@@ -158,7 +159,7 @@ SubWindow::SubWindow(Session& session, QWidget* parent) :
        connect(filter, SIGNAL(returnPressed()),
                this, SLOT(on_filter_return_pressed()));
 
-       connect(tree_view_, SIGNAL(currentChanged(const QModelIndex&)),
+       connect(tree_view_, SIGNAL(current_changed(const QModelIndex&)),
                this, SLOT(on_item_changed(const QModelIndex&)));
        connect(tree_view_, SIGNAL(activated(const QModelIndex&)),
                this, SLOT(on_item_activated(const QModelIndex&)));
@@ -185,7 +186,7 @@ QToolBar* SubWindow::create_toolbar(QWidget *parent) const
 int SubWindow::minimum_width() const
 {
        QFontMetrics m(info_label_body_->font());
-       const int label_width = m.width(QString(tr(initial_notice)));
+       const int label_width = util::text_width(m, tr(initial_notice));
 
        return label_width + min_width_margin;
 }
index c189fb24860efb085a859617e5bd8bf4bcb896eb..f4ec022329b425d63f48f3be766a0b9b40b23582 100644 (file)
@@ -99,7 +99,7 @@ public:
        void currentChanged(const QModelIndex& current, const QModelIndex& previous);
 
 Q_SIGNALS:
-       void currentChanged(const QModelIndex& current);
+       void current_changed(const QModelIndex& current);
 };
 
 class SubWindow : public SubWindowBase
index ab3a08b412af72790fde7eed678030a8bdc4b91e..2688c2b3dcc1a5f6ba062b62b83030874079c21d 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_SUBWINDOWBASE_HPP
-#define PULSEVIEW_PV_SUBWINDOWBASE_HPP
+#ifndef PULSEVIEW_PV_SUBWINDOWS_SUBWINDOWBASE_HPP
+#define PULSEVIEW_PV_SUBWINDOWS_SUBWINDOWBASE_HPP
 
 #include <cstdint>
 #include <memory>
@@ -90,4 +90,4 @@ protected:
 } // namespace subwindows
 } // namespace pv
 
-#endif // PULSEVIEW_PV_SUBWINDOWBASE_HPP
+#endif // PULSEVIEW_PV_SUBWINDOWS_SUBWINDOWBASE_HPP
index a7998b4046511077502a4971bdfa7bc55d3e21d0..cd46c063bdc8a84d78d1e42e1c616c2fc8a3b270 100644 (file)
@@ -35,6 +35,7 @@
 
 #include <boost/algorithm/string/join.hpp>
 
+#include <pv/data/mathsignal.hpp>
 #include <pv/devicemanager.hpp>
 #include <pv/devices/hardwaredevice.hpp>
 #include <pv/devices/inputfile.hpp>
@@ -59,6 +60,7 @@ using std::back_inserter;
 using std::copy;
 using std::list;
 using std::make_pair;
+using std::make_shared;
 using std::map;
 using std::max;
 using std::min;
@@ -90,6 +92,7 @@ MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view
        StandardBar(session, parent, view, false),
        action_new_view_(new QAction(this)),
        action_open_(new QAction(this)),
+       action_save_(new QAction(this)),
        action_save_as_(new QAction(this)),
        action_save_selection_as_(new QAction(this)),
        action_restore_setup_(new QAction(this)),
@@ -107,10 +110,11 @@ MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view
        sample_rate_("Hz", this),
        updating_sample_rate_(false),
        updating_sample_count_(false),
-       sample_count_supported_(false)
+       sample_count_supported_(false),
 #ifdef ENABLE_DECODE
-       , add_decoder_button_(new QToolButton())
+       add_decoder_button_(new QToolButton()),
 #endif
+       add_math_signal_button_(new QToolButton())
 {
        setObjectName(QString::fromUtf8("MainBar"));
 
@@ -126,7 +130,11 @@ MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view
        action_open_->setText(tr("&Open..."));
        action_open_->setIcon(QIcon::fromTheme("document-open",
                QIcon(":/icons/document-open.png")));
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       action_open_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_O));
+#else
        action_open_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_O));
+#endif
        connect(action_open_, SIGNAL(triggered(bool)),
                this, SLOT(on_actionOpen_triggered()));
 
@@ -134,17 +142,31 @@ MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view
        connect(action_restore_setup_, SIGNAL(triggered(bool)),
                this, SLOT(on_actionRestoreSetup_triggered()));
 
-       action_save_as_->setText(tr("&Save As..."));
+       action_save_->setText(tr("&Save..."));
+       action_save_->setIcon(QIcon::fromTheme("document-save-as",
+               QIcon(":/icons/document-save-as.png")));
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       action_save_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
+#else
+       action_save_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
+#endif
+       connect(action_save_, SIGNAL(triggered(bool)),
+               this, SLOT(on_actionSave_triggered()));
+
+       action_save_as_->setText(tr("Save &As..."));
        action_save_as_->setIcon(QIcon::fromTheme("document-save-as",
                QIcon(":/icons/document-save-as.png")));
-       action_save_as_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
        connect(action_save_as_, SIGNAL(triggered(bool)),
                this, SLOT(on_actionSaveAs_triggered()));
 
        action_save_selection_as_->setText(tr("Save Selected &Range As..."));
        action_save_selection_as_->setIcon(QIcon::fromTheme("document-save-as",
                QIcon(":/icons/document-save-as.png")));
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       action_save_selection_as_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_R));
+#else
        action_save_selection_as_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
+#endif
        connect(action_save_selection_as_, SIGNAL(triggered(bool)),
                this, SLOT(on_actionSaveSelectionAs_triggered()));
 
@@ -175,7 +197,7 @@ MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view
 
        for (int i = 0; i < views::ViewTypeCount; i++) {
                QAction *const action = menu_new_view->addAction(tr(views::ViewTypeNames[i]));
-               action->setData(qVariantFromValue(i));
+               action->setData(QVariant::fromValue(i));
        }
 
        new_view_button_->setMenu(menu_new_view);
@@ -201,6 +223,7 @@ MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view
 
        // Save button
        vector<QAction*> save_actions;
+       save_actions.push_back(action_save_);
        save_actions.push_back(action_save_as_);
        save_actions.push_back(action_save_selection_as_);
        QAction* separator_s = new QAction(this);
@@ -214,7 +237,7 @@ MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view
                this, SLOT(export_file(shared_ptr<sigrok::OutputFormat>)));
 
        save_button_->setMenu(export_menu);
-       save_button_->setDefaultAction(action_save_as_);
+       save_button_->setDefaultAction(action_save_);
        save_button_->setPopupMode(QToolButton::MenuButtonPopup);
 
        // Device selector menu
@@ -232,6 +255,16 @@ MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view
                this, SLOT(on_add_decoder_clicked()));
 #endif
 
+       // Setup the math signal button
+       add_math_signal_button_->setIcon(QIcon(":/icons/add-math-signal.svg"));
+       add_math_signal_button_->setPopupMode(QToolButton::InstantPopup);
+       add_math_signal_button_->setToolTip(tr("Add math signal"));
+       add_math_signal_button_->setShortcut(QKeySequence(Qt::Key_M));
+
+       connect(add_math_signal_button_, SIGNAL(clicked()),
+               this, SLOT(on_add_math_signal_clicked()));
+
+
        connect(&sample_count_, SIGNAL(value_changed()),
                this, SLOT(on_sample_count_changed()));
        connect(&sample_rate_, SIGNAL(value_changed()),
@@ -298,6 +331,11 @@ QAction* MainBar::action_open() const
        return action_open_;
 }
 
+QAction* MainBar::action_save() const
+{
+       return action_save_;
+}
+
 QAction* MainBar::action_save_as() const
 {
        return action_save_as_;
@@ -522,7 +560,7 @@ void MainBar::update_device_config_widgets()
                sample_count_supported_ = true;
 
        // Add notification of reconfigure events
-       disconnect(this, SLOT(on_config_changed()));
+       // Note: No need to disconnect the previous signal as that QObject instance is destroyed
        connect(&opts->binding(), SIGNAL(config_changed()),
                this, SLOT(on_config_changed()));
 
@@ -595,7 +633,7 @@ void MainBar::show_session_error(const QString text, const QString info_text)
        msg.exec();
 }
 
-void MainBar::export_file(shared_ptr<OutputFormat> format, bool selection_only)
+void MainBar::export_file(shared_ptr<OutputFormat> format, bool selection_only, QString file_name)
 {
        using pv::dialogs::StoreProgress;
 
@@ -654,8 +692,8 @@ void MainBar::export_file(shared_ptr<OutputFormat> format, bool selection_only)
                        tr("All Files"));
 
        // Show the file dialog
-       const QString file_name = QFileDialog::getSaveFileName(
-               this, tr("Save File"), dir, filter);
+       if (file_name.isEmpty())
+               file_name = QFileDialog::getSaveFileName(this, tr("Save File"), dir, filter);
 
        if (file_name.isEmpty())
                return;
@@ -675,8 +713,13 @@ void MainBar::export_file(shared_ptr<OutputFormat> format, bool selection_only)
                options = dlg.options();
        }
 
-       if (!selection_only)
-               session_.set_name(QFileInfo(file_name).fileName());
+       if (!selection_only) {
+               if (format == session_.device_manager().context()->output_formats()["srzip"]) {
+                       session_.set_save_path(QFileInfo(file_name).absolutePath());
+                       session_.set_name(QFileInfo(file_name).fileName());
+               } else
+                       session_.set_save_path("");
+       }
 
        StoreProgress *dlg = new StoreProgress(file_name, format, options,
                sample_range, session_, this);
@@ -798,6 +841,19 @@ void MainBar::on_actionOpen_triggered()
        }
 }
 
+void MainBar::on_actionSave_triggered()
+{
+       // A path is only set if we loaded/saved an srzip file before
+       if (session_.save_path().isEmpty()) {
+               on_actionSaveAs_triggered();
+               return;
+       }
+
+       QFileInfo fi = QFileInfo(QDir(session_.save_path()), session_.name());
+       export_file(session_.device_manager().context()->output_formats()["srzip"], false,
+               fi.absoluteFilePath());
+}
+
 void MainBar::on_actionSaveAs_triggered()
 {
        export_file(session_.device_manager().context()->output_formats()["srzip"]);
@@ -862,6 +918,12 @@ void MainBar::on_add_decoder_clicked()
        show_decoder_selector(&session_);
 }
 
+void MainBar::on_add_math_signal_clicked()
+{
+       shared_ptr<data::SignalBase> signal = make_shared<data::MathSignal>(session_);
+       session_.add_generated_signal(signal);
+}
+
 void MainBar::add_toolbar_widgets()
 {
        addWidget(new_view_button_);
@@ -881,6 +943,7 @@ void MainBar::add_toolbar_widgets()
        addSeparator();
        addWidget(add_decoder_button_);
 #endif
+       addWidget(add_math_signal_button_);
 }
 
 bool MainBar::eventFilter(QObject *watched, QEvent *event)
index 04c344c5351088e8e636e50159adb1c536d3dc08..e4aa39b6743f1a8484beaced925079269befe8c4 100644 (file)
@@ -96,6 +96,7 @@ public:
 
        QAction* action_new_view() const;
        QAction* action_open() const;
+       QAction* action_save() const;
        QAction* action_save_as() const;
        QAction* action_save_selection_as() const;
        QAction* action_restore_setup() const;
@@ -120,7 +121,7 @@ private Q_SLOTS:
        void show_session_error(const QString text, const QString info_text);
 
        void export_file(shared_ptr<sigrok::OutputFormat> format,
-               bool selection_only = false);
+               bool selection_only = false, QString file_name = "");
        void import_file(shared_ptr<sigrok::InputFormat> format);
 
        void on_device_selected();
@@ -134,6 +135,7 @@ private Q_SLOTS:
        void on_actionNewView_triggered(QAction* action = nullptr);
 
        void on_actionOpen_triggered();
+       void on_actionSave_triggered();
        void on_actionSaveAs_triggered();
        void on_actionSaveSelectionAs_triggered();
 
@@ -143,6 +145,7 @@ private Q_SLOTS:
        void on_actionConnect_triggered();
 
        void on_add_decoder_clicked();
+       void on_add_math_signal_clicked();
 
 protected:
        void add_toolbar_widgets();
@@ -156,6 +159,7 @@ Q_SIGNALS:
 private:
        QAction *const action_new_view_;
        QAction *const action_open_;
+       QAction *const action_save_;
        QAction *const action_save_as_;
        QAction *const action_save_selection_as_;
        QAction *const action_restore_setup_;
@@ -182,6 +186,8 @@ private:
 #ifdef ENABLE_DECODE
        QToolButton *add_decoder_button_;
 #endif
+
+       QToolButton *add_math_signal_button_;
 };
 
 } // namespace toolbars
index 9a9a5065a2e17180eb6cdebb71b4aefc07fb5833..dfb8c72b43504a95ad1f107c66a16e289858ba82 100644 (file)
@@ -110,25 +110,31 @@ static QTextStream& operator<<(QTextStream& stream, const Timestamp& t)
        return stream << QString::fromStdString(str);
 }
 
-QString format_time_si(const Timestamp& v, SIPrefix prefix,
-       unsigned int precision, QString unit, bool sign)
+SIPrefix determine_value_prefix(double v)
 {
-       if (prefix == SIPrefix::unspecified) {
-               // No prefix given, calculate it
-
-               if (v.is_zero()) {
-                       prefix = SIPrefix::none;
-               } else {
-                       int exp = exponent(SIPrefix::yotta);
-                       prefix = SIPrefix::yocto;
-                       while ((fabs(v) * pow(Timestamp(10), exp)) > 999 &&
-                                       prefix < SIPrefix::yotta) {
-                               prefix = successor(prefix);
-                               exp -= 3;
-                       }
+       SIPrefix prefix;
+
+       if (v == 0) {
+               prefix = SIPrefix::none;
+       } else {
+               int exp = exponent(SIPrefix::yotta);
+               prefix = SIPrefix::yocto;
+               while ((fabs(v) * pow(10, exp)) > 999 &&
+                               prefix < SIPrefix::yotta) {
+                       prefix = successor(prefix);
+                       exp -= 3;
                }
        }
 
+       return prefix;
+}
+
+QString format_time_si(const Timestamp& v, SIPrefix prefix,
+       unsigned int precision, QString unit, bool sign)
+{
+       if (prefix == SIPrefix::unspecified)
+               prefix = determine_value_prefix(v.convert_to<double>());
+
        assert(prefix >= SIPrefix::yocto);
        assert(prefix <= SIPrefix::yotta);
 
@@ -137,7 +143,7 @@ QString format_time_si(const Timestamp& v, SIPrefix prefix,
        QString s;
        QTextStream ts(&s);
        if (sign && !v.is_zero())
-               ts << forcesign;
+               ts.setNumberFlags(ts.numberFlags() | QTextStream::ForceSign);
        ts << qSetRealNumberPrecision(precision) << (v * multiplier);
        ts << ' ' << prefix << unit;
 
@@ -148,19 +154,7 @@ QString format_value_si(double v, SIPrefix prefix, unsigned precision,
        QString unit, bool sign)
 {
        if (prefix == SIPrefix::unspecified) {
-               // No prefix given, calculate it
-
-               if (v == 0) {
-                       prefix = SIPrefix::none;
-               } else {
-                       int exp = exponent(SIPrefix::yotta);
-                       prefix = SIPrefix::yocto;
-                       while ((fabs(v) * pow(Timestamp(10), exp)) > 999 &&
-                                       prefix < SIPrefix::yotta) {
-                               prefix = successor(prefix);
-                               exp -= 3;
-                       }
-               }
+               prefix = determine_value_prefix(v);
 
                const int prefix_order = -exponent(prefix);
                precision = (prefix >= SIPrefix::none) ? max((int)(precision + prefix_order), 0) :
@@ -175,7 +169,7 @@ QString format_value_si(double v, SIPrefix prefix, unsigned precision,
        QString s;
        QTextStream ts(&s);
        if (sign && (v != 0))
-               ts << forcesign;
+               ts.setNumberFlags(ts.numberFlags() | QTextStream::ForceSign);
        ts.setRealNumberNotation(QTextStream::FixedNotation);
        ts.setRealNumberPrecision(precision);
        ts << (v * multiplier) << ' ' << prefix << unit;
@@ -285,5 +279,22 @@ vector<string> split_string(string text, string separator)
        return result;
 }
 
+/**
+ * Return the width of a string in a given font.
+ *
+ * @param[in] metric metrics of the font
+ * @param[in] string the string whose width should be determined
+ *
+ * @return width of the string in pixels
+ */
+std::streamsize text_width(const QFontMetrics &metric, const QString &string)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
+       return metric.horizontalAdvance(string);
+#else
+       return metric.width(string);
+#endif
+}
+
 } // namespace util
 } // namespace pv
index e1640c4a374d8a9aed51231ae03a6b1c895486b1..67236c0dcc62420cf1be2a78bc0fa36a5f834d6f 100644 (file)
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_UTIL_HPP
-#define PULSEVIEW_UTIL_HPP
+#ifndef PULSEVIEW_PV_UTIL_HPP
+#define PULSEVIEW_PV_UTIL_HPP
 
 #include <cmath>
 #include <string>
 #include <vector>
 
 #ifndef Q_MOC_RUN
+// Workaround for https://github.com/boostorg/serialization/issues/186
+#include <boost/serialization/nvp.hpp>
+
 #include <boost/multiprecision/cpp_dec_float.hpp>
 #endif
 
 #include <QMetaType>
 #include <QString>
+#include <QFontMetrics>
 
 using std::string;
 using std::vector;
@@ -62,6 +66,11 @@ typedef boost::multiprecision::number<
        boost::multiprecision::cpp_dec_float<24>,
        boost::multiprecision::et_off> Timestamp;
 
+/**
+ * Chooses a prefix so that the value in front of the decimal point is between 1 and 999.
+ */
+SIPrefix determine_value_prefix(double v);
+
 /**
  * Formats a given timestamp with the specified SI prefix.
  *
@@ -138,9 +147,18 @@ QString format_time_minutes(const Timestamp& t, signed precision = 0,
 
 vector<string> split_string(string text, string separator);
 
+/**
+ * Return the width of a string in a given font.
+ * @param[in] metric metrics of the font
+ * @param[in] string the string whose width should be determined
+ *
+ * @return width of the string in pixels
+ */
+std::streamsize text_width(const QFontMetrics &metric, const QString &string);
+
 } // namespace util
 } // namespace pv
 
 Q_DECLARE_METATYPE(pv::util::Timestamp)
 
-#endif // PULSEVIEW_UTIL_HPP
+#endif // PULSEVIEW_PV_UTIL_HPP
diff --git a/pv/views/decoder_binary/QHexView.cpp b/pv/views/decoder_binary/QHexView.cpp
new file mode 100644 (file)
index 0000000..0badadc
--- /dev/null
@@ -0,0 +1,785 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Victor Anjin <virinext@gmail.com>
+ * Copyright (C) 2019 Soeren Apel <soeren@apelpie.net>
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <limits>
+
+#include <QApplication>
+#include <QClipboard>
+#include <QDebug>
+#include <QFont>
+#include <QKeyEvent>
+#include <QScrollBar>
+#include <QSize>
+#include <QString>
+#include <QPainter>
+#include <QPaintEvent>
+
+#include "QHexView.hpp"
+
+using std::make_pair;
+
+const unsigned int BYTES_PER_LINE   = 16;
+const unsigned int HEXCHARS_IN_LINE = BYTES_PER_LINE * 3 - 1;
+const unsigned int GAP_ADR_HEX      = 10;
+const unsigned int GAP_HEX_ASCII    = 10;
+const unsigned int GAP_ASCII_SLIDER = 5;
+
+
+QHexView::QHexView(QWidget *parent):
+       QAbstractScrollArea(parent),
+       mode_(ChunkedDataMode),
+       data_(nullptr),
+       selectBegin_(0),
+       selectEnd_(0),
+       cursorPos_(0),
+       visible_range_(0, 0),
+       highlighted_sample_(std::numeric_limits<uint64_t>::max())
+{
+       setFont(QFont("Courier", 10));
+
+       charWidth_ = fontMetrics().boundingRect('X').width();
+       charHeight_ = fontMetrics().height();
+
+       setFocusPolicy(Qt::StrongFocus);
+
+       if (palette().color(QPalette::ButtonText).toHsv().value() > 127) {
+               // Color is bright
+               chunk_colors_.emplace_back(100, 149, 237); // QColorConstants::Svg::cornflowerblue
+               chunk_colors_.emplace_back(60, 179, 113);  // QColorConstants::Svg::mediumseagreen
+               chunk_colors_.emplace_back(210, 180, 140); // QColorConstants::Svg::tan
+               visible_range_color_ = QColor("#fff5ee");  // QColorConstants::Svg::seashell
+       } else {
+               // Color is dark
+               chunk_colors_.emplace_back(0, 0, 139);    // QColorConstants::Svg::darkblue
+               chunk_colors_.emplace_back(34, 139, 34);  // QColorConstants::Svg::forestgreen
+               chunk_colors_.emplace_back(160, 82, 45);  // QColorConstants::Svg::sienna
+               visible_range_color_ = QColor("#fff5ee"); // QColorConstants::Svg::seashell
+       }
+}
+
+void QHexView::set_mode(Mode m)
+{
+       mode_ = m;
+
+       // This is not expected to be set when data is showing,
+       // so we don't update the viewport here
+}
+
+void QHexView::set_data(const DecodeBinaryClass* data)
+{
+       data_ = data;
+
+       size_t size = 0;
+       if (data) {
+               size_t chunks = data_->chunks.size();
+               for (size_t i = 0; i < chunks; i++)
+                       size += data_->chunks[i].data.size();
+       }
+       data_size_ = size;
+
+       address_digits_ = (uint8_t)QString::number(data_size_, 16).length();
+
+       // Calculate X coordinates of the three sub-areas
+       posAddr_  = 0;
+       posHex_   = address_digits_ * charWidth_ + GAP_ADR_HEX;
+       posAscii_ = posHex_ + HEXCHARS_IN_LINE * charWidth_ + GAP_HEX_ASCII;
+
+       viewport()->update();
+}
+
+void QHexView::set_visible_sample_range(uint64_t start, uint64_t end)
+{
+       visible_range_ = make_pair(start, end);
+
+       viewport()->update();
+}
+
+void QHexView::set_highlighted_data_sample(uint64_t sample)
+{
+       highlighted_sample_ = sample;
+
+       viewport()->update();
+}
+
+unsigned int QHexView::get_bytes_per_line() const
+{
+       return BYTES_PER_LINE;
+}
+
+void QHexView::clear()
+{
+       verticalScrollBar()->setValue(0);
+       data_ = nullptr;
+       data_size_ = 0;
+
+       highlighted_sample_ = std::numeric_limits<uint64_t>::max();
+
+       viewport()->update();
+}
+
+void QHexView::showFromOffset(size_t offset)
+{
+       if (data_ && (offset < data_size_)) {
+               setCursorPos(offset * 2);
+
+               int cursorY = cursorPos_ / (2 * BYTES_PER_LINE);
+               verticalScrollBar() -> setValue(cursorY);
+       }
+
+       viewport()->update();
+}
+
+QSizePolicy QHexView::sizePolicy() const
+{
+       return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
+}
+
+pair<size_t, size_t> QHexView::get_selection() const
+{
+       size_t start = selectBegin_ / 2;
+       size_t end = selectEnd_ / 2;
+
+       if (start == end) {
+               // Nothing is currently selected
+               start = 0;
+               end = data_size_;
+       } if (end < data_size_)
+               end++;
+
+       return std::make_pair(start, end);
+}
+
+size_t QHexView::create_hex_line(size_t start, size_t end, QString* dest,
+       bool with_offset, bool with_ascii)
+{
+       dest->clear();
+
+       // Determine start address for the row
+       uint64_t row = start / BYTES_PER_LINE;
+       uint64_t offset = row * BYTES_PER_LINE;
+       end = std::min((uint64_t)end, offset + BYTES_PER_LINE);
+
+       if (with_offset)
+               dest->append(QString("%1 ").arg(row * BYTES_PER_LINE, address_digits_, 16, QChar('0')).toUpper());
+
+       initialize_byte_iterator(offset);
+       for (size_t i = offset; i < offset + BYTES_PER_LINE; i++) {
+               uint8_t value = 0;
+
+               if (i < end)
+                       value = get_next_byte();
+
+               if ((i < start) || (i >= end))
+                       dest->append("   ");
+               else
+                       dest->append(QString("%1 ").arg(value, 2, 16, QChar('0')).toUpper());
+       }
+
+       if (with_ascii) {
+               initialize_byte_iterator(offset);
+               for (size_t i = offset; i < end; i++) {
+                       uint8_t value = get_next_byte();
+
+                       if ((value < 0x20) || (value > 0x7E))
+                               value = '.';
+
+                       if (i < start)
+                               dest->append(' ');
+                       else
+                               dest->append((char)value);
+               }
+       }
+
+       return end;
+}
+
+void QHexView::initialize_byte_iterator(size_t offset)
+{
+       current_chunk_id_ = 0;
+       current_chunk_offset_ = 0;
+       current_offset_ = offset;
+
+       size_t chunks = data_->chunks.size();
+       for (size_t i = 0; i < chunks; i++) {
+               size_t size = data_->chunks[i].data.size();
+
+               if (offset >= size) {
+                       current_chunk_id_++;
+                       offset -= size;
+               } else {
+                       current_chunk_offset_ = offset;
+                       break;
+               }
+       }
+
+       if (current_chunk_id_ < data_->chunks.size())
+               current_chunk_ = data_->chunks[current_chunk_id_];
+
+       current_chunk_sample_ = current_chunk_.sample;
+
+       // Obtain sample of next chunk if there is one
+       if ((current_chunk_id_ + 1) < data_->chunks.size())
+               next_chunk_sample_ = data_->chunks[current_chunk_id_ + 1].sample;
+       else
+               next_chunk_sample_ = std::numeric_limits<uint64_t>::max();
+}
+
+uint8_t QHexView::get_next_byte(bool* is_new_chunk)
+{
+       if (is_new_chunk != nullptr)
+               *is_new_chunk = (current_chunk_offset_ == 0);
+
+       uint8_t v = 0;
+       if (current_chunk_offset_ < current_chunk_.data.size())
+               v = current_chunk_.data[current_chunk_offset_];
+
+       current_chunk_sample_ = current_chunk_.sample;
+
+       if (is_new_chunk) {
+               // Obtain sample of next chunk if there is one
+               if ((current_chunk_id_ + 1) < data_->chunks.size())
+                       next_chunk_sample_ = data_->chunks[current_chunk_id_ + 1].sample;
+               else
+                       next_chunk_sample_ = std::numeric_limits<uint64_t>::max();
+       }
+
+       current_offset_++;
+       current_chunk_offset_++;
+
+       if (current_offset_ > data_size_) {
+               qWarning() << "QHexView::get_next_byte() overran binary data boundary:" <<
+                       current_offset_ << "of" << data_size_ << "bytes";
+               return 0xEE;
+       }
+
+       if ((current_chunk_offset_ == current_chunk_.data.size()) && (current_offset_ < data_size_)) {
+               current_chunk_id_++;
+               current_chunk_offset_ = 0;
+               current_chunk_ = data_->chunks[current_chunk_id_];
+       }
+
+       return v;
+}
+
+QSize QHexView::getFullSize() const
+{
+       size_t width = posAscii_ + (BYTES_PER_LINE * charWidth_);
+
+       if (verticalScrollBar()->isEnabled())
+               width += GAP_ASCII_SLIDER + verticalScrollBar()->width();
+
+       if (!data_ || (data_size_ == 0))
+               return QSize(width, 0);
+
+       size_t height = data_size_ / BYTES_PER_LINE;
+
+       if (data_size_ % BYTES_PER_LINE)
+               height++;
+
+       height *= charHeight_;
+
+       return QSize(width, height);
+}
+
+void QHexView::paintEvent(QPaintEvent *event)
+{
+       QPainter painter(viewport());
+
+       QFont normal_font = painter.font();
+       QFont bold_font = painter.font();
+       bold_font.setWeight(QFont::Bold);
+
+       bool bold_font_was_used = false;
+
+       // Calculate and update the widget and paint area sizes
+       QSize widgetSize = getFullSize();
+       setMinimumWidth(widgetSize.width());
+       setMaximumWidth(widgetSize.width());
+       QSize areaSize = viewport()->size() - QSize(0, charHeight_);
+
+       // Only show scrollbar if the content goes beyond the visible area
+       if (widgetSize.height() > areaSize.height()) {
+               verticalScrollBar()->setEnabled(true);
+               verticalScrollBar()->setPageStep(areaSize.height() / charHeight_);
+               verticalScrollBar()->setRange(0, ((widgetSize.height() - areaSize.height())) / charHeight_ + 1);
+       } else
+               verticalScrollBar()->setEnabled(false);
+
+       // Fill widget background
+       painter.fillRect(event->rect(), palette().color(QPalette::Base));
+
+       if (!data_ || (data_size_ == 0) || (data_->chunks.empty())) {
+               painter.setPen(palette().color(QPalette::Text));
+               QString s = tr("No data available");
+               int x = (areaSize.width() - fontMetrics().boundingRect(s).width()) / 2;
+               int y = areaSize.height() / 2;
+               painter.drawText(x, y, s);
+               return;
+       }
+
+       // Determine first/last line indices
+       size_t firstLineIdx = verticalScrollBar()->value();
+
+       size_t lastLineIdx = firstLineIdx + (areaSize.height() / charHeight_);
+       if (lastLineIdx > (data_size_ / BYTES_PER_LINE)) {
+               lastLineIdx = data_size_ / BYTES_PER_LINE;
+               if (data_size_ % BYTES_PER_LINE)
+                       lastLineIdx++;
+       }
+
+       // Paint divider line between hex and ASCII areas
+       int line_x = posAscii_ - (GAP_HEX_ASCII / 2);
+       painter.setPen(palette().color(QPalette::Midlight));
+       painter.drawLine(line_x, event->rect().top(), line_x, height());
+
+       // Fill address area background
+       painter.fillRect(QRect(posAddr_, event->rect().top(),
+               posHex_ - (GAP_ADR_HEX / 2), height()), palette().color(QPalette::Window));
+       painter.fillRect(QRect(posAddr_, event->rect().top(),
+               posAscii_ - (GAP_HEX_ASCII / 2), charHeight_ + 2), palette().color(QPalette::Window));
+
+       // Paint address area
+       painter.setPen(palette().color(QPalette::ButtonText));
+
+       int yStart = 2 * charHeight_;
+       for (size_t lineIdx = firstLineIdx, y = yStart; lineIdx < lastLineIdx; lineIdx++) {
+
+               QString address = QString("%1").arg(lineIdx * 16, address_digits_, 16, QChar('0')).toUpper();
+               painter.drawText(posAddr_, y, address);
+               y += charHeight_;
+       }
+
+       // Paint top row with hex offsets
+       painter.setPen(palette().color(QPalette::ButtonText));
+       for (int offset = 0; offset <= 0xF; offset++)
+               painter.drawText(posHex_ + (1 + offset * 3) * charWidth_,
+                       charHeight_ - 3, QString::number(offset, 16).toUpper());
+
+       // Paint hex values
+       QBrush regular_brush = palette().buttonText();
+       QBrush selected_brush = palette().highlight();
+       QBrush visible_range_brush = QBrush(visible_range_color_);
+
+       bool multiple_chunks = (data_->chunks.size() > 1);
+       unsigned int chunk_color = 0;
+
+       initialize_byte_iterator(firstLineIdx * BYTES_PER_LINE);
+
+       yStart = 2 * charHeight_;
+       for (size_t lineIdx = firstLineIdx, y = yStart; lineIdx < lastLineIdx; lineIdx++) {
+
+               int x = posHex_;
+               for (size_t i = 0; (i < BYTES_PER_LINE) && (current_offset_ < data_size_); i++) {
+                       size_t pos = (lineIdx * BYTES_PER_LINE + i) * 2;
+
+                       // Fetch byte
+                       bool is_new_chunk;
+                       uint8_t byte_value = get_next_byte(&is_new_chunk);
+
+                       if (is_new_chunk) {
+                               chunk_color++;
+                               if (chunk_color == chunk_colors_.size())
+                                       chunk_color = 0;
+
+                               // New chunk means also new chunk sample, so check for required changes
+                               if (bold_font_was_used)
+                                       painter.setFont(normal_font);
+                               if ((highlighted_sample_ >= current_chunk_sample_) && (highlighted_sample_ < next_chunk_sample_)) {
+                                       painter.setFont(bold_font);
+                                       bold_font_was_used = true;
+                               }
+                       }
+
+                       // Restore default paint style
+                       painter.setBackground(regular_brush);
+                       painter.setBackgroundMode(Qt::TransparentMode);
+                       if (!multiple_chunks)
+                               painter.setPen(palette().color(QPalette::Text));
+                       else
+                               painter.setPen(chunk_colors_[chunk_color]);
+
+                       // Highlight needed because it's the range visible in main view?
+                       if ((current_chunk_sample_ >= visible_range_.first) && (current_chunk_sample_ < visible_range_.second)) {
+                               painter.setBackgroundMode(Qt::OpaqueMode);
+                               painter.setBackground(visible_range_brush);
+                       }
+
+                       // Highlight for selection range needed? (takes priority over visible range highlight)
+                       if ((pos >= selectBegin_) && (pos < selectEnd_)) {
+                               painter.setBackgroundMode(Qt::OpaqueMode);
+                               painter.setBackground(selected_brush);
+                               painter.setPen(palette().color(QPalette::HighlightedText));
+                       }
+
+                       // First nibble
+                       QString val = QString::number((byte_value & 0xF0) >> 4, 16).toUpper();
+                       painter.drawText(x, y, val);
+
+                       // Second nibble
+                       val = QString::number((byte_value & 0xF), 16).toUpper();
+                       painter.drawText(x + charWidth_, y, val);
+
+                       if ((i < BYTES_PER_LINE - 1) && (current_offset_ < data_size_))
+                               painter.drawText(x + 2 * charWidth_, y, QString(' '));
+
+                       x += 3 * charWidth_;
+               }
+
+               y += charHeight_;
+       }
+
+       // Paint ASCII characters
+       initialize_byte_iterator(firstLineIdx * BYTES_PER_LINE);
+       yStart = 2 * charHeight_;
+       for (size_t lineIdx = firstLineIdx, y = yStart; lineIdx < lastLineIdx; lineIdx++) {
+
+               int x = posAscii_;
+               for (size_t i = 0; (i < BYTES_PER_LINE) && (current_offset_ < data_size_); i++) {
+                       // Fetch byte
+                       bool is_new_chunk;
+                       uint8_t ch = get_next_byte(&is_new_chunk);
+
+                       if (is_new_chunk) {
+                               // New chunk means also new chunk sample, so check for required changes
+                               if (bold_font_was_used)
+                                       painter.setFont(normal_font);
+                               if ((highlighted_sample_ >= current_chunk_sample_) && (highlighted_sample_ < next_chunk_sample_)) {
+                                       painter.setFont(bold_font);
+                                       bold_font_was_used = true;
+                               }
+                       }
+
+                       if ((ch < 0x20) || (ch > 0x7E))
+                               ch = '.';
+
+                       // Restore default paint style
+                       painter.setBackgroundMode(Qt::TransparentMode);
+                       painter.setBackground(regular_brush);
+                       painter.setPen(palette().color(QPalette::Text));
+
+                       // Highlight needed because it's the range visible in main view?
+                       if ((current_chunk_sample_ >= visible_range_.first) && (current_chunk_sample_ < visible_range_.second)) {
+                               painter.setBackgroundMode(Qt::OpaqueMode);
+                               painter.setBackground(visible_range_brush);
+                       }
+
+                       // Highlight for selection range needed? (takes priority over visible range highlight)
+                       size_t pos = (lineIdx * BYTES_PER_LINE + i) * 2;
+                       if ((pos >= selectBegin_) && (pos < selectEnd_)) {
+                               painter.setBackgroundMode(Qt::OpaqueMode);
+                               painter.setBackground(selected_brush);
+                               painter.setPen(palette().color(QPalette::HighlightedText));
+                       }
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+                       painter.drawText(x, y, QString(QChar(ch)));
+#else
+                       painter.drawText(x, y, QString(ch));
+#endif
+                       x += charWidth_;
+               }
+
+               y += charHeight_;
+       }
+
+       // Restore painter defaults
+       painter.setBackgroundMode(Qt::TransparentMode);
+       painter.setBackground(regular_brush);
+
+       // Paint cursor
+       if (hasFocus()) {
+               int x = (cursorPos_ % (2 * BYTES_PER_LINE));
+               int y = cursorPos_ / (2 * BYTES_PER_LINE);
+               y -= firstLineIdx;
+               int cursorX = (((x / 2) * 3) + (x % 2)) * charWidth_ + posHex_;
+               int cursorY = charHeight_ + y * charHeight_ + 4;
+               painter.fillRect(cursorX, cursorY, 2, charHeight_, palette().color(QPalette::WindowText));
+       }
+}
+
+void QHexView::keyPressEvent(QKeyEvent *event)
+{
+       bool setVisible = false;
+
+       // Cursor movements
+       if (event->matches(QKeySequence::MoveToNextChar)) {
+               setCursorPos(cursorPos_ + 1);
+               resetSelection(cursorPos_);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::MoveToPreviousChar)) {
+               setCursorPos(cursorPos_ - 1);
+               resetSelection(cursorPos_);
+               setVisible = true;
+       }
+
+       if (event->matches(QKeySequence::MoveToEndOfLine)) {
+               setCursorPos(cursorPos_ | ((BYTES_PER_LINE * 2) - 1));
+               resetSelection(cursorPos_);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::MoveToStartOfLine)) {
+               setCursorPos(cursorPos_ | (cursorPos_ % (BYTES_PER_LINE * 2)));
+               resetSelection(cursorPos_);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::MoveToPreviousLine)) {
+               setCursorPos(cursorPos_ - BYTES_PER_LINE * 2);
+               resetSelection(cursorPos_);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::MoveToNextLine)) {
+               setCursorPos(cursorPos_ + BYTES_PER_LINE * 2);
+               resetSelection(cursorPos_);
+               setVisible = true;
+       }
+
+       if (event->matches(QKeySequence::MoveToNextPage)) {
+               setCursorPos(cursorPos_ + (viewport()->height() / charHeight_ - 1) * 2 * BYTES_PER_LINE);
+               resetSelection(cursorPos_);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::MoveToPreviousPage)) {
+               setCursorPos(cursorPos_ - (viewport()->height() / charHeight_ - 1) * 2 * BYTES_PER_LINE);
+               resetSelection(cursorPos_);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::MoveToEndOfDocument)) {
+               setCursorPos(data_size_ * 2);
+               resetSelection(cursorPos_);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::MoveToStartOfDocument)) {
+               setCursorPos(0);
+               resetSelection(cursorPos_);
+               setVisible = true;
+       }
+
+       // Select commands
+       if (event->matches(QKeySequence::SelectAll)) {
+               resetSelection(0);
+               setSelection(2 * data_size_);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::SelectNextChar)) {
+               int pos = cursorPos_ + 1;
+               setCursorPos(pos);
+               setSelection(pos);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::SelectPreviousChar)) {
+               int pos = cursorPos_ - 1;
+               setSelection(pos);
+               setCursorPos(pos);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::SelectEndOfLine)) {
+               int pos = cursorPos_ - (cursorPos_ % (2 * BYTES_PER_LINE)) + (2 * BYTES_PER_LINE);
+               setCursorPos(pos);
+               setSelection(pos);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::SelectStartOfLine)) {
+               int pos = cursorPos_ - (cursorPos_ % (2 * BYTES_PER_LINE));
+               setCursorPos(pos);
+               setSelection(pos);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::SelectPreviousLine)) {
+               int pos = cursorPos_ - (2 * BYTES_PER_LINE);
+               setCursorPos(pos);
+               setSelection(pos);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::SelectNextLine)) {
+               int pos = cursorPos_ + (2 * BYTES_PER_LINE);
+               setCursorPos(pos);
+               setSelection(pos);
+               setVisible = true;
+       }
+
+       if (event->matches(QKeySequence::SelectNextPage)) {
+               int pos = cursorPos_ + (((viewport()->height() / charHeight_) - 1) * 2 * BYTES_PER_LINE);
+               setCursorPos(pos);
+               setSelection(pos);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::SelectPreviousPage)) {
+               int pos = cursorPos_ - (((viewport()->height() / charHeight_) - 1) * 2 * BYTES_PER_LINE);
+               setCursorPos(pos);
+               setSelection(pos);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::SelectEndOfDocument)) {
+               int pos = data_size_ * 2;
+               setCursorPos(pos);
+               setSelection(pos);
+               setVisible = true;
+       }
+       if (event->matches(QKeySequence::SelectStartOfDocument)) {
+               setCursorPos(0);
+               setSelection(0);
+               setVisible = true;
+       }
+
+       if (event->matches(QKeySequence::Copy) && (data_)) {
+               QString text;
+
+               initialize_byte_iterator(selectBegin_ / 2);
+
+               size_t selectedSize = (selectEnd_ - selectBegin_ + 1) / 2;
+               for (size_t i = 0; i < selectedSize; i++) {
+                       uint8_t byte_value = get_next_byte();
+
+                       QString s = QString::number((byte_value & 0xF0) >> 4, 16).toUpper() +
+                               QString::number((byte_value & 0xF), 16).toUpper() + " ";
+                       text += s;
+
+                       if (i % BYTES_PER_LINE == (BYTES_PER_LINE - 1))
+                               text += "\n";
+               }
+
+               QClipboard *clipboard = QApplication::clipboard();
+               clipboard->setText(text, QClipboard::Clipboard);
+               if (clipboard->supportsSelection())
+                       clipboard->setText(text, QClipboard::Selection);
+       }
+
+       if (setVisible)
+               ensureVisible();
+
+       viewport()->update();
+}
+
+void QHexView::mouseMoveEvent(QMouseEvent *event)
+{
+       int actPos = cursorPosFromMousePos(event->pos());
+       setCursorPos(actPos);
+       setSelection(actPos);
+
+       viewport()->update();
+}
+
+void QHexView::mousePressEvent(QMouseEvent *event)
+{
+       int cPos = cursorPosFromMousePos(event->pos());
+
+       if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) && (event->button() == Qt::LeftButton))
+               setSelection(cPos);
+       else
+               resetSelection(cPos);
+
+       setCursorPos(cPos);
+
+       viewport()->update();
+}
+
+size_t QHexView::cursorPosFromMousePos(const QPoint &position)
+{
+       size_t pos = -1;
+
+       if (((size_t)position.x() >= posHex_) &&
+               ((size_t)position.x() < (posHex_ + HEXCHARS_IN_LINE * charWidth_))) {
+
+               // Note: We add 1.5 character widths so that selection across
+               // byte gaps is smoother
+               size_t x = (position.x() + (1.5 * charWidth_ / 2) - posHex_) / charWidth_;
+
+               // Note: We allow only full bytes to be selected, not nibbles,
+               // so we round to the nearest byte gap
+               x = (2 * x + 1) / 3;
+
+               size_t firstLineIdx = verticalScrollBar()->value();
+               size_t y = ((position.y() / charHeight_) - 1) * 2 * BYTES_PER_LINE;
+               pos = x + y + firstLineIdx * BYTES_PER_LINE * 2;
+       }
+
+       size_t max_pos = data_size_ * 2;
+
+       return std::min(pos, max_pos);
+}
+
+void QHexView::resetSelection()
+{
+       selectBegin_ = selectInit_;
+       selectEnd_ = selectInit_;
+}
+
+void QHexView::resetSelection(int pos)
+{
+       if (pos < 0)
+               pos = 0;
+
+       selectInit_ = pos;
+       selectBegin_ = pos;
+       selectEnd_ = pos;
+}
+
+void QHexView::setSelection(int pos)
+{
+       if (pos < 0)
+               pos = 0;
+
+       if ((size_t)pos >= selectInit_) {
+               selectEnd_ = pos;
+               selectBegin_ = selectInit_;
+       } else {
+               selectBegin_ = pos;
+               selectEnd_ = selectInit_;
+       }
+}
+
+void QHexView::setCursorPos(int position)
+{
+       if (position < 0)
+               position = 0;
+
+       int max_pos = data_size_ * 2;
+
+       if (position > max_pos)
+               position = max_pos;
+
+       cursorPos_ = position;
+}
+
+void QHexView::ensureVisible()
+{
+       QSize areaSize = viewport()->size();
+
+       int firstLineIdx = verticalScrollBar()->value();
+       int lastLineIdx = firstLineIdx + areaSize.height() / charHeight_;
+
+       int cursorY = cursorPos_ / (2 * BYTES_PER_LINE);
+
+       if (cursorY < firstLineIdx)
+               verticalScrollBar()->setValue(cursorY);
+       else
+               if(cursorY >= lastLineIdx)
+                       verticalScrollBar()->setValue(cursorY - areaSize.height() / charHeight_ + 1);
+}
diff --git a/pv/views/decoder_binary/QHexView.hpp b/pv/views/decoder_binary/QHexView.hpp
new file mode 100644 (file)
index 0000000..4ba995e
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Victor Anjin <virinext@gmail.com>
+ * Copyright (C) 2019 Soeren Apel <soeren@apelpie.net>
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_DECODER_BINARY_QHEXVIEW_HPP
+#define PULSEVIEW_PV_VIEWS_DECODER_BINARY_QHEXVIEW_HPP
+
+#include <QAbstractScrollArea>
+
+#include <pv/data/decodesignal.hpp>
+
+using std::pair;
+using std::size_t;
+using pv::data::DecodeBinaryClass;
+using pv::data::DecodeBinaryDataChunk;
+
+class QHexView: public QAbstractScrollArea
+{
+       Q_OBJECT
+
+public:
+       enum Mode {
+               ChunkedDataMode,    ///< Displays all data chunks in succession
+               MemoryEmulationMode ///< Reconstructs memory contents from data chunks
+       };
+
+public:
+       QHexView(QWidget *parent = nullptr);
+
+       void set_mode(Mode m);
+       void set_data(const DecodeBinaryClass* data);
+
+       /* Sets range of samples that are visible in the main view */
+       void set_visible_sample_range(uint64_t start, uint64_t end);
+
+       /* Sets sample whose associated data we should highlight */
+       void set_highlighted_data_sample(uint64_t sample);
+
+       unsigned int get_bytes_per_line() const;
+
+       void clear();
+       void showFromOffset(size_t offset);
+       virtual QSizePolicy sizePolicy() const;
+
+       pair<size_t, size_t> get_selection() const;
+
+       size_t create_hex_line(size_t start, size_t end, QString* dest,
+               bool with_offset=false, bool with_ascii=false);
+
+protected:
+       void initialize_byte_iterator(size_t offset);
+       uint8_t get_next_byte(bool* is_new_chunk = nullptr);
+
+       void paintEvent(QPaintEvent *event);
+       void keyPressEvent(QKeyEvent *event);
+       void mouseMoveEvent(QMouseEvent *event);
+       void mousePressEvent(QMouseEvent *event);
+
+private:
+       QSize getFullSize() const;
+       void resetSelection();
+       void resetSelection(int pos);
+       void setSelection(int pos);
+       void ensureVisible();
+       void setCursorPos(int pos);
+       size_t cursorPosFromMousePos(const QPoint &position);
+
+private:
+       Mode mode_;
+       const DecodeBinaryClass* data_;
+       size_t data_size_;
+
+       size_t posAddr_, posHex_, posAscii_;
+       size_t charWidth_, charHeight_;
+       size_t selectBegin_, selectEnd_, selectInit_, cursorPos_;
+       uint8_t address_digits_;
+
+       size_t current_chunk_id_, current_chunk_offset_, current_offset_;
+       DecodeBinaryDataChunk current_chunk_; // Cache locally so that we're not messed up when the vector is re-allocating its data
+       uint64_t current_chunk_sample_, next_chunk_sample_;
+
+       pair<uint64_t, uint64_t> visible_range_;
+       uint64_t highlighted_sample_;
+
+       vector<QColor> chunk_colors_;
+       QColor visible_range_color_;
+};
+
+#endif // PULSEVIEW_PV_VIEWS_DECODER_BINARY_QHEXVIEW_HPP
diff --git a/pv/views/decoder_binary/view.cpp b/pv/views/decoder_binary/view.cpp
new file mode 100644 (file)
index 0000000..6e61fa3
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2019 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <climits>
+
+#include <QByteArray>
+#include <QDebug>
+#include <QFileDialog>
+#include <QLabel>
+#include <QMenu>
+#include <QMessageBox>
+#include <QToolBar>
+#include <QVBoxLayout>
+
+#include <libsigrokdecode/libsigrokdecode.h>
+
+#include "view.hpp"
+#include "QHexView.hpp"
+
+#include "pv/globalsettings.hpp"
+#include "pv/session.hpp"
+#include "pv/util.hpp"
+#include "pv/data/decode/decoder.hpp"
+
+using pv::data::DecodeSignal;
+using pv::data::SignalBase;
+using pv::data::decode::Decoder;
+using pv::util::Timestamp;
+
+using std::shared_ptr;
+
+namespace pv {
+namespace views {
+namespace decoder_binary {
+
+const char* SaveTypeNames[SaveTypeCount] = {
+       "Binary",
+       "Hex Dump, plain",
+       "Hex Dump, with offset",
+       "Hex Dump, canonical"
+};
+
+
+View::View(Session &session, bool is_main_view, QMainWindow *parent) :
+       ViewBase(session, is_main_view, parent),
+
+       // Note: Place defaults in View::reset_view_state(), not here
+       parent_(parent),
+       decoder_selector_(new QComboBox()),
+       format_selector_(new QComboBox()),
+       class_selector_(new QComboBox()),
+       stacked_widget_(new QStackedWidget()),
+       hex_view_(new QHexView()),
+       save_button_(new QToolButton()),
+       save_action_(new QAction(this)),
+       signal_(nullptr)
+{
+       QVBoxLayout *root_layout = new QVBoxLayout(this);
+       root_layout->setContentsMargins(0, 0, 0, 0);
+
+       // Create toolbar
+       QToolBar* toolbar = new QToolBar();
+       toolbar->setContextMenuPolicy(Qt::PreventContextMenu);
+       parent->addToolBar(toolbar);
+
+       // Populate toolbar
+       toolbar->addWidget(new QLabel(tr("Decoder:")));
+       toolbar->addWidget(decoder_selector_);
+       toolbar->addWidget(class_selector_);
+       toolbar->addSeparator();
+       toolbar->addWidget(new QLabel(tr("Show data as")));
+       toolbar->addWidget(format_selector_);
+       toolbar->addSeparator();
+       toolbar->addWidget(save_button_);
+
+       // Add format types
+       format_selector_->addItem(tr("Hexdump"), QVariant(QString("text/hexdump")));
+
+       // Add widget stack
+       root_layout->addWidget(stacked_widget_);
+       stacked_widget_->addWidget(hex_view_);
+       stacked_widget_->setCurrentIndex(0);
+
+       connect(decoder_selector_, SIGNAL(currentIndexChanged(int)),
+               this, SLOT(on_selected_decoder_changed(int)));
+       connect(class_selector_, SIGNAL(currentIndexChanged(int)),
+               this, SLOT(on_selected_class_changed(int)));
+
+       // Configure widgets
+       decoder_selector_->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+       class_selector_->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+
+       // Configure actions
+       save_action_->setText(tr("&Save..."));
+       save_action_->setIcon(QIcon::fromTheme("document-save-as",
+               QIcon(":/icons/document-save-as.png")));
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       save_action_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
+#else
+       save_action_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
+#endif
+       connect(save_action_, SIGNAL(triggered(bool)),
+               this, SLOT(on_actionSave_triggered()));
+
+       QMenu *save_menu = new QMenu();
+       connect(save_menu, SIGNAL(triggered(QAction*)),
+               this, SLOT(on_actionSave_triggered(QAction*)));
+
+       for (int i = 0; i < SaveTypeCount; i++) {
+               QAction *const action = save_menu->addAction(tr(SaveTypeNames[i]));
+               action->setData(QVariant::fromValue(i));
+       }
+
+       save_button_->setMenu(save_menu);
+       save_button_->setDefaultAction(save_action_);
+       save_button_->setPopupMode(QToolButton::MenuButtonPopup);
+
+       parent->setSizePolicy(hex_view_->sizePolicy()); // TODO Must be updated when selected widget changes
+
+       // Set up metadata event handler
+       session_.metadata_obj_manager()->add_observer(this);
+
+       reset_view_state();
+}
+
+View::~View()
+{
+       session_.metadata_obj_manager()->remove_observer(this);
+}
+
+ViewType View::get_type() const
+{
+       return ViewTypeDecoderBinary;
+}
+
+void View::reset_view_state()
+{
+       ViewBase::reset_view_state();
+
+       decoder_selector_->clear();
+       class_selector_->clear();
+       format_selector_->setCurrentIndex(0);
+       save_button_->setEnabled(false);
+
+       hex_view_->clear();
+}
+
+void View::clear_decode_signals()
+{
+       ViewBase::clear_decode_signals();
+
+       reset_data();
+       reset_view_state();
+}
+
+void View::add_decode_signal(shared_ptr<data::DecodeSignal> signal)
+{
+       ViewBase::add_decode_signal(signal);
+
+       connect(signal.get(), SIGNAL(name_changed(const QString&)),
+               this, SLOT(on_signal_name_changed(const QString&)));
+       connect(signal.get(), SIGNAL(decoder_stacked(void*)),
+               this, SLOT(on_decoder_stacked(void*)));
+       connect(signal.get(), SIGNAL(decoder_removed(void*)),
+               this, SLOT(on_decoder_removed(void*)));
+
+       // Add all decoders provided by this signal
+       auto stack = signal->decoder_stack();
+       if (stack.size() > 1) {
+               for (const shared_ptr<Decoder>& dec : stack)
+                       // Only add the decoder if it has binary output
+                       if (dec->get_binary_class_count() > 0) {
+                               QString title = QString("%1 (%2)").arg(signal->name(), dec->name());
+                               decoder_selector_->addItem(title, QVariant::fromValue((void*)dec.get()));
+                       }
+       } else
+               if (!stack.empty()) {
+                       shared_ptr<Decoder>& dec = stack.at(0);
+                       if (dec->get_binary_class_count() > 0)
+                               decoder_selector_->addItem(signal->name(), QVariant::fromValue((void*)dec.get()));
+               }
+}
+
+void View::remove_decode_signal(shared_ptr<data::DecodeSignal> signal)
+{
+       // Remove all decoders provided by this signal
+       for (const shared_ptr<Decoder>& dec : signal->decoder_stack()) {
+               int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
+
+               if (index != -1)
+                       decoder_selector_->removeItem(index);
+       }
+
+       ViewBase::remove_decode_signal(signal);
+
+       if (signal.get() == signal_) {
+               reset_data();
+               update_data();
+               reset_view_state();
+       }
+}
+
+void View::save_settings(QSettings &settings) const
+{
+       ViewBase::save_settings(settings);
+}
+
+void View::restore_settings(QSettings &settings)
+{
+       // Note: It is assumed that this function is only called once,
+       // immediately after restoring a previous session.
+       ViewBase::restore_settings(settings);
+}
+
+void View::reset_data()
+{
+       signal_ = nullptr;
+       decoder_ = nullptr;
+       bin_class_id_ = 0;
+       binary_data_exists_ = false;
+
+       hex_view_->clear();
+}
+
+void View::update_data()
+{
+       if (!signal_)
+               return;
+
+       const DecodeBinaryClass* bin_class =
+               signal_->get_binary_data_class(current_segment_, decoder_, bin_class_id_);
+
+       hex_view_->set_data(bin_class);
+
+       if (!binary_data_exists_)
+               return;
+
+       if (!save_button_->isEnabled())
+               save_button_->setEnabled(true);
+}
+
+void View::save_data() const
+{
+       assert(decoder_);
+       assert(signal_);
+
+       if (!signal_)
+               return;
+
+       GlobalSettings settings;
+       const QString dir = settings.value("MainWindow/SaveDirectory").toString();
+
+       const QString file_name = QFileDialog::getSaveFileName(
+               parent_, tr("Save Binary Data"), dir, tr("Binary Data Files (*.bin);;All Files (*)"));
+
+       if (file_name.isEmpty())
+               return;
+
+       QFile file(file_name);
+       if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+               pair<size_t, size_t> selection = hex_view_->get_selection();
+
+               vector<uint8_t> data;
+               data.resize(selection.second - selection.first + 1);
+
+               signal_->get_merged_binary_data_chunks_by_offset(current_segment_, decoder_,
+                       bin_class_id_, selection.first, selection.second, &data);
+
+               int64_t bytes_written = file.write((const char*)data.data(), data.size());
+
+               if ((bytes_written == -1) || ((uint64_t)bytes_written != data.size())) {
+                       QMessageBox msg(parent_);
+                       msg.setText(tr("Error") + "\n\n" + tr("File %1 could not be written to.").arg(file_name));
+                       msg.setStandardButtons(QMessageBox::Ok);
+                       msg.setIcon(QMessageBox::Warning);
+                       msg.exec();
+                       return;
+               }
+       }
+}
+
+void View::save_data_as_hex_dump(bool with_offset, bool with_ascii) const
+{
+       assert(decoder_);
+       assert(signal_);
+
+       if (!signal_)
+               return;
+
+       GlobalSettings settings;
+       const QString dir = settings.value("MainWindow/SaveDirectory").toString();
+
+       const QString file_name = QFileDialog::getSaveFileName(
+               parent_, tr("Save Binary Data"), dir, tr("Hex Dumps (*.txt);;All Files (*)"));
+
+       if (file_name.isEmpty())
+               return;
+
+       QFile file(file_name);
+       if (file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
+               pair<size_t, size_t> selection = hex_view_->get_selection();
+
+               vector<uint8_t> data;
+               data.resize(selection.second - selection.first + 1);
+
+               signal_->get_merged_binary_data_chunks_by_offset(current_segment_, decoder_,
+                       bin_class_id_, selection.first, selection.second, &data);
+
+               QTextStream out_stream(&file);
+
+               uint64_t offset = selection.first;
+               uint64_t n = hex_view_->get_bytes_per_line();
+               QString s;
+
+               while (offset < selection.second) {
+                       size_t end = std::min((uint64_t)(selection.second), offset + n);
+                       offset = hex_view_->create_hex_line(offset, end, &s, with_offset, with_ascii);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+                       out_stream << s << Qt::endl;
+#else
+                       out_stream << s << endl;
+#endif
+               }
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+               out_stream << Qt::endl;
+#else
+               out_stream << endl;
+#endif
+
+               if (out_stream.status() != QTextStream::Ok) {
+                       QMessageBox msg(parent_);
+                       msg.setText(tr("Error") + "\n\n" + tr("File %1 could not be written to.").arg(file_name));
+                       msg.setStandardButtons(QMessageBox::Ok);
+                       msg.setIcon(QMessageBox::Warning);
+                       msg.exec();
+                       return;
+               }
+       }
+}
+
+void View::on_selected_decoder_changed(int index)
+{
+       if (signal_)
+               disconnect(signal_, SIGNAL(new_binary_data(unsigned int, void*, unsigned int)));
+
+       reset_data();
+
+       decoder_ = (Decoder*)decoder_selector_->itemData(index).value<void*>();
+
+       // Find the signal that contains the selected decoder
+       for (const shared_ptr<DecodeSignal>& ds : decode_signals_)
+               for (const shared_ptr<Decoder>& dec : ds->decoder_stack())
+                       if (decoder_ == dec.get())
+                               signal_ = ds.get();
+
+       class_selector_->clear();
+
+       if (signal_) {
+               // Populate binary class selector
+               uint32_t bin_classes = decoder_->get_binary_class_count();
+               for (uint32_t i = 0; i < bin_classes; i++) {
+                       const data::decode::DecodeBinaryClassInfo* class_info = decoder_->get_binary_class(i);
+                       class_selector_->addItem(class_info->description, QVariant::fromValue(i));
+               }
+
+               connect(signal_, SIGNAL(new_binary_data(unsigned int, void*, unsigned int)),
+                       this, SLOT(on_new_binary_data(unsigned int, void*, unsigned int)));
+       }
+
+       update_data();
+}
+
+void View::on_selected_class_changed(int index)
+{
+       bin_class_id_ = class_selector_->itemData(index).value<uint32_t>();
+
+       binary_data_exists_ = (signal_) ?
+               signal_->get_binary_data_chunk_count(current_segment_, decoder_, bin_class_id_) :
+               false;
+
+       update_data();
+}
+
+void View::on_signal_name_changed(const QString &name)
+{
+       (void)name;
+
+       SignalBase* sb = qobject_cast<SignalBase*>(QObject::sender());
+       assert(sb);
+
+       DecodeSignal* signal = dynamic_cast<DecodeSignal*>(sb);
+       assert(signal);
+
+       // Update all decoder entries provided by this signal
+       auto stack = signal->decoder_stack();
+       if (stack.size() > 1) {
+               for (const shared_ptr<Decoder>& dec : stack) {
+                       QString title = QString("%1 (%2)").arg(signal->name(), dec->name());
+                       int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
+
+                       if (index != -1)
+                               decoder_selector_->setItemText(index, title);
+               }
+       } else
+               if (!stack.empty()) {
+                       shared_ptr<Decoder>& dec = stack.at(0);
+                       int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
+
+                       if (index != -1)
+                               decoder_selector_->setItemText(index, signal->name());
+               }
+}
+
+void View::on_new_binary_data(unsigned int segment_id, void* decoder, unsigned int bin_class_id)
+{
+       if ((segment_id == current_segment_) && (decoder == decoder_) && (bin_class_id == bin_class_id_))
+               if (!delayed_view_updater_.isActive())
+                       delayed_view_updater_.start();
+}
+
+void View::on_decoder_stacked(void* decoder)
+{
+       // TODO This doesn't change existing entries for the same signal - but it should as the naming scheme may change
+
+       Decoder* d = static_cast<Decoder*>(decoder);
+
+       // Only add the decoder if it has binary output
+       if (d->get_binary_class_count() == 0)
+               return;
+
+       // Find the signal that contains the selected decoder
+       DecodeSignal* signal = nullptr;
+
+       for (const shared_ptr<DecodeSignal>& ds : decode_signals_)
+               for (const shared_ptr<Decoder>& dec : ds->decoder_stack())
+                       if (d == dec.get())
+                               signal = ds.get();
+
+       assert(signal);
+
+       // Add the decoder to the list
+       QString title = QString("%1 (%2)").arg(signal->name(), d->name());
+       decoder_selector_->addItem(title, QVariant::fromValue((void*)d));
+}
+
+void View::on_decoder_removed(void* decoder)
+{
+       Decoder* d = static_cast<Decoder*>(decoder);
+
+       // Remove the decoder from the list
+       int index = decoder_selector_->findData(QVariant::fromValue((void*)d));
+
+       if (index != -1)
+               decoder_selector_->removeItem(index);
+}
+
+void View::on_actionSave_triggered(QAction* action)
+{
+       int save_type = SaveTypeBinary;
+       if (action)
+               save_type = action->data().toInt();
+
+       switch (save_type)
+       {
+       case SaveTypeBinary: save_data(); break;
+       case SaveTypeHexDumpPlain: save_data_as_hex_dump(false, false); break;
+       case SaveTypeHexDumpWithOffset: save_data_as_hex_dump(true, false); break;
+       case SaveTypeHexDumpComplete: save_data_as_hex_dump(true, true); break;
+       }
+}
+
+void View::on_metadata_object_changed(MetadataObject* obj,
+       MetadataValueType value_type)
+{
+       // Check if we need to update the model's data range. We only work on the
+       // end sample value because the start sample value is updated first and
+       // we need both
+       if ((obj->type() == MetadataObjMainViewRange) &&
+               (value_type == MetadataValueEndSample)) {
+
+               int64_t start_sample = obj->value(MetadataValueStartSample).toLongLong();
+               int64_t end_sample = obj->value(MetadataValueEndSample).toLongLong();
+
+               hex_view_->set_visible_sample_range(start_sample, end_sample);
+       }
+
+       if (obj->type() == MetadataObjMousePos)
+               hex_view_->set_highlighted_data_sample(obj->value(MetadataValueStartSample).toLongLong());
+}
+
+void View::perform_delayed_view_update()
+{
+       if (signal_ && !binary_data_exists_)
+               if (signal_->get_binary_data_chunk_count(current_segment_, decoder_, bin_class_id_))
+                       binary_data_exists_ = true;
+
+       update_data();
+}
+
+
+} // namespace decoder_binary
+} // namespace views
+} // namespace pv
diff --git a/pv/views/decoder_binary/view.hpp b/pv/views/decoder_binary/view.hpp
new file mode 100644 (file)
index 0000000..530e603
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2019 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_DECODER_BINARY_VIEW_HPP
+#define PULSEVIEW_PV_VIEWS_DECODER_BINARY_VIEW_HPP
+
+#include <QAction>
+#include <QComboBox>
+#include <QStackedWidget>
+#include <QToolButton>
+
+#include "pv/metadata_obj.hpp"
+#include "pv/views/viewbase.hpp"
+#include "pv/data/decodesignal.hpp"
+
+#include "QHexView.hpp"
+
+namespace pv {
+
+class Session;
+
+namespace views {
+
+namespace decoder_binary {
+
+// When adding an entry here, don't forget to update SaveTypeNames as well
+enum SaveType {
+       SaveTypeBinary,
+       SaveTypeHexDumpPlain,
+       SaveTypeHexDumpWithOffset,
+       SaveTypeHexDumpComplete,
+       SaveTypeCount  // Indicates how many save types there are, must always be last
+};
+
+extern const char* SaveTypeNames[SaveTypeCount];
+
+
+class View : public ViewBase, public MetadataObjObserverInterface
+{
+       Q_OBJECT
+
+public:
+       explicit View(Session &session, bool is_main_view=false, QMainWindow *parent = nullptr);
+       ~View();
+
+       virtual ViewType get_type() const;
+
+       /**
+        * Resets the view to its default state after construction. It does however
+        * not reset the signal bases or any other connections with the session.
+        */
+       virtual void reset_view_state();
+
+       virtual void clear_decode_signals();
+       virtual void add_decode_signal(shared_ptr<data::DecodeSignal> signal);
+       virtual void remove_decode_signal(shared_ptr<data::DecodeSignal> signal);
+
+       virtual void save_settings(QSettings &settings) const;
+       virtual void restore_settings(QSettings &settings);
+
+private:
+       void reset_data();
+       void update_data();
+
+       void save_data() const;
+       void save_data_as_hex_dump(bool with_offset=false, bool with_ascii=false) const;
+
+private Q_SLOTS:
+       void on_selected_decoder_changed(int index);
+       void on_selected_class_changed(int index);
+       void on_signal_name_changed(const QString &name);
+       void on_new_binary_data(unsigned int segment_id, void* decoder, unsigned int bin_class_id);
+
+       void on_decoder_stacked(void* decoder);
+       void on_decoder_removed(void* decoder);
+
+       void on_actionSave_triggered(QAction* action = nullptr);
+
+       virtual void on_metadata_object_changed(MetadataObject* obj,
+               MetadataValueType value_type);
+
+       virtual void perform_delayed_view_update();
+
+private:
+       QWidget* parent_;
+
+       QComboBox *decoder_selector_, *format_selector_, *class_selector_;
+       QStackedWidget *stacked_widget_;
+       QHexView *hex_view_;
+
+       QToolButton* save_button_;
+       QAction* save_action_;
+
+       data::DecodeSignal *signal_;
+       const data::decode::Decoder *decoder_;
+       uint32_t bin_class_id_;
+       bool binary_data_exists_;
+};
+
+} // namespace decoder_binary
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_DECODER_BINARY_VIEW_HPP
diff --git a/pv/views/decoder_output/QHexView.cpp b/pv/views/decoder_output/QHexView.cpp
deleted file mode 100644 (file)
index 59fe779..0000000
+++ /dev/null
@@ -1,687 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2015 Victor Anjin <virinext@gmail.com>
- * Copyright (C) 2019 Soeren Apel <soeren@apelpie.net>
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include <QApplication>
-#include <QClipboard>
-#include <QDebug>
-#include <QFont>
-#include <QKeyEvent>
-#include <QScrollBar>
-#include <QSize>
-#include <QPainter>
-#include <QPaintEvent>
-
-#include "QHexView.hpp"
-
-const unsigned int BYTES_PER_LINE   = 16;
-const unsigned int HEXCHARS_IN_LINE = BYTES_PER_LINE * 3 - 1;
-const unsigned int GAP_ADR_HEX      = 10;
-const unsigned int GAP_HEX_ASCII    = 10;
-const unsigned int GAP_ASCII_SLIDER = 5;
-
-
-QHexView::QHexView(QWidget *parent):
-       QAbstractScrollArea(parent),
-       mode_(ChunkedDataMode),
-       data_(nullptr),
-       selectBegin_(0),
-       selectEnd_(0),
-       cursorPos_(0)
-{
-       setFont(QFont("Courier", 10));
-
-       charWidth_ = fontMetrics().boundingRect('X').width();
-       charHeight_ = fontMetrics().height();
-
-       // Determine X coordinates of the three sub-areas
-       posAddr_  = 0;
-       posHex_   = 10 * charWidth_ + GAP_ADR_HEX;
-       posAscii_ = posHex_ + HEXCHARS_IN_LINE * charWidth_ + GAP_HEX_ASCII;
-
-       setFocusPolicy(Qt::StrongFocus);
-
-       if (palette().color(QPalette::ButtonText).toHsv().value() > 127) {
-               // Color is bright
-               chunk_colors_.emplace_back(100, 149, 237); // QColorConstants::Svg::cornflowerblue
-               chunk_colors_.emplace_back(60, 179, 113);  // QColorConstants::Svg::mediumseagreen
-               chunk_colors_.emplace_back(210, 180, 140); // QColorConstants::Svg::tan
-       } else {
-               // Color is dark
-               chunk_colors_.emplace_back(0, 0, 139);   // QColorConstants::Svg::darkblue
-               chunk_colors_.emplace_back(34, 139, 34); // QColorConstants::Svg::forestgreen
-               chunk_colors_.emplace_back(160, 82, 45); // QColorConstants::Svg::sienna
-       }
-}
-
-void QHexView::set_mode(Mode m)
-{
-       mode_ = m;
-
-       // This is not expected to be set when data is showing,
-       // so we don't update the viewport here
-}
-
-void QHexView::set_data(const DecodeBinaryClass* data)
-{
-       data_ = data;
-
-       size_t size = 0;
-       if (data) {
-               size_t chunks = data_->chunks.size();
-               for (size_t i = 0; i < chunks; i++)
-                       size += data_->chunks[i].data.size();
-       }
-       data_size_ = size;
-
-       viewport()->update();
-}
-
-unsigned int QHexView::get_bytes_per_line() const
-{
-       return BYTES_PER_LINE;
-}
-
-void QHexView::clear()
-{
-       verticalScrollBar()->setValue(0);
-       data_ = nullptr;
-       data_size_ = 0;
-
-       viewport()->update();
-}
-
-void QHexView::showFromOffset(size_t offset)
-{
-       if (data_ && (offset < data_size_)) {
-               setCursorPos(offset * 2);
-
-               int cursorY = cursorPos_ / (2 * BYTES_PER_LINE);
-               verticalScrollBar() -> setValue(cursorY);
-       }
-
-       viewport()->update();
-}
-
-QSizePolicy QHexView::sizePolicy() const
-{
-       return QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
-}
-
-pair<size_t, size_t> QHexView::get_selection() const
-{
-       size_t start = selectBegin_ / 2;
-       size_t end = selectEnd_ / 2;
-
-       if (start == end) {
-               // Nothing is currently selected
-               start = 0;
-               end = data_size_;
-       } if (end < data_size_)
-               end++;
-
-       return std::make_pair(start, end);
-}
-
-size_t QHexView::create_hex_line(size_t start, size_t end, QString* dest,
-       bool with_offset, bool with_ascii)
-{
-       dest->clear();
-
-       // Determine start address for the row
-       uint64_t row = start / BYTES_PER_LINE;
-       uint64_t offset = row * BYTES_PER_LINE;
-       end = std::min((uint64_t)end, offset + BYTES_PER_LINE);
-
-       if (with_offset)
-               dest->append(QString("%1 ").arg(row * BYTES_PER_LINE, 10, 16, QChar('0')).toUpper());
-
-       initialize_byte_iterator(offset);
-       for (size_t i = offset; i < offset + BYTES_PER_LINE; i++) {
-               uint8_t value = 0;
-
-               if (i < end)
-                       value = get_next_byte();
-
-               if ((i < start) || (i >= end))
-                       dest->append("   ");
-               else
-                       dest->append(QString("%1 ").arg(value, 2, 16, QChar('0')).toUpper());
-       }
-
-       if (with_ascii) {
-               initialize_byte_iterator(offset);
-               for (size_t i = offset; i < end; i++) {
-                       uint8_t value = get_next_byte();
-
-                       if ((value < 0x20) || (value > 0x7E))
-                               value = '.';
-
-                       if (i < start)
-                               dest->append(' ');
-                       else
-                               dest->append((char)value);
-               }
-       }
-
-       return end;
-}
-
-void QHexView::initialize_byte_iterator(size_t offset)
-{
-       current_chunk_id_ = 0;
-       current_chunk_offset_ = 0;
-       current_offset_ = offset;
-
-       size_t chunks = data_->chunks.size();
-       for (size_t i = 0; i < chunks; i++) {
-               size_t size = data_->chunks[i].data.size();
-
-               if (offset >= size) {
-                       current_chunk_id_++;
-                       offset -= size;
-               } else {
-                       current_chunk_offset_ = offset;
-                       break;
-               }
-       }
-
-       if (current_chunk_id_ < data_->chunks.size())
-               current_chunk_ = data_->chunks[current_chunk_id_];
-}
-
-uint8_t QHexView::get_next_byte(bool* is_next_chunk)
-{
-       if (is_next_chunk != nullptr)
-               *is_next_chunk = (current_chunk_offset_ == 0);
-
-       uint8_t v = 0;
-       if (current_chunk_offset_ < current_chunk_.data.size())
-               v = current_chunk_.data[current_chunk_offset_];
-
-       current_offset_++;
-       current_chunk_offset_++;
-
-       if (current_offset_ > data_size_) {
-               qWarning() << "QHexView::get_next_byte() overran binary data boundary:" <<
-                       current_offset_ << "of" << data_size_ << "bytes";
-               return 0xEE;
-       }
-
-       if ((current_chunk_offset_ == current_chunk_.data.size()) && (current_offset_ < data_size_)) {
-               current_chunk_id_++;
-               current_chunk_offset_ = 0;
-               current_chunk_ = data_->chunks[current_chunk_id_];
-       }
-
-       return v;
-}
-
-QSize QHexView::getFullSize() const
-{
-       size_t width = posAscii_ + (BYTES_PER_LINE * charWidth_);
-
-       if (verticalScrollBar()->isEnabled())
-               width += GAP_ASCII_SLIDER + verticalScrollBar()->width();
-
-       if (!data_ || (data_size_ == 0))
-               return QSize(width, 0);
-
-       size_t height = data_size_ / BYTES_PER_LINE;
-
-       if (data_size_ % BYTES_PER_LINE)
-               height++;
-
-       height *= charHeight_;
-
-       return QSize(width, height);
-}
-
-void QHexView::paintEvent(QPaintEvent *event)
-{
-       QPainter painter(viewport());
-
-       // Calculate and update the widget and paint area sizes
-       QSize widgetSize = getFullSize();
-       setMinimumWidth(widgetSize.width());
-       setMaximumWidth(widgetSize.width());
-       QSize areaSize = viewport()->size() - QSize(0, charHeight_);
-
-       // Only show scrollbar if the content goes beyond the visible area
-       if (widgetSize.height() > areaSize.height()) {
-               verticalScrollBar()->setEnabled(true);
-               verticalScrollBar()->setPageStep(areaSize.height() / charHeight_);
-               verticalScrollBar()->setRange(0, ((widgetSize.height() - areaSize.height())) / charHeight_ + 1);
-       } else
-               verticalScrollBar()->setEnabled(false);
-
-       // Fill widget background
-       painter.fillRect(event->rect(), palette().color(QPalette::Base));
-
-       if (!data_ || (data_size_ == 0)) {
-               painter.setPen(palette().color(QPalette::Text));
-               QString s = tr("No data available");
-               int x = (areaSize.width() - fontMetrics().boundingRect(s).width()) / 2;
-               int y = areaSize.height() / 2;
-               painter.drawText(x, y, s);
-               return;
-       }
-
-       // Determine first/last line indices
-       size_t firstLineIdx = verticalScrollBar()->value();
-
-       size_t lastLineIdx = firstLineIdx + (areaSize.height() / charHeight_);
-       if (lastLineIdx > (data_size_ / BYTES_PER_LINE)) {
-               lastLineIdx = data_size_ / BYTES_PER_LINE;
-               if (data_size_ % BYTES_PER_LINE)
-                       lastLineIdx++;
-       }
-
-       // Fill address area background
-       painter.fillRect(QRect(posAddr_, event->rect().top(),
-               posHex_ - (GAP_ADR_HEX / 2), height()), palette().color(QPalette::Window));
-
-       // Paint divider line between hex and ASCII areas
-       int line_x = posAscii_ - (GAP_HEX_ASCII / 2);
-       painter.setPen(palette().color(QPalette::Midlight));
-       painter.drawLine(line_x, event->rect().top(), line_x, height());
-
-       // Paint address area
-       painter.setPen(palette().color(QPalette::ButtonText));
-
-       int yStart = 2 * charHeight_;
-       for (size_t lineIdx = firstLineIdx, y = yStart; lineIdx < lastLineIdx; lineIdx++) {
-
-               QString address = QString("%1").arg(lineIdx * 16, 10, 16, QChar('0')).toUpper();
-               painter.drawText(posAddr_, y, address);
-               y += charHeight_;
-       }
-
-       // Paint top row with hex offsets
-       painter.setPen(palette().color(QPalette::ButtonText));
-       for (int offset = 0; offset <= 0xF; offset++)
-               painter.drawText(posHex_ + (1 + offset * 3) * charWidth_,
-                       charHeight_, QString::number(offset, 16).toUpper());
-
-       // Paint hex values
-       QBrush regular = palette().buttonText();
-       QBrush selected = palette().highlight();
-
-       bool multiple_chunks = (data_->chunks.size() > 1);
-       unsigned int chunk_color = 0;
-
-       initialize_byte_iterator(firstLineIdx * BYTES_PER_LINE);
-       yStart = 2 * charHeight_;
-       for (size_t lineIdx = firstLineIdx, y = yStart; lineIdx < lastLineIdx; lineIdx++) {
-
-               int x = posHex_;
-               for (size_t i = 0; (i < BYTES_PER_LINE) && (current_offset_ < data_size_); i++) {
-                       size_t pos = (lineIdx * BYTES_PER_LINE + i) * 2;
-
-                       // Fetch byte
-                       bool is_next_chunk;
-                       uint8_t byte_value = get_next_byte(&is_next_chunk);
-
-                       if (is_next_chunk) {
-                               chunk_color++;
-                               if (chunk_color == chunk_colors_.size())
-                                       chunk_color = 0;
-                       }
-
-                       if ((pos >= selectBegin_) && (pos < selectEnd_)) {
-                               painter.setBackgroundMode(Qt::OpaqueMode);
-                               painter.setBackground(selected);
-                               painter.setPen(palette().color(QPalette::HighlightedText));
-                       } else {
-                               painter.setBackground(regular);
-                               painter.setBackgroundMode(Qt::TransparentMode);
-                               if (!multiple_chunks)
-                                       painter.setPen(palette().color(QPalette::Text));
-                               else
-                                       painter.setPen(chunk_colors_[chunk_color]);
-                       }
-
-                       // First nibble
-                       QString val = QString::number((byte_value & 0xF0) >> 4, 16).toUpper();
-                       painter.drawText(x, y, val);
-
-                       // Second nibble
-                       val = QString::number((byte_value & 0xF), 16).toUpper();
-                       painter.drawText(x + charWidth_, y, val);
-
-                       if ((pos >= selectBegin_) && (pos < selectEnd_ - 1) && (i < BYTES_PER_LINE - 1))
-                               painter.drawText(x + 2 * charWidth_, y, QString(' '));
-
-                       x += 3 * charWidth_;
-               }
-
-               y += charHeight_;
-       }
-
-       // Paint ASCII characters
-       initialize_byte_iterator(firstLineIdx * BYTES_PER_LINE);
-       yStart = 2 * charHeight_;
-       for (size_t lineIdx = firstLineIdx, y = yStart; lineIdx < lastLineIdx; lineIdx++) {
-
-               int x = posAscii_;
-               for (size_t i = 0; (i < BYTES_PER_LINE) && (current_offset_ < data_size_); i++) {
-                       // Fetch byte
-                       uint8_t ch = get_next_byte();
-
-                       if ((ch < 0x20) || (ch > 0x7E))
-                               ch = '.';
-
-                       size_t pos = (lineIdx * BYTES_PER_LINE + i) * 2;
-                       if ((pos >= selectBegin_) && (pos < selectEnd_)) {
-                               painter.setBackgroundMode(Qt::OpaqueMode);
-                               painter.setBackground(selected);
-                               painter.setPen(palette().color(QPalette::HighlightedText));
-                       } else {
-                               painter.setBackgroundMode(Qt::TransparentMode);
-                               painter.setBackground(regular);
-                               painter.setPen(palette().color(QPalette::Text));
-                       }
-
-                       painter.drawText(x, y, QString(ch));
-                       x += charWidth_;
-               }
-
-               y += charHeight_;
-       }
-
-       // Paint cursor
-       if (hasFocus()) {
-               int x = (cursorPos_ % (2 * BYTES_PER_LINE));
-               int y = cursorPos_ / (2 * BYTES_PER_LINE);
-               y -= firstLineIdx;
-               int cursorX = (((x / 2) * 3) + (x % 2)) * charWidth_ + posHex_;
-               int cursorY = charHeight_ + y * charHeight_ + 4;
-               painter.fillRect(cursorX, cursorY, 2, charHeight_, palette().color(QPalette::WindowText));
-       }
-}
-
-void QHexView::keyPressEvent(QKeyEvent *event)
-{
-       bool setVisible = false;
-
-       // Cursor movements
-       if (event->matches(QKeySequence::MoveToNextChar)) {
-               setCursorPos(cursorPos_ + 1);
-               resetSelection(cursorPos_);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::MoveToPreviousChar)) {
-               setCursorPos(cursorPos_ - 1);
-               resetSelection(cursorPos_);
-               setVisible = true;
-       }
-
-       if (event->matches(QKeySequence::MoveToEndOfLine)) {
-               setCursorPos(cursorPos_ | ((BYTES_PER_LINE * 2) - 1));
-               resetSelection(cursorPos_);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::MoveToStartOfLine)) {
-               setCursorPos(cursorPos_ | (cursorPos_ % (BYTES_PER_LINE * 2)));
-               resetSelection(cursorPos_);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::MoveToPreviousLine)) {
-               setCursorPos(cursorPos_ - BYTES_PER_LINE * 2);
-               resetSelection(cursorPos_);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::MoveToNextLine)) {
-               setCursorPos(cursorPos_ + BYTES_PER_LINE * 2);
-               resetSelection(cursorPos_);
-               setVisible = true;
-       }
-
-       if (event->matches(QKeySequence::MoveToNextPage)) {
-               setCursorPos(cursorPos_ + (viewport()->height() / charHeight_ - 1) * 2 * BYTES_PER_LINE);
-               resetSelection(cursorPos_);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::MoveToPreviousPage)) {
-               setCursorPos(cursorPos_ - (viewport()->height() / charHeight_ - 1) * 2 * BYTES_PER_LINE);
-               resetSelection(cursorPos_);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::MoveToEndOfDocument)) {
-               setCursorPos(data_size_ * 2);
-               resetSelection(cursorPos_);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::MoveToStartOfDocument)) {
-               setCursorPos(0);
-               resetSelection(cursorPos_);
-               setVisible = true;
-       }
-
-       // Select commands
-       if (event->matches(QKeySequence::SelectAll)) {
-               resetSelection(0);
-               setSelection(2 * data_size_);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::SelectNextChar)) {
-               int pos = cursorPos_ + 1;
-               setCursorPos(pos);
-               setSelection(pos);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::SelectPreviousChar)) {
-               int pos = cursorPos_ - 1;
-               setSelection(pos);
-               setCursorPos(pos);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::SelectEndOfLine)) {
-               int pos = cursorPos_ - (cursorPos_ % (2 * BYTES_PER_LINE)) + (2 * BYTES_PER_LINE);
-               setCursorPos(pos);
-               setSelection(pos);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::SelectStartOfLine)) {
-               int pos = cursorPos_ - (cursorPos_ % (2 * BYTES_PER_LINE));
-               setCursorPos(pos);
-               setSelection(pos);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::SelectPreviousLine)) {
-               int pos = cursorPos_ - (2 * BYTES_PER_LINE);
-               setCursorPos(pos);
-               setSelection(pos);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::SelectNextLine)) {
-               int pos = cursorPos_ + (2 * BYTES_PER_LINE);
-               setCursorPos(pos);
-               setSelection(pos);
-               setVisible = true;
-       }
-
-       if (event->matches(QKeySequence::SelectNextPage)) {
-               int pos = cursorPos_ + (((viewport()->height() / charHeight_) - 1) * 2 * BYTES_PER_LINE);
-               setCursorPos(pos);
-               setSelection(pos);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::SelectPreviousPage)) {
-               int pos = cursorPos_ - (((viewport()->height() / charHeight_) - 1) * 2 * BYTES_PER_LINE);
-               setCursorPos(pos);
-               setSelection(pos);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::SelectEndOfDocument)) {
-               int pos = data_size_ * 2;
-               setCursorPos(pos);
-               setSelection(pos);
-               setVisible = true;
-       }
-       if (event->matches(QKeySequence::SelectStartOfDocument)) {
-               setCursorPos(0);
-               setSelection(0);
-               setVisible = true;
-       }
-
-       if (event->matches(QKeySequence::Copy) && (data_)) {
-               QString text;
-
-               initialize_byte_iterator(selectBegin_ / 2);
-
-               size_t selectedSize = (selectEnd_ - selectBegin_ + 1) / 2;
-               for (size_t i = 0; i < selectedSize; i++) {
-                       uint8_t byte_value = get_next_byte();
-
-                       QString s = QString::number((byte_value & 0xF0) >> 4, 16).toUpper() +
-                               QString::number((byte_value & 0xF), 16).toUpper() + " ";
-                       text += s;
-
-                       if (i % BYTES_PER_LINE == (BYTES_PER_LINE - 1))
-                               text += "\n";
-               }
-
-               QClipboard *clipboard = QApplication::clipboard();
-               clipboard->setText(text, QClipboard::Clipboard);
-               if (clipboard->supportsSelection())
-                       clipboard->setText(text, QClipboard::Selection);
-       }
-
-       if (setVisible)
-               ensureVisible();
-
-       viewport()->update();
-}
-
-void QHexView::mouseMoveEvent(QMouseEvent *event)
-{
-       int actPos = cursorPosFromMousePos(event->pos());
-       setCursorPos(actPos);
-       setSelection(actPos);
-
-       viewport()->update();
-}
-
-void QHexView::mousePressEvent(QMouseEvent *event)
-{
-       int cPos = cursorPosFromMousePos(event->pos());
-
-       if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) && (event->button() == Qt::LeftButton))
-               setSelection(cPos);
-       else
-               resetSelection(cPos);
-
-       setCursorPos(cPos);
-
-       viewport()->update();
-}
-
-size_t QHexView::cursorPosFromMousePos(const QPoint &position)
-{
-       size_t pos = -1;
-
-       if (((size_t)position.x() >= posHex_) &&
-               ((size_t)position.x() < (posHex_ + HEXCHARS_IN_LINE * charWidth_))) {
-
-               // Note: We add 1.5 character widths so that selection across
-               // byte gaps is smoother
-               size_t x = (position.x() + (1.5 * charWidth_ / 2) - posHex_) / charWidth_;
-
-               // Note: We allow only full bytes to be selected, not nibbles,
-               // so we round to the nearest byte gap
-               x = (2 * x + 1) / 3;
-
-               size_t firstLineIdx = verticalScrollBar()->value();
-               size_t y = ((position.y() / charHeight_) - 1) * 2 * BYTES_PER_LINE;
-               pos = x + y + firstLineIdx * BYTES_PER_LINE * 2;
-       }
-
-       size_t max_pos = data_size_ * 2;
-
-       return std::min(pos, max_pos);
-}
-
-void QHexView::resetSelection()
-{
-       selectBegin_ = selectInit_;
-       selectEnd_ = selectInit_;
-}
-
-void QHexView::resetSelection(int pos)
-{
-       if (pos < 0)
-               pos = 0;
-
-       selectInit_ = pos;
-       selectBegin_ = pos;
-       selectEnd_ = pos;
-}
-
-void QHexView::setSelection(int pos)
-{
-       if (pos < 0)
-               pos = 0;
-
-       if ((size_t)pos >= selectInit_) {
-               selectEnd_ = pos;
-               selectBegin_ = selectInit_;
-       } else {
-               selectBegin_ = pos;
-               selectEnd_ = selectInit_;
-       }
-}
-
-void QHexView::setCursorPos(int position)
-{
-       if (position < 0)
-               position = 0;
-
-       int max_pos = data_size_ * 2;
-
-       if (position > max_pos)
-               position = max_pos;
-
-       cursorPos_ = position;
-}
-
-void QHexView::ensureVisible()
-{
-       QSize areaSize = viewport()->size();
-
-       int firstLineIdx = verticalScrollBar()->value();
-       int lastLineIdx = firstLineIdx + areaSize.height() / charHeight_;
-
-       int cursorY = cursorPos_ / (2 * BYTES_PER_LINE);
-
-       if (cursorY < firstLineIdx)
-               verticalScrollBar()->setValue(cursorY);
-       else
-               if(cursorY >= lastLineIdx)
-                       verticalScrollBar()->setValue(cursorY - areaSize.height() / charHeight_ + 1);
-}
diff --git a/pv/views/decoder_output/QHexView.hpp b/pv/views/decoder_output/QHexView.hpp
deleted file mode 100644 (file)
index e3c182a..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2015 Victor Anjin <virinext@gmail.com>
- * Copyright (C) 2019 Soeren Apel <soeren@apelpie.net>
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2015
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_DECODEROUTPUT_QHEXVIEW_H
-#define PULSEVIEW_PV_VIEWS_DECODEROUTPUT_QHEXVIEW_H
-
-#include <QAbstractScrollArea>
-
-#include <pv/data/decodesignal.hpp>
-
-using std::pair;
-using std::size_t;
-using pv::data::DecodeBinaryClass;
-using pv::data::DecodeBinaryDataChunk;
-
-class QHexView: public QAbstractScrollArea
-{
-       Q_OBJECT
-
-public:
-       enum Mode {
-               ChunkedDataMode,    ///< Displays all data chunks in succession
-               MemoryEmulationMode ///< Reconstructs memory contents from data chunks
-       };
-
-public:
-       QHexView(QWidget *parent = nullptr);
-
-       void set_mode(Mode m);
-       void set_data(const DecodeBinaryClass* data);
-       unsigned int get_bytes_per_line() const;
-
-       void clear();
-       void showFromOffset(size_t offset);
-       virtual QSizePolicy sizePolicy() const;
-
-       pair<size_t, size_t> get_selection() const;
-
-       size_t create_hex_line(size_t start, size_t end, QString* dest,
-               bool with_offset=false, bool with_ascii=false);
-
-protected:
-       void initialize_byte_iterator(size_t offset);
-       uint8_t get_next_byte(bool* is_next_chunk = nullptr);
-
-       void paintEvent(QPaintEvent *event);
-       void keyPressEvent(QKeyEvent *event);
-       void mouseMoveEvent(QMouseEvent *event);
-       void mousePressEvent(QMouseEvent *event);
-
-private:
-       QSize getFullSize() const;
-       void resetSelection();
-       void resetSelection(int pos);
-       void setSelection(int pos);
-       void ensureVisible();
-       void setCursorPos(int pos);
-       size_t cursorPosFromMousePos(const QPoint &position);
-
-private:
-       Mode mode_;
-       const DecodeBinaryClass* data_;
-       size_t data_size_;
-
-       size_t posAddr_, posHex_, posAscii_;
-       size_t charWidth_, charHeight_;
-       size_t selectBegin_, selectEnd_, selectInit_, cursorPos_;
-
-       size_t current_chunk_id_, current_chunk_offset_, current_offset_;
-       DecodeBinaryDataChunk current_chunk_; // Cache locally so that we're not messed up when the vector is re-allocating its data
-
-       vector<QColor> chunk_colors_;
-};
-
-#endif /* PULSEVIEW_PV_VIEWS_DECODEROUTPUT_QHEXVIEW_H */
diff --git a/pv/views/decoder_output/view.cpp b/pv/views/decoder_output/view.cpp
deleted file mode 100644 (file)
index 0f127c8..0000000
+++ /dev/null
@@ -1,480 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2019 Soeren Apel <soeren@apelpie.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <climits>
-
-#include <QByteArray>
-#include <QDebug>
-#include <QFileDialog>
-#include <QLabel>
-#include <QMenu>
-#include <QMessageBox>
-#include <QToolBar>
-#include <QVBoxLayout>
-
-#include <libsigrokdecode/libsigrokdecode.h>
-
-#include "view.hpp"
-#include "QHexView.hpp"
-
-#include "pv/globalsettings.hpp"
-#include "pv/session.hpp"
-#include "pv/util.hpp"
-#include "pv/data/decode/decoder.hpp"
-
-using pv::data::DecodeSignal;
-using pv::data::SignalBase;
-using pv::data::decode::Decoder;
-using pv::util::Timestamp;
-
-using std::shared_ptr;
-
-namespace pv {
-namespace views {
-namespace decoder_output {
-
-const char* SaveTypeNames[SaveTypeCount] = {
-       "Binary",
-       "Hex Dump, plain",
-       "Hex Dump, with offset",
-       "Hex Dump, canonical"
-};
-
-
-View::View(Session &session, bool is_main_view, QMainWindow *parent) :
-       ViewBase(session, is_main_view, parent),
-
-       // Note: Place defaults in View::reset_view_state(), not here
-       parent_(parent),
-       decoder_selector_(new QComboBox()),
-       format_selector_(new QComboBox()),
-       class_selector_(new QComboBox()),
-       stacked_widget_(new QStackedWidget()),
-       hex_view_(new QHexView()),
-       save_button_(new QToolButton()),
-       save_action_(new QAction(this)),
-       signal_(nullptr)
-{
-       QVBoxLayout *root_layout = new QVBoxLayout(this);
-       root_layout->setContentsMargins(0, 0, 0, 0);
-
-       // Create toolbar
-       QToolBar* toolbar = new QToolBar();
-       toolbar->setContextMenuPolicy(Qt::PreventContextMenu);
-       parent->addToolBar(toolbar);
-
-       // Populate toolbar
-       toolbar->addWidget(new QLabel(tr("Decoder:")));
-       toolbar->addWidget(decoder_selector_);
-       toolbar->addWidget(class_selector_);
-       toolbar->addSeparator();
-       toolbar->addWidget(new QLabel(tr("Show data as")));
-       toolbar->addWidget(format_selector_);
-       toolbar->addSeparator();
-       toolbar->addWidget(save_button_);
-
-       // Add format types
-       format_selector_->addItem(tr("Hexdump"), qVariantFromValue(QString("text/hexdump")));
-
-       // Add widget stack
-       root_layout->addWidget(stacked_widget_);
-       stacked_widget_->addWidget(hex_view_);
-       stacked_widget_->setCurrentIndex(0);
-
-       connect(decoder_selector_, SIGNAL(currentIndexChanged(int)),
-               this, SLOT(on_selected_decoder_changed(int)));
-       connect(class_selector_, SIGNAL(currentIndexChanged(int)),
-               this, SLOT(on_selected_class_changed(int)));
-
-       // Configure widgets
-       decoder_selector_->setSizeAdjustPolicy(QComboBox::AdjustToContents);
-       class_selector_->setSizeAdjustPolicy(QComboBox::AdjustToContents);
-
-       // Configure actions
-       save_action_->setText(tr("&Save..."));
-       save_action_->setIcon(QIcon::fromTheme("document-save-as",
-               QIcon(":/icons/document-save-as.png")));
-       save_action_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
-       connect(save_action_, SIGNAL(triggered(bool)),
-               this, SLOT(on_actionSave_triggered()));
-
-       QMenu *save_menu = new QMenu();
-       connect(save_menu, SIGNAL(triggered(QAction*)),
-               this, SLOT(on_actionSave_triggered(QAction*)));
-
-       for (int i = 0; i < SaveTypeCount; i++) {
-               QAction *const action = save_menu->addAction(tr(SaveTypeNames[i]));
-               action->setData(qVariantFromValue(i));
-       }
-
-       save_button_->setMenu(save_menu);
-       save_button_->setDefaultAction(save_action_);
-       save_button_->setPopupMode(QToolButton::MenuButtonPopup);
-
-       parent->setSizePolicy(hex_view_->sizePolicy()); // TODO Must be updated when selected widget changes
-
-       reset_view_state();
-}
-
-ViewType View::get_type() const
-{
-       return ViewTypeDecoderOutput;
-}
-
-void View::reset_view_state()
-{
-       ViewBase::reset_view_state();
-
-       decoder_selector_->clear();
-       class_selector_->clear();
-       format_selector_->setCurrentIndex(0);
-       save_button_->setEnabled(false);
-
-       hex_view_->clear();
-}
-
-void View::clear_decode_signals()
-{
-       ViewBase::clear_decode_signals();
-
-       reset_data();
-       reset_view_state();
-}
-
-void View::add_decode_signal(shared_ptr<data::DecodeSignal> signal)
-{
-       ViewBase::add_decode_signal(signal);
-
-       connect(signal.get(), SIGNAL(name_changed(const QString&)),
-               this, SLOT(on_signal_name_changed(const QString&)));
-       connect(signal.get(), SIGNAL(decoder_stacked(void*)),
-               this, SLOT(on_decoder_stacked(void*)));
-       connect(signal.get(), SIGNAL(decoder_removed(void*)),
-               this, SLOT(on_decoder_removed(void*)));
-
-       // Add all decoders provided by this signal
-       auto stack = signal->decoder_stack();
-       if (stack.size() > 1) {
-               for (const shared_ptr<Decoder>& dec : stack)
-                       // Only add the decoder if it has binary output
-                       if (dec->get_binary_class_count() > 0) {
-                               QString title = QString("%1 (%2)").arg(signal->name(), dec->name());
-                               decoder_selector_->addItem(title, QVariant::fromValue((void*)dec.get()));
-                       }
-       } else
-               if (!stack.empty()) {
-                       shared_ptr<Decoder>& dec = stack.at(0);
-                       if (dec->get_binary_class_count() > 0)
-                               decoder_selector_->addItem(signal->name(), QVariant::fromValue((void*)dec.get()));
-               }
-}
-
-void View::remove_decode_signal(shared_ptr<data::DecodeSignal> signal)
-{
-       // Remove all decoders provided by this signal
-       for (const shared_ptr<Decoder>& dec : signal->decoder_stack()) {
-               int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
-
-               if (index != -1)
-                       decoder_selector_->removeItem(index);
-       }
-
-       ViewBase::remove_decode_signal(signal);
-
-       if (signal.get() == signal_) {
-               reset_data();
-               update_data();
-               reset_view_state();
-       }
-}
-
-void View::save_settings(QSettings &settings) const
-{
-       (void)settings;
-}
-
-void View::restore_settings(QSettings &settings)
-{
-       // Note: It is assumed that this function is only called once,
-       // immediately after restoring a previous session.
-       (void)settings;
-}
-
-void View::reset_data()
-{
-       signal_ = nullptr;
-       decoder_ = nullptr;
-       bin_class_id_ = 0;
-       binary_data_exists_ = false;
-
-       hex_view_->clear();
-}
-
-void View::update_data()
-{
-       if (!signal_)
-               return;
-
-       const DecodeBinaryClass* bin_class =
-               signal_->get_binary_data_class(current_segment_, decoder_, bin_class_id_);
-
-       hex_view_->set_data(bin_class);
-
-       if (!binary_data_exists_)
-               return;
-
-       if (!save_button_->isEnabled())
-               save_button_->setEnabled(true);
-}
-
-void View::save_data() const
-{
-       assert(decoder_);
-       assert(signal_);
-
-       if (!signal_)
-               return;
-
-       GlobalSettings settings;
-       const QString dir = settings.value("MainWindow/SaveDirectory").toString();
-
-       const QString file_name = QFileDialog::getSaveFileName(
-               parent_, tr("Save Binary Data"), dir, tr("Binary Data Files (*.bin);;All Files (*)"));
-
-       if (file_name.isEmpty())
-               return;
-
-       QFile file(file_name);
-       if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
-               pair<size_t, size_t> selection = hex_view_->get_selection();
-
-               vector<uint8_t> data;
-               data.resize(selection.second - selection.first + 1);
-
-               signal_->get_merged_binary_data_chunks_by_offset(current_segment_, decoder_,
-                       bin_class_id_, selection.first, selection.second, &data);
-
-               int64_t bytes_written = file.write((const char*)data.data(), data.size());
-
-               if ((bytes_written == -1) || ((uint64_t)bytes_written != data.size())) {
-                       QMessageBox msg(parent_);
-                       msg.setText(tr("Error") + "\n\n" + tr("File %1 could not be written to.").arg(file_name));
-                       msg.setStandardButtons(QMessageBox::Ok);
-                       msg.setIcon(QMessageBox::Warning);
-                       msg.exec();
-                       return;
-               }
-       }
-}
-
-void View::save_data_as_hex_dump(bool with_offset, bool with_ascii) const
-{
-       assert(decoder_);
-       assert(signal_);
-
-       if (!signal_)
-               return;
-
-       GlobalSettings settings;
-       const QString dir = settings.value("MainWindow/SaveDirectory").toString();
-
-       const QString file_name = QFileDialog::getSaveFileName(
-               parent_, tr("Save Binary Data"), dir, tr("Hex Dumps (*.txt);;All Files (*)"));
-
-       if (file_name.isEmpty())
-               return;
-
-       QFile file(file_name);
-       if (file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
-               pair<size_t, size_t> selection = hex_view_->get_selection();
-
-               vector<uint8_t> data;
-               data.resize(selection.second - selection.first + 1);
-
-               signal_->get_merged_binary_data_chunks_by_offset(current_segment_, decoder_,
-                       bin_class_id_, selection.first, selection.second, &data);
-
-               QTextStream out_stream(&file);
-
-               uint64_t offset = selection.first;
-               uint64_t n = hex_view_->get_bytes_per_line();
-               QString s;
-
-               while (offset < selection.second) {
-                       size_t end = std::min((uint64_t)(selection.second), offset + n);
-                       offset = hex_view_->create_hex_line(offset, end, &s, with_offset, with_ascii);
-                       out_stream << s << endl;
-               }
-
-               out_stream << endl;
-
-               if (out_stream.status() != QTextStream::Ok) {
-                       QMessageBox msg(parent_);
-                       msg.setText(tr("Error") + "\n\n" + tr("File %1 could not be written to.").arg(file_name));
-                       msg.setStandardButtons(QMessageBox::Ok);
-                       msg.setIcon(QMessageBox::Warning);
-                       msg.exec();
-                       return;
-               }
-       }
-}
-
-void View::on_selected_decoder_changed(int index)
-{
-       if (signal_)
-               disconnect(signal_, SIGNAL(new_binary_data(unsigned int, void*, unsigned int)));
-
-       reset_data();
-
-       decoder_ = (Decoder*)decoder_selector_->itemData(index).value<void*>();
-
-       // Find the signal that contains the selected decoder
-       for (const shared_ptr<DecodeSignal>& ds : decode_signals_)
-               for (const shared_ptr<Decoder>& dec : ds->decoder_stack())
-                       if (decoder_ == dec.get())
-                               signal_ = ds.get();
-
-       class_selector_->clear();
-
-       if (signal_) {
-               // Populate binary class selector
-               uint32_t bin_classes = decoder_->get_binary_class_count();
-               for (uint32_t i = 0; i < bin_classes; i++) {
-                       const data::decode::DecodeBinaryClassInfo* class_info = decoder_->get_binary_class(i);
-                       class_selector_->addItem(class_info->description, QVariant::fromValue(i));
-               }
-
-               connect(signal_, SIGNAL(new_binary_data(unsigned int, void*, unsigned int)),
-                       this, SLOT(on_new_binary_data(unsigned int, void*, unsigned int)));
-       }
-
-       update_data();
-}
-
-void View::on_selected_class_changed(int index)
-{
-       bin_class_id_ = class_selector_->itemData(index).value<uint32_t>();
-
-       binary_data_exists_ =
-               signal_->get_binary_data_chunk_count(current_segment_, decoder_, bin_class_id_);
-
-       update_data();
-}
-
-void View::on_signal_name_changed(const QString &name)
-{
-       (void)name;
-
-       SignalBase* sb = qobject_cast<SignalBase*>(QObject::sender());
-       assert(sb);
-
-       DecodeSignal* signal = dynamic_cast<DecodeSignal*>(sb);
-       assert(signal);
-
-       // Update all decoder entries provided by this signal
-       auto stack = signal->decoder_stack();
-       if (stack.size() > 1) {
-               for (const shared_ptr<Decoder>& dec : stack) {
-                       QString title = QString("%1 (%2)").arg(signal->name(), dec->name());
-                       int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
-
-                       if (index != -1)
-                               decoder_selector_->setItemText(index, title);
-               }
-       } else
-               if (!stack.empty()) {
-                       shared_ptr<Decoder>& dec = stack.at(0);
-                       int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
-
-                       if (index != -1)
-                               decoder_selector_->setItemText(index, signal->name());
-               }
-}
-
-void View::on_new_binary_data(unsigned int segment_id, void* decoder, unsigned int bin_class_id)
-{
-       if ((segment_id == current_segment_) && (decoder == decoder_) && (bin_class_id == bin_class_id_))
-               if (!delayed_view_updater_.isActive())
-                       delayed_view_updater_.start();
-}
-
-void View::on_decoder_stacked(void* decoder)
-{
-       // TODO This doesn't change existing entries for the same signal - but it should as the naming scheme may change
-
-       Decoder* d = static_cast<Decoder*>(decoder);
-
-       // Only add the decoder if it has binary output
-       if (d->get_binary_class_count() == 0)
-               return;
-
-       // Find the signal that contains the selected decoder
-       DecodeSignal* signal = nullptr;
-
-       for (const shared_ptr<DecodeSignal>& ds : decode_signals_)
-               for (const shared_ptr<Decoder>& dec : ds->decoder_stack())
-                       if (d == dec.get())
-                               signal = ds.get();
-
-       assert(signal);
-
-       // Add the decoder to the list
-       QString title = QString("%1 (%2)").arg(signal->name(), d->name());
-       decoder_selector_->addItem(title, QVariant::fromValue((void*)d));
-}
-
-void View::on_decoder_removed(void* decoder)
-{
-       Decoder* d = static_cast<Decoder*>(decoder);
-
-       // Remove the decoder from the list
-       int index = decoder_selector_->findData(QVariant::fromValue((void*)d));
-
-       if (index != -1)
-               decoder_selector_->removeItem(index);
-}
-
-void View::on_actionSave_triggered(QAction* action)
-{
-       int save_type = SaveTypeBinary;
-       if (action)
-               save_type = action->data().toInt();
-
-       switch (save_type)
-       {
-       case SaveTypeBinary: save_data(); break;
-       case SaveTypeHexDumpPlain: save_data_as_hex_dump(false, false); break;
-       case SaveTypeHexDumpWithOffset: save_data_as_hex_dump(true, false); break;
-       case SaveTypeHexDumpComplete: save_data_as_hex_dump(true, true); break;
-       }
-}
-
-void View::perform_delayed_view_update()
-{
-       if (signal_ && !binary_data_exists_)
-               if (signal_->get_binary_data_chunk_count(current_segment_, decoder_, bin_class_id_))
-                       binary_data_exists_ = true;
-
-       update_data();
-}
-
-
-} // namespace decoder_output
-} // namespace views
-} // namespace pv
diff --git a/pv/views/decoder_output/view.hpp b/pv/views/decoder_output/view.hpp
deleted file mode 100644 (file)
index 16d35e8..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2019 Soeren Apel <soeren@apelpie.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSEVIEW_PV_VIEWS_DECODEROUTPUT_VIEW_HPP
-#define PULSEVIEW_PV_VIEWS_DECODEROUTPUT_VIEW_HPP
-
-#include <QAction>
-#include <QComboBox>
-#include <QStackedWidget>
-#include <QToolButton>
-
-#include <pv/views/viewbase.hpp>
-#include <pv/data/decodesignal.hpp>
-
-#include "QHexView.hpp"
-
-namespace pv {
-
-class Session;
-
-namespace views {
-
-namespace decoder_output {
-
-// When adding an entry here, don't forget to update SaveTypeNames as well
-enum SaveType {
-       SaveTypeBinary,
-       SaveTypeHexDumpPlain,
-       SaveTypeHexDumpWithOffset,
-       SaveTypeHexDumpComplete,
-       SaveTypeCount  // Indicates how many save types there are, must always be last
-};
-
-extern const char* SaveTypeNames[SaveTypeCount];
-
-
-class View : public ViewBase
-{
-       Q_OBJECT
-
-public:
-       explicit View(Session &session, bool is_main_view=false, QMainWindow *parent = nullptr);
-
-       virtual ViewType get_type() const;
-
-       /**
-        * Resets the view to its default state after construction. It does however
-        * not reset the signal bases or any other connections with the session.
-        */
-       virtual void reset_view_state();
-
-       virtual void clear_decode_signals();
-       virtual void add_decode_signal(shared_ptr<data::DecodeSignal> signal);
-       virtual void remove_decode_signal(shared_ptr<data::DecodeSignal> signal);
-
-       virtual void save_settings(QSettings &settings) const;
-       virtual void restore_settings(QSettings &settings);
-
-private:
-       void reset_data();
-       void update_data();
-
-       void save_data() const;
-       void save_data_as_hex_dump(bool with_offset=false, bool with_ascii=false) const;
-
-private Q_SLOTS:
-       void on_selected_decoder_changed(int index);
-       void on_selected_class_changed(int index);
-       void on_signal_name_changed(const QString &name);
-       void on_new_binary_data(unsigned int segment_id, void* decoder, unsigned int bin_class_id);
-
-       void on_decoder_stacked(void* decoder);
-       void on_decoder_removed(void* decoder);
-
-       void on_actionSave_triggered(QAction* action = nullptr);
-
-       virtual void perform_delayed_view_update();
-
-private:
-       QWidget* parent_;
-
-       QComboBox *decoder_selector_, *format_selector_, *class_selector_;
-       QStackedWidget *stacked_widget_;
-       QHexView *hex_view_;
-
-       QToolButton* save_button_;
-       QAction* save_action_;
-
-       data::DecodeSignal *signal_;
-       const data::decode::Decoder *decoder_;
-       uint32_t bin_class_id_;
-       bool binary_data_exists_;
-};
-
-} // namespace decoder_output
-} // namespace views
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEWS_DECODEROUTPUT_VIEW_HPP
diff --git a/pv/views/tabular_decoder/model.cpp b/pv/views/tabular_decoder/model.cpp
new file mode 100644 (file)
index 0000000..def753b
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QApplication>
+#include <QDebug>
+#include <QString>
+
+#include "pv/views/tabular_decoder/view.hpp"
+
+#include "view.hpp"
+
+#include "pv/util.hpp"
+#include "pv/globalsettings.hpp"
+
+using std::make_shared;
+
+using pv::util::Timestamp;
+using pv::util::format_time_si;
+using pv::util::format_time_minutes;
+using pv::util::SIPrefix;
+
+namespace pv {
+namespace views {
+namespace tabular_decoder {
+
+AnnotationCollectionModel::AnnotationCollectionModel(QObject* parent) :
+       QAbstractTableModel(parent),
+       all_annotations_(nullptr),
+       dataset_(nullptr),
+       signal_(nullptr),
+       first_hidden_column_(0),
+       prev_segment_(0),
+       prev_last_row_(0),
+       had_highlight_before_(false),
+       hide_hidden_(false)
+{
+       // Note: when adding entries, consider ViewVisibleFilterProxyModel::filterAcceptsRow()
+
+       uint8_t i = 0;
+       header_data_.emplace_back(tr("Sample"));    i++; // Column #0
+       header_data_.emplace_back(tr("Time"));      i++; // Column #1
+       header_data_.emplace_back(tr("Decoder"));   i++; // Column #2
+       header_data_.emplace_back(tr("Ann Row"));   i++; // Column #3
+       header_data_.emplace_back(tr("Ann Class")); i++; // Column #4
+       header_data_.emplace_back(tr("Value"));     i++; // Column #5
+
+       first_hidden_column_ = i;
+       header_data_.emplace_back("End Sample");         // Column #6, hidden
+}
+
+int AnnotationCollectionModel::get_hierarchy_level(const Annotation* ann) const
+{
+       int level = 0;
+
+       const unsigned int ann_stack_level = ann->row_data()->row()->decoder()->get_stack_level();
+       level = (signal_->decoder_stack().size() - 1 - ann_stack_level);
+
+       return level;
+}
+
+QVariant AnnotationCollectionModel::data_from_ann(const Annotation* ann, int index) const
+{
+       switch (index) {
+       case 0: return QVariant((qulonglong)ann->start_sample());  // Column #0, Start Sample
+       case 1: {                                                  // Column #1, Start Time
+                       Timestamp t = ann->start_sample() / signal_->get_samplerate();
+                       QString unit = signal_->get_samplerate() ? tr("s") : tr("sa");
+                       QString s;
+                       if ((t < 60) || (signal_->get_samplerate() == 0))  // i.e. if unit is sa
+                               s = format_time_si(t, SIPrefix::unspecified, 3, unit, false);
+                       else
+                               s = format_time_minutes(t, 3, false);
+                       return QVariant(s);
+               }
+       case 2: return QVariant(ann->row()->decoder()->name());    // Column #2, Decoder
+       case 3: return QVariant(ann->row()->description());        // Column #3, Ann Row
+       case 4: return QVariant(ann->ann_class_description());     // Column #4, Ann Class
+       case 5: return QVariant(ann->longest_annotation());        // Column #5, Value
+       case 6: return QVariant((qulonglong)ann->end_sample());    // Column #6, End Sample
+       default: return QVariant();
+       }
+}
+
+QVariant AnnotationCollectionModel::data(const QModelIndex& index, int role) const
+{
+       if (!signal_ || !index.isValid() || !index.internalPointer())
+               return QVariant();
+
+       const Annotation* ann =
+               static_cast<const Annotation*>(index.internalPointer());
+
+       if ((role == Qt::DisplayRole) || (role == Qt::ToolTipRole))
+               return data_from_ann(ann, index.column());
+
+       if (role == Qt::ForegroundRole) {
+               if (index.column() >= get_hierarchy_level(ann)) {
+                       // Invert the text color if this cell is highlighted
+                       const bool must_highlight = (highlight_sample_num_ > 0) &&
+                               ((int64_t)ann->start_sample() <= highlight_sample_num_) &&
+                               ((int64_t)ann->end_sample() >= highlight_sample_num_);
+
+                       if (must_highlight) {
+                               if (GlobalSettings::current_theme_is_dark())
+                                       return QApplication::palette().brush(QPalette::Window);
+                               else
+                                       return QApplication::palette().brush(QPalette::WindowText);
+                       }
+               }
+
+               return QApplication::palette().brush(QPalette::WindowText);
+       }
+
+       if (role == Qt::BackgroundRole) {
+               // Only use custom cell background color if column index reached the hierarchy level
+               if (index.column() >= get_hierarchy_level(ann)) {
+
+                       QColor color;
+                       const bool must_highlight = (highlight_sample_num_ > 0) &&
+                               ((int64_t)ann->start_sample() <= highlight_sample_num_) &&
+                               ((int64_t)ann->end_sample() >= highlight_sample_num_);
+
+                       if (must_highlight)
+                               color = ann->color();
+                       else
+                               color = GlobalSettings::current_theme_is_dark() ?
+                                       ann->dark_color() : ann->bright_color();
+
+                       return QBrush(color);
+               }
+       }
+
+       return QVariant();
+}
+
+Qt::ItemFlags AnnotationCollectionModel::flags(const QModelIndex& index) const
+{
+       if (!index.isValid())
+               return Qt::NoItemFlags;
+
+       return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
+}
+
+uint8_t AnnotationCollectionModel::first_hidden_column() const
+{
+       return first_hidden_column_;
+}
+
+QVariant AnnotationCollectionModel::headerData(int section, Qt::Orientation orientation,
+       int role) const
+{
+       if ((section < 0) || (section >= (int)header_data_.size()))
+               return QVariant();
+
+       if ((orientation == Qt::Horizontal) && (role == Qt::DisplayRole))
+               return header_data_[section];
+
+       return QVariant();
+}
+
+QModelIndex AnnotationCollectionModel::index(int row, int column,
+       const QModelIndex& parent_idx) const
+{
+       (void)parent_idx;
+       assert(column >= 0);
+
+       if (!dataset_ || (row < 0))
+               return QModelIndex();
+
+       QModelIndex idx;
+
+       if ((size_t)row < dataset_->size())
+               idx = createIndex(row, column, (void*)dataset_->at(row));
+
+       return idx;
+}
+
+QModelIndex AnnotationCollectionModel::parent(const QModelIndex& index) const
+{
+       (void)index;
+
+       return QModelIndex();
+}
+
+int AnnotationCollectionModel::rowCount(const QModelIndex& parent_idx) const
+{
+       (void)parent_idx;
+
+       if (!dataset_)
+               return 0;
+
+       return dataset_->size();
+}
+
+int AnnotationCollectionModel::columnCount(const QModelIndex& parent_idx) const
+{
+       (void)parent_idx;
+
+       return header_data_.size();
+}
+
+void AnnotationCollectionModel::set_signal_and_segment(data::DecodeSignal* signal, uint32_t current_segment)
+{
+       layoutAboutToBeChanged();
+
+       if (!signal) {
+               all_annotations_ = nullptr;
+               dataset_ = nullptr;
+               signal_ = nullptr;
+
+               dataChanged(QModelIndex(), QModelIndex());
+               layoutChanged();
+               return;
+       }
+
+       if (signal_)
+               for (const shared_ptr<Decoder>& dec : signal_->decoder_stack())
+                       disconnect(dec.get(), nullptr, this, SLOT(on_annotation_visibility_changed()));
+
+       all_annotations_ = signal->get_all_annotations_by_segment(current_segment);
+       signal_ = signal;
+
+       for (const shared_ptr<Decoder>& dec : signal_->decoder_stack())
+               connect(dec.get(), SIGNAL(annotation_visibility_changed()),
+                       this, SLOT(on_annotation_visibility_changed()));
+
+       if (hide_hidden_) {
+               update_annotations_without_hidden();
+               dataset_ = &all_annotations_without_hidden_;
+       } else
+               dataset_ = all_annotations_;
+
+       if (!dataset_ || dataset_->empty()) {
+               prev_segment_ = current_segment;
+               return;
+       }
+
+       const size_t new_row_count = dataset_->size() - 1;
+
+       // Force the view associated with this model to update when the segment changes
+       if (prev_segment_ != current_segment) {
+               dataChanged(index(0, 0), index(new_row_count, 0));
+               layoutChanged();
+       } else {
+               // Force the view associated with this model to update when we have more annotations
+               if (prev_last_row_ < new_row_count) {
+                       dataChanged(index(prev_last_row_, 0), index(new_row_count, 0));
+                       layoutChanged();
+               }
+       }
+
+       prev_segment_ = current_segment;
+       prev_last_row_ = new_row_count;
+}
+
+void AnnotationCollectionModel::set_hide_hidden(bool hide_hidden)
+{
+       layoutAboutToBeChanged();
+
+       hide_hidden_ = hide_hidden;
+
+       if (hide_hidden_) {
+               dataset_ = &all_annotations_without_hidden_;
+               update_annotations_without_hidden();
+       } else {
+               dataset_ = all_annotations_;
+               all_annotations_without_hidden_.clear();  // To conserve memory
+       }
+
+       if (dataset_)
+               dataChanged(index(0, 0), index(dataset_->size() - 1, 0));
+       else
+               dataChanged(QModelIndex(), QModelIndex());
+
+       layoutChanged();
+}
+
+void AnnotationCollectionModel::update_annotations_without_hidden()
+{
+       uint64_t count = 0;
+
+       if (!all_annotations_ || all_annotations_->empty()) {
+               all_annotations_without_hidden_.clear();
+               return;
+       }
+
+       for (const Annotation* ann : *all_annotations_) {
+               if (!ann->visible())
+                       continue;
+
+               if (all_annotations_without_hidden_.size() < (count + 100))
+                       all_annotations_without_hidden_.resize(count + 100);
+
+               all_annotations_without_hidden_[count++] = ann;
+       }
+
+       all_annotations_without_hidden_.resize(count);
+}
+
+QModelIndex AnnotationCollectionModel::update_highlighted_rows(QModelIndex first,
+       QModelIndex last, int64_t sample_num)
+{
+       bool has_highlight = false;
+       QModelIndex result;
+
+       highlight_sample_num_ = sample_num;
+
+       if (!dataset_ || dataset_->empty())
+               return result;
+
+       if (sample_num >= 0) {
+               last = last.sibling(last.row() + 1, 0);
+
+               // Check if there are any annotations visible in the table view that
+               // we would need to highlight - only then do we do so
+               QModelIndex index = first;
+               do {
+                       const Annotation* ann = static_cast<const Annotation*>(index.internalPointer());
+                       if (!ann)  // Can happen if the table is being modified at this exact time
+                               return result;
+
+                       if (((int64_t)ann->start_sample() <= sample_num) &&
+                               ((int64_t)ann->end_sample() >= sample_num)) {
+                               result = index;
+                               has_highlight = true;
+                               break;
+                       }
+
+                       index = index.sibling(index.row() + 1, 0);
+               } while (index != last);
+       }
+
+       if (has_highlight || had_highlight_before_)
+               dataChanged(first, last);
+
+       had_highlight_before_ = has_highlight;
+
+       return result;
+}
+
+void AnnotationCollectionModel::on_annotation_visibility_changed()
+{
+       if (!hide_hidden_)
+               return;
+
+       layoutAboutToBeChanged();
+
+       update_annotations_without_hidden();
+
+       if (dataset_)
+               dataChanged(index(0, 0), index(dataset_->size() - 1, 0));
+       else
+               dataChanged(QModelIndex(), QModelIndex());
+
+       layoutChanged();
+}
+
+} // namespace tabular_decoder
+} // namespace views
+} // namespace pv
diff --git a/pv/views/tabular_decoder/view.cpp b/pv/views/tabular_decoder/view.cpp
new file mode 100644 (file)
index 0000000..c1d7429
--- /dev/null
@@ -0,0 +1,710 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <climits>
+
+#include <QApplication>
+#include <QDebug>
+#include <QFileDialog>
+#include <QFontMetrics>
+#include <QHeaderView>
+#include <QLabel>
+#include <QMenu>
+#include <QMessageBox>
+#include <QToolBar>
+#include <QVBoxLayout>
+
+#include <libsigrokdecode/libsigrokdecode.h>
+
+#include "view.hpp"
+
+#include "pv/globalsettings.hpp"
+#include "pv/session.hpp"
+#include "pv/util.hpp"
+#include "pv/data/decode/decoder.hpp"
+
+using pv::data::DecodeSignal;
+using pv::data::SignalBase;
+using pv::data::decode::Decoder;
+using pv::util::Timestamp;
+
+using std::make_shared;
+using std::max;
+using std::shared_ptr;
+
+namespace pv {
+namespace views {
+namespace tabular_decoder {
+
+const char* SaveTypeNames[SaveTypeCount] = {
+       "CSV, commas escaped",
+       "CSV, fields quoted"
+};
+
+const char* ViewModeNames[ViewModeCount] = {
+       "Show all",
+       "Show all and focus on newest",
+       "Show visible in main view"
+};
+
+
+CustomFilterProxyModel::CustomFilterProxyModel(QObject* parent) :
+       QSortFilterProxyModel(parent),
+       range_filtering_enabled_(false)
+{
+}
+
+bool CustomFilterProxyModel::filterAcceptsRow(int sourceRow,
+       const QModelIndex &sourceParent) const
+{
+       (void)sourceParent;
+       assert(sourceModel() != nullptr);
+
+       bool result = true;
+
+       if (range_filtering_enabled_) {
+               const QModelIndex ann_start_sample_idx = sourceModel()->index(sourceRow, 0);
+               const uint64_t ann_start_sample =
+                       sourceModel()->data(ann_start_sample_idx, Qt::DisplayRole).toULongLong();
+
+               const QModelIndex ann_end_sample_idx = sourceModel()->index(sourceRow, 6);
+               const uint64_t ann_end_sample =
+                       sourceModel()->data(ann_end_sample_idx, Qt::DisplayRole).toULongLong();
+
+               // We consider all annotations as visible that either
+               // a) begin to the left of the range and end within the range or
+               // b) begin and end within the range or
+               // c) begin within the range and end to the right of the range
+               // ...which is equivalent to the negation of "begins and ends outside the range"
+
+               const bool left_of_range = (ann_end_sample < range_start_sample_);
+               const bool right_of_range = (ann_start_sample > range_end_sample_);
+               const bool entirely_outside_of_range = left_of_range || right_of_range;
+
+               result = !entirely_outside_of_range;
+       }
+
+       return result;
+}
+
+void CustomFilterProxyModel::set_sample_range(uint64_t start_sample,
+       uint64_t end_sample)
+{
+       range_start_sample_ = start_sample;
+       range_end_sample_ = end_sample;
+
+       invalidateFilter();
+}
+
+void CustomFilterProxyModel::enable_range_filtering(bool value)
+{
+       range_filtering_enabled_ = value;
+
+       invalidateFilter();
+}
+
+
+QSize CustomTableView::minimumSizeHint() const
+{
+       QSize size(QTableView::sizeHint());
+
+       int width = 0;
+       for (int i = 0; i < horizontalHeader()->count(); i++)
+               if (!horizontalHeader()->isSectionHidden(i))
+                       width += horizontalHeader()->sectionSize(i);
+
+       size.setWidth(width + (horizontalHeader()->count() * 1));
+
+       return size;
+}
+
+QSize CustomTableView::sizeHint() const
+{
+       return minimumSizeHint();
+}
+
+void CustomTableView::keyPressEvent(QKeyEvent *event)
+{
+       if ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Enter))
+               activatedByKey(currentIndex());
+       else
+               QTableView::keyPressEvent(event);
+}
+
+
+View::View(Session &session, bool is_main_view, QMainWindow *parent) :
+       ViewBase(session, is_main_view, parent),
+
+       // Note: Place defaults in View::reset_view_state(), not here
+       parent_(parent),
+       decoder_selector_(new QComboBox()),
+       hide_hidden_cb_(new QCheckBox()),
+       view_mode_selector_(new QComboBox()),
+       save_button_(new QToolButton()),
+       save_action_(new QAction(this)),
+       table_view_(new CustomTableView()),
+       model_(new AnnotationCollectionModel(this)),
+       filter_proxy_model_(new CustomFilterProxyModel(this)),
+       signal_(nullptr)
+{
+       QVBoxLayout *root_layout = new QVBoxLayout(this);
+       root_layout->setContentsMargins(0, 0, 0, 0);
+       root_layout->addWidget(table_view_);
+
+       // Create toolbar
+       QToolBar* toolbar = new QToolBar();
+       toolbar->setContextMenuPolicy(Qt::PreventContextMenu);
+       parent->addToolBar(toolbar);
+
+       // Populate toolbar
+       toolbar->addWidget(new QLabel(tr("Decoder:")));
+       toolbar->addWidget(decoder_selector_);
+       toolbar->addSeparator();
+       toolbar->addWidget(save_button_);
+       toolbar->addSeparator();
+       toolbar->addWidget(view_mode_selector_);
+       toolbar->addSeparator();
+       toolbar->addWidget(hide_hidden_cb_);
+
+       connect(decoder_selector_, SIGNAL(currentIndexChanged(int)),
+               this, SLOT(on_selected_decoder_changed(int)));
+       connect(view_mode_selector_, SIGNAL(currentIndexChanged(int)),
+               this, SLOT(on_view_mode_changed(int)));
+       connect(hide_hidden_cb_, SIGNAL(toggled(bool)),
+               this, SLOT(on_hide_hidden_changed(bool)));
+
+       // Configure widgets
+       decoder_selector_->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+
+       for (int i = 0; i < ViewModeCount; i++)
+               view_mode_selector_->addItem(ViewModeNames[i], QVariant::fromValue(i));
+
+       hide_hidden_cb_->setText(tr("Hide Hidden Rows/Classes"));
+       hide_hidden_cb_->setChecked(true);
+
+       // Configure actions
+       save_action_->setText(tr("&Save..."));
+       save_action_->setIcon(QIcon::fromTheme("document-save-as",
+               QIcon(":/icons/document-save-as.png")));
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       save_action_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
+#else
+       save_action_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
+#endif
+       connect(save_action_, SIGNAL(triggered(bool)),
+               this, SLOT(on_actionSave_triggered()));
+
+       QMenu *save_menu = new QMenu();
+       connect(save_menu, SIGNAL(triggered(QAction*)),
+               this, SLOT(on_actionSave_triggered(QAction*)));
+
+       for (int i = 0; i < SaveTypeCount; i++) {
+               QAction *const action = save_menu->addAction(tr(SaveTypeNames[i]));
+               action->setData(QVariant::fromValue(i));
+       }
+
+       save_button_->setMenu(save_menu);
+       save_button_->setDefaultAction(save_action_);
+       save_button_->setPopupMode(QToolButton::MenuButtonPopup);
+
+       // Set up the models and the table view
+       filter_proxy_model_->setSourceModel(model_);
+       table_view_->setModel(filter_proxy_model_);
+
+       table_view_->setSelectionBehavior(QAbstractItemView::SelectRows);
+       table_view_->setSelectionMode(QAbstractItemView::ContiguousSelection);
+       table_view_->setSortingEnabled(true);
+       table_view_->sortByColumn(0, Qt::AscendingOrder);
+
+       for (uint8_t i = model_->first_hidden_column(); i < model_->columnCount(); i++)
+               table_view_->setColumnHidden(i, true);
+
+       const int font_height = QFontMetrics(QApplication::font()).height();
+       table_view_->verticalHeader()->setDefaultSectionSize((font_height * 5) / 4);
+       table_view_->verticalHeader()->setVisible(false);
+
+       table_view_->horizontalHeader()->setStretchLastSection(true);
+       table_view_->horizontalHeader()->setCascadingSectionResizes(true);
+       table_view_->horizontalHeader()->setSectionsMovable(true);
+       table_view_->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
+
+       table_view_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+       parent->setSizePolicy(table_view_->sizePolicy());
+
+       connect(table_view_, SIGNAL(clicked(const QModelIndex&)),
+               this, SLOT(on_table_item_clicked(const QModelIndex&)));
+       connect(table_view_, SIGNAL(doubleClicked(const QModelIndex&)),
+               this, SLOT(on_table_item_double_clicked(const QModelIndex&)));
+       connect(table_view_, SIGNAL(activatedByKey(const QModelIndex&)),
+               this, SLOT(on_table_item_double_clicked(const QModelIndex&)));
+       connect(table_view_->horizontalHeader(), SIGNAL(customContextMenuRequested(const QPoint&)),
+               this, SLOT(on_table_header_requested(const QPoint&)));
+
+       // Set up metadata event handler
+       session_.metadata_obj_manager()->add_observer(this);
+
+       reset_view_state();
+}
+
+View::~View()
+{
+       session_.metadata_obj_manager()->remove_observer(this);
+}
+
+ViewType View::get_type() const
+{
+       return ViewTypeTabularDecoder;
+}
+
+void View::reset_view_state()
+{
+       ViewBase::reset_view_state();
+
+       decoder_selector_->clear();
+}
+
+void View::clear_decode_signals()
+{
+       ViewBase::clear_decode_signals();
+
+       reset_data();
+       reset_view_state();
+}
+
+void View::add_decode_signal(shared_ptr<data::DecodeSignal> signal)
+{
+       ViewBase::add_decode_signal(signal);
+
+       connect(signal.get(), SIGNAL(name_changed(const QString&)),
+               this, SLOT(on_signal_name_changed(const QString&)));
+
+       // Note: At time of initial creation, decode signals have no decoders so we
+       // need to watch for decoder stacking events
+
+       connect(signal.get(), SIGNAL(decoder_stacked(void*)),
+               this, SLOT(on_decoder_stacked(void*)));
+       connect(signal.get(), SIGNAL(decoder_removed(void*)),
+               this, SLOT(on_decoder_removed(void*)));
+
+       // Add the top-level decoder provided by an already-existing signal
+       auto stack = signal->decoder_stack();
+       if (!stack.empty()) {
+               shared_ptr<Decoder>& dec = stack.at(0);
+               decoder_selector_->addItem(signal->name(), QVariant::fromValue((void*)dec.get()));
+       }
+}
+
+void View::remove_decode_signal(shared_ptr<data::DecodeSignal> signal)
+{
+       // Remove all decoders provided by this signal
+       for (const shared_ptr<Decoder>& dec : signal->decoder_stack()) {
+               int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
+
+               if (index != -1)
+                       decoder_selector_->removeItem(index);
+       }
+
+       ViewBase::remove_decode_signal(signal);
+
+       if (signal.get() == signal_) {
+               reset_data();
+               update_data();
+               reset_view_state();
+       }
+}
+
+void View::save_settings(QSettings &settings) const
+{
+       ViewBase::save_settings(settings);
+
+       settings.setValue("view_mode", view_mode_selector_->currentIndex());
+       settings.setValue("hide_hidden", hide_hidden_cb_->isChecked());
+}
+
+void View::restore_settings(QSettings &settings)
+{
+       ViewBase::restore_settings(settings);
+
+       if (settings.contains("view_mode"))
+               view_mode_selector_->setCurrentIndex(settings.value("view_mode").toInt());
+
+       if (settings.contains("hide_hidden"))
+               hide_hidden_cb_->setChecked(settings.value("hide_hidden").toBool());
+}
+
+void View::reset_data()
+{
+       signal_ = nullptr;
+       decoder_ = nullptr;
+}
+
+void View::update_data()
+{
+       model_->set_signal_and_segment(signal_, current_segment_);
+}
+
+void View::save_data_as_csv(unsigned int save_type) const
+{
+       // Note: We try to follow RFC 4180 (https://tools.ietf.org/html/rfc4180)
+
+       assert(decoder_);
+       assert(signal_);
+
+       if (!signal_)
+               return;
+
+       const bool save_all = !table_view_->selectionModel()->hasSelection();
+
+       GlobalSettings settings;
+       const QString dir = settings.value("MainWindow/SaveDirectory").toString();
+
+       const QString file_name = QFileDialog::getSaveFileName(
+               parent_, tr("Save Annotations as CSV"), dir, tr("CSV Files (*.csv);;Text Files (*.txt);;All Files (*)"));
+
+       if (file_name.isEmpty())
+               return;
+
+       QFile file(file_name);
+       if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
+               QTextStream out_stream(&file);
+
+               if (save_all)
+                       table_view_->selectAll();
+
+               // Write out header columns in visual order, not logical order
+               for (int i = 0; i < table_view_->horizontalHeader()->count(); i++) {
+                       int column = table_view_->horizontalHeader()->logicalIndex(i);
+
+                       if (table_view_->horizontalHeader()->isSectionHidden(column))
+                               continue;
+
+                       const QString title = filter_proxy_model_->headerData(column, Qt::Horizontal, Qt::DisplayRole).toString();
+
+                       if (save_type == SaveTypeCSVEscaped)
+                               out_stream << title;
+                       else
+                               out_stream << '"' << title << '"';
+
+                       if (i < (table_view_->horizontalHeader()->count() - 1))
+                               out_stream << ",";
+               }
+               out_stream << '\r' << '\n';
+
+
+               QModelIndexList selected_rows = table_view_->selectionModel()->selectedRows();
+
+               for (int i = 0; i < selected_rows.size(); i++) {
+                       const int row = selected_rows.at(i).row();
+
+                       // Write out columns in visual order, not logical order
+                       for (int c = 0; c < table_view_->horizontalHeader()->count(); c++) {
+                               const int column = table_view_->horizontalHeader()->logicalIndex(c);
+
+                               if (table_view_->horizontalHeader()->isSectionHidden(column))
+                                       continue;
+
+                               const QModelIndex idx = filter_proxy_model_->index(row, column);
+                               QString s = filter_proxy_model_->data(idx, Qt::DisplayRole).toString();
+
+                               if (save_type == SaveTypeCSVEscaped)
+                                       out_stream << s.replace(",", "\\,");
+                               else
+                                       out_stream << '"' << s.replace("\"", "\"\"") << '"';
+
+                               if (c < (table_view_->horizontalHeader()->count() - 1))
+                                       out_stream << ",";
+                       }
+
+                       out_stream << '\r' << '\n';
+               }
+
+               if (out_stream.status() == QTextStream::Ok) {
+                       if (save_all)
+                               table_view_->clearSelection();
+
+                       return;
+               }
+       }
+
+       QMessageBox msg(parent_);
+       msg.setText(tr("Error") + "\n\n" + tr("File %1 could not be written to.").arg(file_name));
+       msg.setStandardButtons(QMessageBox::Ok);
+       msg.setIcon(QMessageBox::Warning);
+       msg.exec();
+}
+
+void View::on_selected_decoder_changed(int index)
+{
+       if (signal_) {
+               disconnect(signal_, SIGNAL(color_changed(QColor)));
+               disconnect(signal_, SIGNAL(new_annotations()));
+               disconnect(signal_, SIGNAL(decode_reset()));
+       }
+
+       reset_data();
+
+       decoder_ = (Decoder*)decoder_selector_->itemData(index).value<void*>();
+
+       // Find the signal that contains the selected decoder
+       for (const shared_ptr<DecodeSignal>& ds : decode_signals_)
+               for (const shared_ptr<Decoder>& dec : ds->decoder_stack())
+                       if (decoder_ == dec.get())
+                               signal_ = ds.get();
+
+       if (signal_) {
+               connect(signal_, SIGNAL(color_changed(QColor)), this, SLOT(on_signal_color_changed(QColor)));
+               connect(signal_, SIGNAL(new_annotations()), this, SLOT(on_new_annotations()));
+               connect(signal_, SIGNAL(decode_reset()), this, SLOT(on_decoder_reset()));
+       }
+
+       update_data();
+
+       // Force repaint, otherwise the new selection isn't shown for some reason
+       table_view_->viewport()->update();
+}
+
+void View::on_hide_hidden_changed(bool checked)
+{
+       model_->set_hide_hidden(checked);
+
+       // Force repaint, otherwise the new selection isn't shown for some reason
+       table_view_->viewport()->update();
+}
+
+void View::on_view_mode_changed(int index)
+{
+       if (index == ViewModeAll)
+               filter_proxy_model_->enable_range_filtering(false);
+
+       if (index == ViewModeVisible) {
+               MetadataObject *md_obj =
+                       session_.metadata_obj_manager()->find_object_by_type(MetadataObjMainViewRange);
+               assert(md_obj);
+
+               int64_t start_sample = md_obj->value(MetadataValueStartSample).toLongLong();
+               int64_t end_sample = md_obj->value(MetadataValueEndSample).toLongLong();
+
+               filter_proxy_model_->enable_range_filtering(true);
+               filter_proxy_model_->set_sample_range(max((int64_t)0, start_sample),
+                       max((int64_t)0, end_sample));
+       }
+
+       if (index == ViewModeLatest) {
+               filter_proxy_model_->enable_range_filtering(false);
+
+               table_view_->scrollTo(
+                       filter_proxy_model_->mapFromSource(model_->index(model_->rowCount() - 1, 0)),
+                       QAbstractItemView::PositionAtBottom);
+       }
+}
+
+void View::on_signal_name_changed(const QString &name)
+{
+       (void)name;
+
+       SignalBase* sb = qobject_cast<SignalBase*>(QObject::sender());
+       assert(sb);
+
+       DecodeSignal* signal = dynamic_cast<DecodeSignal*>(sb);
+       assert(signal);
+
+       // Update the top-level decoder provided by this signal
+       auto stack = signal->decoder_stack();
+       if (!stack.empty()) {
+               shared_ptr<Decoder>& dec = stack.at(0);
+               int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
+
+               if (index != -1)
+                       decoder_selector_->setItemText(index, signal->name());
+       }
+}
+
+void View::on_signal_color_changed(const QColor &color)
+{
+       (void)color;
+
+       // Force immediate repaint, otherwise it's updated after the header popup is closed
+       table_view_->viewport()->update();
+}
+
+void View::on_new_annotations()
+{
+       if (view_mode_selector_->currentIndex() == ViewModeLatest) {
+               update_data();
+               table_view_->scrollTo(
+                       filter_proxy_model_->index(filter_proxy_model_->rowCount() - 1, 0),
+                       QAbstractItemView::PositionAtBottom);
+       } else {
+               if (!delayed_view_updater_.isActive())
+                       delayed_view_updater_.start();
+       }
+}
+
+void View::on_decoder_reset()
+{
+       // Invalidate the model's data connection immediately - otherwise we
+       // will use a stale pointer in model_->index() when called from the table view
+       model_->set_signal_and_segment(signal_, current_segment_);
+}
+
+void View::on_decoder_stacked(void* decoder)
+{
+       Decoder* d = static_cast<Decoder*>(decoder);
+
+       // Find the signal that contains the selected decoder
+       DecodeSignal* signal = nullptr;
+
+       for (const shared_ptr<DecodeSignal>& ds : decode_signals_)
+               for (const shared_ptr<Decoder>& dec : ds->decoder_stack())
+                       if (d == dec.get())
+                               signal = ds.get();
+
+       assert(signal);
+
+       const shared_ptr<Decoder>& dec = signal->decoder_stack().at(0);
+       int index = decoder_selector_->findData(QVariant::fromValue((void*)dec.get()));
+
+       if (index == -1) {
+               // Add the decoder to the list
+               decoder_selector_->addItem(signal->name(), QVariant::fromValue((void*)d));
+       }
+}
+
+void View::on_decoder_removed(void* decoder)
+{
+       Decoder* d = static_cast<Decoder*>(decoder);
+
+       // Remove the decoder from the list
+       int index = decoder_selector_->findData(QVariant::fromValue((void*)d));
+
+       if (index != -1)
+               decoder_selector_->removeItem(index);
+}
+
+void View::on_actionSave_triggered(QAction* action)
+{
+       int save_type = SaveTypeCSVQuoted;
+
+       if (action)
+               save_type = action->data().toInt();
+
+       save_data_as_csv(save_type);
+}
+
+void View::on_table_item_clicked(const QModelIndex& index)
+{
+       (void)index;
+
+       // Force repaint, otherwise the new selection isn't shown for some reason
+       table_view_->viewport()->update();
+}
+
+void View::on_table_item_double_clicked(const QModelIndex& index)
+{
+       const QModelIndex src_idx = filter_proxy_model_->mapToSource(index);
+
+       const Annotation* ann = static_cast<const Annotation*>(src_idx.internalPointer());
+       assert(ann);
+
+       shared_ptr<views::ViewBase> main_view = session_.main_view();
+
+       main_view->focus_on_range(ann->start_sample(), ann->end_sample());
+}
+
+void View::on_table_header_requested(const QPoint& pos)
+{
+       QMenu* menu = new QMenu(this);
+
+       for (int i = 0; i < table_view_->horizontalHeader()->count(); i++) {
+               int column = table_view_->horizontalHeader()->logicalIndex(i);
+
+               const QString title =
+                       filter_proxy_model_->headerData(column, Qt::Horizontal, Qt::DisplayRole).toString();
+               QAction* action = new QAction(title, this);
+
+               action->setCheckable(true);
+               action->setChecked(!table_view_->horizontalHeader()->isSectionHidden(column));
+               action->setData(column);
+
+               connect(action, SIGNAL(toggled(bool)), this, SLOT(on_table_header_toggled(bool)));
+
+               menu->addAction(action);
+       }
+
+       menu->popup(table_view_->horizontalHeader()->viewport()->mapToGlobal(pos));
+}
+
+void View::on_table_header_toggled(bool checked)
+{
+       QAction* action = qobject_cast<QAction*>(QObject::sender());
+       assert(action);
+
+       const int column = action->data().toInt();
+
+       table_view_->horizontalHeader()->setSectionHidden(column, !checked);
+}
+
+void View::on_metadata_object_changed(MetadataObject* obj,
+       MetadataValueType value_type)
+{
+       // Check if we need to update the model's data range. We only work on the
+       // end sample value because the start sample value is updated first and
+       // we don't want to update the model twice
+       if ((view_mode_selector_->currentIndex() == ViewModeVisible) &&
+               (obj->type() == MetadataObjMainViewRange) &&
+               (value_type == MetadataValueEndSample)) {
+
+               int64_t start_sample = obj->value(MetadataValueStartSample).toLongLong();
+               int64_t end_sample = obj->value(MetadataValueEndSample).toLongLong();
+
+               filter_proxy_model_->set_sample_range(max((int64_t)0, start_sample),
+                       max((int64_t)0, end_sample));
+       }
+
+       if (obj->type() == MetadataObjMousePos) {
+               QModelIndex first_visible_idx =
+                       filter_proxy_model_->mapToSource(filter_proxy_model_->index(0, 0));
+               QModelIndex last_visible_idx =
+                       filter_proxy_model_->mapToSource(filter_proxy_model_->index(filter_proxy_model_->rowCount() - 1, 0));
+
+               if (first_visible_idx.isValid()) {
+                       const QModelIndex first_highlighted_idx =
+                               model_->update_highlighted_rows(first_visible_idx, last_visible_idx,
+                                       obj->value(MetadataValueStartSample).toLongLong());
+
+                       if (view_mode_selector_->currentIndex() == ViewModeVisible) {
+                               const QModelIndex idx = filter_proxy_model_->mapFromSource(first_highlighted_idx);
+                               table_view_->scrollTo(idx, QAbstractItemView::EnsureVisible);
+                       }
+
+                       // Force repaint, otherwise the table doesn't immediately update for some reason
+                       table_view_->viewport()->update();
+               }
+       }
+}
+
+void View::perform_delayed_view_update()
+{
+       update_data();
+}
+
+
+} // namespace tabular_decoder
+} // namespace views
+} // namespace pv
diff --git a/pv/views/tabular_decoder/view.hpp b/pv/views/tabular_decoder/view.hpp
new file mode 100644 (file)
index 0000000..3cdcac1
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TABULAR_DECODER_VIEW_HPP
+#define PULSEVIEW_PV_VIEWS_TABULAR_DECODER_VIEW_HPP
+
+#include <QAction>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QKeyEvent>
+#include <QSortFilterProxyModel>
+#include <QTableView>
+#include <QToolButton>
+
+#include "pv/metadata_obj.hpp"
+#include "pv/views/viewbase.hpp"
+#include "pv/data/decodesignal.hpp"
+
+namespace pv {
+class Session;
+
+namespace views {
+
+namespace tabular_decoder {
+
+// When adding an entry here, don't forget to update SaveTypeNames as well
+enum SaveType {
+       SaveTypeCSVEscaped,
+       SaveTypeCSVQuoted,
+       SaveTypeCount  // Indicates how many save types there are, must always be last
+};
+
+// When adding an entry here, don't forget to update ViewModeNames as well
+enum ViewModeType {
+       ViewModeAll,
+       ViewModeLatest,
+       ViewModeVisible,
+       ViewModeCount // Indicates how many view mode types there are, must always be last
+};
+
+extern const char* SaveTypeNames[SaveTypeCount];
+extern const char* ViewModeNames[ViewModeCount];
+
+
+class AnnotationCollectionModel : public QAbstractTableModel
+{
+       Q_OBJECT
+
+public:
+       AnnotationCollectionModel(QObject* parent = nullptr);
+
+       int get_hierarchy_level(const Annotation* ann) const;
+       QVariant data_from_ann(const Annotation* ann, int index) const;
+       QVariant data(const QModelIndex& index, int role) const override;
+       Qt::ItemFlags flags(const QModelIndex& index) const override;
+
+       uint8_t first_hidden_column() const;
+       QVariant headerData(int section, Qt::Orientation orientation,
+               int role = Qt::DisplayRole) const override;
+       QModelIndex index(int row, int column,
+               const QModelIndex& parent_idx = QModelIndex()) const override;
+
+       QModelIndex parent(const QModelIndex& index) const override;
+
+       int rowCount(const QModelIndex& parent_idx = QModelIndex()) const override;
+       int columnCount(const QModelIndex& parent_idx = QModelIndex()) const override;
+
+       void set_signal_and_segment(data::DecodeSignal* signal, uint32_t current_segment);
+       void set_hide_hidden(bool hide_hidden);
+
+       void update_annotations_without_hidden();
+       QModelIndex update_highlighted_rows(QModelIndex first, QModelIndex last,
+               int64_t sample_num);
+
+private Q_SLOTS:
+       void on_annotation_visibility_changed();
+
+private:
+       vector<QVariant> header_data_;
+       const deque<const Annotation*>* all_annotations_;
+       deque<const Annotation*> all_annotations_without_hidden_;
+       const deque<const Annotation*>* dataset_;
+       data::DecodeSignal* signal_;
+       uint8_t first_hidden_column_;
+       uint32_t prev_segment_;
+       uint64_t prev_last_row_;
+       int64_t highlight_sample_num_;
+       bool had_highlight_before_;
+       bool hide_hidden_;
+};
+
+
+class CustomFilterProxyModel : public QSortFilterProxyModel
+{
+       Q_OBJECT
+
+public:
+       CustomFilterProxyModel(QObject* parent = 0);
+
+       void set_sample_range(uint64_t start_sample, uint64_t end_sample);
+
+       void enable_range_filtering(bool value);
+
+protected:
+       bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
+
+private:
+       uint64_t range_start_sample_, range_end_sample_;
+       bool range_filtering_enabled_;
+};
+
+
+class CustomTableView : public QTableView
+{
+       Q_OBJECT
+
+public:
+       virtual QSize minimumSizeHint() const override;
+       virtual QSize sizeHint() const override;
+
+protected:
+       virtual void keyPressEvent(QKeyEvent *event) override;
+
+Q_SIGNALS:
+       void activatedByKey(const QModelIndex &index);
+};
+
+
+class View : public ViewBase, public MetadataObjObserverInterface
+{
+       Q_OBJECT
+
+public:
+       explicit View(Session &session, bool is_main_view=false, QMainWindow *parent = nullptr);
+       ~View();
+
+       virtual ViewType get_type() const;
+
+       /**
+        * Resets the view to its default state after construction. It does however
+        * not reset the signal bases or any other connections with the session.
+        */
+       virtual void reset_view_state();
+
+       virtual void clear_decode_signals();
+       virtual void add_decode_signal(shared_ptr<data::DecodeSignal> signal);
+       virtual void remove_decode_signal(shared_ptr<data::DecodeSignal> signal);
+
+       virtual void save_settings(QSettings &settings) const;
+       virtual void restore_settings(QSettings &settings);
+
+private:
+       void reset_data();
+       void update_data();
+
+       void save_data_as_csv(unsigned int save_type) const;
+
+private Q_SLOTS:
+       void on_selected_decoder_changed(int index);
+       void on_hide_hidden_changed(bool checked);
+       void on_view_mode_changed(int index);
+
+       void on_signal_name_changed(const QString &name);
+       void on_signal_color_changed(const QColor &color);
+       void on_new_annotations();
+
+       void on_decoder_reset();
+       void on_decoder_stacked(void* decoder);
+       void on_decoder_removed(void* decoder);
+
+       void on_actionSave_triggered(QAction* action = nullptr);
+
+       void on_table_item_clicked(const QModelIndex& index);
+       void on_table_item_double_clicked(const QModelIndex& index);
+       void on_table_header_requested(const QPoint& pos);
+       void on_table_header_toggled(bool checked);
+
+       virtual void on_metadata_object_changed(MetadataObject* obj,
+               MetadataValueType value_type);
+
+       virtual void perform_delayed_view_update();
+
+private:
+       QWidget* parent_;
+
+       QComboBox* decoder_selector_;
+       QCheckBox* hide_hidden_cb_;
+       QComboBox* view_mode_selector_;
+
+       QToolButton* save_button_;
+       QAction* save_action_;
+
+       CustomTableView* table_view_;
+       AnnotationCollectionModel* model_;
+       CustomFilterProxyModel* filter_proxy_model_;
+
+       data::DecodeSignal* signal_;
+       const data::decode::Decoder* decoder_;
+};
+
+} // namespace tabular_decoder
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TABULAR_DECODER_VIEW_HPP
index 39ca26f7ad64437da3ca3f163982c0dafe2fa8f2..2a50d5716af2aaf9f88c9468b1e00f50f7b76b63 100644 (file)
@@ -65,23 +65,16 @@ using std::vector;
 using pv::data::LogicSegment;
 using pv::data::SignalBase;
 using pv::util::SIPrefix;
+using pv::util::determine_value_prefix;
 
 namespace pv {
 namespace views {
 namespace trace {
 
-const QColor AnalogSignal::SignalColors[4] = {
-       QColor(0xC4, 0xA0, 0x00),       // Yellow
-       QColor(0x87, 0x20, 0x7A),       // Magenta
-       QColor(0x20, 0x4A, 0x87),       // Blue
-       QColor(0x4E, 0x9A, 0x06)        // Green
-};
-
 const QPen AnalogSignal::AxisPen(QColor(0, 0, 0, 30 * 256 / 100), 2);
 const QColor AnalogSignal::GridMajorColor = QColor(0, 0, 0, 40 * 256 / 100);
 const QColor AnalogSignal::GridMinorColor = QColor(0, 0, 0, 20 * 256 / 100);
 
-const QColor AnalogSignal::SamplingPointColor(0x77, 0x77, 0x77);
 const QColor AnalogSignal::SamplingPointColorLo = QColor(200, 0, 0, 80 * 256 / 100);
 const QColor AnalogSignal::SamplingPointColorNe = QColor(0,   0, 0, 80 * 256 / 100);
 const QColor AnalogSignal::SamplingPointColorHi = QColor(0, 200, 0, 80 * 256 / 100);
@@ -95,28 +88,26 @@ const int64_t AnalogSignal::TracePaintBlockSize = 1024 * 1024;  // 4 MiB (due to
 const float AnalogSignal::EnvelopeThreshold = 64.0f;
 
 const int AnalogSignal::MaximumVDivs = 10;
-const int AnalogSignal::MinScaleIndex = -6;
-const int AnalogSignal::MaxScaleIndex = 7;
+const int AnalogSignal::MinScaleIndex = -6;  // 0.01 units/div
+const int AnalogSignal::MaxScaleIndex = 10;  // 1000 units/div
 
 const int AnalogSignal::InfoTextMarginRight = 20;
 const int AnalogSignal::InfoTextMarginBottom = 5;
 
-AnalogSignal::AnalogSignal(
-       pv::Session &session,
-       shared_ptr<data::SignalBase> base) :
-       Signal(session, base),
+AnalogSignal::AnalogSignal(pv::Session &session, shared_ptr<data::SignalBase> base) :
+       LogicSignal(session, base),
        value_at_hover_pos_(std::numeric_limits<float>::quiet_NaN()),
        scale_index_(4), // 20 per div
        pos_vdivs_(1),
        neg_vdivs_(1),
        resolution_(0),
-       display_type_(DisplayBoth),
+       display_type_(DisplayAnalog),
        autoranging_(true)
 {
        axis_pen_ = AxisPen;
 
        pv::data::Analog* analog_data =
-               dynamic_cast<pv::data::Analog*>(data().get());
+               dynamic_cast<pv::data::Analog*>(base_->analog_data().get());
 
        connect(analog_data, SIGNAL(min_max_changed(float, float)),
                this, SLOT(on_min_max_changed(float, float)));
@@ -134,24 +125,21 @@ AnalogSignal::AnalogSignal(
                settings.value(GlobalSettings::Key_View_ConversionThresholdDispMode).toInt();
        div_height_ = settings.value(GlobalSettings::Key_View_DefaultDivHeight).toInt();
 
-       base_->set_color(SignalColors[base_->index() % countof(SignalColors)]);
+       update_logic_level_offsets();
        update_scale();
 }
 
-shared_ptr<pv::data::SignalData> AnalogSignal::data() const
-{
-       return base_->analog_data();
-}
-
 std::map<QString, QVariant> AnalogSignal::save_settings() const
 {
+       LogicSignal::save_settings();
+
        std::map<QString, QVariant> result;
 
        result["pos_vdivs"] = pos_vdivs_;
        result["neg_vdivs"] = neg_vdivs_;
        result["scale_index"] = scale_index_;
        result["display_type"] = display_type_;
-       result["autoranging"] = pos_vdivs_;
+       result["autoranging"] = autoranging_;
        result["div_height"] = div_height_;
 
        return result;
@@ -159,6 +147,8 @@ std::map<QString, QVariant> AnalogSignal::save_settings() const
 
 void AnalogSignal::restore_settings(std::map<QString, QVariant> settings)
 {
+       LogicSignal::restore_settings(settings);
+
        auto entry = settings.find("pos_vdivs");
        if (entry != settings.end())
                pos_vdivs_ = settings["pos_vdivs"].toInt();
@@ -186,6 +176,9 @@ void AnalogSignal::restore_settings(std::map<QString, QVariant> settings)
                const int old_height = div_height_;
                div_height_ = settings["div_height"].toInt();
 
+               update_logic_level_offsets();
+               update_scale();
+
                if ((div_height_ != old_height) && owner_) {
                        // Call order is important, otherwise the lazy event handler won't work
                        owner_->extents_changed(false, true);
@@ -198,6 +191,7 @@ pair<int, int> AnalogSignal::v_extents() const
 {
        const int ph = pos_vdivs_ * div_height_;
        const int nh = neg_vdivs_ * div_height_;
+
        return make_pair(-ph, nh);
 }
 
@@ -264,32 +258,37 @@ void AnalogSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp)
                paint_grid(p, y, pp.left(), pp.right());
 
                shared_ptr<pv::data::AnalogSegment> segment = get_analog_segment_to_paint();
-               if (!segment || (segment->get_sample_count() == 0))
-                       return;
-
-               const double pixels_offset = pp.pixels_offset();
-               const double samplerate = max(1.0, segment->samplerate());
-               const pv::util::Timestamp& start_time = segment->start_time();
-               const int64_t last_sample = (int64_t)segment->get_sample_count() - 1;
-               const double samples_per_pixel = samplerate * pp.scale();
-               const pv::util::Timestamp start = samplerate * (pp.offset() - start_time);
-               const pv::util::Timestamp end = start + samples_per_pixel * pp.width();
-
-               const int64_t start_sample = min(max(floor(start).convert_to<int64_t>(),
-                       (int64_t)0), last_sample);
-               const int64_t end_sample = min(max((ceil(end) + 1).convert_to<int64_t>(),
-                       (int64_t)0), last_sample);
-
-               if (samples_per_pixel < EnvelopeThreshold)
-                       paint_trace(p, segment, y, pp.left(), start_sample, end_sample,
-                               pixels_offset, samples_per_pixel);
-               else
-                       paint_envelope(p, segment, y, pp.left(), start_sample, end_sample,
-                               pixels_offset, samples_per_pixel);
+
+               if (segment && (segment->get_sample_count() > 0)) {
+                       const double pixels_offset = pp.pixels_offset();
+                       const double samplerate = max(1.0, segment->samplerate());
+                       const pv::util::Timestamp& start_time = segment->start_time();
+                       const int64_t last_sample = (int64_t)segment->get_sample_count() - 1;
+                       const double samples_per_pixel = samplerate * pp.scale();
+                       const pv::util::Timestamp start = samplerate * (pp.offset() - start_time);
+                       const pv::util::Timestamp end = start + samples_per_pixel * pp.width();
+
+                       const int64_t start_sample = min(max(floor(start).convert_to<int64_t>(),
+                               (int64_t)0), last_sample);
+                       const int64_t end_sample = min(max((ceil(end) + 1).convert_to<int64_t>(),
+                               (int64_t)0), last_sample);
+
+                       if (samples_per_pixel < EnvelopeThreshold)
+                               paint_trace(p, segment, y, pp.left(), start_sample, end_sample,
+                                       pixels_offset, samples_per_pixel);
+                       else
+                               paint_envelope(p, segment, y, pp.left(), start_sample, end_sample,
+                                       pixels_offset, samples_per_pixel);
+               }
        }
 
        if ((display_type_ == DisplayConverted) || (display_type_ == DisplayBoth))
-               paint_logic_mid(p, pp);
+               if (base_->logic_data())
+                       LogicSignal::paint_mid(p, pp);
+
+       const QString err = base_->get_error_message();
+       if (!err.isEmpty())
+               paint_error(p, pp);
 }
 
 void AnalogSignal::paint_fore(QPainter &p, ViewItemPaintParams &pp)
@@ -302,12 +301,18 @@ void AnalogSignal::paint_fore(QPainter &p, ViewItemPaintParams &pp)
 
                QString infotext;
 
+               SIPrefix prefix;
+               if (fabs(signal_max_) > fabs(signal_min_))
+                       prefix = determine_value_prefix(fabs(signal_max_));
+               else
+                       prefix = determine_value_prefix(fabs(signal_min_));
+
                // Show the info section on the right side of the trace, including
                // the value at the hover point when the hover marker is enabled
                // and we have corresponding data available
                if (show_hover_marker_ && !std::isnan(value_at_hover_pos_)) {
                        infotext = QString("[%1] %2 V/div")
-                               .arg(format_value_si(value_at_hover_pos_, SIPrefix::unspecified, 2, "V", false))
+                               .arg(format_value_si(value_at_hover_pos_, prefix, 3, "V", false))
                                .arg(resolution_);
                } else
                        infotext = QString("%1 V/div").arg(resolution_);
@@ -321,10 +326,13 @@ void AnalogSignal::paint_fore(QPainter &p, ViewItemPaintParams &pp)
                                v_extents().second - v_extents().first - InfoTextMarginBottom);
 
                p.drawText(bounding_rect, Qt::AlignRight | Qt::AlignBottom, infotext);
+
+               if (show_hover_marker_)
+                       paint_hover_marker(p);
        }
 
-       if (show_hover_marker_)
-               paint_hover_marker(p);
+       if ((display_type_ == DisplayConverted) || (display_type_ == DisplayBoth))
+               LogicSignal::paint_fore(p, pp);
 }
 
 void AnalogSignal::paint_grid(QPainter &p, int y, int left, int right)
@@ -524,186 +532,6 @@ void AnalogSignal::paint_envelope(QPainter &p,
        delete[] e.samples;
 }
 
-void AnalogSignal::paint_logic_mid(QPainter &p, ViewItemPaintParams &pp)
-{
-       QLineF *line;
-
-       vector< pair<int64_t, bool> > edges;
-
-       assert(base_);
-
-       const int y = get_visual_y();
-
-       if (!base_->enabled() || !base_->logic_data())
-               return;
-
-       const int signal_margin =
-               QFontMetrics(QApplication::font()).height() / 2;
-
-       const int ph = min(pos_vdivs_, 1) * div_height_;
-       const int nh = min(neg_vdivs_, 1) * div_height_;
-       const float high_offset = y - ph + signal_margin + 0.5f;
-       const float low_offset = y + nh - signal_margin - 0.5f;
-       const float signal_height = low_offset - high_offset;
-
-       shared_ptr<pv::data::LogicSegment> segment = get_logic_segment_to_paint();
-       if (!segment || (segment->get_sample_count() == 0))
-               return;
-
-       double samplerate = segment->samplerate();
-
-       // Show sample rate as 1Hz when it is unknown
-       if (samplerate == 0.0)
-               samplerate = 1.0;
-
-       const double pixels_offset = pp.pixels_offset();
-       const pv::util::Timestamp& start_time = segment->start_time();
-       const int64_t last_sample = (int64_t)segment->get_sample_count() - 1;
-       const double samples_per_pixel = samplerate * pp.scale();
-       const double pixels_per_sample = 1 / samples_per_pixel;
-       const pv::util::Timestamp start = samplerate * (pp.offset() - start_time);
-       const pv::util::Timestamp end = start + samples_per_pixel * pp.width();
-
-       const int64_t start_sample = min(max(floor(start).convert_to<int64_t>(),
-               (int64_t)0), last_sample);
-       const uint64_t end_sample = min(max(ceil(end).convert_to<int64_t>(),
-               (int64_t)0), last_sample);
-
-       segment->get_subsampled_edges(edges, start_sample, end_sample,
-               samples_per_pixel / LogicSignal::Oversampling, 0);
-       assert(edges.size() >= 2);
-
-       const float first_sample_x =
-               pp.left() + (edges.front().first / samples_per_pixel - pixels_offset);
-       const float last_sample_x =
-               pp.left() + (edges.back().first / samples_per_pixel - pixels_offset);
-
-       // Check whether we need to paint the sampling points
-       const bool show_sampling_points = show_sampling_points_ && (samples_per_pixel < 0.25);
-       vector<QRectF> sampling_points;
-       float sampling_point_x = first_sample_x;
-       int64_t sampling_point_sample = start_sample;
-       const int w = 2;
-
-       if (show_sampling_points)
-               sampling_points.reserve(end_sample - start_sample + 1);
-
-       vector<QRectF> high_rects;
-       float rising_edge_x;
-       bool rising_edge_seen = false;
-
-       // Paint the edges
-       const unsigned int edge_count = edges.size() - 2;
-       QLineF *const edge_lines = new QLineF[edge_count];
-       line = edge_lines;
-
-       if (edges.front().second) {
-               // Beginning of trace is high
-               rising_edge_x = first_sample_x;
-               rising_edge_seen = true;
-       }
-
-       for (auto i = edges.cbegin() + 1; i != edges.cend() - 1; i++) {
-               // Note: multiple edges occupying a single pixel are represented by an edge
-               // with undefined logic level. This means that only the first falling edge
-               // after a rising edge corresponds to said rising edge - and vice versa. If
-               // more edges with the same logic level follow, they denote multiple edges.
-
-               const float x = pp.left() + ((*i).first / samples_per_pixel - pixels_offset);
-               *line++ = QLineF(x, high_offset, x, low_offset);
-
-               if (fill_high_areas_) {
-                       // Any edge terminates a high area
-                       if (rising_edge_seen) {
-                               const int width = x - rising_edge_x;
-                               if (width > 0)
-                                       high_rects.emplace_back(rising_edge_x, high_offset,
-                                               width, signal_height);
-                               rising_edge_seen = false;
-                       }
-
-                       // Only rising edges start high areas
-                       if ((*i).second) {
-                               rising_edge_x = x;
-                               rising_edge_seen = true;
-                       }
-               }
-
-               if (show_sampling_points)
-                       while (sampling_point_sample < (*i).first) {
-                               const float y = (*i).second ? low_offset : high_offset;
-                               sampling_points.emplace_back(
-                                       QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
-                               sampling_point_sample++;
-                               sampling_point_x += pixels_per_sample;
-                       };
-       }
-
-       // Calculate the sample points from the last edge to the end of the trace
-       if (show_sampling_points)
-               while ((uint64_t)sampling_point_sample <= end_sample) {
-                       // Signal changed after the last edge, so the level is inverted
-                       const float y = (edges.cend() - 1)->second ? high_offset : low_offset;
-                       sampling_points.emplace_back(
-                               QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
-                       sampling_point_sample++;
-                       sampling_point_x += pixels_per_sample;
-               };
-
-       if (fill_high_areas_) {
-               // Add last high rectangle if the signal is still high at the end of the trace
-               if (rising_edge_seen && (edges.cend() - 1)->second)
-                       high_rects.emplace_back(rising_edge_x, high_offset,
-                               last_sample_x - rising_edge_x, signal_height);
-
-               p.setPen(high_fill_color_);
-               p.setBrush(high_fill_color_);
-               p.drawRects((const QRectF*)(high_rects.data()), high_rects.size());
-       }
-
-       p.setPen(LogicSignal::EdgeColor);
-       p.drawLines(edge_lines, edge_count);
-       delete[] edge_lines;
-
-       // Paint the caps
-       const unsigned int max_cap_line_count = edges.size();
-       QLineF *const cap_lines = new QLineF[max_cap_line_count];
-
-       p.setPen(LogicSignal::HighColor);
-       paint_logic_caps(p, cap_lines, edges, true, samples_per_pixel,
-               pixels_offset, pp.left(), high_offset);
-       p.setPen(LogicSignal::LowColor);
-       paint_logic_caps(p, cap_lines, edges, false, samples_per_pixel,
-               pixels_offset, pp.left(), low_offset);
-
-       delete[] cap_lines;
-
-       // Paint the sampling points
-       if (show_sampling_points) {
-               p.setPen(SamplingPointColor);
-               p.drawRects(sampling_points.data(), sampling_points.size());
-       }
-}
-
-void AnalogSignal::paint_logic_caps(QPainter &p, QLineF *const lines,
-       vector< pair<int64_t, bool> > &edges, bool level,
-       double samples_per_pixel, double pixels_offset, float x_offset,
-       float y_offset)
-{
-       QLineF *line = lines;
-
-       for (auto i = edges.begin(); i != (edges.end() - 1); i++)
-               if ((*i).second == level) {
-                       *line++ = QLineF(
-                               ((*i).first / samples_per_pixel -
-                                       pixels_offset) + x_offset, y_offset,
-                               ((*(i+1)).first / samples_per_pixel -
-                                       pixels_offset) + x_offset, y_offset);
-               }
-
-       p.drawLines(lines, line - lines);
-}
-
 shared_ptr<pv::data::AnalogSegment> AnalogSignal::get_analog_segment_to_paint() const
 {
        shared_ptr<pv::data::AnalogSegment> segment;
@@ -716,7 +544,7 @@ shared_ptr<pv::data::AnalogSegment> AnalogSignal::get_analog_segment_to_paint()
                        segment = segments.back();
 
                if ((segment_display_mode_ == ShowSingleSegmentOnly) ||
-                               (segment_display_mode_ == ShowLastCompleteSegmentOnly)) {
+                       (segment_display_mode_ == ShowLastCompleteSegmentOnly)) {
                        try {
                                segment = segments.at(current_segment_);
                        } catch (out_of_range&) {
@@ -728,30 +556,6 @@ shared_ptr<pv::data::AnalogSegment> AnalogSignal::get_analog_segment_to_paint()
        return segment;
 }
 
-shared_ptr<pv::data::LogicSegment> AnalogSignal::get_logic_segment_to_paint() const
-{
-       shared_ptr<pv::data::LogicSegment> segment;
-
-       const deque< shared_ptr<pv::data::LogicSegment> > &segments =
-               base_->logic_data()->logic_segments();
-
-       if (!segments.empty()) {
-               if (segment_display_mode_ == ShowLastSegmentOnly)
-                       segment = segments.back();
-
-               if ((segment_display_mode_ == ShowSingleSegmentOnly) ||
-                               (segment_display_mode_ == ShowLastCompleteSegmentOnly)) {
-                       try {
-                               segment = segments.at(current_segment_);
-                       } catch (out_of_range&) {
-                               qDebug() << "Current logic segment out of range for signal" << base_->name() << ":" << current_segment_;
-                       }
-               }
-       }
-
-       return segment;
-}
-
 float AnalogSignal::get_resolution(int scale_index)
 {
        const float seq[] = {1.0f, 2.0f, 5.0f};
@@ -769,6 +573,17 @@ void AnalogSignal::update_scale()
        scale_ = div_height_ / resolution_;
 }
 
+void AnalogSignal::update_logic_level_offsets()
+{
+       const int signal_margin = QFontMetrics(QApplication::font()).height() / 2;
+
+       const int ph = min(pos_vdivs_, 1) * div_height_;
+       const int nh = min(neg_vdivs_, 1) * div_height_;
+
+       high_level_offset_ = -ph + signal_margin + 0.5f;
+       low_level_offset_  =  nh - signal_margin - 0.5f;
+}
+
 void AnalogSignal::update_conversion_widgets()
 {
        SignalBase::ConversionType conv_type = base_->get_conversion_type();
@@ -852,7 +667,6 @@ void AnalogSignal::perform_autoranging(bool keep_divs, bool force_update)
        if (segments.empty())
                return;
 
-       double signal_min_ = 0, signal_max_ = 0;
        double min = 0, max = 0;
 
        for (const shared_ptr<pv::data::AnalogSegment>& segment : segments) {
@@ -882,16 +696,18 @@ void AnalogSignal::perform_autoranging(bool keep_divs, bool force_update)
                }
        }
 
+       const bool showing_logic = (display_type_ == DisplayConverted) || (display_type_ == DisplayBoth);
+
        // If there is still no positive div when we need it, add one
        // (this can happen when pos_vdivs==neg_vdivs==0)
-       if ((max > 0) && (pos_vdivs_ == 0)) {
+       if (((max > 0) && (pos_vdivs_ == 0)) || showing_logic) {
                pos_vdivs_ = 1;
                owner_->extents_changed(false, true);
        }
 
        // If there is still no negative div when we need it, add one
        // (this can happen when pos_vdivs was 0 or 1 when trying to split)
-       if ((min < 0) && (neg_vdivs_ == 0)) {
+       if (((min < 0) && (neg_vdivs_ == 0)) || showing_logic) {
                neg_vdivs_ = 1;
                owner_->extents_changed(false, true);
        }
@@ -977,22 +793,22 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        // Add the standard options
        Signal::populate_popup_form(parent, form);
 
-       QFormLayout *const layout = new QFormLayout;
-
        // Add div-related settings
        pvdiv_sb_ = new QSpinBox(parent);
        pvdiv_sb_->setRange(0, MaximumVDivs);
        pvdiv_sb_->setValue(pos_vdivs_);
+       pvdiv_sb_->setEnabled(!autoranging_);
        connect(pvdiv_sb_, SIGNAL(valueChanged(int)),
                this, SLOT(on_pos_vdivs_changed(int)));
-       layout->addRow(tr("Number of pos vertical divs"), pvdiv_sb_);
+       form->addRow(tr("Number of pos vertical divs"), pvdiv_sb_);
 
        nvdiv_sb_ = new QSpinBox(parent);
        nvdiv_sb_->setRange(0, MaximumVDivs);
        nvdiv_sb_->setValue(neg_vdivs_);
+       nvdiv_sb_->setEnabled(!autoranging_);
        connect(nvdiv_sb_, SIGNAL(valueChanged(int)),
                this, SLOT(on_neg_vdivs_changed(int)));
-       layout->addRow(tr("Number of neg vertical divs"), nvdiv_sb_);
+       form->addRow(tr("Number of neg vertical divs"), nvdiv_sb_);
 
        div_height_sb_ = new QSpinBox(parent);
        div_height_sb_->setRange(20, 1000);
@@ -1001,10 +817,11 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        div_height_sb_->setValue(div_height_);
        connect(div_height_sb_, SIGNAL(valueChanged(int)),
                this, SLOT(on_div_height_changed(int)));
-       layout->addRow(tr("Div height"), div_height_sb_);
+       form->addRow(tr("Div height"), div_height_sb_);
 
        // Add the vertical resolution
        resolution_cb_ = new QComboBox(parent);
+       resolution_cb_->setEnabled(!autoranging_);
 
        for (int i = MinScaleIndex; i < MaxScaleIndex; i++) {
                const QString label = QString("%1").arg(get_resolution(i));
@@ -1022,7 +839,7 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        vdiv_layout->addWidget(resolution_cb_, 0, 0);
        vdiv_layout->addWidget(vdiv_unit, 0, 1);
 
-       layout->addRow(tr("Vertical resolution"), vdiv_layout);
+       form->addRow(tr("Vertical resolution"), vdiv_layout);
 
        // Add the autoranging checkbox
        QCheckBox* autoranging_cb = new QCheckBox();
@@ -1031,7 +848,7 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        connect(autoranging_cb, SIGNAL(stateChanged(int)),
                this, SLOT(on_autoranging_changed(int)));
 
-       layout->addRow(tr("Autoranging"), autoranging_cb);
+       form->addRow(tr("Autoranging"), autoranging_cb);
 
        // Add the conversion type dropdown
        conversion_cb_ = new QComboBox();
@@ -1046,7 +863,7 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        cur_idx = conversion_cb_->findData(QVariant(base_->get_conversion_type()));
        conversion_cb_->setCurrentIndex(cur_idx);
 
-       layout->addRow(tr("Conversion"), conversion_cb_);
+       form->addRow(tr("Conversion"), conversion_cb_);
 
        connect(conversion_cb_, SIGNAL(currentIndexChanged(int)),
                this, SLOT(on_conversion_changed(int)));
@@ -1055,7 +872,7 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
     conv_threshold_cb_ = new QComboBox();
     conv_threshold_cb_->setEditable(true);
 
-    layout->addRow(tr("Conversion threshold(s)"), conv_threshold_cb_);
+    form->addRow(tr("Conversion threshold(s)"), conv_threshold_cb_);
 
     connect(conv_threshold_cb_, SIGNAL(currentIndexChanged(int)),
             this, SLOT(on_conv_threshold_changed(int)));
@@ -1072,15 +889,13 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        cur_idx = display_type_cb_->findData(QVariant(display_type_));
        display_type_cb_->setCurrentIndex(cur_idx);
 
-       layout->addRow(tr("Show traces for"), display_type_cb_);
+       form->addRow(tr("Show traces for"), display_type_cb_);
 
        connect(display_type_cb_, SIGNAL(currentIndexChanged(int)),
                this, SLOT(on_display_type_changed(int)));
 
        // Update the conversion widget contents and states
        update_conversion_widgets();
-
-       form->addRow(layout);
 }
 
 void AnalogSignal::hover_point_changed(const QPoint &hp)
@@ -1126,11 +941,12 @@ void AnalogSignal::on_setting_changed(const QString &key, const QVariant &value)
 
 void AnalogSignal::on_min_max_changed(float min, float max)
 {
-       (void)min;
-       (void)max;
-
        if (autoranging_)
                perform_autoranging(false, false);
+       else {
+               if (min < signal_min_) signal_min_ = min;
+               if (max > signal_max_) signal_max_ = max;
+       }
 }
 
 void AnalogSignal::on_pos_vdivs_changed(int vdivs)
@@ -1157,6 +973,8 @@ void AnalogSignal::on_pos_vdivs_changed(int vdivs)
                }
        }
 
+       update_logic_level_offsets();
+
        if (owner_) {
                // Call order is important, otherwise the lazy event handler won't work
                owner_->extents_changed(false, true);
@@ -1188,6 +1006,8 @@ void AnalogSignal::on_neg_vdivs_changed(int vdivs)
                }
        }
 
+       update_logic_level_offsets();
+
        if (owner_) {
                // Call order is important, otherwise the lazy event handler won't work
                owner_->extents_changed(false, true);
@@ -1198,6 +1018,7 @@ void AnalogSignal::on_neg_vdivs_changed(int vdivs)
 void AnalogSignal::on_div_height_changed(int height)
 {
        div_height_ = height;
+       update_logic_level_offsets();
        update_scale();
 
        if (owner_) {
@@ -1220,6 +1041,13 @@ void AnalogSignal::on_autoranging_changed(int state)
 {
        autoranging_ = (state == Qt::Checked);
 
+       if (pvdiv_sb_)
+               pvdiv_sb_->setEnabled(!autoranging_);
+       if (nvdiv_sb_)
+               nvdiv_sb_->setEnabled(!autoranging_);
+       if (resolution_cb_)
+               resolution_cb_->setEnabled(!autoranging_);
+
        if (autoranging_)
                perform_autoranging(false, true);
 
@@ -1241,6 +1069,11 @@ void AnalogSignal::on_conversion_changed(int index)
                base_->set_conversion_type(conv_type);
                update_conversion_widgets();
 
+               if (conv_type == SignalBase::ConversionType::NoConversion)
+                       on_display_type_changed(DisplayType::DisplayAnalog);
+               else
+                       on_display_type_changed(DisplayType::DisplayBoth);
+
                if (owner_)
                        owner_->row_item_appearance_changed(false, true);
        }
@@ -1268,13 +1101,20 @@ void AnalogSignal::on_conv_threshold_changed(int index)
                // https://txt2re.com/index-c++.php3?s=0.1V&1&-13
                QString re1 = "([+-]?\\d*[\\.,]?\\d*)"; // Float value
                QString re2 = "([a-zA-Z]*)"; // SI unit
-               QRegExp regex(re1 + re2);
-
                const QString text = conv_threshold_cb_->currentText();
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               QRegularExpression regex(re1 + re2);
+               if (!regex.match(text).hasMatch())
+                       return;  // String doesn't match the regex
+
+               QStringList tokens = regex.match(text).capturedTexts();
+#else
+               QRegExp regex(re1 + re2);
                if (!regex.exactMatch(text))
                        return;  // String doesn't match the regex
 
                QStringList tokens = regex.capturedTexts();
+#endif
 
                // For now, we simply assume that the unit is volt without modifiers
                const double thr = tokens.at(1).toDouble();
@@ -1295,13 +1135,22 @@ void AnalogSignal::on_conv_threshold_changed(int index)
                QString re3 = "\\/"; // Forward slash, not captured
                QString re4 = "([+-]?\\d*[\\.,]?\\d*)"; // Float value
                QString re5 = "([a-zA-Z]*)"; // SI unit
+               const QString text = conv_threshold_cb_->currentText();
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               QRegularExpression regex(re1 + re2 + re3 + re4 + re5);
+
+               if (!regex.match(text).hasMatch())
+                       return;  // String doesn't match the regex
+
+               QStringList tokens = regex.match(text).capturedTexts();
+#else
                QRegExp regex(re1 + re2 + re3 + re4 + re5);
 
-               const QString text = conv_threshold_cb_->currentText();
                if (!regex.exactMatch(text))
                        return;  // String doesn't match the regex
 
                QStringList tokens = regex.capturedTexts();
+#endif
 
                // For now, we simply assume that the unit is volt without modifiers
                const double low_thr = tokens.at(1).toDouble();
@@ -1331,8 +1180,21 @@ void AnalogSignal::on_delayed_conversion_starter()
 
 void AnalogSignal::on_display_type_changed(int index)
 {
+       const bool prev_showing_logic = (display_type_ == DisplayConverted) || (display_type_ == DisplayBoth);
+
        display_type_ = (DisplayType)(display_type_cb_->itemData(index).toInt());
 
+       const bool showing_logic = (display_type_ == DisplayConverted) || (display_type_ == DisplayBoth);
+
+       // If we show a logic trace, make sure we have at least one div for each
+       // polarity as that's where we paint it
+       if (showing_logic && !prev_showing_logic) {
+               if (pos_vdivs_ == 0)
+                       on_pos_vdivs_changed(1);
+               if (neg_vdivs_ == 0)
+                       on_neg_vdivs_changed(1);
+       }
+
        if (owner_)
                owner_->row_item_appearance_changed(false, true);
 }
index c588eb88bf26e33abb7e7a8ac0fc53e7dc30af47..6fbcc24b5ae7cadc709f3ffe0985d01225bd43e3 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_ANALOGSIGNAL_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_ANALOGSIGNAL_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_ANALOGSIGNAL_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_ANALOGSIGNAL_HPP
 
 #include <memory>
 
@@ -27,6 +27,7 @@
 #include <QSpinBox>
 
 #include <pv/views/trace/signal.hpp>
+#include <pv/views/trace/logicsignal.hpp>
 
 using std::pair;
 using std::shared_ptr;
@@ -42,15 +43,13 @@ class SignalBase;
 namespace views {
 namespace trace {
 
-class AnalogSignal : public Signal
+class AnalogSignal : public LogicSignal
 {
        Q_OBJECT
 
 private:
        static const QPen AxisPen;
-       static const QColor SignalColors[4];
        static const QColor GridMajorColor, GridMinorColor;
-       static const QColor SamplingPointColor;
        static const QColor SamplingPointColorLo;
        static const QColor SamplingPointColorNe;
        static const QColor SamplingPointColorHi;
@@ -75,8 +74,6 @@ private:
 public:
        AnalogSignal(pv::Session &session, shared_ptr<data::SignalBase> base);
 
-       shared_ptr<pv::data::SignalData> data() const;
-
        virtual std::map<QString, QVariant> save_settings() const;
        virtual void restore_settings(std::map<QString, QVariant> settings);
 
@@ -84,28 +81,28 @@ public:
         * Computes the vertical extents of the contents of this row item.
         * @return A pair containing the minimum and maximum y-values.
         */
-       pair<int, int> v_extents() const;
+       virtual pair<int, int> v_extents() const;
 
        /**
         * Paints the background layer of the signal with a QPainter
         * @param p the QPainter to paint into.
         * @param pp the painting parameters object to paint with..
         */
-       void paint_back(QPainter &p, ViewItemPaintParams &pp);
+       virtual void paint_back(QPainter &p, ViewItemPaintParams &pp);
 
        /**
         * Paints the mid-layer of the signal with a QPainter
         * @param p the QPainter to paint into.
         * @param pp the painting parameters object to paint with..
         */
-       void paint_mid(QPainter &p, ViewItemPaintParams &pp);
+       virtual void paint_mid(QPainter &p, ViewItemPaintParams &pp);
 
        /**
         * Paints the foreground layer of the item with a QPainter
         * @param p the QPainter to paint into.
         * @param pp the painting parameters object to paint with.
         */
-       void paint_fore(QPainter &p, ViewItemPaintParams &pp);
+       virtual void paint_fore(QPainter &p, ViewItemPaintParams &pp);
 
 private:
        void paint_grid(QPainter &p, int y, int left, int right);
@@ -120,15 +117,7 @@ private:
                int y, int left, const int64_t start, const int64_t end,
                const double pixels_offset, const double samples_per_pixel);
 
-       void paint_logic_mid(QPainter &p, ViewItemPaintParams &pp);
-
-       void paint_logic_caps(QPainter &p, QLineF *const lines,
-               vector< pair<int64_t, bool> > &edges,
-               bool level, double samples_per_pixel, double pixels_offset,
-               float x_offset, float y_offset);
-
        shared_ptr<pv::data::AnalogSegment> get_analog_segment_to_paint() const;
-       shared_ptr<pv::data::LogicSegment> get_logic_segment_to_paint() const;
 
        /**
         * Computes the scale factor from the scale index and vdiv settings.
@@ -136,6 +125,7 @@ private:
        float get_resolution(int scale_index);
 
        void update_scale();
+       virtual void update_logic_level_offsets();
 
        void update_conversion_widgets();
 
@@ -197,7 +187,8 @@ private:
        int current_pixel_pos_;  // Only used during lookup table update
 
        // ---------------------------------------------------------------------------
-       // Note: Make sure to update .. when adding a trace-configurable variable here
+       // Note: Make sure to update save_settings() and restore_settings() when
+       //       adding a trace-configurable variable here
        float scale_;
        int scale_index_;
 
@@ -213,4 +204,4 @@ private:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_ANALOGSIGNAL_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_ANALOGSIGNAL_HPP
index 80eaba232b1d8157b2bbfacbb40ebe202e79670a..8462a3a604c6e2509f0389c5ffbec96ba91c5908 100644 (file)
@@ -72,7 +72,7 @@ QRectF Cursor::label_rect(const QRectF &rect) const
        const float x = get_x();
 
        QFontMetrics m(QApplication::font());
-       QSize text_size = m.boundingRect(get_text()).size();
+       QSize text_size = m.boundingRect(get_display_text()).size();
 
        const QSizeF label_size(
                text_size.width() + LabelPadding.width() * 2,
index 0da72e9fa5e3744b9d7c8f3ed2f1c84b3be02f71..73e84ee8d377fbb4ab006ff4ba5781bfd260c851 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSOR_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSOR_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_CURSOR_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_CURSOR_HPP
 
 #include "timemarker.hpp"
 
@@ -77,4 +77,4 @@ private:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSOR_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_CURSOR_HPP
index 81688337f94e238c8eaaf0310a54d5ced35a1ee4..ec7b75addd75180b9b26d288c4b1a20cb805fae9 100644 (file)
@@ -125,37 +125,40 @@ QMenu *CursorPair::create_header_context_menu(QWidget *parent)
 {
        QMenu *menu = new QMenu(parent);
 
-       QAction *displayIntervalAction = new QAction(tr("Display interval"));
+       QAction *displayIntervalAction = new QAction(tr("Display interval"), this);
        displayIntervalAction->setCheckable(true);
        displayIntervalAction->setChecked(show_interval_);
        menu->addAction(displayIntervalAction);
 
-       connect(displayIntervalAction, &QAction::toggled, [=]{
-               GlobalSettings settings;
-               settings.setValue(GlobalSettings::Key_View_CursorShowInterval,
-                       !settings.value(GlobalSettings::Key_View_CursorShowInterval).value<bool>());
+       connect(displayIntervalAction, &QAction::toggled, displayIntervalAction,
+               [=]{
+                       GlobalSettings settings;
+                       settings.setValue(GlobalSettings::Key_View_CursorShowInterval,
+                               !settings.value(GlobalSettings::Key_View_CursorShowInterval).value<bool>());
                });
 
-       QAction *displayFrequencyAction = new QAction(tr("Display frequency"));
+       QAction *displayFrequencyAction = new QAction(tr("Display frequency"), this);
        displayFrequencyAction->setCheckable(true);
        displayFrequencyAction->setChecked(show_frequency_);
        menu->addAction(displayFrequencyAction);
 
-       connect(displayFrequencyAction, &QAction::toggled, [=]{
-               GlobalSettings settings;
-               settings.setValue(GlobalSettings::Key_View_CursorShowFrequency,
-                       !settings.value(GlobalSettings::Key_View_CursorShowFrequency).value<bool>());
+       connect(displayFrequencyAction, &QAction::toggled, displayFrequencyAction,
+               [=]{
+                       GlobalSettings settings;
+                       settings.setValue(GlobalSettings::Key_View_CursorShowFrequency,
+                               !settings.value(GlobalSettings::Key_View_CursorShowFrequency).value<bool>());
                });
 
-       QAction *displaySamplesAction = new QAction(tr("Display samples"));
+       QAction *displaySamplesAction = new QAction(tr("Display samples"), this);
        displaySamplesAction->setCheckable(true);
        displaySamplesAction->setChecked(show_samples_);
        menu->addAction(displaySamplesAction);
 
-       connect(displaySamplesAction, &QAction::toggled, [=]{
-               GlobalSettings settings;
-               settings.setValue(GlobalSettings::Key_View_CursorShowSamples,
-                       !settings.value(GlobalSettings::Key_View_CursorShowSamples).value<bool>());
+       connect(displaySamplesAction, &QAction::toggled, displaySamplesAction,
+               [=]{
+                       GlobalSettings settings;
+                       settings.setValue(GlobalSettings::Key_View_CursorShowSamples,
+                               !settings.value(GlobalSettings::Key_View_CursorShowSamples).value<bool>());
                });
 
        return menu;
@@ -199,11 +202,13 @@ void CursorPair::paint_label(QPainter &p, const QRect &rect, bool hover)
 
        text_size_ = p.boundingRect(QRectF(), 0, text).size();
 
+       /* Currently, selecting the middle section between two cursors doesn't do
+        * anything, so don't highlight it when selected
        if (selected()) {
                p.setBrush(Qt::transparent);
                p.setPen(highlight_pen());
                p.drawRoundedRect(delta_rect, radius, radius);
-       }
+       } */
 
        p.setBrush(hover ? Cursor::FillColor.lighter() : Cursor::FillColor);
        p.setPen(Cursor::FillColor.darker());
index d59d9414d6ae5488896131ccf9c667716bd0737f..ba6b2a7097590120942ad530325bf48bb4321aa0 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSORPAIR_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSORPAIR_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_CURSORPAIR_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_CURSORPAIR_HPP
 
 #include "cursor.hpp"
 #include "pv/globalsettings.hpp"
@@ -135,4 +135,4 @@ private:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_CURSORPAIR_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_CURSORPAIR_HPP
index a91f5e03985504a585e0f5dfe08f1c78ce47d7c2..63384911d7f515b160249e94daa3c23b65fdd743 100644 (file)
@@ -17,9 +17,7 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-extern "C" {
 #include <libsigrokdecode/libsigrokdecode.h>
-}
 
 #include <limits>
 #include <mutex>
@@ -82,12 +80,9 @@ namespace pv {
 namespace views {
 namespace trace {
 
-#define DECODETRACE_COLOR_SATURATION (180) /* 0-255 */
-#define DECODETRACE_COLOR_VALUE (170) /* 0-255 */
-
-const QColor DecodeTrace::ErrorBgColor = QColor(0xEF, 0x29, 0x29);
 const QColor DecodeTrace::NoDecodeColor = QColor(0x88, 0x8A, 0x85);
 const QColor DecodeTrace::ExpandMarkerWarnColor = QColor(0xFF, 0xA5, 0x00); // QColorConstants::Svg::orange
+const QColor DecodeTrace::ExpandMarkerHiddenColor = QColor(0x69, 0x69, 0x69); // QColorConstants::Svg::dimgray
 const uint8_t DecodeTrace::ExpansionAreaHeaderAlpha = 10 * 255 / 100;
 const uint8_t DecodeTrace::ExpansionAreaAlpha = 5 * 255 / 100;
 
@@ -97,8 +92,8 @@ const int DecodeTrace::RowTitleMargin = 7;
 const int DecodeTrace::DrawPadding = 100;
 
 const int DecodeTrace::MaxTraceUpdateRate = 1; // No more than 1 Hz
-const unsigned int DecodeTrace::AnimationDurationInTicks = 7;
-
+const int DecodeTrace::AnimationDurationInTicks = 7;
+const int DecodeTrace::HiddenRowHideDelay = 1000; // 1 second
 
 /**
  * Helper function for forceUpdate()
@@ -149,7 +144,7 @@ DecodeTrace::DecodeTrace(pv::Session &session,
        shared_ptr<data::SignalBase> signalbase, int index) :
        Trace(signalbase),
        session_(session),
-       max_visible_rows_(0),
+       show_hidden_rows_(false),
        delete_mapper_(this),
        show_hide_mapper_(this),
        row_show_hide_mapper_(this)
@@ -163,7 +158,8 @@ DecodeTrace::DecodeTrace(pv::Session &session,
 
        // Determine shortest string we want to see displayed in full
        QFontMetrics m(QApplication::font());
-       min_useful_label_width_ = m.width("XX"); // e.g. two hex characters
+       // e.g. two hex characters
+       min_useful_label_width_ = util::text_width(m, "XX");
 
        default_row_height_ = (ViewItemPaintParams::text_height() * 6) / 4;
        annotation_height_ = (ViewItemPaintParams::text_height() * 5) / 4;
@@ -174,11 +170,14 @@ DecodeTrace::DecodeTrace(pv::Session &session,
        // Note: The offset equals the color of the first annotation
        QColor color;
        const int h = (120 + 160 * index) % 360;
-       const int s = DECODETRACE_COLOR_SATURATION;
-       const int v = DECODETRACE_COLOR_VALUE;
+       const int s = DECODE_COLOR_SATURATION;
+       const int v = DECODE_COLOR_VALUE;
        color.setHsv(h, s, v);
        base_->set_color(color);
 
+       connect(decode_signal_.get(), SIGNAL(color_changed(QColor)),
+               this, SLOT(on_color_changed(QColor)));
+
        connect(decode_signal_.get(), SIGNAL(new_annotations()),
                this, SLOT(on_new_annotations()));
        connect(decode_signal_.get(), SIGNAL(decode_reset()),
@@ -187,7 +186,16 @@ DecodeTrace::DecodeTrace(pv::Session &session,
                this, SLOT(on_decode_finished()));
        connect(decode_signal_.get(), SIGNAL(channels_updated()),
                this, SLOT(on_channels_updated()));
-
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(&delete_mapper_, SIGNAL(mappedInt(int)),
+               this, SLOT(on_delete_decoder(int)));
+       connect(&show_hide_mapper_, SIGNAL(mappedInt(int)),
+               this, SLOT(on_show_hide_decoder(int)));
+       connect(&row_show_hide_mapper_, SIGNAL(mappedInt(int)),
+               this, SLOT(on_show_hide_row(int)));
+       connect(&class_show_hide_mapper_, SIGNAL(mappedObject(QObject*)),
+               this, SLOT(on_show_hide_class(QObject*)));
+#else
        connect(&delete_mapper_, SIGNAL(mapped(int)),
                this, SLOT(on_delete_decoder(int)));
        connect(&show_hide_mapper_, SIGNAL(mapped(int)),
@@ -196,6 +204,7 @@ DecodeTrace::DecodeTrace(pv::Session &session,
                this, SLOT(on_show_hide_row(int)));
        connect(&class_show_hide_mapper_, SIGNAL(mapped(QWidget*)),
                this, SLOT(on_show_hide_class(QWidget*)));
+#endif
 
        connect(&delayed_trace_updater_, SIGNAL(timeout()),
                this, SLOT(on_delayed_trace_update()));
@@ -206,6 +215,11 @@ DecodeTrace::DecodeTrace(pv::Session &session,
                this, SLOT(on_animation_timer()));
        animation_timer_.setInterval(1000 / 50);
 
+       connect(&delayed_hidden_row_hider_, SIGNAL(timeout()),
+               this, SLOT(on_hide_hidden_rows()));
+       delayed_hidden_row_hider_.setSingleShot(true);
+       delayed_hidden_row_hider_.setInterval(HiddenRowHideDelay);
+
        default_marker_shape_ << QPoint(0,         -ArrowSize);
        default_marker_shape_ << QPoint(ArrowSize,  0);
        default_marker_shape_ << QPoint(0,          ArrowSize);
@@ -235,10 +249,21 @@ shared_ptr<data::SignalBase> DecodeTrace::base() const
        return base_;
 }
 
+void DecodeTrace::set_owner(TraceTreeItemOwner *owner)
+{
+       Trace::set_owner(owner);
+
+       // The owner is set in trace::View::signals_changed(), which is a slot.
+       // So after this trace was added to the view, we won't have an owner
+       // that we need to initialize in update_rows(). Once we do, we call it
+       // from on_decode_reset().
+       on_decode_reset();
+}
+
 pair<int, int> DecodeTrace::v_extents() const
 {
        // Make an empty decode trace appear symmetrical
-       if (max_visible_rows_ == 0)
+       if (visible_rows_ == 0)
                return make_pair(-default_row_height_, default_row_height_);
 
        unsigned int height = 0;
@@ -258,6 +283,7 @@ void DecodeTrace::paint_back(QPainter &p, ViewItemPaintParams &pp)
 void DecodeTrace::paint_mid(QPainter &p, ViewItemPaintParams &pp)
 {
        lock_guard<mutex> lock(row_modification_mutex_);
+       unsigned int visible_rows;
 
 #if DECODETRACE_SHOW_RENDER_TIME
        render_time_.restart();
@@ -274,14 +300,15 @@ void DecodeTrace::paint_mid(QPainter &p, ViewItemPaintParams &pp)
        sample_range.second = min((int64_t)sample_range.second,
                decode_signal_->get_decoded_sample_count(current_segment_, false));
 
-       visible_rows_ = 0;
+       visible_rows = 0;
        int y = get_visual_y();
 
        for (DecodeTraceRow& r : rows_) {
                // If the row is hidden, we don't want to fetch annotations
                assert(r.decode_row);
                assert(r.decode_row->decoder());
-               if ((!r.decode_row->decoder()->visible()) || (!r.decode_row->visible())) {
+               if ((!r.decode_row->decoder()->visible()) ||
+                       ((!r.decode_row->visible() && (!show_hidden_rows_) && (!r.expanding) && (!r.expanded) && (!r.collapsing)))) {
                        r.currently_visible = false;
                        continue;
                }
@@ -296,30 +323,30 @@ void DecodeTrace::paint_mid(QPainter &p, ViewItemPaintParams &pp)
                r.currently_visible = !annotations.empty();
                if (!r.currently_visible) {
                        size_t ann_count = decode_signal_->get_annotation_count(r.decode_row, current_segment_);
-                       r.currently_visible = (always_show_all_rows_ || r.has_hidden_classes) &&
-                               (ann_count > 0);
+                       r.currently_visible = ((always_show_all_rows_ || r.has_hidden_classes) &&
+                               (ann_count > 0)) || r.expanded;
                }
 
                if (r.currently_visible) {
                        draw_annotations(annotations, p, pp, y, r);
                        y += r.height;
-                       visible_rows_++;
+                       visible_rows++;
                }
        }
 
        draw_unresolved_period(p, pp.left(), pp.right());
 
-       if (visible_rows_ > max_visible_rows_) {
-               max_visible_rows_ = visible_rows_;
+       if (visible_rows != visible_rows_) {
+               visible_rows_ = visible_rows;
 
                // Call order is important, otherwise the lazy event handler won't work
                owner_->extents_changed(false, true);
                owner_->row_item_appearance_changed(false, true);
        }
 
-       const QString err = decode_signal_->error_message();
+       const QString err = base_->get_error_message();
        if (!err.isEmpty())
-               draw_error(p, err, pp);
+               paint_error(p, pp);
 
 #if DECODETRACE_SHOW_RENDER_TIME
        qDebug() << "Rendering" << base_->name() << "took" << render_time_.elapsed() << "ms";
@@ -340,6 +367,8 @@ void DecodeTrace::paint_fore(QPainter &p, ViewItemPaintParams &pp)
 
                if (r.expand_marker_highlighted)
                        p.setBrush(QApplication::palette().brush(QPalette::Highlight));
+               else if (!r.decode_row->visible())
+                       p.setBrush(ExpandMarkerHiddenColor);
                else if (r.has_hidden_classes)
                        p.setBrush(ExpandMarkerWarnColor);
                else
@@ -366,7 +395,11 @@ void DecodeTrace::paint_fore(QPainter &p, ViewItemPaintParams &pp)
                                        p.drawText(text_rect.translated(dx, dy), f, h);
 
                // Draw the text
-               p.setPen(QApplication::palette().color(QPalette::WindowText));
+               if (!r.decode_row->visible())
+                       p.setPen(ExpandMarkerHiddenColor);
+               else
+                       p.setPen(QApplication::palette().color(QPalette::WindowText));
+
                p.drawText(text_rect, f, h);
 
                y += r.height;
@@ -587,10 +620,17 @@ void DecodeTrace::hover_point_changed(const QPoint &hp)
                r.expand_marker_highlighted = false;
 
        if (hover_row) {
-               int row_y = get_row_y(hover_row);
+               const pair<int, int> extents = v_extents();
+               const int trace_top = get_visual_y() + extents.first;
+               const int trace_btm = get_visual_y() + extents.second;
+
                if ((hp.x() > 0) && (hp.x() < (int)(ArrowSize + 3 + hover_row->title_width)) &&
-                       (hp.y() > (int)(row_y - ArrowSize)) && (hp.y() < (int)(row_y + ArrowSize)))
+                       (hp.y() > trace_top) && (hp.y() < trace_btm)) {
+
                        hover_row->expand_marker_highlighted = true;
+                       show_hidden_rows_ = true;
+                       delayed_hidden_row_hider_.start();
+               }
        }
 
        // Tooltip handling
@@ -637,10 +677,19 @@ void DecodeTrace::mouse_left_press_event(const QMouseEvent* event)
                        continue;
 
                unsigned int y = get_row_y(&r);
-               if ((event->x() > 0) && (event->x() <= (int)(ArrowSize + 3 + r.title_width)) &&
-                       (event->y() > (int)(y - (default_row_height_ / 2))) &&
-                       (event->y() <= (int)(y + (default_row_height_ / 2)))) {
-
+               bool need_anim = true;
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               need_anim &= event->position().x() > 0;
+               need_anim &= event->position().x() <= (int)(ArrowSize + 3 + r.title_width);
+               need_anim &= event->position().y() > (int)(y - (default_row_height_ / 2));
+               need_anim &= event->position().y() <= (int)(y + (default_row_height_ / 2));
+#else
+               need_anim &= event->x() > 0;
+               need_anim &= event->x() <= (int)(ArrowSize + 3 + r.title_width);
+               need_anim &= event->y() > (int)(y - (default_row_height_ / 2));
+               need_anim &= event->y() <= (int)(y + (default_row_height_ / 2));
+#endif
+               if (need_anim) {
                        if (r.expanded) {
                                r.collapsing = true;
                                r.expanded = false;
@@ -668,7 +717,7 @@ void DecodeTrace::mouse_left_press_event(const QMouseEvent* event)
 void DecodeTrace::draw_annotations(deque<const Annotation*>& annotations,
                QPainter &p, const ViewItemPaintParams &pp, int y, const DecodeTraceRow& row)
 {
-       Annotation::Class block_class = 0;
+       uint32_t block_class = 0;
        bool block_class_uniform = true;
        qreal block_start = 0;
        int block_ann_count = 0;
@@ -760,8 +809,8 @@ void DecodeTrace::draw_annotation(const Annotation* a, QPainter &p,
        const double start = a->start_sample() / samples_per_pixel - pixels_offset;
        const double end = a->end_sample() / samples_per_pixel - pixels_offset;
 
-       p.setPen(row.ann_class_dark_color.at(a->ann_class_id()));
-       p.setBrush(row.ann_class_color.at(a->ann_class_id()));
+       p.setPen(a->dark_color());
+       p.setBrush(a->color());
 
        if ((start > (pp.right() + DrawPadding)) || (end < (pp.left() - DrawPadding)))
                return;
@@ -773,7 +822,7 @@ void DecodeTrace::draw_annotation(const Annotation* a, QPainter &p,
 }
 
 void DecodeTrace::draw_annotation_block(qreal start, qreal end,
-       Annotation::Class ann_class, bool use_ann_format, QPainter &p, int y,
+       uint32_t ann_class, bool use_ann_format, QPainter &p, int y,
        const DecodeTraceRow& row) const
 {
        const double top = y + .5 - annotation_height_ / 2;
@@ -784,8 +833,8 @@ void DecodeTrace::draw_annotation_block(qreal start, qreal end,
        // one format that all of these annotations have. Otherwise, we should use
        // a neutral color (i.e. gray)
        if (use_ann_format) {
-               p.setPen(row.ann_class_dark_color.at(ann_class));
-               p.setBrush(QBrush(row.ann_class_color.at(ann_class), Qt::Dense4Pattern));
+               p.setPen(row.decode_row->get_dark_class_color(ann_class));
+               p.setBrush(QBrush(row.decode_row->get_class_color(ann_class), Qt::Dense4Pattern));
        } else {
                p.setPen(QColor(Qt::darkGray));
                p.setBrush(QBrush(Qt::gray, Qt::Dense4Pattern));
@@ -875,28 +924,6 @@ void DecodeTrace::draw_range(const Annotation* a, QPainter &p,
                best_annotation, Qt::ElideRight, rect.width()));
 }
 
-void DecodeTrace::draw_error(QPainter &p, const QString &message,
-       const ViewItemPaintParams &pp)
-{
-       const int y = get_visual_y();
-
-       double samples_per_pixel, pixels_offset;
-       tie(pixels_offset, samples_per_pixel) = get_pixels_offset_samples_per_pixel();
-
-       p.setPen(ErrorBgColor.darker());
-       p.setBrush(ErrorBgColor);
-
-       const QRectF bounding_rect = QRectF(pp.left(), INT_MIN / 2 + y, pp.right(), INT_MAX);
-
-       const QRectF text_rect = p.boundingRect(bounding_rect, Qt::AlignCenter, message);
-       const qreal r = text_rect.height() / 4;
-
-       p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r, Qt::AbsoluteSize);
-
-       p.setPen(Qt::black);
-       p.drawText(text_rect, message);
-}
-
 void DecodeTrace::draw_unresolved_period(QPainter &p, int left, int right) const
 {
        double samples_per_pixel, pixels_offset;
@@ -942,7 +969,7 @@ pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
        const double pixels_offset =
                ((view->offset() - decode_signal_->start_time()) / scale).convert_to<double>();
 
-       double samplerate = decode_signal_->samplerate();
+       double samplerate = decode_signal_->get_samplerate();
 
        // Show sample rate as 1Hz when it is unknown
        if (samplerate == 0.0)
@@ -966,34 +993,6 @@ pair<uint64_t, uint64_t> DecodeTrace::get_view_sample_range(
        return make_pair(start, end);
 }
 
-QColor DecodeTrace::get_row_color(int row_index) const
-{
-       // For each row color, use the base color hue and add an offset that's
-       // not a dividend of 360
-
-       QColor color;
-       const int h = (base_->color().toHsv().hue() + 20 * row_index) % 360;
-       const int s = DECODETRACE_COLOR_SATURATION;
-       const int v = DECODETRACE_COLOR_VALUE;
-       color.setHsl(h, s, v);
-
-       return color;
-}
-
-QColor DecodeTrace::get_annotation_color(QColor row_color, int annotation_index) const
-{
-       // For each row color, use the base color hue and add an offset that's
-       // not a dividend of 360 and not a multiple of the row offset
-
-       QColor color(row_color);
-       const int h = (color.toHsv().hue() + 55 * annotation_index) % 360;
-       const int s = DECODETRACE_COLOR_SATURATION;
-       const int v = DECODETRACE_COLOR_VALUE;
-       color.setHsl(h, s, v);
-
-       return color;
-}
-
 unsigned int DecodeTrace::get_row_y(const DecodeTraceRow* row) const
 {
        assert(row);
@@ -1050,8 +1049,17 @@ const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
        decode_signal_->get_annotation_subset(annotations, r->decode_row,
                current_segment_, sample_range.first, sample_range.second);
 
-       return (annotations.empty()) ?
-               QString() : annotations[0]->annotations()->front();
+       const Annotation* a = annotations.empty() ? nullptr : annotations[0];
+
+       // Create a string of the format "CLASS: VALUE"
+       QString s;
+       if (a) {
+               if (!a->ann_class_description().isEmpty())
+                       s = a->ann_class_description() + ": ";
+               s += a->annotations()->front();
+       }
+
+       return s;
 }
 
 void DecodeTrace::create_decoder_form(int index, shared_ptr<Decoder> &dec,
@@ -1141,7 +1149,7 @@ QComboBox* DecodeTrace::create_channel_selector(QWidget *parent, const DecodeCha
 
        QComboBox *selector = new QComboBox(parent);
 
-       selector->addItem("-", qVariantFromValue((void*)nullptr));
+       selector->addItem("-", QVariant::fromValue((void*)nullptr));
 
        if (!ch->assigned_signal)
                selector->setCurrentIndex(0);
@@ -1149,10 +1157,9 @@ QComboBox* DecodeTrace::create_channel_selector(QWidget *parent, const DecodeCha
        for (const shared_ptr<data::SignalBase> &b : sig_list) {
                assert(b);
                if (b->logic_data() && b->enabled()) {
-                       selector->addItem(b->name(),
-                               qVariantFromValue((void*)b.get()));
+                       selector->addItem(b->name(), QVariant::fromValue(b));
 
-                       if (ch->assigned_signal == b.get())
+                       if (ch->assigned_signal == b)
                                selector->setCurrentIndex(selector->count() - 1);
                }
        }
@@ -1165,9 +1172,9 @@ QComboBox* DecodeTrace::create_channel_selector_init_state(QWidget *parent,
 {
        QComboBox *selector = new QComboBox(parent);
 
-       selector->addItem("0", qVariantFromValue((int)SRD_INITIAL_PIN_LOW));
-       selector->addItem("1", qVariantFromValue((int)SRD_INITIAL_PIN_HIGH));
-       selector->addItem("X", qVariantFromValue((int)SRD_INITIAL_PIN_SAME_AS_SAMPLE0));
+       selector->addItem("0", QVariant::fromValue((int)SRD_INITIAL_PIN_LOW));
+       selector->addItem("1", QVariant::fromValue((int)SRD_INITIAL_PIN_HIGH));
+       selector->addItem("X", QVariant::fromValue((int)SRD_INITIAL_PIN_SAME_AS_SAMPLE0));
 
        selector->setCurrentIndex(ch->initial_pin_state);
 
@@ -1191,30 +1198,54 @@ void DecodeTrace::export_annotations(deque<const Annotation*>& annotations) cons
        const QString quote = format.contains("%q") ? "\"" : "";
        format = format.remove("%q");
 
+       const bool has_sample_range   = format.contains("%s");
+       const bool has_row_name       = format.contains("%r");
+       const bool has_dec_name       = format.contains("%d");
+       const bool has_class_name     = format.contains("%c");
+       const bool has_first_ann_text = format.contains("%1");
+       const bool has_all_ann_text   = format.contains("%a");
+
        QFile file(file_name);
        if (file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
                QTextStream out_stream(&file);
 
                for (const Annotation* ann : annotations) {
-                       const QString sample_range = QString("%1-%2") \
-                               .arg(QString::number(ann->start_sample()), QString::number(ann->end_sample()));
+                       QString out_text = format;
 
-                       const QString row_name = quote + ann->row()->description() + quote;
+                       if (has_sample_range) {
+                               const QString sample_range = QString("%1-%2") \
+                                       .arg(QString::number(ann->start_sample()), QString::number(ann->end_sample()));
+                               out_text = out_text.replace("%s", sample_range);
+                       }
 
-                       QString all_ann_text;
-                       for (const QString &s : *(ann->annotations()))
-                               all_ann_text = all_ann_text + quote + s + quote + ",";
-                       all_ann_text.chop(1);
+                       if (has_dec_name)
+                               out_text = out_text.replace("%d",
+                                       quote + QString::fromUtf8(ann->row()->decoder()->name()) + quote);
 
-                       const QString first_ann_text = quote + ann->annotations()->front() + quote;
+                       if (has_row_name) {
+                               const QString row_name = quote + ann->row()->description() + quote;
+                               out_text = out_text.replace("%r", row_name);
+                       }
+
+                       if (has_class_name) {
+                               const QString class_name = quote + ann->ann_class_name() + quote;
+                               out_text = out_text.replace("%c", class_name);
+                       }
+
+                       if (has_first_ann_text) {
+                               const QString first_ann_text = quote + ann->annotations()->front() + quote;
+                               out_text = out_text.replace("%1", first_ann_text);
+                       }
+
+                       if (has_all_ann_text) {
+                               QString all_ann_text;
+                               for (const QString &s : *(ann->annotations()))
+                                       all_ann_text = all_ann_text + quote + s + quote + ",";
+                               all_ann_text.chop(1);
+
+                               out_text = out_text.replace("%a", all_ann_text);
+                       }
 
-                       QString out_text = format;
-                       out_text = out_text.replace("%s", sample_range);
-                       out_text = out_text.replace("%d",
-                               quote + QString::fromUtf8(ann->row()->decoder()->name()) + quote);
-                       out_text = out_text.replace("%r", row_name);
-                       out_text = out_text.replace("%1", first_ann_text);
-                       out_text = out_text.replace("%a", all_ann_text);
                        out_stream << out_text << '\n';
                }
 
@@ -1237,6 +1268,19 @@ void DecodeTrace::initialize_row_widgets(DecodeTraceRow* r, unsigned int row_id)
        QPalette header_palette = owner_->view()->palette();
        QPalette selector_palette = owner_->view()->palette();
 
+#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
+       if (GlobalSettings::current_theme_is_dark()) {
+               header_palette.setColor(QPalette::Window,
+                       QColor(255, 255, 255, ExpansionAreaHeaderAlpha));
+               selector_palette.setColor(QPalette::Window,
+                       QColor(255, 255, 255, ExpansionAreaAlpha));
+       } else {
+               header_palette.setColor(QPalette::Window,
+                       QColor(0, 0, 0, ExpansionAreaHeaderAlpha));
+               selector_palette.setColor(QPalette::Window,
+                       QColor(0, 0, 0, ExpansionAreaAlpha));
+       }
+#else
        if (GlobalSettings::current_theme_is_dark()) {
                header_palette.setColor(QPalette::Background,
                        QColor(255, 255, 255, ExpansionAreaHeaderAlpha));
@@ -1248,6 +1292,7 @@ void DecodeTrace::initialize_row_widgets(DecodeTraceRow* r, unsigned int row_id)
                selector_palette.setColor(QPalette::Background,
                        QColor(0, 0, 0, ExpansionAreaAlpha));
        }
+#endif
 
        const int w = m.boundingRect(r->decode_row->title()).width() + RowTitleMargin;
        r->title_width = w;
@@ -1274,6 +1319,7 @@ void DecodeTrace::initialize_row_widgets(DecodeTraceRow* r, unsigned int row_id)
 
        // Add widgets inside the header container
        QCheckBox* cb = new QCheckBox();
+       r->row_visibility_checkbox = cb;
        header_container_layout->addWidget(cb);
        cb->setText(tr("Show this row"));
        cb->setChecked(r->decode_row->visible());
@@ -1282,8 +1328,6 @@ void DecodeTrace::initialize_row_widgets(DecodeTraceRow* r, unsigned int row_id)
        connect(cb, SIGNAL(stateChanged(int)),
                &row_show_hide_mapper_, SLOT(map()));
 
-       cb->setEnabled(false);
-
        QPushButton* btn = new QPushButton();
        header_container_layout->addWidget(btn);
        btn->setFlat(true);
@@ -1316,11 +1360,11 @@ void DecodeTrace::initialize_row_widgets(DecodeTraceRow* r, unsigned int row_id)
        for (const AnnotationClass* ann_class : ann_classes) {
                cb = new QCheckBox();
                cb->setText(tr(ann_class->description));
-               cb->setChecked(ann_class->visible);
+               cb->setChecked(ann_class->visible());
 
                int dim = ViewItemPaintParams::text_height() - 2;
                QPixmap pixmap(dim, dim);
-               pixmap.fill(r->ann_class_color[ann_class->id]);
+               pixmap.fill(r->decode_row->get_class_color(ann_class->id));
                cb->setIcon(pixmap);
 
                r->selector_container->layout()->addWidget(cb);
@@ -1337,6 +1381,9 @@ void DecodeTrace::initialize_row_widgets(DecodeTraceRow* r, unsigned int row_id)
 
 void DecodeTrace::update_rows()
 {
+       if (!owner_)
+               return;
+
        lock_guard<mutex> lock(row_modification_mutex_);
 
        for (DecodeTraceRow& r : rows_)
@@ -1353,6 +1400,7 @@ void DecodeTrace::update_rows()
                        // Row doesn't exist yet, create and append it
                        DecodeTraceRow nr;
                        nr.decode_row = decode_row;
+                       nr.decode_row->set_base_color(base_->color());
                        nr.height = default_row_height_;
                        nr.expanded_height = default_row_height_;
                        nr.currently_visible = false;
@@ -1366,16 +1414,6 @@ void DecodeTrace::update_rows()
                        nr.header_container = new QWidget(nr.container);
                        nr.selector_container = new QWidget(nr.container);
 
-                       nr.row_color = get_row_color(decode_row->index());
-
-                       vector<AnnotationClass*> ann_classes = decode_row->ann_classes();
-                       for (const AnnotationClass* ann_class : ann_classes) {
-                               nr.ann_class_color[ann_class->id] =
-                                       get_annotation_color(nr.row_color, ann_class->id);
-                               nr.ann_class_dark_color[ann_class->id] =
-                                       nr.ann_class_color[ann_class->id].darker();
-                       }
-
                        rows_.push_back(nr);
                        r = &rows_.back();
                        initialize_row_widgets(r, row_id);
@@ -1386,6 +1424,10 @@ void DecodeTrace::update_rows()
                row_id++;
        }
 
+       // If there's only one row, it must not be hidden or else it can't be un-hidden
+       if (row_id == 1)
+               rows_.front().row_visibility_checkbox->setEnabled(false);
+
        // Remove any rows that no longer exist, obeying that iterators are invalidated
        bool any_exists;
        do {
@@ -1393,6 +1435,8 @@ void DecodeTrace::update_rows()
 
                for (unsigned int i = 0; i < rows_.size(); i++)
                        if (!rows_[i].exists) {
+                               delete rows_[i].row_visibility_checkbox;
+
                                for (QCheckBox* cb : rows_[i].selectors)
                                        delete cb;
 
@@ -1420,8 +1464,6 @@ void DecodeTrace::set_row_expanded(DecodeTraceRow* r)
 
        r->container->resize(owner_->view()->viewport()->width() - r->container->pos().x(),
                r->height - 2 * default_row_height_);
-
-       max_visible_rows_ = 0;
 }
 
 void DecodeTrace::set_row_collapsed(DecodeTraceRow* r)
@@ -1434,8 +1476,6 @@ void DecodeTrace::set_row_collapsed(DecodeTraceRow* r)
 
        r->container->resize(owner_->view()->viewport()->width() - r->container->pos().x(),
                r->height - 2 * default_row_height_);
-
-       max_visible_rows_ = 0;
 }
 
 void DecodeTrace::update_expanded_rows()
@@ -1465,10 +1505,17 @@ void DecodeTrace::on_setting_changed(const QString &key, const QVariant &value)
 {
        Trace::on_setting_changed(key, value);
 
-       if (key == GlobalSettings::Key_Dec_AlwaysShowAllRows) {
-               max_visible_rows_ = 0;
+       if (key == GlobalSettings::Key_Dec_AlwaysShowAllRows)
                always_show_all_rows_ = value.toBool();
-       }
+}
+
+void DecodeTrace::on_color_changed(const QColor &color)
+{
+       for (DecodeTraceRow& r : rows_)
+               r.decode_row->set_base_color(color);
+
+       if (owner_)
+               owner_->row_item_appearance_changed(false, true);
 }
 
 void DecodeTrace::on_new_annotations()
@@ -1485,7 +1532,6 @@ void DecodeTrace::on_delayed_trace_update()
 
 void DecodeTrace::on_decode_reset()
 {
-       max_visible_rows_ = 0;
        update_rows();
 
        if (owner_)
@@ -1516,8 +1562,8 @@ void DecodeTrace::on_channel_selected(int)
        QComboBox *cb = qobject_cast<QComboBox*>(QObject::sender());
 
        // Determine signal that was selected
-       const data::SignalBase *signal =
-               (data::SignalBase*)cb->itemData(cb->currentIndex()).value<void*>();
+       shared_ptr<data::SignalBase> signal =
+               cb->itemData(cb->currentIndex()).value<shared_ptr<data::SignalBase>>();
 
        // Determine decode channel ID this combo box is the channel selector for
        const uint16_t id = channel_id_map_.at(cb);
@@ -1557,8 +1603,6 @@ void DecodeTrace::on_delete_decoder(int index)
        decode_signal_->remove_decoder(index);
        update_rows();
 
-       // Force re-calculation of the trace height
-       max_visible_rows_ = 0;
        owner_->extents_changed(false, true);
 
        create_popup_form();
@@ -1571,11 +1615,8 @@ void DecodeTrace::on_show_hide_decoder(int index)
        assert(index < (int)decoder_forms_.size());
        decoder_forms_[index]->set_decoder_visible(state);
 
-       if (!state) {
-               // Force re-calculation of the trace height, see paint_mid()
-               max_visible_rows_ = 0;
+       if (!state)
                owner_->extents_changed(false, true);
-       }
 
        owner_->row_item_appearance_changed(false, true);
 }
@@ -1585,22 +1626,26 @@ void DecodeTrace::on_show_hide_row(int row_id)
        if (row_id >= (int)rows_.size())
                return;
 
-       set_row_collapsed(&rows_[row_id]);
        rows_[row_id].decode_row->set_visible(!rows_[row_id].decode_row->visible());
 
-       // Force re-calculation of the trace height, see paint_mid()
-       max_visible_rows_ = 0;
+       if (!rows_[row_id].decode_row->visible())
+               set_row_collapsed(&rows_[row_id]);
+
        owner_->extents_changed(false, true);
        owner_->row_item_appearance_changed(false, true);
 }
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+void DecodeTrace::on_show_hide_class(QObject* sender)
+#else
 void DecodeTrace::on_show_hide_class(QWidget* sender)
+#endif
 {
        void* ann_class_ptr = sender->property("ann_class_ptr").value<void*>();
        assert(ann_class_ptr);
        AnnotationClass* ann_class = (AnnotationClass*)ann_class_ptr;
 
-       ann_class->visible = !ann_class->visible;
+       ann_class->set_visible(!ann_class->visible());
 
        void* row_ptr = sender->property("decode_trace_row_ptr").value<void*>();
        assert(row_ptr);
@@ -1771,7 +1816,7 @@ void DecodeTrace::on_animation_timer()
                if (r.expanding) {
                        if (r.height < r.expanded_height) {
                                r.anim_height += height_delta / (float)AnimationDurationInTicks;
-                               r.height = r.anim_height;
+                               r.height = min((int)r.anim_height, (int)r.expanded_height);
                                r.anim_shape += ArrowSize / (float)AnimationDurationInTicks;
                                animation_finished = false;
                        } else
@@ -1781,7 +1826,7 @@ void DecodeTrace::on_animation_timer()
                if (r.collapsing) {
                        if (r.height > default_row_height_) {
                                r.anim_height -= height_delta / (float)AnimationDurationInTicks;
-                               r.height = r.anim_height;
+                               r.height = max((int)r.anim_height, (int)0);
                                r.anim_shape -= ArrowSize / (float)AnimationDurationInTicks;
                                animation_finished = false;
                        } else
@@ -1804,6 +1849,23 @@ void DecodeTrace::on_animation_timer()
        owner_->row_item_appearance_changed(false, true);
 }
 
+void DecodeTrace::on_hide_hidden_rows()
+{
+       // Make all hidden traces invisible again unless the user is hovering over a row name
+       bool any_highlighted = false;
+
+       for (DecodeTraceRow& r : rows_)
+               if (r.expand_marker_highlighted)
+                       any_highlighted = true;
+
+       if (!any_highlighted) {
+               show_hidden_rows_ = false;
+
+               owner_->extents_changed(false, true);
+               owner_->row_item_appearance_changed(false, true);
+       }
+}
+
 } // namespace trace
 } // namespace views
 } // namespace pv
index f8976cb7b2da84a22c0968715972e67affbfd3c1..0ba52b357c8ed80e108b25559b5aa2559716c5cc 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_DECODETRACE_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_DECODETRACE_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_DECODETRACE_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_DECODETRACE_HPP
 
 #include <config.h>
 #include "trace.hpp"
@@ -29,6 +29,7 @@
 #include <vector>
 
 #include <QColor>
+#include <QComboBox>
 #include <QCheckBox>
 #include <QElapsedTimer>
 #include <QPolygon>
@@ -97,11 +98,8 @@ struct DecodeTraceRow {
        ContainerWidget* container;
        QWidget* header_container;
        QWidget* selector_container;
+       QCheckBox* row_visibility_checkbox;
        vector<QCheckBox*> selectors;
-
-       QColor row_color;
-       map<uint32_t, QColor> ann_class_color;
-       map<uint32_t, QColor> ann_class_dark_color;
 };
 
 class ContainerWidget : public QWidget
@@ -122,9 +120,9 @@ class DecodeTrace : public Trace
        Q_OBJECT
 
 private:
-       static const QColor ErrorBgColor;
        static const QColor NoDecodeColor;
        static const QColor ExpandMarkerWarnColor;
+       static const QColor ExpandMarkerHiddenColor;
        static const uint8_t ExpansionAreaHeaderAlpha;
        static const uint8_t ExpansionAreaAlpha;
 
@@ -134,7 +132,8 @@ private:
        static const int DrawPadding;
 
        static const int MaxTraceUpdateRate;
-       static const unsigned int AnimationDurationInTicks;
+       static const int AnimationDurationInTicks;
+       static const int HiddenRowHideDelay;
 
 public:
        DecodeTrace(pv::Session &session, shared_ptr<SignalBase> signalbase,
@@ -146,6 +145,12 @@ public:
 
        shared_ptr<SignalBase> base() const;
 
+       /**
+        * Sets the owner this trace in the view trace hierachy.
+        * @param The new owner of the trace.
+        */
+       virtual void set_owner(TraceTreeItemOwner *owner);
+
        /**
         * Computes the vertical extents of the contents of this row item.
         * @return A pair containing the minimum and maximum y-values.
@@ -192,7 +197,7 @@ private:
        void draw_annotation(const Annotation* a, QPainter &p,
                const ViewItemPaintParams &pp, int y, const DecodeTraceRow& row) const;
 
-       void draw_annotation_block(qreal start, qreal end, Annotation::Class ann_class,
+       void draw_annotation_block(qreal start, qreal end, uint32_t ann_class,
                bool use_ann_format, QPainter &p, int y, const DecodeTraceRow& row) const;
 
        void draw_instant(const Annotation* a, QPainter &p, qreal x, int y) const;
@@ -215,9 +220,6 @@ private:
         */
        pair<uint64_t, uint64_t> get_view_sample_range(int x_start, int x_end) const;
 
-       QColor get_row_color(int row_index) const;
-       QColor get_annotation_color(QColor row_color, int annotation_index) const;
-
        unsigned int get_row_y(const DecodeTraceRow* row) const;
 
        DecodeTraceRow* get_row_at_point(const QPoint &point);
@@ -254,6 +256,8 @@ private:
 private Q_SLOTS:
        void on_setting_changed(const QString &key, const QVariant &value);
 
+       void on_color_changed(const QColor &color);
+
        void on_new_annotations();
        void on_delayed_trace_update();
        void on_decode_reset();
@@ -274,7 +278,11 @@ private Q_SLOTS:
 
        void on_show_hide_decoder(int index);
        void on_show_hide_row(int row_id);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       void on_show_hide_class(QObject* sender);
+#else
        void on_show_hide_class(QWidget* sender);
+#endif
        void on_show_all_classes();
        void on_hide_all_classes();
        void on_row_container_resized(QWidget* sender);
@@ -289,6 +297,7 @@ private Q_SLOTS:
        void on_export_all_rows_from_here();
 
        void on_animation_timer();
+       void on_hide_hidden_rows();
 
 private:
        pv::Session &session_;
@@ -308,15 +317,15 @@ private:
        QPushButton* stack_button_;
 
        unsigned int default_row_height_, annotation_height_;
-       unsigned int visible_rows_, max_visible_rows_;
+       unsigned int visible_rows_;
 
        int min_useful_label_width_;
-       bool always_show_all_rows_;
+       bool always_show_all_rows_, show_hidden_rows_;
 
        QSignalMapper delete_mapper_, show_hide_mapper_;
        QSignalMapper row_show_hide_mapper_, class_show_hide_mapper_;
 
-       QTimer delayed_trace_updater_, animation_timer_;
+       QTimer delayed_trace_updater_, animation_timer_, delayed_hidden_row_hider_;
 
        QPolygon default_marker_shape_;
 
@@ -329,4 +338,4 @@ private:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_DECODETRACE_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_DECODETRACE_HPP
index ea3cd609854c813783ffe6e406143b887915693a..c356b15bf62bba31341b0f22f8a24a2ed5813dd1 100644 (file)
@@ -57,7 +57,11 @@ bool Flag::enabled() const
        return true;
 }
 
-QString Flag::get_text() const
+/**
+ * Returns the text used to display this flag item. This is not necessarily the
+ * name that the user has given it.
+ */
+QString Flag::get_display_text() const
 {
        QString s;
 
@@ -73,6 +77,20 @@ QString Flag::get_text() const
        return s;
 }
 
+/**
+ * Returns the text of this flag item, i.e. the user-editable name.
+ */
+QString Flag::get_text() const
+{
+       return text_;
+}
+
+void Flag::set_text(const QString &text)
+{
+       text_ = text;
+       view_.time_item_appearance_changed(true, false);
+}
+
 QRectF Flag::label_rect(const QRectF &rect) const
 {
        QRectF r;
@@ -86,7 +104,7 @@ QRectF Flag::label_rect(const QRectF &rect) const
                const float x = get_x();
 
                QFontMetrics m(QApplication::font());
-               QSize text_size = m.boundingRect(get_text()).size();
+               QSize text_size = m.boundingRect(get_display_text()).size();
 
                const QSizeF label_size(
                        text_size.width() + LabelPadding.width() * 2,
@@ -158,8 +176,7 @@ void Flag::on_delete()
 
 void Flag::on_text_changed(const QString &text)
 {
-       text_ = text;
-       view_.time_item_appearance_changed(true, false);
+       set_text(text);
 }
 
 } // namespace trace
index 5edf90b01ef63997595415d32e0fc6faea35c0d3..eb4bf87c6008d9f84f737e1dd4a0086812ce47e7 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_FLAG_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_FLAG_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_FLAG_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_FLAG_HPP
 
 #include <memory>
 
@@ -63,10 +63,21 @@ public:
        virtual bool enabled() const override;
 
        /**
-        * Gets the text to show in the marker.
+        * Gets the current text to show in the marker - this may be dynamic.
+        */
+       virtual QString get_display_text() const override;
+
+       /**
+        * Gets the default text used to show the marker - e.g. the user-editable
+        * name.
         */
        virtual QString get_text() const override;
 
+       /**
+        * Sets the text to show in the marker.
+        */
+       virtual void set_text(const QString &text) override;
+
        virtual pv::widgets::Popup* create_popup(QWidget *parent) override;
 
        virtual QMenu* create_header_context_menu(QWidget *parent) override;
@@ -88,4 +99,4 @@ private:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_FLAG_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_FLAG_HPP
index 5d2ba4b7ce1c9e794a5ffa8f27cf1c8b4b02f04c..32b7e6128bddb4f4f82a88c241843fe87c32bec0 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_HEADER_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_HEADER_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_HEADER_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_HEADER_HPP
 
 #include <list>
 #include <memory>
@@ -94,4 +94,4 @@ private Q_SLOTS:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_HEADER_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_HEADER_HPP
index a3fd7a0bbdcf9dc18c3561673497414c65a3b1b5..732d46d2d9e82e86e933a71b6c41760951ae4227 100644 (file)
@@ -70,19 +70,6 @@ const QColor LogicSignal::HighColor(0x00, 0xC0, 0x00);
 const QColor LogicSignal::LowColor(0xC0, 0x00, 0x00);
 const QColor LogicSignal::SamplingPointColor(0x77, 0x77, 0x77);
 
-const QColor LogicSignal::SignalColors[10] = {
-       QColor(0x16, 0x19, 0x1A),       // Black
-       QColor(0x8F, 0x52, 0x02),       // Brown
-       QColor(0xCC, 0x00, 0x00),       // Red
-       QColor(0xF5, 0x79, 0x00),       // Orange
-       QColor(0xED, 0xD4, 0x00),       // Yellow
-       QColor(0x73, 0xD2, 0x16),       // Green
-       QColor(0x34, 0x65, 0xA4),       // Blue
-       QColor(0x75, 0x50, 0x7B),       // Violet
-       QColor(0x88, 0x8A, 0x85),       // Grey
-       QColor(0xEE, 0xEE, 0xEC),       // White
-};
-
 QColor LogicSignal::TriggerMarkerBackgroundColor = QColor(0xED, 0xD4, 0x00);
 const int LogicSignal::TriggerMarkerPadding = 2;
 const char* LogicSignal::TriggerMarkerIcons[8] = {
@@ -99,12 +86,8 @@ const char* LogicSignal::TriggerMarkerIcons[8] = {
 QCache<QString, const QIcon> LogicSignal::icon_cache_;
 QCache<QString, const QPixmap> LogicSignal::pixmap_cache_;
 
-LogicSignal::LogicSignal(
-       pv::Session &session,
-       shared_ptr<devices::Device> device,
-       shared_ptr<data::SignalBase> base) :
+LogicSignal::LogicSignal(pv::Session &session, shared_ptr<data::SignalBase> base) :
        Signal(session, base),
-       device_(device),
        trigger_types_(get_trigger_types()),
        trigger_none_(nullptr),
        trigger_rising_(nullptr),
@@ -113,10 +96,6 @@ LogicSignal::LogicSignal(
        trigger_low_(nullptr),
        trigger_change_(nullptr)
 {
-       shared_ptr<Trigger> trigger;
-
-       base_->set_color(SignalColors[base->index() % countof(SignalColors)]);
-
        GlobalSettings settings;
        signal_height_ = settings.value(GlobalSettings::Key_View_DefaultLogicHeight).toInt();
        show_sampling_points_ =
@@ -126,26 +105,18 @@ LogicSignal::LogicSignal(
        high_fill_color_ = QColor::fromRgba(settings.value(
                GlobalSettings::Key_View_FillSignalHighAreaColor).value<uint32_t>());
 
+       update_logic_level_offsets();
+
        /* Populate this channel's trigger setting with whatever we
         * find in the current session trigger, if anything. */
        trigger_match_ = nullptr;
-       if ((trigger = session_.session()->trigger()))
+       if (shared_ptr<Trigger> trigger = session_.session()->trigger())
                for (auto stage : trigger->stages())
                        for (auto match : stage->matches())
                                if (match->channel() == base_->channel())
                                        trigger_match_ = match->type();
 }
 
-shared_ptr<pv::data::SignalData> LogicSignal::data() const
-{
-       return base_->logic_data();
-}
-
-shared_ptr<pv::data::Logic> LogicSignal::logic_data() const
-{
-       return base_->logic_data();
-}
-
 std::map<QString, QVariant> LogicSignal::save_settings() const
 {
        std::map<QString, QVariant> result;
@@ -163,6 +134,8 @@ void LogicSignal::restore_settings(std::map<QString, QVariant> settings)
                signal_height_ = settings["trace_height"].toInt();
 
                if ((signal_height_ != old_height) && owner_) {
+                       update_logic_level_offsets();
+
                        // Call order is important, otherwise the lazy event handler won't work
                        owner_->extents_changed(false, true);
                        owner_->row_item_appearance_changed(false, true);
@@ -191,8 +164,9 @@ void LogicSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp)
        if (!base_->enabled())
                return;
 
-       const float low_offset = y + 0.5f;
-       const float high_offset = low_offset - signal_height_;
+       const float low_offset = y + low_level_offset_;
+       const float high_offset = y + high_level_offset_;
+       const float fill_height = low_offset - high_offset;
 
        shared_ptr<LogicSegment> segment = get_logic_segment_to_paint();
        if (!segment || (segment->get_sample_count() == 0))
@@ -218,7 +192,7 @@ void LogicSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp)
                (int64_t)0), last_sample);
 
        segment->get_subsampled_edges(edges, start_sample, end_sample,
-               samples_per_pixel / Oversampling, base_->index());
+               samples_per_pixel / Oversampling, base_->logic_bit_index());
        assert(edges.size() >= 2);
 
        const float first_sample_x =
@@ -265,8 +239,7 @@ void LogicSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp)
                        if (rising_edge_seen) {
                                const int width = x - rising_edge_x;
                                if (width > 0)
-                                       high_rects.emplace_back(rising_edge_x, high_offset,
-                                               width, signal_height_);
+                                       high_rects.emplace_back(rising_edge_x, high_offset, width, fill_height);
                                rising_edge_seen = false;
                        }
 
@@ -428,12 +401,11 @@ shared_ptr<pv::data::LogicSegment> LogicSignal::get_logic_segment_to_paint() con
                base_->logic_data()->logic_segments();
 
        if (!segments.empty()) {
-               if (segment_display_mode_ == ShowLastSegmentOnly) {
+               if (segment_display_mode_ == ShowLastSegmentOnly)
                        segment = segments.back();
-               }
 
-       if ((segment_display_mode_ == ShowSingleSegmentOnly) ||
-               (segment_display_mode_ == ShowLastCompleteSegmentOnly)) {
+               if ((segment_display_mode_ == ShowSingleSegmentOnly) ||
+                       (segment_display_mode_ == ShowLastCompleteSegmentOnly)) {
                        try {
                                segment = segments.at(current_segment_);
                        } catch (out_of_range&) {
@@ -481,10 +453,10 @@ void LogicSignal::init_trigger_actions(QWidget *parent)
 const vector<int32_t> LogicSignal::get_trigger_types() const
 {
        // We may not be associated with a device
-       if (!device_)
+       if (!session_.device())
                return vector<int32_t>();
 
-       const auto sr_dev = device_->device();
+       const auto sr_dev = session_.device()->device();
        if (sr_dev->config_check(ConfigKey::TRIGGER_MATCH, Capability::LIST)) {
                const Glib::VariantContainerBase gvar =
                        sr_dev->config_list(ConfigKey::TRIGGER_MATCH);
@@ -652,6 +624,12 @@ const QPixmap* LogicSignal::get_pixmap(const char *path)
        return pixmap_cache_.take(path);
 }
 
+void LogicSignal::update_logic_level_offsets()
+{
+       low_level_offset_ = 0.5f;
+       high_level_offset_ = low_level_offset_ - signal_height_;
+}
+
 void LogicSignal::on_setting_changed(const QString &key, const QVariant &value)
 {
        Signal::on_setting_changed(key, value);
@@ -684,6 +662,8 @@ void LogicSignal::on_signal_height_changed(int height)
        signal_height_ = height;
 
        if (owner_) {
+               update_logic_level_offsets();
+
                // Call order is important, otherwise the lazy event handler won't work
                owner_->extents_changed(false, true);
                owner_->row_item_appearance_changed(false, true);
index b769ec55914cfea4af1d6a685e3a53a96528fe96..eee6b9c4ce081f4b74d6ccf30ffe206e6aa6965b 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_LOGICSIGNAL_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_LOGICSIGNAL_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_LOGICSIGNAL_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_LOGICSIGNAL_HPP
 
 #include <QCache>
 #include <QColor>
@@ -65,22 +65,14 @@ public:
        static const QColor LowColor;
        static const QColor SamplingPointColor;
 
-       static const QColor SignalColors[10];
-
        static QColor TriggerMarkerBackgroundColor;
        static const int TriggerMarkerPadding;
        static const char* TriggerMarkerIcons[8];
 
-       LogicSignal(pv::Session &session,
-               shared_ptr<devices::Device> device,
-               shared_ptr<data::SignalBase> base);
+       LogicSignal(pv::Session &session, shared_ptr<data::SignalBase> base);
 
        virtual ~LogicSignal() = default;
 
-       shared_ptr<pv::data::SignalData> data() const;
-
-       shared_ptr<pv::data::Logic> logic_data() const;
-
        virtual std::map<QString, QVariant> save_settings() const;
        virtual void restore_settings(std::map<QString, QVariant> settings);
 
@@ -88,14 +80,14 @@ public:
         * Computes the vertical extents of the contents of this row item.
         * @return A pair containing the minimum and maximum y-values.
         */
-       pair<int, int> v_extents() const;
+       virtual pair<int, int> v_extents() const;
 
        /**
         * Paints the mid-layer of the signal with a QPainter
         * @param p the QPainter to paint into.
         * @param pp the painting parameters object to paint with..
         */
-       void paint_mid(QPainter &p, ViewItemPaintParams &pp);
+       virtual void paint_mid(QPainter &p, ViewItemPaintParams &pp);
 
        /**
         * Paints the foreground layer of the signal with a QPainter
@@ -113,7 +105,7 @@ public:
         */
        virtual vector<data::LogicSegment::EdgePair> get_nearest_level_changes(uint64_t sample_pos);
 
-private:
+protected:
        void paint_caps(QPainter &p, QLineF *const lines,
                vector< pair<int64_t, bool> > &edges,
                bool level, double samples_per_pixel, double pixels_offset,
@@ -133,19 +125,19 @@ private:
        static const QIcon* get_icon(const char *path);
        static const QPixmap* get_pixmap(const char *path);
 
-private Q_SLOTS:
+       virtual void update_logic_level_offsets();
+
+protected Q_SLOTS:
        void on_setting_changed(const QString &key, const QVariant &value);
 
        void on_trigger();
 
        void on_signal_height_changed(int height);
 
-private:
-       int signal_height_;
+protected:
        QColor high_fill_color_;
        bool show_sampling_points_, fill_high_areas_;
-
-       shared_ptr<pv::devices::Device> device_;
+       float high_level_offset_, low_level_offset_;  // y offsets relative to trace
 
        QSpinBox *signal_height_sb_;
 
@@ -161,10 +153,15 @@ private:
 
        static QCache<QString, const QIcon> icon_cache_;
        static QCache<QString, const QPixmap> pixmap_cache_;
+
+       // ---------------------------------------------------------------------------
+       // Note: Make sure to update save_settings() and restore_settings() when
+       //       adding a trace-configurable variable here
+       int signal_height_;
 };
 
 } // namespace trace
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_LOGICSIGNAL_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_LOGICSIGNAL_HPP
index 537ffe5ef298ad29b1cc6dd00a9ab5bab1992fd5..347b41edfe7bcfb6afef775b58aa96568762fdb1 100644 (file)
@@ -48,10 +48,10 @@ void MarginWidget::show_popup(const shared_ptr<ViewItem> &item)
 {
        pv::widgets::Popup *const p = item->create_popup(this);
 
-       connect(p, SIGNAL(closed()), this, SLOT(on_popup_closed()));
-
-       if (p)
+       if (p) {
+               connect(p, SIGNAL(closed()), this, SLOT(on_popup_closed()));
                p->show();
+       }
 }
 
 void MarginWidget::contextMenuEvent(QContextMenuEvent *event)
index ef5e6350fe1cd1e4b1f6b9ba0ddfd01e4dc7ce6f..bedf8a92bea06051e69eea31d19d11a840894b10 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_MARGINWIDGET_HPP
-#define PULSEVIEW_PV_MARGINWIDGET_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_MARGINWIDGET_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_MARGINWIDGET_HPP
 
 #include <memory>
 
@@ -73,4 +73,4 @@ protected Q_SLOTS:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_MARGINWIDGET_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_MARGINWIDGET_HPP
diff --git a/pv/views/trace/mathsignal.cpp b/pv/views/trace/mathsignal.cpp
new file mode 100644 (file)
index 0000000..665b1a4
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <extdef.h>
+
+#include <QComboBox>
+#include <QDebug>
+#include <QDialogButtonBox>
+#include <QFormLayout>
+#include <QGridLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QString>
+#include <QTabWidget>
+#include <QVBoxLayout>
+
+#include "mathsignal.hpp"
+
+#include "pv/data/signalbase.hpp"
+
+using pv::data::SignalBase;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+#define MATHSIGNAL_INPUT_TIMEOUT (2000)
+
+const vector< pair<string, string> > MathEditDialog::Examples = {
+       {"Product of signals named A1 and A2:",
+        "A1 * A2"},
+       {"Create a sine wave with f=1MHz:",
+        "sin(1e6 * 2 * pi * t)"},
+       {"Average the past 4 samples of A2:",
+        "var v := 0;\n" \
+        "var n := 4;\n\n" \
+        "for (var i := 0; i < n; i += 1) {\n" \
+        "\tv += sample('A2', s - i) / n;\n" \
+        "}"},
+       {"Create a <a href=https://en.wikipedia.org/wiki/Chirp#Linear>frequency sweep</a> from 1Hz to 1MHz over 10 seconds:",
+        "var f_min := 1;\n" \
+        "var f_max := 1e6;\n" \
+        "var duration := 10;\n" \
+        "var gradient := (f_max - f_min) / duration;\n" \
+        "// phase is the antiderivative of (gradient * t + f_min) dt\n" \
+        "var phase := gradient * (t ^ 2 / 2) + f_min * t;\n" \
+        "sin(2 * pi * phase)"},
+       {"Single-pole low-pass IIR filter, see " \
+        "<a href=https://tomroelandts.com/articles/low-pass-single-pole-iir-filter>this</a> " \
+        "and <a href=https://dsp.stackexchange.com/questions/34969/cutoff-frequency-of-a-first-order-recursive-filter>this</a> article:",
+        "// -3db point is set to 10% of the sampling frequency\n" \
+        "var f_c := 0.1;\n" \
+        "var omega_c := 2 * pi * f_c;\n" \
+        "var b := 2 - cos(omega_c) - sqrt(((2 - cos(omega_c)) ^ 2) - 1);\n" \
+        "var a := 1 - b;\n\n" \
+        "// formula is y[n] = ax[n] + (1 - a) * y[n - 1]\n" \
+        "// x[n] becomes the input signal, here A4\n" \
+        "// y[n - 1] becomes sample('Math1', s - 1)\n" \
+        "a * A4 + (1 - a) * sample('Math1', s - 1)"}
+};
+
+
+MathEditDialog::MathEditDialog(pv::Session &session,
+       shared_ptr<pv::data::MathSignal> math_signal, QWidget *parent) :
+       QDialog(parent),
+       session_(session),
+       math_signal_(math_signal),
+       old_expression_(math_signal->get_expression()),
+       expr_edit_(new QPlainTextEdit())
+{
+       setWindowTitle(tr("Math Expression Editor"));
+
+       // Create the pages
+       QWidget *basics_page = new QWidget();
+       QVBoxLayout *basics_layout = new QVBoxLayout(basics_page);
+       basics_layout->addWidget(new QLabel("<b>" + tr("Inputs:") + "</b>"));
+       basics_layout->addWidget(new QLabel("signal_name\tCurrent value of signal signal_name (e.g. A0 or CH1)"));
+       basics_layout->addWidget(new QLabel("T\t\tSampling interval (= 1 / sample rate)"));
+       basics_layout->addWidget(new QLabel("t\t\tCurrent time in seconds"));
+       basics_layout->addWidget(new QLabel("s\t\tCurrent sample number"));
+       basics_layout->addWidget(new QLabel("sample('s', n)\tValue of sample #n of the signal named s"));
+       basics_layout->addWidget(new QLabel("<b>" + tr("Variables:") + "</b>"));
+       basics_layout->addWidget(new QLabel("var x;\t\tCreate variable\nvar x := 5;\tCreate variable with initial value"));
+       basics_layout->addWidget(new QLabel("<b>" + tr("Basic operators:") + "</b>"));
+       basics_layout->addWidget(new QLabel("x + y\t\tAddition of x and y"));
+       basics_layout->addWidget(new QLabel("x - y\t\tSubtraction of x and y"));
+       basics_layout->addWidget(new QLabel("x * y\t\tMultiplication of x and y"));
+       basics_layout->addWidget(new QLabel("x / y\t\tDivision of x and y"));
+       basics_layout->addWidget(new QLabel("x % y\t\tRemainder of division x / y"));
+       basics_layout->addWidget(new QLabel("x ^ y\t\tx to the power of y"));
+       basics_layout->addWidget(new QLabel("<b>" + tr("Assignments:") + "</b>"));
+       basics_layout->addWidget(new QLabel("x := y\t\tAssign the value of y to x"));
+       basics_layout->addWidget(new QLabel("x += y\t\tIncrement x by y"));
+       basics_layout->addWidget(new QLabel("x -= y\t\tDecrement x by y"));
+       basics_layout->addWidget(new QLabel("x *= y\t\tMultiply x by y"));
+       basics_layout->addWidget(new QLabel("x /= y\t\tDivide x by y"));
+       basics_layout->addWidget(new QLabel("x %= y\t\tSet x to the value of x % y"));
+
+       QWidget *func1_page = new QWidget();
+       QVBoxLayout *func1_layout = new QVBoxLayout(func1_page);
+       func1_layout->addWidget(new QLabel("<b>" + tr("General purpose functions:") + "</b>"));
+       func1_layout->addWidget(new QLabel(tr("abs(x)\t\tAbsolute value of x")));
+       func1_layout->addWidget(new QLabel(tr("avg(x, y, ...)\tAverage of all input values")));
+       func1_layout->addWidget(new QLabel(tr("ceil(x)\t\tSmallest integer that is greater than or equal to x")));
+       func1_layout->addWidget(new QLabel(tr("clamp(lb, x, ub)\tClamp x in range between lb and ub, where lb < ub")));
+       func1_layout->addWidget(new QLabel(tr("equal(x, y)\tEquality test between x and y using normalised epsilon")));
+       func1_layout->addWidget(new QLabel(tr("erf(x)\t\tError function of x")));
+       func1_layout->addWidget(new QLabel(tr("erfc(x)\t\tComplimentary error function of x")));
+       func1_layout->addWidget(new QLabel(tr("exp(x)\t\te to the power of x")));
+       func1_layout->addWidget(new QLabel(tr("expm1(x)\te to the power of x minus 1, where x is very small.")));
+       func1_layout->addWidget(new QLabel(tr("floor(x)\t\tLargest integer that is less than or equal to x")));
+       func1_layout->addWidget(new QLabel(tr("frac(x)\t\tFractional portion of x")));
+       func1_layout->addWidget(new QLabel(tr("hypot(x)\t\tHypotenuse of x and y (i.e. sqrt(x*x + y*y))")));
+       func1_layout->addWidget(new QLabel(tr("iclamp(lb, x, ub)\tInverse-clamp x outside of the range lb and ub, where lb < ub.\n\t\tIf x is within the range it will snap to the closest bound")));
+       func1_layout->addWidget(new QLabel(tr("inrange(lb, x, ub)\tIn-range returns true when x is within the range lb and ub, where lb < ub.")));
+       func1_layout->addWidget(new QLabel(tr("log(x)\t\tNatural logarithm of x")));
+       func1_layout->addWidget(new QLabel(tr("log10(x)\t\tBase 10 logarithm of x")));
+
+       QWidget *func2_page = new QWidget();
+       QVBoxLayout *func2_layout = new QVBoxLayout(func2_page);
+       func2_layout->addWidget(new QLabel(tr("log1p(x)\t\tNatural logarithm of 1 + x, where x is very small")));
+       func2_layout->addWidget(new QLabel(tr("log2(x)\t\tBase 2 logarithm of x")));
+       func2_layout->addWidget(new QLabel(tr("logn(x)\t\tBase N logarithm of x, where n is a positive integer")));
+       func2_layout->addWidget(new QLabel(tr("max(x, y, ...)\tLargest value of all the inputs")));
+       func2_layout->addWidget(new QLabel(tr("min(x, y, ...)\tSmallest value of all the inputs")));
+       func2_layout->addWidget(new QLabel(tr("mul(x, y, ...)\tProduct of all the inputs")));
+       func2_layout->addWidget(new QLabel(tr("ncdf(x)\t\tNormal cumulative distribution function")));
+       func2_layout->addWidget(new QLabel(tr("nequal(x, y)\tNot-equal test between x and y using normalised epsilon")));
+       func2_layout->addWidget(new QLabel(tr("pow(x, y)\tx to the power of y")));
+       func2_layout->addWidget(new QLabel(tr("root(x, n)\tNth-Root of x, where n is a positive integer")));
+       func2_layout->addWidget(new QLabel(tr("round(x)\t\tRound x to the nearest integer")));
+       func2_layout->addWidget(new QLabel(tr("roundn(x, n)\tRound x to n decimal places, where n > 0 and is an integer")));
+       func2_layout->addWidget(new QLabel(tr("sgn(x)\t\tSign of x; -1 if x < 0, +1 if x > 0, else zero")));
+       func2_layout->addWidget(new QLabel(tr("sqrt(x)\t\tSquare root of x, where x >= 0")));
+       func2_layout->addWidget(new QLabel(tr("sum(x, y, ..,)\tSum of all the inputs")));
+       func2_layout->addWidget(new QLabel(tr("swap(x, y)\tSwap the values of the variables x and y and return the current value of y")));
+       func2_layout->addWidget(new QLabel(tr("trunc(x)\t\tInteger portion of x")));
+
+       QWidget *trig_page = new QWidget();
+       QVBoxLayout *trig_layout = new QVBoxLayout(trig_page);
+       trig_layout->addWidget(new QLabel("<b>" + tr("Trigonometry functions:") + "</b>"));
+       trig_layout->addWidget(new QLabel(tr("acos(x)\t\tArc cosine of x expressed in radians. Interval [-1,+1]")));
+       trig_layout->addWidget(new QLabel(tr("acosh(x)\t\tInverse hyperbolic cosine of x expressed in radians")));
+       trig_layout->addWidget(new QLabel(tr("asin(x)\t\tArc sine of x expressed in radians. Interval [-1,+1]")));
+       trig_layout->addWidget(new QLabel(tr("asinh(x)\t\tInverse hyperbolic sine of x expressed in radians")));
+       trig_layout->addWidget(new QLabel(tr("atan(x)\t\tArc tangent of x expressed in radians. Interval [-1,+1]")));
+       trig_layout->addWidget(new QLabel(tr("atan2(x, y)\tArc tangent of (x / y) expressed in radians. [-pi,+pi]  ")));
+       trig_layout->addWidget(new QLabel(tr("atanh(x)\t\tInverse hyperbolic tangent of x expressed in radians")));
+       trig_layout->addWidget(new QLabel(tr("cos(x)\t\tCosine of x")));
+       trig_layout->addWidget(new QLabel(tr("cosh(x)\t\tHyperbolic cosine of x")));
+       trig_layout->addWidget(new QLabel(tr("cot(x)\t\tCotangent of x")));
+       trig_layout->addWidget(new QLabel(tr("csc(x)\t\tCosectant of x")));
+       trig_layout->addWidget(new QLabel(tr("sec(x)\t\tSecant of x")));
+       trig_layout->addWidget(new QLabel(tr("sin(x)\t\tSine of x")));
+       trig_layout->addWidget(new QLabel(tr("sinc(x)\t\tSine cardinal of x")));
+       trig_layout->addWidget(new QLabel(tr("sinh(x)\t\tHyperbolic sine of x")));
+       trig_layout->addWidget(new QLabel(tr("tan(x)\t\tTangent of x")));
+       trig_layout->addWidget(new QLabel(tr("tanh(x)\t\tHyperbolic tangent of x")));
+       trig_layout->addWidget(new QLabel(tr("deg2rad(x)\tConvert x from degrees to radians")));
+       trig_layout->addWidget(new QLabel(tr("deg2grad(x)\tConvert x from degrees to gradians")));
+       trig_layout->addWidget(new QLabel(tr("rad2deg(x)\tConvert x from radians to degrees")));
+       trig_layout->addWidget(new QLabel(tr("grad2deg(x)\tConvert x from gradians to degrees")));
+
+       QWidget *logic_page = new QWidget();
+       QVBoxLayout *logic_layout = new QVBoxLayout(logic_page);
+       logic_layout->addWidget(new QLabel("<b>" + tr("Logic operators:") + "</b>"));
+       logic_layout->addWidget(new QLabel("true\t\tTrue state or any value other than zero (typically 1)"));
+       logic_layout->addWidget(new QLabel("false\t\tFalse state, value of exactly zero"));
+       logic_layout->addWidget(new QLabel("x and y\t\tLogical AND, true only if x and y are both true"));
+       logic_layout->addWidget(new QLabel("mand(x, y, z, ...)\tMulti-input logical AND, true only if all inputs are true"));
+       logic_layout->addWidget(new QLabel("x or y\t\tLogical OR, true if either x or y is true"));
+       logic_layout->addWidget(new QLabel("mor(x, y, z, ...)\tMulti-input logical OR, true if at least one of the inputs is true"));
+       logic_layout->addWidget(new QLabel("x nand y\t\tLogical NAND, true only if either x or y is false"));
+       logic_layout->addWidget(new QLabel("x nor y\t\tLogical NOR, true only if the result of x or y is false"));
+       logic_layout->addWidget(new QLabel("not(x)\t\tLogical NOT, negate the logical sense of the input"));
+       logic_layout->addWidget(new QLabel("x xor y\t\tLogical NOR, true only if the logical states of x and y differ"));
+       logic_layout->addWidget(new QLabel("x xnor y\t\tLogical NOR, true only if x and y have the same state"));
+       logic_layout->addWidget(new QLabel("x & y\t\tSame as AND but with left to right expression short circuiting optimisation"));
+       logic_layout->addWidget(new QLabel("x | y\t\tSame as OR but with left to right expression short circuiting optimisation"));
+
+       QWidget *control1_page = new QWidget();
+       QVBoxLayout *control1_layout = new QVBoxLayout(control1_page);
+       control1_layout->addWidget(new QLabel("<b>" + tr("Comparisons:") + "</b>"));
+       control1_layout->addWidget(new QLabel(tr("x = y or x == y\tTrue only if x is strictly equal to y")));
+       control1_layout->addWidget(new QLabel(tr("x <> y or x != y\tTrue only if x does not equal y")));
+       control1_layout->addWidget(new QLabel(tr("x < y\t\tTrue only if x is less than y")));
+       control1_layout->addWidget(new QLabel(tr("x <= y\t\tTrue only if x is less than or equal to y")));
+       control1_layout->addWidget(new QLabel(tr("x > y\t\tTrue only if x is greater than y")));
+       control1_layout->addWidget(new QLabel(tr("x >= y\t\tTrue only if x is greater than or equal to y")));
+       control1_layout->addWidget(new QLabel("<b>" + tr("Flow control:") + "</b>"));
+       control1_layout->addWidget(new QLabel(tr("{ ... }\t\tBeginning and end of instruction block")));
+       control1_layout->addWidget(new QLabel(tr("if (x, y, z)\tIf x is true then return y else return z\nif (x) y;\t\t\tvariant without implied else\nif (x) { y };\t\tvariant with an instruction block\nif (x) y; else z;\t\tvariant with explicit else\nif (x) { y } else { z };\tvariant with instruction blocks")));
+       control1_layout->addWidget(new QLabel(tr("x ? y : z\tTernary operator, equivalent to 'if (x, y, z)'")));
+       control1_layout->addWidget(new QLabel(tr("switch {\t\tThe first true case condition that is encountered will\n case x > 1: a;\tdetermine the result of the switch. If none of the case\n case x < 1: b;\tconditions hold true, the default action is used\n default:     c;\tto determine the return value\n}")));
+
+       QWidget *control2_page = new QWidget();
+       QVBoxLayout *control2_layout = new QVBoxLayout(control2_page);
+       control2_layout->addWidget(new QLabel(tr("while (conditon) {\tEvaluates expression repeatedly as long as condition is true,\n expression;\treturning the last value of expression\n}")));
+       control2_layout->addWidget(new QLabel(tr("repeat\t\tEvalues expression repeatedly as long as condition is false,\n expression;\treturning the last value of expression\nuntil (condition)\n")));
+       control2_layout->addWidget(new QLabel(tr("for (var x := 0; condition; x += 1) {\tRepeatedly evaluates expression while the condition is true,\n expression\t\t\twhile evaluating the 'increment' expression on each loop\n}")));
+       control2_layout->addWidget(new QLabel(tr("break\t\tTerminates the execution of the nearest enclosed loop, returning NaN")));
+       control2_layout->addWidget(new QLabel(tr("break[x]\t\tTerminates the execution of the nearest enclosed loop, returning x")));
+       control2_layout->addWidget(new QLabel(tr("continue\t\tInterrupts loop execution and resumes with the next loop iteration")));
+       control2_layout->addWidget(new QLabel(tr("return[x]\t\tReturns immediately from within the current expression, returning x")));
+       control2_layout->addWidget(new QLabel(tr("~(expr; expr; ...)\tEvaluates each sub-expression and returns the value of the last one\n~{expr; expr; ...}")));
+
+       QWidget *example_page = new QWidget();
+       QVBoxLayout *example_layout = new QVBoxLayout(example_page);
+       for (const pair<string, string> &entry : Examples) {
+               const string &desc = entry.first;
+               const string &example = entry.second;
+
+               QLabel *desc_label = new QLabel("<b>" + tr(desc.c_str()) + "</b>");
+               desc_label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard);
+               desc_label->setOpenExternalLinks(true);
+               example_layout->addWidget(desc_label);
+               QPushButton *btn = new QPushButton(tr("Copy to expression"));
+               connect(btn, &QPushButton::clicked, this, [&]() { set_expr(QString::fromStdString(example)); });
+               QGridLayout *gridlayout = new QGridLayout();
+               gridlayout->addWidget(new QLabel(QString::fromStdString(example)), 0, 0, Qt::AlignLeft);
+               gridlayout->addWidget(btn, 0, 1, Qt::AlignRight);
+               example_layout->addLayout(gridlayout);
+       }
+
+       // Create the rest of the dialog
+       QDialogButtonBox *button_box = new QDialogButtonBox(
+               QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+
+       QTabWidget *tabs = new QTabWidget();
+       tabs->addTab(basics_page, tr("Basics"));
+       tabs->addTab(func1_page, tr("Functions 1"));
+       tabs->addTab(func2_page, tr("Functions 2"));
+       tabs->addTab(trig_page, tr("Trigonometry"));
+       tabs->addTab(logic_page, tr("Logic"));
+       tabs->addTab(control1_page, tr("Flow Control 1"));
+       tabs->addTab(control2_page, tr("Flow Control 2"));
+       tabs->addTab(example_page, tr("Examples"));
+
+       QVBoxLayout *root_layout = new QVBoxLayout(this);
+       root_layout->addWidget(tabs);
+       root_layout->addWidget(expr_edit_);
+       root_layout->addWidget(button_box);
+
+       // Set tab width to 4 characters
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
+       expr_edit_->setTabStopDistance(util::text_width(QFontMetrics(font()), "XXXX"));
+#else
+       expr_edit_->setTabStopWidth(util::text_width(QFontMetrics(font()), "XXXX"));
+#endif
+
+       connect(button_box, SIGNAL(accepted()), this, SLOT(accept()));
+       connect(button_box, SIGNAL(rejected()), this, SLOT(reject()));
+}
+
+void MathEditDialog::set_expr(const QString &expr)
+{
+       expr_edit_->document()->setPlainText(expr);
+}
+
+void MathEditDialog::accept()
+{
+       math_signal_->set_expression(expr_edit_->document()->toPlainText());
+       QDialog::accept();
+}
+
+void MathEditDialog::reject()
+{
+       math_signal_->set_expression(old_expression_);
+       QDialog::reject();
+}
+
+
+MathSignal::MathSignal(
+       pv::Session &session,
+       shared_ptr<data::SignalBase> base) :
+       AnalogSignal(session, base),
+       math_signal_(dynamic_pointer_cast<pv::data::MathSignal>(base))
+{
+       delayed_expr_updater_.setSingleShot(true);
+       delayed_expr_updater_.setInterval(MATHSIGNAL_INPUT_TIMEOUT);
+       connect(&delayed_expr_updater_, &QTimer::timeout,
+               this, [this]() { math_signal_->set_expression(expression_edit_->text()); });
+}
+
+void MathSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
+{
+       AnalogSignal::populate_popup_form(parent, form);
+
+       expression_edit_ = new QLineEdit();
+       expression_edit_->setText(math_signal_->get_expression());
+
+       const QIcon edit_icon(QIcon::fromTheme("edit", QIcon(":/icons/math.svg")));
+       QAction *edit_action =
+               expression_edit_->addAction(edit_icon, QLineEdit::TrailingPosition);
+
+       connect(expression_edit_, SIGNAL(textEdited(QString)),
+               this, SLOT(on_expression_changed(QString)));
+       connect(edit_action, SIGNAL(triggered(bool)),
+               this, SLOT(on_edit_clicked()));
+       form->addRow(tr("Expression"), expression_edit_);
+
+       sample_count_cb_ = new QComboBox();
+       sample_count_cb_->setEditable(true);
+       sample_count_cb_->addItem(tr("same as session"));
+       sample_count_cb_->addItem(tr("100"));
+       sample_count_cb_->addItem(tr("10000"));
+       sample_count_cb_->addItem(tr("1000000"));
+       connect(sample_count_cb_, SIGNAL(editTextChanged(QString)),
+               this, SLOT(on_sample_count_changed(QString)));
+       form->addRow(tr("Number of Samples"), sample_count_cb_);
+
+       sample_rate_cb_ = new QComboBox();
+       sample_rate_cb_->setEditable(true);
+       sample_rate_cb_->addItem(tr("same as session"));
+       sample_rate_cb_->addItem(tr("100"));
+       sample_rate_cb_->addItem(tr("10000"));
+       sample_rate_cb_->addItem(tr("1000000"));
+       form->addRow(tr("Sample rate"), sample_rate_cb_);
+}
+
+void MathSignal::on_expression_changed(const QString &text)
+{
+       (void)text;
+
+       // Restart update timer
+       delayed_expr_updater_.start();
+}
+
+void MathSignal::on_sample_count_changed(const QString &text)
+{
+       (void)text;
+}
+
+void MathSignal::on_edit_clicked()
+{
+       MathEditDialog dlg(session_, math_signal_);
+       dlg.set_expr(expression_edit_->text());
+
+       dlg.exec();
+}
+
+} // namespace trace
+} // namespace views
+} // namespace pv
diff --git a/pv/views/trace/mathsignal.hpp b/pv/views/trace/mathsignal.hpp
new file mode 100644 (file)
index 0000000..6af708e
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_MATHSIGNAL_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_MATHSIGNAL_HPP
+
+#include <memory>
+#include <vector>
+
+#include <QComboBox>
+#include <QDialog>
+#include <QPlainTextEdit>
+#include <QString>
+#include <QTimer>
+
+#include <pv/data/mathsignal.hpp>
+#include <pv/views/trace/analogsignal.hpp>
+
+using std::pair;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+
+namespace pv {
+namespace views {
+namespace trace {
+
+class MathEditDialog : public QDialog
+{
+       Q_OBJECT
+
+private:
+       static const vector< pair<string, string> > Examples;
+
+public:
+       MathEditDialog(pv::Session &session, shared_ptr<pv::data::MathSignal> math_signal,
+               QWidget *parent = nullptr);
+
+       void set_expr(const QString &expr);
+
+private Q_SLOTS:
+       void accept();
+       void reject();
+
+private:
+       pv::Session &session_;
+       shared_ptr<pv::data::MathSignal> math_signal_;
+       QString old_expression_;
+
+       QPlainTextEdit *expr_edit_;
+};
+
+
+class MathSignal : public AnalogSignal
+{
+       Q_OBJECT
+
+public:
+       MathSignal(pv::Session &session, shared_ptr<data::SignalBase> base);
+
+protected:
+       void populate_popup_form(QWidget *parent, QFormLayout *form);
+
+       shared_ptr<pv::data::MathSignal> math_signal_;
+
+private Q_SLOTS:
+       void on_expression_changed(const QString &text);
+       void on_sample_count_changed(const QString &text);
+
+       void on_edit_clicked();
+
+private:
+       QLineEdit *expression_edit_;
+       QComboBox *sample_count_cb_, *sample_rate_cb_;
+       QString sample_count_text_, sample_rate_text_;
+       QTimer delayed_expr_updater_, delayed_count_updater_, delayed_rate_updater_;
+};
+
+} // namespace trace
+} // namespace views
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWS_TRACE_MATHSIGNAL_HPP
index bebf529147da67f5bc4d591255bce144d5d1674b..64581459593a1fcc8148a4c4d2e14dee31164b64 100644 (file)
@@ -158,6 +158,12 @@ void Ruler::contextMenuEvent(QContextMenuEvent *event)
        connect(set_zero_position, SIGNAL(triggered()), this, SLOT(on_setZeroPosition()));
        menu->addAction(set_zero_position);
 
+       if (view_.zero_offset().convert_to<double>() != 0) {
+               QAction *const reset_zero_position = new QAction(tr("Reset zero point"), this);
+               connect(reset_zero_position, SIGNAL(triggered()), this, SLOT(on_resetZeroPosition()));
+               menu->addAction(reset_zero_position);
+       }
+
        QAction *const toggle_hover_marker = new QAction(this);
        connect(toggle_hover_marker, SIGNAL(triggered()), this, SLOT(on_toggleHoverMarker()));
        menu->addAction(toggle_hover_marker);
@@ -236,7 +242,11 @@ shared_ptr<ViewItem> Ruler::get_mouse_over_item(const QPoint &pt)
 
 void Ruler::mouseDoubleClickEvent(QMouseEvent *event)
 {
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       hover_item_ = view_.add_flag(get_absolute_time_from_x_pos(event->pos().x()));
+#else
        hover_item_ = view_.add_flag(get_absolute_time_from_x_pos(event->x()));
+#endif
 }
 
 void Ruler::paintEvent(QPaintEvent*)
@@ -277,7 +287,7 @@ void Ruler::paintEvent(QPaintEvent*)
                const int rightedge = width();
                const int x_tick = tick.first;
                if ((x_tick > leftedge) && (x_tick < rightedge)) {
-                       const int x_left_bound = QFontMetrics(font()).width(tick.second) / 2;
+                       const int x_left_bound = util::text_width(QFontMetrics(font()), tick.second) / 2;
                        const int x_right_bound = rightedge - x_left_bound;
                        const int x_legend = min(max(x_tick, x_left_bound), x_right_bound);
                        p.drawText(x_legend, ValueMargin, 0, text_height,
@@ -397,6 +407,11 @@ void Ruler::on_setZeroPosition()
        view_.set_zero_position(get_absolute_time_from_x_pos(mouse_down_point_.x()));
 }
 
+void Ruler::on_resetZeroPosition()
+{
+       view_.reset_zero_position();
+}
+
 void Ruler::on_toggleHoverMarker()
 {
        GlobalSettings settings;
index 18a09d9baf5fce069f8ebeaf8b584a142058e36f..55c156f5942e34f8a97912322b25a3b2df4c669b 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_RULER_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_RULER_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_RULER_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_RULER_HPP
 
 #include <functional>
 #include <memory>
@@ -184,6 +184,7 @@ private Q_SLOTS:
 
        void on_createMarker();
        void on_setZeroPosition();
+       void on_resetZeroPosition();
        void on_toggleHoverMarker();
 
 private:
@@ -202,4 +203,4 @@ private:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_RULER_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_RULER_HPP
index b770dee3acadd0d5a45cf38a323850aeed7bb665..8840825c2fe2c08cba62cc646a162176b6a49b76 100644 (file)
@@ -44,23 +44,28 @@ namespace trace {
 const char *const ChannelNames[] = {
        "CLK",
        "DATA",
+       "EN",
        "IN",
        "OUT",
        "RST",
        "TX",
        "RX",
-       "EN",
+       "SDA",
+       "SCL",
        "SCLK",
        "MOSI",
        "MISO",
+       "/CS",
+       "nCS",
        "/SS",
-       "SDA",
-       "SCL"
+       "nSS",
+       "/RST",
+       "nRST",
 };
 
 Signal::Signal(pv::Session &session,
-       shared_ptr<data::SignalBase> channel) :
-       Trace(channel),
+       shared_ptr<data::SignalBase> signal) :
+       Trace(signal),
        session_(session),
        name_widget_(nullptr)
 {
@@ -156,10 +161,17 @@ QMenu* Signal::create_header_context_menu(QWidget *parent)
 
        menu->addSeparator();
 
-       QAction *const disable = new QAction(tr("Disable"), this);
-       disable->setShortcuts(QKeySequence::Delete);
-       connect(disable, SIGNAL(triggered()), this, SLOT(on_disable()));
-       menu->addAction(disable);
+       QString caption;
+
+       if (base_->is_generated())
+               caption = tr("Remove");
+       else
+               caption = tr("Disable");
+
+       QAction *const a = new QAction(caption, this);
+       a->setShortcuts(QKeySequence::Delete);
+       connect(a, SIGNAL(triggered()), this, SLOT(on_disable()));
+       menu->addAction(a);
 
        return menu;
 }
@@ -184,7 +196,11 @@ void Signal::on_name_changed(const QString &text)
 
 void Signal::on_disable()
 {
-       base_->set_enabled(false);
+       // For generated signals, "disable" means "remove"
+       if (base_->is_generated())
+               session_.remove_generated_signal(base_);
+       else
+               base_->set_enabled(false);
 }
 
 void Signal::on_enabled_changed(bool enabled)
index c9f38dd96107657d7e16fd6fb05b71a16488dfa2..e10e93eac4d2863d200c530fbcb7ff4a07e72b7d 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNAL_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNAL_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_SIGNAL_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_SIGNAL_HPP
 
 #include <memory>
 
@@ -62,7 +62,7 @@ class Signal : public Trace, public ViewItemOwner
        Q_OBJECT
 
 protected:
-       Signal(pv::Session &session, shared_ptr<data::SignalBase> channel);
+       Signal(pv::Session &session, shared_ptr<data::SignalBase> signal);
 
 public:
        /**
@@ -70,8 +70,6 @@ public:
         */
        virtual void set_name(QString name);
 
-       virtual shared_ptr<pv::data::SignalData> data() const = 0;
-
        /**
         * Determines the closest level change (i.e. edge) to a given sample, which
         * is useful for e.g. the "snap to edge" functionality.
@@ -119,4 +117,4 @@ protected:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_SIGNAL_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_SIGNAL_HPP
index 06d850505ce8ebde350ecfbcbaeecbd5b3a79c3a..acac3273955f49c862932fbfe4ad3654e6e0ac07 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TIMEITEM_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TIMEITEM_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_TIMEITEM_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_TIMEITEM_HPP
 
 #include "viewitem.hpp"
 
@@ -77,4 +77,4 @@ protected:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TIMEITEM_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_TIMEITEM_HPP
index 9cb33a7637e1d7a3d90550c5d90656d2b3fbedd6..0f390f528004858f3a835fcb781bc5d1ad332b18 100644 (file)
@@ -88,7 +88,7 @@ QRectF TimeMarker::label_rect(const QRectF &rect) const
 {
        QFontMetrics m(QApplication::font());
        const QSizeF text_size(
-               max(m.boundingRect(get_text()).size().width(), ArrowSize),
+               max(m.boundingRect(get_display_text()).size().width(), ArrowSize),
                m.height());
        const QSizeF label_size(text_size + LabelPadding * 2);
        const float top = rect.height() - label_size.height() -
@@ -105,6 +105,16 @@ QRectF TimeMarker::hit_box_rect(const ViewItemPaintParams &pp) const
        return QRectF(x - h / 2.0f, pp.top(), h, pp.height());
 }
 
+QString TimeMarker::get_display_text() const
+{
+       return get_text();
+}
+
+void TimeMarker::set_text(const QString &text)
+{
+       (void)text;
+}
+
 void TimeMarker::paint_label(QPainter &p, const QRect &rect, bool hover)
 {
        if (!enabled())
@@ -153,7 +163,7 @@ void TimeMarker::paint_label(QPainter &p, const QRect &rect, bool hover)
        p.drawPolygon(points, countof(points));
 
        p.setPen(select_text_color(color_));
-       p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, get_text());
+       p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, get_display_text());
 }
 
 void TimeMarker::paint_fore(QPainter &p, ViewItemPaintParams &pp)
@@ -174,6 +184,8 @@ pv::widgets::Popup* TimeMarker::create_popup(QWidget *parent)
        popup->set_position(parent->mapToGlobal(
                drag_point(parent->rect())), Popup::Bottom);
 
+       connect(popup, SIGNAL(closed()), this, SLOT(on_popup_closed()));
+
        QFormLayout *const form = new QFormLayout(popup);
        popup->setLayout(form);
 
@@ -188,6 +200,13 @@ pv::widgets::Popup* TimeMarker::create_popup(QWidget *parent)
        return popup;
 }
 
+void TimeMarker::on_popup_closed()
+{
+       GlobalSettings settings;
+       if (!settings.value(GlobalSettings::Key_View_KeepRulerItemSelected).toBool())
+               select(false);
+}
+
 void TimeMarker::on_value_changed(const pv::util::Timestamp& value)
 {
        set_time(view_.ruler()->get_absolute_time_from_ruler_time(value));
index 99c56d34beedad769a7b4e988b456178210fa412..f67a0f17a1e36567bb050afa23d6ecedaccdbb2c 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_MARKER_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_MARKER_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_TIMEMARKER_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_TIMEMARKER_HPP
 
 #include <QColor>
 #include <QDoubleSpinBox>
@@ -95,10 +95,21 @@ public:
        QRectF hit_box_rect(const ViewItemPaintParams &pp) const override;
 
        /**
-        * Gets the text to show in the marker.
+        * Gets the current text to show in the marker - this may be dynamic.
+        */
+       virtual QString get_display_text() const;
+
+       /**
+        * Gets the default text used to show the marker - e.g. the user-editable
+        * name.
         */
        virtual QString get_text() const = 0;
 
+       /**
+        * Sets the text to show in the marker.
+        */
+       virtual void set_text(const QString &text);
+
        /**
         * Paints the marker's label to the ruler.
         * @param p The painter to draw with.
@@ -117,6 +128,8 @@ public:
        virtual pv::widgets::Popup* create_popup(QWidget *parent) override;
 
 private Q_SLOTS:
+       void on_popup_closed();
+
        void on_value_changed(const pv::util::Timestamp& value);
 
 protected:
@@ -134,4 +147,4 @@ protected:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_MARKER_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_TIMEMARKER_HPP
index 6d44da2c889d8babc73cd6e2054965a1aa980a6c..4535a13efcb31f6e504d77963179e550c891349f 100644 (file)
@@ -49,19 +49,22 @@ const int Trace::LabelHitPadding = 2;
 
 const QColor Trace::BrightGrayBGColor = QColor(0, 0, 0, 10 * 255 / 100);
 const QColor Trace::DarkGrayBGColor = QColor(0, 0, 0, 15 * 255 / 100);
+const QColor Trace::ErrorBgColor = QColor(0xEF, 0x29, 0x29);
 
-Trace::Trace(shared_ptr<data::SignalBase> channel) :
-       base_(channel),
+Trace::Trace(shared_ptr<data::SignalBase> signal) :
+       base_(signal),
        axis_pen_(AxisPen),
        segment_display_mode_(ShowLastSegmentOnly),  // Will be overwritten by View
        current_segment_(0),
        popup_(nullptr),
        popup_form_(nullptr)
 {
-       connect(channel.get(), SIGNAL(name_changed(const QString&)),
+       connect(signal.get(), SIGNAL(name_changed(const QString&)),
                this, SLOT(on_name_changed(const QString&)));
-       connect(channel.get(), SIGNAL(color_changed(const QColor&)),
+       connect(signal.get(), SIGNAL(color_changed(const QColor&)),
                this, SLOT(on_color_changed(const QColor&)));
+       connect(signal.get(), SIGNAL(error_message_changed(const QString&)),
+               this, SLOT(on_error_message_changed(const QString&)));
 
        GlobalSettings::add_change_handler(this);
 
@@ -175,6 +178,26 @@ void Trace::paint_label(QPainter &p, const QRect &rect, bool hover)
                Qt::AlignCenter | Qt::AlignVCenter, base_->name());
 }
 
+void Trace::paint_error(QPainter &p, const ViewItemPaintParams &pp)
+{
+       const QString message = base_->get_error_message();
+
+       const int y = get_visual_y();
+
+       p.setPen(ErrorBgColor.darker());
+       p.setBrush(ErrorBgColor);
+
+       const QRectF bounding_rect = QRectF(pp.left(), INT_MIN / 2 + y, pp.right(), INT_MAX);
+
+       const QRectF text_rect = p.boundingRect(bounding_rect, Qt::AlignCenter, message);
+       const qreal r = text_rect.height() / 4;
+
+       p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r, Qt::AbsoluteSize);
+
+       p.setPen(Qt::black);
+       p.drawText(text_rect, message);
+}
+
 QMenu* Trace::create_header_context_menu(QWidget *parent)
 {
        QMenu *const menu = ViewItem::create_header_context_menu(parent);
@@ -402,6 +425,14 @@ void Trace::on_color_changed(const QColor &color)
                owner_->row_item_appearance_changed(true, true);
 }
 
+void Trace::on_error_message_changed(const QString &msg)
+{
+       (void)msg;
+
+       if (owner_)
+               owner_->row_item_appearance_changed(false, true);
+}
+
 void Trace::on_popup_closed()
 {
        popup_ = nullptr;
@@ -411,7 +442,8 @@ void Trace::on_popup_closed()
 void Trace::on_nameedit_changed(const QString &name)
 {
        /* This event handler notifies SignalBase that the name changed */
-       base_->set_name(name);
+       if (!name.isEmpty())
+               base_->set_name(name);
 }
 
 void Trace::on_coloredit_changed(const QColor &color)
index 2b98811b32ea3506865fc8db5e9e4924644539c7..741f346957735c92aa1f62f1dc37e5cc3c85aad4 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACE_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACE_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_TRACE_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_TRACE_HPP
 
 #include <QColor>
 #include <QPainter>
@@ -91,9 +91,10 @@ private:
 
        static const QColor BrightGrayBGColor;
        static const QColor DarkGrayBGColor;
+       static const QColor ErrorBgColor;
 
 protected:
-       Trace(shared_ptr<data::SignalBase> channel);
+       Trace(shared_ptr<data::SignalBase> signal);
        ~Trace();
 
 public:
@@ -127,6 +128,13 @@ public:
         */
        virtual void paint_label(QPainter &p, const QRect &rect, bool hover);
 
+       /**
+        * Paints the signal's current error message text.
+        * @param p the QPainter to paint into.
+        * @param pp The painting parameters object to paint with.
+        */
+       virtual void paint_error(QPainter &p, const ViewItemPaintParams &pp);
+
        virtual QMenu* create_header_context_menu(QWidget *parent);
 
        virtual QMenu* create_view_context_menu(QWidget *parent, QPoint &click_pos);
@@ -142,7 +150,7 @@ public:
 
        /**
         * Computes the outline rectangle of the viewport hit-box.
-        * @param rect the rectangle of the viewport area.
+        * @param pp The painting parameters object to paint with.
         * @return Returns the rectangle of the hit-box.
         * @remarks The default implementation returns an empty hit-box.
         */
@@ -184,8 +192,8 @@ protected:
 
 protected Q_SLOTS:
        virtual void on_name_changed(const QString &text);
-
        virtual void on_color_changed(const QColor &color);
+       virtual void on_error_message_changed(const QString &msg);
 
        void on_popup_closed();
 
@@ -217,4 +225,4 @@ private:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACE_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_TRACE_HPP
index 3cbca90934d68a8417e17d5feb6bc70fd79bb3a6..ecc97b6ee80153671c2a1cc4512a0530a99ea9c4 100644 (file)
@@ -138,7 +138,11 @@ QMenu* TraceGroup::create_header_context_menu(QWidget *parent)
        QMenu *const menu = new QMenu(parent);
 
        QAction *const ungroup = new QAction(tr("Ungroup"), this);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       ungroup->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_U));
+#else
        ungroup->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U));
+#endif
        connect(ungroup, SIGNAL(triggered()), this, SLOT(on_ungroup()));
        menu->addAction(ungroup);
 
index 4a793665886856971b52e56d8647a96ab6fc92af..b41db6bca2bcbf0f41f7e85a583f25e82a9522e9 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEGROUP_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEGROUP_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_TRACEGROUP_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_TRACEGROUP_HPP
 
 #include "tracetreeitem.hpp"
 #include "tracetreeitemowner.hpp"
@@ -131,4 +131,4 @@ private Q_SLOTS:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEGROUP_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_TRACEGROUP_HPP
index 7819ee6c44dcde4576ed0ed4fd81b561df40a014..6273605d83d84ed81b7911f538d53db838d5d3d7 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEPALETTE_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEPALETTE_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_TRACEPALETTE_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_TRACEPALETTE_HPP
 
 #include <QColor>
 
@@ -38,4 +38,4 @@ public:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACEPALETTE_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_TRACEPALETTE_HPP
index 932e2b8c932285b0967993e2b5564569df6c5026..e5ebbdd74dc40651d7531ec6f0ba539f0c15430f 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEM_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEM_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_TRACETREEITEM_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_TRACETREEITEM_HPP
 
 #include <memory>
 
@@ -94,7 +94,7 @@ public:
         * Sets the owner this trace in the view trace hierachy.
         * @param The new owner of the trace.
         */
-       void set_owner(TraceTreeItemOwner *owner);
+       virtual void set_owner(TraceTreeItemOwner *owner);
 
        /**
         * Gets the visual y-offset of the axis.
@@ -133,4 +133,4 @@ private:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEM_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_TRACETREEITEM_HPP
index 571090072df8a123f5d29f7f06863e06685ec2fa..b7e95843485c9a2dea03c515e3a9e3c490ba99c7 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEMOWNER_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEMOWNER_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_TRACETREEITEMOWNER_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_TRACETREEITEMOWNER_HPP
 
 #include "viewitemowner.hpp"
 #include "tracetreeitem.hpp"
@@ -110,4 +110,4 @@ public:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRACETREEITEMOWNER_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_TRACETREEITEMOWNER_HPP
index 222d3fb9f78c76450d75f9dde25b32f6dc1240c0..40c096c0292fa32778c078f0a26bb5fbb1059789 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_TRIGGER_MARKER_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_TRIGGER_MARKER_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_TRIGGERMARKER_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_TRIGGERMARKER_HPP
 
 #include "timeitem.hpp"
 
@@ -92,4 +92,4 @@ private:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_TRIGGER_MARKER_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_TRIGGERMARKER_HPP
index 15e65127b2932062b46a5734575febf9d163631c..33d8b0a44aa7ed9530adb4b4349439d8f9cc1c49 100644 (file)
 #include <cmath>
 #include <iostream>
 #include <iterator>
-#include <unordered_set>
-
-#include <boost/archive/text_iarchive.hpp>
-#include <boost/archive/text_oarchive.hpp>
-#include <boost/serialization/serialization.hpp>
 
 #include <QApplication>
+#include <QDebug>
 #include <QEvent>
 #include <QFontMetrics>
 #include <QMenu>
 
 #include <libsigrokcxx/libsigrokcxx.hpp>
 
-#include "analogsignal.hpp"
-#include "header.hpp"
-#include "logicsignal.hpp"
-#include "ruler.hpp"
-#include "signal.hpp"
-#include "tracegroup.hpp"
-#include "triggermarker.hpp"
 #include "view.hpp"
-#include "viewport.hpp"
 
-#include "pv/data/logic.hpp"
-#include "pv/data/logicsegment.hpp"
-#include "pv/devices/device.hpp"
 #include "pv/globalsettings.hpp"
+#include "pv/metadata_obj.hpp"
 #include "pv/session.hpp"
 #include "pv/util.hpp"
+#include "pv/data/logic.hpp"
+#include "pv/data/logicsegment.hpp"
+#include "pv/data/signalbase.hpp"
+#include "pv/devices/device.hpp"
+#include "pv/views/trace/mathsignal.hpp"
+#include "pv/views/trace/analogsignal.hpp"
+#include "pv/views/trace/header.hpp"
+#include "pv/views/trace/logicsignal.hpp"
+#include "pv/views/trace/ruler.hpp"
+#include "pv/views/trace/signal.hpp"
+#include "pv/views/trace/tracegroup.hpp"
+#include "pv/views/trace/triggermarker.hpp"
+#include "pv/views/trace/viewport.hpp"
 
 #ifdef ENABLE_DECODE
-#include "decodetrace.hpp"
+#include "pv/views/trace/decodetrace.hpp"
 #endif
 
+using pv::data::SignalBase;
 using pv::data::SignalData;
 using pv::data::Segment;
 using pv::util::TimeUnit;
@@ -75,6 +75,7 @@ using std::back_inserter;
 using std::copy_if;
 using std::count_if;
 using std::inserter;
+using std::lock_guard;
 using std::max;
 using std::make_pair;
 using std::make_shared;
@@ -84,9 +85,6 @@ using std::pair;
 using std::set;
 using std::set_difference;
 using std::shared_ptr;
-using std::stringstream;
-using std::unordered_map;
-using std::unordered_set;
 using std::vector;
 
 namespace pv {
@@ -94,12 +92,14 @@ namespace views {
 namespace trace {
 
 const Timestamp View::MaxScale("1e9");
-const Timestamp View::MinScale("1e-12");
+const Timestamp View::MinScale("1e-14");
 
 const int View::MaxScrollValue = INT_MAX / 2;
 
-const int View::ScaleUnits[3] = {1, 2, 5};
+/* Area at the top and bottom of the view that can't be scrolled out of sight */
+const int View::ViewScrollMargin = 50;
 
+const int View::ScaleUnits[3] = {1, 2, 5};
 
 CustomScrollArea::CustomScrollArea(QWidget *parent) :
        QAbstractScrollArea(parent)
@@ -184,6 +184,13 @@ View::View(Session &session, bool is_main_view, QMainWindow *parent) :
 
        GlobalSettings::add_change_handler(this);
 
+       // Set up metadata objects and event handlers
+       if (is_main_view) {
+               session_.metadata_obj_manager()->create_object(MetadataObjMainViewRange);
+               session_.metadata_obj_manager()->create_object(MetadataObjMousePos);
+       }
+
+       // Set up UI event handlers
        connect(scrollarea_->horizontalScrollBar(), SIGNAL(valueChanged(int)),
                this, SLOT(h_scroll_value_changed(int)));
        connect(scrollarea_->verticalScrollBar(), SIGNAL(valueChanged(int)),
@@ -205,6 +212,7 @@ View::View(Session &session, bool is_main_view, QMainWindow *parent) :
        connect(&lazy_event_handler_, SIGNAL(timeout()),
                this, SLOT(process_sticky_events()));
        lazy_event_handler_.setSingleShot(true);
+       lazy_event_handler_.setInterval(1000 / ViewBase::MaxViewAutoUpdateRate);
 
        // Set up local keyboard shortcuts
        zoom_in_shortcut_ = new QShortcut(QKeySequence(Qt::Key_Plus), this,
@@ -276,8 +284,10 @@ void View::reset_view_state()
        scale_ = 1e-3;
        offset_ = 0;
        ruler_offset_ = 0;
+       zero_offset_ = 0;
+       custom_zero_offset_set_ = false;
        updating_scroll_ = false;
-       settings_restored_ = false;
+       restoring_state_ = false;
        always_zoom_to_fit_ = false;
        tick_period_ = 0;
        tick_prefix_ = pv::util::SIPrefix::yocto;
@@ -291,10 +301,8 @@ void View::reset_view_state()
        grabbed_widget_ = nullptr;
        hover_point_ = QPoint(-1, -1);
        scroll_needs_defaults_ = true;
-       saved_v_offset_ = 0;
        scale_at_acq_start_ = 0;
        offset_at_acq_start_ = 0;
-       suppress_zoom_to_fit_after_acq_ = false;
 
        show_cursors_ = false;
        cursor_state_changed(show_cursors_);
@@ -305,6 +313,8 @@ void View::reset_view_state()
 
        // Make sure the standard bar's segment selector is in sync
        set_segment_display_mode(segment_display_mode_);
+
+       scrollarea_->verticalScrollBar()->setRange(-100000000, 100000000);
 }
 
 Session& View::session()
@@ -317,7 +327,7 @@ const Session& View::session() const
        return session_;
 }
 
-unordered_set< shared_ptr<Signal> > View::signals() const
+vector< shared_ptr<Signal> > View::signals() const
 {
        return signals_;
 }
@@ -335,24 +345,64 @@ shared_ptr<Signal> View::get_signal_by_signalbase(shared_ptr<data::SignalBase> b
        return ret_val;
 }
 
-void View::clear_signals()
+void View::clear_signalbases()
 {
-       ViewBase::clear_signals();
+       ViewBase::clear_signalbases();
        signals_.clear();
 }
 
-void View::add_signal(const shared_ptr<Signal> signal)
+void View::add_signalbase(const shared_ptr<data::SignalBase> signalbase)
 {
-       ViewBase::add_signalbase(signal->base());
-       signals_.insert(signal);
+       ViewBase::add_signalbase(signalbase);
+
+       shared_ptr<Signal> signal;
+
+       switch (signalbase->type()) {
+       case SignalBase::LogicChannel:
+               signal = shared_ptr<Signal>(new LogicSignal(session_, signalbase));
+               break;
+
+       case SignalBase::AnalogChannel:
+               signal = shared_ptr<Signal>(new AnalogSignal(session_, signalbase));
+               break;
+
+       case SignalBase::MathChannel:
+               signal = shared_ptr<Signal>(new MathSignal(session_, signalbase));
+               break;
+
+       default:
+               qDebug() << "Unknown signalbase type:" << signalbase->type();
+               assert(false);
+               break;
+       }
+
+       signals_.push_back(signal);
 
        signal->set_segment_display_mode(segment_display_mode_);
        signal->set_current_segment(current_segment_);
 
+       // Secondary views use the signal's settings in the main view
+       if (!is_main_view()) {
+               shared_ptr<View> main_tv = dynamic_pointer_cast<View>(session_.main_view());
+               shared_ptr<Signal> main_signal = main_tv->get_signal_by_signalbase(signalbase);
+               if (main_signal)
+                       signal->restore_settings(main_signal->save_settings());
+       }
+
        connect(signal->base().get(), SIGNAL(name_changed(const QString&)),
                this, SLOT(on_signal_name_changed()));
 }
 
+void View::remove_signalbase(const shared_ptr<data::SignalBase> signalbase)
+{
+       ViewBase::remove_signalbase(signalbase);
+
+       shared_ptr<Signal> signal = get_signal_by_signalbase(signalbase);
+
+       if (signal)
+               remove_trace(signal);
+}
+
 #ifdef ENABLE_DECODE
 void View::clear_decode_signals()
 {
@@ -380,14 +430,34 @@ void View::remove_decode_signal(shared_ptr<data::DecodeSignal> signal)
        for (auto i = decode_traces_.begin(); i != decode_traces_.end(); i++)
                if ((*i)->base() == signal) {
                        decode_traces_.erase(i);
-                       signals_changed();
-                       return;
+                       break;
                }
 
        ViewBase::remove_decode_signal(signal);
 }
 #endif
 
+void View::remove_trace(shared_ptr<Trace> trace)
+{
+       TraceTreeItemOwner *const owner = trace->owner();
+       assert(owner);
+       owner->remove_child_item(trace);
+
+       for (auto i = signals_.begin(); i != signals_.end(); i++)
+               if ((*i) == trace) {
+                       signals_.erase(i);
+                       break;
+               }
+
+       if (!header_was_shrunk_)
+               resize_header_to_fit();
+
+       update_layout();
+
+       header_->update();
+       viewport_->update();
+}
+
 shared_ptr<Signal> View::get_signal_under_mouse_cursor() const
 {
        return signal_under_mouse_cursor_;
@@ -432,12 +502,12 @@ void View::save_settings(QSettings &settings) const
        settings.setValue("splitter_state", splitter_->saveState());
        settings.setValue("segment_display_mode", segment_display_mode_);
 
-       {
-               stringstream ss;
-               boost::archive::text_oarchive oa(ss);
-               oa << boost::serialization::make_nvp("offset", offset_);
-               settings.setValue("offset", QString::fromStdString(ss.str()));
-       }
+       GlobalSettings::store_timestamp(settings, "offset", offset_);
+
+       if (custom_zero_offset_set_)
+               GlobalSettings::store_timestamp(settings, "zero_offset", -zero_offset_);
+       else
+               settings.remove("zero_offset");
 
        for (const shared_ptr<Signal>& signal : signals_) {
                settings.beginGroup(signal->base()->internal_name());
@@ -455,20 +525,13 @@ void View::restore_settings(QSettings &settings)
                set_scale(settings.value("scale").toDouble());
 
        if (settings.contains("offset")) {
-               util::Timestamp offset;
-               stringstream ss;
-               ss << settings.value("offset").toString().toStdString();
-
-               try {
-                       boost::archive::text_iarchive ia(ss);
-                       ia >> boost::serialization::make_nvp("offset", offset);
-                       // This also updates ruler_offset_
-                       set_offset(offset);
-               } catch (boost::archive::archive_exception&) {
-                       qDebug() << "Could not restore the view offset";
-               }
+               // This also updates ruler_offset_
+               set_offset(GlobalSettings::restore_timestamp(settings, "offset"));
        }
 
+       if (settings.contains("zero_offset"))
+               set_zero_position(GlobalSettings::restore_timestamp(settings, "zero_offset"));
+
        if (settings.contains("splitter_state"))
                splitter_->restoreState(settings.value("splitter_state").toByteArray());
 
@@ -483,14 +546,12 @@ void View::restore_settings(QSettings &settings)
        }
 
        if (settings.contains("v_offset")) {
+               // Note: see eventFilter() for additional information
                saved_v_offset_ = settings.value("v_offset").toInt();
-               set_v_offset(saved_v_offset_);
                scroll_needs_defaults_ = false;
-               // Note: see eventFilter() for additional information
        }
 
-       settings_restored_ = true;
-       suppress_zoom_to_fit_after_acq_ = true;
+       restoring_state_ = true;
 
        // Update the ruler so that it uses the new scale
        calculate_tick_spacing();
@@ -522,6 +583,9 @@ void View::set_scale(double scale)
 {
        if (scale_ != scale) {
                scale_ = scale;
+
+               update_view_range_metaobject();
+
                scale_changed();
        }
 }
@@ -531,6 +595,9 @@ void View::set_offset(const pv::util::Timestamp& offset, bool force_update)
        if ((offset_ != offset) || force_update) {
                offset_ = offset;
                ruler_offset_ = offset_ + zero_offset_;
+
+               update_view_range_metaobject();
+
                offset_changed();
        }
 }
@@ -548,6 +615,7 @@ const Timestamp& View::ruler_offset() const
 void View::set_zero_position(const pv::util::Timestamp& position)
 {
        zero_offset_ = -position;
+       custom_zero_offset_set_ = true;
 
        // Force an immediate update of the offsets
        set_offset(offset_, true);
@@ -558,6 +626,19 @@ void View::reset_zero_position()
 {
        zero_offset_ = 0;
 
+       // When enabled, the first trigger for this segment is used as the zero position
+       GlobalSettings settings;
+       bool trigger_is_zero_time = settings.value(GlobalSettings::Key_View_TriggerIsZeroTime).toBool();
+
+       if (trigger_is_zero_time) {
+               vector<util::Timestamp> triggers = session_.get_triggers(current_segment_);
+
+               if (triggers.size() > 0)
+                       zero_offset_ = triggers.front();
+       }
+
+       custom_zero_offset_set_ = false;
+
        // Force an immediate update of the offsets
        set_offset(offset_, true);
        ruler_->update();
@@ -676,12 +757,8 @@ void View::set_current_segment(uint32_t segment_id)
        for (util::Timestamp timestamp : triggers)
                trigger_markers_.push_back(make_shared<TriggerMarker>(*this, timestamp));
 
-       // When enabled, the first trigger for this segment is used as the zero position
-       GlobalSettings settings;
-       bool trigger_is_zero_time = settings.value(GlobalSettings::Key_View_TriggerIsZeroTime).toBool();
-
-       if (trigger_is_zero_time && (triggers.size() > 0))
-               set_zero_position(triggers.front());
+       if (!custom_zero_offset_set_)
+               reset_zero_position();
 
        viewport_->update();
 
@@ -705,7 +782,7 @@ void View::set_segment_display_mode(Trace::SegmentDisplayMode mode)
        for (const shared_ptr<Signal>& signal : signals_)
                signal->set_segment_display_mode(mode);
 
-       uint32_t last_segment = session_.get_segment_count() - 1;
+       uint32_t last_segment = session_.get_highest_segment_id();
 
        switch (mode) {
        case Trace::ShowLastSegmentOnly:
@@ -779,12 +856,42 @@ void View::zoom_fit(bool gui_state)
        set_scale_offset(scale.convert_to<double>(), extents.first);
 }
 
+void View::focus_on_range(uint64_t start_sample, uint64_t end_sample)
+{
+       assert(viewport_);
+       const uint64_t w = viewport_->width();
+       if (w <= 0)
+               return;
+
+       const double samplerate = session_.get_samplerate();
+       const double samples_per_pixel = samplerate * scale_;
+       const uint64_t viewport_samples = w * samples_per_pixel;
+
+       const uint64_t sample_delta = (end_sample - start_sample);
+
+       // Note: We add 20% margin on the left and 5% on the right
+       const uint64_t ext_sample_delta = sample_delta * 1.25;
+
+       // Check if we can keep the zoom level and just center the supplied range
+       if (viewport_samples >= ext_sample_delta) {
+               // Note: offset is the left edge of the view so to center, we subtract half the view width
+               const int64_t sample_offset = (start_sample + (sample_delta / 2) - (viewport_samples / 2));
+               const Timestamp offset = sample_offset / samplerate;
+               set_scale_offset(scale_, offset);
+       } else {
+               const Timestamp offset = (start_sample - sample_delta * 0.20) / samplerate;
+               const Timestamp delta = ext_sample_delta / samplerate;
+               const Timestamp scale = max(min(delta / w, MaxScale), MinScale);
+               set_scale_offset(scale.convert_to<double>(), offset);
+       }
+}
+
 void View::set_scale_offset(double scale, const Timestamp& offset)
 {
        // Disable sticky scrolling / always zoom to fit when acquisition runs
        // and user drags the viewport
        if ((scale_ == scale) && (offset_ != offset) &&
-                       (session_.get_capture_state() == Session::Running)) {
+               (session_.get_capture_state() == Session::Running)) {
 
                if (sticky_scrolling_) {
                        sticky_scrolling_ = false;
@@ -807,13 +914,13 @@ void View::set_scale_offset(double scale, const Timestamp& offset)
        viewport_->update();
 }
 
-set< shared_ptr<SignalData> > View::get_visible_data() const
+vector< shared_ptr<SignalData> > View::get_visible_data() const
 {
        // Make a set of all the visible data objects
-       set< shared_ptr<SignalData> > visible_data;
+       vector< shared_ptr<SignalData> > visible_data;
        for (const shared_ptr<Signal>& sig : signals_)
                if (sig->enabled())
-                       visible_data.insert(sig->data());
+                       visible_data.push_back(sig->base()->data());
 
        return visible_data;
 }
@@ -821,8 +928,16 @@ set< shared_ptr<SignalData> > View::get_visible_data() const
 pair<Timestamp, Timestamp> View::get_time_extents() const
 {
        boost::optional<Timestamp> left_time, right_time;
-       const set< shared_ptr<SignalData> > visible_data = get_visible_data();
-       for (const shared_ptr<SignalData>& d : visible_data) {
+
+       vector< shared_ptr<SignalData> > data;
+       if (signals_.size() == 0)
+               return make_pair(0, 0);
+
+       for (const shared_ptr<Signal>& s : signals_)
+               if (s->base()->data() && (s->base()->data()->segments().size() > 0))
+                       data.push_back(s->base()->data());
+
+       for (const shared_ptr<SignalData>& d : data) {
                const vector< shared_ptr<Segment> > segments = d->segments();
                for (const shared_ptr<Segment>& s : segments) {
                        double samplerate = s->samplerate();
@@ -841,7 +956,7 @@ pair<Timestamp, Timestamp> View::get_time_extents() const
        if (!left_time || !right_time)
                return make_pair(0, 0);
 
-       assert(*left_time < *right_time);
+       assert(*left_time <= *right_time);
        return make_pair(*left_time, *right_time);
 }
 
@@ -1088,15 +1203,8 @@ void View::trigger_event(int segment_id, util::Timestamp location)
        if ((uint32_t)segment_id != current_segment_)
                return;
 
-       // Set zero location if the Key_View_TriggerIsZeroTime setting is set and
-       // if this is the first trigger for this segment.
-       GlobalSettings settings;
-       bool trigger_is_zero_time = settings.value(GlobalSettings::Key_View_TriggerIsZeroTime).toBool();
-
-       size_t trigger_count = session_.get_triggers(current_segment_).size();
-
-       if (trigger_is_zero_time && trigger_count == 1)
-               set_zero_position(location);
+       if (!custom_zero_offset_set_)
+               reset_zero_position();
 
        trigger_markers_.push_back(make_shared<TriggerMarker>(*this, location));
 }
@@ -1251,9 +1359,13 @@ void View::update_scroll()
        const pair<int, int> extents = v_extents();
 
        // Don't change the scrollbar range if there are no traces
-       if (extents.first != extents.second)
-               vscrollbar->setRange(extents.first - areaSize.height(),
-                       extents.second);
+       if (extents.first != extents.second) {
+               int top_margin = ViewScrollMargin;
+               int btm_margin = ViewScrollMargin;
+
+               vscrollbar->setRange(extents.first - areaSize.height() + top_margin,
+                       extents.second - btm_margin);
+       }
 
        if (scroll_needs_defaults_) {
                set_scroll_default();
@@ -1320,16 +1432,17 @@ void View::resize_header_to_fit()
 void View::update_layout()
 {
        update_scroll();
+
+       update_view_range_metaobject();
 }
 
 TraceTreeItemOwner* View::find_prevalent_trace_group(
        const shared_ptr<sigrok::ChannelGroup> &group,
-       const unordered_map<shared_ptr<data::SignalBase>, shared_ptr<Signal> >
-               &signal_map)
+       const map<shared_ptr<data::SignalBase>, shared_ptr<Signal> > &signal_map)
 {
        assert(group);
 
-       unordered_set<TraceTreeItemOwner*> owners;
+       set<TraceTreeItemOwner*> owners;
        vector<TraceTreeItemOwner*> owner_list;
 
        // Make a set and a list of all the owners
@@ -1361,8 +1474,7 @@ TraceTreeItemOwner* View::find_prevalent_trace_group(
 
 vector< shared_ptr<Trace> > View::extract_new_traces_for_channels(
        const vector< shared_ptr<sigrok::Channel> > &channels,
-       const unordered_map<shared_ptr<data::SignalBase>, shared_ptr<Signal> >
-               &signal_map,
+       const map<shared_ptr<data::SignalBase>, shared_ptr<Signal> > &signal_map,
        set< shared_ptr<Trace> > &add_list)
 {
        vector< shared_ptr<Trace> > filtered_traces;
@@ -1390,7 +1502,7 @@ void View::determine_time_unit()
        if (time_unit_ == util::TimeUnit::Samples) {
                // Check all signals but...
                for (const shared_ptr<Signal>& signal : signals_) {
-                       const shared_ptr<SignalData> data = signal->data();
+                       const shared_ptr<SignalData> data = signal->base()->data();
 
                        // ...only check first segment of each
                        const vector< shared_ptr<Segment> > segments = data->segments();
@@ -1418,7 +1530,11 @@ bool View::eventFilter(QObject *object, QEvent *event)
                else if (object == ruler_)
                        hover_point_ = mouse_event->pos();
                else if (object == header_)
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+                       hover_point_ = QPoint(0, mouse_event->pos().y());
+#else
                        hover_point_ = QPoint(0, mouse_event->y());
+#endif
                else
                        hover_point_ = QPoint(-1, -1);
 
@@ -1460,7 +1576,7 @@ bool View::eventFilter(QObject *object, QEvent *event)
                // resized to their final sizes.
                update_layout();
 
-               if (settings_restored_)
+               if (restoring_state_)
                        determine_if_header_was_shrunk();
                else
                        resize_header_to_fit();
@@ -1470,10 +1586,8 @@ bool View::eventFilter(QObject *object, QEvent *event)
                        scroll_needs_defaults_ = false;
                }
 
-               if (saved_v_offset_) {
+               if (restoring_state_)
                        set_v_offset(saved_v_offset_);
-                       saved_v_offset_ = 0;
-               }
        }
 
        return QObject::eventFilter(object, event);
@@ -1484,10 +1598,22 @@ void View::contextMenuEvent(QContextMenuEvent *event)
        QPoint pos = event->pos() - QPoint(0, ruler_->sizeHint().height());
 
        const shared_ptr<ViewItem> r = viewport_->get_mouse_over_item(pos);
-       if (!r)
-               return;
 
-       QMenu *menu = r->create_view_context_menu(this, pos);
+       QMenu* menu = nullptr;
+
+       if (!r) {
+               context_menu_x_pos_ = pos.x();
+
+               // No view item under cursor, use generic menu
+               menu = new QMenu(this);
+
+               QAction *const create_marker_here = new QAction(tr("Create marker here"), this);
+               connect(create_marker_here, SIGNAL(triggered()), this, SLOT(on_create_marker_here()));
+               menu->addAction(create_marker_here);
+       } else {
+               menu = r->create_view_context_menu(this, pos);
+       }
+
        if (menu)
                menu->popup(event->globalPos());
 }
@@ -1501,6 +1627,31 @@ void View::resizeEvent(QResizeEvent* event)
        update_layout();
 }
 
+void View::update_view_range_metaobject() const
+{
+       const int w = viewport_->width();
+       if (w > 0) {
+               const double samplerate = session_.get_samplerate();
+               // Note: sample_num = time * samplerate
+               // Note: samples_per_pixel = samplerate * scale
+               const int64_t start_sample = (offset_ * samplerate).convert_to<int64_t>();
+               const int64_t end_sample = (offset_ * samplerate).convert_to<int64_t>() +
+                       (w * session_.get_samplerate() * scale_);
+
+               MetadataObject* md_obj =
+                       session_.metadata_obj_manager()->find_object_by_type(MetadataObjMainViewRange);
+
+               const int64_t old_start_sample = md_obj->value(MetadataValueStartSample).toLongLong();
+               const int64_t old_end_sample = md_obj->value(MetadataValueEndSample).toLongLong();
+
+               if (start_sample != old_start_sample)
+                       md_obj->set_value(MetadataValueStartSample, QVariant((qlonglong)start_sample));
+
+               if (end_sample != old_end_sample)
+                       md_obj->set_value(MetadataValueEndSample, QVariant((qlonglong)end_sample));
+       }
+}
+
 void View::update_hover_point()
 {
        // Determine signal that the mouse cursor is hovering over
@@ -1522,8 +1673,19 @@ void View::update_hover_point()
        for (const shared_ptr<TraceTreeItem>& r : trace_tree_items)
                r->hover_point_changed(hover_point_);
 
-       // Notify any other listeners
+       // Notify this view's listeners
        hover_point_changed(hover_widget_, hover_point_);
+
+       // Hover point is -1 when invalid and 0 for the header
+       if (hover_point_.x() > 0) {
+               // Notify global listeners
+               pv::util::Timestamp mouse_time = offset_ + hover_point_.x() * scale_;
+               int64_t sample_num = (mouse_time * session_.get_samplerate()).convert_to<int64_t>();
+
+               MetadataObject* md_obj =
+                       session_.metadata_obj_manager()->find_object_by_type(MetadataObjMousePos);
+               md_obj->set_value(MetadataValueStartSample, QVariant((qlonglong)sample_num));
+       }
 }
 
 void View::row_item_appearance_changed(bool label, bool content)
@@ -1553,8 +1715,8 @@ void View::extents_changed(bool horz, bool vert)
                (horz ? TraceTreeItemHExtentsChanged : 0) |
                (vert ? TraceTreeItemVExtentsChanged : 0);
 
-       lazy_event_handler_.stop();
-       lazy_event_handler_.start();
+       if (!lazy_event_handler_.isActive())
+               lazy_event_handler_.start();
 }
 
 void View::on_signal_name_changed()
@@ -1648,6 +1810,8 @@ void View::signals_changed()
 {
        using sigrok::Channel;
 
+       lock_guard<mutex> lock(signal_mutex_);
+
        vector< shared_ptr<Channel> > channels;
        shared_ptr<sigrok::Device> sr_dev;
        bool signals_added_or_removed = false;
@@ -1670,7 +1834,9 @@ void View::signals_changed()
        vector< shared_ptr<TraceTreeItem> > new_top_level_items;
 
        // Make a list of traces that are being added, and a list of traces
-       // that are being removed
+       // that are being removed. The set_difference() algorithms require
+       // both sets to be in the exact same order, which means that PD signals
+       // must always be last as they interrupt the sort order otherwise
        const vector<shared_ptr<Trace>> prev_trace_list = list_by_type<Trace>();
        const set<shared_ptr<Trace>> prev_traces(
                prev_trace_list.begin(), prev_trace_list.end());
@@ -1680,7 +1846,6 @@ void View::signals_changed()
 #ifdef ENABLE_DECODE
        traces.insert(decode_traces_.begin(), decode_traces_.end());
 #endif
-
        set< shared_ptr<Trace> > add_traces;
        set_difference(traces.begin(), traces.end(),
                prev_traces.begin(), prev_traces.end(),
@@ -1692,8 +1857,7 @@ void View::signals_changed()
                inserter(remove_traces, remove_traces.begin()));
 
        // Make a look-up table of sigrok Channels to pulseview Signals
-       unordered_map<shared_ptr<data::SignalBase>, shared_ptr<Signal> >
-               signal_map;
+       map<shared_ptr<data::SignalBase>, shared_ptr<Signal> > signal_map;
        for (const shared_ptr<Signal>& sig : signals_)
                signal_map[sig->base()] = sig;
 
@@ -1794,7 +1958,11 @@ void View::signals_changed()
 
        // Add and position the pending top levels items
        int offset = v_extents().second;
-       for (auto item : new_top_level_items) {
+       for (shared_ptr<TraceTreeItem> item : new_top_level_items) {
+               // items may already have gained an owner when they were added to a group above
+               if (item->owner())
+                       continue;
+
                add_child_item(item);
 
                // Position the item after the last item or at the top if there is none
@@ -1831,7 +1999,8 @@ void View::capture_state_updated(int state)
                set_time_unit(util::TimeUnit::Samples);
 
                trigger_markers_.clear();
-               set_zero_position(0);
+               if (!custom_zero_offset_set_)
+                       set_zero_position(0);
 
                scale_at_acq_start_ = scale_;
                offset_at_acq_start_ = offset_;
@@ -1840,13 +2009,14 @@ void View::capture_state_updated(int state)
                // the main view of this session (other trace views may be used for
                // zooming and we don't want to mess them up)
                bool state = settings.value(GlobalSettings::Key_View_ZoomToFitDuringAcq).toBool();
-               if (is_main_view_ && state) {
+               if (is_main_view_ && state && !restoring_state_) {
                        always_zoom_to_fit_ = true;
                        always_zoom_to_fit_changed(always_zoom_to_fit_);
                }
 
                // Enable sticky scrolling if the setting is enabled
-               sticky_scrolling_ = settings.value(GlobalSettings::Key_View_StickyScrolling).toBool();
+               sticky_scrolling_ = !restoring_state_ &&
+                       settings.value(GlobalSettings::Key_View_StickyScrolling).toBool();
 
                // Reset all traces to segment 0
                current_segment_ = 0;
@@ -1872,12 +2042,13 @@ void View::capture_state_updated(int state)
                // Only perform zoom-to-fit if the user hasn't altered the viewport and
                // we didn't restore settings in the meanwhile
                if (zoom_to_fit_after_acq &&
-                       !suppress_zoom_to_fit_after_acq_ &&
+                       !restoring_state_ &&
                        (scale_ == scale_at_acq_start_) &&
-                       (offset_ == offset_at_acq_start_))
+                       (sticky_scrolling_ || (offset_ == offset_at_acq_start_))) {
                        zoom_fit(false);  // We're stopped, so the GUI state doesn't matter
+               }
 
-               suppress_zoom_to_fit_after_acq_ = false;
+               restoring_state_ = false;
        }
 }
 
@@ -1912,14 +2083,18 @@ void View::on_segment_changed(int segment)
        }
 }
 
+void View::on_create_marker_here()
+{
+       const QPoint p = ruler_->mapFrom(this, QPoint(context_menu_x_pos_, 0));
+
+       add_flag(ruler_->get_absolute_time_from_x_pos(p.x()));
+}
+
 void View::on_settingViewTriggerIsZeroTime_changed(const QVariant new_value)
 {
-       if (new_value.toBool()) {
-               // The first trigger for this segment is used as the zero position
-               vector<util::Timestamp> triggers = session_.get_triggers(current_segment_);
-               if (triggers.size() > 0)
-                       set_zero_position(triggers.front());
-       } else
+       (void)new_value;
+
+       if (!custom_zero_offset_set_)
                reset_zero_position();
 }
 
index 2938d00fc0014296712adecc921ada3f2e5511d2..7fef8419e625232b7013eb4e4e84a7bbcae60af0 100644 (file)
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEW_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEW_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_VIEW_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_VIEW_HPP
 
 #include <cstdint>
 #include <list>
 #include <memory>
+#include <mutex>
 #include <set>
-#include <unordered_map>
 #include <vector>
 
 #include <QAbstractScrollArea>
@@ -43,8 +43,7 @@
 #include "tracetreeitemowner.hpp"
 
 using std::list;
-using std::unordered_map;
-using std::unordered_set;
+using std::map;
 using std::set;
 using std::shared_ptr;
 using std::vector;
@@ -96,6 +95,7 @@ private:
        static const pv::util::Timestamp MinScale;
 
        static const int MaxScrollValue;
+       static const int ViewScrollMargin;
 
        static const int ScaleUnits[3];
 
@@ -118,13 +118,13 @@ public:
        /**
         * Returns the signals contained in this view.
         */
-       unordered_set< shared_ptr<Signal> > signals() const;
+       vector< shared_ptr<Signal> > signals() const;
 
        shared_ptr<Signal> get_signal_by_signalbase(shared_ptr<data::SignalBase> base) const;
 
-       virtual void clear_signals();
-
-       void add_signal(const shared_ptr<Signal> signal);
+       virtual void clear_signalbases();
+       virtual void add_signalbase(const shared_ptr<data::SignalBase> signalbase);
+       virtual void remove_signalbase(const shared_ptr<data::SignalBase> signalbase);
 
 #ifdef ENABLE_DECODE
        virtual void clear_decode_signals();
@@ -134,6 +134,8 @@ public:
        virtual void remove_decode_signal(shared_ptr<data::DecodeSignal> signal);
 #endif
 
+       void remove_trace(shared_ptr<Trace> trace);
+
        shared_ptr<Signal> get_signal_under_mouse_cursor() const;
 
        /**
@@ -154,7 +156,6 @@ public:
        const Ruler* ruler() const;
 
        virtual void save_settings(QSettings &settings) const;
-
        virtual void restore_settings(QSettings &settings);
 
        /**
@@ -254,6 +255,8 @@ public:
 
        void zoom_fit(bool gui_state);
 
+       virtual void focus_on_range(uint64_t start_sample, uint64_t end_sample);
+
        /**
         * Sets the scale and offset.
         * @param scale The new view scale in seconds per pixel.
@@ -261,7 +264,7 @@ public:
         */
        void set_scale_offset(double scale, const pv::util::Timestamp& offset);
 
-       set< shared_ptr<pv::data::SignalData> > get_visible_data() const;
+       vector< shared_ptr<pv::data::SignalData> > get_visible_data() const;
 
        pair<pv::util::Timestamp, pv::util::Timestamp> get_time_extents() const;
 
@@ -390,9 +393,7 @@ private:
        void adjust_top_margin();
 
        void update_scroll();
-
        void reset_scroll();
-
        void set_scroll_default();
 
        void determine_if_header_was_shrunk();
@@ -403,14 +404,12 @@ private:
 
        TraceTreeItemOwner* find_prevalent_trace_group(
                const shared_ptr<sigrok::ChannelGroup> &group,
-               const unordered_map<shared_ptr<data::SignalBase>,
-                       shared_ptr<Signal> > &signal_map);
+               const map<shared_ptr<data::SignalBase>, shared_ptr<Signal> > &signal_map);
 
        static vector< shared_ptr<Trace> >
                extract_new_traces_for_channels(
                const vector< shared_ptr<sigrok::Channel> > &channels,
-               const unordered_map<shared_ptr<data::SignalBase>,
-                       shared_ptr<Signal> > &signal_map,
+               const map<shared_ptr<data::SignalBase>, shared_ptr<Signal> > &signal_map,
                set< shared_ptr<Trace> > &add_list);
 
        void determine_time_unit();
@@ -421,6 +420,7 @@ private:
 
        void resizeEvent(QResizeEvent *event);
 
+       void update_view_range_metaobject() const;
        void update_hover_point();
 
 public:
@@ -452,6 +452,8 @@ private Q_SLOTS:
 
        void on_settingViewTriggerIsZeroTime_changed(const QVariant new_value);
 
+       void on_create_marker_here();
+
        virtual void perform_delayed_view_update();
 
        void process_sticky_events();
@@ -510,7 +512,8 @@ private:
        QShortcut *grab_ruler_left_shortcut_, *grab_ruler_right_shortcut_;
        QShortcut *cancel_grab_shortcut_;
 
-       unordered_set< shared_ptr<Signal> > signals_;
+       mutable mutex signal_mutex_;
+       vector< shared_ptr<Signal> > signals_;
 
 #ifdef ENABLE_DECODE
        vector< shared_ptr<DecodeTrace> > decode_traces_;
@@ -530,9 +533,11 @@ private:
        pv::util::Timestamp ruler_offset_;
        /// The offset of the zero point in seconds.
        pv::util::Timestamp zero_offset_;
+       /// Shows whether the user set a custom zero offset that we should keep
+       bool custom_zero_offset_set_;
 
        bool updating_scroll_;
-       bool settings_restored_;
+       bool restoring_state_;
        bool header_was_shrunk_;
 
        bool sticky_scrolling_;
@@ -565,22 +570,19 @@ private:
        // This is true when the defaults couldn't be set due to insufficient info
        bool scroll_needs_defaults_;
 
-       // A nonzero value indicates the v offset to restore. See View::resizeEvent()
+       // The v offset to restore. See View::eventFilter()
        int saved_v_offset_;
 
        // These are used to determine whether the view was altered after acq started
        double scale_at_acq_start_;
        pv::util::Timestamp offset_at_acq_start_;
 
-       // Used to suppress performing a "zoom to fit" when the session stops. This
-       // is needed when the view's settings are restored before acquisition ends.
-       // In that case we want to keep the restored settings, not have a "zoom to fit"
-       // mess them up.
-       bool suppress_zoom_to_fit_after_acq_;
+       // X coordinate of mouse cursor where the user clicked to open a context menu
+       uint32_t context_menu_x_pos_;
 };
 
 } // namespace trace
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEW_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_VIEW_HPP
index 423e75d2f313e38ec7cd156831c1119edaea47f8..77c8665e17522a3d08c3e07f631b866078f21ed6 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWITEM_HPP
-#define PULSEVIEW_PV_VIEWITEM_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_VIEWITEM_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_VIEWITEM_HPP
 
 #include <list>
 
@@ -191,4 +191,4 @@ private:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWITEM_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_VIEWITEM_HPP
index 88600d08982660f2eb4ee99f52c59f5590228a91..f94efa90964ef376ca383a8741573a6bc8685015 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMITERATOR_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMITERATOR_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_VIEWITEMITERATOR_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_VIEWITEMITERATOR_HPP
 
 #include <algorithm>
 #include <cassert>
@@ -129,4 +129,4 @@ void swap(ViewItemIterator<Owner, Item>& a, ViewItemIterator<Owner, Item>& b)
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMITERATOR_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_VIEWITEMITERATOR_HPP
index 29f74b5aff7ddcdfb47cf2fad387ee98e2b11b55..9f4143cb366c973a6e5770c24bd271a0084f2c98 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMOWNER_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMOWNER_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_VIEWITEMOWNER_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_VIEWITEMOWNER_HPP
 
 #include <memory>
 #include <vector>
@@ -98,4 +98,4 @@ protected:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMOWNER_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_VIEWITEMOWNER_HPP
index 725a4336bbcb4944d3ca09992ba9da293523ee6c..e52b92f18e3b020ce0a82405aea2f94a4e8bd570 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMPAINTPARAMS_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMPAINTPARAMS_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_VIEWITEMPAINTPARAMS_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_VIEWITEMPAINTPARAMS_HPP
 
 #include "pv/util.hpp"
 
@@ -97,4 +97,4 @@ private:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWITEMPAINTPARAMS_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_VIEWITEMPAINTPARAMS_HPP
index 0c73ec494adfbc72257252c59ead85d609a6ad24..e2a32aa20bdd4ec41dc6460b30b3f72a8e46f894 100644 (file)
@@ -54,6 +54,17 @@ Viewport::Viewport(View &parent) :
 {
        setAutoFillBackground(true);
        setBackgroundRole(QPalette::Base);
+
+       // Set up settings and event handlers
+       GlobalSettings settings;
+       allow_vertical_dragging_ = settings.value(GlobalSettings::Key_View_AllowVerticalDragging).toBool();
+
+       GlobalSettings::add_change_handler(this);
+}
+
+Viewport::~Viewport()
+{
+       GlobalSettings::remove_change_handler(this);
 }
 
 shared_ptr<ViewItem> Viewport::get_mouse_over_item(const QPoint &pt)
@@ -78,7 +89,9 @@ void Viewport::item_hover(const shared_ptr<ViewItem> &item, QPoint pos)
 void Viewport::drag()
 {
        drag_offset_ = view_.offset();
-       drag_v_offset_ = view_.owner_visual_v_offset();
+
+       if (allow_vertical_dragging_)
+               drag_v_offset_ = view_.owner_visual_v_offset();
 }
 
 void Viewport::drag_by(const QPoint &delta)
@@ -89,7 +102,8 @@ void Viewport::drag_by(const QPoint &delta)
        view_.set_scale_offset(view_.scale(),
                (*drag_offset_ - delta.x() * view_.scale()));
 
-       view_.set_v_offset(-drag_v_offset_ - delta.y());
+       if (allow_vertical_dragging_)
+               view_.set_v_offset(-drag_v_offset_ - delta.y());
 }
 
 void Viewport::drag_release()
@@ -110,12 +124,40 @@ vector< shared_ptr<ViewItem> > Viewport::items()
 
 bool Viewport::touch_event(QTouchEvent *event)
 {
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       QList<QEventPoint> touchPoints = event->points();
+#else
        QList<QTouchEvent::TouchPoint> touchPoints = event->touchPoints();
+#endif
 
        if (touchPoints.count() != 2) {
                pinch_zoom_active_ = false;
                return false;
        }
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       if (event->device()->type() == QInputDevice::DeviceType::TouchPad) {
+               return false;
+       }
+
+       const QEventPoint &touchPoint0 = touchPoints.first();
+       const QEventPoint &touchPoint1 = touchPoints.last();
+
+       if (!pinch_zoom_active_ ||
+               (event->touchPointStates() & QEventPoint::Pressed)) {
+               pinch_offset0_ = (view_.offset() + view_.scale() * touchPoint0.position().x()).convert_to<double>();
+               pinch_offset1_ = (view_.offset() + view_.scale() * touchPoint1.position().x()).convert_to<double>();
+               pinch_zoom_active_ = true;
+       }
+
+       double w = touchPoint1.position().x() - touchPoint0.position().x();
+       if (abs(w) >= 1.0) {
+               const double scale =
+                       fabs((pinch_offset1_ - pinch_offset0_) / w);
+               double offset = pinch_offset0_ - touchPoint0.position().x() * scale;
+               if (scale > 0)
+                       view_.set_scale_offset(scale, offset);
+       }
+#else
        if (event->device()->type() == QTouchDevice::TouchPad) {
                return false;
        }
@@ -138,6 +180,7 @@ bool Viewport::touch_event(QTouchEvent *event)
                if (scale > 0)
                        view_.set_scale_offset(scale, offset);
        }
+#endif
 
        if (event->touchPointStates() & Qt::TouchPointReleased) {
                pinch_zoom_active_ = false;
@@ -148,7 +191,11 @@ bool Viewport::touch_event(QTouchEvent *event)
                } else {
                        // Update the mouse down fields so that continued
                        // dragging with the primary touch will work correctly
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+                       mouse_down_point_ = touchPoint0.position().toPoint();
+#else
                        mouse_down_point_ = touchPoint0.pos().toPoint();
+#endif
                        drag();
                }
        }
@@ -201,33 +248,70 @@ void Viewport::mouseDoubleClickEvent(QMouseEvent *event)
 {
        assert(event);
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       if (event->buttons() & Qt::LeftButton)
+               view_.zoom(2.0, event->position().x());
+       else if (event->buttons() & Qt::RightButton)
+               view_.zoom(-2.0, event->position().x());
+#else
        if (event->buttons() & Qt::LeftButton)
                view_.zoom(2.0, event->x());
        else if (event->buttons() & Qt::RightButton)
                view_.zoom(-2.0, event->x());
+#endif
 }
 
 void Viewport::wheelEvent(QWheelEvent *event)
 {
        assert(event);
 
+#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
+       int delta = (event->angleDelta().x() != 0) ? event->angleDelta().x() : event->angleDelta().y();
+#else
+       int delta = event->delta();
+#endif
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
+       if (event->angleDelta().y() != 0) {
+#else
        if (event->orientation() == Qt::Vertical) {
+#endif
                if (event->modifiers() & Qt::ControlModifier) {
                        // Vertical scrolling with the control key pressed
                        // is intrepretted as vertical scrolling
                        view_.set_v_offset(-view_.owner_visual_v_offset() -
-                               (event->delta() * height()) / (8 * 120));
+                               (delta * height()) / (8 * 120));
+               } else if (event->modifiers() & Qt::ShiftModifier) {
+                       // Vertical scrolling with the shift key pressed
+                       // acts as horizontal scrolling like in Gimp
+                       // and Inkscape.
+                       view_.set_scale_offset(view_.scale(),
+                               - delta * view_.scale() + view_.offset());
                } else {
                        // Vertical scrolling is interpreted as zooming in/out
-                       view_.zoom(event->delta() / 120.0, event->x());
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+                       view_.zoom(delta / 120.0, event->position().x());
+#else
+                       view_.zoom(delta / 120.0, event->x());
+#endif
                }
+#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
+       } else if (event->angleDelta().x() != 0) {
+#else
        } else if (event->orientation() == Qt::Horizontal) {
+#endif
                // Horizontal scrolling is interpreted as moving left/right
                view_.set_scale_offset(view_.scale(),
-                       event->delta() * view_.scale() + view_.offset());
+                       delta * view_.scale() + view_.offset());
        }
 }
 
+void Viewport::on_setting_changed(const QString &key, const QVariant &value)
+{
+       if (key == GlobalSettings::Key_View_AllowVerticalDragging)
+               allow_vertical_dragging_ = value.toBool();
+}
+
 } // namespace trace
 } // namespace views
 } // namespace pv
index 1b77a0d0699e7264170f869f39097216d923103b..8c82354ea8dcfb6f2e75ddeb8a34b35932087ef5 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWPORT_HPP
-#define PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWPORT_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_VIEWPORT_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_VIEWPORT_HPP
 
 #include <boost/optional.hpp>
 
@@ -26,6 +26,8 @@
 #include <QTimer>
 #include <QTouchEvent>
 
+#include <pv/globalsettings.hpp>
+
 #include "pv/util.hpp"
 #include "viewwidget.hpp"
 
@@ -42,12 +44,13 @@ namespace trace {
 
 class View;
 
-class Viewport : public ViewWidget
+class Viewport : public ViewWidget, public GlobalSettingsInterface
 {
        Q_OBJECT
 
 public:
        explicit Viewport(View &parent);
+       ~Viewport();
 
        /**
         * Gets the first view item which has a hit-box that contains @c pt .
@@ -92,15 +95,18 @@ private:
         */
        bool touch_event(QTouchEvent *event);
 
-private:
        void paintEvent(QPaintEvent *event);
 
        void mouseDoubleClickEvent(QMouseEvent *event);
+
        void wheelEvent(QWheelEvent *event);
 
+       void on_setting_changed(const QString &key, const QVariant &value);
+
 private:
        boost::optional<pv::util::Timestamp> drag_offset_;
        int drag_v_offset_;
+       bool allow_vertical_dragging_;
 
        double pinch_offset0_;
        double pinch_offset1_;
@@ -111,4 +117,4 @@ private:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWS_TRACEVIEW_VIEWPORT_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_VIEWPORT_HPP
index 427a8994e60cbeadf91995ce5fc6c3bfda76137e..ef3cfa2fc303d9692d84e596d486191888cda143 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_VIEWWIDGET_HPP
-#define PULSEVIEW_PV_VIEWWIDGET_HPP
+#ifndef PULSEVIEW_PV_VIEWS_TRACE_VIEWWIDGET_HPP
+#define PULSEVIEW_PV_VIEWS_TRACE_VIEWWIDGET_HPP
 
 #include <memory>
 
@@ -161,4 +161,4 @@ protected:
 } // namespace views
 } // namespace pv
 
-#endif // PULSEVIEW_PV_VIEWWIDGET_HPP
+#endif // PULSEVIEW_PV_VIEWS_TRACE_VIEWWIDGET_HPP
index ecdefdcde294a68f188336f8b47180155057ab9e..ff3a4fdbbb3e30be432667338dba559f0806e0bd 100644 (file)
@@ -36,7 +36,8 @@ namespace views {
 const char* ViewTypeNames[ViewTypeCount] = {
        "Trace View",
 #ifdef ENABLE_DECODE
-       "Decoder Output View"
+       "Binary Decoder Output View",
+       "Tabular Decoder Output View"
 #endif
 };
 
@@ -83,12 +84,7 @@ const Session& ViewBase::session() const
        return session_;
 }
 
-void ViewBase::clear_signals()
-{
-       clear_signalbases();
-}
-
-unordered_set< shared_ptr<data::SignalBase> > ViewBase::signalbases() const
+vector< shared_ptr<data::SignalBase> > ViewBase::signalbases() const
 {
        return signalbases_;
 }
@@ -107,7 +103,7 @@ void ViewBase::clear_signalbases()
 
 void ViewBase::add_signalbase(const shared_ptr<data::SignalBase> signalbase)
 {
-       signalbases_.insert(signalbase);
+       signalbases_.push_back(signalbase);
 
        connect(signalbase.get(), SIGNAL(samples_cleared()),
                this, SLOT(on_data_updated()));
@@ -115,6 +111,18 @@ void ViewBase::add_signalbase(const shared_ptr<data::SignalBase> signalbase)
                this, SLOT(on_samples_added(uint64_t, uint64_t, uint64_t)));
 }
 
+void ViewBase::remove_signalbase(const shared_ptr<data::SignalBase> signalbase)
+{
+       disconnect(signalbase.get(), SIGNAL(samples_cleared()),
+               this, SLOT(on_data_updated()));
+       disconnect(signalbase.get(), SIGNAL(samples_added(uint64_t, uint64_t, uint64_t)),
+               this, SLOT(on_samples_added(uint64_t, uint64_t, uint64_t)));
+
+       signalbases_.erase(std::remove_if(signalbases_.begin(), signalbases_.end(),
+               [&](shared_ptr<data::SignalBase> s) { return s == signalbase; }),
+               signalbases_.end());
+}
+
 #ifdef ENABLE_DECODE
 void ViewBase::clear_decode_signals()
 {
@@ -123,12 +131,15 @@ void ViewBase::clear_decode_signals()
 
 void ViewBase::add_decode_signal(shared_ptr<data::DecodeSignal> signal)
 {
-       decode_signals_.insert(signal);
+       decode_signals_.push_back(signal);
 }
 
 void ViewBase::remove_decode_signal(shared_ptr<data::DecodeSignal> signal)
 {
-       decode_signals_.erase(signal);
+       decode_signals_.erase(std::remove_if(
+               decode_signals_.begin(), decode_signals_.end(),
+               [&](shared_ptr<data::DecodeSignal> s) { return s == signal; }),
+               decode_signals_.end());
 }
 #endif
 
@@ -142,6 +153,12 @@ void ViewBase::restore_settings(QSettings &settings)
        (void)settings;
 }
 
+void ViewBase::focus_on_range(uint64_t start_sample, uint64_t end_sample)
+{
+       (void)start_sample;
+       (void)end_sample;
+}
+
 void ViewBase::trigger_event(int segment_id, util::Timestamp location)
 {
        (void)segment_id;
index d3111dc79cdfbc5929f260d8f0181a2238f592a9..972b04ea6ed4cf8d99e3f9b83d192175ceea8269 100644 (file)
@@ -38,7 +38,7 @@
 #endif
 
 using std::shared_ptr;
-using std::unordered_set;
+using std::vector;
 
 namespace pv {
 
@@ -55,7 +55,8 @@ namespace views {
 enum ViewType {
        ViewTypeTrace,
 #ifdef ENABLE_DECODE
-       ViewTypeDecoderOutput,
+       ViewTypeDecoderBinary,
+       ViewTypeTabularDecoder,
 #endif
        ViewTypeCount  // Indicates how many view types there are, must always be last
 };
@@ -84,29 +85,26 @@ public:
        Session& session();
        const Session& session() const;
 
-       virtual void clear_signals();
-
        /**
         * Returns the signal bases contained in this view.
         */
-       unordered_set< shared_ptr<data::SignalBase> > signalbases() const;
+       vector< shared_ptr<data::SignalBase> > signalbases() const;
 
        virtual void clear_signalbases();
-
        virtual void add_signalbase(const shared_ptr<data::SignalBase> signalbase);
+       virtual void remove_signalbase(const shared_ptr<data::SignalBase> signalbase);
 
 #ifdef ENABLE_DECODE
        virtual void clear_decode_signals();
-
        virtual void add_decode_signal(shared_ptr<data::DecodeSignal> signal);
-
        virtual void remove_decode_signal(shared_ptr<data::DecodeSignal> signal);
 #endif
 
        virtual void save_settings(QSettings &settings) const;
-
        virtual void restore_settings(QSettings &settings);
 
+       virtual void focus_on_range(uint64_t start_sample, uint64_t end_sample);
+
 public Q_SLOTS:
        virtual void trigger_event(int segment_id, util::Timestamp location);
        virtual void signals_changed();
@@ -128,9 +126,9 @@ protected:
 
        util::TimeUnit time_unit_;
 
-       unordered_set< shared_ptr<data::SignalBase> > signalbases_;
+       vector< shared_ptr<data::SignalBase> > signalbases_;
 #ifdef ENABLE_DECODE
-       unordered_set< shared_ptr<data::DecodeSignal> > decode_signals_;
+       vector< shared_ptr<data::DecodeSignal> > decode_signals_;
 #endif
 
        /// The ID of the currently displayed segment
index 505a1f67e9e4179c756f2ae33f148cf793686738..5cf99e5212690ee18e18b2fe08195d94db7d7041 100644 (file)
@@ -51,13 +51,17 @@ DecoderMenu::DecoderMenu(QWidget *parent, const char* input, bool first_level_de
                }
 
                QAction *const action = addAction(QString::fromUtf8(d->name));
-               action->setData(qVariantFromValue(l->data));
+               action->setData(QVariant::fromValue(l->data));
                mapper_.setMapping(action, action);
                connect(action, SIGNAL(triggered()), &mapper_, SLOT(map()));
        }
        g_slist_free(li);
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(&mapper_, SIGNAL(mappedObject(QObject*)), this, SLOT(on_action(QObject*)));
+#else
        connect(&mapper_, SIGNAL(mapped(QObject*)), this, SLOT(on_action(QObject*)));
+#endif
 }
 
 int DecoderMenu::decoder_name_cmp(const void *a, const void *b)
index 6a12aa25c446904651b60bfe6de58e13d27eaa12..2e76f689a61f448b068e149e2c1886a5f67992b8 100644 (file)
@@ -55,8 +55,13 @@ DeviceToolButton::DeviceToolButton(QWidget *parent,
        setDefaultAction(connect_action_);
        setMinimumWidth(QFontMetrics(font()).averageCharWidth() * 24);
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(&mapper_, SIGNAL(mappedObject(QObject*)),
+               this, SLOT(on_action(QObject*)));
+#else
        connect(&mapper_, SIGNAL(mapped(QObject*)),
                this, SLOT(on_action(QObject*)));
+#endif
 
        connect(&menu_, SIGNAL(hovered(QAction*)),
                this, SLOT(on_menu_hovered(QAction*)));
@@ -100,7 +105,7 @@ void DeviceToolButton::update_device_list()
                        dev->display_name(device_manager_)), this);
                a->setCheckable(true);
                a->setChecked(selected_device_ == dev);
-               a->setData(qVariantFromValue((void*)dev.get()));
+               a->setData(QVariant::fromValue((void*)dev.get()));
                a->setToolTip(QString::fromStdString(dev->full_name()));
                mapper_.setMapping(a, a);
 
index ff82b6aa2b13e206fba69e94dc5cf9fca3a4ed76..bda669288f3731b9f3706fd9603de490eaee25a6 100644 (file)
@@ -63,20 +63,25 @@ ExportMenu::ExportMenu(QWidget *parent, shared_ptr<Context> context,
        const map<string, shared_ptr<OutputFormat> > formats =
                context->output_formats();
 
-       for (const pair<string, shared_ptr<OutputFormat> > &f : formats) {
+       for (const pair<const string, shared_ptr<OutputFormat> > &f : formats) {
                if (f.first == "srzip")
                        continue;
 
                assert(f.second);
                QAction *const action = addAction(tr("Export %1...")
                        .arg(QString::fromStdString(f.second->description())));
-               action->setData(qVariantFromValue((void*)f.second.get()));
+               action->setData(QVariant::fromValue((void*)f.second.get()));
                mapper_.setMapping(action, action);
                connect(action, SIGNAL(triggered()), &mapper_, SLOT(map()));
        }
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(&mapper_, SIGNAL(mappedObject(QObject*)),
+               this, SLOT(on_action(QObject*)));
+#else
        connect(&mapper_, SIGNAL(mapped(QObject*)),
                this, SLOT(on_action(QObject*)));
+#endif
 }
 
 void ExportMenu::on_action(QObject *action)
index efd862f97b1fb9581307fe92e920d9903b50f2dc..73916700253087617a0539a44e6caa6579043840 100644 (file)
@@ -146,7 +146,13 @@ QSize FlowLayout::minimumSize() const
                        size.setHeight(h);
        }
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       int left, top, right, bottom;
+       getContentsMargins(&left, &top, &right, &bottom);
+       size += QSize(left + right, top + bottom);
+#else
        size += QSize(2 * margin(), 2 * margin());
+#endif
 
        return size;
 }
index 81407d1b40972e7eb360d82def71c64d7108758a..c8ee094197ddaeacfa258ebb5389f3d12e583d72 100644 (file)
@@ -38,8 +38,8 @@
  **
  ****************************************************************************/
 
-#ifndef FLOWLAYOUT_H
-#define FLOWLAYOUT_H
+#ifndef PULSEVIEW_PV_WIDGETS_FLOWLAYOUT_HPP
+#define PULSEVIEW_PV_WIDGETS_FLOWLAYOUT_HPP
 
 #include <QLayout>
 #include <QRect>
@@ -75,4 +75,4 @@ private:
        int m_hSpace, m_vSpace;
 };
 
-#endif
+#endif // PULSEVIEW_PV_WIDGETS_FLOWLAYOUT_HPP
index e3034249769e66c73041b37af30fcab6f0ec1392..dcca307ecc731bb01e6946fd9f38d4c0ec17f4ac 100644 (file)
@@ -63,17 +63,22 @@ ImportMenu::ImportMenu(QWidget *parent, shared_ptr<Context> context,
        const map<string, shared_ptr<InputFormat> > formats =
                context->input_formats();
 
-       for (const pair<string, shared_ptr<InputFormat> > &f : formats) {
+       for (const pair<const string, shared_ptr<InputFormat> > &f : formats) {
                assert(f.second);
                QAction *const action = addAction(tr("Import %1...")
                        .arg(QString::fromStdString(f.second->description())));
-               action->setData(qVariantFromValue((void*)f.second.get()));
+               action->setData(QVariant::fromValue((void*)f.second.get()));
                mapper_.setMapping(action, action);
                connect(action, SIGNAL(triggered()), &mapper_, SLOT(map()));
        }
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(&mapper_, SIGNAL(mappedObject(QObject*)),
+               this, SLOT(on_action(QObject*)));
+#else
        connect(&mapper_, SIGNAL(mapped(QObject*)),
                this, SLOT(on_action(QObject*)));
+#endif
 }
 
 void ImportMenu::on_action(QObject *action)
index ec6d29c981c235b86451353ca620d9c5cf147868..bcfd8753c6b6dbe2c8e6da5a8032d75057b8bc50 100644 (file)
 #include <cassert>
 
 #include <QApplication>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#include <QScreen>
+#else
 #include <QDesktopWidget>
+#endif
 #include <QLineEdit>
 #include <QScrollBar>
 #include <QStyle>
@@ -252,8 +256,12 @@ void Popup::reposition_widget()
 {
        QPoint o;
 
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
+       const QRect screen_rect = QApplication::screenAt(point_)->availableGeometry();
+#else
        const QRect screen_rect = QApplication::desktop()->availableGeometry(
                QApplication::desktop()->screenNumber(point_));
+#endif
 
        if (pos_ == Right || pos_ == Left)
                o.ry() = -height() / 2;
index 12f5969eb707c9997c212e1d39f838541c1f5588..0f1eb6d9ff2595f0f484c357655b9691ebbcbf92 100644 (file)
@@ -54,7 +54,11 @@ SweepTimingWidget::SweepTimingWidget(const char *suffix,
                this, SIGNAL(value_changed()));
 
        setLayout(&layout_);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       layout_.setContentsMargins(0, 0, 0, 0);
+#else
        layout_.setMargin(0);
+#endif
        layout_.addWidget(&list_);
        layout_.addWidget(&value_);
 
@@ -95,7 +99,7 @@ void SweepTimingWidget::show_list(const uint64_t *vals, size_t count)
        list_.clear();
        for (size_t i = 0; i < count; i++) {
                char *const s = sr_si_string_u64(vals[i], suffix_);
-               list_.addItem(QString::fromUtf8(s), qVariantFromValue(vals[i]));
+               list_.addItem(QString::fromUtf8(s), QVariant::fromValue(vals[i]));
                g_free(s);
        }
 
index fea8175e8e00b74b21f6690cf9d726e3a9cd418c..b99a640a843055fa2d2271bcae9c67d6607f63a9 100644 (file)
 #include "timestampspinbox.hpp"
 
 #include <QLineEdit>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#include <QRegularExpression>
+#else
 #include <QRegExp>
+#endif
 
 namespace pv {
 namespace widgets {
@@ -76,7 +80,7 @@ QSize TimestampSpinBox::minimumSizeHint() const
 {
        const QFontMetrics fm(fontMetrics());
        const int l = round(value_).str().size() + precision_ + 10;
-       const int w = fm.width(QString(l, '0'));
+       const int w = util::text_width(fm, QString(l, '0'));
        const int h = lineEdit()->minimumSizeHint().height();
        return QSize(w, h);
 }
@@ -93,10 +97,25 @@ void TimestampSpinBox::setValue(const pv::util::Timestamp& val)
 
 void TimestampSpinBox::on_editingFinished()
 {
-       QRegExp re(R"(\s*([-+]?)\s*([0-9]+\.?[0-9]*).*)");
+       static const auto re_pattern = R"(\s*([-+]?)\s*([0-9]+\.?[0-9]*).*)";
+
+       bool has_match;
+       QStringList captures;
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       QRegularExpression re(re_pattern);
+       has_match = re.match(text()).hasMatch();
+       if (has_match) {
+               captures = re.match(text()).capturedTexts();
+       }
+#else
+       QRegExp re(re_pattern);
+       has_match = re.exactMatch(text());
+       if (has_match) {
+               captures = re.capturedTexts();
+       }
+#endif
 
-       if (re.exactMatch(text())) {
-               QStringList captures = re.capturedTexts();
+       if (has_match) {
                captures.removeFirst(); // remove entire match
                QString str = captures.join("");
                setValue(pv::util::Timestamp(str.toStdString()));
index 123ef876d7b19f205deea84c229fc7d97a5fc244..25e30ac5ed4a711a766d59dbce64b175c72b7464 100644 (file)
@@ -89,4 +89,4 @@ private:
 }  // namespace widgets
 }  // namespace pv
 
-#endif
+#endif // PULSEVIEW_PV_WIDGETS_TIMESTAMPSPINBOX_HPP
index 04a7ccec9723c999cea7b23bae94db1a25c03eee..655ad87ef5639d8f9d11b14686b73eacbe0587f7 100644 (file)
@@ -252,7 +252,6 @@ QBrush WellArray::cellBrush(int row, int col)
 }
 
 
-
 /*!\reimp
 */
 
index 14706261b7d483bc0a031be43f82104549b6f5ca..d228718f00f8d861f8626d96e932c1b407e70fb0 100644 (file)
@@ -43,7 +43,8 @@ bool SignalHandler::prepare_signals()
        sig_action.sa_flags = SA_RESTART;
 
        if (sigaction(SIGINT, &sig_action, nullptr) != 0 ||
-               sigaction(SIGTERM, &sig_action, nullptr) != 0) {
+               sigaction(SIGTERM, &sig_action, nullptr) != 0 ||
+               sigaction(SIGUSR1, &sig_action, nullptr) != 0) {
                close(sockets_[0]);
                close(sockets_[1]);
                return false;
@@ -78,6 +79,9 @@ void SignalHandler::on_socket_notifier_activated()
        case SIGTERM:
                Q_EMIT term_received();
                break;
+       case SIGUSR1:
+               Q_EMIT usr1_received();
+               break;
        }
 
        socket_notifier_->setEnabled(true);
index a17b04b9eb885d8de55f5bc507d0974eae554668..0246dc991935a46457bd9e348105ac48ceca1710 100644 (file)
@@ -17,8 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PULSEVIEW_PV_SIGNALHANDLER_HPP
-#define PULSEVIEW_PV_SIGNALHANDLER_HPP
+#ifndef PULSEVIEW_SIGNALHANDLER_HPP
+#define PULSEVIEW_SIGNALHANDLER_HPP
 
 #include <QObject>
 
@@ -37,6 +37,7 @@ public:
 Q_SIGNALS:
        void int_received();
        void term_received();
+       void usr1_received();
 
 private Q_SLOTS:
        void on_socket_notifier_activated();
@@ -51,4 +52,4 @@ private:
        static int sockets_[2];
 };
 
-#endif // PULSEVIEW_PV_SIGNALHANDLER_HPP
+#endif // PULSEVIEW_SIGNALHANDLER_HPP
index dc874d59cc5d88498641c7ee0e655fa2d1b1b268..aefe1432b14307a97a307ceb8b1975f1e7b16bc2 100644 (file)
@@ -24,6 +24,7 @@ set(pulseview_TEST_SOURCES
        ${PROJECT_SOURCE_DIR}/pv/globalsettings.cpp
        ${PROJECT_SOURCE_DIR}/pv/logging.cpp
        ${PROJECT_SOURCE_DIR}/pv/mainwindow.cpp
+       ${PROJECT_SOURCE_DIR}/pv/metadata_obj.cpp
        ${PROJECT_SOURCE_DIR}/pv/session.cpp
        ${PROJECT_SOURCE_DIR}/pv/storesession.cpp
        ${PROJECT_SOURCE_DIR}/pv/util.cpp
@@ -34,6 +35,7 @@ set(pulseview_TEST_SOURCES
        ${PROJECT_SOURCE_DIR}/pv/data/analogsegment.cpp
        ${PROJECT_SOURCE_DIR}/pv/data/logic.cpp
        ${PROJECT_SOURCE_DIR}/pv/data/logicsegment.cpp
+       ${PROJECT_SOURCE_DIR}/pv/data/mathsignal.cpp
        ${PROJECT_SOURCE_DIR}/pv/data/segment.cpp
        ${PROJECT_SOURCE_DIR}/pv/data/signalbase.cpp
        ${PROJECT_SOURCE_DIR}/pv/data/signaldata.cpp
@@ -61,6 +63,7 @@ set(pulseview_TEST_SOURCES
        ${PROJECT_SOURCE_DIR}/pv/views/trace/cursorpair.cpp
        ${PROJECT_SOURCE_DIR}/pv/views/trace/flag.cpp
        ${PROJECT_SOURCE_DIR}/pv/views/trace/header.cpp
+       ${PROJECT_SOURCE_DIR}/pv/views/trace/mathsignal.cpp
        ${PROJECT_SOURCE_DIR}/pv/views/trace/marginwidget.cpp
        ${PROJECT_SOURCE_DIR}/pv/views/trace/logicsignal.cpp
        ${PROJECT_SOURCE_DIR}/pv/views/trace/ruler.cpp
@@ -104,9 +107,11 @@ set(pulseview_TEST_SOURCES
 set(pulseview_TEST_HEADERS
        ${PROJECT_SOURCE_DIR}/pv/application.hpp
        ${PROJECT_SOURCE_DIR}/pv/devicemanager.hpp
+       ${PROJECT_SOURCE_DIR}/pv/exprtk.hpp
        ${PROJECT_SOURCE_DIR}/pv/globalsettings.hpp
        ${PROJECT_SOURCE_DIR}/pv/logging.hpp
        ${PROJECT_SOURCE_DIR}/pv/mainwindow.hpp
+       ${PROJECT_SOURCE_DIR}/pv/metadata_obj.hpp
        ${PROJECT_SOURCE_DIR}/pv/session.hpp
        ${PROJECT_SOURCE_DIR}/pv/storesession.hpp
        ${PROJECT_SOURCE_DIR}/pv/binding/device.hpp
@@ -114,6 +119,7 @@ set(pulseview_TEST_HEADERS
        ${PROJECT_SOURCE_DIR}/pv/data/analogsegment.hpp
        ${PROJECT_SOURCE_DIR}/pv/data/logic.hpp
        ${PROJECT_SOURCE_DIR}/pv/data/logicsegment.hpp
+       ${PROJECT_SOURCE_DIR}/pv/data/mathsignal.hpp
        ${PROJECT_SOURCE_DIR}/pv/data/signalbase.hpp
        ${PROJECT_SOURCE_DIR}/pv/devices/device.hpp
        ${PROJECT_SOURCE_DIR}/pv/dialogs/connect.hpp
@@ -135,6 +141,7 @@ set(pulseview_TEST_HEADERS
        ${PROJECT_SOURCE_DIR}/pv/views/trace/flag.hpp
        ${PROJECT_SOURCE_DIR}/pv/views/trace/header.hpp
        ${PROJECT_SOURCE_DIR}/pv/views/trace/logicsignal.hpp
+       ${PROJECT_SOURCE_DIR}/pv/views/trace/mathsignal.hpp
        ${PROJECT_SOURCE_DIR}/pv/views/trace/marginwidget.hpp
        ${PROJECT_SOURCE_DIR}/pv/views/trace/ruler.hpp
        ${PROJECT_SOURCE_DIR}/pv/views/trace/signal.hpp
@@ -174,8 +181,10 @@ if(ENABLE_DECODE)
                ${PROJECT_SOURCE_DIR}/pv/subwindows/decoder_selector/item.cpp
                ${PROJECT_SOURCE_DIR}/pv/subwindows/decoder_selector/model.cpp
                ${PROJECT_SOURCE_DIR}/pv/subwindows/decoder_selector/subwindow.cpp
-               ${PROJECT_SOURCE_DIR}/pv/views/decoder_output/view.cpp
-               ${PROJECT_SOURCE_DIR}/pv/views/decoder_output/QHexView.cpp
+               ${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/view.cpp
+               ${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/QHexView.cpp
+               ${PROJECT_SOURCE_DIR}/pv/views/tabular_decoder/model.cpp
+               ${PROJECT_SOURCE_DIR}/pv/views/tabular_decoder/view.cpp
                ${PROJECT_SOURCE_DIR}/pv/views/trace/decodetrace.cpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.cpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.cpp
@@ -184,8 +193,9 @@ if(ENABLE_DECODE)
        list(APPEND pulseview_TEST_HEADERS
                ${PROJECT_SOURCE_DIR}/pv/data/decodesignal.hpp
                ${PROJECT_SOURCE_DIR}/pv/subwindows/decoder_selector/subwindow.hpp
-               ${PROJECT_SOURCE_DIR}/pv/views/decoder_output/view.hpp
-               ${PROJECT_SOURCE_DIR}/pv/views/decoder_output/QHexView.hpp
+               ${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/view.hpp
+               ${PROJECT_SOURCE_DIR}/pv/views/decoder_binary/QHexView.hpp
+               ${PROJECT_SOURCE_DIR}/pv/views/tabular_decoder/view.hpp
                ${PROJECT_SOURCE_DIR}/pv/views/trace/decodetrace.hpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.hpp
                ${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.hpp
index 62e6cf0da4bddbaaa44f75c36770b76e33a97d4b..41e96accdfa49ad12d4f9dfb11b67e906f163686 100644 (file)
@@ -26,4 +26,4 @@ using std::ostream;
 
 ostream& operator<<(ostream& stream, const QString& str);
 
-#endif
+#endif // PULSEVIEW_TEST_TEST_HPP
index 5cef38d5ce20211c1ca8e329ca8e05f0a62629a1..4a4eac8f34d5822cacec1b622c4fe8c8f9f047bc 100644 (file)
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 107100 // 1.71 deprecated the old header location.
+#include <boost/test/tools/floating_point_comparison.hpp>
+#else
 #include <boost/test/floating_point_comparison.hpp>
+#endif
 #include <boost/test/unit_test.hpp>
 
 #include "pv/views/trace/ruler.hpp"
index 103fa359dfd3e93412073e126ff5b4ad5971fc15..d5f282344af7f33fc17c0205aadf0f12aa6a0003 100644 (file)
@@ -1,5 +1,8 @@
 <RCC>
        <qresource prefix="/">
                <file>l10n/de.qm</file>
+               <file>l10n/es_MX.qm</file>
+               <file>l10n/ja_jp.qm</file>
+               <file>l10n/zh_cn.qm</file>
        </qresource>
 </RCC>