]> code.bitgloo.com Git - bitgloo/dsp-paw.git/commitdiff
initial commit
authorClyne Sullivan <clyne@bitgloo.com>
Wed, 9 Aug 2023 03:10:02 +0000 (23:10 -0400)
committerClyne Sullivan <clyne@bitgloo.com>
Wed, 9 Aug 2023 03:10:02 +0000 (23:10 -0400)
* combine all source files into this monorepo

* convert all third-party source packages into submodules

* small fixes due to changes in latest third-part packages

95 files changed:
.gitignore [new file with mode: 0644]
.gitmodules [new file with mode: 0644]
README.md [new file with mode: 0644]
firmware/ChibiOS [new submodule]
firmware/LICENSE [new file with mode: 0644]
firmware/Makefile [new file with mode: 0644]
firmware/openocd.cfg [new file with mode: 0644]
firmware/source/board/board.mk [new file with mode: 0644]
firmware/source/board/board_h7.c [new file with mode: 0644]
firmware/source/board/board_l4.c [new file with mode: 0644]
firmware/source/board/h7/board.h [new file with mode: 0644]
firmware/source/board/l4/board.h [new file with mode: 0644]
firmware/source/cfg/chconf.h [new file with mode: 0644]
firmware/source/cfg/halconf.h [new file with mode: 0644]
firmware/source/cfg/mcuconf.h [new file with mode: 0644]
firmware/source/cfg/mcuconf_h7.h [new file with mode: 0644]
firmware/source/cfg/mcuconf_l4.h [new file with mode: 0644]
firmware/source/communication.cpp [new file with mode: 0644]
firmware/source/communication.hpp [new file with mode: 0644]
firmware/source/conversion.cpp [new file with mode: 0644]
firmware/source/conversion.hpp [new file with mode: 0644]
firmware/source/elf.h [new file with mode: 0644]
firmware/source/elfload.cpp [new file with mode: 0644]
firmware/source/elfload.hpp [new file with mode: 0644]
firmware/source/error.cpp [new file with mode: 0644]
firmware/source/error.hpp [new file with mode: 0644]
firmware/source/handlers.cpp [new file with mode: 0644]
firmware/source/handlers.hpp [new file with mode: 0644]
firmware/source/ld/STM32H723xG.ld [new file with mode: 0644]
firmware/source/ld/STM32L476xG.ld [new file with mode: 0644]
firmware/source/main.cpp [new file with mode: 0644]
firmware/source/monitor.cpp [new file with mode: 0644]
firmware/source/monitor.hpp [new file with mode: 0644]
firmware/source/periph/adc.cpp [new file with mode: 0644]
firmware/source/periph/adc.hpp [new file with mode: 0644]
firmware/source/periph/cordic.cpp [new file with mode: 0644]
firmware/source/periph/cordic.hpp [new file with mode: 0644]
firmware/source/periph/dac.cpp [new file with mode: 0644]
firmware/source/periph/dac.hpp [new file with mode: 0644]
firmware/source/periph/usbcfg.c [new file with mode: 0644]
firmware/source/periph/usbcfg.h [new file with mode: 0644]
firmware/source/periph/usbserial.cpp [new file with mode: 0644]
firmware/source/periph/usbserial.hpp [new file with mode: 0644]
firmware/source/runstatus.hpp [new file with mode: 0644]
firmware/source/samplebuffer.cpp [new file with mode: 0644]
firmware/source/samplebuffer.hpp [new file with mode: 0644]
firmware/source/samples.cpp [new file with mode: 0644]
firmware/source/samples.hpp [new file with mode: 0644]
firmware/source/sclock.cpp [new file with mode: 0644]
firmware/source/sclock.hpp [new file with mode: 0644]
gui/LICENSE [new file with mode: 0644]
gui/Makefile [new file with mode: 0644]
gui/examples/1_convolve_simple.cpp [new file with mode: 0644]
gui/examples/2_convolve_overlap_save.cpp [new file with mode: 0644]
gui/examples/3_fir.cpp [new file with mode: 0644]
gui/examples/4_fir_pro.cpp [new file with mode: 0644]
gui/examples/5_fir_differentiator.cpp [new file with mode: 0644]
gui/examples/6_iir_test.cpp [new file with mode: 0644]
gui/examples/7_iir_echo.cpp [new file with mode: 0644]
gui/fonts/LICENSE-2.0.txt [new file with mode: 0644]
gui/fonts/Roboto-Medium.ttf [new file with mode: 0644]
gui/fonts/Roboto-Regular.ttf [new file with mode: 0644]
gui/fonts/RobotoMono-Regular.ttf [new file with mode: 0644]
gui/source/ImGuiColorTextEdit [new submodule]
gui/source/ImGuiFileDialog [new submodule]
gui/source/circular.hpp [new file with mode: 0644]
gui/source/code.cpp [new file with mode: 0644]
gui/source/code.hpp [new file with mode: 0644]
gui/source/device.cpp [new file with mode: 0644]
gui/source/device_formula.cpp [new file with mode: 0644]
gui/source/exprtk.hpp [new file with mode: 0644]
gui/source/file.cpp [new file with mode: 0644]
gui/source/gui.cpp [new file with mode: 0644]
gui/source/gui_code.cpp [new file with mode: 0644]
gui/source/gui_device.cpp [new file with mode: 0644]
gui/source/gui_help.cpp [new file with mode: 0644]
gui/source/gui_help.hpp [new file with mode: 0644]
gui/source/imgui [new submodule]
gui/source/logview.cpp [new file with mode: 0644]
gui/source/logview.h [new file with mode: 0644]
gui/source/main.cpp [new file with mode: 0644]
gui/source/main.hpp [new file with mode: 0644]
gui/source/serial [new submodule]
gui/source/stmdsp/stmdsp.cpp [new file with mode: 0644]
gui/source/stmdsp/stmdsp.hpp [new file with mode: 0644]
gui/source/stmdsp/stmdsp_code.hpp [new file with mode: 0644]
gui/source/wav.hpp [new file with mode: 0644]
hardware/DSP PAW add-on board.kicad_pro [new file with mode: 0755]
hardware/DSP PAW add-on board.kicad_sch [new file with mode: 0755]
hardware/DSP PAW add-on board.xml [new file with mode: 0755]
hardware/LICENSE [new file with mode: 0644]
hardware/analog_io.kicad_sch [new file with mode: 0755]
hardware/board_connectors.kicad_sch [new file with mode: 0755]
hardware/power_regulation.kicad_sch [new file with mode: 0755]
hardware/user_io.kicad_sch [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..60424bf
--- /dev/null
@@ -0,0 +1,13 @@
+.*
+*.o
+*.so
+*.exe
+*.dll
+perf*
+
+firmware/build
+gui/imgui.ini
+gui/stmdspgui
+gui/stmdspgui.exe
+hardware/*-backups
+hardware/*.kicad_prl
diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..57329a9
--- /dev/null
@@ -0,0 +1,16 @@
+[submodule "firmware/ChibiOS"]
+       path = firmware/ChibiOS
+       url = https://github.com/ChibiOS/ChibiOS
+       branch = stable_20.3.x
+[submodule "gui/source/serial"]
+       path = gui/source/serial
+       url = https://www.github.com/wjwwood/serial
+[submodule "gui/source/imgui"]
+       path = gui/source/imgui
+       url = https://github.com/ocornut/imgui
+[submodule "gui/source/ImGuiFileDialog"]
+       path = gui/source/ImGuiFileDialog
+       url = https://github.com/aiekick/ImGuiFileDialog
+[submodule "gui/source/ImGuiColorTextEdit"]
+       path = gui/source/ImGuiColorTextEdit
+       url = https://github.com/BalazsJako/ImGuiColorTextEdit
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..411f81e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,38 @@
+# dsp-paw
+
+This is the DSP PAW "monorepo", containing the microcontroller firmware, add-on board design files, and the source code for the computer graphical interface (GUI).
+
+*DSP PAW* (Digital Signal Processing Portable All-in-one Workstation) provides a solution for creating and studying DSP algorithms on embedded systems. Using the project's software and hardware with certain [NUCLEO development boards](https://www.st.com/en/evaluation-tools/stm32-nucleo-boards.html) will enable you to portably design DSP algorithms, without the need for external tools or laboratory equipment.
+
+## Project components
+
+1. [Firmware](https://code.bitgloo.com/bitgloo/dsp-paw/src/branch/main/firmware) that allows users to load and execute custom DSP algorithms for real-time signal processing.
+2. [A custom add-on board](https://code.bitgloo.com/bitgloo/dsp-paw/src/branch/main/hardware) which provides the necessary circuitry for interfacing with external signals and the host computer.
+3. [Computer software](https://code.bitgloo.com/bitgloo/dsp-paw/src/branch/main/gui) that facilitates algorithm design, execution, and detailed analysis.
+
+## Features
+
+* Real-time signal processing: signal read by the ADC are streamed through the loaded algorithm and output via the DAC.
+* The core firmware facilitates algorithm uploads over USB, enabling a fast design and test process.
+* Supports signal sampling rates from 8kS/s up to 96kS/s, with buffer size of up to 4,096 samples.
+* Supports external signals between -2V and +2V.
+* Two potentiometers for adjusting algorithm parameters while the algorithm is running.
+* An on-board signal generator that eliminates the need for input signals from external hardware.
+* Numerous analysis features, including signal visualization and algorithm execution time measurement, eliminate the need for other test equipment like oscilloscopes.
+
+## Build notes
+
+The add-on board was designed using [KiCad](https://www.kicad.org/).
+
+Both the firmware and user interface software are written in C++, and have single Makefiles for their build processes. Both also depend on git submodules, so be sure to fetch those before compilation.
+
+The firmware additionally requires the `arm-none-eabi` toolchain, and the GUI requires [SDL 2.0](https://www.libsdl.org/).
+
+## Learn more
+
+The [project's wiki](https://code.bitgloo.com/bitgloo/dsp-paw/wiki) will be updated over time with more information about all aspects of the project.
+
+### Licensing
+
+DSP PAW source code is licensed under version three of the GNU General Public License. The add-on board design files are licensed under the CERN Open Hardware License Version 2 - Strongly Reciprocal. See the `LICENSE` files in each component's folder for more information.
+
diff --git a/firmware/ChibiOS b/firmware/ChibiOS
new file mode 160000 (submodule)
index 0000000..dd0783c
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit dd0783ca6f450486a96deadb98b6df01d6bb6e85
diff --git a/firmware/LICENSE b/firmware/LICENSE
new file mode 100644 (file)
index 0000000..6a34f51
--- /dev/null
@@ -0,0 +1,620 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
diff --git a/firmware/Makefile b/firmware/Makefile
new file mode 100644 (file)
index 0000000..0db249b
--- /dev/null
@@ -0,0 +1,222 @@
+##############################################################################
+# Build global options
+# NOTE: Can be overridden externally.
+#
+
+# Set the target platform, either L4, G4, or H7
+TARGET_PLATFORM = L4
+
+# Compiler options here.
+ifeq ($(USE_OPT),)
+  USE_OPT = -O0 -g3 -ggdb -fomit-frame-pointer -falign-functions=16 --specs=nosys.specs
+endif
+
+# C specific options here (added to USE_OPT).
+ifeq ($(USE_COPT),)
+  USE_COPT = 
+endif
+
+# C++ specific options here (added to USE_OPT).
+ifeq ($(USE_CPPOPT),)
+  USE_CPPOPT = -std=c++2a -fno-rtti -fno-exceptions
+endif
+
+# Enable this if you want the linker to remove unused code and data.
+ifeq ($(USE_LINK_GC),)
+  USE_LINK_GC = yes
+endif
+
+# Linker extra options here.
+ifeq ($(USE_LDOPT),)
+#  USE_LDOPT = -L.,-lzig
+endif
+
+# Enable this if you want link time optimizations (LTO).
+ifeq ($(USE_LTO),)
+  USE_LTO = yes
+endif
+
+# Enable this if you want to see the full log while compiling.
+ifeq ($(USE_VERBOSE_COMPILE),)
+  USE_VERBOSE_COMPILE = no
+endif
+
+# If enabled, this option makes the build process faster by not compiling
+# modules not used in the current configuration.
+ifeq ($(USE_SMART_BUILD),)
+  USE_SMART_BUILD = yes
+endif
+
+#
+# Build global options
+##############################################################################
+
+##############################################################################
+# Architecture or project specific options
+#
+
+# Stack size to be allocated to the Cortex-M process stack. This stack is
+# the stack used by the main() thread.
+ifeq ($(USE_PROCESS_STACKSIZE),)
+  USE_PROCESS_STACKSIZE = 1024
+endif
+
+# Stack size to the allocated to the Cortex-M main/exceptions stack. This
+# stack is used for processing interrupts and exceptions.
+ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
+  USE_EXCEPTIONS_STACKSIZE = 2048
+endif
+
+# Enables the use of FPU (no, softfp, hard).
+ifeq ($(USE_FPU),)
+  USE_FPU = hard
+endif
+
+# FPU-related options.
+ifeq ($(USE_FPU_OPT),)
+ifeq ($(TARGET_PLATFORM),H7)
+  USE_FPU_OPT = -mfloat-abi=$(USE_FPU) -mfpu=fpv5-d16
+endif
+ifeq ($(TARGET_PLATFORM),L4)
+  USE_FPU_OPT = -mfloat-abi=$(USE_FPU) -mfpu=fpv4-sp-d16
+endif
+endif
+
+#
+# Architecture or project specific options
+##############################################################################
+
+##############################################################################
+# Project, target, sources and paths
+#
+
+# Define project name here
+PROJECT = ch
+
+# Target settings.
+ifeq ($(TARGET_PLATFORM),H7)
+  MCU = cortex-m7
+else
+  MCU = cortex-m4
+endif
+
+# Imported source files and paths.
+CHIBIOS  := ./ChibiOS
+CONFDIR  := ./source/cfg
+BUILDDIR := ./build
+DEPDIR   := ./.dep
+
+# Licensing files.
+include $(CHIBIOS)/os/license/license.mk
+# Startup files.
+ifeq ($(TARGET_PLATFORM),H7)
+  include $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_stm32h7xx.mk
+else
+  include $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_stm32l4xx.mk
+endif
+# HAL-OSAL files (optional).
+include $(CHIBIOS)/os/hal/hal.mk
+ifeq ($(TARGET_PLATFORM),H7)
+  include $(CHIBIOS)/os/hal/ports/STM32/STM32H7xx/platform.mk
+else
+  include $(CHIBIOS)/os/hal/ports/STM32/STM32L4xx/platform.mk
+endif
+include ./source/board/board.mk
+include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk
+# RTOS files (optional).
+include $(CHIBIOS)/os/rt/rt.mk
+include $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v7m.mk
+# Auto-build files in ./source recursively.
+#include $(CHIBIOS)/tools/mk/autobuild.mk
+ALLCSRC += $(wildcard source/*.c) $(wildcard source/periph/*.c)
+ALLCPPSRC += $(wildcard source/*.cpp) $(wildcard source/periph/*.cpp)
+ALLASMSRC += $(wildcard source/*.s)
+# Other files (optional).
+#include $(CHIBIOS)/test/lib/test.mk
+#include $(CHIBIOS)/test/rt/rt_test.mk
+#include $(CHIBIOS)/test/oslib/oslib_test.mk
+
+# Define linker script file here
+ifeq ($(TARGET_PLATFORM),H7)
+  LDSCRIPT = source/ld/STM32H723xG.ld
+else
+  LDSCRIPT = source/ld/STM32L476xG.ld
+endif
+
+# C sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CSRC = $(ALLCSRC)
+
+# C++ sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CPPSRC = $(ALLCPPSRC)
+
+# List ASM source files here.
+ASMSRC = $(ALLASMSRC)
+
+# List ASM with preprocessor source files here.
+ASMXSRC = $(ALLXASMSRC)
+
+# Inclusion directories.
+INCDIR = $(CONFDIR) $(ALLINC) $(TESTINC) \
+         source source/periph
+
+# Define C warning options here.
+CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes -pedantic
+
+# Define C++ warning options here.
+CPPWARN = -Wall -Wextra -Wundef -pedantic -Wno-volatile
+
+#
+# Project, target, sources and paths
+##############################################################################
+
+##############################################################################
+# Start of user section
+#
+
+# List all user C define here, like -D_DEBUG=1
+UDEFS = -DCORTEX_ENABLE_WFI_IDLE=TRUE \
+        -DPORT_USE_SYSCALL=TRUE \
+        -DPORT_USE_GUARD_MPU_REGION=MPU_REGION_0 \
+        -DTARGET_PLATFORM_$(TARGET_PLATFORM)
+
+# Define ASM defines here
+UADEFS =
+
+# List all user directories here
+UINCDIR =
+
+# List the user directory to look for the libraries here
+ULIBDIR =
+
+# List all user libraries here
+ifeq ($(TARGET_PLATFORM),L4)
+  ULIBS = -lm
+else
+  ULIBS =
+endif
+
+#
+# End of user section
+##############################################################################
+
+##############################################################################
+# Common rules
+#
+
+RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk
+include $(RULESPATH)/arm-none-eabi.mk
+include $(RULESPATH)/rules.mk
+
+#
+# Common rules
+##############################################################################
+
+##############################################################################
+# Custom rules
+#
+
+#
+# Custom rules
+##############################################################################
diff --git a/firmware/openocd.cfg b/firmware/openocd.cfg
new file mode 100644 (file)
index 0000000..47e6c7d
--- /dev/null
@@ -0,0 +1,3 @@
+source [find interface/stlink.cfg]
+source [find target/stm32l4x.cfg]
+
diff --git a/firmware/source/board/board.mk b/firmware/source/board/board.mk
new file mode 100644 (file)
index 0000000..155285a
--- /dev/null
@@ -0,0 +1,17 @@
+# List of all the board related files.\r
+ifeq ($(TARGET_PLATFORM),H7)\r
+  BOARDSRC = ./source/board/board_h7.c\r
+else\r
+  BOARDSRC = ./source/board/board_l4.c\r
+endif\r
+\r
+# Required include directories\r
+ifeq ($(TARGET_PLATFORM),H7)\r
+  BOARDINC = ./source/board/h7\r
+else\r
+  BOARDINC = ./source/board/l4\r
+endif\r
+\r
+# Shared variables\r
+ALLCSRC += $(BOARDSRC)\r
+ALLINC  += $(BOARDINC)\r
diff --git a/firmware/source/board/board_h7.c b/firmware/source/board/board_h7.c
new file mode 100644 (file)
index 0000000..74285cf
--- /dev/null
@@ -0,0 +1,287 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2019 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+/*\r
+ * This file has been automatically generated using ChibiStudio board\r
+ * generator plugin. Do not edit manually.\r
+ */\r
+\r
+#include "hal.h"\r
+#include "stm32_gpio.h"\r
+\r
+/*===========================================================================*/\r
+/* Driver local definitions.                                                 */\r
+/*===========================================================================*/\r
+\r
+/*===========================================================================*/\r
+/* Driver exported variables.                                                */\r
+/*===========================================================================*/\r
+\r
+/*===========================================================================*/\r
+/* Driver local variables and types.                                         */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Type of STM32 GPIO port setup.\r
+ */\r
+typedef struct {\r
+  uint32_t              moder;\r
+  uint32_t              otyper;\r
+  uint32_t              ospeedr;\r
+  uint32_t              pupdr;\r
+  uint32_t              odr;\r
+  uint32_t              afrl;\r
+  uint32_t              afrh;\r
+} gpio_setup_t;\r
+\r
+/**\r
+ * @brief   Type of STM32 GPIO initialization data.\r
+ */\r
+typedef struct {\r
+#if STM32_HAS_GPIOA || defined(__DOXYGEN__)\r
+  gpio_setup_t          PAData;\r
+#endif\r
+#if STM32_HAS_GPIOB || defined(__DOXYGEN__)\r
+  gpio_setup_t          PBData;\r
+#endif\r
+#if STM32_HAS_GPIOC || defined(__DOXYGEN__)\r
+  gpio_setup_t          PCData;\r
+#endif\r
+#if STM32_HAS_GPIOD || defined(__DOXYGEN__)\r
+  gpio_setup_t          PDData;\r
+#endif\r
+#if STM32_HAS_GPIOE || defined(__DOXYGEN__)\r
+  gpio_setup_t          PEData;\r
+#endif\r
+#if STM32_HAS_GPIOF || defined(__DOXYGEN__)\r
+  gpio_setup_t          PFData;\r
+#endif\r
+#if STM32_HAS_GPIOG || defined(__DOXYGEN__)\r
+  gpio_setup_t          PGData;\r
+#endif\r
+#if STM32_HAS_GPIOH || defined(__DOXYGEN__)\r
+  gpio_setup_t          PHData;\r
+#endif\r
+#if STM32_HAS_GPIOI || defined(__DOXYGEN__)\r
+  gpio_setup_t          PIData;\r
+#endif\r
+#if STM32_HAS_GPIOJ || defined(__DOXYGEN__)\r
+  gpio_setup_t          PJData;\r
+#endif\r
+#if STM32_HAS_GPIOK || defined(__DOXYGEN__)\r
+  gpio_setup_t          PKData;\r
+#endif\r
+} gpio_config_t;\r
+\r
+/**\r
+ * @brief   STM32 GPIO static initialization data.\r
+ */\r
+static const gpio_config_t gpio_default_config = {\r
+#if STM32_HAS_GPIOA\r
+  {VAL_GPIOA_MODER, VAL_GPIOA_OTYPER, VAL_GPIOA_OSPEEDR, VAL_GPIOA_PUPDR,\r
+   VAL_GPIOA_ODR,   VAL_GPIOA_AFRL,   VAL_GPIOA_AFRH},\r
+#endif\r
+#if STM32_HAS_GPIOB\r
+  {VAL_GPIOB_MODER, VAL_GPIOB_OTYPER, VAL_GPIOB_OSPEEDR, VAL_GPIOB_PUPDR,\r
+   VAL_GPIOB_ODR,   VAL_GPIOB_AFRL,   VAL_GPIOB_AFRH},\r
+#endif\r
+#if STM32_HAS_GPIOC\r
+  {VAL_GPIOC_MODER, VAL_GPIOC_OTYPER, VAL_GPIOC_OSPEEDR, VAL_GPIOC_PUPDR,\r
+   VAL_GPIOC_ODR,   VAL_GPIOC_AFRL,   VAL_GPIOC_AFRH},\r
+#endif\r
+#if STM32_HAS_GPIOD\r
+  {VAL_GPIOD_MODER, VAL_GPIOD_OTYPER, VAL_GPIOD_OSPEEDR, VAL_GPIOD_PUPDR,\r
+   VAL_GPIOD_ODR,   VAL_GPIOD_AFRL,   VAL_GPIOD_AFRH},\r
+#endif\r
+#if STM32_HAS_GPIOE\r
+  {VAL_GPIOE_MODER, VAL_GPIOE_OTYPER, VAL_GPIOE_OSPEEDR, VAL_GPIOE_PUPDR,\r
+   VAL_GPIOE_ODR,   VAL_GPIOE_AFRL,   VAL_GPIOE_AFRH},\r
+#endif\r
+#if STM32_HAS_GPIOF\r
+  {VAL_GPIOF_MODER, VAL_GPIOF_OTYPER, VAL_GPIOF_OSPEEDR, VAL_GPIOF_PUPDR,\r
+   VAL_GPIOF_ODR,   VAL_GPIOF_AFRL,   VAL_GPIOF_AFRH},\r
+#endif\r
+#if STM32_HAS_GPIOG\r
+  {VAL_GPIOG_MODER, VAL_GPIOG_OTYPER, VAL_GPIOG_OSPEEDR, VAL_GPIOG_PUPDR,\r
+   VAL_GPIOG_ODR,   VAL_GPIOG_AFRL,   VAL_GPIOG_AFRH},\r
+#endif\r
+#if STM32_HAS_GPIOH\r
+  {VAL_GPIOH_MODER, VAL_GPIOH_OTYPER, VAL_GPIOH_OSPEEDR, VAL_GPIOH_PUPDR,\r
+   VAL_GPIOH_ODR,   VAL_GPIOH_AFRL,   VAL_GPIOH_AFRH},\r
+#endif\r
+#if STM32_HAS_GPIOI\r
+  {VAL_GPIOI_MODER, VAL_GPIOI_OTYPER, VAL_GPIOI_OSPEEDR, VAL_GPIOI_PUPDR,\r
+   VAL_GPIOI_ODR,   VAL_GPIOI_AFRL,   VAL_GPIOI_AFRH},\r
+#endif\r
+#if STM32_HAS_GPIOJ\r
+  {VAL_GPIOJ_MODER, VAL_GPIOJ_OTYPER, VAL_GPIOJ_OSPEEDR, VAL_GPIOJ_PUPDR,\r
+   VAL_GPIOJ_ODR,   VAL_GPIOJ_AFRL,   VAL_GPIOJ_AFRH},\r
+#endif\r
+#if STM32_HAS_GPIOK\r
+  {VAL_GPIOK_MODER, VAL_GPIOK_OTYPER, VAL_GPIOK_OSPEEDR, VAL_GPIOK_PUPDR,\r
+   VAL_GPIOK_ODR,   VAL_GPIOK_AFRL,   VAL_GPIOK_AFRH}\r
+#endif\r
+};\r
+\r
+/*===========================================================================*/\r
+/* Driver local functions.                                                   */\r
+/*===========================================================================*/\r
+\r
+static void gpio_init(stm32_gpio_t *gpiop, const gpio_setup_t *config) {\r
+\r
+  gpiop->OTYPER  = config->otyper;\r
+  gpiop->OSPEEDR = config->ospeedr;\r
+  gpiop->PUPDR   = config->pupdr;\r
+  gpiop->ODR     = config->odr;\r
+  gpiop->AFRL    = config->afrl;\r
+  gpiop->AFRH    = config->afrh;\r
+  gpiop->MODER   = config->moder;\r
+}\r
+\r
+static void stm32_gpio_init(void) {\r
+\r
+  /* Enabling GPIO-related clocks, the mask comes from the\r
+     registry header file.*/\r
+  rccResetAHB4(STM32_GPIO_EN_MASK);\r
+  rccEnableAHB4(STM32_GPIO_EN_MASK, true);\r
+\r
+  /* Initializing all the defined GPIO ports.*/\r
+#if STM32_HAS_GPIOA\r
+  gpio_init(GPIOA, &gpio_default_config.PAData);\r
+#endif\r
+#if STM32_HAS_GPIOB\r
+  gpio_init(GPIOB, &gpio_default_config.PBData);\r
+#endif\r
+#if STM32_HAS_GPIOC\r
+  gpio_init(GPIOC, &gpio_default_config.PCData);\r
+#endif\r
+#if STM32_HAS_GPIOD\r
+  gpio_init(GPIOD, &gpio_default_config.PDData);\r
+#endif\r
+#if STM32_HAS_GPIOE\r
+  gpio_init(GPIOE, &gpio_default_config.PEData);\r
+#endif\r
+#if STM32_HAS_GPIOF\r
+  gpio_init(GPIOF, &gpio_default_config.PFData);\r
+#endif\r
+#if STM32_HAS_GPIOG\r
+  gpio_init(GPIOG, &gpio_default_config.PGData);\r
+#endif\r
+#if STM32_HAS_GPIOH\r
+  gpio_init(GPIOH, &gpio_default_config.PHData);\r
+#endif\r
+#if STM32_HAS_GPIOI\r
+  gpio_init(GPIOI, &gpio_default_config.PIData);\r
+#endif\r
+#if STM32_HAS_GPIOJ\r
+  gpio_init(GPIOJ, &gpio_default_config.PJData);\r
+#endif\r
+#if STM32_HAS_GPIOK\r
+  gpio_init(GPIOK, &gpio_default_config.PKData);\r
+#endif\r
+}\r
+\r
+/*===========================================================================*/\r
+/* Driver interrupt handlers.                                                */\r
+/*===========================================================================*/\r
+\r
+/*===========================================================================*/\r
+/* Driver exported functions.                                                */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Early initialization code.\r
+ * @details GPIO ports and system clocks are initialized before everything\r
+ *          else.\r
+ */\r
+void __early_init(void) {\r
+\r
+  stm32_gpio_init();\r
+  stm32_clock_init();\r
+}\r
+\r
+#if HAL_USE_SDC || defined(__DOXYGEN__)\r
+/**\r
+ * @brief   SDC card detection.\r
+ */\r
+bool sdc_lld_is_card_inserted(SDCDriver *sdcp) {\r
+\r
+  (void)sdcp;\r
+  /* CHTODO: Fill the implementation.*/\r
+  return true;\r
+}\r
+\r
+/**\r
+ * @brief   SDC card write protection detection.\r
+ */\r
+bool sdc_lld_is_write_protected(SDCDriver *sdcp) {\r
+\r
+  (void)sdcp;\r
+  /* CHTODO: Fill the implementation.*/\r
+  return false;\r
+}\r
+#endif /* HAL_USE_SDC */\r
+\r
+#if HAL_USE_MMC_SPI || defined(__DOXYGEN__)\r
+/**\r
+ * @brief   MMC_SPI card detection.\r
+ */\r
+bool mmc_lld_is_card_inserted(MMCDriver *mmcp) {\r
+\r
+  (void)mmcp;\r
+  /* CHTODO: Fill the implementation.*/\r
+  return true;\r
+}\r
+\r
+/**\r
+ * @brief   MMC_SPI card write protection detection.\r
+ */\r
+bool mmc_lld_is_write_protected(MMCDriver *mmcp) {\r
+\r
+  (void)mmcp;\r
+  /* CHTODO: Fill the implementation.*/\r
+  return false;\r
+}\r
+#endif\r
+\r
+/**\r
+ * @brief   Board-specific initialization code.\r
+ * @note    You can add your board-specific code here.\r
+ */\r
+void boardInit(void) {\r
+    // Enable the FPU (floating-point unit)\r
+    SCB->CPACR |= 0xF << 20;\r
+\r
+    // Setup the MPU (memory protection unit):\r
+    //     Region 2: Data for algorithm thread\r
+    //     Region 3: Code for algorithm thread\r
+    //     Region 4: User algorithm code\r
+    mpuConfigureRegion(MPU_REGION_2,\r
+                       0x20000000,\r
+                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |\r
+                       MPU_RASR_SIZE_64K |\r
+                       MPU_RASR_ENABLE);\r
+    mpuConfigureRegion(MPU_REGION_3,\r
+                       0x0807F800,\r
+                       MPU_RASR_ATTR_AP_RO_RO | MPU_RASR_ATTR_NON_CACHEABLE |\r
+                       MPU_RASR_SIZE_2K |\r
+                       MPU_RASR_ENABLE);\r
+    mpuConfigureRegion(MPU_REGION_4,\r
+                       0x00000000,\r
+                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |\r
+                       MPU_RASR_SIZE_64K |\r
+                       MPU_RASR_ENABLE);\r
+}\r
diff --git a/firmware/source/board/board_l4.c b/firmware/source/board/board_l4.c
new file mode 100644 (file)
index 0000000..55af697
--- /dev/null
@@ -0,0 +1,307 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+/*\r
+ * This file has been automatically generated using ChibiStudio board\r
+ * generator plugin. Do not edit manually.\r
+ */\r
+\r
+#include "hal.h"\r
+#include "stm32_gpio.h"\r
+\r
+/*===========================================================================*/\r
+/* Driver local definitions.                                                 */\r
+/*===========================================================================*/\r
+\r
+/*===========================================================================*/\r
+/* Driver exported variables.                                                */\r
+/*===========================================================================*/\r
+\r
+/*===========================================================================*/\r
+/* Driver local variables and types.                                         */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Type of STM32 GPIO port setup.\r
+ */\r
+typedef struct {\r
+  uint32_t              moder;\r
+  uint32_t              otyper;\r
+  uint32_t              ospeedr;\r
+  uint32_t              pupdr;\r
+  uint32_t              odr;\r
+  uint32_t              afrl;\r
+  uint32_t              afrh;\r
+  uint32_t              ascr;\r
+  uint32_t              lockr;\r
+} gpio_setup_t;\r
+\r
+/**\r
+ * @brief   Type of STM32 GPIO initialization data.\r
+ */\r
+typedef struct {\r
+#if STM32_HAS_GPIOA || defined(__DOXYGEN__)\r
+  gpio_setup_t          PAData;\r
+#endif\r
+#if STM32_HAS_GPIOB || defined(__DOXYGEN__)\r
+  gpio_setup_t          PBData;\r
+#endif\r
+#if STM32_HAS_GPIOC || defined(__DOXYGEN__)\r
+  gpio_setup_t          PCData;\r
+#endif\r
+#if STM32_HAS_GPIOD || defined(__DOXYGEN__)\r
+  gpio_setup_t          PDData;\r
+#endif\r
+#if STM32_HAS_GPIOE || defined(__DOXYGEN__)\r
+  gpio_setup_t          PEData;\r
+#endif\r
+#if STM32_HAS_GPIOF || defined(__DOXYGEN__)\r
+  gpio_setup_t          PFData;\r
+#endif\r
+#if STM32_HAS_GPIOG || defined(__DOXYGEN__)\r
+  gpio_setup_t          PGData;\r
+#endif\r
+#if STM32_HAS_GPIOH || defined(__DOXYGEN__)\r
+  gpio_setup_t          PHData;\r
+#endif\r
+#if STM32_HAS_GPIOI || defined(__DOXYGEN__)\r
+  gpio_setup_t          PIData;\r
+#endif\r
+#if STM32_HAS_GPIOJ || defined(__DOXYGEN__)\r
+  gpio_setup_t          PJData;\r
+#endif\r
+#if STM32_HAS_GPIOK || defined(__DOXYGEN__)\r
+  gpio_setup_t          PKData;\r
+#endif\r
+} gpio_config_t;\r
+\r
+/**\r
+ * @brief   STM32 GPIO static initialization data.\r
+ */\r
+static const gpio_config_t gpio_default_config = {\r
+#if STM32_HAS_GPIOA\r
+  {VAL_GPIOA_MODER, VAL_GPIOA_OTYPER, VAL_GPIOA_OSPEEDR, VAL_GPIOA_PUPDR,\r
+   VAL_GPIOA_ODR,   VAL_GPIOA_AFRL,   VAL_GPIOA_AFRH,    VAL_GPIOA_ASCR,\r
+   VAL_GPIOA_LOCKR},\r
+#endif\r
+#if STM32_HAS_GPIOB\r
+  {VAL_GPIOB_MODER, VAL_GPIOB_OTYPER, VAL_GPIOB_OSPEEDR, VAL_GPIOB_PUPDR,\r
+   VAL_GPIOB_ODR,   VAL_GPIOB_AFRL,   VAL_GPIOB_AFRH,    VAL_GPIOB_ASCR,\r
+   VAL_GPIOB_LOCKR},\r
+#endif\r
+#if STM32_HAS_GPIOC\r
+  {VAL_GPIOC_MODER, VAL_GPIOC_OTYPER, VAL_GPIOC_OSPEEDR, VAL_GPIOC_PUPDR,\r
+   VAL_GPIOC_ODR,   VAL_GPIOC_AFRL,   VAL_GPIOC_AFRH,    VAL_GPIOC_ASCR,\r
+   VAL_GPIOC_LOCKR},\r
+#endif\r
+#if STM32_HAS_GPIOD\r
+  {VAL_GPIOD_MODER, VAL_GPIOD_OTYPER, VAL_GPIOD_OSPEEDR, VAL_GPIOD_PUPDR,\r
+   VAL_GPIOD_ODR,   VAL_GPIOD_AFRL,   VAL_GPIOD_AFRH,    VAL_GPIOD_ASCR,\r
+   VAL_GPIOD_LOCKR},\r
+#endif\r
+#if STM32_HAS_GPIOE\r
+  {VAL_GPIOE_MODER, VAL_GPIOE_OTYPER, VAL_GPIOE_OSPEEDR, VAL_GPIOE_PUPDR,\r
+   VAL_GPIOE_ODR,   VAL_GPIOE_AFRL,   VAL_GPIOE_AFRH,    VAL_GPIOE_ASCR,\r
+   VAL_GPIOE_LOCKR},\r
+#endif\r
+#if STM32_HAS_GPIOF\r
+  {VAL_GPIOF_MODER, VAL_GPIOF_OTYPER, VAL_GPIOF_OSPEEDR, VAL_GPIOF_PUPDR,\r
+   VAL_GPIOF_ODR,   VAL_GPIOF_AFRL,   VAL_GPIOF_AFRH,    VAL_GPIOF_ASCR,\r
+   VAL_GPIOF_LOCKR},\r
+#endif\r
+#if STM32_HAS_GPIOG\r
+  {VAL_GPIOG_MODER, VAL_GPIOG_OTYPER, VAL_GPIOG_OSPEEDR, VAL_GPIOG_PUPDR,\r
+   VAL_GPIOG_ODR,   VAL_GPIOG_AFRL,   VAL_GPIOG_AFRH,    VAL_GPIOG_ASCR,\r
+   VAL_GPIOG_LOCKR},\r
+#endif\r
+#if STM32_HAS_GPIOH\r
+  {VAL_GPIOH_MODER, VAL_GPIOH_OTYPER, VAL_GPIOH_OSPEEDR, VAL_GPIOH_PUPDR,\r
+   VAL_GPIOH_ODR,   VAL_GPIOH_AFRL,   VAL_GPIOH_AFRH,    VAL_GPIOH_ASCR,\r
+   VAL_GPIOH_LOCKR},\r
+#endif\r
+#if STM32_HAS_GPIOI\r
+  {VAL_GPIOI_MODER, VAL_GPIOI_OTYPER, VAL_GPIOI_OSPEEDR, VAL_GPIOI_PUPDR,\r
+   VAL_GPIOI_ODR,   VAL_GPIOI_AFRL,   VAL_GPIOI_AFRH,    VAL_GPIOI_ASCR,\r
+   VAL_GPIOI_LOCKR},\r
+#endif\r
+#if STM32_HAS_GPIOJ\r
+  {VAL_GPIOJ_MODER, VAL_GPIOJ_OTYPER, VAL_GPIOJ_OSPEEDR, VAL_GPIOJ_PUPDR,\r
+   VAL_GPIOJ_ODR,   VAL_GPIOJ_AFRL,   VAL_GPIOJ_AFRH,    VAL_GPIOJ_ASCR,\r
+   VAL_GPIOJ_LOCKR},\r
+#endif\r
+#if STM32_HAS_GPIOK\r
+  {VAL_GPIOK_MODER, VAL_GPIOK_OTYPER, VAL_GPIOK_OSPEEDR, VAL_GPIOK_PUPDR,\r
+   VAL_GPIOK_ODR,   VAL_GPIOK_AFRL,   VAL_GPIOK_AFRH,    VAL_GPIOK_ASCR,\r
+   VAL_GPIOK_LOCKR}\r
+#endif\r
+};\r
+\r
+/*===========================================================================*/\r
+/* Driver local functions.                                                   */\r
+/*===========================================================================*/\r
+\r
+static void gpio_init(stm32_gpio_t *gpiop, const gpio_setup_t *config) {\r
+\r
+  gpiop->OTYPER  = config->otyper;\r
+  gpiop->ASCR    = config->ascr;\r
+  gpiop->OSPEEDR = config->ospeedr;\r
+  gpiop->PUPDR   = config->pupdr;\r
+  gpiop->ODR     = config->odr;\r
+  gpiop->AFRL    = config->afrl;\r
+  gpiop->AFRH    = config->afrh;\r
+  gpiop->MODER   = config->moder;\r
+  gpiop->LOCKR   = config->lockr;\r
+}\r
+\r
+static void stm32_gpio_init(void) {\r
+\r
+  /* Enabling GPIO-related clocks, the mask comes from the\r
+     registry header file.*/\r
+  rccResetAHB2(STM32_GPIO_EN_MASK);\r
+  rccEnableAHB2(STM32_GPIO_EN_MASK, true);\r
+\r
+  /* Initializing all the defined GPIO ports.*/\r
+#if STM32_HAS_GPIOA\r
+  gpio_init(GPIOA, &gpio_default_config.PAData);\r
+#endif\r
+#if STM32_HAS_GPIOB\r
+  gpio_init(GPIOB, &gpio_default_config.PBData);\r
+#endif\r
+#if STM32_HAS_GPIOC\r
+  gpio_init(GPIOC, &gpio_default_config.PCData);\r
+#endif\r
+#if STM32_HAS_GPIOD\r
+  gpio_init(GPIOD, &gpio_default_config.PDData);\r
+#endif\r
+#if STM32_HAS_GPIOE\r
+  gpio_init(GPIOE, &gpio_default_config.PEData);\r
+#endif\r
+#if STM32_HAS_GPIOF\r
+  gpio_init(GPIOF, &gpio_default_config.PFData);\r
+#endif\r
+#if STM32_HAS_GPIOG\r
+  gpio_init(GPIOG, &gpio_default_config.PGData);\r
+#endif\r
+#if STM32_HAS_GPIOH\r
+  gpio_init(GPIOH, &gpio_default_config.PHData);\r
+#endif\r
+#if STM32_HAS_GPIOI\r
+  gpio_init(GPIOI, &gpio_default_config.PIData);\r
+#endif\r
+#if STM32_HAS_GPIOJ\r
+  gpio_init(GPIOJ, &gpio_default_config.PJData);\r
+#endif\r
+#if STM32_HAS_GPIOK\r
+  gpio_init(GPIOK, &gpio_default_config.PKData);\r
+#endif\r
+}\r
+\r
+/*===========================================================================*/\r
+/* Driver interrupt handlers.                                                */\r
+/*===========================================================================*/\r
+\r
+/*===========================================================================*/\r
+/* Driver exported functions.                                                */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Early initialization code.\r
+ * @details GPIO ports and system clocks are initialized before everything\r
+ *          else.\r
+ */\r
+void __early_init(void) {\r
+\r
+  stm32_gpio_init();\r
+  stm32_clock_init();\r
+}\r
+\r
+#if HAL_USE_SDC || defined(__DOXYGEN__)\r
+/**\r
+ * @brief   SDC card detection.\r
+ */\r
+bool sdc_lld_is_card_inserted(SDCDriver *sdcp) {\r
+\r
+  (void)sdcp;\r
+  /* CHTODO: Fill the implementation.*/\r
+  return true;\r
+}\r
+\r
+/**\r
+ * @brief   SDC card write protection detection.\r
+ */\r
+bool sdc_lld_is_write_protected(SDCDriver *sdcp) {\r
+\r
+  (void)sdcp;\r
+  /* CHTODO: Fill the implementation.*/\r
+  return false;\r
+}\r
+#endif /* HAL_USE_SDC */\r
+\r
+#if HAL_USE_MMC_SPI || defined(__DOXYGEN__)\r
+/**\r
+ * @brief   MMC_SPI card detection.\r
+ */\r
+bool mmc_lld_is_card_inserted(MMCDriver *mmcp) {\r
+\r
+  (void)mmcp;\r
+  /* CHTODO: Fill the implementation.*/\r
+  return true;\r
+}\r
+\r
+/**\r
+ * @brief   MMC_SPI card write protection detection.\r
+ */\r
+bool mmc_lld_is_write_protected(MMCDriver *mmcp) {\r
+\r
+  (void)mmcp;\r
+  /* CHTODO: Fill the implementation.*/\r
+  return false;\r
+}\r
+#endif\r
+\r
+/**\r
+ * @brief   Board-specific initialization code.\r
+ * @note    You can add your board-specific code here.\r
+ */\r
+void boardInit(void) {\r
+    palSetLineMode(LINE_LED_RED, PAL_MODE_OUTPUT_PUSHPULL);\r
+    palSetLineMode(LINE_LED_GREEN, PAL_MODE_OUTPUT_PUSHPULL);\r
+    palSetLineMode(LINE_LED_BLUE, PAL_MODE_OUTPUT_PUSHPULL);\r
+    palClearLine(LINE_LED_RED);\r
+    palClearLine(LINE_LED_GREEN);\r
+    palClearLine(LINE_LED_BLUE);\r
+\r
+    SCB->CPACR |= 0xF << 20; // Enable FPU\r
+\r
+    // Region 2: Data for algorithm thread and ADC/DAC buffers\r
+    // Region 3: Code for algorithm thread\r
+    // Region 4: User algorithm code\r
+    mpuConfigureRegion(MPU_REGION_2,\r
+                       0x20008000,\r
+                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |\r
+                       MPU_RASR_SIZE_128K |\r
+                       MPU_RASR_ENABLE);\r
+    mpuConfigureRegion(MPU_REGION_3,\r
+                       0x0807F800,\r
+                       MPU_RASR_ATTR_AP_RO_RO | MPU_RASR_ATTR_NON_CACHEABLE |\r
+                       MPU_RASR_SIZE_2K |\r
+                       MPU_RASR_ENABLE);\r
+    mpuConfigureRegion(MPU_REGION_4,\r
+                       0x10000000,\r
+                       MPU_RASR_ATTR_AP_RW_RW | MPU_RASR_ATTR_NON_CACHEABLE |\r
+                       MPU_RASR_SIZE_32K |\r
+                       MPU_RASR_ENABLE);\r
+}\r
diff --git a/firmware/source/board/h7/board.h b/firmware/source/board/h7/board.h
new file mode 100644 (file)
index 0000000..58ba40e
--- /dev/null
@@ -0,0 +1,1642 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2019 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+/*\r
+ * This file has been automatically generated using ChibiStudio board\r
+ * generator plugin. Do not edit manually.\r
+ */\r
+\r
+#ifndef BOARD_H\r
+#define BOARD_H\r
+\r
+/*===========================================================================*/\r
+/* Driver constants.                                                         */\r
+/*===========================================================================*/\r
+\r
+/*\r
+ * Setup for STMicroelectronics STM32 Nucleo144-H743ZI board.\r
+ */\r
+\r
+/*\r
+ * Board identifier.\r
+ */\r
+#define BOARD_ST_NUCLEO144_H743ZI\r
+#define BOARD_NAME                  "STMicroelectronics STM32 Nucleo144-H743ZI"\r
+\r
+/*\r
+ * Ethernet PHY type.\r
+ */\r
+#define BOARD_PHY_ID                MII_LAN8742A_ID\r
+#define BOARD_PHY_RMII\r
+\r
+/*\r
+ * Board oscillators-related settings.\r
+ */\r
+#if !defined(STM32_LSECLK)\r
+#define STM32_LSECLK                32768U\r
+#endif\r
+\r
+#define STM32_LSEDRV                (3U << 3U)\r
+\r
+#if !defined(STM32_HSECLK)\r
+#define STM32_HSECLK                8000000U\r
+#endif\r
+\r
+#define STM32_HSE_BYPASS\r
+\r
+/*\r
+ * MCU type as defined in the ST header.\r
+ */\r
+#define STM32H723xx\r
+\r
+/*\r
+ * IO pins assignments.\r
+ */\r
+#define GPIOA_PIN0                  0U\r
+#define GPIOA_RMII_REF_CLK          1U\r
+#define GPIOA_RMII_MDIO             2U\r
+#define GPIOA_PIN3                  3U\r
+#define GPIOA_PIN4                  4U\r
+#define GPIOA_PIN5                  5U\r
+#define GPIOA_PIN6                  6U\r
+#define GPIOA_RMII_CRS_DV           7U\r
+#define GPIOA_USB_SOF               8U\r
+#define GPIOA_MCO1                  8U\r
+#define GPIOA_USB_VBUS              9U\r
+#define GPIOA_USB_ID                10U\r
+#define GPIOA_USB_DM                11U\r
+#define GPIOA_USB_DP                12U\r
+#define GPIOA_SWDIO                 13U\r
+#define GPIOA_SWCLK                 14U\r
+#define GPIOA_T_JTDI                15U\r
+\r
+#define GPIOB_LED1                  0U\r
+#define GPIOB_LED_GREEN             0U\r
+#define GPIOB_LED                   0U\r
+#define GPIOB_PIN1                  1U\r
+#define GPIOB_PIN2                  2U\r
+#define GPIOB_SWO                   3U\r
+#define GPIOB_PIN4                  4U\r
+#define GPIOB_PIN5                  5U\r
+#define GPIOB_PIN6                  6U\r
+#define GPIOB_PIN7                  7U\r
+#define GPIOB_PIN8                  8U\r
+#define GPIOB_PIN9                  9U\r
+#define GPIOB_PIN10                 10U\r
+#define GPIOB_PIN11                 11U\r
+#define GPIOB_PIN12                 12U\r
+#define GPIOB_RMII_TXD1             13U\r
+#define GPIOB_LED3                  14U\r
+#define GPIOB_LED_RED               14U\r
+#define GPIOB_PIN15                 15U\r
+\r
+#define GPIOC_PIN0                  0U\r
+#define GPIOC_RMII_MDC              1U\r
+#define GPIOC_PIN2                  2U\r
+#define GPIOC_PIN3                  3U\r
+#define GPIOC_RMII_RXD0             4U\r
+#define GPIOC_RMII_RXD1             5U\r
+#define GPIOC_PIN6                  6U\r
+#define GPIOC_PIN7                  7U\r
+#define GPIOC_PIN8                  8U\r
+#define GPIOC_PIN9                  9U\r
+#define GPIOC_PIN10                 10U\r
+#define GPIOC_PIN11                 11U\r
+#define GPIOC_PIN12                 12U\r
+#define GPIOC_BUTTON                13U\r
+#define GPIOC_OSC32_IN              14U\r
+#define GPIOC_OSC32_OUT             15U\r
+\r
+#define GPIOD_PIN0                  0U\r
+#define GPIOD_PIN1                  1U\r
+#define GPIOD_PIN2                  2U\r
+#define GPIOD_PIN3                  3U\r
+#define GPIOD_PIN4                  4U\r
+#define GPIOD_PIN5                  5U\r
+#define GPIOD_PIN6                  6U\r
+#define GPIOD_PIN7                  7U\r
+#define GPIOD_USART3_RX             8U\r
+#define GPIOD_STLK_RX               8U\r
+#define GPIOD_USART3_TX             9U\r
+#define GPIOD_STLK_TX               9U\r
+#define GPIOD_PIN10                 10U\r
+#define GPIOD_PIN11                 11U\r
+#define GPIOD_PIN12                 12U\r
+#define GPIOD_PIN13                 13U\r
+#define GPIOD_PIN14                 14U\r
+#define GPIOD_PIN15                 15U\r
+\r
+#define GPIOE_PIN0                  0U\r
+#define GPIOE_LED2                  1U\r
+#define GPIOE_LED_YELLOW            1U\r
+#define GPIOE_PIN2                  2U\r
+#define GPIOE_PIN3                  3U\r
+#define GPIOE_PIN4                  4U\r
+#define GPIOE_PIN5                  5U\r
+#define GPIOE_PIN6                  6U\r
+#define GPIOE_PIN7                  7U\r
+#define GPIOE_PIN8                  8U\r
+#define GPIOE_PIN9                  9U\r
+#define GPIOE_PIN10                 10U\r
+#define GPIOE_PIN11                 11U\r
+#define GPIOE_PIN12                 12U\r
+#define GPIOE_PIN13                 13U\r
+#define GPIOE_PIN14                 14U\r
+#define GPIOE_PIN15                 15U\r
+\r
+#define GPIOF_PIN0                  0U\r
+#define GPIOF_PIN1                  1U\r
+#define GPIOF_PIN2                  2U\r
+#define GPIOF_PIN3                  3U\r
+#define GPIOF_PIN4                  4U\r
+#define GPIOF_PIN5                  5U\r
+#define GPIOF_PIN6                  6U\r
+#define GPIOF_PIN7                  7U\r
+#define GPIOF_PIN8                  8U\r
+#define GPIOF_PIN9                  9U\r
+#define GPIOF_PIN10                 10U\r
+#define GPIOF_PIN11                 11U\r
+#define GPIOF_PIN12                 12U\r
+#define GPIOF_PIN13                 13U\r
+#define GPIOF_PIN14                 14U\r
+#define GPIOF_PIN15                 15U\r
+\r
+#define GPIOG_PIN0                  0U\r
+#define GPIOG_PIN1                  1U\r
+#define GPIOG_PIN2                  2U\r
+#define GPIOG_PIN3                  3U\r
+#define GPIOG_PIN4                  4U\r
+#define GPIOG_PIN5                  5U\r
+#define GPIOG_USB_FS_PWR_EN         6U\r
+#define GPIOG_USB_FS_OVCR           7U\r
+#define GPIOG_PIN8                  8U\r
+#define GPIOG_PIN9                  9U\r
+#define GPIOG_PIN10                 10U\r
+#define GPIOG_RMII_TX_EN            11U\r
+#define GPIOG_PIN12                 12U\r
+#define GPIOG_RMII_TXD0             13U\r
+#define GPIOG_PIN14                 14U\r
+#define GPIOG_PIN15                 15U\r
+\r
+#define GPIOH_OSC_IN                0U\r
+#define GPIOH_OSC_OUT               1U\r
+#define GPIOH_PIN2                  2U\r
+#define GPIOH_PIN3                  3U\r
+#define GPIOH_PIN4                  4U\r
+#define GPIOH_PIN5                  5U\r
+#define GPIOH_PIN6                  6U\r
+#define GPIOH_PIN7                  7U\r
+#define GPIOH_PIN8                  8U\r
+#define GPIOH_PIN9                  9U\r
+#define GPIOH_PIN10                 10U\r
+#define GPIOH_PIN11                 11U\r
+#define GPIOH_PIN12                 12U\r
+#define GPIOH_PIN13                 13U\r
+#define GPIOH_PIN14                 14U\r
+#define GPIOH_PIN15                 15U\r
+\r
+#define GPIOI_PIN0                  0U\r
+#define GPIOI_PIN1                  1U\r
+#define GPIOI_PIN2                  2U\r
+#define GPIOI_PIN3                  3U\r
+#define GPIOI_PIN4                  4U\r
+#define GPIOI_PIN5                  5U\r
+#define GPIOI_PIN6                  6U\r
+#define GPIOI_PIN7                  7U\r
+#define GPIOI_PIN8                  8U\r
+#define GPIOI_PIN9                  9U\r
+#define GPIOI_PIN10                 10U\r
+#define GPIOI_PIN11                 11U\r
+#define GPIOI_PIN12                 12U\r
+#define GPIOI_PIN13                 13U\r
+#define GPIOI_PIN14                 14U\r
+#define GPIOI_PIN15                 15U\r
+\r
+#define GPIOJ_PIN0                  0U\r
+#define GPIOJ_PIN1                  1U\r
+#define GPIOJ_PIN2                  2U\r
+#define GPIOJ_PIN3                  3U\r
+#define GPIOJ_PIN4                  4U\r
+#define GPIOJ_PIN5                  5U\r
+#define GPIOJ_PIN6                  6U\r
+#define GPIOJ_PIN7                  7U\r
+#define GPIOJ_PIN8                  8U\r
+#define GPIOJ_PIN9                  9U\r
+#define GPIOJ_PIN10                 10U\r
+#define GPIOJ_PIN11                 11U\r
+#define GPIOJ_PIN12                 12U\r
+#define GPIOJ_PIN13                 13U\r
+#define GPIOJ_PIN14                 14U\r
+#define GPIOJ_PIN15                 15U\r
+\r
+#define GPIOK_PIN0                  0U\r
+#define GPIOK_PIN1                  1U\r
+#define GPIOK_PIN2                  2U\r
+#define GPIOK_PIN3                  3U\r
+#define GPIOK_PIN4                  4U\r
+#define GPIOK_PIN5                  5U\r
+#define GPIOK_PIN6                  6U\r
+#define GPIOK_PIN7                  7U\r
+#define GPIOK_PIN8                  8U\r
+#define GPIOK_PIN9                  9U\r
+#define GPIOK_PIN10                 10U\r
+#define GPIOK_PIN11                 11U\r
+#define GPIOK_PIN12                 12U\r
+#define GPIOK_PIN13                 13U\r
+#define GPIOK_PIN14                 14U\r
+#define GPIOK_PIN15                 15U\r
+\r
+/*\r
+ * IO lines assignments.\r
+ */\r
+#define LINE_RMII_REF_CLK           PAL_LINE(GPIOA, 1U)\r
+#define LINE_RMII_MDIO              PAL_LINE(GPIOA, 2U)\r
+#define LINE_RMII_CRS_DV            PAL_LINE(GPIOA, 7U)\r
+#define LINE_USB_SOF                PAL_LINE(GPIOA, 8U)\r
+#define LINE_MCO1                   PAL_LINE(GPIOA, 8U)\r
+#define LINE_USB_VBUS               PAL_LINE(GPIOA, 9U)\r
+#define LINE_USB_ID                 PAL_LINE(GPIOA, 10U)\r
+#define LINE_USB_DM                 PAL_LINE(GPIOA, 11U)\r
+#define LINE_USB_DP                 PAL_LINE(GPIOA, 12U)\r
+#define LINE_SWDIO                  PAL_LINE(GPIOA, 13U)\r
+#define LINE_SWCLK                  PAL_LINE(GPIOA, 14U)\r
+#define LINE_T_JTDI                 PAL_LINE(GPIOA, 15U)\r
+#define LINE_LED1                   PAL_LINE(GPIOB, 0U)\r
+#define LINE_LED_GREEN              PAL_LINE(GPIOB, 0U)\r
+#define LINE_LED                    PAL_LINE(GPIOB, 0U)\r
+#define LINE_SWO                    PAL_LINE(GPIOB, 3U)\r
+#define LINE_LED2                   PAL_LINE(GPIOE, 1U)\r
+#define LINE_LED_YELLOW             PAL_LINE(GPIOE, 1U)\r
+#define LINE_RMII_TXD1              PAL_LINE(GPIOB, 13U)\r
+#define LINE_LED3                   PAL_LINE(GPIOB, 14U)\r
+#define LINE_LED_RED                PAL_LINE(GPIOB, 14U)\r
+#define LINE_RMII_MDC               PAL_LINE(GPIOC, 1U)\r
+#define LINE_RMII_RXD0              PAL_LINE(GPIOC, 4U)\r
+#define LINE_RMII_RXD1              PAL_LINE(GPIOC, 5U)\r
+#define LINE_BUTTON                 PAL_LINE(GPIOC, 13U)\r
+#define LINE_OSC32_IN               PAL_LINE(GPIOC, 14U)\r
+#define LINE_OSC32_OUT              PAL_LINE(GPIOC, 15U)\r
+#define LINE_USART3_RX              PAL_LINE(GPIOD, 8U)\r
+#define LINE_STLK_RX                PAL_LINE(GPIOD, 8U)\r
+#define LINE_USART3_TX              PAL_LINE(GPIOD, 9U)\r
+#define LINE_STLK_TX                PAL_LINE(GPIOD, 9U)\r
+#define LINE_USB_FS_PWR_EN          PAL_LINE(GPIOG, 6U)\r
+#define LINE_USB_FS_OVCR            PAL_LINE(GPIOG, 7U)\r
+#define LINE_RMII_TX_EN             PAL_LINE(GPIOG, 11U)\r
+#define LINE_RMII_TXD0              PAL_LINE(GPIOG, 13U)\r
+#define LINE_OSC_IN                 PAL_LINE(GPIOH, 0U)\r
+#define LINE_OSC_OUT                PAL_LINE(GPIOH, 1U)\r
+\r
+/*===========================================================================*/\r
+/* Driver pre-compile time settings.                                         */\r
+/*===========================================================================*/\r
+\r
+/*===========================================================================*/\r
+/* Derived constants and error checks.                                       */\r
+/*===========================================================================*/\r
+\r
+/*===========================================================================*/\r
+/* Driver data structures and types.                                         */\r
+/*===========================================================================*/\r
+\r
+/*===========================================================================*/\r
+/* Driver macros.                                                            */\r
+/*===========================================================================*/\r
+\r
+/*\r
+ * I/O ports initial setup, this configuration is established soon after reset\r
+ * in the initialization code.\r
+ * Please refer to the STM32 Reference Manual for details.\r
+ */\r
+#define PIN_MODE_INPUT(n)           (0U << ((n) * 2U))\r
+#define PIN_MODE_OUTPUT(n)          (1U << ((n) * 2U))\r
+#define PIN_MODE_ALTERNATE(n)       (2U << ((n) * 2U))\r
+#define PIN_MODE_ANALOG(n)          (3U << ((n) * 2U))\r
+#define PIN_ODR_LOW(n)              (0U << (n))\r
+#define PIN_ODR_HIGH(n)             (1U << (n))\r
+#define PIN_OTYPE_PUSHPULL(n)       (0U << (n))\r
+#define PIN_OTYPE_OPENDRAIN(n)      (1U << (n))\r
+#define PIN_OSPEED_VERYLOW(n)       (0U << ((n) * 2U))\r
+#define PIN_OSPEED_LOW(n)           (1U << ((n) * 2U))\r
+#define PIN_OSPEED_MEDIUM(n)        (2U << ((n) * 2U))\r
+#define PIN_OSPEED_HIGH(n)          (3U << ((n) * 2U))\r
+#define PIN_PUPDR_FLOATING(n)       (0U << ((n) * 2U))\r
+#define PIN_PUPDR_PULLUP(n)         (1U << ((n) * 2U))\r
+#define PIN_PUPDR_PULLDOWN(n)       (2U << ((n) * 2U))\r
+#define PIN_AFIO_AF(n, v)           ((v) << (((n) % 8U) * 4U))\r
+\r
+/*\r
+ * GPIOA setup:\r
+ *\r
+ * PA0  - PIN0                      (input pullup).\r
+ * PA1  - RMII_REF_CLK              (alternate 11).\r
+ * PA2  - RMII_MDIO                 (alternate 11).\r
+ * PA3  - PIN3                      (input pullup).\r
+ * PA4  - PIN4                      (input pullup).\r
+ * PA5  - PIN5                      (input pullup).\r
+ * PA6  - PIN6                      (input pullup).\r
+ * PA7  - RMII_CRS_DV               (alternate 11).\r
+ * PA8  - USB_SOF MCO1              (alternate 10).\r
+ * PA9  - USB_VBUS                  (analog).\r
+ * PA10 - USB_ID                    (alternate 10).\r
+ * PA11 - USB_DM                    (alternate 10).\r
+ * PA12 - USB_DP                    (alternate 10).\r
+ * PA13 - SWDIO                     (alternate 0).\r
+ * PA14 - SWCLK                     (alternate 0).\r
+ * PA15 - T_JTDI                    (alternate 0).\r
+ */\r
+#define VAL_GPIOA_MODER             (PIN_MODE_INPUT(GPIOA_PIN0) |           \\r
+                                     PIN_MODE_ALTERNATE(GPIOA_RMII_REF_CLK) |\\r
+                                     PIN_MODE_ALTERNATE(GPIOA_RMII_MDIO) |  \\r
+                                     PIN_MODE_INPUT(GPIOA_PIN3) |           \\r
+                                     PIN_MODE_INPUT(GPIOA_PIN4) |           \\r
+                                     PIN_MODE_INPUT(GPIOA_PIN5) |           \\r
+                                     PIN_MODE_INPUT(GPIOA_PIN6) |           \\r
+                                     PIN_MODE_ALTERNATE(GPIOA_RMII_CRS_DV) |\\r
+                                     PIN_MODE_ALTERNATE(GPIOA_USB_SOF) |    \\r
+                                     PIN_MODE_ANALOG(GPIOA_USB_VBUS) |      \\r
+                                     PIN_MODE_ALTERNATE(GPIOA_USB_ID) |     \\r
+                                     PIN_MODE_ALTERNATE(GPIOA_USB_DM) |     \\r
+                                     PIN_MODE_ALTERNATE(GPIOA_USB_DP) |     \\r
+                                     PIN_MODE_ALTERNATE(GPIOA_SWDIO) |      \\r
+                                     PIN_MODE_ALTERNATE(GPIOA_SWCLK) |      \\r
+                                     PIN_MODE_ALTERNATE(GPIOA_T_JTDI))\r
+#define VAL_GPIOA_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOA_PIN0) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_RMII_REF_CLK) |\\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_RMII_MDIO) |  \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_RMII_CRS_DV) |\\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_USB_SOF) |    \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_USB_VBUS) |   \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_USB_ID) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_USB_DM) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_USB_DP) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_SWDIO) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_SWCLK) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_T_JTDI))\r
+#define VAL_GPIOA_OSPEEDR           (PIN_OSPEED_VERYLOW(GPIOA_PIN0) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOA_RMII_REF_CLK) |  \\r
+                                     PIN_OSPEED_HIGH(GPIOA_RMII_MDIO) |     \\r
+                                     PIN_OSPEED_VERYLOW(GPIOA_PIN3) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOA_PIN4) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOA_PIN5) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOA_PIN6) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOA_RMII_CRS_DV) |   \\r
+                                     PIN_OSPEED_HIGH(GPIOA_USB_SOF) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOA_USB_VBUS) |      \\r
+                                     PIN_OSPEED_HIGH(GPIOA_USB_ID) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOA_USB_DM) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOA_USB_DP) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOA_SWDIO) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOA_SWCLK) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOA_T_JTDI))\r
+#define VAL_GPIOA_PUPDR             (PIN_PUPDR_PULLUP(GPIOA_PIN0) |         \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_RMII_REF_CLK) |\\r
+                                     PIN_PUPDR_PULLUP(GPIOA_RMII_MDIO) |    \\r
+                                     PIN_PUPDR_PULLUP(GPIOA_PIN3) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOA_PIN4) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOA_PIN5) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOA_PIN6) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOA_RMII_CRS_DV) |  \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_USB_SOF) |    \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_USB_VBUS) |   \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_USB_ID) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_USB_DM) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_USB_DP) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_SWDIO) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_SWCLK) |      \\r
+                                     PIN_PUPDR_PULLUP(GPIOA_T_JTDI))\r
+#define VAL_GPIOA_ODR               (PIN_ODR_HIGH(GPIOA_PIN0) |             \\r
+                                     PIN_ODR_HIGH(GPIOA_RMII_REF_CLK) |     \\r
+                                     PIN_ODR_HIGH(GPIOA_RMII_MDIO) |        \\r
+                                     PIN_ODR_HIGH(GPIOA_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOA_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOA_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOA_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOA_RMII_CRS_DV) |      \\r
+                                     PIN_ODR_HIGH(GPIOA_USB_SOF) |          \\r
+                                     PIN_ODR_HIGH(GPIOA_USB_VBUS) |         \\r
+                                     PIN_ODR_HIGH(GPIOA_USB_ID) |           \\r
+                                     PIN_ODR_HIGH(GPIOA_USB_DM) |           \\r
+                                     PIN_ODR_HIGH(GPIOA_USB_DP) |           \\r
+                                     PIN_ODR_HIGH(GPIOA_SWDIO) |            \\r
+                                     PIN_ODR_HIGH(GPIOA_SWCLK) |            \\r
+                                     PIN_ODR_HIGH(GPIOA_T_JTDI))\r
+#define VAL_GPIOA_AFRL              (PIN_AFIO_AF(GPIOA_PIN0, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOA_RMII_REF_CLK, 11U) | \\r
+                                     PIN_AFIO_AF(GPIOA_RMII_MDIO, 11U) |    \\r
+                                     PIN_AFIO_AF(GPIOA_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOA_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOA_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOA_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOA_RMII_CRS_DV, 11U))\r
+#define VAL_GPIOA_AFRH              (PIN_AFIO_AF(GPIOA_USB_SOF, 10U) |      \\r
+                                     PIN_AFIO_AF(GPIOA_USB_VBUS, 0U) |      \\r
+                                     PIN_AFIO_AF(GPIOA_USB_ID, 10U) |       \\r
+                                     PIN_AFIO_AF(GPIOA_USB_DM, 10U) |       \\r
+                                     PIN_AFIO_AF(GPIOA_USB_DP, 10U) |       \\r
+                                     PIN_AFIO_AF(GPIOA_SWDIO, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOA_SWCLK, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOA_T_JTDI, 0U))\r
+\r
+/*\r
+ * GPIOB setup:\r
+ *\r
+ * PB0  - LED1 LED_GREEN LED        (output pushpull maximum).\r
+ * PB1  - PIN1                      (input pullup).\r
+ * PB2  - PIN2                      (input pullup).\r
+ * PB3  - SWO                       (alternate 0).\r
+ * PB4  - PIN4                      (input pullup).\r
+ * PB5  - PIN5                      (input pullup).\r
+ * PB6  - PIN6                      (input pullup).\r
+ * PB7  - PIN7                      (input pullup).\r
+ * PB8  - PIN8                      (input pullup).\r
+ * PB9  - PIN9                      (input pullup).\r
+ * PB10 - PIN10                     (input pullup).\r
+ * PB11 - PIN11                     (input pullup).\r
+ * PB12 - PIN12                     (input pullup).\r
+ * PB13 - RMII_TXD1                 (alternate 11).\r
+ * PB14 - LED3 LED_RED              (output pushpull maximum).\r
+ * PB15 - PIN15                     (input pullup).\r
+ */\r
+#define VAL_GPIOB_MODER             (PIN_MODE_OUTPUT(GPIOB_LED1) |          \\r
+                                     PIN_MODE_INPUT(GPIOB_PIN1) |           \\r
+                                     PIN_MODE_INPUT(GPIOB_PIN2) |           \\r
+                                     PIN_MODE_ALTERNATE(GPIOB_SWO) |        \\r
+                                     PIN_MODE_INPUT(GPIOB_PIN4) |           \\r
+                                     PIN_MODE_INPUT(GPIOB_PIN5) |           \\r
+                                     PIN_MODE_INPUT(GPIOB_PIN6) |           \\r
+                                     PIN_MODE_INPUT(GPIOB_PIN7) |           \\r
+                                     PIN_MODE_INPUT(GPIOB_PIN8) |           \\r
+                                     PIN_MODE_INPUT(GPIOB_PIN9) |           \\r
+                                     PIN_MODE_INPUT(GPIOB_PIN10) |          \\r
+                                     PIN_MODE_INPUT(GPIOB_PIN11) |          \\r
+                                     PIN_MODE_INPUT(GPIOB_PIN12) |          \\r
+                                     PIN_MODE_ALTERNATE(GPIOB_RMII_TXD1) |  \\r
+                                     PIN_MODE_OUTPUT(GPIOB_LED3) |          \\r
+                                     PIN_MODE_INPUT(GPIOB_PIN15))\r
+#define VAL_GPIOB_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOB_LED1) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN1) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_SWO) |        \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_RMII_TXD1) |  \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_LED3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN15))\r
+#define VAL_GPIOB_OSPEEDR           (PIN_OSPEED_HIGH(GPIOB_LED1) |          \\r
+                                     PIN_OSPEED_VERYLOW(GPIOB_PIN1) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOB_PIN2) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOB_SWO) |           \\r
+                                     PIN_OSPEED_VERYLOW(GPIOB_PIN4) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOB_PIN5) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOB_PIN6) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOB_PIN7) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOB_PIN8) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOB_PIN9) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOB_PIN10) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOB_PIN11) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOB_PIN12) |      \\r
+                                     PIN_OSPEED_HIGH(GPIOB_RMII_TXD1) |     \\r
+                                     PIN_OSPEED_HIGH(GPIOB_LED3) |          \\r
+                                     PIN_OSPEED_VERYLOW(GPIOB_PIN15))\r
+#define VAL_GPIOB_PUPDR             (PIN_PUPDR_FLOATING(GPIOB_LED1) |       \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_PIN1) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_PIN2) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_SWO) |          \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_PIN4) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_PIN5) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_PIN6) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_PIN7) |       \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_PIN8) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_PIN9) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_PIN10) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_PIN11) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_PIN12) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_RMII_TXD1) |    \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_LED3) |       \\r
+                                     PIN_PUPDR_PULLUP(GPIOB_PIN15))\r
+#define VAL_GPIOB_ODR               (PIN_ODR_LOW(GPIOB_LED1) |              \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN1) |             \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOB_SWO) |              \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOB_RMII_TXD1) |        \\r
+                                     PIN_ODR_LOW(GPIOB_LED3) |              \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN15))\r
+#define VAL_GPIOB_AFRL              (PIN_AFIO_AF(GPIOB_LED1, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOB_PIN1, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOB_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOB_SWO, 0U) |           \\r
+                                     PIN_AFIO_AF(GPIOB_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOB_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOB_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOB_PIN7, 0U))\r
+#define VAL_GPIOB_AFRH              (PIN_AFIO_AF(GPIOB_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOB_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOB_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOB_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOB_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOB_RMII_TXD1, 11U) |    \\r
+                                     PIN_AFIO_AF(GPIOB_LED3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOB_PIN15, 0U))\r
+\r
+/*\r
+ * GPIOC setup:\r
+ *\r
+ * PC0  - PIN0                      (input pullup).\r
+ * PC1  - RMII_MDC                  (alternate 11).\r
+ * PC2  - PIN2                      (input pullup).\r
+ * PC3  - PIN3                      (input pullup).\r
+ * PC4  - RMII_RXD0                 (alternate 11).\r
+ * PC5  - RMII_RXD1                 (alternate 11).\r
+ * PC6  - PIN6                      (input pullup).\r
+ * PC7  - PIN7                      (input pullup).\r
+ * PC8  - PIN8                      (input pullup).\r
+ * PC9  - PIN9                      (input pullup).\r
+ * PC10 - PIN10                     (input pullup).\r
+ * PC11 - PIN11                     (input pullup).\r
+ * PC12 - PIN12                     (input pullup).\r
+ * PC13 - BUTTON                    (input floating).\r
+ * PC14 - OSC32_IN                  (input floating).\r
+ * PC15 - OSC32_OUT                 (input floating).\r
+ */\r
+#define VAL_GPIOC_MODER             (PIN_MODE_INPUT(GPIOC_PIN0) |           \\r
+                                     PIN_MODE_ALTERNATE(GPIOC_RMII_MDC) |   \\r
+                                     PIN_MODE_INPUT(GPIOC_PIN2) |           \\r
+                                     PIN_MODE_INPUT(GPIOC_PIN3) |           \\r
+                                     PIN_MODE_ALTERNATE(GPIOC_RMII_RXD0) |  \\r
+                                     PIN_MODE_ALTERNATE(GPIOC_RMII_RXD1) |  \\r
+                                     PIN_MODE_INPUT(GPIOC_PIN6) |           \\r
+                                     PIN_MODE_INPUT(GPIOC_PIN7) |           \\r
+                                     PIN_MODE_INPUT(GPIOC_PIN8) |           \\r
+                                     PIN_MODE_INPUT(GPIOC_PIN9) |           \\r
+                                     PIN_MODE_INPUT(GPIOC_PIN10) |          \\r
+                                     PIN_MODE_INPUT(GPIOC_PIN11) |          \\r
+                                     PIN_MODE_INPUT(GPIOC_PIN12) |          \\r
+                                     PIN_MODE_INPUT(GPIOC_BUTTON) |         \\r
+                                     PIN_MODE_INPUT(GPIOC_OSC32_IN) |       \\r
+                                     PIN_MODE_INPUT(GPIOC_OSC32_OUT))\r
+#define VAL_GPIOC_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOC_PIN0) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_RMII_MDC) |   \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_RMII_RXD0) |  \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_RMII_RXD1) |  \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_BUTTON) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_OSC32_IN) |   \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_OSC32_OUT))\r
+#define VAL_GPIOC_OSPEEDR           (PIN_OSPEED_VERYLOW(GPIOC_PIN0) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOC_RMII_MDC) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOC_PIN2) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOC_PIN3) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOC_RMII_RXD0) |     \\r
+                                     PIN_OSPEED_HIGH(GPIOC_RMII_RXD1) |     \\r
+                                     PIN_OSPEED_VERYLOW(GPIOC_PIN6) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOC_PIN7) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOC_PIN8) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOC_PIN9) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOC_PIN10) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOC_PIN11) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOC_PIN12) |      \\r
+                                     PIN_OSPEED_HIGH(GPIOC_BUTTON) |        \\r
+                                     PIN_OSPEED_VERYLOW(GPIOC_OSC32_IN) |   \\r
+                                     PIN_OSPEED_VERYLOW(GPIOC_OSC32_OUT))\r
+#define VAL_GPIOC_PUPDR             (PIN_PUPDR_PULLUP(GPIOC_PIN0) |         \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_RMII_MDC) |   \\r
+                                     PIN_PUPDR_PULLUP(GPIOC_PIN2) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOC_PIN3) |         \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_RMII_RXD0) |  \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_RMII_RXD1) |  \\r
+                                     PIN_PUPDR_PULLUP(GPIOC_PIN6) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOC_PIN7) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOC_PIN8) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOC_PIN9) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOC_PIN10) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOC_PIN11) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOC_PIN12) |        \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_BUTTON) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_OSC32_IN) |   \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_OSC32_OUT))\r
+#define VAL_GPIOC_ODR               (PIN_ODR_HIGH(GPIOC_PIN0) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_RMII_MDC) |         \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_RMII_RXD0) |        \\r
+                                     PIN_ODR_HIGH(GPIOC_RMII_RXD1) |        \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOC_BUTTON) |           \\r
+                                     PIN_ODR_HIGH(GPIOC_OSC32_IN) |         \\r
+                                     PIN_ODR_HIGH(GPIOC_OSC32_OUT))\r
+#define VAL_GPIOC_AFRL              (PIN_AFIO_AF(GPIOC_PIN0, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOC_RMII_MDC, 11U) |     \\r
+                                     PIN_AFIO_AF(GPIOC_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOC_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOC_RMII_RXD0, 11U) |    \\r
+                                     PIN_AFIO_AF(GPIOC_RMII_RXD1, 11U) |    \\r
+                                     PIN_AFIO_AF(GPIOC_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOC_PIN7, 0U))\r
+#define VAL_GPIOC_AFRH              (PIN_AFIO_AF(GPIOC_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOC_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOC_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOC_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOC_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOC_BUTTON, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOC_OSC32_IN, 0U) |      \\r
+                                     PIN_AFIO_AF(GPIOC_OSC32_OUT, 0U))\r
+\r
+/*\r
+ * GPIOD setup:\r
+ *\r
+ * PD0  - PIN0                      (input pullup).\r
+ * PD1  - PIN1                      (input pullup).\r
+ * PD2  - PIN2                      (input pullup).\r
+ * PD3  - PIN3                      (input pullup).\r
+ * PD4  - PIN4                      (input pullup).\r
+ * PD5  - PIN5                      (input pullup).\r
+ * PD6  - PIN6                      (input pullup).\r
+ * PD7  - PIN7                      (input pullup).\r
+ * PD8  - USART3_RX STLK_RX         (alternate 7).\r
+ * PD9  - USART3_TX STLK_TX         (alternate 7).\r
+ * PD10 - PIN10                     (input pullup).\r
+ * PD11 - PIN11                     (input pullup).\r
+ * PD12 - PIN12                     (input pullup).\r
+ * PD13 - PIN13                     (input pullup).\r
+ * PD14 - PIN14                     (input pullup).\r
+ * PD15 - PIN15                     (input pullup).\r
+ */\r
+#define VAL_GPIOD_MODER             (PIN_MODE_INPUT(GPIOD_PIN0) |           \\r
+                                     PIN_MODE_INPUT(GPIOD_PIN1) |           \\r
+                                     PIN_MODE_INPUT(GPIOD_PIN2) |           \\r
+                                     PIN_MODE_INPUT(GPIOD_PIN3) |           \\r
+                                     PIN_MODE_INPUT(GPIOD_PIN4) |           \\r
+                                     PIN_MODE_INPUT(GPIOD_PIN5) |           \\r
+                                     PIN_MODE_INPUT(GPIOD_PIN6) |           \\r
+                                     PIN_MODE_INPUT(GPIOD_PIN7) |           \\r
+                                     PIN_MODE_ALTERNATE(GPIOD_USART3_RX) |  \\r
+                                     PIN_MODE_ALTERNATE(GPIOD_USART3_TX) |  \\r
+                                     PIN_MODE_INPUT(GPIOD_PIN10) |          \\r
+                                     PIN_MODE_INPUT(GPIOD_PIN11) |          \\r
+                                     PIN_MODE_INPUT(GPIOD_PIN12) |          \\r
+                                     PIN_MODE_INPUT(GPIOD_PIN13) |          \\r
+                                     PIN_MODE_INPUT(GPIOD_PIN14) |          \\r
+                                     PIN_MODE_INPUT(GPIOD_PIN15))\r
+#define VAL_GPIOD_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOD_PIN0) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN1) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_USART3_RX) |  \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_USART3_TX) |  \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN13) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN15))\r
+#define VAL_GPIOD_OSPEEDR           (PIN_OSPEED_VERYLOW(GPIOD_PIN0) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOD_PIN1) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOD_PIN2) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOD_PIN3) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOD_PIN4) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOD_PIN5) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOD_PIN6) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOD_PIN7) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOD_USART3_RX) |     \\r
+                                     PIN_OSPEED_HIGH(GPIOD_USART3_TX) |     \\r
+                                     PIN_OSPEED_VERYLOW(GPIOD_PIN10) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOD_PIN11) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOD_PIN12) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOD_PIN13) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOD_PIN14) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOD_PIN15))\r
+#define VAL_GPIOD_PUPDR             (PIN_PUPDR_PULLUP(GPIOD_PIN0) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOD_PIN1) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOD_PIN2) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOD_PIN3) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOD_PIN4) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOD_PIN5) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOD_PIN6) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOD_PIN7) |         \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_USART3_RX) |  \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_USART3_TX) |  \\r
+                                     PIN_PUPDR_PULLUP(GPIOD_PIN10) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOD_PIN11) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOD_PIN12) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOD_PIN13) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOD_PIN14) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOD_PIN15))\r
+#define VAL_GPIOD_ODR               (PIN_ODR_HIGH(GPIOD_PIN0) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN1) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_USART3_RX) |        \\r
+                                     PIN_ODR_HIGH(GPIOD_USART3_TX) |        \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN13) |            \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN15))\r
+#define VAL_GPIOD_AFRL              (PIN_AFIO_AF(GPIOD_PIN0, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN1, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN7, 0U))\r
+#define VAL_GPIOD_AFRH              (PIN_AFIO_AF(GPIOD_USART3_RX, 7U) |     \\r
+                                     PIN_AFIO_AF(GPIOD_USART3_TX, 7U) |     \\r
+                                     PIN_AFIO_AF(GPIOD_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOD_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOD_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOD_PIN13, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOD_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOD_PIN15, 0U))\r
+\r
+/*\r
+ * GPIOE setup:\r
+ *\r
+ * PE0  - PIN0                      (input pullup).\r
+ * PE1  - PIN1                      (input pullup).\r
+ * PE2  - PIN2                      (input pullup).\r
+ * PE3  - PIN3                      (input pullup).\r
+ * PE4  - PIN4                      (input pullup).\r
+ * PE5  - PIN5                      (input pullup).\r
+ * PE6  - PIN6                      (input pullup).\r
+ * PE7  - PIN7                      (input pullup).\r
+ * PE8  - PIN8                      (input pullup).\r
+ * PE9  - PIN9                      (input pullup).\r
+ * PE10 - PIN10                     (input pullup).\r
+ * PE11 - PIN11                     (input pullup).\r
+ * PE12 - PIN12                     (input pullup).\r
+ * PE13 - PIN13                     (input pullup).\r
+ * PE14 - PIN14                     (input pullup).\r
+ * PE15 - PIN15                     (input pullup).\r
+ */\r
+#define VAL_GPIOE_MODER             (PIN_MODE_INPUT(GPIOE_PIN0) |           \\r
+                                     PIN_MODE_OUTPUT(GPIOE_LED2) |          \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN2) |           \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN3) |           \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN4) |           \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN5) |           \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN6) |           \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN7) |           \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN8) |           \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN9) |           \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN10) |          \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN11) |          \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN12) |          \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN13) |          \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN14) |          \\r
+                                     PIN_MODE_INPUT(GPIOE_PIN15))\r
+#define VAL_GPIOE_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOE_PIN0) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_LED2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN13) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN15))\r
+#define VAL_GPIOE_OSPEEDR           (PIN_OSPEED_VERYLOW(GPIOE_PIN0) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOE_LED2) |          \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN2) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN3) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN4) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN5) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN6) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN7) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN8) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN9) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN10) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN11) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN12) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN13) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN14) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOE_PIN15))\r
+#define VAL_GPIOE_PUPDR             (PIN_PUPDR_PULLUP(GPIOE_PIN0) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_LED2) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN2) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN3) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN4) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN5) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN6) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN7) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN8) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN9) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN10) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN11) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN12) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN13) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN14) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOE_PIN15))\r
+#define VAL_GPIOE_ODR               (PIN_ODR_HIGH(GPIOE_PIN0) |             \\r
+                                     PIN_ODR_LOW(GPIOE_LED2) |              \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN13) |            \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN15))\r
+#define VAL_GPIOE_AFRL              (PIN_AFIO_AF(GPIOE_PIN0, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_LED2, 0U)) |         \\r
+                                     PIN_AFIO_AF(GPIOE_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN7, 0U)\r
+#define VAL_GPIOE_AFRH              (PIN_AFIO_AF(GPIOE_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOE_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOE_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOE_PIN13, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOE_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOE_PIN15, 0U))\r
+\r
+/*\r
+ * GPIOF setup:\r
+ *\r
+ * PF0  - PIN0                      (input pullup).\r
+ * PF1  - PIN1                      (input pullup).\r
+ * PF2  - PIN2                      (input pullup).\r
+ * PF3  - PIN3                      (input pullup).\r
+ * PF4  - PIN4                      (input pullup).\r
+ * PF5  - PIN5                      (input pullup).\r
+ * PF6  - PIN6                      (input pullup).\r
+ * PF7  - PIN7                      (input pullup).\r
+ * PF8  - PIN8                      (input pullup).\r
+ * PF9  - PIN9                      (input pullup).\r
+ * PF10 - PIN10                     (input pullup).\r
+ * PF11 - PIN11                     (input pullup).\r
+ * PF12 - PIN12                     (input pullup).\r
+ * PF13 - PIN13                     (input pullup).\r
+ * PF14 - PIN14                     (input pullup).\r
+ * PF15 - PIN15                     (input pullup).\r
+ */\r
+#define VAL_GPIOF_MODER             (PIN_MODE_INPUT(GPIOF_PIN0) |           \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN1) |           \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN2) |           \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN3) |           \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN4) |           \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN5) |           \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN6) |           \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN7) |           \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN8) |           \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN9) |           \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN10) |          \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN11) |          \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN12) |          \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN13) |          \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN14) |          \\r
+                                     PIN_MODE_INPUT(GPIOF_PIN15))\r
+#define VAL_GPIOF_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOF_PIN0) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN1) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN13) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN15))\r
+#define VAL_GPIOF_OSPEEDR           (PIN_OSPEED_VERYLOW(GPIOF_PIN0) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN1) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN2) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN3) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN4) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN5) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN6) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN7) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN8) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN9) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN10) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN11) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN12) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN13) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN14) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOF_PIN15))\r
+#define VAL_GPIOF_PUPDR             (PIN_PUPDR_PULLUP(GPIOF_PIN0) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN1) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN2) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN3) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN4) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN5) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN6) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN7) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN8) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN9) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN10) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN11) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN12) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN13) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN14) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOF_PIN15))\r
+#define VAL_GPIOF_ODR               (PIN_ODR_HIGH(GPIOF_PIN0) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN1) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN13) |            \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN15))\r
+#define VAL_GPIOF_AFRL              (PIN_AFIO_AF(GPIOF_PIN0, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN1, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN7, 0U))\r
+#define VAL_GPIOF_AFRH              (PIN_AFIO_AF(GPIOF_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOF_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOF_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOF_PIN13, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOF_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOF_PIN15, 0U))\r
+\r
+/*\r
+ * GPIOG setup:\r
+ *\r
+ * PG0  - PIN0                      (input pullup).\r
+ * PG1  - PIN1                      (input pullup).\r
+ * PG2  - PIN2                      (input pullup).\r
+ * PG3  - PIN3                      (input pullup).\r
+ * PG4  - PIN4                      (input pullup).\r
+ * PG5  - PIN5                      (input pullup).\r
+ * PG6  - USB_FS_PWR_EN             (output pushpull minimum).\r
+ * PG7  - USB_FS_OVCR               (input floating).\r
+ * PG8  - PIN8                      (input pullup).\r
+ * PG9  - PIN9                      (input pullup).\r
+ * PG10 - PIN10                     (input pullup).\r
+ * PG11 - RMII_TX_EN                (alternate 11).\r
+ * PG12 - PIN12                     (input pullup).\r
+ * PG13 - RMII_TXD0                 (alternate 11).\r
+ * PG14 - PIN14                     (input pullup).\r
+ * PG15 - PIN15                     (input pullup).\r
+ */\r
+#define VAL_GPIOG_MODER             (PIN_MODE_INPUT(GPIOG_PIN0) |           \\r
+                                     PIN_MODE_INPUT(GPIOG_PIN1) |           \\r
+                                     PIN_MODE_INPUT(GPIOG_PIN2) |           \\r
+                                     PIN_MODE_INPUT(GPIOG_PIN3) |           \\r
+                                     PIN_MODE_INPUT(GPIOG_PIN4) |           \\r
+                                     PIN_MODE_INPUT(GPIOG_PIN5) |           \\r
+                                     PIN_MODE_OUTPUT(GPIOG_USB_FS_PWR_EN) | \\r
+                                     PIN_MODE_INPUT(GPIOG_USB_FS_OVCR) |    \\r
+                                     PIN_MODE_INPUT(GPIOG_PIN8) |           \\r
+                                     PIN_MODE_INPUT(GPIOG_PIN9) |           \\r
+                                     PIN_MODE_INPUT(GPIOG_PIN10) |          \\r
+                                     PIN_MODE_ALTERNATE(GPIOG_RMII_TX_EN) | \\r
+                                     PIN_MODE_INPUT(GPIOG_PIN12) |          \\r
+                                     PIN_MODE_ALTERNATE(GPIOG_RMII_TXD0) |  \\r
+                                     PIN_MODE_INPUT(GPIOG_PIN14) |          \\r
+                                     PIN_MODE_INPUT(GPIOG_PIN15))\r
+#define VAL_GPIOG_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOG_PIN0) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN1) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_USB_FS_PWR_EN) |\\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_USB_FS_OVCR) |\\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_RMII_TX_EN) | \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_RMII_TXD0) |  \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN15))\r
+#define VAL_GPIOG_OSPEEDR           (PIN_OSPEED_VERYLOW(GPIOG_PIN0) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN1) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN2) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN3) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN4) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN5) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_USB_FS_PWR_EN) |\\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_USB_FS_OVCR) |\\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN8) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN9) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN10) |      \\r
+                                     PIN_OSPEED_HIGH(GPIOG_RMII_TX_EN) |    \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN12) |      \\r
+                                     PIN_OSPEED_HIGH(GPIOG_RMII_TXD0) |     \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN14) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN15))\r
+#define VAL_GPIOG_PUPDR             (PIN_PUPDR_PULLUP(GPIOG_PIN0) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOG_PIN1) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOG_PIN2) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOG_PIN3) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOG_PIN4) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOG_PIN5) |         \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_USB_FS_PWR_EN) |\\r
+                                     PIN_PUPDR_FLOATING(GPIOG_USB_FS_OVCR) |\\r
+                                     PIN_PUPDR_PULLUP(GPIOG_PIN8) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOG_PIN9) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOG_PIN10) |        \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_RMII_TX_EN) | \\r
+                                     PIN_PUPDR_PULLUP(GPIOG_PIN12) |        \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_RMII_TXD0) |  \\r
+                                     PIN_PUPDR_PULLUP(GPIOG_PIN14) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOG_PIN15))\r
+#define VAL_GPIOG_ODR               (PIN_ODR_HIGH(GPIOG_PIN0) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN1) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN5) |             \\r
+                                     PIN_ODR_LOW(GPIOG_USB_FS_PWR_EN) |     \\r
+                                     PIN_ODR_HIGH(GPIOG_USB_FS_OVCR) |      \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOG_RMII_TX_EN) |       \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOG_RMII_TXD0) |        \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN15))\r
+#define VAL_GPIOG_AFRL              (PIN_AFIO_AF(GPIOG_PIN0, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN1, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_USB_FS_PWR_EN, 0U) | \\r
+                                     PIN_AFIO_AF(GPIOG_USB_FS_OVCR, 0U))\r
+#define VAL_GPIOG_AFRH              (PIN_AFIO_AF(GPIOG_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOG_RMII_TX_EN, 11U) |   \\r
+                                     PIN_AFIO_AF(GPIOG_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOG_RMII_TXD0, 11U) |    \\r
+                                     PIN_AFIO_AF(GPIOG_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOG_PIN15, 0U))\r
+\r
+/*\r
+ * GPIOH setup:\r
+ *\r
+ * PH0  - OSC_IN                    (input floating).\r
+ * PH1  - OSC_OUT                   (input floating).\r
+ * PH2  - PIN2                      (input pullup).\r
+ * PH3  - PIN3                      (input pullup).\r
+ * PH4  - PIN4                      (input pullup).\r
+ * PH5  - PIN5                      (input pullup).\r
+ * PH6  - PIN6                      (input pullup).\r
+ * PH7  - PIN7                      (input pullup).\r
+ * PH8  - PIN8                      (input pullup).\r
+ * PH9  - PIN9                      (input pullup).\r
+ * PH10 - PIN10                     (input pullup).\r
+ * PH11 - PIN11                     (input pullup).\r
+ * PH12 - PIN12                     (input pullup).\r
+ * PH13 - PIN13                     (input pullup).\r
+ * PH14 - PIN14                     (input pullup).\r
+ * PH15 - PIN15                     (input pullup).\r
+ */\r
+#define VAL_GPIOH_MODER             (PIN_MODE_INPUT(GPIOH_OSC_IN) |         \\r
+                                     PIN_MODE_INPUT(GPIOH_OSC_OUT) |        \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN2) |           \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN3) |           \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN4) |           \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN5) |           \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN6) |           \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN7) |           \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN8) |           \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN9) |           \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN10) |          \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN11) |          \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN12) |          \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN13) |          \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN14) |          \\r
+                                     PIN_MODE_INPUT(GPIOH_PIN15))\r
+#define VAL_GPIOH_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOH_OSC_IN) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_OSC_OUT) |    \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN13) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN15))\r
+#define VAL_GPIOH_OSPEEDR           (PIN_OSPEED_HIGH(GPIOH_OSC_IN) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOH_OSC_OUT) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN2) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN3) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN4) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN5) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN6) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN7) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN8) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN9) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN10) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN11) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN12) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN13) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN14) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN15))\r
+#define VAL_GPIOH_PUPDR             (PIN_PUPDR_FLOATING(GPIOH_OSC_IN) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_OSC_OUT) |    \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN2) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN3) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN4) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN5) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN6) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN7) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN8) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN9) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN10) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN11) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN12) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN13) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN14) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOH_PIN15))\r
+#define VAL_GPIOH_ODR               (PIN_ODR_HIGH(GPIOH_OSC_IN) |           \\r
+                                     PIN_ODR_HIGH(GPIOH_OSC_OUT) |          \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN13) |            \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN15))\r
+#define VAL_GPIOH_AFRL              (PIN_AFIO_AF(GPIOH_OSC_IN, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOH_OSC_OUT, 0U) |       \\r
+                                     PIN_AFIO_AF(GPIOH_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN7, 0U))\r
+#define VAL_GPIOH_AFRH              (PIN_AFIO_AF(GPIOH_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOH_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOH_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOH_PIN13, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOH_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOH_PIN15, 0U))\r
+\r
+/*\r
+ * GPIOI setup:\r
+ *\r
+ * PI0  - PIN0                      (input pullup).\r
+ * PI1  - PIN1                      (input pullup).\r
+ * PI2  - PIN2                      (input pullup).\r
+ * PI3  - PIN3                      (input pullup).\r
+ * PI4  - PIN4                      (input pullup).\r
+ * PI5  - PIN5                      (input pullup).\r
+ * PI6  - PIN6                      (input pullup).\r
+ * PI7  - PIN7                      (input pullup).\r
+ * PI8  - PIN8                      (input pullup).\r
+ * PI9  - PIN9                      (input pullup).\r
+ * PI10 - PIN10                     (input pullup).\r
+ * PI11 - PIN11                     (input pullup).\r
+ * PI12 - PIN12                     (input pullup).\r
+ * PI13 - PIN13                     (input pullup).\r
+ * PI14 - PIN14                     (input pullup).\r
+ * PI15 - PIN15                     (input pullup).\r
+ */\r
+#define VAL_GPIOI_MODER             (PIN_MODE_INPUT(GPIOI_PIN0) |           \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN1) |           \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN2) |           \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN3) |           \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN4) |           \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN5) |           \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN6) |           \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN7) |           \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN8) |           \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN9) |           \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN10) |          \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN11) |          \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN12) |          \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN13) |          \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN14) |          \\r
+                                     PIN_MODE_INPUT(GPIOI_PIN15))\r
+#define VAL_GPIOI_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOI_PIN0) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN1) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN13) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOI_PIN15))\r
+#define VAL_GPIOI_OSPEEDR           (PIN_OSPEED_VERYLOW(GPIOI_PIN0) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN1) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN2) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN3) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN4) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN5) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN6) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN7) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN8) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN9) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN10) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN11) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN12) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN13) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN14) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOI_PIN15))\r
+#define VAL_GPIOI_PUPDR             (PIN_PUPDR_PULLUP(GPIOI_PIN0) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN1) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN2) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN3) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN4) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN5) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN6) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN7) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN8) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN9) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN10) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN11) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN12) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN13) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN14) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOI_PIN15))\r
+#define VAL_GPIOI_ODR               (PIN_ODR_HIGH(GPIOI_PIN0) |             \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN1) |             \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN13) |            \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOI_PIN15))\r
+#define VAL_GPIOI_AFRL              (PIN_AFIO_AF(GPIOI_PIN0, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOI_PIN1, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOI_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOI_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOI_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOI_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOI_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOI_PIN7, 0U))\r
+#define VAL_GPIOI_AFRH              (PIN_AFIO_AF(GPIOI_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOI_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOI_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOI_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOI_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOI_PIN13, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOI_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOI_PIN15, 0U))\r
+\r
+/*\r
+ * GPIOJ setup:\r
+ *\r
+ * PJ0  - PIN0                      (input pullup).\r
+ * PJ1  - PIN1                      (input pullup).\r
+ * PJ2  - PIN2                      (input pullup).\r
+ * PJ3  - PIN3                      (input pullup).\r
+ * PJ4  - PIN4                      (input pullup).\r
+ * PJ5  - PIN5                      (input pullup).\r
+ * PJ6  - PIN6                      (input pullup).\r
+ * PJ7  - PIN7                      (input pullup).\r
+ * PJ8  - PIN8                      (input pullup).\r
+ * PJ9  - PIN9                      (input pullup).\r
+ * PJ10 - PIN10                     (input pullup).\r
+ * PJ11 - PIN11                     (input pullup).\r
+ * PJ12 - PIN12                     (input pullup).\r
+ * PJ13 - PIN13                     (input pullup).\r
+ * PJ14 - PIN14                     (input pullup).\r
+ * PJ15 - PIN15                     (input pullup).\r
+ */\r
+#define VAL_GPIOJ_MODER             (PIN_MODE_INPUT(GPIOJ_PIN0) |           \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN1) |           \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN2) |           \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN3) |           \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN4) |           \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN5) |           \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN6) |           \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN7) |           \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN8) |           \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN9) |           \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN10) |          \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN11) |          \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN12) |          \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN13) |          \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN14) |          \\r
+                                     PIN_MODE_INPUT(GPIOJ_PIN15))\r
+#define VAL_GPIOJ_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOJ_PIN0) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN1) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN13) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOJ_PIN15))\r
+#define VAL_GPIOJ_OSPEEDR           (PIN_OSPEED_VERYLOW(GPIOJ_PIN0) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN1) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN2) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN3) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN4) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN5) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN6) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN7) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN8) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN9) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN10) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN11) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN12) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN13) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN14) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOJ_PIN15))\r
+#define VAL_GPIOJ_PUPDR             (PIN_PUPDR_PULLUP(GPIOJ_PIN0) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN1) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN2) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN3) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN4) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN5) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN6) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN7) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN8) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN9) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN10) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN11) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN12) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN13) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN14) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOJ_PIN15))\r
+#define VAL_GPIOJ_ODR               (PIN_ODR_HIGH(GPIOJ_PIN0) |             \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN1) |             \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN13) |            \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOJ_PIN15))\r
+#define VAL_GPIOJ_AFRL              (PIN_AFIO_AF(GPIOJ_PIN0, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN1, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN7, 0U))\r
+#define VAL_GPIOJ_AFRH              (PIN_AFIO_AF(GPIOJ_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN13, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOJ_PIN15, 0U))\r
+\r
+/*\r
+ * GPIOK setup:\r
+ *\r
+ * PK0  - PIN0                      (input pullup).\r
+ * PK1  - PIN1                      (input pullup).\r
+ * PK2  - PIN2                      (input pullup).\r
+ * PK3  - PIN3                      (input pullup).\r
+ * PK4  - PIN4                      (input pullup).\r
+ * PK5  - PIN5                      (input pullup).\r
+ * PK6  - PIN6                      (input pullup).\r
+ * PK7  - PIN7                      (input pullup).\r
+ * PK8  - PIN8                      (input pullup).\r
+ * PK9  - PIN9                      (input pullup).\r
+ * PK10 - PIN10                     (input pullup).\r
+ * PK11 - PIN11                     (input pullup).\r
+ * PK12 - PIN12                     (input pullup).\r
+ * PK13 - PIN13                     (input pullup).\r
+ * PK14 - PIN14                     (input pullup).\r
+ * PK15 - PIN15                     (input pullup).\r
+ */\r
+#define VAL_GPIOK_MODER             (PIN_MODE_INPUT(GPIOK_PIN0) |           \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN1) |           \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN2) |           \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN3) |           \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN4) |           \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN5) |           \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN6) |           \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN7) |           \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN8) |           \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN9) |           \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN10) |          \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN11) |          \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN12) |          \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN13) |          \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN14) |          \\r
+                                     PIN_MODE_INPUT(GPIOK_PIN15))\r
+#define VAL_GPIOK_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOK_PIN0) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN1) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN13) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOK_PIN15))\r
+#define VAL_GPIOK_OSPEEDR           (PIN_OSPEED_VERYLOW(GPIOK_PIN0) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN1) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN2) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN3) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN4) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN5) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN6) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN7) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN8) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN9) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN10) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN11) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN12) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN13) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN14) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOK_PIN15))\r
+#define VAL_GPIOK_PUPDR             (PIN_PUPDR_PULLUP(GPIOK_PIN0) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN1) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN2) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN3) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN4) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN5) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN6) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN7) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN8) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN9) |         \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN10) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN11) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN12) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN13) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN14) |        \\r
+                                     PIN_PUPDR_PULLUP(GPIOK_PIN15))\r
+#define VAL_GPIOK_ODR               (PIN_ODR_HIGH(GPIOK_PIN0) |             \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN1) |             \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN13) |            \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOK_PIN15))\r
+#define VAL_GPIOK_AFRL              (PIN_AFIO_AF(GPIOK_PIN0, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOK_PIN1, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOK_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOK_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOK_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOK_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOK_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOK_PIN7, 0U))\r
+#define VAL_GPIOK_AFRH              (PIN_AFIO_AF(GPIOK_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOK_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOK_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOK_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOK_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOK_PIN13, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOK_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOK_PIN15, 0U))\r
+\r
+/*===========================================================================*/\r
+/* External declarations.                                                    */\r
+/*===========================================================================*/\r
+\r
+#if !defined(_FROM_ASM_)\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+  void boardInit(void);\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+#endif /* _FROM_ASM_ */\r
+\r
+#endif /* BOARD_H */\r
diff --git a/firmware/source/board/l4/board.h b/firmware/source/board/l4/board.h
new file mode 100644 (file)
index 0000000..4b2642a
--- /dev/null
@@ -0,0 +1,1509 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+/*\r
+ * This file has been automatically generated using ChibiStudio board\r
+ * generator plugin. Do not edit manually.\r
+ */\r
+\r
+#ifndef BOARD_H\r
+#define BOARD_H\r
+\r
+/*===========================================================================*/\r
+/* Driver constants.                                                         */\r
+/*===========================================================================*/\r
+\r
+/*\r
+ * Setup for STMicroelectronics STM32 Nucleo64-L476RG board.\r
+ */\r
+\r
+/*\r
+ * Board identifier.\r
+ */\r
+#define BOARD_ST_NUCLEO64_L476RG\r
+#define BOARD_NAME                  "STMicroelectronics STM32 Nucleo64-L476RG"\r
+\r
+/*\r
+ * Board oscillators-related settings.\r
+ */\r
+#if !defined(STM32_LSECLK)\r
+#define STM32_LSECLK                32768U\r
+#endif\r
+\r
+#define STM32_LSEDRV                (3U << 3U)\r
+\r
+#if !defined(STM32_HSECLK)\r
+#define STM32_HSECLK                8000000U\r
+#endif\r
+\r
+#define STM32_HSE_BYPASS\r
+\r
+/*\r
+ * Board voltages.\r
+ * Required for performance limits calculation.\r
+ */\r
+#define STM32_VDD                   300U\r
+\r
+/*\r
+ * MCU type as defined in the ST header.\r
+ */\r
+#define STM32L476xx\r
+\r
+/*\r
+ * IO pins assignments.\r
+ */\r
+#define GPIOA_ARD_A0                0U\r
+#define GPIOA_ACD12_IN5             0U\r
+#define GPIOA_ARD_A1                1U\r
+#define GPIOA_ACD12_IN6             1U\r
+#define GPIOA_ARD_D1                2U\r
+#define GPIOA_USART2_TX             2U\r
+#define GPIOA_ARD_D0                3U\r
+#define GPIOA_USART2_RX             3U\r
+#define GPIOA_ARD_A2                4U\r
+#define GPIOA_ACD12_IN9             4U\r
+#define GPIOA_ARD_D13               5U\r
+#define GPIOA_LED_GREEN             5U\r
+#define GPIOA_ARD_D12               6U\r
+#define GPIOA_ARD_D11               7U\r
+#define GPIOA_ARD_D7                8U\r
+#define GPIOA_ARD_D8                9U\r
+#define GPIOA_ARD_D2                10U\r
+#define GPIOA_PIN11                 11U\r
+#define GPIOA_PIN12                 12U\r
+#define GPIOA_SWDIO                 13U\r
+#define GPIOA_SWCLK                 14U\r
+#define GPIOA_PIN15                 15U\r
+\r
+#define GPIOB_ARD_A3                0U\r
+#define GPIOB_ACD12_IN15            0U\r
+#define GPIOB_PIN1                  1U\r
+#define GPIOB_PIN2                  2U\r
+#define GPIOB_ARD_D3                3U\r
+#define GPIOB_SWO                   3U\r
+#define GPIOB_ARD_D5                4U\r
+#define GPIOB_ARD_D4                5U\r
+#define GPIOB_ARD_D10               6U\r
+#define GPIOB_PIN7                  7U\r
+#define GPIOB_ARD_D15               8U\r
+#define GPIOB_ARD_D14               9U\r
+#define GPIOB_ARD_D6                10U\r
+#define GPIOB_PIN11                 11U\r
+#define GPIOB_PIN12                 12U\r
+#define GPIOB_PIN13                 13U\r
+#define GPIOB_PIN14                 14U\r
+#define GPIOB_PIN15                 15U\r
+\r
+#define GPIOC_ARD_A5                0U\r
+#define GPIOC_ACD123_IN1            0U\r
+#define GPIOC_ARD_A4                1U\r
+#define GPIOC_ACD123_IN2            1U\r
+#define GPIOC_PIN2                  2U\r
+#define GPIOC_PIN3                  3U\r
+#define GPIOC_PIN4                  4U\r
+#define GPIOC_PIN5                  5U\r
+#define GPIOC_PIN6                  6U\r
+#define GPIOC_ARD_D9                7U\r
+#define GPIOC_PIN8                  8U\r
+#define GPIOC_PIN9                  9U\r
+#define GPIOC_PIN10                 10U\r
+#define GPIOC_PIN11                 11U\r
+#define GPIOC_PIN12                 12U\r
+#define GPIOC_BUTTON                13U\r
+#define GPIOC_OSC32_IN              14U\r
+#define GPIOC_OSC32_OUT             15U\r
+\r
+#define GPIOD_PIN0                  0U\r
+#define GPIOD_PIN1                  1U\r
+#define GPIOD_PIN2                  2U\r
+#define GPIOD_PIN3                  3U\r
+#define GPIOD_PIN4                  4U\r
+#define GPIOD_PIN5                  5U\r
+#define GPIOD_PIN6                  6U\r
+#define GPIOD_PIN7                  7U\r
+#define GPIOD_PIN8                  8U\r
+#define GPIOD_PIN9                  9U\r
+#define GPIOD_PIN10                 10U\r
+#define GPIOD_PIN11                 11U\r
+#define GPIOD_PIN12                 12U\r
+#define GPIOD_PIN13                 13U\r
+#define GPIOD_PIN14                 14U\r
+#define GPIOD_PIN15                 15U\r
+\r
+#define GPIOE_PIN0                  0U\r
+#define GPIOE_PIN1                  1U\r
+#define GPIOE_PIN2                  2U\r
+#define GPIOE_PIN3                  3U\r
+#define GPIOE_PIN4                  4U\r
+#define GPIOE_PIN5                  5U\r
+#define GPIOE_PIN6                  6U\r
+#define GPIOE_PIN7                  7U\r
+#define GPIOE_PIN8                  8U\r
+#define GPIOE_PIN9                  9U\r
+#define GPIOE_PIN10                 10U\r
+#define GPIOE_PIN11                 11U\r
+#define GPIOE_PIN12                 12U\r
+#define GPIOE_PIN13                 13U\r
+#define GPIOE_PIN14                 14U\r
+#define GPIOE_PIN15                 15U\r
+\r
+#define GPIOF_PIN0                  0U\r
+#define GPIOF_PIN1                  1U\r
+#define GPIOF_PIN2                  2U\r
+#define GPIOF_PIN3                  3U\r
+#define GPIOF_PIN4                  4U\r
+#define GPIOF_PIN5                  5U\r
+#define GPIOF_PIN6                  6U\r
+#define GPIOF_PIN7                  7U\r
+#define GPIOF_PIN8                  8U\r
+#define GPIOF_PIN9                  9U\r
+#define GPIOF_PIN10                 10U\r
+#define GPIOF_PIN11                 11U\r
+#define GPIOF_PIN12                 12U\r
+#define GPIOF_PIN13                 13U\r
+#define GPIOF_PIN14                 14U\r
+#define GPIOF_PIN15                 15U\r
+\r
+#define GPIOG_PIN0                  0U\r
+#define GPIOG_PIN1                  1U\r
+#define GPIOG_PIN2                  2U\r
+#define GPIOG_PIN3                  3U\r
+#define GPIOG_PIN4                  4U\r
+#define GPIOG_PIN5                  5U\r
+#define GPIOG_PIN6                  6U\r
+#define GPIOG_PIN7                  7U\r
+#define GPIOG_PIN8                  8U\r
+#define GPIOG_PIN9                  9U\r
+#define GPIOG_PIN10                 10U\r
+#define GPIOG_PIN11                 11U\r
+#define GPIOG_PIN12                 12U\r
+#define GPIOG_PIN13                 13U\r
+#define GPIOG_PIN14                 14U\r
+#define GPIOG_PIN15                 15U\r
+\r
+#define GPIOH_OSC_IN                0U\r
+#define GPIOH_OSC_OUT               1U\r
+#define GPIOH_PIN2                  2U\r
+#define GPIOH_PIN3                  3U\r
+#define GPIOH_PIN4                  4U\r
+#define GPIOH_PIN5                  5U\r
+#define GPIOH_PIN6                  6U\r
+#define GPIOH_PIN7                  7U\r
+#define GPIOH_PIN8                  8U\r
+#define GPIOH_PIN9                  9U\r
+#define GPIOH_PIN10                 10U\r
+#define GPIOH_PIN11                 11U\r
+#define GPIOH_PIN12                 12U\r
+#define GPIOH_PIN13                 13U\r
+#define GPIOH_PIN14                 14U\r
+#define GPIOH_PIN15                 15U\r
+\r
+/*\r
+ * IO lines assignments.\r
+ */\r
+#define LINE_ARD_A0                 PAL_LINE(GPIOA, 0U)\r
+#define LINE_ACD12_IN5              PAL_LINE(GPIOA, 0U)\r
+#define LINE_ARD_A1                 PAL_LINE(GPIOA, 1U)\r
+#define LINE_ACD12_IN6              PAL_LINE(GPIOA, 1U)\r
+#define LINE_ARD_D1                 PAL_LINE(GPIOA, 2U)\r
+#define LINE_USART2_TX              PAL_LINE(GPIOA, 2U)\r
+#define LINE_ARD_D0                 PAL_LINE(GPIOA, 3U)\r
+#define LINE_USART2_RX              PAL_LINE(GPIOA, 3U)\r
+#define LINE_ARD_A2                 PAL_LINE(GPIOA, 4U)\r
+#define LINE_ACD12_IN9              PAL_LINE(GPIOA, 4U)\r
+#define LINE_ARD_D13                PAL_LINE(GPIOA, 5U)\r
+//#define LINE_LED_GREEN              PAL_LINE(GPIOA, 5U)\r
+#define LINE_ARD_D12                PAL_LINE(GPIOA, 6U)\r
+#define LINE_ARD_D11                PAL_LINE(GPIOA, 7U)\r
+#define LINE_ARD_D7                 PAL_LINE(GPIOA, 8U)\r
+#define LINE_ARD_D8                 PAL_LINE(GPIOA, 9U)\r
+#define LINE_ARD_D2                 PAL_LINE(GPIOA, 10U)\r
+#define LINE_SWDIO                  PAL_LINE(GPIOA, 13U)\r
+#define LINE_SWCLK                  PAL_LINE(GPIOA, 14U)\r
+#define LINE_ARD_A3                 PAL_LINE(GPIOB, 0U)\r
+#define LINE_ACD12_IN15             PAL_LINE(GPIOB, 0U)\r
+#define LINE_ARD_D3                 PAL_LINE(GPIOB, 3U)\r
+#define LINE_SWO                    PAL_LINE(GPIOB, 3U)\r
+#define LINE_ARD_D5                 PAL_LINE(GPIOB, 4U)\r
+#define LINE_ARD_D4                 PAL_LINE(GPIOB, 5U)\r
+#define LINE_ARD_D10                PAL_LINE(GPIOB, 6U)\r
+#define LINE_ARD_D15                PAL_LINE(GPIOB, 8U)\r
+#define LINE_ARD_D14                PAL_LINE(GPIOB, 9U)\r
+#define LINE_ARD_D6                 PAL_LINE(GPIOB, 10U)\r
+#define LINE_ARD_A5                 PAL_LINE(GPIOC, 0U)\r
+#define LINE_ACD123_IN1             PAL_LINE(GPIOC, 0U)\r
+#define LINE_ARD_A4                 PAL_LINE(GPIOC, 1U)\r
+#define LINE_ACD123_IN2             PAL_LINE(GPIOC, 1U)\r
+#define LINE_ARD_D9                 PAL_LINE(GPIOC, 7U)\r
+#define LINE_BUTTON                 PAL_LINE(GPIOC, 13U)\r
+#define LINE_OSC32_IN               PAL_LINE(GPIOC, 14U)\r
+#define LINE_OSC32_OUT              PAL_LINE(GPIOC, 15U)\r
+#define LINE_OSC_IN                 PAL_LINE(GPIOH, 0U)\r
+#define LINE_OSC_OUT                PAL_LINE(GPIOH, 1U)\r
+\r
+/*===========================================================================*/\r
+/* Driver pre-compile time settings.                                         */\r
+/*===========================================================================*/\r
+\r
+/*===========================================================================*/\r
+/* Derived constants and error checks.                                       */\r
+/*===========================================================================*/\r
+\r
+/*===========================================================================*/\r
+/* Driver data structures and types.                                         */\r
+/*===========================================================================*/\r
+\r
+/*===========================================================================*/\r
+/* Driver macros.                                                            */\r
+/*===========================================================================*/\r
+\r
+/*\r
+ * I/O ports initial setup, this configuration is established soon after reset\r
+ * in the initialization code.\r
+ * Please refer to the STM32 Reference Manual for details.\r
+ */\r
+#define PIN_MODE_INPUT(n)           (0U << ((n) * 2U))\r
+#define PIN_MODE_OUTPUT(n)          (1U << ((n) * 2U))\r
+#define PIN_MODE_ALTERNATE(n)       (2U << ((n) * 2U))\r
+#define PIN_MODE_ANALOG(n)          (3U << ((n) * 2U))\r
+#define PIN_ODR_LOW(n)              (0U << (n))\r
+#define PIN_ODR_HIGH(n)             (1U << (n))\r
+#define PIN_OTYPE_PUSHPULL(n)       (0U << (n))\r
+#define PIN_OTYPE_OPENDRAIN(n)      (1U << (n))\r
+#define PIN_OSPEED_VERYLOW(n)       (0U << ((n) * 2U))\r
+#define PIN_OSPEED_LOW(n)           (1U << ((n) * 2U))\r
+#define PIN_OSPEED_MEDIUM(n)        (2U << ((n) * 2U))\r
+#define PIN_OSPEED_HIGH(n)          (3U << ((n) * 2U))\r
+#define PIN_PUPDR_FLOATING(n)       (0U << ((n) * 2U))\r
+#define PIN_PUPDR_PULLUP(n)         (1U << ((n) * 2U))\r
+#define PIN_PUPDR_PULLDOWN(n)       (2U << ((n) * 2U))\r
+#define PIN_AFIO_AF(n, v)           ((v) << (((n) % 8U) * 4U))\r
+#define PIN_ASCR_DISABLED(n)        (0U << (n))\r
+#define PIN_ASCR_ENABLED(n)         (1U << (n))\r
+#define PIN_LOCKR_DISABLED(n)       (0U << (n))\r
+#define PIN_LOCKR_ENABLED(n)        (1U << (n))\r
+\r
+/*\r
+ * GPIOA setup:\r
+ *\r
+ * PA0  - ARD_A0 ACD12_IN5          (analog).\r
+ * PA1  - ARD_A1 ACD12_IN6          (analog).\r
+ * PA2  - ARD_D1 USART2_TX          (alternate 7).\r
+ * PA3  - ARD_D0 USART2_RX          (alternate 7).\r
+ * PA4  - ARD_A2 ACD12_IN9          (analog).\r
+ * PA5  - ARD_D13 LED_GREEN         (output pushpull maximum).\r
+ * PA6  - ARD_D12                   (analog).\r
+ * PA7  - ARD_D11                   (analog).\r
+ * PA8  - ARD_D7                    (analog).\r
+ * PA9  - ARD_D8                    (analog).\r
+ * PA10 - ARD_D2                    (analog).\r
+ * PA11 - PIN11                     (analog).\r
+ * PA12 - PIN12                     (analog).\r
+ * PA13 - SWDIO                     (alternate 0).\r
+ * PA14 - SWCLK                     (alternate 0).\r
+ * PA15 - PIN15                     (analog).\r
+ */\r
+#define VAL_GPIOA_MODER             (PIN_MODE_ANALOG(GPIOA_ARD_A0) |        \\r
+                                     PIN_MODE_ANALOG(GPIOA_ARD_A1) |        \\r
+                                     PIN_MODE_ALTERNATE(GPIOA_ARD_D1) |     \\r
+                                     PIN_MODE_ALTERNATE(GPIOA_ARD_D0) |     \\r
+                                     PIN_MODE_ANALOG(GPIOA_ARD_A2) |        \\r
+                                     PIN_MODE_OUTPUT(GPIOA_ARD_D13) |       \\r
+                                     PIN_MODE_ANALOG(GPIOA_ARD_D12) |       \\r
+                                     PIN_MODE_ANALOG(GPIOA_ARD_D11) |       \\r
+                                     PIN_MODE_ANALOG(GPIOA_ARD_D7) |        \\r
+                                     PIN_MODE_ANALOG(GPIOA_ARD_D8) |        \\r
+                                     PIN_MODE_ANALOG(GPIOA_ARD_D2) |        \\r
+                                     PIN_MODE_ANALOG(GPIOA_PIN11) |         \\r
+                                     PIN_MODE_ANALOG(GPIOA_PIN12) |         \\r
+                                     PIN_MODE_ALTERNATE(GPIOA_SWDIO) |      \\r
+                                     PIN_MODE_ALTERNATE(GPIOA_SWCLK) |      \\r
+                                     PIN_MODE_ANALOG(GPIOA_PIN15))\r
+#define VAL_GPIOA_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOA_ARD_A0) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_ARD_A1) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_ARD_D1) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_ARD_D0) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_ARD_A2) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_ARD_D13) |    \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_ARD_D12) |    \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_ARD_D11) |    \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_ARD_D7) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_ARD_D8) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_ARD_D2) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_SWDIO) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_SWCLK) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN15))\r
+#define VAL_GPIOA_OSPEEDR           (PIN_OSPEED_HIGH(GPIOA_ARD_A0) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOA_ARD_A1) |        \\r
+                                     PIN_OSPEED_MEDIUM(GPIOA_ARD_D1) |      \\r
+                                     PIN_OSPEED_MEDIUM(GPIOA_ARD_D0) |      \\r
+                                     PIN_OSPEED_HIGH(GPIOA_ARD_A2) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOA_ARD_D13) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOA_ARD_D12) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOA_ARD_D11) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOA_ARD_D7) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOA_ARD_D8) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOA_ARD_D2) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOA_PIN11) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOA_PIN12) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOA_SWDIO) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOA_SWCLK) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOA_PIN15))\r
+#define VAL_GPIOA_PUPDR             (PIN_PUPDR_FLOATING(GPIOA_ARD_A0) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_ARD_A1) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_ARD_D1) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_ARD_D0) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_ARD_A2) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_ARD_D13) |    \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_ARD_D12) |    \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_ARD_D11) |    \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_ARD_D7) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_ARD_D8) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_ARD_D2) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_PIN11) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_PIN12) |      \\r
+                                     PIN_PUPDR_PULLUP(GPIOA_SWDIO) |        \\r
+                                     PIN_PUPDR_PULLDOWN(GPIOA_SWCLK) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOA_PIN15))\r
+#define VAL_GPIOA_ODR               (PIN_ODR_HIGH(GPIOA_ARD_A0) |           \\r
+                                     PIN_ODR_HIGH(GPIOA_ARD_A1) |           \\r
+                                     PIN_ODR_HIGH(GPIOA_ARD_D1) |           \\r
+                                     PIN_ODR_HIGH(GPIOA_ARD_D0) |           \\r
+                                     PIN_ODR_HIGH(GPIOA_ARD_A2) |           \\r
+                                     PIN_ODR_LOW(GPIOA_ARD_D13) |           \\r
+                                     PIN_ODR_HIGH(GPIOA_ARD_D12) |          \\r
+                                     PIN_ODR_HIGH(GPIOA_ARD_D11) |          \\r
+                                     PIN_ODR_HIGH(GPIOA_ARD_D7) |           \\r
+                                     PIN_ODR_HIGH(GPIOA_ARD_D8) |           \\r
+                                     PIN_ODR_HIGH(GPIOA_ARD_D2) |           \\r
+                                     PIN_ODR_HIGH(GPIOA_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOA_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOA_SWDIO) |            \\r
+                                     PIN_ODR_HIGH(GPIOA_SWCLK) |            \\r
+                                     PIN_ODR_HIGH(GPIOA_PIN15))\r
+#define VAL_GPIOA_AFRL              (PIN_AFIO_AF(GPIOA_ARD_A0, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOA_ARD_A1, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOA_ARD_D1, 7U) |        \\r
+                                     PIN_AFIO_AF(GPIOA_ARD_D0, 7U) |        \\r
+                                     PIN_AFIO_AF(GPIOA_ARD_A2, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOA_ARD_D13, 0U) |       \\r
+                                     PIN_AFIO_AF(GPIOA_ARD_D12, 0U) |       \\r
+                                     PIN_AFIO_AF(GPIOA_ARD_D11, 0U))\r
+#define VAL_GPIOA_AFRH              (PIN_AFIO_AF(GPIOA_ARD_D7, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOA_ARD_D8, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOA_ARD_D2, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOA_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOA_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOA_SWDIO, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOA_SWCLK, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOA_PIN15, 0U))\r
+#define VAL_GPIOA_ASCR              (PIN_ASCR_DISABLED(GPIOA_ARD_A0) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOA_ARD_A1) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOA_ARD_D1) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOA_ARD_D0) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOA_ARD_A2) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOA_ARD_D13) |     \\r
+                                     PIN_ASCR_DISABLED(GPIOA_ARD_D12) |     \\r
+                                     PIN_ASCR_DISABLED(GPIOA_ARD_D11) |     \\r
+                                     PIN_ASCR_DISABLED(GPIOA_ARD_D7) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOA_ARD_D8) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOA_ARD_D2) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOA_PIN11) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOA_PIN12) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOA_SWDIO) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOA_SWCLK) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOA_PIN15))\r
+#define VAL_GPIOA_LOCKR             (PIN_LOCKR_DISABLED(GPIOA_ARD_A0) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_ARD_A1) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_ARD_D1) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_ARD_D0) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_ARD_A2) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_ARD_D13) |    \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_ARD_D12) |    \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_ARD_D11) |    \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_ARD_D7) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_ARD_D8) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_ARD_D2) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_PIN11) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_PIN12) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_SWDIO) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_SWCLK) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOA_PIN15))\r
+\r
+/*\r
+ * GPIOB setup:\r
+ *\r
+ * PB0  - ARD_A3 ACD12_IN15         (analog).\r
+ * PB1  - PIN1                      (analog).\r
+ * PB2  - PIN2                      (analog).\r
+ * PB3  - ARD_D3 SWO                (analog).\r
+ * PB4  - ARD_D5                    (analog).\r
+ * PB5  - ARD_D4                    (analog).\r
+ * PB6  - ARD_D10                   (analog).\r
+ * PB7  - PIN7                      (analog).\r
+ * PB8  - ARD_D15                   (analog).\r
+ * PB9  - ARD_D14                   (analog).\r
+ * PB10 - ARD_D6                    (analog).\r
+ * PB11 - PIN11                     (analog).\r
+ * PB12 - PIN12                     (analog).\r
+ * PB13 - PIN13                     (analog).\r
+ * PB14 - PIN14                     (analog).\r
+ * PB15 - PIN15                     (analog).\r
+ */\r
+#define VAL_GPIOB_MODER             (PIN_MODE_ANALOG(GPIOB_ARD_A3) |        \\r
+                                     PIN_MODE_ANALOG(GPIOB_PIN1) |          \\r
+                                     PIN_MODE_ANALOG(GPIOB_PIN2) |          \\r
+                                     PIN_MODE_ANALOG(GPIOB_ARD_D3) |        \\r
+                                     PIN_MODE_ANALOG(GPIOB_ARD_D5) |        \\r
+                                     PIN_MODE_ANALOG(GPIOB_ARD_D4) |        \\r
+                                     PIN_MODE_ANALOG(GPIOB_ARD_D10) |       \\r
+                                     PIN_MODE_ANALOG(GPIOB_PIN7) |          \\r
+                                     PIN_MODE_ANALOG(GPIOB_ARD_D15) |       \\r
+                                     PIN_MODE_ANALOG(GPIOB_ARD_D14) |       \\r
+                                     PIN_MODE_ANALOG(GPIOB_ARD_D6) |        \\r
+                                     PIN_MODE_ANALOG(GPIOB_PIN11) |         \\r
+                                     PIN_MODE_ANALOG(GPIOB_PIN12) |         \\r
+                                     PIN_MODE_ANALOG(GPIOB_PIN13) |         \\r
+                                     PIN_MODE_ANALOG(GPIOB_PIN14) |         \\r
+                                     PIN_MODE_ANALOG(GPIOB_PIN15))\r
+#define VAL_GPIOB_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOB_ARD_A3) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN1) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_ARD_D3) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_ARD_D5) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_ARD_D4) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_ARD_D10) |    \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_ARD_D15) |    \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_ARD_D14) |    \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_ARD_D6) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN13) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN15))\r
+#define VAL_GPIOB_OSPEEDR           (PIN_OSPEED_HIGH(GPIOB_ARD_A3) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOB_PIN1) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOB_PIN2) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOB_ARD_D3) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOB_ARD_D5) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOB_ARD_D4) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOB_ARD_D10) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOB_PIN7) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOB_ARD_D15) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOB_ARD_D14) |       \\r
+                                     PIN_OSPEED_HIGH(GPIOB_ARD_D6) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOB_PIN11) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOB_PIN12) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOB_PIN13) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOB_PIN14) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOB_PIN15))\r
+#define VAL_GPIOB_PUPDR             (PIN_PUPDR_FLOATING(GPIOB_ARD_A3) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_PIN1) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_PIN2) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_ARD_D3) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_ARD_D5) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_ARD_D4) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_ARD_D10) |    \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_PIN7) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_ARD_D15) |    \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_ARD_D14) |    \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_ARD_D6) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_PIN11) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_PIN12) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_PIN13) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_PIN14) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOB_PIN15))\r
+#define VAL_GPIOB_ODR               (PIN_ODR_HIGH(GPIOB_ARD_A3) |           \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN1) |             \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOB_ARD_D3) |           \\r
+                                     PIN_ODR_HIGH(GPIOB_ARD_D5) |           \\r
+                                     PIN_ODR_HIGH(GPIOB_ARD_D4) |           \\r
+                                     PIN_ODR_HIGH(GPIOB_ARD_D10) |          \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOB_ARD_D15) |          \\r
+                                     PIN_ODR_HIGH(GPIOB_ARD_D14) |          \\r
+                                     PIN_ODR_HIGH(GPIOB_ARD_D6) |           \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN13) |            \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOB_PIN15))\r
+#define VAL_GPIOB_AFRL              (PIN_AFIO_AF(GPIOB_ARD_A3, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOB_PIN1, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOB_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOB_ARD_D3, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOB_ARD_D5, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOB_ARD_D4, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOB_ARD_D10, 0U) |       \\r
+                                     PIN_AFIO_AF(GPIOB_PIN7, 0U))\r
+#define VAL_GPIOB_AFRH              (PIN_AFIO_AF(GPIOB_ARD_D15, 0U) |       \\r
+                                     PIN_AFIO_AF(GPIOB_ARD_D14, 0U) |       \\r
+                                     PIN_AFIO_AF(GPIOB_ARD_D6, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOB_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOB_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOB_PIN13, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOB_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOB_PIN15, 0U))\r
+#define VAL_GPIOB_ASCR              (PIN_ASCR_DISABLED(GPIOB_ARD_A3) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOB_PIN1) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOB_PIN2) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOB_ARD_D3) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOB_ARD_D5) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOB_ARD_D4) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOB_ARD_D10) |     \\r
+                                     PIN_ASCR_DISABLED(GPIOB_PIN7) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOB_ARD_D15) |     \\r
+                                     PIN_ASCR_DISABLED(GPIOB_ARD_D14) |     \\r
+                                     PIN_ASCR_DISABLED(GPIOB_ARD_D6) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOB_PIN11) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOB_PIN12) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOB_PIN13) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOB_PIN14) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOB_PIN15))\r
+#define VAL_GPIOB_LOCKR             (PIN_LOCKR_DISABLED(GPIOB_ARD_A3) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_PIN1) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_PIN2) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_ARD_D3) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_ARD_D5) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_ARD_D4) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_ARD_D10) |    \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_PIN7) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_ARD_D15) |    \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_ARD_D14) |    \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_ARD_D6) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_PIN11) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_PIN12) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_PIN13) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_PIN14) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOB_PIN15))\r
+\r
+/*\r
+ * GPIOC setup:\r
+ *\r
+ * PC0  - ARD_A5 ACD123_IN1         (analog).\r
+ * PC1  - ARD_A4 ACD123_IN2         (analog).\r
+ * PC2  - PIN2                      (analog).\r
+ * PC3  - PIN3                      (analog).\r
+ * PC4  - PIN4                      (analog).\r
+ * PC5  - PIN5                      (analog).\r
+ * PC6  - PIN6                      (analog).\r
+ * PC7  - ARD_D9                    (analog).\r
+ * PC8  - PIN8                      (analog).\r
+ * PC9  - PIN9                      (analog).\r
+ * PC10 - PIN10                     (analog).\r
+ * PC11 - PIN11                     (analog).\r
+ * PC12 - PIN12                     (analog).\r
+ * PC13 - BUTTON                    (input floating).\r
+ * PC14 - OSC32_IN                  (input floating).\r
+ * PC15 - OSC32_OUT                 (input floating).\r
+ */\r
+#define VAL_GPIOC_MODER             (PIN_MODE_ANALOG(GPIOC_ARD_A5) |        \\r
+                                     PIN_MODE_ANALOG(GPIOC_ARD_A4) |        \\r
+                                     PIN_MODE_ANALOG(GPIOC_PIN2) |          \\r
+                                     PIN_MODE_ANALOG(GPIOC_PIN3) |          \\r
+                                     PIN_MODE_ANALOG(GPIOC_PIN4) |          \\r
+                                     PIN_MODE_ANALOG(GPIOC_PIN5) |          \\r
+                                     PIN_MODE_ANALOG(GPIOC_PIN6) |          \\r
+                                     PIN_MODE_ANALOG(GPIOC_ARD_D9) |        \\r
+                                     PIN_MODE_ANALOG(GPIOC_PIN8) |          \\r
+                                     PIN_MODE_ANALOG(GPIOC_PIN9) |          \\r
+                                     PIN_MODE_ANALOG(GPIOC_PIN10) |         \\r
+                                     PIN_MODE_ANALOG(GPIOC_PIN11) |         \\r
+                                     PIN_MODE_ANALOG(GPIOC_PIN12) |         \\r
+                                     PIN_MODE_INPUT(GPIOC_BUTTON) |         \\r
+                                     PIN_MODE_INPUT(GPIOC_OSC32_IN) |       \\r
+                                     PIN_MODE_INPUT(GPIOC_OSC32_OUT))\r
+#define VAL_GPIOC_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOC_ARD_A5) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_ARD_A4) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_ARD_D9) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_BUTTON) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_OSC32_IN) |   \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOC_OSC32_OUT))\r
+#define VAL_GPIOC_OSPEEDR           (PIN_OSPEED_HIGH(GPIOC_ARD_A5) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOC_ARD_A4) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOC_PIN2) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOC_PIN3) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOC_PIN4) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOC_PIN5) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOC_PIN6) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOC_ARD_D9) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOC_PIN8) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOC_PIN9) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOC_PIN10) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOC_PIN11) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOC_PIN12) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOC_BUTTON) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOC_OSC32_IN) |      \\r
+                                     PIN_OSPEED_HIGH(GPIOC_OSC32_OUT))\r
+#define VAL_GPIOC_PUPDR             (PIN_PUPDR_FLOATING(GPIOC_ARD_A5) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_ARD_A4) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_PIN2) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_PIN3) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_PIN4) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_PIN5) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_PIN6) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_ARD_D9) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_PIN8) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_PIN9) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_PIN10) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_PIN11) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_PIN12) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_BUTTON) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_OSC32_IN) |   \\r
+                                     PIN_PUPDR_FLOATING(GPIOC_OSC32_OUT))\r
+#define VAL_GPIOC_ODR               (PIN_ODR_HIGH(GPIOC_ARD_A5) |           \\r
+                                     PIN_ODR_HIGH(GPIOC_ARD_A4) |           \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_ARD_D9) |           \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOC_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOC_BUTTON) |           \\r
+                                     PIN_ODR_HIGH(GPIOC_OSC32_IN) |         \\r
+                                     PIN_ODR_HIGH(GPIOC_OSC32_OUT))\r
+#define VAL_GPIOC_AFRL              (PIN_AFIO_AF(GPIOC_ARD_A5, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOC_ARD_A4, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOC_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOC_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOC_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOC_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOC_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOC_ARD_D9, 0U))\r
+#define VAL_GPIOC_AFRH              (PIN_AFIO_AF(GPIOC_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOC_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOC_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOC_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOC_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOC_BUTTON, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOC_OSC32_IN, 0U) |      \\r
+                                     PIN_AFIO_AF(GPIOC_OSC32_OUT, 0U))\r
+#define VAL_GPIOC_ASCR              (PIN_ASCR_DISABLED(GPIOC_ARD_A5) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOC_ARD_A4) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOC_PIN2) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOC_PIN3) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOC_PIN4) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOC_PIN5) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOC_PIN6) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOC_ARD_D9) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOC_PIN8) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOC_PIN9) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOC_PIN10) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOC_PIN11) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOC_PIN12) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOC_BUTTON) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOC_OSC32_IN) |    \\r
+                                     PIN_ASCR_DISABLED(GPIOC_OSC32_OUT))\r
+#define VAL_GPIOC_LOCKR             (PIN_LOCKR_DISABLED(GPIOC_ARD_A5) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_ARD_A4) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_PIN2) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_PIN3) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_PIN4) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_PIN5) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_PIN6) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_ARD_D9) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_PIN8) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_PIN9) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_PIN10) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_PIN11) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_PIN12) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_BUTTON) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_OSC32_IN) |   \\r
+                                     PIN_LOCKR_DISABLED(GPIOC_OSC32_OUT))\r
+\r
+/*\r
+ * GPIOD setup:\r
+ *\r
+ * PD0  - PIN0                      (analog).\r
+ * PD1  - PIN1                      (analog).\r
+ * PD2  - PIN2                      (analog).\r
+ * PD3  - PIN3                      (analog).\r
+ * PD4  - PIN4                      (analog).\r
+ * PD5  - PIN5                      (analog).\r
+ * PD6  - PIN6                      (analog).\r
+ * PD7  - PIN7                      (analog).\r
+ * PD8  - PIN8                      (analog).\r
+ * PD9  - PIN9                      (analog).\r
+ * PD10 - PIN10                     (analog).\r
+ * PD11 - PIN11                     (analog).\r
+ * PD12 - PIN12                     (analog).\r
+ * PD13 - PIN13                     (analog).\r
+ * PD14 - PIN14                     (analog).\r
+ * PD15 - PIN15                     (analog).\r
+ */\r
+#define VAL_GPIOD_MODER             (PIN_MODE_ANALOG(GPIOD_PIN0) |          \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN1) |          \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN2) |          \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN3) |          \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN4) |          \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN5) |          \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN6) |          \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN7) |          \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN8) |          \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN9) |          \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN10) |         \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN11) |         \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN12) |         \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN13) |         \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN14) |         \\r
+                                     PIN_MODE_ANALOG(GPIOD_PIN15))\r
+#define VAL_GPIOD_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOD_PIN0) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN1) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN13) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN15))\r
+#define VAL_GPIOD_OSPEEDR           (PIN_OSPEED_HIGH(GPIOD_PIN0) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN1) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN2) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN3) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN4) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN5) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN6) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN7) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN8) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN9) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN10) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN11) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN12) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN13) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN14) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOD_PIN15))\r
+#define VAL_GPIOD_PUPDR             (PIN_PUPDR_FLOATING(GPIOD_PIN0) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN1) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN2) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN3) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN4) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN5) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN6) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN7) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN8) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN9) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN10) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN11) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN12) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN13) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN14) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOD_PIN15))\r
+#define VAL_GPIOD_ODR               (PIN_ODR_HIGH(GPIOD_PIN0) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN1) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN13) |            \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOD_PIN15))\r
+#define VAL_GPIOD_AFRL              (PIN_AFIO_AF(GPIOD_PIN0, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN1, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN7, 0U))\r
+#define VAL_GPIOD_AFRH              (PIN_AFIO_AF(GPIOD_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOD_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOD_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOD_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOD_PIN13, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOD_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOD_PIN15, 0U))\r
+#define VAL_GPIOD_ASCR              (PIN_ASCR_DISABLED(GPIOD_PIN0) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN1) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN2) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN3) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN4) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN5) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN6) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN7) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN8) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN9) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN10) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN11) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN12) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN13) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN14) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOD_PIN15))\r
+#define VAL_GPIOD_LOCKR             (PIN_LOCKR_DISABLED(GPIOD_PIN0) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN1) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN2) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN3) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN4) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN5) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN6) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN7) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN8) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN9) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN10) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN11) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN12) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN13) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN14) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOD_PIN15))\r
+\r
+/*\r
+ * GPIOE setup:\r
+ *\r
+ * PE0  - PIN0                      (analog).\r
+ * PE1  - PIN1                      (analog).\r
+ * PE2  - PIN2                      (analog).\r
+ * PE3  - PIN3                      (analog).\r
+ * PE4  - PIN4                      (analog).\r
+ * PE5  - PIN5                      (analog).\r
+ * PE6  - PIN6                      (analog).\r
+ * PE7  - PIN7                      (analog).\r
+ * PE8  - PIN8                      (analog).\r
+ * PE9  - PIN9                      (analog).\r
+ * PE10 - PIN10                     (analog).\r
+ * PE11 - PIN11                     (analog).\r
+ * PE12 - PIN12                     (analog).\r
+ * PE13 - PIN13                     (analog).\r
+ * PE14 - PIN14                     (analog).\r
+ * PE15 - PIN15                     (analog).\r
+ */\r
+#define VAL_GPIOE_MODER             (PIN_MODE_ANALOG(GPIOE_PIN0) |          \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN1) |          \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN2) |          \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN3) |          \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN4) |          \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN5) |          \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN6) |          \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN7) |          \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN8) |          \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN9) |          \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN10) |         \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN11) |         \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN12) |         \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN13) |         \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN14) |         \\r
+                                     PIN_MODE_ANALOG(GPIOE_PIN15))\r
+#define VAL_GPIOE_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOE_PIN0) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN1) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN13) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN15))\r
+#define VAL_GPIOE_OSPEEDR           (PIN_OSPEED_HIGH(GPIOE_PIN0) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN1) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN2) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN3) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN4) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN5) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN6) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN7) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN8) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN9) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN10) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN11) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN12) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN13) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN14) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOE_PIN15))\r
+#define VAL_GPIOE_PUPDR             (PIN_PUPDR_FLOATING(GPIOE_PIN0) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN1) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN2) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN3) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN4) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN5) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN6) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN7) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN8) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN9) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN10) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN11) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN12) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN13) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN14) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOE_PIN15))\r
+#define VAL_GPIOE_ODR               (PIN_ODR_HIGH(GPIOE_PIN0) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN1) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN13) |            \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOE_PIN15))\r
+#define VAL_GPIOE_AFRL              (PIN_AFIO_AF(GPIOE_PIN0, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN1, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN7, 0U))\r
+#define VAL_GPIOE_AFRH              (PIN_AFIO_AF(GPIOE_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOE_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOE_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOE_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOE_PIN13, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOE_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOE_PIN15, 0U))\r
+#define VAL_GPIOE_ASCR              (PIN_ASCR_DISABLED(GPIOE_PIN0) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN1) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN2) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN3) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN4) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN5) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN6) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN7) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN8) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN9) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN10) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN11) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN12) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN13) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN14) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOE_PIN15))\r
+#define VAL_GPIOE_LOCKR             (PIN_LOCKR_DISABLED(GPIOE_PIN0) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN1) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN2) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN3) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN4) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN5) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN6) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN7) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN8) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN9) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN10) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN11) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN12) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN13) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN14) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOE_PIN15))\r
+\r
+/*\r
+ * GPIOF setup:\r
+ *\r
+ * PF0  - PIN0                      (analog).\r
+ * PF1  - PIN1                      (analog).\r
+ * PF2  - PIN2                      (analog).\r
+ * PF3  - PIN3                      (analog).\r
+ * PF4  - PIN4                      (analog).\r
+ * PF5  - PIN5                      (analog).\r
+ * PF6  - PIN6                      (analog).\r
+ * PF7  - PIN7                      (analog).\r
+ * PF8  - PIN8                      (analog).\r
+ * PF9  - PIN9                      (analog).\r
+ * PF10 - PIN10                     (analog).\r
+ * PF11 - PIN11                     (analog).\r
+ * PF12 - PIN12                     (analog).\r
+ * PF13 - PIN13                     (analog).\r
+ * PF14 - PIN14                     (analog).\r
+ * PF15 - PIN15                     (analog).\r
+ */\r
+#define VAL_GPIOF_MODER             (PIN_MODE_ANALOG(GPIOF_PIN0) |          \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN1) |          \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN2) |          \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN3) |          \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN4) |          \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN5) |          \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN6) |          \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN7) |          \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN8) |          \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN9) |          \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN10) |         \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN11) |         \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN12) |         \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN13) |         \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN14) |         \\r
+                                     PIN_MODE_ANALOG(GPIOF_PIN15))\r
+#define VAL_GPIOF_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOF_PIN0) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN1) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN13) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN15))\r
+#define VAL_GPIOF_OSPEEDR           (PIN_OSPEED_HIGH(GPIOF_PIN0) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN1) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN2) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN3) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN4) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN5) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN6) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN7) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN8) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN9) |          \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN10) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN11) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN12) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN13) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN14) |         \\r
+                                     PIN_OSPEED_HIGH(GPIOF_PIN15))\r
+#define VAL_GPIOF_PUPDR             (PIN_PUPDR_FLOATING(GPIOF_PIN0) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN1) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN2) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN3) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN4) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN5) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN6) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN7) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN8) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN9) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN10) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN11) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN12) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN13) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN14) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOF_PIN15))\r
+#define VAL_GPIOF_ODR               (PIN_ODR_HIGH(GPIOF_PIN0) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN1) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN13) |            \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOF_PIN15))\r
+#define VAL_GPIOF_AFRL              (PIN_AFIO_AF(GPIOF_PIN0, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN1, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN7, 0U))\r
+#define VAL_GPIOF_AFRH              (PIN_AFIO_AF(GPIOF_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOF_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOF_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOF_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOF_PIN13, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOF_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOF_PIN15, 0U))\r
+#define VAL_GPIOF_ASCR              (PIN_ASCR_DISABLED(GPIOF_PIN0) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN1) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN2) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN3) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN4) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN5) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN6) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN7) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN8) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN9) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN10) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN11) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN12) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN13) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN14) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOF_PIN15))\r
+#define VAL_GPIOF_LOCKR             (PIN_LOCKR_DISABLED(GPIOF_PIN0) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN1) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN2) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN3) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN4) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN5) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN6) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN7) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN8) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN9) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN10) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN11) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN12) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN13) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN14) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOF_PIN15))\r
+\r
+/*\r
+ * GPIOG setup:\r
+ *\r
+ * PG0  - PIN0                      (analog).\r
+ * PG1  - PIN1                      (analog).\r
+ * PG2  - PIN2                      (analog).\r
+ * PG3  - PIN3                      (analog).\r
+ * PG4  - PIN4                      (analog).\r
+ * PG5  - PIN5                      (analog).\r
+ * PG6  - PIN6                      (analog).\r
+ * PG7  - PIN7                      (analog).\r
+ * PG8  - PIN8                      (analog).\r
+ * PG9  - PIN9                      (analog).\r
+ * PG10 - PIN10                     (analog).\r
+ * PG11 - PIN11                     (analog).\r
+ * PG12 - PIN12                     (analog).\r
+ * PG13 - PIN13                     (analog).\r
+ * PG14 - PIN14                     (analog).\r
+ * PG15 - PIN15                     (analog).\r
+ */\r
+#define VAL_GPIOG_MODER             (PIN_MODE_ANALOG(GPIOG_PIN0) |          \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN1) |          \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN2) |          \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN3) |          \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN4) |          \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN5) |          \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN6) |          \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN7) |          \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN8) |          \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN9) |          \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN10) |         \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN11) |         \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN12) |         \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN13) |         \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN14) |         \\r
+                                     PIN_MODE_ANALOG(GPIOG_PIN15))\r
+#define VAL_GPIOG_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOG_PIN0) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN1) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN13) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOG_PIN15))\r
+#define VAL_GPIOG_OSPEEDR           (PIN_OSPEED_VERYLOW(GPIOG_PIN0) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN1) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN2) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN3) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN4) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN5) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN6) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN7) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN8) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN9) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN10) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN11) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN12) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN13) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN14) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOG_PIN15))\r
+#define VAL_GPIOG_PUPDR             (PIN_PUPDR_FLOATING(GPIOG_PIN0) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN1) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN2) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN3) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN4) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN5) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN6) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN7) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN8) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN9) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN10) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN11) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN12) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN13) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN14) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOG_PIN15))\r
+#define VAL_GPIOG_ODR               (PIN_ODR_HIGH(GPIOG_PIN0) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN1) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN13) |            \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOG_PIN15))\r
+#define VAL_GPIOG_AFRL              (PIN_AFIO_AF(GPIOG_PIN0, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN1, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN7, 0U))\r
+#define VAL_GPIOG_AFRH              (PIN_AFIO_AF(GPIOG_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOG_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOG_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOG_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOG_PIN13, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOG_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOG_PIN15, 0U))\r
+#define VAL_GPIOG_ASCR              (PIN_ASCR_DISABLED(GPIOG_PIN0) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN1) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN2) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN3) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN4) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN5) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN6) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN7) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN8) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN9) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN10) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN11) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN12) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN13) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN14) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOG_PIN15))\r
+#define VAL_GPIOG_LOCKR             (PIN_LOCKR_DISABLED(GPIOG_PIN0) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN1) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN2) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN3) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN4) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN5) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN6) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN7) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN8) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN9) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN10) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN11) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN12) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN13) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN14) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOG_PIN15))\r
+\r
+/*\r
+ * GPIOH setup:\r
+ *\r
+ * PH0  - OSC_IN                    (input floating).\r
+ * PH1  - OSC_OUT                   (input floating).\r
+ * PH2  - PIN2                      (analog).\r
+ * PH3  - PIN3                      (analog).\r
+ * PH4  - PIN4                      (analog).\r
+ * PH5  - PIN5                      (analog).\r
+ * PH6  - PIN6                      (analog).\r
+ * PH7  - PIN7                      (analog).\r
+ * PH8  - PIN8                      (analog).\r
+ * PH9  - PIN9                      (analog).\r
+ * PH10 - PIN10                     (analog).\r
+ * PH11 - PIN11                     (analog).\r
+ * PH12 - PIN12                     (analog).\r
+ * PH13 - PIN13                     (analog).\r
+ * PH14 - PIN14                     (analog).\r
+ * PH15 - PIN15                     (analog).\r
+ */\r
+#define VAL_GPIOH_MODER             (PIN_MODE_INPUT(GPIOH_OSC_IN) |         \\r
+                                     PIN_MODE_INPUT(GPIOH_OSC_OUT) |        \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN2) |          \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN3) |          \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN4) |          \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN5) |          \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN6) |          \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN7) |          \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN8) |          \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN9) |          \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN10) |         \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN11) |         \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN12) |         \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN13) |         \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN14) |         \\r
+                                     PIN_MODE_ANALOG(GPIOH_PIN15))\r
+#define VAL_GPIOH_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOH_OSC_IN) |     \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_OSC_OUT) |    \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN2) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN3) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN4) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN5) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN6) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN7) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN8) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN9) |       \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN10) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN11) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN12) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN13) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN14) |      \\r
+                                     PIN_OTYPE_PUSHPULL(GPIOH_PIN15))\r
+#define VAL_GPIOH_OSPEEDR           (PIN_OSPEED_HIGH(GPIOH_OSC_IN) |        \\r
+                                     PIN_OSPEED_HIGH(GPIOH_OSC_OUT) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN2) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN3) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN4) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN5) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN6) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN7) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN8) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN9) |       \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN10) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN11) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN12) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN13) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN14) |      \\r
+                                     PIN_OSPEED_VERYLOW(GPIOH_PIN15))\r
+#define VAL_GPIOH_PUPDR             (PIN_PUPDR_FLOATING(GPIOH_OSC_IN) |     \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_OSC_OUT) |    \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN2) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN3) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN4) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN5) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN6) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN7) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN8) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN9) |       \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN10) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN11) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN12) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN13) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN14) |      \\r
+                                     PIN_PUPDR_FLOATING(GPIOH_PIN15))\r
+#define VAL_GPIOH_ODR               (PIN_ODR_HIGH(GPIOH_OSC_IN) |           \\r
+                                     PIN_ODR_HIGH(GPIOH_OSC_OUT) |          \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN2) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN3) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN4) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN5) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN6) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN7) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN8) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN9) |             \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN10) |            \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN11) |            \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN12) |            \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN13) |            \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN14) |            \\r
+                                     PIN_ODR_HIGH(GPIOH_PIN15))\r
+#define VAL_GPIOH_AFRL              (PIN_AFIO_AF(GPIOH_OSC_IN, 0U) |        \\r
+                                     PIN_AFIO_AF(GPIOH_OSC_OUT, 0U) |       \\r
+                                     PIN_AFIO_AF(GPIOH_PIN2, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN3, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN4, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN5, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN6, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN7, 0U))\r
+#define VAL_GPIOH_AFRH              (PIN_AFIO_AF(GPIOH_PIN8, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN9, 0U) |          \\r
+                                     PIN_AFIO_AF(GPIOH_PIN10, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOH_PIN11, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOH_PIN12, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOH_PIN13, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOH_PIN14, 0U) |         \\r
+                                     PIN_AFIO_AF(GPIOH_PIN15, 0U))\r
+#define VAL_GPIOH_ASCR              (PIN_ASCR_DISABLED(GPIOH_OSC_IN) |      \\r
+                                     PIN_ASCR_DISABLED(GPIOH_OSC_OUT) |     \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN2) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN3) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN4) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN5) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN6) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN7) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN8) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN9) |        \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN10) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN11) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN12) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN13) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN14) |       \\r
+                                     PIN_ASCR_DISABLED(GPIOH_PIN15))\r
+#define VAL_GPIOH_LOCKR             (PIN_LOCKR_DISABLED(GPIOH_OSC_IN) |     \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_OSC_OUT) |    \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN2) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN3) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN4) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN5) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN6) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN7) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN8) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN9) |       \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN10) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN11) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN12) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN13) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN14) |      \\r
+                                     PIN_LOCKR_DISABLED(GPIOH_PIN15))\r
+\r
+/*===========================================================================*/\r
+/* External declarations.                                                    */\r
+/*===========================================================================*/\r
+\r
+#if !defined(_FROM_ASM_)\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+  void boardInit(void);\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+#endif /* _FROM_ASM_ */\r
+\r
+#define LINE_LED_RED   PAL_LINE(GPIOC_BASE, 10U)\r
+#define LINE_LED_GREEN PAL_LINE(GPIOC_BASE, 11U)\r
+#define LINE_LED_BLUE  PAL_LINE(GPIOC_BASE, 12U)\r
+\r
+#endif /* BOARD_H */\r
diff --git a/firmware/source/cfg/chconf.h b/firmware/source/cfg/chconf.h
new file mode 100644 (file)
index 0000000..628d7e8
--- /dev/null
@@ -0,0 +1,756 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+/**\r
+ * @file    rt/templates/chconf.h\r
+ * @brief   Configuration file template.\r
+ * @details A copy of this file must be placed in each project directory, it\r
+ *          contains the application specific kernel settings.\r
+ *\r
+ * @addtogroup config\r
+ * @details Kernel related settings and hooks.\r
+ * @{\r
+ */\r
+\r
+#ifndef CHCONF_H\r
+#define CHCONF_H\r
+\r
+#define _CHIBIOS_RT_CONF_\r
+#define _CHIBIOS_RT_CONF_VER_6_1_\r
+\r
+/*===========================================================================*/\r
+/**\r
+ * @name System timers settings\r
+ * @{\r
+ */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   System time counter resolution.\r
+ * @note    Allowed values are 16 or 32 bits.\r
+ */\r
+#if !defined(CH_CFG_ST_RESOLUTION)\r
+#define CH_CFG_ST_RESOLUTION                32\r
+#endif\r
+\r
+/**\r
+ * @brief   System tick frequency.\r
+ * @details Frequency of the system timer that drives the system ticks. This\r
+ *          setting also defines the system tick time unit.\r
+ */\r
+#if !defined(CH_CFG_ST_FREQUENCY)\r
+#define CH_CFG_ST_FREQUENCY                 10000\r
+#endif\r
+\r
+/**\r
+ * @brief   Time intervals data size.\r
+ * @note    Allowed values are 16, 32 or 64 bits.\r
+ */\r
+#if !defined(CH_CFG_INTERVALS_SIZE)\r
+#define CH_CFG_INTERVALS_SIZE               32\r
+#endif\r
+\r
+/**\r
+ * @brief   Time types data size.\r
+ * @note    Allowed values are 16 or 32 bits.\r
+ */\r
+#if !defined(CH_CFG_TIME_TYPES_SIZE)\r
+#define CH_CFG_TIME_TYPES_SIZE              32\r
+#endif\r
+\r
+/**\r
+ * @brief   Time delta constant for the tick-less mode.\r
+ * @note    If this value is zero then the system uses the classic\r
+ *          periodic tick. This value represents the minimum number\r
+ *          of ticks that is safe to specify in a timeout directive.\r
+ *          The value one is not valid, timeouts are rounded up to\r
+ *          this value.\r
+ */\r
+#if !defined(CH_CFG_ST_TIMEDELTA)\r
+#define CH_CFG_ST_TIMEDELTA                 2\r
+#endif\r
+\r
+/** @} */\r
+\r
+/*===========================================================================*/\r
+/**\r
+ * @name Kernel parameters and options\r
+ * @{\r
+ */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Round robin interval.\r
+ * @details This constant is the number of system ticks allowed for the\r
+ *          threads before preemption occurs. Setting this value to zero\r
+ *          disables the preemption for threads with equal priority and the\r
+ *          round robin becomes cooperative. Note that higher priority\r
+ *          threads can still preempt, the kernel is always preemptive.\r
+ * @note    Disabling the round robin preemption makes the kernel more compact\r
+ *          and generally faster.\r
+ * @note    The round robin preemption is not supported in tickless mode and\r
+ *          must be set to zero in that case.\r
+ */\r
+#if !defined(CH_CFG_TIME_QUANTUM)\r
+#define CH_CFG_TIME_QUANTUM                 0\r
+#endif\r
+\r
+/**\r
+ * @brief   Idle thread automatic spawn suppression.\r
+ * @details When this option is activated the function @p chSysInit()\r
+ *          does not spawn the idle thread. The application @p main()\r
+ *          function becomes the idle thread and must implement an\r
+ *          infinite loop.\r
+ */\r
+#if !defined(CH_CFG_NO_IDLE_THREAD)\r
+#define CH_CFG_NO_IDLE_THREAD               FALSE\r
+#endif\r
+\r
+/** @} */\r
+\r
+/*===========================================================================*/\r
+/**\r
+ * @name Performance options\r
+ * @{\r
+ */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   OS optimization.\r
+ * @details If enabled then time efficient rather than space efficient code\r
+ *          is used when two possible implementations exist.\r
+ *\r
+ * @note    This is not related to the compiler optimization options.\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_OPTIMIZE_SPEED)\r
+#define CH_CFG_OPTIMIZE_SPEED               TRUE\r
+#endif\r
+\r
+/** @} */\r
+\r
+/*===========================================================================*/\r
+/**\r
+ * @name Subsystem options\r
+ * @{\r
+ */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Time Measurement APIs.\r
+ * @details If enabled then the time measurement APIs are included in\r
+ *          the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_TM)\r
+#define CH_CFG_USE_TM                       TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Threads registry APIs.\r
+ * @details If enabled then the registry APIs are included in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_REGISTRY)\r
+#define CH_CFG_USE_REGISTRY                 TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Threads synchronization APIs.\r
+ * @details If enabled then the @p chThdWait() function is included in\r
+ *          the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_WAITEXIT)\r
+#define CH_CFG_USE_WAITEXIT                 TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Semaphores APIs.\r
+ * @details If enabled then the Semaphores APIs are included in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_SEMAPHORES)\r
+#define CH_CFG_USE_SEMAPHORES               TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Semaphores queuing mode.\r
+ * @details If enabled then the threads are enqueued on semaphores by\r
+ *          priority rather than in FIFO order.\r
+ *\r
+ * @note    The default is @p FALSE. Enable this if you have special\r
+ *          requirements.\r
+ * @note    Requires @p CH_CFG_USE_SEMAPHORES.\r
+ */\r
+#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)\r
+#define CH_CFG_USE_SEMAPHORES_PRIORITY      FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Mutexes APIs.\r
+ * @details If enabled then the mutexes APIs are included in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_MUTEXES)\r
+#define CH_CFG_USE_MUTEXES                  TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables recursive behavior on mutexes.\r
+ * @note    Recursive mutexes are heavier and have an increased\r
+ *          memory footprint.\r
+ *\r
+ * @note    The default is @p FALSE.\r
+ * @note    Requires @p CH_CFG_USE_MUTEXES.\r
+ */\r
+#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)\r
+#define CH_CFG_USE_MUTEXES_RECURSIVE        FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Conditional Variables APIs.\r
+ * @details If enabled then the conditional variables APIs are included\r
+ *          in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ * @note    Requires @p CH_CFG_USE_MUTEXES.\r
+ */\r
+#if !defined(CH_CFG_USE_CONDVARS)\r
+#define CH_CFG_USE_CONDVARS                 TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Conditional Variables APIs with timeout.\r
+ * @details If enabled then the conditional variables APIs with timeout\r
+ *          specification are included in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ * @note    Requires @p CH_CFG_USE_CONDVARS.\r
+ */\r
+#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)\r
+#define CH_CFG_USE_CONDVARS_TIMEOUT         TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Events Flags APIs.\r
+ * @details If enabled then the event flags APIs are included in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_EVENTS)\r
+#define CH_CFG_USE_EVENTS                   TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Events Flags APIs with timeout.\r
+ * @details If enabled then the events APIs with timeout specification\r
+ *          are included in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ * @note    Requires @p CH_CFG_USE_EVENTS.\r
+ */\r
+#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)\r
+#define CH_CFG_USE_EVENTS_TIMEOUT           TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Synchronous Messages APIs.\r
+ * @details If enabled then the synchronous messages APIs are included\r
+ *          in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_MESSAGES)\r
+#define CH_CFG_USE_MESSAGES                 TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Synchronous Messages queuing mode.\r
+ * @details If enabled then messages are served by priority rather than in\r
+ *          FIFO order.\r
+ *\r
+ * @note    The default is @p FALSE. Enable this if you have special\r
+ *          requirements.\r
+ * @note    Requires @p CH_CFG_USE_MESSAGES.\r
+ */\r
+#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)\r
+#define CH_CFG_USE_MESSAGES_PRIORITY        FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Dynamic Threads APIs.\r
+ * @details If enabled then the dynamic threads creation APIs are included\r
+ *          in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ * @note    Requires @p CH_CFG_USE_WAITEXIT.\r
+ * @note    Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.\r
+ */\r
+#if !defined(CH_CFG_USE_DYNAMIC)\r
+#define CH_CFG_USE_DYNAMIC                  TRUE\r
+#endif\r
+\r
+/** @} */\r
+\r
+/*===========================================================================*/\r
+/**\r
+ * @name OSLIB options\r
+ * @{\r
+ */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Mailboxes APIs.\r
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are\r
+ *          included in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ * @note    Requires @p CH_CFG_USE_SEMAPHORES.\r
+ */\r
+#if !defined(CH_CFG_USE_MAILBOXES)\r
+#define CH_CFG_USE_MAILBOXES                TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Core Memory Manager APIs.\r
+ * @details If enabled then the core memory manager APIs are included\r
+ *          in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_MEMCORE)\r
+#define CH_CFG_USE_MEMCORE                  TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Managed RAM size.\r
+ * @details Size of the RAM area to be managed by the OS. If set to zero\r
+ *          then the whole available RAM is used. The core memory is made\r
+ *          available to the heap allocator and/or can be used directly through\r
+ *          the simplified core memory allocator.\r
+ *\r
+ * @note    In order to let the OS manage the whole RAM the linker script must\r
+ *          provide the @p __heap_base__ and @p __heap_end__ symbols.\r
+ * @note    Requires @p CH_CFG_USE_MEMCORE.\r
+ */\r
+#if !defined(CH_CFG_MEMCORE_SIZE)\r
+#define CH_CFG_MEMCORE_SIZE                 0\r
+#endif\r
+\r
+/**\r
+ * @brief   Heap Allocator APIs.\r
+ * @details If enabled then the memory heap allocator APIs are included\r
+ *          in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ * @note    Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or\r
+ *          @p CH_CFG_USE_SEMAPHORES.\r
+ * @note    Mutexes are recommended.\r
+ */\r
+#if !defined(CH_CFG_USE_HEAP)\r
+#define CH_CFG_USE_HEAP                     TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Memory Pools Allocator APIs.\r
+ * @details If enabled then the memory pools allocator APIs are included\r
+ *          in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_MEMPOOLS)\r
+#define CH_CFG_USE_MEMPOOLS                 TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Objects FIFOs APIs.\r
+ * @details If enabled then the objects FIFOs APIs are included\r
+ *          in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_OBJ_FIFOS)\r
+#define CH_CFG_USE_OBJ_FIFOS                TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Pipes APIs.\r
+ * @details If enabled then the pipes APIs are included\r
+ *          in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_PIPES)\r
+#define CH_CFG_USE_PIPES                    TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Objects Caches APIs.\r
+ * @details If enabled then the objects caches APIs are included\r
+ *          in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_OBJ_CACHES)\r
+#define CH_CFG_USE_OBJ_CACHES               TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Delegate threads APIs.\r
+ * @details If enabled then the delegate threads APIs are included\r
+ *          in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_DELEGATES)\r
+#define CH_CFG_USE_DELEGATES                TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Jobs Queues APIs.\r
+ * @details If enabled then the jobs queues APIs are included\r
+ *          in the kernel.\r
+ *\r
+ * @note    The default is @p TRUE.\r
+ */\r
+#if !defined(CH_CFG_USE_JOBS)\r
+#define CH_CFG_USE_JOBS                     TRUE\r
+#endif\r
+\r
+/** @} */\r
+\r
+/*===========================================================================*/\r
+/**\r
+ * @name Objects factory options\r
+ * @{\r
+ */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Objects Factory APIs.\r
+ * @details If enabled then the objects factory APIs are included in the\r
+ *          kernel.\r
+ *\r
+ * @note    The default is @p FALSE.\r
+ */\r
+#if !defined(CH_CFG_USE_FACTORY)\r
+#define CH_CFG_USE_FACTORY                  TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Maximum length for object names.\r
+ * @details If the specified length is zero then the name is stored by\r
+ *          pointer but this could have unintended side effects.\r
+ */\r
+#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)\r
+#define CH_CFG_FACTORY_MAX_NAMES_LENGTH     8\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the registry of generic objects.\r
+ */\r
+#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)\r
+#define CH_CFG_FACTORY_OBJECTS_REGISTRY     TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables factory for generic buffers.\r
+ */\r
+#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)\r
+#define CH_CFG_FACTORY_GENERIC_BUFFERS      TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables factory for semaphores.\r
+ */\r
+#if !defined(CH_CFG_FACTORY_SEMAPHORES)\r
+#define CH_CFG_FACTORY_SEMAPHORES           TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables factory for mailboxes.\r
+ */\r
+#if !defined(CH_CFG_FACTORY_MAILBOXES)\r
+#define CH_CFG_FACTORY_MAILBOXES            TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables factory for objects FIFOs.\r
+ */\r
+#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)\r
+#define CH_CFG_FACTORY_OBJ_FIFOS            TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables factory for Pipes.\r
+ */\r
+#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)\r
+#define CH_CFG_FACTORY_PIPES                TRUE\r
+#endif\r
+\r
+/** @} */\r
+\r
+/*===========================================================================*/\r
+/**\r
+ * @name Debug options\r
+ * @{\r
+ */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Debug option, kernel statistics.\r
+ *\r
+ * @note    The default is @p FALSE.\r
+ */\r
+#if !defined(CH_DBG_STATISTICS)\r
+#define CH_DBG_STATISTICS                   FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Debug option, system state check.\r
+ * @details If enabled the correct call protocol for system APIs is checked\r
+ *          at runtime.\r
+ *\r
+ * @note    The default is @p FALSE.\r
+ */\r
+#if !defined(CH_DBG_SYSTEM_STATE_CHECK)\r
+#define CH_DBG_SYSTEM_STATE_CHECK           TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Debug option, parameters checks.\r
+ * @details If enabled then the checks on the API functions input\r
+ *          parameters are activated.\r
+ *\r
+ * @note    The default is @p FALSE.\r
+ */\r
+#if !defined(CH_DBG_ENABLE_CHECKS)\r
+#define CH_DBG_ENABLE_CHECKS                TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Debug option, consistency checks.\r
+ * @details If enabled then all the assertions in the kernel code are\r
+ *          activated. This includes consistency checks inside the kernel,\r
+ *          runtime anomalies and port-defined checks.\r
+ *\r
+ * @note    The default is @p FALSE.\r
+ */\r
+#if !defined(CH_DBG_ENABLE_ASSERTS)\r
+#define CH_DBG_ENABLE_ASSERTS               TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Debug option, trace buffer.\r
+ * @details If enabled then the trace buffer is activated.\r
+ *\r
+ * @note    The default is @p CH_DBG_TRACE_MASK_DISABLED.\r
+ */\r
+#if !defined(CH_DBG_TRACE_MASK)\r
+#define CH_DBG_TRACE_MASK                   CH_DBG_TRACE_MASK_NONE\r
+#endif\r
+\r
+/**\r
+ * @brief   Trace buffer entries.\r
+ * @note    The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is\r
+ *          different from @p CH_DBG_TRACE_MASK_DISABLED.\r
+ */\r
+#if !defined(CH_DBG_TRACE_BUFFER_SIZE)\r
+#define CH_DBG_TRACE_BUFFER_SIZE            128\r
+#endif\r
+\r
+/**\r
+ * @brief   Debug option, stack checks.\r
+ * @details If enabled then a runtime stack check is performed.\r
+ *\r
+ * @note    The default is @p FALSE.\r
+ * @note    The stack check is performed in a architecture/port dependent way.\r
+ *          It may not be implemented or some ports.\r
+ * @note    The default failure mode is to halt the system with the global\r
+ *          @p panic_msg variable set to @p NULL.\r
+ */\r
+#if !defined(CH_DBG_ENABLE_STACK_CHECK)\r
+#define CH_DBG_ENABLE_STACK_CHECK           FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Debug option, stacks initialization.\r
+ * @details If enabled then the threads working area is filled with a byte\r
+ *          value when a thread is created. This can be useful for the\r
+ *          runtime measurement of the used stack.\r
+ *\r
+ * @note    The default is @p FALSE.\r
+ */\r
+#if !defined(CH_DBG_FILL_THREADS)\r
+#define CH_DBG_FILL_THREADS                 FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Debug option, threads profiling.\r
+ * @details If enabled then a field is added to the @p thread_t structure that\r
+ *          counts the system ticks occurred while executing the thread.\r
+ *\r
+ * @note    The default is @p FALSE.\r
+ * @note    This debug option is not currently compatible with the\r
+ *          tickless mode.\r
+ */\r
+#if !defined(CH_DBG_THREADS_PROFILING)\r
+#define CH_DBG_THREADS_PROFILING            FALSE\r
+#endif\r
+\r
+/** @} */\r
+\r
+/*===========================================================================*/\r
+/**\r
+ * @name Kernel hooks\r
+ * @{\r
+ */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   System structure extension.\r
+ * @details User fields added to the end of the @p ch_system_t structure.\r
+ */\r
+#define CH_CFG_SYSTEM_EXTRA_FIELDS                                          \\r
+  /* Add threads custom fields here.*/\r
+\r
+/**\r
+ * @brief   System initialization hook.\r
+ * @details User initialization code added to the @p chSysInit() function\r
+ *          just before interrupts are enabled globally.\r
+ */\r
+#define CH_CFG_SYSTEM_INIT_HOOK() {                                         \\r
+  /* Add threads initialization code here.*/                                \\r
+}\r
+\r
+/**\r
+ * @brief   Threads descriptor structure extension.\r
+ * @details User fields added to the end of the @p thread_t structure.\r
+ */\r
+#define CH_CFG_THREAD_EXTRA_FIELDS                                          \\r
+  /* Add threads custom fields here.*/\r
+\r
+/**\r
+ * @brief   Threads initialization hook.\r
+ * @details User initialization code added to the @p _thread_init() function.\r
+ *\r
+ * @note    It is invoked from within @p _thread_init() and implicitly from all\r
+ *          the threads creation APIs.\r
+ */\r
+#define CH_CFG_THREAD_INIT_HOOK(tp) {                                       \\r
+  /* Add threads initialization code here.*/                                \\r
+}\r
+\r
+/**\r
+ * @brief   Threads finalization hook.\r
+ * @details User finalization code added to the @p chThdExit() API.\r
+ */\r
+#define CH_CFG_THREAD_EXIT_HOOK(tp) {                                       \\r
+  /* Add threads finalization code here.*/                                  \\r
+}\r
+\r
+/**\r
+ * @brief   Context switch hook.\r
+ * @details This hook is invoked just before switching between threads.\r
+ */\r
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) {                              \\r
+  /* Context switch code here.*/                                            \\r
+}\r
+\r
+/**\r
+ * @brief   ISR enter hook.\r
+ */\r
+#define CH_CFG_IRQ_PROLOGUE_HOOK() {                                        \\r
+  /* IRQ prologue code here.*/                                              \\r
+}\r
+\r
+/**\r
+ * @brief   ISR exit hook.\r
+ */\r
+#define CH_CFG_IRQ_EPILOGUE_HOOK() {                                        \\r
+  /* IRQ epilogue code here.*/                                              \\r
+}\r
+\r
+/**\r
+ * @brief   Idle thread enter hook.\r
+ * @note    This hook is invoked within a critical zone, no OS functions\r
+ *          should be invoked from here.\r
+ * @note    This macro can be used to activate a power saving mode.\r
+ */\r
+#define CH_CFG_IDLE_ENTER_HOOK() {                                          \\r
+  /* Idle-enter code here.*/                                                \\r
+}\r
+\r
+/**\r
+ * @brief   Idle thread leave hook.\r
+ * @note    This hook is invoked within a critical zone, no OS functions\r
+ *          should be invoked from here.\r
+ * @note    This macro can be used to deactivate a power saving mode.\r
+ */\r
+#define CH_CFG_IDLE_LEAVE_HOOK() {                                          \\r
+  /* Idle-leave code here.*/                                                \\r
+}\r
+\r
+/**\r
+ * @brief   Idle Loop hook.\r
+ * @details This hook is continuously invoked by the idle thread loop.\r
+ */\r
+#define CH_CFG_IDLE_LOOP_HOOK() {                                           \\r
+  /* Idle loop code here.*/                                                 \\r
+}\r
+\r
+/**\r
+ * @brief   System tick event hook.\r
+ * @details This hook is invoked in the system tick handler immediately\r
+ *          after processing the virtual timers queue.\r
+ */\r
+#define CH_CFG_SYSTEM_TICK_HOOK() {                                         \\r
+  /* System tick event code here.*/                                         \\r
+}\r
+\r
+/**\r
+ * @brief   System halt hook.\r
+ * @details This hook is invoked in case to a system halting error before\r
+ *          the system is halted.\r
+ */\r
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) {                                   \\r
+  /* System halt code here.*/                                               \\r
+}\r
+\r
+/**\r
+ * @brief   Trace hook.\r
+ * @details This hook is invoked each time a new record is written in the\r
+ *          trace buffer.\r
+ */\r
+#define CH_CFG_TRACE_HOOK(tep) {                                            \\r
+  /* Trace code here.*/                                                     \\r
+}\r
+\r
+/** @} */\r
+\r
+/*===========================================================================*/\r
+/* Port-specific settings (override port settings defaulted in chcore.h).    */\r
+/*===========================================================================*/\r
+\r
+#endif  /* CHCONF_H */\r
+\r
+/** @} */\r
diff --git a/firmware/source/cfg/halconf.h b/firmware/source/cfg/halconf.h
new file mode 100644 (file)
index 0000000..24c6fde
--- /dev/null
@@ -0,0 +1,531 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+/**\r
+ * @file    templates/halconf.h\r
+ * @brief   HAL configuration header.\r
+ * @details HAL configuration file, this file allows to enable or disable the\r
+ *          various device drivers from your application. You may also use\r
+ *          this file in order to override the device drivers default settings.\r
+ *\r
+ * @addtogroup HAL_CONF\r
+ * @{\r
+ */\r
+\r
+#ifndef HALCONF_H\r
+#define HALCONF_H\r
+\r
+#define _CHIBIOS_HAL_CONF_\r
+#define _CHIBIOS_HAL_CONF_VER_7_1_\r
+\r
+#include "mcuconf.h"\r
+\r
+/**\r
+ * @brief   Enables the PAL subsystem.\r
+ */\r
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)\r
+#define HAL_USE_PAL                         TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the ADC subsystem.\r
+ */\r
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)\r
+#define HAL_USE_ADC                         TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the CAN subsystem.\r
+ */\r
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)\r
+#define HAL_USE_CAN                         FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the cryptographic subsystem.\r
+ */\r
+#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)\r
+#define HAL_USE_CRY                         FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the DAC subsystem.\r
+ */\r
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)\r
+#define HAL_USE_DAC                         TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the EFlash subsystem.\r
+ */\r
+#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)\r
+#define HAL_USE_EFL                         FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the GPT subsystem.\r
+ */\r
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)\r
+#define HAL_USE_GPT                         TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the I2C subsystem.\r
+ */\r
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)\r
+#define HAL_USE_I2C                         FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the I2S subsystem.\r
+ */\r
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)\r
+#define HAL_USE_I2S                         FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the ICU subsystem.\r
+ */\r
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)\r
+#define HAL_USE_ICU                         FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the MAC subsystem.\r
+ */\r
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)\r
+#define HAL_USE_MAC                         FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the MMC_SPI subsystem.\r
+ */\r
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)\r
+#define HAL_USE_MMC_SPI                     FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the PWM subsystem.\r
+ */\r
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)\r
+#define HAL_USE_PWM                         FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the RTC subsystem.\r
+ */\r
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)\r
+#define HAL_USE_RTC                         FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the SDC subsystem.\r
+ */\r
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)\r
+#define HAL_USE_SDC                         FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the SERIAL subsystem.\r
+ */\r
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)\r
+#define HAL_USE_SERIAL                      FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the SERIAL over USB subsystem.\r
+ */\r
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)\r
+#define HAL_USE_SERIAL_USB                  TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the SIO subsystem.\r
+ */\r
+#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)\r
+#define HAL_USE_SIO                         FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the SPI subsystem.\r
+ */\r
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)\r
+#define HAL_USE_SPI                         FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the TRNG subsystem.\r
+ */\r
+#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)\r
+#define HAL_USE_TRNG                        FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the UART subsystem.\r
+ */\r
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)\r
+#define HAL_USE_UART                        FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the USB subsystem.\r
+ */\r
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)\r
+#define HAL_USE_USB                         TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the WDG subsystem.\r
+ */\r
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)\r
+#define HAL_USE_WDG                         FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the WSPI subsystem.\r
+ */\r
+#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)\r
+#define HAL_USE_WSPI                        FALSE\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* PAL driver related settings.                                              */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Enables synchronous APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)\r
+#define PAL_USE_CALLBACKS                   FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables synchronous APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)\r
+#define PAL_USE_WAIT                        FALSE\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* ADC driver related settings.                                              */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Enables synchronous APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)\r
+#define ADC_USE_WAIT                        FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\r
+#define ADC_USE_MUTUAL_EXCLUSION            FALSE\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* CAN driver related settings.                                              */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Sleep mode related APIs inclusion switch.\r
+ */\r
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)\r
+#define CAN_USE_SLEEP_MODE                  TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enforces the driver to use direct callbacks rather than OSAL events.\r
+ */\r
+#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)\r
+#define CAN_ENFORCE_USE_CALLBACKS           FALSE\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* CRY driver related settings.                                              */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Enables the SW fall-back of the cryptographic driver.\r
+ * @details When enabled, this option, activates a fall-back software\r
+ *          implementation for algorithms not supported by the underlying\r
+ *          hardware.\r
+ * @note    Fall-back implementations may not be present for all algorithms.\r
+ */\r
+#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)\r
+#define HAL_CRY_USE_FALLBACK                FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Makes the driver forcibly use the fall-back implementations.\r
+ */\r
+#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)\r
+#define HAL_CRY_ENFORCE_FALLBACK            FALSE\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* DAC driver related settings.                                              */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Enables synchronous APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)\r
+#define DAC_USE_WAIT                        FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\r
+#define DAC_USE_MUTUAL_EXCLUSION            FALSE\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* I2C driver related settings.                                              */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Enables the mutual exclusion APIs on the I2C bus.\r
+ */\r
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\r
+#define I2C_USE_MUTUAL_EXCLUSION            TRUE\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* MAC driver related settings.                                              */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Enables the zero-copy API.\r
+ */\r
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)\r
+#define MAC_USE_ZERO_COPY                   FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables an event sources for incoming packets.\r
+ */\r
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)\r
+#define MAC_USE_EVENTS                      TRUE\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* MMC_SPI driver related settings.                                          */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Delays insertions.\r
+ * @details If enabled this options inserts delays into the MMC waiting\r
+ *          routines releasing some extra CPU time for the threads with\r
+ *          lower priority, this may slow down the driver a bit however.\r
+ *          This option is recommended also if the SPI driver does not\r
+ *          use a DMA channel and heavily loads the CPU.\r
+ */\r
+#if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__)\r
+#define MMC_NICE_WAITING                    TRUE\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* SDC driver related settings.                                              */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Number of initialization attempts before rejecting the card.\r
+ * @note    Attempts are performed at 10mS intervals.\r
+ */\r
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)\r
+#define SDC_INIT_RETRY                      100\r
+#endif\r
+\r
+/**\r
+ * @brief   Include support for MMC cards.\r
+ * @note    MMC support is not yet implemented so this option must be kept\r
+ *          at @p FALSE.\r
+ */\r
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)\r
+#define SDC_MMC_SUPPORT                     FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Delays insertions.\r
+ * @details If enabled this options inserts delays into the MMC waiting\r
+ *          routines releasing some extra CPU time for the threads with\r
+ *          lower priority, this may slow down the driver a bit however.\r
+ */\r
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)\r
+#define SDC_NICE_WAITING                    TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   OCR initialization constant for V20 cards.\r
+ */\r
+#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)\r
+#define SDC_INIT_OCR_V20                    0x50FF8000U\r
+#endif\r
+\r
+/**\r
+ * @brief   OCR initialization constant for non-V20 cards.\r
+ */\r
+#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)\r
+#define SDC_INIT_OCR                        0x80100000U\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* SERIAL driver related settings.                                           */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Default bit rate.\r
+ * @details Configuration parameter, this is the baud rate selected for the\r
+ *          default configuration.\r
+ */\r
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)\r
+#define SERIAL_DEFAULT_BITRATE              38400\r
+#endif\r
+\r
+/**\r
+ * @brief   Serial buffers size.\r
+ * @details Configuration parameter, you can change the depth of the queue\r
+ *          buffers depending on the requirements of your application.\r
+ * @note    The default is 16 bytes for both the transmission and receive\r
+ *          buffers.\r
+ */\r
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)\r
+#define SERIAL_BUFFERS_SIZE                 16\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* SERIAL_USB driver related setting.                                        */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Serial over USB buffers size.\r
+ * @details Configuration parameter, the buffer size must be a multiple of\r
+ *          the USB data endpoint maximum packet size.\r
+ * @note    The default is 256 bytes for both the transmission and receive\r
+ *          buffers.\r
+ */\r
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)\r
+#define SERIAL_USB_BUFFERS_SIZE             256\r
+#endif\r
+\r
+/**\r
+ * @brief   Serial over USB number of buffers.\r
+ * @note    The default is 2 buffers.\r
+ */\r
+#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)\r
+#define SERIAL_USB_BUFFERS_NUMBER           2\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* SPI driver related settings.                                              */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Enables synchronous APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)\r
+#define SPI_USE_WAIT                        TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables circular transfers APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(SPI_USE_CIRCULAR) || defined(__DOXYGEN__)\r
+#define SPI_USE_CIRCULAR                    FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\r
+#define SPI_USE_MUTUAL_EXCLUSION            TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Handling method for SPI CS line.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)\r
+#define SPI_SELECT_MODE                     SPI_SELECT_MODE_PAD\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* UART driver related settings.                                             */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Enables synchronous APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)\r
+#define UART_USE_WAIT                       FALSE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\r
+#define UART_USE_MUTUAL_EXCLUSION           FALSE\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* USB driver related settings.                                              */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Enables synchronous APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)\r
+#define USB_USE_WAIT                        FALSE\r
+#endif\r
+\r
+/*===========================================================================*/\r
+/* WSPI driver related settings.                                             */\r
+/*===========================================================================*/\r
+\r
+/**\r
+ * @brief   Enables synchronous APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)\r
+#define WSPI_USE_WAIT                       TRUE\r
+#endif\r
+\r
+/**\r
+ * @brief   Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.\r
+ * @note    Disabling this option saves both code and data space.\r
+ */\r
+#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\r
+#define WSPI_USE_MUTUAL_EXCLUSION           TRUE\r
+#endif\r
+\r
+#endif /* HALCONF_H */\r
+\r
+/** @} */\r
diff --git a/firmware/source/cfg/mcuconf.h b/firmware/source/cfg/mcuconf.h
new file mode 100644 (file)
index 0000000..0d4d5c1
--- /dev/null
@@ -0,0 +1,7 @@
+#if defined(TARGET_PLATFORM_L4)\r
+#include "mcuconf_l4.h"\r
+#elif defined(TARGET_PLATFORM_H7)\r
+#include "mcuconf_h7.h"\r
+#elif defined(TARGET_PLATFORM_G4)\r
+#include "mcuconf_g4.h"\r
+#endif\r
diff --git a/firmware/source/cfg/mcuconf_h7.h b/firmware/source/cfg/mcuconf_h7.h
new file mode 100644 (file)
index 0000000..c6a254b
--- /dev/null
@@ -0,0 +1,483 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+#ifndef MCUCONF_H\r
+#define MCUCONF_H\r
+\r
+/*\r
+ * STM32H7xx drivers configuration.\r
+ * The following settings override the default settings present in\r
+ * the various device driver implementation headers.\r
+ * Note that the settings for each driver only have effect if the whole\r
+ * driver is enabled in halconf.h.\r
+ *\r
+ * IRQ priorities:\r
+ * 15...0       Lowest...Highest.\r
+ *\r
+ * DMA priorities:\r
+ * 0...3        Lowest...Highest.\r
+ */\r
+\r
+#define STM32H7xx_MCUCONF\r
+#define STM32H723_MCUCONF\r
+#define STM32H725_MCUCONF\r
+//#define STM32H743_MCUCONF\r
+\r
+/*\r
+ * General settings.\r
+ */\r
+#define STM32_NO_INIT                       FALSE\r
+#define STM32_TARGET_CORE                   1\r
+\r
+/*\r
+ * Memory attributes settings.\r
+ */\r
+#define STM32_NOCACHE_MPU_REGION            MPU_REGION_1\r
+#define STM32_NOCACHE_SRAM1_SRAM2           FALSE\r
+#define STM32_NOCACHE_SRAM3                 FALSE\r
+#define STM32_NOCACHE_ALLSRAM               TRUE\r
+\r
+/*\r
+ * PWR system settings.\r
+ * Reading STM32 Reference Manual is required, settings in PWR_CR3 are\r
+ * very critical.\r
+ * Register constants are taken from the ST header.\r
+ */\r
+#define STM32_VOS                           STM32_VOS_SCALE1\r
+#define STM32_PWR_CR1                       (PWR_CR1_SVOS_1 | PWR_CR1_SVOS_0)\r
+#define STM32_PWR_CR2                       (PWR_CR2_BREN)\r
+#define STM32_PWR_CR3                       (PWR_CR3_LDOEN | PWR_CR3_USB33DEN)\r
+#define STM32_PWR_CPUCR                     0\r
+\r
+/*\r
+ * Clock tree static settings.\r
+ * Reading STM32 Reference Manual is required.\r
+ */\r
+#define STM32_HSI_ENABLED                   TRUE\r
+#define STM32_LSI_ENABLED                   TRUE\r
+#define STM32_CSI_ENABLED                   FALSE\r
+#define STM32_HSI48_ENABLED                 TRUE\r
+#define STM32_HSE_ENABLED                   FALSE\r
+#define STM32_LSE_ENABLED                   FALSE\r
+#define STM32_HSIDIV                        STM32_HSIDIV_DIV8 // HSI = 8MHz\r
+\r
+/*\r
+ * PLLs static settings.\r
+ * Reading STM32 Reference Manual is required.\r
+ */\r
+#define STM32_PLLSRC                        STM32_PLLSRC_HSI_CK\r
+#define STM32_PLLCFGR_MASK                  ~0\r
+#define STM32_PLL1_ENABLED                  TRUE\r
+#define STM32_PLL1_P_ENABLED                TRUE\r
+#define STM32_PLL1_Q_ENABLED                FALSE\r
+#define STM32_PLL1_R_ENABLED                FALSE\r
+#define STM32_PLL1_DIVM_VALUE               4   // 8 / 4 = 2MHz\r
+#define STM32_PLL1_DIVN_VALUE               240 // = 2 * 240\r
+#define STM32_PLL1_FRACN_VALUE              0\r
+#define STM32_PLL1_DIVP_VALUE               1   // = 480MHz\r
+#define STM32_PLL1_DIVQ_VALUE               16\r
+#define STM32_PLL1_DIVR_VALUE               8\r
+#define STM32_PLL2_ENABLED                  TRUE  // PLL2 adjusted by adc.cpp\r
+#define STM32_PLL2_P_ENABLED                TRUE\r
+#define STM32_PLL2_Q_ENABLED                FALSE\r
+#define STM32_PLL2_R_ENABLED                FALSE\r
+#define STM32_PLL2_DIVM_VALUE               4\r
+#define STM32_PLL2_DIVN_VALUE               80\r
+#define STM32_PLL2_FRACN_VALUE              0\r
+#define STM32_PLL2_DIVP_VALUE               20\r
+#define STM32_PLL2_DIVQ_VALUE               8\r
+#define STM32_PLL2_DIVR_VALUE               8\r
+#define STM32_PLL3_ENABLED                  FALSE\r
+#define STM32_PLL3_P_ENABLED                FALSE\r
+#define STM32_PLL3_Q_ENABLED                FALSE\r
+#define STM32_PLL3_R_ENABLED                FALSE\r
+#define STM32_PLL3_DIVM_VALUE               4\r
+#define STM32_PLL3_DIVN_VALUE               400\r
+#define STM32_PLL3_FRACN_VALUE              0\r
+#define STM32_PLL3_DIVP_VALUE               8\r
+#define STM32_PLL3_DIVQ_VALUE               8\r
+#define STM32_PLL3_DIVR_VALUE               8\r
+\r
+/*\r
+ * Core clocks dynamic settings (can be changed at runtime).\r
+ * Reading STM32 Reference Manual is required.\r
+ */\r
+#define STM32_SW                            STM32_SW_PLL1_P_CK\r
+#define STM32_RTCSEL                        STM32_RTCSEL_LSI_CK\r
+#define STM32_D1CPRE                        STM32_D1CPRE_DIV1\r
+#define STM32_D1HPRE                        STM32_D1HPRE_DIV2  // /2 = 240MHz\r
+#define STM32_D1PPRE3                       STM32_D1PPRE3_DIV2\r
+#define STM32_D2PPRE1                       STM32_D2PPRE1_DIV2\r
+#define STM32_D2PPRE2                       STM32_D2PPRE2_DIV2\r
+#define STM32_D3PPRE4                       STM32_D3PPRE4_DIV2\r
+\r
+/*\r
+ * Peripherals clocks static settings.\r
+ * Reading STM32 Reference Manual is required.\r
+ */\r
+#define STM32_MCO1SEL                       STM32_MCO1SEL_HSI_CK\r
+#define STM32_MCO1PRE_VALUE                 4\r
+#define STM32_MCO2SEL                       STM32_MCO2SEL_SYS_CK\r
+#define STM32_MCO2PRE_VALUE                 4\r
+#define STM32_TIMPRE_ENABLE                 TRUE\r
+#define STM32_HRTIMSEL                      0\r
+#define STM32_STOPKERWUCK                   0\r
+#define STM32_STOPWUCK                      0\r
+#define STM32_RTCPRE_VALUE                  8\r
+#define STM32_CKPERSEL                      STM32_CKPERSEL_HSI_CK\r
+#define STM32_SDMMCSEL                      STM32_SDMMCSEL_PLL1_Q_CK\r
+//#define STM32_OCTOSPISEL                    STM32_OCTOSPISEL_HCLK\r
+//#define STM32_FMCSEL                        STM32_OCTOSPISEL_HCLK\r
+#define STM32_SWPSEL                        STM32_SWPSEL_PCLK1\r
+#define STM32_FDCANSEL                      STM32_FDCANSEL_HSE_CK\r
+#define STM32_DFSDM1SEL                     STM32_DFSDM1SEL_PCLK2\r
+#define STM32_SPDIFSEL                      STM32_SPDIFSEL_PLL1_Q_CK\r
+#define STM32_SPI45SEL                      STM32_SPI45SEL_PCLK2\r
+#define STM32_SPI123SEL                     STM32_SPI123SEL_PLL1_Q_CK\r
+//#define STM32_SAI23SEL                      STM32_SAI23SEL_PLL1_Q_CK\r
+#define STM32_SAI1SEL                       STM32_SAI1SEL_PLL1_Q_CK\r
+#define STM32_LPTIM1SEL                     STM32_LPTIM1SEL_PCLK1\r
+#define STM32_CECSEL                        STM32_CECSEL_LSE_CK\r
+#define STM32_USBSEL                        STM32_USBSEL_HSI48_CK\r
+#define STM32_I2C1235SEL                    STM32_I2C1235SEL_PCLK1\r
+#define STM32_RNGSEL                        STM32_RNGSEL_HSI48_CK\r
+#define STM32_USART16910SEL                 STM32_USART16910SEL_PCLK2\r
+#define STM32_USART234578SEL                STM32_USART234578SEL_PCLK1\r
+#define STM32_SPI6SEL                       STM32_SPI6SEL_PCLK4\r
+#define STM32_SAI4BSEL                      STM32_SAI4BSEL_PLL1_Q_CK\r
+#define STM32_SAI4ASEL                      STM32_SAI4ASEL_PLL1_Q_CK\r
+#define STM32_ADCSEL                        STM32_ADCSEL_PLL2_P_CK\r
+#define STM32_LPTIM345SEL                   STM32_LPTIM345SEL_PCLK4\r
+#define STM32_LPTIM2SEL                     STM32_LPTIM2SEL_PCLK4\r
+#define STM32_I2C4SEL                       STM32_I2C4SEL_PCLK4\r
+#define STM32_LPUART1SEL                    STM32_LPUART1SEL_PCLK4\r
+\r
+/*\r
+ * IRQ system settings.\r
+ */\r
+#define STM32_IRQ_EXTI0_PRIORITY            6\r
+#define STM32_IRQ_EXTI1_PRIORITY            6\r
+#define STM32_IRQ_EXTI2_PRIORITY            6\r
+#define STM32_IRQ_EXTI3_PRIORITY            6\r
+#define STM32_IRQ_EXTI4_PRIORITY            6\r
+#define STM32_IRQ_EXTI5_9_PRIORITY          6\r
+#define STM32_IRQ_EXTI10_15_PRIORITY        6\r
+#define STM32_IRQ_EXTI16_PRIORITY           6\r
+#define STM32_IRQ_EXTI17_PRIORITY           6\r
+#define STM32_IRQ_EXTI18_PRIORITY           6\r
+#define STM32_IRQ_EXTI19_PRIORITY           6\r
+#define STM32_IRQ_EXTI20_21_PRIORITY        6\r
+\r
+#define STM32_IRQ_FDCAN1_PRIORITY           10\r
+#define STM32_IRQ_FDCAN2_PRIORITY           10\r
+\r
+#define STM32_IRQ_MDMA_PRIORITY             9\r
+\r
+#define STM32_IRQ_QUADSPI1_PRIORITY         10\r
+\r
+#define STM32_IRQ_SDMMC1_PRIORITY           9\r
+#define STM32_IRQ_SDMMC2_PRIORITY           9\r
+\r
+#define STM32_IRQ_TIM1_UP_PRIORITY          7\r
+#define STM32_IRQ_TIM1_CC_PRIORITY          7\r
+#define STM32_IRQ_TIM2_PRIORITY             7\r
+#define STM32_IRQ_TIM3_PRIORITY             7\r
+#define STM32_IRQ_TIM4_PRIORITY             7\r
+#define STM32_IRQ_TIM5_PRIORITY             7\r
+#define STM32_IRQ_TIM6_PRIORITY             7\r
+#define STM32_IRQ_TIM7_PRIORITY             7\r
+#define STM32_IRQ_TIM8_BRK_TIM12_PRIORITY   7\r
+#define STM32_IRQ_TIM8_UP_TIM13_PRIORITY    7\r
+#define STM32_IRQ_TIM8_TRGCO_TIM14_PRIORITY 7\r
+#define STM32_IRQ_TIM8_CC_PRIORITY          7\r
+#define STM32_IRQ_TIM15_PRIORITY            7\r
+#define STM32_IRQ_TIM16_PRIORITY            7\r
+#define STM32_IRQ_TIM17_PRIORITY            7\r
+\r
+#define STM32_IRQ_USART1_PRIORITY           12\r
+#define STM32_IRQ_USART2_PRIORITY           12\r
+#define STM32_IRQ_USART3_PRIORITY           12\r
+#define STM32_IRQ_UART4_PRIORITY            12\r
+#define STM32_IRQ_UART5_PRIORITY            12\r
+#define STM32_IRQ_USART6_PRIORITY           12\r
+#define STM32_IRQ_UART7_PRIORITY            12\r
+#define STM32_IRQ_UART8_PRIORITY            12\r
+#define STM32_IRQ_LPUART1_PRIORITY          12\r
+\r
+/*\r
+ * ADC driver system settings.\r
+ */\r
+#define STM32_ADC_DUAL_MODE                 FALSE\r
+#define STM32_ADC_COMPACT_SAMPLES           FALSE\r
+#define STM32_ADC_USE_ADC12                 FALSE\r
+#define STM32_ADC_USE_ADC3                  TRUE\r
+#define STM32_ADC_ADC12_DMA_STREAM          STM32_DMA_STREAM_ID_ANY\r
+#define STM32_ADC_ADC3_BDMA_STREAM          STM32_BDMA_STREAM_ID_ANY\r
+#define STM32_ADC_ADC12_DMA_PRIORITY        2\r
+#define STM32_ADC_ADC3_DMA_PRIORITY         2\r
+#define STM32_ADC_ADC12_IRQ_PRIORITY        5\r
+#define STM32_ADC_ADC3_IRQ_PRIORITY         5\r
+#define STM32_ADC_ADC12_CLOCK_MODE          ADC_CCR_CKMODE_AHB_DIV4\r
+#define STM32_ADC_ADC3_CLOCK_MODE           ADC_CCR_CKMODE_ADCCK\r
+#define STM32_ADC_ADC3_PRESC                (5 << ADC_CCR_PRESC_Pos) // /10\r
+\r
+/*\r
+ * CAN driver system settings.\r
+ */\r
+#define STM32_CAN_USE_FDCAN1                FALSE\r
+#define STM32_CAN_USE_FDCAN2                FALSE\r
+\r
+/*\r
+ * DAC driver system settings.\r
+ */\r
+#define STM32_DAC_DUAL_MODE                 FALSE\r
+#define STM32_DAC_USE_DAC1_CH1              TRUE\r
+#define STM32_DAC_USE_DAC1_CH2              TRUE\r
+#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     10\r
+#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     10\r
+#define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\r
+#define STM32_DAC_DAC1_CH2_DMA_PRIORITY     2\r
+#define STM32_DAC_DAC1_CH1_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\r
+#define STM32_DAC_DAC1_CH2_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\r
+\r
+/*\r
+ * GPT driver system settings.\r
+ */\r
+#define STM32_GPT_USE_TIM1                  FALSE\r
+#define STM32_GPT_USE_TIM2                  FALSE\r
+#define STM32_GPT_USE_TIM3                  FALSE\r
+#define STM32_GPT_USE_TIM4                  FALSE\r
+#define STM32_GPT_USE_TIM5                  FALSE\r
+#define STM32_GPT_USE_TIM6                  TRUE\r
+#define STM32_GPT_USE_TIM7                  FALSE\r
+#define STM32_GPT_USE_TIM8                  FALSE\r
+#define STM32_GPT_USE_TIM12                 FALSE\r
+#define STM32_GPT_USE_TIM13                 FALSE\r
+#define STM32_GPT_USE_TIM14                 FALSE\r
+#define STM32_GPT_USE_TIM15                 FALSE\r
+#define STM32_GPT_USE_TIM16                 FALSE\r
+#define STM32_GPT_USE_TIM17                 FALSE\r
+\r
+/*\r
+ * I2C driver system settings.\r
+ */\r
+#define STM32_I2C_USE_I2C1                  FALSE\r
+#define STM32_I2C_USE_I2C2                  FALSE\r
+#define STM32_I2C_USE_I2C3                  FALSE\r
+#define STM32_I2C_USE_I2C4                  FALSE\r
+#define STM32_I2C_BUSY_TIMEOUT              50\r
+#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_I2C_I2C4_RX_BDMA_STREAM       STM32_BDMA_STREAM_ID_ANY\r
+#define STM32_I2C_I2C4_TX_BDMA_STREAM       STM32_BDMA_STREAM_ID_ANY\r
+#define STM32_I2C_I2C1_IRQ_PRIORITY         5\r
+#define STM32_I2C_I2C2_IRQ_PRIORITY         5\r
+#define STM32_I2C_I2C3_IRQ_PRIORITY         5\r
+#define STM32_I2C_I2C4_IRQ_PRIORITY         5\r
+#define STM32_I2C_I2C1_DMA_PRIORITY         3\r
+#define STM32_I2C_I2C2_DMA_PRIORITY         3\r
+#define STM32_I2C_I2C3_DMA_PRIORITY         3\r
+#define STM32_I2C_I2C4_DMA_PRIORITY         3\r
+#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt("DMA failure")\r
+\r
+/*\r
+ * ICU driver system settings.\r
+ */\r
+#define STM32_ICU_USE_TIM1                  FALSE\r
+#define STM32_ICU_USE_TIM2                  FALSE\r
+#define STM32_ICU_USE_TIM3                  FALSE\r
+#define STM32_ICU_USE_TIM4                  FALSE\r
+#define STM32_ICU_USE_TIM5                  FALSE\r
+#define STM32_ICU_USE_TIM8                  FALSE\r
+#define STM32_ICU_USE_TIM12                 FALSE\r
+#define STM32_ICU_USE_TIM13                 FALSE\r
+#define STM32_ICU_USE_TIM14                 FALSE\r
+#define STM32_ICU_USE_TIM15                 FALSE\r
+#define STM32_ICU_USE_TIM16                 FALSE\r
+#define STM32_ICU_USE_TIM17                 FALSE\r
+\r
+/*\r
+ * MAC driver system settings.\r
+ */\r
+#define STM32_MAC_TRANSMIT_BUFFERS          2\r
+#define STM32_MAC_RECEIVE_BUFFERS           4\r
+#define STM32_MAC_BUFFERS_SIZE              1522\r
+#define STM32_MAC_PHY_TIMEOUT               100\r
+#define STM32_MAC_ETH1_CHANGE_PHY_STATE     TRUE\r
+#define STM32_MAC_ETH1_IRQ_PRIORITY         13\r
+#define STM32_MAC_IP_CHECKSUM_OFFLOAD       0\r
+\r
+/*\r
+ * PWM driver system settings.\r
+ */\r
+#define STM32_PWM_USE_ADVANCED              FALSE\r
+#define STM32_PWM_USE_TIM1                  FALSE\r
+#define STM32_PWM_USE_TIM2                  FALSE\r
+#define STM32_PWM_USE_TIM3                  FALSE\r
+#define STM32_PWM_USE_TIM4                  FALSE\r
+#define STM32_PWM_USE_TIM5                  FALSE\r
+#define STM32_PWM_USE_TIM8                  FALSE\r
+#define STM32_PWM_USE_TIM12                 FALSE\r
+#define STM32_PWM_USE_TIM13                 FALSE\r
+#define STM32_PWM_USE_TIM14                 FALSE\r
+#define STM32_PWM_USE_TIM15                 FALSE\r
+#define STM32_PWM_USE_TIM16                 FALSE\r
+#define STM32_PWM_USE_TIM17                 FALSE\r
+\r
+/*\r
+ * RTC driver system settings.\r
+ */\r
+#define STM32_RTC_PRESA_VALUE               32\r
+#define STM32_RTC_PRESS_VALUE               1024\r
+#define STM32_RTC_CR_INIT                   0\r
+#define STM32_RTC_TAMPCR_INIT               0\r
+\r
+/*\r
+ * SDC driver system settings.\r
+ */\r
+#define STM32_SDC_USE_SDMMC1                FALSE\r
+#define STM32_SDC_USE_SDMMC2                FALSE\r
+#define STM32_SDC_SDMMC_UNALIGNED_SUPPORT   TRUE\r
+#define STM32_SDC_SDMMC_WRITE_TIMEOUT       1000000\r
+#define STM32_SDC_SDMMC_READ_TIMEOUT        1000000\r
+#define STM32_SDC_SDMMC_CLOCK_DELAY         10\r
+#define STM32_SDC_SDMMC_PWRSAV              TRUE\r
+\r
+/*\r
+ * SERIAL driver system settings.\r
+ */\r
+#define STM32_SERIAL_USE_USART1             FALSE\r
+#define STM32_SERIAL_USE_USART2             FALSE\r
+#define STM32_SERIAL_USE_USART3             FALSE\r
+#define STM32_SERIAL_USE_UART4              FALSE\r
+#define STM32_SERIAL_USE_UART5              FALSE\r
+#define STM32_SERIAL_USE_USART6             FALSE\r
+#define STM32_SERIAL_USE_UART7              FALSE\r
+#define STM32_SERIAL_USE_UART8              FALSE\r
+\r
+/*\r
+ * SPI driver system settings.\r
+ */\r
+#define STM32_SPI_USE_SPI1                  FALSE\r
+#define STM32_SPI_USE_SPI2                  FALSE\r
+#define STM32_SPI_USE_SPI3                  FALSE\r
+#define STM32_SPI_USE_SPI4                  FALSE\r
+#define STM32_SPI_USE_SPI5                  FALSE\r
+#define STM32_SPI_USE_SPI6                  FALSE\r
+#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_SPI_SPI4_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_SPI_SPI4_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_SPI_SPI5_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_SPI_SPI5_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\r
+#define STM32_SPI_SPI6_RX_BDMA_STREAM       STM32_BDMA_STREAM_ID_ANY\r
+#define STM32_SPI_SPI6_TX_BDMA_STREAM       STM32_BDMA_STREAM_ID_ANY\r
+#define STM32_SPI_SPI1_DMA_PRIORITY         1\r
+#define STM32_SPI_SPI2_DMA_PRIORITY         1\r
+#define STM32_SPI_SPI3_DMA_PRIORITY         1\r
+#define STM32_SPI_SPI4_DMA_PRIORITY         1\r
+#define STM32_SPI_SPI5_DMA_PRIORITY         1\r
+#define STM32_SPI_SPI6_DMA_PRIORITY         1\r
+#define STM32_SPI_SPI1_IRQ_PRIORITY         10\r
+#define STM32_SPI_SPI2_IRQ_PRIORITY         10\r
+#define STM32_SPI_SPI3_IRQ_PRIORITY         10\r
+#define STM32_SPI_SPI4_IRQ_PRIORITY         10\r
+#define STM32_SPI_SPI5_IRQ_PRIORITY         10\r
+#define STM32_SPI_SPI6_IRQ_PRIORITY         10\r
+#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt("DMA failure")\r
+\r
+/*\r
+ * ST driver system settings.\r
+ */\r
+#define STM32_ST_IRQ_PRIORITY               8\r
+#define STM32_ST_USE_TIMER                  2\r
+\r
+/*\r
+ * TRNG driver system settings.\r
+ */\r
+#define STM32_TRNG_USE_RNG1                 FALSE\r
+\r
+/*\r
+ * UART driver system settings.\r
+ */\r
+#define STM32_UART_USE_USART1               FALSE\r
+#define STM32_UART_USE_USART2               FALSE\r
+#define STM32_UART_USE_USART3               FALSE\r
+#define STM32_UART_USE_UART4                FALSE\r
+#define STM32_UART_USE_UART5                FALSE\r
+#define STM32_UART_USE_USART6               FALSE\r
+#define STM32_UART_USE_UART7                FALSE\r
+#define STM32_UART_USE_UART8                FALSE\r
+#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_USART3_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_USART3_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_UART4_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_UART4_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_UART5_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_UART5_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_USART6_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_USART6_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_UART7_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_UART7_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_UART8_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_UART8_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\r
+#define STM32_UART_USART1_DMA_PRIORITY      0\r
+#define STM32_UART_USART2_DMA_PRIORITY      0\r
+#define STM32_UART_USART3_DMA_PRIORITY      0\r
+#define STM32_UART_UART4_DMA_PRIORITY       0\r
+#define STM32_UART_UART5_DMA_PRIORITY       0\r
+#define STM32_UART_USART6_DMA_PRIORITY      0\r
+#define STM32_UART_UART7_DMA_PRIORITY       0\r
+#define STM32_UART_UART8_DMA_PRIORITY       0\r
+#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt("DMA failure")\r
+\r
+/*\r
+ * USB driver system settings.\r
+ */\r
+#define STM32_USB_USE_OTG1                  FALSE\r
+#define STM32_USB_USE_OTG2                  TRUE\r
+#define STM32_USB_OTG1_IRQ_PRIORITY         14\r
+#define STM32_USB_OTG2_IRQ_PRIORITY         14\r
+#define STM32_USB_OTG1_RX_FIFO_SIZE         512\r
+#define STM32_USB_OTG2_RX_FIFO_SIZE         1024\r
+#define STM32_USB_HOST_WAKEUP_DURATION      2\r
+\r
+/*\r
+ * WDG driver system settings.\r
+ */\r
+#define STM32_WDG_USE_IWDG                  FALSE\r
+\r
+/*\r
+ * WSPI driver system settings.\r
+ */\r
+#define STM32_WSPI_USE_QUADSPI1             FALSE\r
+#define STM32_WSPI_QUADSPI1_PRESCALER_VALUE 1\r
+#define STM32_WSPI_QUADSPI1_MDMA_CHANNEL    STM32_MDMA_CHANNEL_ID_ANY\r
+#define STM32_WSPI_QUADSPI1_MDMA_PRIORITY   1\r
+#define STM32_WSPI_MDMA_ERROR_HOOK(qspip)   osalSysHalt("MDMA failure")\r
+\r
+#endif /* MCUCONF_H */\r
diff --git a/firmware/source/cfg/mcuconf_l4.h b/firmware/source/cfg/mcuconf_l4.h
new file mode 100644 (file)
index 0000000..bf19f7a
--- /dev/null
@@ -0,0 +1,360 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+/*\r
+ * STM32L4xx drivers configuration.\r
+ * The following settings override the default settings present in\r
+ * the various device driver implementation headers.\r
+ * Note that the settings for each driver only have effect if the whole\r
+ * driver is enabled in halconf.h.\r
+ *\r
+ * IRQ priorities:\r
+ * 15...0       Lowest...Highest.\r
+ *\r
+ * DMA priorities:\r
+ * 0...3        Lowest...Highest.\r
+ */\r
+\r
+#ifndef MCUCONF_H\r
+#define MCUCONF_H\r
+\r
+#define STM32L4xx_MCUCONF\r
+#define STM32L476_MCUCONF\r
+//#define STM32L432_MCUCONF\r
+\r
+/*\r
+ * HAL driver system settings.\r
+ */\r
+#define STM32_NO_INIT                       FALSE\r
+#define STM32_VOS                           STM32_VOS_RANGE1\r
+#define STM32_PVD_ENABLE                    FALSE\r
+#define STM32_PLS                           STM32_PLS_LEV0\r
+#define STM32_HSI16_ENABLED                 FALSE\r
+#define STM32_LSI_ENABLED                   TRUE\r
+#define STM32_HSE_ENABLED                   FALSE\r
+#define STM32_LSE_ENABLED                   FALSE\r
+#define STM32_MSIPLL_ENABLED                FALSE\r
+#define STM32_MSIRANGE                      STM32_MSIRANGE_4M\r
+#define STM32_MSISRANGE                     STM32_MSISRANGE_4M\r
+#define STM32_SW                            STM32_SW_PLL\r
+#define STM32_PLLSRC                        STM32_PLLSRC_MSI\r
+#define STM32_PLLM_VALUE                    1\r
+#define STM32_PLLN_VALUE                    72\r
+#define STM32_PLLP_VALUE                    7\r
+#define STM32_PLLQ_VALUE                    6\r
+#define STM32_PLLR_VALUE                    4\r
+#define STM32_HPRE                          STM32_HPRE_DIV1\r
+#define STM32_PPRE1                         STM32_PPRE1_DIV1\r
+#define STM32_PPRE2                         STM32_PPRE2_DIV1\r
+#define STM32_STOPWUCK                      STM32_STOPWUCK_MSI\r
+#define STM32_MCOSEL                        STM32_MCOSEL_NOCLOCK\r
+#define STM32_MCOPRE                        STM32_MCOPRE_DIV1\r
+#define STM32_LSCOSEL                       STM32_LSCOSEL_NOCLOCK\r
+#define STM32_PLLSAI1N_VALUE                24\r
+#define STM32_PLLSAI1P_VALUE                7\r
+#define STM32_PLLSAI1Q_VALUE                2\r
+#define STM32_PLLSAI1R_VALUE                4\r
+#define STM32_PLLSAI2N_VALUE                24\r
+#define STM32_PLLSAI2P_VALUE                7\r
+#define STM32_PLLSAI2R_VALUE                8\r
+\r
+/*\r
+ * Peripherals clock sources.\r
+ */\r
+#define STM32_USART1SEL                     STM32_USART1SEL_SYSCLK\r
+#define STM32_USART2SEL                     STM32_USART2SEL_SYSCLK\r
+#define STM32_USART3SEL                     STM32_USART3SEL_SYSCLK\r
+#define STM32_UART4SEL                      STM32_UART4SEL_SYSCLK\r
+#define STM32_UART5SEL                      STM32_UART5SEL_SYSCLK\r
+#define STM32_LPUART1SEL                    STM32_LPUART1SEL_SYSCLK\r
+#define STM32_I2C1SEL                       STM32_I2C1SEL_SYSCLK\r
+#define STM32_I2C2SEL                       STM32_I2C2SEL_SYSCLK\r
+#define STM32_I2C3SEL                       STM32_I2C3SEL_SYSCLK\r
+#define STM32_LPTIM1SEL                     STM32_LPTIM1SEL_PCLK1\r
+#define STM32_LPTIM2SEL                     STM32_LPTIM2SEL_PCLK1\r
+#define STM32_SAI1SEL                       STM32_SAI1SEL_OFF\r
+#define STM32_SAI2SEL                       STM32_SAI2SEL_OFF\r
+#define STM32_CLK48SEL                      STM32_CLK48SEL_PLLSAI1\r
+#define STM32_ADCSEL                        STM32_ADCSEL_PLLSAI2\r
+#define STM32_SWPMI1SEL                     STM32_SWPMI1SEL_PCLK1\r
+#define STM32_DFSDMSEL                      STM32_DFSDMSEL_PCLK2\r
+#define STM32_RTCSEL                        STM32_RTCSEL_LSI\r
+\r
+/*\r
+ * IRQ system settings.\r
+ */\r
+#define STM32_IRQ_EXTI0_PRIORITY            6\r
+#define STM32_IRQ_EXTI1_PRIORITY            6\r
+#define STM32_IRQ_EXTI2_PRIORITY            6\r
+#define STM32_IRQ_EXTI3_PRIORITY            6\r
+#define STM32_IRQ_EXTI4_PRIORITY            6\r
+#define STM32_IRQ_EXTI5_9_PRIORITY          6\r
+#define STM32_IRQ_EXTI10_15_PRIORITY        6\r
+#define STM32_IRQ_EXTI1635_38_PRIORITY      6\r
+#define STM32_IRQ_EXTI18_PRIORITY           6\r
+#define STM32_IRQ_EXTI19_PRIORITY           6\r
+#define STM32_IRQ_EXTI20_PRIORITY           6\r
+#define STM32_IRQ_EXTI21_22_PRIORITY        15\r
+\r
+#define STM32_IRQ_TIM1_BRK_TIM15_PRIORITY   7\r
+#define STM32_IRQ_TIM1_UP_TIM16_PRIORITY    7\r
+#define STM32_IRQ_TIM1_TRGCO_TIM17_PRIORITY 7\r
+#define STM32_IRQ_TIM1_CC_PRIORITY          7\r
+#define STM32_IRQ_TIM2_PRIORITY             7\r
+#define STM32_IRQ_TIM3_PRIORITY             7\r
+#define STM32_IRQ_TIM4_PRIORITY             7\r
+#define STM32_IRQ_TIM5_PRIORITY             7\r
+#define STM32_IRQ_TIM6_PRIORITY             7\r
+#define STM32_IRQ_TIM7_PRIORITY             7\r
+#define STM32_IRQ_TIM8_UP_PRIORITY          7\r
+#define STM32_IRQ_TIM8_CC_PRIORITY          7\r
+\r
+#define STM32_IRQ_USART1_PRIORITY           12\r
+#define STM32_IRQ_USART2_PRIORITY           12\r
+#define STM32_IRQ_USART3_PRIORITY           12\r
+#define STM32_IRQ_UART4_PRIORITY            12\r
+#define STM32_IRQ_UART5_PRIORITY            12\r
+#define STM32_IRQ_LPUART1_PRIORITY          12\r
+\r
+/*\r
+ * ADC driver system settings.\r
+ */\r
+#define STM32_ADC_DUAL_MODE                 FALSE\r
+#define STM32_ADC_COMPACT_SAMPLES           FALSE\r
+#define STM32_ADC_USE_ADC1                  TRUE\r
+#define STM32_ADC_USE_ADC2                  FALSE\r
+#define STM32_ADC_USE_ADC3                  TRUE\r
+#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(1, 1)\r
+#define STM32_ADC_ADC2_DMA_STREAM           STM32_DMA_STREAM_ID(1, 2)\r
+#define STM32_ADC_ADC3_DMA_STREAM           STM32_DMA_STREAM_ID(1, 3)\r
+#define STM32_ADC_ADC1_DMA_PRIORITY         2\r
+#define STM32_ADC_ADC2_DMA_PRIORITY         2\r
+#define STM32_ADC_ADC3_DMA_PRIORITY         2\r
+#define STM32_ADC_ADC12_IRQ_PRIORITY        5\r
+#define STM32_ADC_ADC3_IRQ_PRIORITY         5\r
+#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     5\r
+#define STM32_ADC_ADC2_DMA_IRQ_PRIORITY     5\r
+#define STM32_ADC_ADC3_DMA_IRQ_PRIORITY     5\r
+#define STM32_ADC_ADC123_CLOCK_MODE         ADC_CCR_CKMODE_ADCCK\r
+#define STM32_ADC_ADC123_PRESC              ADC_CCR_PRESC_DIV10\r
+\r
+//#define ADC123_PRESC_VALUE 1\r
+\r
+/*\r
+ * CAN driver system settings.\r
+ */\r
+#define STM32_CAN_USE_CAN1                  FALSE\r
+#define STM32_CAN_CAN1_IRQ_PRIORITY         11\r
+\r
+/*\r
+ * DAC driver system settings.\r
+ */\r
+#define STM32_DAC_DUAL_MODE                 FALSE\r
+#define STM32_DAC_USE_DAC1_CH1              TRUE\r
+#define STM32_DAC_USE_DAC1_CH2              TRUE\r
+#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     10\r
+#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     10\r
+#define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\r
+#define STM32_DAC_DAC1_CH2_DMA_PRIORITY     2\r
+#define STM32_DAC_DAC1_CH1_DMA_STREAM       STM32_DMA_STREAM_ID(2, 4)\r
+#define STM32_DAC_DAC1_CH2_DMA_STREAM       STM32_DMA_STREAM_ID(1, 4)\r
+\r
+/*\r
+ * GPT driver system settings.\r
+ */\r
+#define STM32_GPT_USE_TIM1                  FALSE\r
+#define STM32_GPT_USE_TIM2                  FALSE\r
+#define STM32_GPT_USE_TIM3                  FALSE\r
+#define STM32_GPT_USE_TIM4                  FALSE\r
+#define STM32_GPT_USE_TIM5                  FALSE\r
+#define STM32_GPT_USE_TIM6                  TRUE\r
+#define STM32_GPT_USE_TIM7                  TRUE\r
+#define STM32_GPT_USE_TIM8                  FALSE\r
+#define STM32_GPT_USE_TIM15                 FALSE\r
+#define STM32_GPT_USE_TIM16                 FALSE\r
+#define STM32_GPT_USE_TIM17                 FALSE\r
+\r
+/*\r
+ * I2C driver system settings.\r
+ */\r
+#define STM32_I2C_USE_I2C1                  FALSE\r
+#define STM32_I2C_USE_I2C2                  FALSE\r
+#define STM32_I2C_USE_I2C3                  FALSE\r
+#define STM32_I2C_BUSY_TIMEOUT              50\r
+#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\r
+#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 6)\r
+#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 5)\r
+#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\r
+#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\r
+#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\r
+#define STM32_I2C_I2C1_IRQ_PRIORITY         5\r
+#define STM32_I2C_I2C2_IRQ_PRIORITY         5\r
+#define STM32_I2C_I2C3_IRQ_PRIORITY         5\r
+#define STM32_I2C_I2C1_DMA_PRIORITY         3\r
+#define STM32_I2C_I2C2_DMA_PRIORITY         3\r
+#define STM32_I2C_I2C3_DMA_PRIORITY         3\r
+#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt("DMA failure")\r
+\r
+/*\r
+ * ICU driver system settings.\r
+ */\r
+#define STM32_ICU_USE_TIM1                  FALSE\r
+#define STM32_ICU_USE_TIM2                  FALSE\r
+#define STM32_ICU_USE_TIM3                  FALSE\r
+#define STM32_ICU_USE_TIM4                  FALSE\r
+#define STM32_ICU_USE_TIM5                  FALSE\r
+#define STM32_ICU_USE_TIM8                  FALSE\r
+#define STM32_ICU_USE_TIM15                 FALSE\r
+#define STM32_ICU_USE_TIM16                 FALSE\r
+#define STM32_ICU_USE_TIM17                 FALSE\r
+\r
+/*\r
+ * PWM driver system settings.\r
+ */\r
+#define STM32_PWM_USE_ADVANCED              FALSE\r
+#define STM32_PWM_USE_TIM1                  FALSE\r
+#define STM32_PWM_USE_TIM2                  FALSE\r
+#define STM32_PWM_USE_TIM3                  FALSE\r
+#define STM32_PWM_USE_TIM4                  FALSE\r
+#define STM32_PWM_USE_TIM5                  FALSE\r
+#define STM32_PWM_USE_TIM8                  FALSE\r
+#define STM32_PWM_USE_TIM15                 FALSE\r
+#define STM32_PWM_USE_TIM16                 FALSE\r
+#define STM32_PWM_USE_TIM17                 FALSE\r
+\r
+/*\r
+ * RTC driver system settings.\r
+ */\r
+#define STM32_RTC_PRESA_VALUE               32\r
+#define STM32_RTC_PRESS_VALUE               1024\r
+#define STM32_RTC_CR_INIT                   0\r
+#define STM32_RTC_TAMPCR_INIT               0\r
+\r
+/*\r
+ * SDC driver system settings.\r
+ */\r
+#define STM32_SDC_USE_SDMMC1                FALSE\r
+#define STM32_SDC_SDMMC_UNALIGNED_SUPPORT   TRUE\r
+#define STM32_SDC_SDMMC_WRITE_TIMEOUT       1000\r
+#define STM32_SDC_SDMMC_READ_TIMEOUT        1000\r
+#define STM32_SDC_SDMMC_CLOCK_DELAY         10\r
+#define STM32_SDC_SDMMC1_DMA_PRIORITY       3\r
+#define STM32_SDC_SDMMC1_IRQ_PRIORITY       9\r
+#define STM32_SDC_SDMMC1_DMA_STREAM         STM32_DMA_STREAM_ID(2, 4)\r
+\r
+/*\r
+ * SERIAL driver system settings.\r
+ */\r
+#define STM32_SERIAL_USE_USART1             FALSE\r
+#define STM32_SERIAL_USE_USART2             TRUE\r
+#define STM32_SERIAL_USE_USART3             FALSE\r
+#define STM32_SERIAL_USE_UART4              FALSE\r
+#define STM32_SERIAL_USE_UART5              FALSE\r
+#define STM32_SERIAL_USE_LPUART1            FALSE\r
+#define STM32_SERIAL_USART1_PRIORITY        12\r
+#define STM32_SERIAL_USART2_PRIORITY        12\r
+#define STM32_SERIAL_USART3_PRIORITY        12\r
+#define STM32_SERIAL_UART4_PRIORITY         12\r
+#define STM32_SERIAL_UART5_PRIORITY         12\r
+#define STM32_SERIAL_LPUART1_PRIORITY       12\r
+\r
+/*\r
+ * SPI driver system settings.\r
+ */\r
+#define STM32_SPI_USE_SPI1                  FALSE\r
+#define STM32_SPI_USE_SPI2                  FALSE\r
+#define STM32_SPI_USE_SPI3                  FALSE\r
+#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\r
+#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 4)\r
+#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\r
+#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 5)\r
+#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 1)\r
+#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 2)\r
+#define STM32_SPI_SPI1_DMA_PRIORITY         1\r
+#define STM32_SPI_SPI2_DMA_PRIORITY         1\r
+#define STM32_SPI_SPI3_DMA_PRIORITY         1\r
+#define STM32_SPI_SPI1_IRQ_PRIORITY         10\r
+#define STM32_SPI_SPI2_IRQ_PRIORITY         10\r
+#define STM32_SPI_SPI3_IRQ_PRIORITY         10\r
+#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt("DMA failure")\r
+\r
+/*\r
+ * ST driver system settings.\r
+ */\r
+#define STM32_ST_IRQ_PRIORITY               8\r
+#define STM32_ST_USE_TIMER                  2\r
+\r
+/*\r
+ * TRNG driver system settings.\r
+ */\r
+#define STM32_TRNG_USE_RNG1                 FALSE\r
+\r
+/*\r
+ * UART driver system settings.\r
+ */\r
+#define STM32_UART_USE_USART1               FALSE\r
+#define STM32_UART_USE_USART2               FALSE\r
+#define STM32_UART_USE_USART3               FALSE\r
+#define STM32_UART_USE_UART4                FALSE\r
+#define STM32_UART_USE_UART5                FALSE\r
+#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\r
+#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 6)\r
+#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 6)\r
+#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 7)\r
+#define STM32_UART_USART3_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 3)\r
+#define STM32_UART_USART3_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 2)\r
+#define STM32_UART_UART4_RX_DMA_STREAM      STM32_DMA_STREAM_ID(2, 5)\r
+#define STM32_UART_UART4_TX_DMA_STREAM      STM32_DMA_STREAM_ID(2, 3)\r
+#define STM32_UART_UART5_RX_DMA_STREAM      STM32_DMA_STREAM_ID(2, 2)\r
+#define STM32_UART_UART5_TX_DMA_STREAM      STM32_DMA_STREAM_ID(2, 1)\r
+#define STM32_UART_USART1_IRQ_PRIORITY      12\r
+#define STM32_UART_USART2_IRQ_PRIORITY      12\r
+#define STM32_UART_USART3_IRQ_PRIORITY      12\r
+#define STM32_UART_UART4_IRQ_PRIORITY       12\r
+#define STM32_UART_UART5_IRQ_PRIORITY       12\r
+#define STM32_UART_USART1_DMA_PRIORITY      0\r
+#define STM32_UART_USART2_DMA_PRIORITY      0\r
+#define STM32_UART_USART3_DMA_PRIORITY      0\r
+#define STM32_UART_UART4_DMA_PRIORITY       0\r
+#define STM32_UART_UART5_DMA_PRIORITY       0\r
+#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt("DMA failure")\r
+\r
+/*\r
+ * USB driver system settings.\r
+ */\r
+#ifdef STM32L476_MCUCONF\r
+#define STM32_USB_USE_OTG1                  TRUE\r
+#define STM32_USB_OTG1_IRQ_PRIORITY         14\r
+#define STM32_USB_OTG1_RX_FIFO_SIZE         512\r
+#else\r
+#define STM32_USB_USE_USB1                  TRUE\r
+#define STM32_USB_LOW_POWER_ON_SUSPEND      FALSE\r
+#define STM32_USB_USB1_HP_IRQ_PRIORITY      13\r
+#define STM32_USB_USB1_LP_IRQ_PRIORITY      14\r
+#endif // STM32L476_MCUCONF\r
+\r
+/*\r
+ * WDG driver system settings.\r
+ */\r
+#define STM32_WDG_USE_IWDG                  FALSE\r
+\r
+/*\r
+ * WSPI driver system settings.\r
+ */\r
+#define STM32_WSPI_USE_QUADSPI1             FALSE\r
+#define STM32_WSPI_QUADSPI1_DMA_STREAM      STM32_DMA_STREAM_ID(2, 7)\r
+\r
+#endif /* MCUCONF_H */\r
diff --git a/firmware/source/communication.cpp b/firmware/source/communication.cpp
new file mode 100644 (file)
index 0000000..e85828b
--- /dev/null
@@ -0,0 +1,293 @@
+#include "communication.hpp"
+
+#include "ch.h"
+#include "hal.h"
+
+#include "periph/adc.hpp"
+#include "periph/dac.hpp"
+#include "periph/usbserial.hpp"
+#include "elfload.hpp"
+#include "error.hpp"
+#include "conversion.hpp"
+#include "runstatus.hpp"
+#include "samples.hpp"
+
+#include <algorithm>
+#include <tuple>
+
+__attribute__((section(".stacks")))
+std::array<char, 4096> CommunicationManager::m_thread_stack = {};
+
+void CommunicationManager::begin()
+{
+    chThdCreateStatic(m_thread_stack.data(),
+                      m_thread_stack.size(),
+                      NORMALPRIO,
+                      threadComm,
+                      nullptr);
+}
+
+static void writeADCBuffer(unsigned char *);
+static void setBufferSize(unsigned char *);
+static void updateGenerator(unsigned char *);
+static void loadAlgorithm(unsigned char *);
+static void readStatus(unsigned char *);
+static void measureConversion(unsigned char *);
+static void startConversion(unsigned char *);
+static void stopConversion(unsigned char *);
+static void startGenerator(unsigned char *);
+static void readADCBuffer(unsigned char *);
+static void readDACBuffer(unsigned char *);
+static void unloadAlgorithm(unsigned char *);
+static void readIdentifier(unsigned char *);
+static void readExecTime(unsigned char *);
+static void sampleRate(unsigned char *);
+static void readConversionResults(unsigned char *);
+static void readConversionInput(unsigned char *);
+static void readMessage(unsigned char *);
+static void stopGenerator(unsigned char *);
+
+static const std::array<std::pair<char, void (*)(unsigned char *)>, 19> commandTable {{
+    {'A', writeADCBuffer},
+    {'B', setBufferSize},
+    {'D', updateGenerator},
+    {'E', loadAlgorithm},
+    {'I', readStatus},
+    {'M', measureConversion},
+    {'R', startConversion},
+    {'S', stopConversion},
+    {'W', startGenerator},
+    {'a', readADCBuffer},
+    {'d', readDACBuffer},
+    {'e', unloadAlgorithm},
+    {'i', readIdentifier},
+    {'m', readExecTime},
+    {'r', sampleRate},
+    {'s', readConversionResults},
+    {'t', readConversionInput},
+    {'u', readMessage},
+    {'w', stopGenerator}
+}};
+
+void CommunicationManager::threadComm(void *)
+{
+       while (1) {
+        if (USBSerial::isActive()) {
+            // Attempt to receive a command packet
+            if (unsigned char cmd[3]; USBSerial::read(&cmd[0], 1) > 0) {
+                // Packet received, first byte represents the desired command/action
+                auto func = std::find_if(commandTable.cbegin(), commandTable.cend(),
+                                         [&cmd](const auto& f) { return f.first == cmd[0]; });
+                if (func != commandTable.cend())
+                    func->second(cmd);
+            }
+        }
+
+               chThdSleepMicroseconds(100);
+    }
+}
+
+void writeADCBuffer(unsigned char *)
+{
+    USBSerial::read(Samples::In.bytedata(), Samples::In.bytesize());
+}
+
+void setBufferSize(unsigned char *cmd)
+{
+    if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle) &&
+        EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize))
+    {
+        // count is multiplied by two since this command receives size of buffer
+        // for each algorithm application.
+        unsigned int count = (cmd[1] | (cmd[2] << 8)) * 2;
+        if (EM.assert(count <= MAX_SAMPLE_BUFFER_SIZE, Error::BadParam)) {
+            Samples::In.setSize(count);
+            Samples::Out.setSize(count);
+        }
+    }
+}
+
+void updateGenerator(unsigned char *cmd)
+{
+    if (EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize)) {
+        unsigned int count = cmd[1] | (cmd[2] << 8);
+        if (EM.assert(count <= MAX_SAMPLE_BUFFER_SIZE, Error::BadParam)) {
+            if (!DAC::isSigGenRunning()) {
+                Samples::Generator.setSize(count);
+                USBSerial::read(
+                    reinterpret_cast<uint8_t *>(Samples::Generator.data()),
+                    Samples::Generator.bytesize());
+            } else {
+                const int more = DAC::sigGenWantsMore();
+                if (more == -1) {
+                    USBSerial::write(reinterpret_cast<const uint8_t *>("\0"), 1);
+                } else {
+                    USBSerial::write(reinterpret_cast<const uint8_t *>("\1"), 1);
+
+                    // Receive streamed samples in half-buffer chunks.
+                    USBSerial::read(reinterpret_cast<uint8_t *>(
+                        more == 0 ? Samples::Generator.data() : Samples::Generator.middata()),
+                        Samples::Generator.bytesize() / 2);
+                }
+            }
+        }
+    }
+}
+
+void loadAlgorithm(unsigned char *cmd)
+{
+    if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle) &&
+        EM.assert(USBSerial::read(&cmd[1], 2) == 2, Error::BadParamSize))
+    {
+        // Only load the binary if it can fit in the memory reserved for it.
+        unsigned int size = cmd[1] | (cmd[2] << 8);
+        if (EM.assert(size < MAX_ELF_FILE_SIZE, Error::BadUserCodeSize)) {
+            USBSerial::read(ELFManager::fileBuffer(), size);
+            auto success = ELFManager::loadFromInternalBuffer();
+            EM.assert(success, Error::BadUserCodeLoad);
+        }
+    }
+}
+
+void readStatus(unsigned char *)
+{
+    unsigned char buf[2] = {
+        static_cast<unsigned char>(run_status),
+        static_cast<unsigned char>(EM.pop())
+    };
+
+    USBSerial::write(buf, sizeof(buf));
+}
+
+void measureConversion(unsigned char *)
+{
+    if (EM.assert(run_status == RunStatus::Running, Error::NotRunning))
+        ConversionManager::startMeasurement();
+}
+
+void startConversion(unsigned char *)
+{
+    if (EM.assert(run_status == RunStatus::Idle, Error::NotIdle)) {
+        run_status = RunStatus::Running;
+        ConversionManager::start();
+    }
+}
+
+void stopConversion(unsigned char *)
+{
+    if (EM.assert(run_status == RunStatus::Running, Error::NotRunning)) {
+        ConversionManager::stop();
+        run_status = RunStatus::Idle;
+    }
+}
+
+void startGenerator(unsigned char *)
+{
+    DAC::start(1, Samples::Generator.data(), Samples::Generator.size());
+}
+
+void readADCBuffer(unsigned char *)
+{
+    USBSerial::write(Samples::In.bytedata(), Samples::In.bytesize());
+}
+
+void readDACBuffer(unsigned char *)
+{
+
+    USBSerial::write(Samples::Out.bytedata(), Samples::Out.bytesize());
+}
+
+void unloadAlgorithm(unsigned char *)
+{
+    ELFManager::unload();
+}
+
+void readIdentifier(unsigned char *)
+{
+#if defined(TARGET_PLATFORM_H7)
+    USBSerial::write(reinterpret_cast<const uint8_t *>("stmdsph"), 7);
+#else
+    USBSerial::write(reinterpret_cast<const uint8_t *>("stmdspl"), 7);
+#endif
+}
+
+void readExecTime(unsigned char *)
+{
+    // Stores the measured execution time.
+    extern time_measurement_t conversion_time_measurement;
+    USBSerial::write(reinterpret_cast<uint8_t *>(&conversion_time_measurement.last),
+                     sizeof(rtcnt_t));
+}
+
+void sampleRate(unsigned char *cmd)
+{
+    if (EM.assert(USBSerial::read(&cmd[1], 1) == 1, Error::BadParamSize)) {
+        if (cmd[1] == 0xFF) {
+            unsigned char r = SClock::getRate();
+            USBSerial::write(&r, 1);
+        } else {
+            auto r = static_cast<SClock::Rate>(cmd[1]);
+            SClock::setRate(r);
+            ADC::setRate(r);
+        }
+    }
+}
+
+void readConversionResults(unsigned char *)
+{
+    if (auto samps = Samples::Out.modified(); samps != nullptr) {
+        unsigned char buf[2] = {
+            static_cast<unsigned char>(Samples::Out.size() / 2 & 0xFF),
+            static_cast<unsigned char>(((Samples::Out.size() / 2) >> 8) & 0xFF)
+        };
+        USBSerial::write(buf, 2);
+        unsigned int total = Samples::Out.bytesize() / 2;
+        unsigned int offset = 0;
+        unsigned char unused;
+        while (total > 512) {
+            USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, 512);
+            while (USBSerial::read(&unused, 1) == 0);
+            offset += 512;
+            total -= 512;
+        }
+        USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, total);
+        while (USBSerial::read(&unused, 1) == 0);
+    } else {
+        USBSerial::write(reinterpret_cast<const uint8_t *>("\0\0"), 2);
+    }
+}
+
+void readConversionInput(unsigned char *)
+{
+    if (auto samps = Samples::In.modified(); samps != nullptr) {
+        unsigned char buf[2] = {
+            static_cast<unsigned char>(Samples::In.size() / 2 & 0xFF),
+            static_cast<unsigned char>(((Samples::In.size() / 2) >> 8) & 0xFF)
+        };
+        USBSerial::write(buf, 2);
+        unsigned int total = Samples::In.bytesize() / 2;
+        unsigned int offset = 0;
+        unsigned char unused;
+        while (total > 512) {
+            USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, 512);
+            while (USBSerial::read(&unused, 1) == 0);
+            offset += 512;
+            total -= 512;
+        }
+        USBSerial::write(reinterpret_cast<uint8_t *>(samps) + offset, total);
+        while (USBSerial::read(&unused, 1) == 0);
+    } else {
+        USBSerial::write(reinterpret_cast<const uint8_t *>("\0\0"), 2);
+    }
+}
+
+void readMessage(unsigned char *)
+{
+    //USBSerial::write(reinterpret_cast<uint8_t *>(userMessageBuffer), userMessageSize);
+}
+
+void stopGenerator(unsigned char *)
+{
+    DAC::stop(1);
+}
+
diff --git a/firmware/source/communication.hpp b/firmware/source/communication.hpp
new file mode 100644 (file)
index 0000000..03220b8
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * @file communication.hpp
+ * @brief Manages communication with the host computer.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_COMMUNICATION_HPP
+#define STMDSP_COMMUNICATION_HPP
+
+#include <array>
+
+class CommunicationManager
+{
+public:
+    static void begin();
+
+private:
+    static void threadComm(void *);
+
+    static std::array<char, 4096> m_thread_stack;
+};
+
+#endif // STMDSP_COMMUNICATION_HPP
+
diff --git a/firmware/source/conversion.cpp b/firmware/source/conversion.cpp
new file mode 100644 (file)
index 0000000..56a689e
--- /dev/null
@@ -0,0 +1,218 @@
+/**
+ * @file conversion.cpp
+ * @brief Manages algorithm application (converts input samples to output).
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "conversion.hpp"
+
+#include "periph/adc.hpp"
+#include "periph/dac.hpp"
+#include "elfload.hpp"
+#include "error.hpp"
+#include "runstatus.hpp"
+#include "samples.hpp"
+
+// MSG_* things below are macros rather than constexpr
+// to ensure inlining.
+
+#define MSG_CONVFIRST          (1)
+#define MSG_CONVSECOND         (2)
+#define MSG_CONVFIRST_MEASURE  (3)
+#define MSG_CONVSECOND_MEASURE (4)
+
+#define MSG_FOR_FIRST(msg)   (msg & 1)
+#define MSG_FOR_MEASURE(msg) (msg > 2)
+
+__attribute__((section(".convdata")))
+thread_t *ConversionManager::m_thread_monitor = nullptr;
+thread_t *ConversionManager::m_thread_runner = nullptr;
+
+__attribute__((section(".stacks")))
+std::array<char, 1024> ConversionManager::m_thread_monitor_stack = {};
+__attribute__((section(".stacks")))
+std::array<char, THD_WORKING_AREA_SIZE(128)> ConversionManager::m_thread_runner_entry_stack = {};
+__attribute__((section(".convdata")))
+std::array<char, CONVERSION_THREAD_STACK_SIZE> ConversionManager::m_thread_runner_stack = {};
+
+std::array<msg_t, 2> ConversionManager::m_mailbox_buffer;
+mailbox_t ConversionManager::m_mailbox = _MAILBOX_DATA(m_mailbox, m_mailbox_buffer.data(), m_mailbox_buffer.size());
+
+void ConversionManager::begin()
+{
+    m_thread_monitor = chThdCreateStatic(m_thread_monitor_stack.data(),
+                                         m_thread_monitor_stack.size(),
+                                         NORMALPRIO + 1,
+                                         threadMonitor,
+                                         nullptr);
+    auto runner_stack_end = &m_thread_runner_stack[CONVERSION_THREAD_STACK_SIZE];
+    m_thread_runner = chThdCreateStatic(m_thread_runner_entry_stack.data(),
+                                        m_thread_runner_entry_stack.size(),
+                                        HIGHPRIO,
+                                        threadRunnerEntry,
+                                        runner_stack_end);
+}
+
+void ConversionManager::start()
+{
+    Samples::Out.clear();
+    ADC::start(Samples::In.data(), Samples::In.size(), adcReadHandler);
+    DAC::start(0, Samples::Out.data(), Samples::Out.size());
+}
+
+void ConversionManager::startMeasurement()
+{
+    ADC::setOperation(adcReadHandlerMeasure);
+}
+
+void ConversionManager::stop()
+{
+    DAC::stop(0);
+    ADC::stop();
+}
+
+thread_t *ConversionManager::getMonitorHandle()
+{
+    return m_thread_monitor;
+}
+
+void ConversionManager::abort(bool fpu_stacked)
+{
+    ELFManager::unload();
+    EM.add(Error::ConversionAborted);
+    //run_status = RunStatus::Recovering;
+
+    // Confirm that the exception return thread is the algorithm...
+    uint32_t *psp;
+       asm("mrs %0, psp" : "=r" (psp));
+
+    bool isRunnerStack = 
+           (uint32_t)psp >= reinterpret_cast<uint32_t>(m_thread_runner_stack.data()) &&
+           (uint32_t)psp <= reinterpret_cast<uint32_t>(m_thread_runner_stack.data() +
+                                                       m_thread_runner_stack.size());
+
+    if (isRunnerStack)
+    {
+        // If it is, we can force the algorithm to exit by "resetting" its thread.
+        // We do this by rebuilding the thread's stacked exception return.
+        auto newpsp = reinterpret_cast<uint32_t *>(m_thread_runner_stack.data() + 
+                                                   m_thread_runner_stack.size() -
+                                                   (fpu_stacked ? 26 : 8) * sizeof(uint32_t));
+        // Set the LR register to the thread's entry point.
+        newpsp[5] = reinterpret_cast<uint32_t>(threadRunner);
+        // Overwrite the instruction we'll return to with "bx lr" (jump to address in LR).
+        newpsp[6] = psp[6];
+        *reinterpret_cast<uint16_t *>(newpsp[6]) = 0x4770; // "bx lr"
+        // Keep PSR contents (bit set forces Thumb mode, just in case).
+        newpsp[7] = psp[7] | (1 << 24);
+        // Set the new stack pointer.
+           asm("msr psp, %0" :: "r" (newpsp));
+    }
+}
+
+void ConversionManager::threadMonitor(void *)
+{
+    while (1) {
+        msg_t message;
+        msg_t fetch = chMBFetchTimeout(&m_mailbox, &message, TIME_INFINITE);
+        if (fetch == MSG_OK)
+            chMsgSend(m_thread_runner, message);
+    }
+}
+
+void ConversionManager::threadRunnerEntry(void *stack)
+{
+    ELFManager::unload();
+    port_unprivileged_jump(reinterpret_cast<uint32_t>(threadRunner),
+                           reinterpret_cast<uint32_t>(stack));
+}
+
+__attribute__((section(".convcode")))
+void ConversionManager::threadRunner(void *)
+{
+    while (1) {
+        // Sleep until we receive a mailbox message.
+        msg_t message;
+        asm("svc 0; mov %0, r0" : "=r" (message));
+
+        if (message != 0) {
+            auto samples = MSG_FOR_FIRST(message) ? Samples::In.data()
+                                                  : Samples::In.middata();
+            auto size = Samples::In.size() / 2;
+
+            auto entry = ELFManager::loadedElf();
+            if (entry) {
+                // Below, we remember the stack pointer just in case the
+                // loaded algorithm messes things up.
+                uint32_t sp;
+
+                if (!MSG_FOR_MEASURE(message)) {
+                    asm("mov %0, sp" : "=r" (sp));
+                    samples = entry(samples, size);
+                    asm("mov sp, %0" :: "r" (sp));
+                    volatile auto testRead = *samples;
+                } else {
+                    // Start execution timer:
+                    asm("mov %0, sp; eor r0, r0; svc 2" : "=r" (sp));
+                    samples = entry(samples, size);
+                    // Stop execution timer:
+                    asm("mov r0, #1; svc 2; mov sp, %0" :: "r" (sp));
+                    volatile auto testRead = *samples;
+                } 
+            }
+
+            // Update the sample out buffer with the transformed samples.
+            if (samples != nullptr) {
+                if (MSG_FOR_FIRST(message))
+                    Samples::Out.modify(samples, size);
+                else
+                    Samples::Out.midmodify(samples, size);
+            }
+        }
+    }
+}
+
+void ConversionManager::adcReadHandler(adcsample_t *buffer, size_t)
+{
+    chSysLockFromISR();
+
+    // If previous request hasn't been handled, then we're going too slow.
+    // We'll need to abort.
+    if (chMBGetUsedCountI(&m_mailbox) > 1) {
+        chMBResetI(&m_mailbox);
+        chMBResumeX(&m_mailbox);
+        chSysUnlockFromISR();
+        abort();
+    } else {
+        // Mark the modified samples as 'fresh' or ready for manipulation.
+        if (buffer == Samples::In.data()) {
+            Samples::In.setModified();
+            chMBPostI(&m_mailbox, MSG_CONVFIRST);
+        } else {
+            Samples::In.setMidmodified();
+            chMBPostI(&m_mailbox, MSG_CONVSECOND);
+        }
+        chSysUnlockFromISR();
+    }
+}
+
+void ConversionManager::adcReadHandlerMeasure(adcsample_t *buffer, size_t)
+{
+    chSysLockFromISR();
+    if (buffer == Samples::In.data()) {
+        Samples::In.setModified();
+        chMBPostI(&m_mailbox, MSG_CONVFIRST_MEASURE);
+    } else {
+        Samples::In.setMidmodified();
+        chMBPostI(&m_mailbox, MSG_CONVSECOND_MEASURE);
+    }
+    chSysUnlockFromISR();
+
+    ADC::setOperation(adcReadHandler);
+}
+
diff --git a/firmware/source/conversion.hpp b/firmware/source/conversion.hpp
new file mode 100644 (file)
index 0000000..ca0054a
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * @file conversion.hpp
+ * @brief Manages algorithm application (converts input samples to output).
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_CONVERSION_HPP
+#define STMDSP_CONVERSION_HPP
+
+#include "ch.h"
+#include "hal.h"
+
+#include <array>
+
+constexpr unsigned int CONVERSION_THREAD_STACK_SIZE = 
+#if defined(TARGET_PLATFORM_H7)
+                                                  62 * 1024;
+#else
+                                                  15 * 1024;
+#endif
+
+class ConversionManager
+{
+public:
+    static void begin();
+
+    // Begins sample conversion.
+    static void start();
+    // Prepare to measure execution time of next conversion.
+    static void startMeasurement();
+    // Stops conversion.
+    static void stop();
+
+    static thread_t *getMonitorHandle();
+
+    // Internal only: Aborts a running conversion.
+    static void abort(bool fpu_stacked = true);
+
+private:
+    static void threadMonitor(void *);
+    static void threadRunnerEntry(void *stack);
+
+    static void threadRunner(void *);
+    static void adcReadHandler(adcsample_t *buffer, size_t);
+    static void adcReadHandlerMeasure(adcsample_t *buffer, size_t);
+
+    static thread_t *m_thread_monitor;
+    static thread_t *m_thread_runner;
+
+    static std::array<char, 1024> m_thread_monitor_stack;
+    static std::array<char, THD_WORKING_AREA_SIZE(128)> m_thread_runner_entry_stack;
+    static std::array<char, CONVERSION_THREAD_STACK_SIZE> m_thread_runner_stack;
+
+    static std::array<msg_t, 2> m_mailbox_buffer;
+    static mailbox_t m_mailbox;
+};
+
+#endif // STMDSP_CONVERSION_HPP
+
diff --git a/firmware/source/elf.h b/firmware/source/elf.h
new file mode 100644 (file)
index 0000000..998356d
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ * @file elf.h
+ * @brief Defines ELF binary format info.
+ *
+ * Free to use, written by Clyne Sullivan.
+ */
+
+#ifndef STMDSP_ELF_HPP
+#define STMDSP_ELF_HPP
+
+#include <cstdint>
+
+#define EI_NIDENT 16
+
+#define PT_NULL     0
+#define PT_LOAD     1
+#define PT_DYNAMIC  2
+#define PT_INTERP   3
+#define PT_NOTE     4
+#define PT_SHLIB    5
+#define PT_PHDR     6
+#define PT_RESERVED 0x70000000
+
+#define ELF32_ST_BIND(i)    ((i) >> 4)
+#define ELF32_ST_TYPE(i)    ((i) & 0xF)
+#define ELF32_ST_INFO(b, t) (((b) << 4) + ((t) & 0xF))
+
+#define ELF32_R_SYM(i)     ((i) >> 8)
+#define ELF32_R_TYPE(i)    ((i) & 0xFF)
+#define ELF32_R_INFO(s, t) (((s) << 8) + ((t) & 0xFF))
+
+typedef uint32_t Elf32_Addr;
+typedef uint16_t Elf32_Half;
+typedef uint32_t Elf32_Off;
+typedef uint32_t Elf32_Sword;
+typedef uint32_t Elf32_Word;
+
+typedef struct {
+       unsigned char e_ident[EI_NIDENT];
+       Elf32_Half    e_type;
+       Elf32_Half    e_machine;
+       Elf32_Word    e_version;
+       Elf32_Addr    e_entry;
+       Elf32_Off     e_phoff;
+       Elf32_Off     e_shoff;
+       Elf32_Word    e_flags;
+       Elf32_Half    e_ehsize;
+       Elf32_Half    e_phentsize;
+       Elf32_Half    e_phnum;
+       Elf32_Half    e_shentsize;
+       Elf32_Half    e_shnum;
+       Elf32_Half    e_shstrndx;
+} __attribute__((packed)) Elf32_Ehdr;
+
+typedef struct {
+       Elf32_Word sh_name;
+       Elf32_Word sh_type;
+       Elf32_Word sh_flags;
+       Elf32_Addr sh_addr;
+       Elf32_Off  sh_offset;
+       Elf32_Word sh_size;
+       Elf32_Word sh_link;
+       Elf32_Word sh_info;
+       Elf32_Word sh_addralign;
+       Elf32_Word sh_entsize;
+} __attribute__((packed)) Elf32_Shdr;
+
+typedef struct {
+       Elf32_Word    st_name;
+       Elf32_Addr    st_value;
+       Elf32_Word    st_size;
+       unsigned char st_info;
+       unsigned char st_other;
+       Elf32_Half    st_shndx;
+} __attribute__((packed)) Elf32_Sym;
+
+typedef struct {
+       Elf32_Addr r_offset;
+       Elf32_Word r_info;
+} __attribute__((packed)) Elf32_Rel;
+
+typedef struct {
+       Elf32_Addr  r_offset;
+       Elf32_Word  r_info;
+       Elf32_Sword r_addend;
+} __attribute__((packed)) Elf32_Rela;
+
+typedef struct {
+       Elf32_Word p_type;
+       Elf32_Off  p_offset;
+       Elf32_Addr p_vaddr;
+       Elf32_Addr p_paddr;
+       Elf32_Word p_filesz;
+       Elf32_Word p_memsz;
+       Elf32_Word p_flags;
+       Elf32_Word p_align;
+} __attribute__((packed)) Elf32_Phdr;
+
+#endif // STMDSP_ELF_HPP
+
diff --git a/firmware/source/elfload.cpp b/firmware/source/elfload.cpp
new file mode 100644 (file)
index 0000000..87461e4
--- /dev/null
@@ -0,0 +1,84 @@
+/**
+ * @file elfload.cpp
+ * @brief Loads ELF binary data into memory for execution.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "elfload.hpp"
+#include "elf.h"
+
+#include <algorithm>
+#include <cstring>
+
+__attribute__((section(".convdata")))
+ELFManager::EntryFunc ELFManager::m_entry = nullptr;
+std::array<unsigned char, MAX_ELF_FILE_SIZE> ELFManager::m_file_buffer = {};
+
+static const unsigned char elf_header[] = { '\177', 'E', 'L', 'F' };
+
+__attribute__((section(".convcode")))
+ELFManager::EntryFunc ELFManager::loadedElf()
+{
+    return m_entry;
+}
+
+unsigned char *ELFManager::fileBuffer()
+{
+    return m_file_buffer.data();
+}
+
+void ELFManager::unload()
+{
+    m_entry = nullptr;
+}
+
+template<typename T>
+constexpr static auto ptr_from_offset(void *base, uint32_t offset)
+{
+    return reinterpret_cast<T>(reinterpret_cast<uint8_t *>(base) + offset);
+}
+
+bool ELFManager::loadFromInternalBuffer()
+{
+    m_entry = nullptr;
+
+    auto elf_data = m_file_buffer.data();
+
+    // Check the ELF's header signature
+    auto ehdr = reinterpret_cast<Elf32_Ehdr *>(elf_data);
+    if (!std::equal(ehdr->e_ident, ehdr->e_ident + 4, elf_header))
+        return false;
+
+    // Iterate through program header LOAD sections
+    bool loaded = false;
+    auto phdr = ptr_from_offset<Elf32_Phdr *>(elf_data, ehdr->e_phoff);
+    for (Elf32_Half i = 0; i < ehdr->e_phnum; i++) {
+        if (phdr->p_type == PT_LOAD) {
+            if (phdr->p_filesz == 0) {
+                std::memset(reinterpret_cast<void *>(phdr->p_vaddr),
+                            0,
+                            phdr->p_memsz);
+            } else {
+                std::memcpy(reinterpret_cast<void *>(phdr->p_vaddr),
+                            ptr_from_offset<void *>(elf_data, phdr->p_offset),
+                            phdr->p_filesz);
+                if (!loaded)
+                    loaded = true;
+            }
+        }
+
+        phdr = ptr_from_offset<Elf32_Phdr *>(phdr, ehdr->e_phentsize);
+    }
+
+
+    if (loaded)
+        m_entry = reinterpret_cast<ELFManager::EntryFunc>(ehdr->e_entry);
+
+    return loaded;
+}
+
diff --git a/firmware/source/elfload.hpp b/firmware/source/elfload.hpp
new file mode 100644 (file)
index 0000000..10d95d7
--- /dev/null
@@ -0,0 +1,39 @@
+/**
+ * @file elfload.hpp
+ * @brief Loads ELF binary data into memory for execution.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef ELF_LOAD_HPP_
+#define ELF_LOAD_HPP_
+
+#include "samplebuffer.hpp"
+
+#include <array>
+#include <cstddef>
+
+constexpr unsigned int MAX_ELF_FILE_SIZE = 16 * 1024;
+
+class ELFManager
+{
+public:
+    using EntryFunc = Sample *(*)(Sample *, size_t);
+    
+    static bool loadFromInternalBuffer();
+    static EntryFunc loadedElf();
+    static unsigned char *fileBuffer();
+    static void unload();
+
+private:
+    static EntryFunc m_entry;
+
+    static std::array<unsigned char, MAX_ELF_FILE_SIZE> m_file_buffer;
+};
+
+#endif // ELF_LOAD_HPP_
+
diff --git a/firmware/source/error.cpp b/firmware/source/error.cpp
new file mode 100644 (file)
index 0000000..9f2e98f
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * @file error.cpp
+ * @brief Tracks and reports non-critical errors.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "error.hpp"
+
+ErrorManager EM;
+
+void ErrorManager::add(Error error)
+{ 
+    if (m_index < m_queue.size())
+        m_queue[m_index++] = error;
+}
+
+bool ErrorManager::assert(bool condition, Error error)
+{
+    if (!condition)
+        add(error);
+    return condition;
+}
+
+bool ErrorManager::hasError()
+{
+    return m_index > 0;
+}
+
+Error ErrorManager::pop()
+{
+    return m_index == 0 ? Error::None : m_queue[--m_index];
+}
+
diff --git a/firmware/source/error.hpp b/firmware/source/error.hpp
new file mode 100644 (file)
index 0000000..c6a7f5c
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * @file error.hpp
+ * @brief Tracks and reports non-critical errors.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_ERROR_HPP
+#define STMDSP_ERROR_HPP
+
+#include <array>
+
+enum class Error : char
+{
+    None = 0,
+    BadParam,
+    BadParamSize,
+    BadUserCodeLoad,
+    BadUserCodeSize,
+    NotIdle,
+    ConversionAborted,
+    NotRunning
+};
+
+class ErrorManager
+{
+    constexpr static unsigned int MAX_ERROR_QUEUE_SIZE = 8;
+
+public:
+    void add(Error error);
+    bool assert(bool condition, Error error);
+    bool hasError();
+    Error pop();
+
+private:
+    std::array<Error, MAX_ERROR_QUEUE_SIZE> m_queue;
+    unsigned int m_index = 0;
+};
+
+extern ErrorManager EM;
+
+#endif // STMDSP_ERROR_HPP
+
diff --git a/firmware/source/handlers.cpp b/firmware/source/handlers.cpp
new file mode 100644 (file)
index 0000000..43e65c3
--- /dev/null
@@ -0,0 +1,140 @@
+#include "handlers.hpp"
+
+#include "adc.hpp"
+#include "conversion.hpp"
+#include "cordic.hpp"
+#include "runstatus.hpp"
+
+extern "C" {
+
+time_measurement_t conversion_time_measurement;
+
+__attribute__((naked))
+void port_syscall(struct port_extctx *ctxp, uint32_t n)
+{
+    switch (n) {
+
+    // Sleeps the current thread until a message is received.
+    // Used the algorithm runner to wait for new data.
+    case 0:
+        {
+            chSysLock();
+            chMsgWaitS();
+            auto monitor = ConversionManager::getMonitorHandle();
+            auto msg = chMsgGet(monitor);
+            chMsgReleaseS(monitor, MSG_OK);
+            chSysUnlock();
+            ctxp->r0 = msg;
+        }
+        break;
+
+    // Provides access to advanced math functions.
+    // A service call like this is required for some hardware targets that
+    // provide hardware-accelerated math computations (e.g. CORDIC).
+    case 1:
+        {
+            using mathcall = void (*)();
+            static mathcall funcs[3] = {
+                reinterpret_cast<mathcall>(cordic::sin),
+                reinterpret_cast<mathcall>(cordic::cos),
+                reinterpret_cast<mathcall>(cordic::tan),
+            };
+#if defined(PLATFORM_H7)
+            asm("vmov.f64 d0, %0, %1" :: "r" (ctxp->r1), "r" (ctxp->r2));
+            if (ctxp->r0 < 3) {
+                funcs[ctxp->r0]();
+                asm("vmov.f64 %0, %1, d0" : "=r" (ctxp->r1), "=r" (ctxp->r2));
+            } else {
+                asm("eor r0, r0; vmov.f64 d0, r0, r0");
+            }
+#else
+            asm("vmov.f32 s0, %0" :: "r" (ctxp->r1));
+            if (ctxp->r0 < 3) {
+                funcs[ctxp->r0]();
+                asm("vmov.f32 %0, s0" : "=r" (ctxp->r1));
+            } else {
+                asm("eor r0, r0; vmov.f32 s0, r0");
+            }
+#endif
+        }
+        break;
+
+    // Starts or stops precise cycle time measurement.
+    // Used to measure algorithm execution time.
+    case 2:
+        if (ctxp->r0 == 0) {
+            chTMStartMeasurementX(&conversion_time_measurement);
+        } else {
+            chTMStopMeasurementX(&conversion_time_measurement);
+            // Subtract measurement overhead from the result.
+            // Running an empty algorithm ("bx lr") takes 196 cycles as of 2/4/21.
+            // Only measures algorithm code time (loading args/storing result takes 9 cycles).
+            constexpr rtcnt_t measurement_overhead = 196 - 1;
+            if (conversion_time_measurement.last > measurement_overhead)
+                conversion_time_measurement.last -= measurement_overhead;
+        }
+        break;
+
+    // Reads one of the analog inputs made available for algorithm run-time input.
+    case 3:
+        ctxp->r0 = ADC::readAlt(ctxp->r0);
+        break;
+
+    //case 4:
+    //    {
+    //        const char *str = reinterpret_cast<const char *>(ctxp->r0);
+    //        auto src = str;
+    //        auto dst = userMessageBuffer;
+    //        while (*src)
+    //            *dst++ = *src++;
+    //        *dst = '\0';
+    //        userMessageSize = src - str;
+    //    }
+    //    break;
+    default:
+        while (1);
+        break;
+    }
+
+    asm("svc 0");
+    while (1);
+}
+
+__attribute__((naked))
+void MemManage_Handler()
+{
+    // 1. Get the stack pointer.
+    uint32_t lr;
+    asm("mov %0, lr" : "=r" (lr));
+
+    // 2. Recover from the fault.
+    ConversionManager::abort((lr & (1 << 4)) ? false : true);
+
+    // 3. Return.
+    asm("mov lr, %0; bx lr" :: "r" (lr));
+}
+
+__attribute__((naked))
+void HardFault_Handler()
+{
+    // Get the stack pointer.
+    //uint32_t *stack;
+    uint32_t lr;
+    asm("mov %0, lr" : "=r" (lr));
+    /*asm("\
+        tst lr, #4; \
+        ite eq; \
+        mrseq %0, msp; \
+        mrsne %0, psp; \
+        mov %1, lr; \
+    " : "=r" (stack), "=r" (lr));*/
+
+    // If coming from the algorithm, attempt to recover; otherwise, give up.
+    if (run_status != RunStatus::Running && (lr & 4) != 0)
+        MemManage_Handler();
+
+    while (1);
+}
+
+} // extern "C"
+
diff --git a/firmware/source/handlers.hpp b/firmware/source/handlers.hpp
new file mode 100644 (file)
index 0000000..fd7e10c
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * @file handlers.hpp
+ * @brief Interrupt service routine handlers.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_HANDLERS_HPP
+#define STMDSP_HANDLERS_HPP
+
+#include "ch.h"
+
+extern "C" {
+
+__attribute__((naked))
+void port_syscall(struct port_extctx *ctxp, uint32_t n);
+
+__attribute__((naked))
+void MemManage_Handler();
+
+__attribute__((naked))
+void HardFault_Handler();
+
+}
+
+#endif // STMDSP_HANDLERS_HPP
+
diff --git a/firmware/source/ld/STM32H723xG.ld b/firmware/source/ld/STM32H723xG.ld
new file mode 100644 (file)
index 0000000..7d5bafb
--- /dev/null
@@ -0,0 +1,124 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+/*\r
+ * AXI SRAM     - BSS, Data, Heap.\r
+ * SRAM1        - SIGGEN.\r
+ * SRAM2        - DAC.\r
+ * SRAM4        - ADC.\r
+ * DTCM-RAM     - Process stacks.\r
+ * ITCM-RAM     - STMDSP Algorithm.\r
+ * BCKP SRAM    - None.\r
+ */\r
+MEMORY\r
+{\r
+    flash0 (rx) : org = 0x08000000, len = 1M       /* Flash bank1 + bank2 */\r
+    flash1 (rx) : org = 0x08000000, len = 510K     /* Flash bank 1 */\r
+    flashc (rx) : org = 0x0807F800, len = 2K       /* Unprivileged firmware */\r
+    flash2 (rx) : org = 0x08080000, len = 512K     /* Flash bank 2 */\r
+    flash3 (rx) : org = 0x00000000, len = 0\r
+    flash4 (rx) : org = 0x00000000, len = 0\r
+    flash5 (rx) : org = 0x00000000, len = 0\r
+    flash6 (rx) : org = 0x00000000, len = 0\r
+    flash7 (rx) : org = 0x00000000, len = 0\r
+    ram0   (wx) : org = 0x24000000, len = 320K     /* AXI SRAM */\r
+    ram1   (wx) : org = 0x30000000, len = 16K      /* AHB SRAM1 */\r
+    ram2   (wx) : org = 0x30004000, len = 16K      /* AHB SRAM2 */\r
+    ram3   (wx) : org = 0x38000000, len = 16K      /* AHB SRAM4 */\r
+    ram4   (wx) : org = 0x00000000, len = 0\r
+    ramc   (wx) : org = 0x20000000, len = 64K      /* Unprivileged data */\r
+    ram5   (wx) : org = 0x20010000, len = 64K      /* DTCM-RAM */\r
+    ram6   (wx) : org = 0x00000000, len = 64K      /* ITCM-RAM */\r
+    ram7   (wx) : org = 0x38800000, len = 4K       /* BCKP SRAM */\r
+}\r
+\r
+/* For each data/text section two region are defined, a virtual region\r
+   and a load region (_LMA suffix).*/\r
+\r
+/* Flash region to be used for exception vectors.*/\r
+REGION_ALIAS("VECTORS_FLASH", flash0);\r
+REGION_ALIAS("VECTORS_FLASH_LMA", flash0);\r
+\r
+/* Flash region to be used for constructors and destructors.*/\r
+REGION_ALIAS("XTORS_FLASH", flash0);\r
+REGION_ALIAS("XTORS_FLASH_LMA", flash0);\r
+\r
+/* Flash region to be used for code text.*/\r
+REGION_ALIAS("TEXT_FLASH", flash0);\r
+REGION_ALIAS("TEXT_FLASH_LMA", flash0);\r
+\r
+/* Flash region to be used for read only data.*/\r
+REGION_ALIAS("RODATA_FLASH", flash0);\r
+REGION_ALIAS("RODATA_FLASH_LMA", flash0);\r
+\r
+/* Flash region to be used for various.*/\r
+REGION_ALIAS("VARIOUS_FLASH", flash0);\r
+REGION_ALIAS("VARIOUS_FLASH_LMA", flash0);\r
+\r
+/* Flash region to be used for RAM(n) initialization data.*/\r
+REGION_ALIAS("RAM_INIT_FLASH_LMA", flash0);\r
+\r
+/* RAM region to be used for Main stack. This stack accommodates the processing\r
+   of all exceptions and interrupts.*/\r
+REGION_ALIAS("MAIN_STACK_RAM", ram5);\r
+\r
+/* RAM region to be used for the process stack. This is the stack used by\r
+   the main() function.*/\r
+REGION_ALIAS("PROCESS_STACK_RAM", ram5);\r
+\r
+/* RAM region to be used for data segment.*/\r
+REGION_ALIAS("DATA_RAM", ram0);\r
+REGION_ALIAS("DATA_RAM_LMA", flash0);\r
+\r
+/* RAM region to be used for BSS segment.*/\r
+REGION_ALIAS("BSS_RAM", ram0);\r
+\r
+/* RAM region to be used for the default heap.*/\r
+REGION_ALIAS("HEAP_RAM", ram0);\r
+\r
+/* Stack rules inclusion.*/\r
+INCLUDE rules_stacks.ld\r
+\r
+SECTIONS\r
+{\r
+    .convdata : ALIGN(4)\r
+    {\r
+        *(.convdata)\r
+        . = ALIGN(4);\r
+    } > ramc\r
+\r
+    .stacks : ALIGN(4)\r
+    {\r
+        *(.stacks)\r
+        . = ALIGN(4);\r
+    } > ram5\r
+\r
+    .convcode : ALIGN(4)\r
+    {\r
+        *(.convcode)\r
+        . = ALIGN(4);\r
+    } > flashc\r
+}\r
+\r
+/* Code rules inclusion.*/\r
+INCLUDE rules_code.ld\r
+\r
+/* Data rules inclusion.*/\r
+INCLUDE rules_data.ld\r
+\r
+/* Memory rules inclusion.*/\r
+INCLUDE rules_memory.ld\r
+\r
diff --git a/firmware/source/ld/STM32L476xG.ld b/firmware/source/ld/STM32L476xG.ld
new file mode 100644 (file)
index 0000000..b3a332d
--- /dev/null
@@ -0,0 +1,116 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+/*\r
+ * STM32L476xG memory setup.\r
+ * A total of 1MB of flash is available.\r
+ * Firmware uses first 510K, then 2K after is used for unprivileged code.\r
+ * A total of 128K of RAM is available.\r
+ * SRAM2 (32K) is used for ELF binary loading.\r
+ * 32K of SRAM1 is used for system RAM.\r
+ * 48K is used for ADC and DAC buffers.\r
+ * 16K is used for unprivileged data (incl. 8K  stack).\r
+ */\r
+MEMORY\r
+{\r
+    flash0 (rx) : org = 0x08000000, len = 510K   /* Flash bank 1 (reduced from 1M to 510K) */\r
+    flash1 (rx) : org = 0x00000000, len = 0\r
+    flash2 (rx) : org = 0x00000000, len = 0\r
+    flash3 (rx) : org = 0x00000000, len = 0\r
+    flash4 (rx) : org = 0x00000000, len = 0\r
+    flash5 (rx) : org = 0x00000000, len = 0\r
+    flash6 (rx) : org = 0x00000000, len = 0\r
+    flash7 (rx) : org = 0x00000000, len = 0\r
+    ram0   (wx) : org = 0x20000000, len = 32K  /* SRAM (actual total = 96K) */\r
+    ram1   (wx) : org = 0x20008000, len = 48K  /* ADC/DAC buffers (16K * 3) */\r
+    ram2   (wx) : org = 0x00000000, len = 0\r
+    ram3   (wx) : org = 0x00000000, len = 0\r
+    ram4   (wx) : org = 0x10000000, len = 32K  /* User algorithm */\r
+    ram5   (wx) : org = 0x00000000, len = 0\r
+    ram6   (wx) : org = 0x00000000, len = 0\r
+    ram7   (wx) : org = 0x00000000, len = 0\r
+    flashc (rx) : org = 0x0807F800, len = 2K   /* Unprivileged firmware */\r
+    ramc   (wx) : org = 0x20014000, len = 16K  /* Unprivileged data */\r
+}\r
+\r
+/* For each data/text section two region are defined, a virtual region\r
+   and a load region (_LMA suffix).*/\r
+\r
+/* Flash region to be used for exception vectors.*/\r
+REGION_ALIAS("VECTORS_FLASH", flash0);\r
+REGION_ALIAS("VECTORS_FLASH_LMA", flash0);\r
+\r
+/* Flash region to be used for constructors and destructors.*/\r
+REGION_ALIAS("XTORS_FLASH", flash0);\r
+REGION_ALIAS("XTORS_FLASH_LMA", flash0);\r
+\r
+/* Flash region to be used for code text.*/\r
+REGION_ALIAS("TEXT_FLASH", flash0);\r
+REGION_ALIAS("TEXT_FLASH_LMA", flash0);\r
+\r
+/* Flash region to be used for read only data.*/\r
+REGION_ALIAS("RODATA_FLASH", flash0);\r
+REGION_ALIAS("RODATA_FLASH_LMA", flash0);\r
+\r
+/* Flash region to be used for various.*/\r
+REGION_ALIAS("VARIOUS_FLASH", flash0);\r
+REGION_ALIAS("VARIOUS_FLASH_LMA", flash0);\r
+\r
+/* Flash region to be used for RAM(n) initialization data.*/\r
+REGION_ALIAS("RAM_INIT_FLASH_LMA", flash0);\r
+\r
+/* RAM region to be used for Main stack. This stack accommodates the processing\r
+   of all exceptions and interrupts.*/\r
+REGION_ALIAS("MAIN_STACK_RAM", ram0);\r
+\r
+/* RAM region to be used for the process stack. This is the stack used by\r
+   the main() function.*/\r
+REGION_ALIAS("PROCESS_STACK_RAM", ram0);\r
+\r
+/* RAM region to be used for data segment.*/\r
+REGION_ALIAS("DATA_RAM", ram0);\r
+REGION_ALIAS("DATA_RAM_LMA", flash0);\r
+\r
+/* RAM region to be used for BSS segment.*/\r
+REGION_ALIAS("BSS_RAM", ram0);\r
+\r
+/* RAM region to be used for the default heap.*/\r
+REGION_ALIAS("HEAP_RAM", ram0);\r
+\r
+SECTIONS\r
+{\r
+    .convdata : ALIGN(4)\r
+    {\r
+        *(.convdata)\r
+        . = ALIGN(4);\r
+    } > ramc\r
+\r
+    /*.stacks : ALIGN(4)\r
+    {\r
+        *(.stacks)\r
+        . = ALIGN(4);\r
+    } > ram5*/\r
+\r
+    .convcode : ALIGN(4)\r
+    {\r
+        *(.convcode)\r
+        . = ALIGN(4);\r
+    } > flashc\r
+}\r
+\r
+\r
+/* Generic rules inclusion.*/\r
+INCLUDE rules.ld\r
diff --git a/firmware/source/main.cpp b/firmware/source/main.cpp
new file mode 100644 (file)
index 0000000..9a22a73
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * @file main.cpp
+ * @brief Program entry point.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+#include "adc.hpp"
+#include "cordic.hpp"
+#include "dac.hpp"
+#include "error.hpp"
+#include "sclock.hpp"
+#include "usbserial.hpp"
+
+#include "runstatus.hpp"
+RunStatus run_status = RunStatus::Idle;
+
+// Other variables
+//
+//static char userMessageBuffer[128];
+//static unsigned char userMessageSize = 0;
+
+#include "conversion.hpp"
+#include "communication.hpp"
+#include "monitor.hpp"
+
+int main()
+{
+    // Initialize ChibiOS
+    halInit();
+    chSysInit();
+
+    // Init peripherials
+    ADC::begin();
+    DAC::begin();
+    SClock::begin();
+    USBSerial::begin();
+    cordic::init();
+
+    SClock::setRate(SClock::Rate::R32K);
+    ADC::setRate(SClock::Rate::R32K);
+
+    // Start our threads.
+    ConversionManager::begin();
+    CommunicationManager::begin();
+    Monitor::begin();
+
+    chThdExit(0);
+    return 0;
+}
+
diff --git a/firmware/source/monitor.cpp b/firmware/source/monitor.cpp
new file mode 100644 (file)
index 0000000..6ef97e9
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+ * @file monitor.cpp
+ * @brief Manages the device monitoring thread (status LEDs, etc.).
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "monitor.hpp"
+
+#include "error.hpp"
+#include "runstatus.hpp"
+
+#include "hal.h"
+
+__attribute__((section(".stacks")))
+std::array<char, THD_WORKING_AREA_SIZE(256)> Monitor::m_thread_stack = {};
+
+void Monitor::begin()
+{
+    chThdCreateStatic(m_thread_stack.data(),
+                      m_thread_stack.size(),
+                      LOWPRIO,
+                      threadMonitor,
+                      nullptr);
+}
+
+void Monitor::threadMonitor(void *)
+{
+    palSetLineMode(LINE_BUTTON, PAL_MODE_INPUT_PULLUP);
+    auto readButton = [] {
+#ifdef TARGET_PLATFORM_L4
+        return !palReadLine(LINE_BUTTON);
+#else
+        return palReadLine(LINE_BUTTON);
+#endif
+    };
+
+    palSetLine(LINE_LED_RED);
+    palSetLine(LINE_LED_GREEN);
+    palSetLine(LINE_LED_BLUE);
+
+    while (1) {
+        bool isidle = run_status == RunStatus::Idle;
+        auto led = isidle ? LINE_LED_GREEN : LINE_LED_BLUE;
+        auto delay = isidle ? 500 : 250;
+
+        palToggleLine(led);
+        chThdSleepMilliseconds(delay);
+        palToggleLine(led);
+        chThdSleepMilliseconds(delay);
+
+        if (isidle && readButton()) {
+            palClearLine(LINE_LED_GREEN);
+            palClearLine(LINE_LED_BLUE);
+            chSysLock();
+            while (readButton())
+                asm("nop");
+            while (!readButton())
+                asm("nop");
+            chSysUnlock();
+            palSetLine(LINE_LED_GREEN);
+            palSetLine(LINE_LED_BLUE);
+            chThdSleepMilliseconds(500);
+        }
+
+        static bool erroron = false;
+        if (auto err = EM.hasError(); err ^ erroron) {
+            erroron = err;
+            if (err)
+                palClearLine(LINE_LED_RED);
+            else
+                palSetLine(LINE_LED_RED);
+        }
+    }
+}
+
diff --git a/firmware/source/monitor.hpp b/firmware/source/monitor.hpp
new file mode 100644 (file)
index 0000000..93f75e3
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * @file monitor.hpp
+ * @brief Manages the device monitoring thread (status LEDs, etc.).
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_MONITOR_HPP
+#define STMDSP_MONITOR_HPP
+
+#include "ch.h"
+
+#include <array>
+
+class Monitor
+{
+public:
+    static void begin();
+
+private:
+    static void threadMonitor(void *);
+
+    static std::array<char, THD_WORKING_AREA_SIZE(256)> m_thread_stack;
+};
+
+#endif // STMDSP_MONITOR_HPP
+
diff --git a/firmware/source/periph/adc.cpp b/firmware/source/periph/adc.cpp
new file mode 100644 (file)
index 0000000..4667307
--- /dev/null
@@ -0,0 +1,245 @@
+/**
+ * @file adc.cpp
+ * @brief Manages signal reading through the ADC.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "adc.hpp"
+
+#if defined(TARGET_PLATFORM_L4)
+ADCDriver *ADC::m_driver = &ADCD1;
+ADCDriver *ADC::m_driver2 = &ADCD3;
+#else
+ADCDriver *ADC::m_driver = &ADCD3;
+//ADCDriver *ADC::m_driver2 = &ADCD1; // TODO
+#endif
+
+const ADCConfig ADC::m_config = {
+    .difsel = 0,
+#if defined(TARGET_PLATFORM_H7)
+    .calibration = 0,
+#endif
+};
+
+const ADCConfig ADC::m_config2 = {
+    .difsel = 0,
+#if defined(TARGET_PLATFORM_H7)
+    .calibration = 0,
+#endif
+};
+
+ADCConversionGroup ADC::m_group_config = {
+    .circular = true,
+    .num_channels = 1,
+    .end_cb = ADC::conversionCallback,
+    .error_cb = nullptr,
+    .cfgr = ADC_CFGR_EXTEN_RISING | ADC_CFGR_EXTSEL_SRC(13),  /* TIM6_TRGO */
+    .cfgr2 = 0,//ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSS_0, // Oversampling 2x
+#if defined(TARGET_PLATFORM_H7)
+    .ccr = 0,
+    .pcsel = 0,
+    .ltr1 = 0, .htr1 = 4095,
+    .ltr2 = 0, .htr2 = 4095,
+    .ltr3 = 0, .htr3 = 4095,
+#else
+    .tr1 = ADC_TR(0, 4095),
+    .tr2 = ADC_TR(0, 4095),
+    .tr3 = ADC_TR(0, 4095),
+    .awd2cr = 0,
+    .awd3cr = 0,
+#endif
+    .smpr = {
+        ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_12P5), 0
+    },
+    .sqr = {
+        ADC_SQR1_SQ1_N(ADC_CHANNEL_IN5),
+        0, 0, 0
+    },
+};
+
+static bool readAltDone = false;
+static void readAltCallback(ADCDriver *)
+{
+    readAltDone = true;
+}
+ADCConversionGroup ADC::m_group_config2 = {
+    .circular = false,
+    .num_channels = 2,
+    .end_cb = readAltCallback,
+    .error_cb = nullptr,
+    .cfgr = ADC_CFGR_EXTEN_RISING | ADC_CFGR_EXTSEL_SRC(13),  /* TIM6_TRGO */
+    .cfgr2 = 0,//ADC_CFGR2_ROVSE | ADC_CFGR2_OVSR_1 | ADC_CFGR2_OVSS_0, // Oversampling 2x
+#if defined(TARGET_PLATFORM_H7)
+    .ccr = 0,
+    .pcsel = 0,
+    .ltr1 = 0, .htr1 = 4095,
+    .ltr2 = 0, .htr2 = 4095,
+    .ltr3 = 0, .htr3 = 4095,
+#else
+    .tr1 = ADC_TR(0, 4095),
+    .tr2 = ADC_TR(0, 4095),
+    .tr3 = ADC_TR(0, 4095),
+    .awd2cr = 0,
+    .awd3cr = 0,
+#endif
+    .smpr = {
+        ADC_SMPR1_SMP_AN1(ADC_SMPR_SMP_2P5) | ADC_SMPR1_SMP_AN2(ADC_SMPR_SMP_2P5), 0
+    },
+    .sqr = {
+        ADC_SQR1_SQ1_N(ADC_CHANNEL_IN1) | ADC_SQR1_SQ2_N(ADC_CHANNEL_IN2),
+        0, 0, 0
+    },
+};
+
+adcsample_t *ADC::m_current_buffer = nullptr;
+size_t ADC::m_current_buffer_size = 0;
+ADC::Operation ADC::m_operation = nullptr;
+
+void ADC::begin()
+{
+#if defined(TARGET_PLATFORM_H7)
+    palSetPadMode(GPIOF, 3, PAL_MODE_INPUT_ANALOG);
+#else
+    palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG); // Algorithm in
+    palSetPadMode(GPIOC, 0, PAL_MODE_INPUT_ANALOG); // Potentiometer 1
+    palSetPadMode(GPIOC, 1, PAL_MODE_INPUT_ANALOG); // Potentiometer 2
+#endif
+
+    adcStart(m_driver, &m_config);
+    adcStart(m_driver2, &m_config2);
+}
+
+void ADC::start(adcsample_t *buffer, size_t count, Operation operation)
+{
+    m_current_buffer = buffer;
+    m_current_buffer_size = count;
+    m_operation = operation;
+
+    adcStartConversion(m_driver, &m_group_config, buffer, count);
+    SClock::start();
+}
+
+void ADC::stop()
+{
+    SClock::stop();
+    adcStopConversion(m_driver);
+
+    m_current_buffer = nullptr;
+    m_current_buffer_size = 0;
+    m_operation = nullptr;
+}
+
+adcsample_t ADC::readAlt(unsigned int id)
+{
+    if (id > 1)
+        return 0;
+    static adcsample_t result[16] = {};
+    readAltDone = false;
+    adcStartConversion(m_driver2, &m_group_config2, result, 8);
+    while (!readAltDone);
+        //__WFI();
+    adcStopConversion(m_driver2);
+    return result[id];
+}
+
+void ADC::setRate(SClock::Rate rate)
+{
+#if defined(TARGET_PLATFORM_H7)
+    std::array<std::array<uint32_t, 2>, 6> m_rate_presets = {{
+         // Rate   PLL N  PLL P
+        {/* 8k  */ 80,    20},
+        {/* 16k */ 80,    10},
+        {/* 20k */ 80,    8},
+        {/* 32k */ 80,    5},
+        {/* 48k */ 96,    4},
+        {/* 96k */ 288,   10}
+    }};
+
+    auto& preset = m_rate_presets[static_cast<unsigned int>(rate)];
+    auto pllbits = (preset[0] << RCC_PLL2DIVR_N2_Pos) |
+                   (preset[1] << RCC_PLL2DIVR_P2_Pos);
+
+    adcStop(m_driver);
+
+    // Adjust PLL2
+    RCC->CR &= ~(RCC_CR_PLL2ON);
+    while ((RCC->CR & RCC_CR_PLL2RDY) == RCC_CR_PLL2RDY);
+    auto pll2divr = RCC->PLL2DIVR &
+                    ~(RCC_PLL2DIVR_N2_Msk | RCC_PLL2DIVR_P2_Msk);
+    pll2divr |= pllbits;
+    RCC->PLL2DIVR = pll2divr;
+    RCC->CR |= RCC_CR_PLL2ON;
+    while ((RCC->CR & RCC_CR_PLL2RDY) != RCC_CR_PLL2RDY);
+
+    m_group_config.smpr[0] = rate != SClock::Rate::R96K ? ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_12P5)
+                                                        : ADC_SMPR1_SMP_AN5(ADC_SMPR_SMP_2P5);
+
+    adcStart(m_driver, &m_config);
+#elif defined(TARGET_PLATFORM_L4)
+    std::array<std::array<uint32_t, 3>, 6> m_rate_presets = {{
+        // PLLSAI2 sources MSI of 4MHz, divided by PLLM of /1 = 4MHz.
+        // 4MHz is then multiplied by PLLSAI2N (x8 to x86), with result
+        // between 64 and 344 MHz.
+        //
+        // SAI2N MUST BE AT LEAST 16 TO MAKE 64MHz MINIMUM.
+        //
+        // That is then divided by PLLSAI2R:
+        //     R of 0 = /2; 1 = /4, 2 = /6, 3 = /8.
+        // PLLSAI2 then feeds into the ADC, which has a prescaler of /10.
+        // Finally, the ADC's SMP value produces the desired sample rate.
+        //
+        // 4MHz * N / R / 10 / SMP = sample rate.
+        //
+        // With oversampling, must create faster clock
+        // (x2 oversampling requires x2 sample rate clock).
+        //
+        //  Rate   PLLSAI2N  R  SMPR
+        {/* 8k  */ 16,       1, ADC_SMPR_SMP_12P5}, // R3=32k (min), R1=64k
+        {/* 16k */ 16,       0, ADC_SMPR_SMP_12P5},
+        {/* 20k */ 20,       0, ADC_SMPR_SMP_12P5},
+        {/* 32k */ 32,       0, ADC_SMPR_SMP_12P5},
+        {/* 48k */ 48,       0, ADC_SMPR_SMP_12P5},
+        {/* 96k */ 73,       0, ADC_SMPR_SMP_6P5}   // Technically 96.05263kS/s
+    }};
+
+    auto& preset = m_rate_presets[static_cast<int>(rate)];
+    auto pllnr = (preset[0] << RCC_PLLSAI2CFGR_PLLSAI2N_Pos) |
+                 (preset[1] << RCC_PLLSAI2CFGR_PLLSAI2R_Pos);
+    auto smpr = preset[2];
+
+    // Adjust PLLSAI2
+    RCC->CR &= ~(RCC_CR_PLLSAI2ON);
+    while ((RCC->CR & RCC_CR_PLLSAI2RDY) == RCC_CR_PLLSAI2RDY);
+    RCC->PLLSAI2CFGR = (RCC->PLLSAI2CFGR & ~(RCC_PLLSAI2CFGR_PLLSAI2N_Msk | RCC_PLLSAI2CFGR_PLLSAI2R_Msk)) | pllnr;
+    RCC->CR |= RCC_CR_PLLSAI2ON;
+    while ((RCC->CR & RCC_CR_PLLSAI2RDY) != RCC_CR_PLLSAI2RDY);
+
+    m_group_config.smpr[0] = ADC_SMPR1_SMP_AN5(smpr);
+
+    // 8x oversample
+    m_group_config.cfgr2 = ADC_CFGR2_ROVSE | (2 << ADC_CFGR2_OVSR_Pos) | (3 << ADC_CFGR2_OVSS_Pos);
+    m_group_config2.cfgr2 = ADC_CFGR2_ROVSE | (2 << ADC_CFGR2_OVSR_Pos) | (3 << ADC_CFGR2_OVSS_Pos);
+#endif
+}
+
+void ADC::setOperation(ADC::Operation operation)
+{
+    m_operation = operation;
+}
+
+void ADC::conversionCallback(ADCDriver *driver)
+{
+    if (m_operation != nullptr) {
+        auto half_size = m_current_buffer_size / 2;
+        if (adcIsBufferComplete(driver))
+            m_operation(m_current_buffer + half_size, half_size);
+        else
+            m_operation(m_current_buffer, half_size);
+    }
+}
+
diff --git a/firmware/source/periph/adc.hpp b/firmware/source/periph/adc.hpp
new file mode 100644 (file)
index 0000000..5f7fa08
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * @file adc.hpp
+ * @brief Manages signal reading through the ADC.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_ADC_HPP_
+#define STMDSP_ADC_HPP_
+
+#include "hal.h"
+#include "sclock.hpp"
+
+#include <array>
+
+class ADC
+{
+public:
+    using Operation = void (*)(adcsample_t *buffer, size_t count);
+
+    static void begin();
+
+    static void start(adcsample_t *buffer, size_t count, Operation operation);
+    static void stop();
+
+    static adcsample_t readAlt(unsigned int id);
+
+    static void setRate(SClock::Rate rate);
+    static void setOperation(Operation operation);
+
+private:
+    static ADCDriver *m_driver;
+    static ADCDriver *m_driver2;
+
+    static const ADCConfig m_config;
+    static const ADCConfig m_config2;
+    static ADCConversionGroup m_group_config;
+    static ADCConversionGroup m_group_config2;
+
+    static adcsample_t *m_current_buffer;
+    static size_t m_current_buffer_size;
+    static Operation m_operation;
+
+public:
+    static void conversionCallback(ADCDriver *);
+};
+
+#endif // STMDSP_ADC_HPP_
+
diff --git a/firmware/source/periph/cordic.cpp b/firmware/source/periph/cordic.cpp
new file mode 100644 (file)
index 0000000..29ee068
--- /dev/null
@@ -0,0 +1,113 @@
+#include "cordic.hpp"
+#include "hal.h"
+
+#if !defined(TARGET_PLATFORM_L4)
+namespace cordic {
+
+void init()
+{
+    RCC->AHB2ENR |= RCC_AHB2ENR_CORDICEN;
+}
+
+static void prepare() {
+    while (CORDIC->CSR & CORDIC_CSR_RRDY)
+        asm("mov r0, %0" :: "r" (CORDIC->RDATA));
+}
+
+static uint32_t dtoq(double) {
+    uint32_t res;
+    asm("vcvt.s32.f64 d0, d0, #31;"
+        "vmov %0, r5, d0"
+    : "=r" (res));
+    return res;
+}
+__attribute__((naked))
+static double qtod(uint32_t) {
+    asm("eor r1, r1;"
+        "vmov d0, r0, r1;"
+        "vcvt.f64.s32 d0, d0, #31;"
+        "bx lr");
+    return 0;
+}
+__attribute__((naked))
+double mod(double, double) {
+    asm("vdiv.f64   d2, d0, d1;"
+        "vrintz.f64 d2;"
+        "vmul.f64   d1, d1, d2;"
+        "vsub.f64   d0, d0, d1;"
+        "bx lr");
+    return 0;
+}
+
+double cos(double x) {
+    x = mod(x, 2 * math::PI) / math::PI;
+    auto input = dtoq(x > 1. ? x - 2 : x);
+
+    prepare();
+    CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
+                  (6 << CORDIC_CSR_PRECISION_Pos) |
+                  (0 << CORDIC_CSR_FUNC_Pos);
+
+    CORDIC->WDATA = input;
+    CORDIC->WDATA = input;
+    while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
+
+    double cosx = qtod(CORDIC->RDATA) / x;
+    [[maybe_unused]] auto sinx = CORDIC->RDATA;
+    return cosx;
+}
+
+double sin(double x) {
+    x = mod(x, 2 * math::PI) / math::PI;
+    auto input = dtoq(x > 1. ? x - 2 : x);
+
+    prepare();
+    CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
+                  (6 << CORDIC_CSR_PRECISION_Pos) |
+                  (1 << CORDIC_CSR_FUNC_Pos);
+
+    CORDIC->WDATA = input;
+    CORDIC->WDATA = input;
+    while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
+
+    double sinx = qtod(CORDIC->RDATA) / x;
+    [[maybe_unused]] auto cosx = CORDIC->RDATA;
+    return sinx;
+}
+
+double tan(double x) {
+    x = mod(x, 2 * math::PI) / math::PI;
+    auto input = dtoq(x > 1. ? x - 2 : x);
+
+    prepare();
+    CORDIC->CSR = CORDIC_CSR_NARGS | CORDIC_CSR_NRES |
+                  (6 << CORDIC_CSR_PRECISION_Pos) |
+                  (1 << CORDIC_CSR_FUNC_Pos);
+
+    CORDIC->WDATA = input;
+    CORDIC->WDATA = input;
+    while (!(CORDIC->CSR & CORDIC_CSR_RRDY));
+
+    double sinx = qtod(CORDIC->RDATA) / x;
+    double tanx = sinx * x / qtod(CORDIC->RDATA);
+    return tanx;
+}
+
+}
+#else // L4
+#include <cmath>
+namespace cordic {
+
+void init() {}
+
+float mod(float a, float b) {
+    return a - (b * std::floor(a / b));
+}
+
+float cos(float x) { return std::cos(x); }
+float sin(float x) { return std::sin(x); }
+float tan(float x) { return std::tan(x); }
+
+}
+#endif
+
diff --git a/firmware/source/periph/cordic.hpp b/firmware/source/periph/cordic.hpp
new file mode 100644 (file)
index 0000000..5d640cc
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef CORDIC_HPP_
+#define CORDIC_HPP_
+
+namespace cordic {
+    constexpr double PI = 3.1415926535L;
+
+    void init();
+
+#if !defined(TARGET_PLATFORM_L4)
+    double mod(double n, double d);
+
+    double cos(double x);
+    double sin(double x);
+    double tan(double x);
+#else
+    float mod(float n, float d);
+
+    float cos(float x);
+    float sin(float x);
+    float tan(float x);
+#endif
+}
+
+#endif // CORDIC_HPP_
+
diff --git a/firmware/source/periph/dac.cpp b/firmware/source/periph/dac.cpp
new file mode 100644 (file)
index 0000000..35c2908
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * @file dac.cpp
+ * @brief Manages signal creation using the DAC.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "dac.hpp"
+#include "sclock.hpp"
+
+DACDriver *DAC::m_driver[2] = {
+    &DACD1, &DACD2
+};
+
+const DACConfig DAC::m_config = {
+    .init = 2048,
+    .datamode = DAC_DHRM_12BIT_RIGHT,
+    .cr = 0
+};
+
+static int dacIsDone = -1;
+static void dacEndCallback(DACDriver *dacd)
+{
+    if (dacd == &DACD2)
+        dacIsDone = dacIsBufferComplete(dacd) ? 1 : 0;
+}
+
+const DACConversionGroup DAC::m_group_config = {
+    .num_channels = 1,
+    .end_cb = dacEndCallback,
+    .error_cb = nullptr,
+#if defined(TARGET_PLATFORM_H7)
+    .trigger = 5 // TIM6_TRGO
+#elif defined(TARGET_PLATFORM_L4)
+    .trigger = 0 // TIM6_TRGO
+#endif
+};
+
+void DAC::begin()
+{
+    palSetPadMode(GPIOA, 4, PAL_STM32_MODE_ANALOG);
+    palSetPadMode(GPIOA, 5, PAL_STM32_MODE_ANALOG);
+
+    dacStart(m_driver[0], &m_config);
+    dacStart(m_driver[1], &m_config);
+}
+
+void DAC::start(int channel, dacsample_t *buffer, size_t count)
+{
+    if (channel >= 0 && channel < 2) {
+        if (channel == 1)
+            dacIsDone = -1;
+        dacStartConversion(m_driver[channel], &m_group_config, buffer, count);
+        SClock::start();
+    }
+}
+
+int DAC::sigGenWantsMore()
+{
+    if (dacIsDone != -1) {
+        int tmp = dacIsDone;
+        dacIsDone = -1;
+        return tmp;
+    } else {
+        return -1;
+    }
+}
+
+int DAC::isSigGenRunning()
+{
+    return m_driver[1]->state == DAC_ACTIVE;
+}
+
+void DAC::stop(int channel)
+{
+    if (channel >= 0 && channel < 2) {
+        dacStopConversion(m_driver[channel]);
+        SClock::stop();
+    }
+}
+
diff --git a/firmware/source/periph/dac.hpp b/firmware/source/periph/dac.hpp
new file mode 100644 (file)
index 0000000..7250a52
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * @file dac.hpp
+ * @brief Manages signal creation using the DAC.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_DAC_HPP_
+#define STMDSP_DAC_HPP_
+
+#include "hal.h"
+#undef DAC
+
+class DAC
+{
+public:
+    static void begin();
+
+    static void start(int channel, dacsample_t *buffer, size_t count);
+    static void stop(int channel);
+
+    static int sigGenWantsMore();
+    static int isSigGenRunning();
+
+private:
+    static DACDriver *m_driver[2];
+
+    static const DACConfig m_config;
+    static const DACConversionGroup m_group_config;
+};
+
+#endif // STMDSP_DAC_HPP_
+
diff --git a/firmware/source/periph/usbcfg.c b/firmware/source/periph/usbcfg.c
new file mode 100644 (file)
index 0000000..b726e23
--- /dev/null
@@ -0,0 +1,346 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+#include "hal.h"\r
+\r
+/* Virtual serial port over USB.*/\r
+SerialUSBDriver SDU1;\r
+\r
+/*\r
+ * Endpoints to be used for USBD1.\r
+ */\r
+#define USBD1_DATA_REQUEST_EP           1\r
+#define USBD1_DATA_AVAILABLE_EP         1\r
+#define USBD1_INTERRUPT_REQUEST_EP      2\r
+\r
+/*\r
+ * USB Device Descriptor.\r
+ */\r
+static const uint8_t vcom_device_descriptor_data[18] = {\r
+  USB_DESC_DEVICE       (0x0110,        /* bcdUSB (1.1).                    */\r
+                         0x02,          /* bDeviceClass (CDC).              */\r
+                         0x00,          /* bDeviceSubClass.                 */\r
+                         0x00,          /* bDeviceProtocol.                 */\r
+                         0x40,          /* bMaxPacketSize.                  */\r
+                         0x0483,        /* idVendor (ST).                   */\r
+                         0x5740,        /* idProduct.                       */\r
+                         0x0200,        /* bcdDevice.                       */\r
+                         1,             /* iManufacturer.                   */\r
+                         2,             /* iProduct.                        */\r
+                         3,             /* iSerialNumber.                   */\r
+                         1)             /* bNumConfigurations.              */\r
+};\r
+\r
+/*\r
+ * Device Descriptor wrapper.\r
+ */\r
+static const USBDescriptor vcom_device_descriptor = {\r
+  sizeof vcom_device_descriptor_data,\r
+  vcom_device_descriptor_data\r
+};\r
+\r
+/* Configuration Descriptor tree for a CDC.*/\r
+static const uint8_t vcom_configuration_descriptor_data[67] = {\r
+  /* Configuration Descriptor.*/\r
+  USB_DESC_CONFIGURATION(67,            /* wTotalLength.                    */\r
+                         0x02,          /* bNumInterfaces.                  */\r
+                         0x01,          /* bConfigurationValue.             */\r
+                         0,             /* iConfiguration.                  */\r
+                         0xC0,          /* bmAttributes (self powered).     */\r
+                         50),           /* bMaxPower (100mA).               */\r
+  /* Interface Descriptor.*/\r
+  USB_DESC_INTERFACE    (0x00,          /* bInterfaceNumber.                */\r
+                         0x00,          /* bAlternateSetting.               */\r
+                         0x01,          /* bNumEndpoints.                   */\r
+                         0x02,          /* bInterfaceClass (Communications\r
+                                           Interface Class, CDC section\r
+                                           4.2).                            */\r
+                         0x02,          /* bInterfaceSubClass (Abstract\r
+                                         Control Model, CDC section 4.3).   */\r
+                         0x01,          /* bInterfaceProtocol (AT commands,\r
+                                           CDC section 4.4).                */\r
+                         0),            /* iInterface.                      */\r
+  /* Header Functional Descriptor (CDC section 5.2.3).*/\r
+  USB_DESC_BYTE         (5),            /* bLength.                         */\r
+  USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */\r
+  USB_DESC_BYTE         (0x00),         /* bDescriptorSubtype (Header\r
+                                           Functional Descriptor.           */\r
+  USB_DESC_BCD          (0x0110),       /* bcdCDC.                          */\r
+  /* Call Management Functional Descriptor. */\r
+  USB_DESC_BYTE         (5),            /* bFunctionLength.                 */\r
+  USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */\r
+  USB_DESC_BYTE         (0x01),         /* bDescriptorSubtype (Call Management\r
+                                           Functional Descriptor).          */\r
+  USB_DESC_BYTE         (0x00),         /* bmCapabilities (D0+D1).          */\r
+  USB_DESC_BYTE         (0x01),         /* bDataInterface.                  */\r
+  /* ACM Functional Descriptor.*/\r
+  USB_DESC_BYTE         (4),            /* bFunctionLength.                 */\r
+  USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */\r
+  USB_DESC_BYTE         (0x02),         /* bDescriptorSubtype (Abstract\r
+                                           Control Management Descriptor).  */\r
+  USB_DESC_BYTE         (0x02),         /* bmCapabilities.                  */\r
+  /* Union Functional Descriptor.*/\r
+  USB_DESC_BYTE         (5),            /* bFunctionLength.                 */\r
+  USB_DESC_BYTE         (0x24),         /* bDescriptorType (CS_INTERFACE).  */\r
+  USB_DESC_BYTE         (0x06),         /* bDescriptorSubtype (Union\r
+                                           Functional Descriptor).          */\r
+  USB_DESC_BYTE         (0x00),         /* bMasterInterface (Communication\r
+                                           Class Interface).                */\r
+  USB_DESC_BYTE         (0x01),         /* bSlaveInterface0 (Data Class\r
+                                           Interface).                      */\r
+  /* Endpoint 2 Descriptor.*/\r
+  USB_DESC_ENDPOINT     (USBD1_INTERRUPT_REQUEST_EP|0x80,\r
+                         0x03,          /* bmAttributes (Interrupt).        */\r
+                         0x0008,        /* wMaxPacketSize.                  */\r
+                         0xFF),         /* bInterval.                       */\r
+  /* Interface Descriptor.*/\r
+  USB_DESC_INTERFACE    (0x01,          /* bInterfaceNumber.                */\r
+                         0x00,          /* bAlternateSetting.               */\r
+                         0x02,          /* bNumEndpoints.                   */\r
+                         0x0A,          /* bInterfaceClass (Data Class\r
+                                           Interface, CDC section 4.5).     */\r
+                         0x00,          /* bInterfaceSubClass (CDC section\r
+                                           4.6).                            */\r
+                         0x00,          /* bInterfaceProtocol (CDC section\r
+                                           4.7).                            */\r
+                         0x00),         /* iInterface.                      */\r
+  /* Endpoint 3 Descriptor.*/\r
+  USB_DESC_ENDPOINT     (USBD1_DATA_AVAILABLE_EP,       /* bEndpointAddress.*/\r
+                         0x02,          /* bmAttributes (Bulk).             */\r
+                         0x0040,        /* wMaxPacketSize.                  */\r
+                         0x00),         /* bInterval.                       */\r
+  /* Endpoint 1 Descriptor.*/\r
+  USB_DESC_ENDPOINT     (USBD1_DATA_REQUEST_EP|0x80,    /* bEndpointAddress.*/\r
+                         0x02,          /* bmAttributes (Bulk).             */\r
+                         0x0040,        /* wMaxPacketSize.                  */\r
+                         0x00)          /* bInterval.                       */\r
+};\r
+\r
+/*\r
+ * Configuration Descriptor wrapper.\r
+ */\r
+static const USBDescriptor vcom_configuration_descriptor = {\r
+  sizeof vcom_configuration_descriptor_data,\r
+  vcom_configuration_descriptor_data\r
+};\r
+\r
+/*\r
+ * U.S. English language identifier.\r
+ */\r
+static const uint8_t vcom_string0[] = {\r
+  USB_DESC_BYTE(4),                     /* bLength.                         */\r
+  USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */\r
+  USB_DESC_WORD(0x0409)                 /* wLANGID (U.S. English).          */\r
+};\r
+\r
+/*\r
+ * Vendor string.\r
+ */\r
+static const uint8_t vcom_string1[] = {\r
+  USB_DESC_BYTE(38),                    /* bLength.                         */\r
+  USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */\r
+  'S', 0, 'T', 0, 'M', 0, 'i', 0, 'c', 0, 'r', 0, 'o', 0, 'e', 0,\r
+  'l', 0, 'e', 0, 'c', 0, 't', 0, 'r', 0, 'o', 0, 'n', 0, 'i', 0,\r
+  'c', 0, 's', 0\r
+};\r
+\r
+/*\r
+ * Device Description string.\r
+ */\r
+static const uint8_t vcom_string2[] = {\r
+  USB_DESC_BYTE(56),                    /* bLength.                         */\r
+  USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */\r
+  'C', 0, 'h', 0, 'i', 0, 'b', 0, 'i', 0, 'O', 0, 'S', 0, '/', 0,\r
+  'R', 0, 'T', 0, ' ', 0, 'V', 0, 'i', 0, 'r', 0, 't', 0, 'u', 0,\r
+  'a', 0, 'l', 0, ' ', 0, 'C', 0, 'O', 0, 'M', 0, ' ', 0, 'P', 0,\r
+  'o', 0, 'r', 0, 't', 0\r
+};\r
+\r
+/*\r
+ * Serial Number string.\r
+ */\r
+static const uint8_t vcom_string3[] = {\r
+  USB_DESC_BYTE(8),                     /* bLength.                         */\r
+  USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType.                 */\r
+  '0' + CH_KERNEL_MAJOR, 0,\r
+  '0' + CH_KERNEL_MINOR, 0,\r
+  '0' + CH_KERNEL_PATCH, 0\r
+};\r
+\r
+/*\r
+ * Strings wrappers array.\r
+ */\r
+static const USBDescriptor vcom_strings[] = {\r
+  {sizeof vcom_string0, vcom_string0},\r
+  {sizeof vcom_string1, vcom_string1},\r
+  {sizeof vcom_string2, vcom_string2},\r
+  {sizeof vcom_string3, vcom_string3}\r
+};\r
+\r
+/*\r
+ * Handles the GET_DESCRIPTOR callback. All required descriptors must be\r
+ * handled here.\r
+ */\r
+static const USBDescriptor *get_descriptor(USBDriver *usbp,\r
+                                           uint8_t dtype,\r
+                                           uint8_t dindex,\r
+                                           uint16_t lang) {\r
+\r
+  (void)usbp;\r
+  (void)lang;\r
+  switch (dtype) {\r
+  case USB_DESCRIPTOR_DEVICE:\r
+    return &vcom_device_descriptor;\r
+  case USB_DESCRIPTOR_CONFIGURATION:\r
+    return &vcom_configuration_descriptor;\r
+  case USB_DESCRIPTOR_STRING:\r
+    if (dindex < 4)\r
+      return &vcom_strings[dindex];\r
+  }\r
+  return NULL;\r
+}\r
+\r
+/**\r
+ * @brief   IN EP1 state.\r
+ */\r
+static USBInEndpointState ep1instate;\r
+\r
+/**\r
+ * @brief   OUT EP1 state.\r
+ */\r
+static USBOutEndpointState ep1outstate;\r
+\r
+/**\r
+ * @brief   EP1 initialization structure (both IN and OUT).\r
+ */\r
+static const USBEndpointConfig ep1config = {\r
+  USB_EP_MODE_TYPE_BULK,\r
+  NULL,\r
+  sduDataTransmitted,\r
+  sduDataReceived,\r
+  0x0040,\r
+  0x0040,\r
+  &ep1instate,\r
+  &ep1outstate,\r
+  1,\r
+  NULL\r
+};\r
+\r
+/**\r
+ * @brief   IN EP2 state.\r
+ */\r
+static USBInEndpointState ep2instate;\r
+\r
+/**\r
+ * @brief   EP2 initialization structure (IN only).\r
+ */\r
+static const USBEndpointConfig ep2config = {\r
+  USB_EP_MODE_TYPE_INTR,\r
+  NULL,\r
+  sduInterruptTransmitted,\r
+  NULL,\r
+  0x0010,\r
+  0x0000,\r
+  &ep2instate,\r
+  NULL,\r
+  1,\r
+  NULL\r
+};\r
+\r
+/*\r
+ * Handles the USB driver global events.\r
+ */\r
+static void usb_event(USBDriver *usbp, usbevent_t event) {\r
+  extern SerialUSBDriver SDU1;\r
+\r
+  switch (event) {\r
+  case USB_EVENT_ADDRESS:\r
+    return;\r
+  case USB_EVENT_CONFIGURED:\r
+    chSysLockFromISR();\r
+\r
+    /* Enables the endpoints specified into the configuration.\r
+       Note, this callback is invoked from an ISR so I-Class functions\r
+       must be used.*/\r
+    usbInitEndpointI(usbp, USBD1_DATA_REQUEST_EP, &ep1config);\r
+    usbInitEndpointI(usbp, USBD1_INTERRUPT_REQUEST_EP, &ep2config);\r
+\r
+    /* Resetting the state of the CDC subsystem.*/\r
+    sduConfigureHookI(&SDU1);\r
+\r
+    chSysUnlockFromISR();\r
+    return;\r
+  case USB_EVENT_RESET:\r
+    /* Falls into.*/\r
+  case USB_EVENT_UNCONFIGURED:\r
+    /* Falls into.*/\r
+  case USB_EVENT_SUSPEND:\r
+    chSysLockFromISR();\r
+\r
+    /* Disconnection event on suspend.*/\r
+    sduSuspendHookI(&SDU1);\r
+\r
+    chSysUnlockFromISR();\r
+    return;\r
+  case USB_EVENT_WAKEUP:\r
+    chSysLockFromISR();\r
+\r
+    /* Connection event on wakeup.*/\r
+    sduWakeupHookI(&SDU1);\r
+\r
+    chSysUnlockFromISR();\r
+    return;\r
+  case USB_EVENT_STALLED:\r
+    return;\r
+  }\r
+  return;\r
+}\r
+\r
+/*\r
+ * Handles the USB driver global events.\r
+ */\r
+static void sof_handler(USBDriver *usbp) {\r
+\r
+  (void)usbp;\r
+\r
+  osalSysLockFromISR();\r
+  sduSOFHookI(&SDU1);\r
+  osalSysUnlockFromISR();\r
+}\r
+\r
+/*\r
+ * USB driver configuration.\r
+ */\r
+const USBConfig usbcfg = {\r
+  usb_event,\r
+  get_descriptor,\r
+  sduRequestsHook,\r
+  sof_handler\r
+};\r
+\r
+/*\r
+ * Serial over USB driver configuration.\r
+ */\r
+const SerialUSBConfig serusbcfg = {\r
+#if defined(TARGET_PLATFORM_H7)\r
+  &USBD2,\r
+#else\r
+  &USBD1,\r
+#endif\r
+  USBD1_DATA_REQUEST_EP,\r
+  USBD1_DATA_AVAILABLE_EP,\r
+  USBD1_INTERRUPT_REQUEST_EP\r
+};\r
diff --git a/firmware/source/periph/usbcfg.h b/firmware/source/periph/usbcfg.h
new file mode 100644 (file)
index 0000000..2fceccb
--- /dev/null
@@ -0,0 +1,28 @@
+/*\r
+    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\r
+\r
+    Licensed under the Apache License, Version 2.0 (the "License");\r
+    you may not use this file except in compliance with the License.\r
+    You may obtain a copy of the License at\r
+\r
+        http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+    Unless required by applicable law or agreed to in writing, software\r
+    distributed under the License is distributed on an "AS IS" BASIS,\r
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+    See the License for the specific language governing permissions and\r
+    limitations under the License.\r
+*/\r
+\r
+#ifndef USBCFG_H\r
+#define USBCFG_H\r
+\r
+#include "hal.h"\r
+\r
+extern const USBConfig usbcfg;\r
+extern SerialUSBConfig serusbcfg;\r
+extern SerialUSBDriver SDU1;\r
+\r
+#endif  /* USBCFG_H */\r
+\r
+/** @} */\r
diff --git a/firmware/source/periph/usbserial.cpp b/firmware/source/periph/usbserial.cpp
new file mode 100644 (file)
index 0000000..775a911
--- /dev/null
@@ -0,0 +1,52 @@
+/**
+ * @file usbserial.cpp
+ * @brief Wrapper for ChibiOS's SerialUSBDriver.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "usbserial.hpp"
+
+SerialUSBDriver *USBSerial::m_driver = &SDU1;
+
+void USBSerial::begin()
+{
+    palSetPadMode(GPIOA, 11, PAL_MODE_ALTERNATE(10));
+    palSetPadMode(GPIOA, 12, PAL_MODE_ALTERNATE(10));
+
+    sduObjectInit(m_driver);
+    sduStart(m_driver, &serusbcfg);
+
+    // Reconnect bus so device can re-enumerate on reset
+    usbDisconnectBus(serusbcfg.usbp);
+    chThdSleepMilliseconds(1500);
+    usbStart(serusbcfg.usbp, &usbcfg);
+    usbConnectBus(serusbcfg.usbp);
+}
+
+bool USBSerial::isActive()
+{
+    if (auto config = m_driver->config; config != nullptr) {
+        if (auto usbp = config->usbp; usbp != nullptr)
+            return usbp->state == USB_ACTIVE && !ibqIsEmptyI(&m_driver->ibqueue);
+    }
+
+    return false;
+}
+
+size_t USBSerial::read(unsigned char *buffer, size_t count)
+{
+    auto bss = reinterpret_cast<BaseSequentialStream *>(m_driver);
+    return streamRead(bss, buffer, count);
+}
+
+size_t USBSerial::write(const unsigned char *buffer, size_t count)
+{
+    auto bss = reinterpret_cast<BaseSequentialStream *>(m_driver);
+    return streamWrite(bss, buffer, count);
+}
+
diff --git a/firmware/source/periph/usbserial.hpp b/firmware/source/periph/usbserial.hpp
new file mode 100644 (file)
index 0000000..58113c9
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * @file usbserial.hpp
+ * @brief Wrapper for ChibiOS's SerialUSBDriver.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_USBSERIAL_HPP_
+#define STMDSP_USBSERIAL_HPP_
+
+#include "usbcfg.h"
+
+class USBSerial
+{
+public:
+    static void begin();
+
+    static bool isActive();
+
+    static size_t read(unsigned char *buffer, size_t count);
+    static size_t write(const unsigned char *buffer, size_t count);
+
+private:
+    static SerialUSBDriver *m_driver;
+};
+
+#endif // STMDSP_USBSERIAL_HPP_
+
diff --git a/firmware/source/runstatus.hpp b/firmware/source/runstatus.hpp
new file mode 100644 (file)
index 0000000..ab269b4
--- /dev/null
@@ -0,0 +1,11 @@
+// Run status
+//
+enum class RunStatus : char
+{
+    Idle = '1',
+    Running,
+    Recovering
+};
+
+extern RunStatus run_status;
+
diff --git a/firmware/source/samplebuffer.cpp b/firmware/source/samplebuffer.cpp
new file mode 100644 (file)
index 0000000..74c6778
--- /dev/null
@@ -0,0 +1,117 @@
+/**
+ * @file samplebuffer.cpp
+ * @brief Manages ADC/DAC buffer data.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "samplebuffer.hpp"
+
+SampleBuffer::SampleBuffer(Sample *buffer) :
+    m_buffer(buffer) {}
+
+void SampleBuffer::clear() {
+    std::fill(m_buffer, m_buffer + m_size, 2048);
+}
+__attribute__((section(".convcode")))
+void SampleBuffer::modify(Sample *data, unsigned int srcsize) {
+    auto size = srcsize < m_size ? srcsize : m_size;
+    size = (size + 15) & (~15);
+
+    m_modified = m_buffer;
+    const int *src = reinterpret_cast<const int *>(data);
+    const int * const srcend = src + (size / 2);
+    int *dst = reinterpret_cast<int *>(m_buffer);
+    do {
+        int a = src[0];
+        int b = src[1];
+        int c = src[2];
+        int d = src[3];
+        int e = src[4];
+        int f = src[5];
+        int g = src[6];
+        int h = src[7];
+        dst[0] = a;
+        dst[1] = b;
+        dst[2] = c;
+        dst[3] = d;
+        dst[4] = e;
+        dst[5] = f;
+        dst[6] = g;
+        dst[7] = h;
+        src += 8;
+        dst += 8;
+    } while (src < srcend);
+}
+__attribute__((section(".convcode")))
+void SampleBuffer::midmodify(Sample *data, unsigned int srcsize) {
+    auto size = srcsize < m_size / 2 ? srcsize : m_size / 2;
+    size = (size + 15) & (~15);
+
+    m_modified = middata();
+    const int *src = reinterpret_cast<const int *>(data);
+    const int * const srcend = src + (size / 2);
+    int *dst = reinterpret_cast<int *>(middata());
+    do {
+        int a = src[0];
+        int b = src[1];
+        int c = src[2];
+        int d = src[3];
+        int e = src[4];
+        int f = src[5];
+        int g = src[6];
+        int h = src[7];
+        dst[0] = a;
+        dst[1] = b;
+        dst[2] = c;
+        dst[3] = d;
+        dst[4] = e;
+        dst[5] = f;
+        dst[6] = g;
+        dst[7] = h;
+        src += 8;
+        dst += 8;
+    } while (src < srcend);
+}
+
+void SampleBuffer::setModified() {
+    m_modified = m_buffer;
+}
+
+void SampleBuffer::setMidmodified() {
+    m_modified = middata();
+}
+
+void SampleBuffer::setSize(unsigned int size) {
+    m_size = size < MAX_SAMPLE_BUFFER_SIZE ? size : MAX_SAMPLE_BUFFER_SIZE;
+}
+
+__attribute__((section(".convcode")))
+Sample *SampleBuffer::data() {
+    return m_buffer;
+}
+__attribute__((section(".convcode")))
+Sample *SampleBuffer::middata() {
+    return m_buffer + m_size / 2;
+}
+uint8_t *SampleBuffer::bytedata() {
+    return reinterpret_cast<uint8_t *>(m_buffer);
+}
+
+Sample *SampleBuffer::modified() {
+    auto m = m_modified;
+    m_modified = nullptr;
+    return m;
+}
+__attribute__((section(".convcode")))
+unsigned int SampleBuffer::size() const {
+    return m_size;
+}
+unsigned int SampleBuffer::bytesize() const {
+    return m_size * sizeof(Sample);
+}
+
diff --git a/firmware/source/samplebuffer.hpp b/firmware/source/samplebuffer.hpp
new file mode 100644 (file)
index 0000000..d13023a
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * @file samplebuffer.hpp
+ * @brief Manages ADC/DAC buffer data.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef SAMPLEBUFFER_HPP_
+#define SAMPLEBUFFER_HPP_
+
+#include <array>
+#include <cstdint>
+
+using Sample = uint16_t;
+
+constexpr unsigned int MAX_SAMPLE_BUFFER_BYTESIZE = sizeof(Sample) * 8192;
+constexpr unsigned int MAX_SAMPLE_BUFFER_SIZE = MAX_SAMPLE_BUFFER_BYTESIZE / sizeof(Sample);
+
+class SampleBuffer
+{
+public:
+    SampleBuffer(Sample *buffer);
+
+    void clear();
+
+    void modify(Sample *data, unsigned int srcsize);
+    void midmodify(Sample *data, unsigned int srcsize);
+    void setModified();
+    void setMidmodified();
+    Sample *modified();
+
+    Sample *data();
+    Sample *middata();
+    uint8_t *bytedata();
+
+    void setSize(unsigned int size);
+    unsigned int size() const;
+    unsigned int bytesize() const;
+
+private:
+    Sample *m_buffer = nullptr;
+    unsigned int m_size = MAX_SAMPLE_BUFFER_SIZE;
+    Sample *m_modified = nullptr;
+};
+
+#endif // SAMPLEBUFFER_HPP_
+
diff --git a/firmware/source/samples.cpp b/firmware/source/samples.cpp
new file mode 100644 (file)
index 0000000..cfbf835
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * @file samples.cpp
+ * @brief Provides sample buffers for inputs and outputs.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "samples.hpp"
+
+#include "ch.h"
+#include "hal.h"
+
+#include <cstdint>
+
+static_assert(sizeof(adcsample_t) == sizeof(uint16_t));
+static_assert(sizeof(dacsample_t) == sizeof(uint16_t));
+
+#if defined(TARGET_PLATFORM_H7)
+__attribute__((section(".convdata")))
+SampleBuffer Samples::In (reinterpret_cast<Sample *>(0x38000000)); // 16k
+__attribute__((section(".convdata")))
+SampleBuffer Samples::Out (reinterpret_cast<Sample *>(0x30004000)); // 16k
+SampleBuffer Samples::SigGen (reinterpret_cast<Sample *>(0x30000000)); // 16k
+#else
+__attribute__((section(".convdata")))
+SampleBuffer Samples::In (reinterpret_cast<Sample *>(0x20008000)); // 16k
+__attribute__((section(".convdata")))
+SampleBuffer Samples::Out (reinterpret_cast<Sample *>(0x2000C000)); // 16k
+SampleBuffer Samples::Generator (reinterpret_cast<Sample *>(0x20010000)); // 16k
+#endif
+
diff --git a/firmware/source/samples.hpp b/firmware/source/samples.hpp
new file mode 100644 (file)
index 0000000..6551e25
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * @file samples.hpp
+ * @brief Provides sample buffers for inputs and outputs.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_SAMPLES_HPP
+#define STMDSP_SAMPLES_HPP
+
+#include "samplebuffer.hpp"
+
+class Samples
+{
+public:
+    static SampleBuffer In;
+    static SampleBuffer Out;
+    static SampleBuffer Generator;
+};
+
+#endif // STMDSP_SAMPLES_HPP
+
diff --git a/firmware/source/sclock.cpp b/firmware/source/sclock.cpp
new file mode 100644 (file)
index 0000000..6660f95
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * @file sclock.cpp
+ * @brief Manages sampling rate clock speeds.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "sclock.hpp"
+
+GPTDriver *SClock::m_timer = &GPTD6;
+unsigned int SClock::m_div = 1;
+unsigned int SClock::m_runcount = 0;
+
+const GPTConfig SClock::m_timer_config = {
+#if defined(TARGET_PLATFORM_H7)
+    .frequency = 4800000,
+#else
+    .frequency = 36000000,
+#endif
+    .callback = nullptr,
+    .cr2 = TIM_CR2_MMS_1, /* TRGO */
+    .dier = 0
+};
+
+const std::array<unsigned int, 6> SClock::m_rate_divs = {{
+#if defined(TARGET_PLATFORM_H7)
+    /* 8k  */ 600,
+    /* 16k */ 300,
+    /* 20k */ 240,
+    /* 32k */ 150,
+    /* 48k */ 100,
+    /* 96k */ 50
+#else
+    /* 8k  */ 4500,
+    /* 16k */ 2250,
+    /* 20k */ 1800,
+    /* 32k */ 1125,
+    /* 48k */ 750,
+    /* 96k */ 375
+#endif
+}};
+
+void SClock::begin()
+{
+    gptStart(m_timer, &m_timer_config);
+}
+
+void SClock::start()
+{
+    if (m_runcount++ == 0)
+        gptStartContinuous(m_timer, m_div);
+}
+
+void SClock::stop()
+{
+    if (--m_runcount == 0)
+        gptStopTimer(m_timer);
+}
+
+void SClock::setRate(SClock::Rate rate)
+{
+    m_div = m_rate_divs[static_cast<unsigned int>(rate)];
+}
+
+unsigned int SClock::getRate()
+{
+    for (unsigned int i = 0; i < m_rate_divs.size(); ++i) {
+        if (m_rate_divs[i] == m_div)
+            return i;
+    }
+
+    return static_cast<unsigned int>(-1);
+}
+
diff --git a/firmware/source/sclock.hpp b/firmware/source/sclock.hpp
new file mode 100644 (file)
index 0000000..d5b93df
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * @file sclock.hpp
+ * @brief Manages sampling rate clock speeds.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef SCLOCK_HPP_
+#define SCLOCK_HPP_
+
+#include "hal.h"
+
+#include <array>
+
+class SClock
+{
+public:
+    enum class Rate : unsigned int {
+        R8K = 0,
+        R16K,
+        R20K,
+        R32K,
+        R48K,
+        R96K
+    };
+
+    static void begin();
+    static void start();
+    static void stop();
+
+    static void setRate(Rate rate);
+    static unsigned int getRate();
+
+private:
+    static GPTDriver *m_timer;
+    static unsigned int m_div;
+    static unsigned int m_runcount;
+    static const GPTConfig m_timer_config;
+    static const std::array<unsigned int, 6> m_rate_divs;
+};
+
+#endif // SCLOCK_HPP_
+
diff --git a/gui/LICENSE b/gui/LICENSE
new file mode 100644 (file)
index 0000000..6a34f51
--- /dev/null
@@ -0,0 +1,620 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
diff --git a/gui/Makefile b/gui/Makefile
new file mode 100644 (file)
index 0000000..0ff9c48
--- /dev/null
@@ -0,0 +1,46 @@
+CXX = g++
+
+CXXFILES := \
+    source/serial/src/serial.cc \
+    source/imgui/backends/imgui_impl_sdl2.cpp \
+    source/imgui/backends/imgui_impl_opengl2.cpp \
+    source/imgui/imgui.cpp \
+    source/imgui/imgui_draw.cpp \
+    source/imgui/imgui_tables.cpp \
+    source/imgui/imgui_widgets.cpp \
+    source/ImGuiFileDialog/ImGuiFileDialog.cpp \
+    source/ImGuiColorTextEdit/TextEditor.cpp \
+    $(wildcard source/stmdsp/*.cpp) \
+    $(wildcard source/*.cpp)
+
+CXXFLAGS := -std=c++20 -O2 \
+            -Isource -Isource/imgui -Isource/stmdsp -Isource/serial/include \
+            -Isource/ImGuiColorTextEdit -Isource/ImGuiFileDialog \
+            $(shell sdl2-config --cflags) \
+            -Wall -Wextra -pedantic #-DSTMDSP_DISABLE_FORMULAS
+
+ifeq ($(OS),Windows_NT)
+CXXFILES += source/serial/src/impl/win.cc \
+            source/serial/src/impl/list_ports/list_ports_win.cc
+CXXFLAGS += -DSTMDSP_WIN32 -Wa,-mbig-obj
+LDFLAGS = -mwindows -lSDL2 -lopengl32 -lsetupapi -lole32
+OUTPUT := stmdspgui.exe
+else
+CXXFILES += source/serial/src/impl/unix.cc \
+            source/serial/src/impl/list_ports/list_ports_linux.cc
+LDFLAGS = $(shell sdl2-config --libs) -lGL -lpthread
+OUTPUT := stmdspgui
+endif
+
+OFILES := $(patsubst %.cc, %.o, $(patsubst %.cpp, %.o, $(CXXFILES)))
+
+all: $(OUTPUT)
+
+$(OUTPUT): $(OFILES)
+       @echo "  LD    " $(OUTPUT)
+       @$(CXX) $(OFILES) -o $(OUTPUT) $(LDFLAGS)
+
+clean:
+       @echo "  CLEAN"
+       @rm -f $(OFILES) $(OUTPUT)
+
diff --git a/gui/examples/1_convolve_simple.cpp b/gui/examples/1_convolve_simple.cpp
new file mode 100644 (file)
index 0000000..95877f1
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * 1_convolve_simple.cpp
+ * Written by Clyne Sullivan.
+ *
+ * Computes a convolution in the simplest way possible. While the code is brief, it lacks many
+ * possible optimizations. The convolution's result will not fill the output buffer either, as the
+ * transient response is not calculated.
+ */
+
+Sample* process_data(Samples samples)
+{
+    // Define our output buffer.
+    static Samples buffer;
+
+    // Define our filter
+    constexpr unsigned int filter_size = 3;
+       float filter[filter_size] = {
+        0.3333, 0.3333, 0.3333
+    };
+
+    // Begin convolving:
+    // SIZE is the size of the sample buffer.
+    for (int n = 0; n < SIZE - (filter_size - 1); n++) {
+        buffer[n] = 0;
+        for (int k = 0; k < filter_size; k++)
+            buffer[n] += samples[n + k] * filter[k];
+    }
+
+    return buffer;
+}
diff --git a/gui/examples/2_convolve_overlap_save.cpp b/gui/examples/2_convolve_overlap_save.cpp
new file mode 100644 (file)
index 0000000..5651f3e
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * 2_convolve_overlap_save.cpp
+ * Written by Clyne Sullivan.
+ *
+ * This convolution examples takes an overlap-save approach, where samples from the previous run
+ * are saved so that the overall operation is not interrupted (i.e. the observed output will
+ * transition smoothly between processed "chunks").
+ *
+ * Note that there are still improvements that can be made to the code; for example, notice every
+ * spot where an integer/float conversion is necessary. Operations like these may slow down the
+ * computation.
+ */
+
+Sample* process_data(Samples samples)
+{
+    static Samples buffer;
+
+    constexpr unsigned int filter_size = 3;
+       float filter[filter_size] = {
+        0.3333, 0.3333, 0.3333
+    };
+
+    // Keep a buffer of extra samples for overlap-save
+    static Sample prev[filter_size];
+
+    for (int n = 0; n < SIZE; n++) {
+        buffer[n] = 0;
+
+        for (int k = 0; k < filter_size; k++) {
+            int i = n - (filter_size - 1) + k;
+
+            // If i is >= 0, access current sample buffer.
+            // If i is < 0, provide the previous samples from the 'prev' buffer
+            if (i >= 0)
+                buffer[n] += samples[i] * filter[k];
+            else
+                buffer[n] += prev[filter_size - 1 + i] * filter[k];
+        }
+    }
+
+    // Save samples for the next convolution run
+    for (int i = 0; i < filter_size; i++)
+        prev[i] = samples[SIZE - filter_size + i];
+
+    return buffer;
+}
+
diff --git a/gui/examples/3_fir.cpp b/gui/examples/3_fir.cpp
new file mode 100644 (file)
index 0000000..b6d8751
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * 3_fir.cpp
+ * Written by Clyne Sullivan.
+ *
+ * The below code was written for applying FIR filters. While this is still essentially an overlap-
+ * save convolution, other optimizations have been made to allow for larger filters to be applied
+ * within the available execution time. Samples are also normalized so that they center around zero.
+ */
+
+Sample* process_data(Samples samples)
+{
+    static Samples buffer;
+
+       // Define the filter:
+       constexpr unsigned int filter_size = 3;
+       static float filter[filter_size] = {
+        // Put filter values here (note: precision will be truncated for 'float' size).
+        0.3333, 0.3333, 0.3333
+       };
+
+    // Do an overlap-save convolution
+    static Sample prev[filter_size];
+
+    for (int n = 0; n < SIZE; n++) {
+        // Using a float variable for accumulation allows for better code optimization
+        float v = 0;
+
+        for (int k = 0; k < filter_size; k++) {
+            int i = n - (filter_size - 1) + k;
+
+            auto s = i >= 0 ? samples[i] : prev[filter_size - 1 + i];
+                       // Sample values are 0 to 4095. Below, the original sample is normalized to a -1.0 to
+            // 1.0 range for calculation.
+            v += (s / 2048.f - 1) * filter[k];
+        }
+
+        // Return value to sample range of 0-4095.
+        buffer[n] = (v + 1) * 2048.f;
+    }
+
+    // Save samples for next convolution
+    for (int i = 0; i < filter_size; i++)
+        prev[i] = samples[SIZE - filter_size + i];
+
+    return buffer;
+}
+
diff --git a/gui/examples/4_fir_pro.cpp b/gui/examples/4_fir_pro.cpp
new file mode 100644 (file)
index 0000000..1771cd5
--- /dev/null
@@ -0,0 +1,478 @@
+#include <cstdint>
+using float32_t = float;
+
+typedef struct
+{
+    uint16_t numTaps;     /**< number of filter coefficients in the filter. */
+    float32_t *pState;    /**< points to the state variable array. The array is of length numTaps+blockSize-1. */
+    float32_t *pCoeffs;   /**< points to the coefficient array. The array is of length numTaps. */
+} arm_fir_instance_f32;
+
+static void arm_fir_f32(const arm_fir_instance_f32 * S, float32_t * pSrc, float32_t * pDst, uint32_t blockSize);
+
+Sample* process_data(Samples samples)
+{
+       // 1. Define our array sizes (Be sure to set Run > Set buffer size... to below value!)
+       constexpr unsigned int buffer_size = 500;
+       constexpr unsigned int filter_size = 100;
+
+       // 2. Define our filter and the working arrays
+       static float filter[filter_size] = {
+               .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+               .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+               .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+               .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+               .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+               .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+               .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+               .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+               .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+               .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f
+       };
+       static float input[buffer_size];
+       static float output[buffer_size];
+       static float working[buffer_size + filter_size];
+
+       // 3. Scale 0-4095 interger sample values to +/- 1.0 floats
+       for (unsigned int i = 0; i < SIZE; i++)
+               input[i] = (samples[i] - 2048) / 2048.f;
+
+       // 4. Compute the FIR
+       arm_fir_instance_f32 fir { filter_size, working, filter };
+       arm_fir_f32(&fir, input, output, SIZE);
+
+       // 5. Convert float results back to 0-4095 range for output
+       for (unsigned int i = 0; i < SIZE; i++)
+               samples[i] = output[i] * 2048.f + 2048;
+
+    return samples;
+}
+
+// Below taken from the CMSIS DSP Library (find it on GitHub)
+void arm_fir_f32(
+       const arm_fir_instance_f32 * S,
+       float32_t * pSrc,
+       float32_t * pDst,
+       uint32_t blockSize)
+{
+   float32_t *pState = S->pState;                 /* State pointer */
+   float32_t *pCoeffs = S->pCoeffs;               /* Coefficient pointer */
+   float32_t *pStateCurnt;                        /* Points to the current sample of the state */
+   float32_t *px, *pb;                            /* Temporary pointers for state and coefficient buffers */
+   float32_t acc0, acc1, acc2, acc3, acc4, acc5, acc6, acc7;     /* Accumulators */
+   float32_t x0, x1, x2, x3, x4, x5, x6, x7, c0;  /* Temporary variables to hold state and coefficient values */
+   uint32_t numTaps = S->numTaps;                 /* Number of filter coefficients in the filter */
+   uint32_t i, tapCnt, blkCnt;                    /* Loop counters */
+   float32_t p0,p1,p2,p3,p4,p5,p6,p7;             /* Temporary product values */
+
+   /* S->pState points to state array which contains previous frame (numTaps - 1) samples */
+   /* pStateCurnt points to the location where the new input data should be written */
+   pStateCurnt = &(S->pState[(numTaps - 1u)]);
+
+   /* Apply loop unrolling and compute 8 output values simultaneously.  
+    * The variables acc0 ... acc7 hold output values that are being computed:  
+    *  
+    *    acc0 =  b[numTaps-1] * x[n-numTaps-1] + b[numTaps-2] * x[n-numTaps-2] + b[numTaps-3] * x[n-numTaps-3] +...+ b[0] * x[0]  
+    *    acc1 =  b[numTaps-1] * x[n-numTaps] +   b[numTaps-2] * x[n-numTaps-1] + b[numTaps-3] * x[n-numTaps-2] +...+ b[0] * x[1]  
+    *    acc2 =  b[numTaps-1] * x[n-numTaps+1] + b[numTaps-2] * x[n-numTaps] +   b[numTaps-3] * x[n-numTaps-1] +...+ b[0] * x[2]  
+    *    acc3 =  b[numTaps-1] * x[n-numTaps+2] + b[numTaps-2] * x[n-numTaps+1] + b[numTaps-3] * x[n-numTaps]   +...+ b[0] * x[3]  
+    */
+   blkCnt = blockSize >> 3;
+
+   /* First part of the processing with loop unrolling.  Compute 8 outputs at a time.  
+   ** a second loop below computes the remaining 1 to 7 samples. */
+   while(blkCnt > 0u)
+   {
+      /* Copy four new input samples into the state buffer */
+      *pStateCurnt++ = *pSrc++;
+      *pStateCurnt++ = *pSrc++;
+      *pStateCurnt++ = *pSrc++;
+      *pStateCurnt++ = *pSrc++;
+
+      /* Set all accumulators to zero */
+      acc0 = 0.0f;
+      acc1 = 0.0f;
+      acc2 = 0.0f;
+      acc3 = 0.0f;
+      acc4 = 0.0f;
+      acc5 = 0.0f;
+      acc6 = 0.0f;
+      acc7 = 0.0f;             
+
+      /* Initialize state pointer */
+      px = pState;
+
+      /* Initialize coeff pointer */
+      pb = (pCoeffs);          
+   
+      /* This is separated from the others to avoid 
+       * a call to __aeabi_memmove which would be slower
+       */
+      *pStateCurnt++ = *pSrc++;
+      *pStateCurnt++ = *pSrc++;
+      *pStateCurnt++ = *pSrc++;
+      *pStateCurnt++ = *pSrc++;
+
+      /* Read the first seven samples from the state buffer:  x[n-numTaps], x[n-numTaps-1], x[n-numTaps-2] */
+      x0 = *px++;
+      x1 = *px++;
+      x2 = *px++;
+      x3 = *px++;
+      x4 = *px++;
+      x5 = *px++;
+      x6 = *px++;
+
+      /* Loop unrolling.  Process 8 taps at a time. */
+      tapCnt = numTaps >> 3u;
+      
+      /* Loop over the number of taps.  Unroll by a factor of 8.  
+       ** Repeat until we've computed numTaps-8 coefficients. */
+      while(tapCnt > 0u)
+      {
+         /* Read the b[numTaps-1] coefficient */
+         c0 = *(pb++);
+
+         /* Read x[n-numTaps-3] sample */
+         x7 = *(px++);
+
+         /* acc0 +=  b[numTaps-1] * x[n-numTaps] */
+         p0 = x0 * c0;
+
+         /* acc1 +=  b[numTaps-1] * x[n-numTaps-1] */
+         p1 = x1 * c0;
+
+         /* acc2 +=  b[numTaps-1] * x[n-numTaps-2] */
+         p2 = x2 * c0;
+
+         /* acc3 +=  b[numTaps-1] * x[n-numTaps-3] */
+         p3 = x3 * c0;
+
+         /* acc4 +=  b[numTaps-1] * x[n-numTaps-4] */
+         p4 = x4 * c0;
+
+         /* acc1 +=  b[numTaps-1] * x[n-numTaps-5] */
+         p5 = x5 * c0;
+
+         /* acc2 +=  b[numTaps-1] * x[n-numTaps-6] */
+         p6 = x6 * c0;
+
+         /* acc3 +=  b[numTaps-1] * x[n-numTaps-7] */
+         p7 = x7 * c0;
+         
+         /* Read the b[numTaps-2] coefficient */
+         c0 = *(pb++);
+
+         /* Read x[n-numTaps-4] sample */
+         x0 = *(px++);
+         
+         acc0 += p0;
+         acc1 += p1;
+         acc2 += p2;
+         acc3 += p3;
+         acc4 += p4;
+         acc5 += p5;
+         acc6 += p6;
+         acc7 += p7;
+
+
+         /* Perform the multiply-accumulate */
+         p0 = x1 * c0;
+         p1 = x2 * c0;   
+         p2 = x3 * c0;   
+         p3 = x4 * c0;   
+         p4 = x5 * c0;   
+         p5 = x6 * c0;   
+         p6 = x7 * c0;   
+         p7 = x0 * c0;   
+         
+         /* Read the b[numTaps-3] coefficient */
+         c0 = *(pb++);
+
+         /* Read x[n-numTaps-5] sample */
+         x1 = *(px++);
+         
+         acc0 += p0;
+         acc1 += p1;
+         acc2 += p2;
+         acc3 += p3;
+         acc4 += p4;
+         acc5 += p5;
+         acc6 += p6;
+         acc7 += p7;
+
+         /* Perform the multiply-accumulates */      
+         p0 = x2 * c0;
+         p1 = x3 * c0;   
+         p2 = x4 * c0;   
+         p3 = x5 * c0;   
+         p4 = x6 * c0;   
+         p5 = x7 * c0;   
+         p6 = x0 * c0;   
+         p7 = x1 * c0;   
+
+         /* Read the b[numTaps-4] coefficient */
+         c0 = *(pb++);
+
+         /* Read x[n-numTaps-6] sample */
+         x2 = *(px++);
+         
+         acc0 += p0;
+         acc1 += p1;
+         acc2 += p2;
+         acc3 += p3;
+         acc4 += p4;
+         acc5 += p5;
+         acc6 += p6;
+         acc7 += p7;
+
+         /* Perform the multiply-accumulates */      
+         p0 = x3 * c0;
+         p1 = x4 * c0;   
+         p2 = x5 * c0;   
+         p3 = x6 * c0;   
+         p4 = x7 * c0;   
+         p5 = x0 * c0;   
+         p6 = x1 * c0;   
+         p7 = x2 * c0;   
+
+         /* Read the b[numTaps-4] coefficient */
+         c0 = *(pb++);
+
+         /* Read x[n-numTaps-6] sample */
+         x3 = *(px++);
+         
+         acc0 += p0;
+         acc1 += p1;
+         acc2 += p2;
+         acc3 += p3;
+         acc4 += p4;
+         acc5 += p5;
+         acc6 += p6;
+         acc7 += p7;
+
+         /* Perform the multiply-accumulates */      
+         p0 = x4 * c0;
+         p1 = x5 * c0;   
+         p2 = x6 * c0;   
+         p3 = x7 * c0;   
+         p4 = x0 * c0;   
+         p5 = x1 * c0;   
+         p6 = x2 * c0;   
+         p7 = x3 * c0;   
+
+         /* Read the b[numTaps-4] coefficient */
+         c0 = *(pb++);
+
+         /* Read x[n-numTaps-6] sample */
+         x4 = *(px++);
+         
+         acc0 += p0;
+         acc1 += p1;
+         acc2 += p2;
+         acc3 += p3;
+         acc4 += p4;
+         acc5 += p5;
+         acc6 += p6;
+         acc7 += p7;
+
+         /* Perform the multiply-accumulates */      
+         p0 = x5 * c0;
+         p1 = x6 * c0;   
+         p2 = x7 * c0;   
+         p3 = x0 * c0;   
+         p4 = x1 * c0;   
+         p5 = x2 * c0;   
+         p6 = x3 * c0;   
+         p7 = x4 * c0;   
+
+         /* Read the b[numTaps-4] coefficient */
+         c0 = *(pb++);
+
+         /* Read x[n-numTaps-6] sample */
+         x5 = *(px++);
+         
+         acc0 += p0;
+         acc1 += p1;
+         acc2 += p2;
+         acc3 += p3;
+         acc4 += p4;
+         acc5 += p5;
+         acc6 += p6;
+         acc7 += p7;
+
+         /* Perform the multiply-accumulates */      
+         p0 = x6 * c0;
+         p1 = x7 * c0;   
+         p2 = x0 * c0;   
+         p3 = x1 * c0;   
+         p4 = x2 * c0;   
+         p5 = x3 * c0;   
+         p6 = x4 * c0;   
+         p7 = x5 * c0;   
+
+         /* Read the b[numTaps-4] coefficient */
+         c0 = *(pb++);
+
+         /* Read x[n-numTaps-6] sample */
+         x6 = *(px++);
+         
+         acc0 += p0;
+         acc1 += p1;
+         acc2 += p2;
+         acc3 += p3;
+         acc4 += p4;
+         acc5 += p5;
+         acc6 += p6;
+         acc7 += p7;
+
+         /* Perform the multiply-accumulates */      
+         p0 = x7 * c0;
+         p1 = x0 * c0;   
+         p2 = x1 * c0;   
+         p3 = x2 * c0;   
+         p4 = x3 * c0;   
+         p5 = x4 * c0;   
+         p6 = x5 * c0;   
+         p7 = x6 * c0;   
+
+         tapCnt--;
+         
+         acc0 += p0;
+         acc1 += p1;
+         acc2 += p2;
+         acc3 += p3;
+         acc4 += p4;
+         acc5 += p5;
+         acc6 += p6;
+         acc7 += p7;
+      }
+
+      /* If the filter length is not a multiple of 8, compute the remaining filter taps */
+      tapCnt = numTaps % 0x8u;
+
+      while(tapCnt > 0u)
+      {
+         /* Read coefficients */
+         c0 = *(pb++);
+
+         /* Fetch 1 state variable */
+         x7 = *(px++);
+
+         /* Perform the multiply-accumulates */      
+         p0 = x0 * c0;
+         p1 = x1 * c0;   
+         p2 = x2 * c0;   
+         p3 = x3 * c0;   
+         p4 = x4 * c0;   
+         p5 = x5 * c0;   
+         p6 = x6 * c0;   
+         p7 = x7 * c0;   
+
+         /* Reuse the present sample states for next sample */
+         x0 = x1;
+         x1 = x2;
+         x2 = x3;
+         x3 = x4;
+         x4 = x5;
+         x5 = x6;
+         x6 = x7;
+         
+         acc0 += p0;
+         acc1 += p1;
+         acc2 += p2;
+         acc3 += p3;
+         acc4 += p4;
+         acc5 += p5;
+         acc6 += p6;
+         acc7 += p7;
+
+         /* Decrement the loop counter */
+         tapCnt--;
+      }
+
+      /* Advance the state pointer by 8 to process the next group of 8 samples */
+      pState = pState + 8;
+
+      /* The results in the 8 accumulators, store in the destination buffer. */
+      *pDst++ = acc0;
+      *pDst++ = acc1;
+      *pDst++ = acc2;
+      *pDst++ = acc3;
+      *pDst++ = acc4;
+      *pDst++ = acc5;
+      *pDst++ = acc6;
+      *pDst++ = acc7;
+
+      blkCnt--;
+   }
+
+   /* If the blockSize is not a multiple of 8, compute any remaining output samples here.  
+   ** No loop unrolling is used. */
+   blkCnt = blockSize % 0x8u;
+
+   while(blkCnt > 0u)
+   {
+      /* Copy one sample at a time into state buffer */
+      *pStateCurnt++ = *pSrc++;
+
+      /* Set the accumulator to zero */
+      acc0 = 0.0f;
+
+      /* Initialize state pointer */
+      px = pState;
+
+      /* Initialize Coefficient pointer */
+      pb = (pCoeffs);
+
+      i = numTaps;
+
+      /* Perform the multiply-accumulates */
+      do
+      {
+         acc0 += *px++ * *pb++;
+         i--;
+
+      } while(i > 0u);
+
+      /* The result is store in the destination buffer. */
+      *pDst++ = acc0;
+
+      /* Advance state pointer by 1 for the next sample */
+      pState = pState + 1;
+
+      blkCnt--;
+   }
+
+   /* Processing is complete.  
+   ** Now copy the last numTaps - 1 samples to the start of the state buffer.  
+   ** This prepares the state buffer for the next function call. */
+
+   /* Points to the start of the state buffer */
+   pStateCurnt = S->pState;
+
+   tapCnt = (numTaps - 1u) >> 2u;
+
+   /* copy data */
+   while(tapCnt > 0u)
+   {
+      *pStateCurnt++ = *pState++;
+      *pStateCurnt++ = *pState++;
+      *pStateCurnt++ = *pState++;
+      *pStateCurnt++ = *pState++;
+
+      /* Decrement the loop counter */
+      tapCnt--;
+   }
+
+   /* Calculate remaining number of copies */
+   tapCnt = (numTaps - 1u) % 0x4u;
+
+   /* Copy the remaining q31_t data */
+   while(tapCnt > 0u)
+   {
+      *pStateCurnt++ = *pState++;
+
+      /* Decrement the loop counter */
+      tapCnt--;
+   }
+}
diff --git a/gui/examples/5_fir_differentiator.cpp b/gui/examples/5_fir_differentiator.cpp
new file mode 100644 (file)
index 0000000..1500dee
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * 5_fir_differentiator.cpp
+ * Written by Clyne Sullivan.
+ *
+ * Does an FIR differentiation on the incoming signal, so that the output is representative of the
+ * rate of change of the input.
+ * A scaling factor is applied so that the output's form is more clearly visible.
+ */
+
+Sample* process_data(Samples samples)
+{
+    constexpr int scaling_factor = 4;
+    static Samples output;
+    static Sample prev = 2048;
+
+    // Compute the first output value using the saved sample.
+    output[0] = 2048 + ((samples[0] - prev) * scaling_factor);
+
+       for (unsigned int i = 1; i < SIZE; i++) {
+        // Take the rate of change and scale it.
+        // 2048 is added as the output should be centered in the voltage range.
+               output[i] = 2048 + ((samples[i] - samples[i - 1]) * scaling_factor);
+    }
+
+       // Save the last sample for the next iteration.
+    prev = samples[SIZE - 1];
+
+    return output;
+}
+
diff --git a/gui/examples/6_iir_test.cpp b/gui/examples/6_iir_test.cpp
new file mode 100644 (file)
index 0000000..e0b266d
--- /dev/null
@@ -0,0 +1,23 @@
+/**
+ * 6_iir_test.cpp
+ * Written by Clyne Sullivan.
+ *
+ * Implements a simple infinite impulse response (IIR) filter using an alpha
+ * parameter.
+ * To build upon this example, try setting `alpha` with a parameter knob:
+ * alpha = param1() / 4095.0
+ */
+
+Sample* process_data(Samples samples)
+{
+       constexpr float alpha = 0.7;
+
+       static Sample prev = 2048;
+
+       samples[0] = (1 - alpha) * samples[0] + alpha * prev;
+       for (unsigned int i = 1; i < SIZE; i++)
+               samples[i] = (1 - alpha) * samples[i] + alpha * samples[i - 1];
+       prev = samples[SIZE - 1];
+
+       return samples;
+}
diff --git a/gui/examples/7_iir_echo.cpp b/gui/examples/7_iir_echo.cpp
new file mode 100644 (file)
index 0000000..75bf56e
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * 7_iir_echo.cpp
+ * Written by Clyne Sullivan.
+ *
+ * This filter produces an echo of the given input. There are two parameters:
+ * alpha controls the feedback gain, and D controls the echo/delay length.
+ */
+
+Sample* process_data(Samples samples)
+{
+       constexpr float alpha = 0.75;
+       constexpr unsigned int D = 100;
+
+       static Samples output;
+       static Sample prev[D]; // prev[0] = output[0 - D]
+
+       // Do calculations with previous output
+       for (unsigned int i = 0; i < D; i++)
+               output[i] = samples[i] + alpha * (prev[i] - 2048);
+
+       // Do calculations with current samples
+       for (unsigned int i = D; i < SIZE; i++)
+               output[i] = samples[i] + alpha * (output[i - D] - 2048);
+
+       // Save outputs for next computation
+       for (unsigned int i = 0; i < D; i++)
+               prev[i] = output[SIZE - (D - i)];
+
+       return output;
+}
diff --git a/gui/fonts/LICENSE-2.0.txt b/gui/fonts/LICENSE-2.0.txt
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/gui/fonts/Roboto-Medium.ttf b/gui/fonts/Roboto-Medium.ttf
new file mode 100644 (file)
index 0000000..39c63d7
Binary files /dev/null and b/gui/fonts/Roboto-Medium.ttf differ
diff --git a/gui/fonts/Roboto-Regular.ttf b/gui/fonts/Roboto-Regular.ttf
new file mode 100644 (file)
index 0000000..3d6861b
Binary files /dev/null and b/gui/fonts/Roboto-Regular.ttf differ
diff --git a/gui/fonts/RobotoMono-Regular.ttf b/gui/fonts/RobotoMono-Regular.ttf
new file mode 100644 (file)
index 0000000..7c4ce36
Binary files /dev/null and b/gui/fonts/RobotoMono-Regular.ttf differ
diff --git a/gui/source/ImGuiColorTextEdit b/gui/source/ImGuiColorTextEdit
new file mode 160000 (submodule)
index 0000000..0a88824
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 0a88824f7de8d0bd11d8419066caa7d3469395c4
diff --git a/gui/source/ImGuiFileDialog b/gui/source/ImGuiFileDialog
new file mode 160000 (submodule)
index 0000000..25ed815
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 25ed815799f44d97c77b82a168fa94e8804e237a
diff --git a/gui/source/circular.hpp b/gui/source/circular.hpp
new file mode 100644 (file)
index 0000000..4f49322
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * @file circular.hpp
+ * @brief Small utility for filling a buffer in a circular manner.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef CIRCULAR_HPP
+#define CIRCULAR_HPP
+
+#include <iterator>
+
+template<template<typename> class Container, typename T>
+class CircularBuffer
+{
+public:
+    CircularBuffer(Container<T>& container) :
+        m_begin(std::begin(container)),
+        m_end(std::end(container)),
+        m_current(m_begin) {}
+
+    void put(const T& value) noexcept {
+        *m_current = value;
+        if (++m_current == m_end)
+            m_current = m_begin;
+    }
+
+    std::size_t size() const noexcept {
+        return std::distance(m_begin, m_end);
+    }
+
+    void reset(const T& fill) noexcept {
+        std::fill(m_begin, m_end, fill);
+        m_current = m_begin;
+    }
+
+private:
+    Container<T>::iterator m_begin;
+    Container<T>::iterator m_end;
+    Container<T>::iterator m_current;
+};
+
+#endif // CIRCULAR_HPP
+
diff --git a/gui/source/code.cpp b/gui/source/code.cpp
new file mode 100644 (file)
index 0000000..f24bb5c
--- /dev/null
@@ -0,0 +1,172 @@
+/**
+ * @file code.cpp
+ * @brief Functionality for compiling and disassembling source code.
+ *
+ * Copyright (C) 2022 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "main.hpp"
+#include "stmdsp.hpp"
+#include "stmdsp_code.hpp"
+
+#include <cstdio>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+
+extern std::shared_ptr<stmdsp::device> m_device;
+
+// Stores the temporary file name currently used for compiling the algorithm.
+static std::string tempFileName;
+
+/**
+ * Generates a new temporary file name.
+ * @return A string containing the path and file name.
+ */
+static std::string newTempFileName();
+
+/**
+ * Executes the given command using system(), collecting the text output in the
+ * given file.
+ * @param command The command to be executed.
+ * @param file The file to write command output to.
+ * @return True if the command was successful (i.e. returned zero).
+ */
+static bool codeExecuteCommand(const std::string& command, const std::string& file);
+
+/**
+ * Does an in-place replacement of all occurances of "what" with "with".
+ * @param str The text string to operate on.
+ * @param what The text to search for.
+ * @param with The text that will replace occurances of "what".
+ */
+static void stringReplaceAll(std::string& str, const std::string& what,
+    const std::string& with);
+
+std::ifstream compileOpenBinaryFile()
+{
+    if (!tempFileName.empty())
+        return std::ifstream(tempFileName + ".o");
+    else
+        return std::ifstream();
+}
+
+void compileEditorCode(const std::string& code)
+{
+    log("Compiling...");
+
+    if (tempFileName.empty()) {
+        tempFileName = newTempFileName();
+    } else {
+        std::filesystem::remove(tempFileName + ".o");
+        std::filesystem::remove(tempFileName + ".orig.o");
+    }
+
+    const auto platform = m_device ? m_device->get_platform()
+                                   : stmdsp::platform::L4;
+
+    {
+        std::ofstream file (tempFileName, std::ios::trunc | std::ios::binary);
+
+        auto file_text =
+            platform == stmdsp::platform::L4 ? stmdsp::file_header_l4
+                                             : stmdsp::file_header_h7;
+        const auto buffer_size = m_device ? m_device->get_buffer_size()
+                                          : stmdsp::SAMPLES_MAX;
+
+        stringReplaceAll(file_text, "$0", std::to_string(buffer_size));
+
+        file << file_text << '\n' << code;
+    }
+
+    const auto scriptFile = tempFileName +
+#ifndef STMDSP_WIN32
+        ".sh";
+#else
+        ".bat";
+#endif
+
+    {
+        std::ofstream makefile (scriptFile, std::ios::binary);
+        auto make_text =
+            platform == stmdsp::platform::L4 ? stmdsp::makefile_text_l4
+                                             : stmdsp::makefile_text_h7;
+
+        stringReplaceAll(make_text, "$0", tempFileName);
+        stringReplaceAll(make_text, "$1",
+                         std::filesystem::current_path().string());
+
+        makefile << make_text;
+    }
+
+#ifndef STMDSP_WIN32
+    system((std::string("chmod +x ") + scriptFile).c_str());
+#endif
+
+    const auto makeOutput = scriptFile + ".log";
+    const auto makeCommand = scriptFile + " > " + makeOutput + " 2>&1";
+    if (codeExecuteCommand(makeCommand, makeOutput))
+        log("Compilation succeeded.");
+    else
+        log("Compilation failed.");
+
+    std::filesystem::remove(tempFileName);
+    std::filesystem::remove(scriptFile);
+}
+
+void disassembleCode()
+{
+    log("Disassembling...");
+
+    //if (tempFileName.empty())
+    //    compileEditorCode();
+
+    const auto output = tempFileName + ".asm.log";
+    const auto command =
+        std::string("arm-none-eabi-objdump -d --no-show-raw-insn ") +
+        tempFileName + ".orig.o > " + output + " 2>&1";
+
+    if (codeExecuteCommand(command, output))
+        log("Ready.");
+    else
+        log("Failed to load disassembly.");
+}
+
+std::string newTempFileName()
+{
+    const auto path = std::filesystem::temp_directory_path() / "stmdspgui_build";
+    return path.string();
+}
+
+bool codeExecuteCommand(const std::string& command, const std::string& file)
+{
+    bool success = system(command.c_str()) == 0;
+
+    if (std::ifstream output (file); output.good()) {
+        std::ostringstream sstr;
+        sstr << output.rdbuf();
+        log(sstr.str().c_str());
+    } else {
+        log("Could not read command output!");
+    }
+
+    std::filesystem::remove(file);
+
+    return success;
+}
+
+void stringReplaceAll(std::string& str, const std::string& what, const std::string& with)
+{
+    std::size_t i;
+    while ((i = str.find(what)) != std::string::npos) {
+        str.replace(i, what.size(), with);
+        i += what.size();
+    }
+};
+
diff --git a/gui/source/code.hpp b/gui/source/code.hpp
new file mode 100644 (file)
index 0000000..edb46e7
--- /dev/null
@@ -0,0 +1,39 @@
+/**
+ * @file code.hpp
+ * @brief Functionality for compiling and disassembling source code.
+ *
+ * Copyright (C) 2022 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSPGUI_CODE_HPP
+#define STMDSPGUI_CODE_HPP
+
+#include <fstream>
+#include <istream>
+#include <string>
+
+/**
+ * Attempts to open the most recently created binary file.
+ * @return An opened stream of the file if it exists, an empty stream otherwise.
+ */
+std::ifstream compileOpenBinaryFile();
+
+/**
+ * Attempts to compile the given C++ algorithm code into a binary.
+ * Errors are reported to the log view.
+ * @param code The C++ code for the algorithm (usually from the text editor).
+ */
+void compileEditorCode(const std::string& code);
+
+/**
+ * Disassembles the most recently compiled binary, outputting the results to
+ * the log view.
+ */
+void disassembleCode();
+
+#endif // STMDSPGUI_CODE_HPP
+
diff --git a/gui/source/device.cpp b/gui/source/device.cpp
new file mode 100644 (file)
index 0000000..9c50a0d
--- /dev/null
@@ -0,0 +1,481 @@
+/**
+ * @file device.cpp
+ * @brief Contains code for device-related UI elements and logic.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "stmdsp.hpp"
+
+#include "circular.hpp"
+#include "imgui.h"
+#include "wav.hpp"
+
+#include <array>
+#include <cctype>
+#include <charconv>
+#include <cmath>
+#include <deque>
+#include <fstream>
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <string_view>
+#include <thread>
+#include <vector>
+
+extern void log(const std::string& str);
+extern std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string&);
+extern std::ifstream compileOpenBinaryFile();
+extern void deviceRenderDisconnect();
+
+std::shared_ptr<stmdsp::device> m_device;
+
+static std::timed_mutex mutexDrawSamples;
+static std::timed_mutex mutexDeviceLoad;
+static std::ofstream logSamplesFile;
+static wav::clip wavOutput;
+static std::deque<stmdsp::dacsample_t> drawSamplesQueue;
+static std::deque<stmdsp::dacsample_t> drawSamplesInputQueue;
+static bool drawSamplesInput = false;
+static unsigned int drawSamplesBufferSize = 1;
+
+bool deviceConnect();
+
+void deviceSetInputDrawing(bool enabled)
+{
+    drawSamplesInput = enabled;
+    if (enabled) {
+        drawSamplesQueue.clear();
+        drawSamplesInputQueue.clear();
+    }
+}
+
+static void measureCodeTask(std::shared_ptr<stmdsp::device> device)
+{
+    std::this_thread::sleep_for(std::chrono::seconds(1));
+
+    if (device) {
+        const auto cycles = device->measurement_read();
+        log(std::string("Execution time: ") + std::to_string(cycles) + " cycles.");
+    }
+}
+
+static std::vector<stmdsp::dacsample_t> tryReceiveChunk(
+    std::shared_ptr<stmdsp::device> device,
+    auto readFunc)
+{
+    for (int tries = 0; tries < 100; ++tries) {
+        if (!device->is_running())
+            break;
+
+        const auto chunk = readFunc(device.get());
+        if (!chunk.empty())
+            return chunk;
+        else
+            std::this_thread::sleep_for(std::chrono::microseconds(20));
+    }
+
+    return {};
+}
+
+static std::chrono::duration<double> getBufferPeriod(
+    std::shared_ptr<stmdsp::device> device,
+    const double factor = 0.975)
+{
+    if (device) {
+        const double bufferSize = device->get_buffer_size();
+        const double sampleRate = device->get_sample_rate();
+        return std::chrono::duration<double>(bufferSize / sampleRate * factor);
+    } else {
+        return {};
+    }
+}
+
+static void drawSamplesTask(std::shared_ptr<stmdsp::device> device)
+{
+    if (!device)
+        return;
+
+    // This is the amount of time to wait between device reads.
+    const auto bufferTime = getBufferPeriod(device, 1);
+
+    // Adds the given chunk of samples to the given queue.
+    const auto addToQueue = [](auto& queue, const auto& chunk) {
+        std::scoped_lock lock (mutexDrawSamples);
+        std::copy(chunk.cbegin(), chunk.cend(), std::back_inserter(queue));
+    };
+
+    std::unique_lock<std::timed_mutex> lockDevice (mutexDeviceLoad, std::defer_lock);
+
+    while (device && device->is_running()) {
+        const auto next = std::chrono::high_resolution_clock::now() + bufferTime;
+
+        if (lockDevice.try_lock_until(next)) {
+            std::vector<stmdsp::dacsample_t> chunk, chunk2;
+
+            chunk = tryReceiveChunk(device,
+                std::mem_fn(&stmdsp::device::continuous_read));
+            if (drawSamplesInput) {
+                chunk2 = tryReceiveChunk(device,
+                    std::mem_fn(&stmdsp::device::continuous_read_input));
+            }
+
+            lockDevice.unlock();
+
+            addToQueue(drawSamplesQueue, chunk);
+            if (drawSamplesInput)
+                addToQueue(drawSamplesInputQueue, chunk2);
+
+            if (logSamplesFile.is_open()) {
+                for (const auto& s : chunk)
+                    logSamplesFile << s << '\n';
+            }
+        } else {
+            // Device must be busy, back off for a bit.
+            std::this_thread::sleep_for(std::chrono::milliseconds(50));
+        }
+
+        std::this_thread::sleep_until(next);
+    }
+}
+
+static void feedSigGenTask(std::shared_ptr<stmdsp::device> device)
+{
+    if (!device)
+        return;
+
+    const auto delay = getBufferPeriod(device);
+    const auto uploadDelay = getBufferPeriod(device, 0.001);
+
+    std::vector<stmdsp::dacsample_t> wavBuf (device->get_buffer_size() * 2, 2048);
+
+    {
+        std::scoped_lock lock (mutexDeviceLoad);
+        device->siggen_upload(wavBuf.data(), wavBuf.size());
+        device->siggen_start();
+        std::this_thread::sleep_for(std::chrono::milliseconds(1));
+    }
+
+    wavBuf.resize(wavBuf.size() / 2);
+    std::vector<int16_t> wavIntBuf (wavBuf.size());
+
+    while (device->is_siggening()) {
+        const auto next = std::chrono::high_resolution_clock::now() + delay;
+
+        wavOutput.next(wavIntBuf.data(), wavIntBuf.size());
+        std::transform(wavIntBuf.cbegin(), wavIntBuf.cend(),
+            wavBuf.begin(),
+            [](auto i) { return static_cast<stmdsp::dacsample_t>(i / 16 + 2048); });
+
+        {
+            std::scoped_lock lock (mutexDeviceLoad);
+            while (!device->siggen_upload(wavBuf.data(), wavBuf.size()))
+                std::this_thread::sleep_for(uploadDelay);
+        }
+
+        std::this_thread::sleep_until(next);
+    }
+}
+
+static void statusTask(std::shared_ptr<stmdsp::device> device)
+{
+    if (!device)
+        return;
+
+    while (device->connected()) {
+        mutexDeviceLoad.lock();
+        const auto [status, error] = device->get_status();
+        mutexDeviceLoad.unlock();
+
+        if (error != stmdsp::Error::None) {
+            switch (error) {
+            case stmdsp::Error::NotIdle:
+                log("Error: Device already running...");
+                break;
+            case stmdsp::Error::ConversionAborted:
+                log("Error: Algorithm unloaded, a fault occurred!");
+                break;
+            case stmdsp::Error::GUIDisconnect:
+                // Do GUI events for disconnect if device was lost.
+                deviceConnect();
+                deviceRenderDisconnect();
+                return;
+                break;
+            default:
+                log("Error: Device had an issue...");
+                break;
+            }
+        }
+
+        std::this_thread::sleep_for(std::chrono::seconds(1));
+    }
+}
+
+void deviceLoadAudioFile(const std::string& file)
+{
+    wavOutput = wav::clip(file);
+    if (wavOutput.valid())
+        log("Audio file loaded.");
+    else
+        log("Error: Bad WAV audio file.");
+}
+
+void deviceLoadLogFile(const std::string& file)
+{
+    logSamplesFile = std::ofstream(file);
+    if (logSamplesFile.is_open())
+        log("Log file ready.");
+    else
+        log("Error: Could not open log file.");
+}
+
+bool deviceGenStartToggle()
+{
+    if (m_device) {
+        const bool running = m_device->is_siggening();
+
+        if (!running) {
+            if (wavOutput.valid()) {
+                std::thread(feedSigGenTask, m_device).detach();
+            } else {
+                std::scoped_lock dlock (mutexDeviceLoad);
+                m_device->siggen_start();
+            }
+            log("Generator started.");
+        } else {
+            {
+                std::scoped_lock dlock (mutexDeviceLoad);
+                m_device->siggen_stop();
+            }
+            log("Generator stopped.");
+        }
+
+        return !running;
+    }
+
+    return false;
+}
+
+void deviceUpdateDrawBufferSize(double timeframe)
+{
+    drawSamplesBufferSize = std::round(
+        m_device->get_sample_rate() * timeframe);
+}
+
+void deviceSetSampleRate(unsigned int rate)
+{
+    do {
+        m_device->set_sample_rate(rate);
+        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    } while (m_device->get_sample_rate() != rate);
+}
+
+bool deviceConnect()
+{
+    static std::thread statusThread;
+
+    if (!m_device) {
+        stmdsp::scanner scanner;
+        if (const auto devices = scanner.scan(); !devices.empty()) {
+            try {
+                m_device.reset(new stmdsp::device(devices.front()));
+            } catch (...) {
+                log("Failed to connect (check permissions?).");
+                m_device.reset();
+            }
+
+            if (m_device) {
+                if (m_device->connected()) {
+                    log("Connected!");
+                    statusThread = std::thread(statusTask, m_device);
+                    statusThread.detach();
+                    return true;
+                } else {
+                    m_device.reset();
+                    log("Failed to connect.");
+                }
+            }
+        } else {
+            log("No devices found.");
+        }
+    } else {
+        m_device->disconnect();
+        if (statusThread.joinable())
+            statusThread.join();
+        m_device.reset();
+        log("Disconnected.");
+    }
+
+    return false;
+}
+
+void deviceStart(bool logResults, bool drawSamples)
+{
+    if (!m_device) {
+        log("No device connected.");
+        return;
+    }
+
+    if (m_device->is_running()) {
+        {
+            std::scoped_lock lock (mutexDrawSamples, mutexDeviceLoad);
+            std::this_thread::sleep_for(std::chrono::microseconds(150));
+            m_device->continuous_stop();
+        }
+        if (logSamplesFile.is_open()) {
+            logSamplesFile.close();
+            log("Log file saved and closed.");
+        }
+        log("Ready.");
+    } else {
+        m_device->continuous_start();
+        if (drawSamples || logResults || wavOutput.valid())
+            std::thread(drawSamplesTask, m_device).detach();
+
+        log("Running.");
+    }
+}
+
+void deviceStartMeasurement()
+{
+    if (m_device && m_device->is_running()) {
+        m_device->measurement_start();
+        std::thread(measureCodeTask, m_device).detach();
+    }
+}
+
+void deviceAlgorithmUpload()
+{
+    if (!m_device) {
+        log("No device connected.");
+    } else if (m_device->is_running()) {
+        log("Cannot upload algorithm while running.");
+    } else if (auto algo = compileOpenBinaryFile(); algo.is_open()) {
+        std::ostringstream sstr;
+        sstr << algo.rdbuf();
+        auto str = sstr.str();
+
+        m_device->upload_filter(reinterpret_cast<unsigned char *>(&str[0]), str.size());
+        log("Algorithm uploaded.");
+    } else {
+        log("Algorithm must be compiled first.");
+    }
+}
+
+void deviceAlgorithmUnload()
+{
+    if (!m_device) {
+        log("No device connected.");
+    } else if (m_device->is_running()) {
+        log("Cannot unload algorithm while running.");
+    } else {
+        m_device->unload_filter();
+        log("Algorithm unloaded.");
+    }
+}
+
+void deviceGenLoadList(const std::string_view list)
+{
+    std::vector<stmdsp::dacsample_t> samples;
+
+    auto it = list.cbegin();
+    while (it != list.cend()) {
+        const auto itend = std::find_if(it, list.cend(),
+            [](char c) { return !isdigit(c); });
+
+        unsigned long n;
+        const auto ec = std::from_chars(it, itend, n).ec;
+        if (ec != std::errc()) {
+            log("Error: Bad data in sample list.");
+            break;
+        } else if (n > 4095) {
+            log("Error: Sample data value larger than max of 4095.");
+            break;
+        } else {
+            samples.push_back(n & 4095);
+            if (samples.size() >= stmdsp::SAMPLES_MAX * 2) {
+                log("Error: Too many samples for signal generator.");
+                break;
+            }
+        }
+
+        it = std::find_if(itend, list.cend(), isdigit);
+    }
+
+    if (it == list.cend()) {
+        // DAC buffer must be of even size
+        if (samples.size() % 2 != 0)
+            samples.push_back(samples.back());
+
+        m_device->siggen_upload(samples.data(), samples.size());
+        log("Generator ready.");
+    }
+}
+
+void deviceGenLoadFormula(const std::string& formula)
+{
+    auto samples = deviceGenLoadFormulaEval(formula);
+
+    if (!samples.empty()) {
+        m_device->siggen_upload(samples.data(), samples.size());
+        log("Generator ready.");
+    } else {
+        log("Error: Bad formula.");
+    }
+}
+
+std::size_t pullFromQueue(
+    std::deque<stmdsp::dacsample_t>& queue,
+    CircularBuffer<std::vector, stmdsp::dacsample_t>& circ)
+{
+    // We know how big the circular buffer should be to hold enough samples to
+    // fill the current draw samples view.
+    // If the given buffer does not match this size, notify the caller.
+    // TODO this could be done better... drawSamplesBufferSize should be a GUI-
+    // only thing.
+    if (circ.size() != drawSamplesBufferSize)
+        return drawSamplesBufferSize;
+
+    std::scoped_lock lock (mutexDrawSamples);
+
+    // The render code will draw all of the new samples we add to the buffer.
+    // So, we must provide a certain amount of samples at a time to make the
+    // render appear smooth.
+    // The 1.025 factor keeps us on top of the stream; don't want to fall
+    // behind.
+    const double FPS = ImGui::GetIO().Framerate;
+    const auto desiredCount = m_device->get_sample_rate() / FPS;
+
+    // Transfer from the queue to the render buffer.
+    auto count = std::min(queue.size(), static_cast<std::size_t>(desiredCount));
+    while (count--) {
+        circ.put(queue.front());
+        queue.pop_front();
+    }
+
+    return 0;
+}
+
+/**
+ * Pulls a render frame's worth of samples from the draw samples queue, adding
+ * the samples to the given buffer.
+ */
+std::size_t pullFromDrawQueue(
+    CircularBuffer<std::vector, stmdsp::dacsample_t>& circ)
+{
+    return pullFromQueue(drawSamplesQueue, circ);
+}
+
+std::size_t pullFromInputDrawQueue(
+    CircularBuffer<std::vector, stmdsp::dacsample_t>& circ)
+{
+    return pullFromQueue(drawSamplesInputQueue, circ);
+}
+
diff --git a/gui/source/device_formula.cpp b/gui/source/device_formula.cpp
new file mode 100644 (file)
index 0000000..e21d374
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * @file device_formula.cpp
+ * @brief Function for filling generator buffer using a mathematical formula.
+ * This is kept in its own file as exprtk.hpp takes forever to compile.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "stmdsp.hpp"
+
+#include <algorithm>
+#include <random>
+#include <string_view>
+#include <vector>
+
+#ifndef STMDSP_DISABLE_FORMULAS
+
+#define exprtk_disable_comments
+#define exprtk_disable_break_continue
+#define exprtk_disable_sc_andor
+#define exprtk_disable_return_statement
+#define exprtk_disable_enhanced_features
+//#define exprtk_disable_string_capabilities
+#define exprtk_disable_superscalar_unroll
+#define exprtk_disable_rtl_io_file
+#define exprtk_disable_rtl_vecops
+//#define exprtk_disable_caseinsensitivity
+#include "exprtk.hpp"
+
+static std::random_device randomDevice;
+
+std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string& formulaString)
+{
+    double x = 0;
+
+    exprtk::symbol_table<double> symbol_table;
+    exprtk::function_compositor<double> compositor (symbol_table);
+    exprtk::expression<double> expression;
+    exprtk::parser<double> parser;
+
+    symbol_table.add_constants();
+    symbol_table.add_variable("x", x);
+    symbol_table.add_function("random",
+        [](double l, double h) -> double {
+            return std::uniform_real_distribution<double>(l, h)(randomDevice);
+        });
+    compositor.add(exprtk::function_compositor<double>::function()
+                       .name("square")
+                       .var("X")
+                       .expression("ceil(sin(pi*X))"));
+    compositor.add(exprtk::function_compositor<double>::function()
+                       .name("triangle")
+                       .var("X")
+                       .expression("ceil(sin(pi*X))*(X-floor(X))+ceil(-sin(pi*X))*(-X-floor(-X))"));
+    compositor.add(exprtk::function_compositor<double>::function()
+                       .name("pulse")
+                       .var("L")
+                       .var("X")
+                       .expression("if(X<=L,1,0)"));
+    expression.register_symbol_table(symbol_table);
+    parser.compile(formulaString, expression);
+
+    const auto genFun = [&x, &expression] {
+        const auto s = std::clamp(expression.value(), -1., 1.) * 2048. + 2048.;
+        ++x;
+        return static_cast<stmdsp::dacsample_t>(std::min(s, 4095.));
+    };
+
+    std::vector<stmdsp::dacsample_t> samples (stmdsp::SAMPLES_MAX);
+    std::generate(samples.begin(), samples.end(), genFun);
+    return samples;
+}
+
+#else // no formula support
+
+std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string&)
+{
+    return {};
+}
+
+#endif // STMDSP_DISABLE_FORMULAS
+
diff --git a/gui/source/exprtk.hpp b/gui/source/exprtk.hpp
new file mode 100644 (file)
index 0000000..bb108c2
--- /dev/null
@@ -0,0 +1,40121 @@
+/*
+ ******************************************************************
+ *           C++ Mathematical Expression Toolkit Library          *
+ *                                                                *
+ * Author: Arash Partow (1999-2021)                               *
+ * 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 <cassert>
+#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
+
+   #if __cplusplus >= 201103L
+      #define exprtk_override override
+      #define exprtk_final    final
+   #else
+      #define exprtk_override
+      #define exprtk_final
+   #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);
+      }
+
+      inline bool is_valid_string_char(const char_t c)
+      {
+         return std::isprint(static_cast<uchar_t>(c)) ||
+                is_whitespace(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_t>(std::tolower(s1[i]));
+               const char_t c2 = static_cast<char_t>(std::tolower(s2[i]));
+
+               if (c1 > c2)
+                  return false;
+               else if (c1 < c2)
+                  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' + static_cast<char_t>(-(i % 10));
+            }
+
+            result += '-';
+         }
+         else
+         {
+            for ( ; i; i /= 10)
+            {
+               result += '0' + static_cast<char_t>(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<uchar_t>(std::toupper(h) - 'A');
+      }
+
+      template <typename Iterator>
+      inline bool 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' != std::toupper(*(itr + 1))) ||
+              (!is_hex_digit(*(itr + 2)))       ||
+              (!is_hex_digit(*(itr + 3)))
+            )
+         {
+            return false;
+         }
+
+         result = hex_to_bin(static_cast<uchar_t>(*(itr + 2))) << 4 |
+                  hex_to_bin(static_cast<uchar_t>(*(itr + 3))) ;
+
+         return true;
+      }
+
+      inline bool 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))
+            {
+               if (end == ++itr1)
+               {
+                  return false;
+               }
+               else if (parse_hex(itr1, end, *itr2))
+               {
+                  itr1+= 4;
+                  itr2+= 1;
+                  removal_count +=4;
+               }
+               else if ('a' == (*itr1)) { (*itr2++) = '\a'; ++itr1; ++removal_count; }
+               else if ('b' == (*itr1)) { (*itr2++) = '\b'; ++itr1; ++removal_count; }
+               else if ('f' == (*itr1)) { (*itr2++) = '\f'; ++itr1; ++removal_count; }
+               else if ('n' == (*itr1)) { (*itr2++) = '\n'; ++itr1; ++removal_count; }
+               else if ('r' == (*itr1)) { (*itr2++) = '\r'; ++itr1; ++removal_count; }
+               else if ('t' == (*itr1)) { (*itr2++) = '\t'; ++itr1; ++removal_count; }
+               else if ('v' == (*itr1)) { (*itr2++) = '\v'; ++itr1; ++removal_count; }
+               else if ('0' == (*itr1)) { (*itr2++) = '\0'; ++itr1; ++removal_count; }
+               else
+               {
+                  (*itr2++) = (*itr1++);
+                  ++removal_count;
+               }
+               continue;
+            }
+            else
+               (*itr2++) = (*itr1++);
+         }
+
+         if ((removal_count > s.size()) || (0 == removal_count))
+            return false;
+
+         s.resize(s.size() - removal_count);
+
+         return true;
+      }
+
+      class build_string
+      {
+      public:
+
+         build_string(const std::size_t& initial_size = 64)
+         {
+            data_.reserve(initial_size);
+         }
+
+         inline build_string& operator << (const std::string& s)
+         {
+            data_ += s;
+            return (*this);
+         }
+
+         inline build_string& operator << (char_cptr s)
+         {
+            data_ += std::string(s);
+            return (*this);
+         }
+
+         inline operator std::string () const
+         {
+            return data_;
+         }
+
+         inline std::string as_string() const
+         {
+            return data_;
+         }
+
+      private:
+
+         std::string data_;
+      };
+
+      static const std::string reserved_words[] =
+                                  {
+                                    "break",  "case",  "continue",  "default",  "false",  "for",
+                                    "if", "else", "ilike",  "in", "like", "and",  "nand", "nor",
+                                    "not",  "null",  "or",   "repeat", "return",  "shl",  "shr",
+                                    "swap", "switch", "true",  "until", "var",  "while", "xnor",
+                                    "xor", "&", "|"
+                                  };
+
+      static const std::size_t reserved_words_size = sizeof(reserved_words) / sizeof(std::string);
+
+      static const std::string reserved_symbols[] =
+                                  {
+                                    "abs",  "acos",  "acosh",  "and",  "asin",  "asinh", "atan",
+                                    "atanh", "atan2", "avg",  "break", "case", "ceil",  "clamp",
+                                    "continue",   "cos",   "cosh",   "cot",   "csc",  "default",
+                                    "deg2grad",  "deg2rad",   "equal",  "erf",   "erfc",  "exp",
+                                    "expm1",  "false",   "floor",  "for",   "frac",  "grad2deg",
+                                    "hypot", "iclamp", "if",  "else", "ilike", "in",  "inrange",
+                                    "like",  "log",  "log10", "log2",  "logn",  "log1p", "mand",
+                                    "max", "min",  "mod", "mor",  "mul", "ncdf",  "nand", "nor",
+                                    "not",   "not_equal",   "null",   "or",   "pow",  "rad2deg",
+                                    "repeat", "return", "root", "round", "roundn", "sec", "sgn",
+                                    "shl", "shr", "sin", "sinc", "sinh", "sqrt",  "sum", "swap",
+                                    "switch", "tan",  "tanh", "true",  "trunc", "until",  "var",
+                                    "while", "xnor", "xor", "&", "|"
+                                  };
+
+      static const std::size_t reserved_symbols_size = sizeof(reserved_symbols) / sizeof(std::string);
+
+      static const std::string base_function_list[] =
+                                  {
+                                    "abs", "acos",  "acosh", "asin",  "asinh", "atan",  "atanh",
+                                    "atan2",  "avg",  "ceil",  "clamp",  "cos",  "cosh",  "cot",
+                                    "csc",  "equal",  "erf",  "erfc",  "exp",  "expm1", "floor",
+                                    "frac", "hypot", "iclamp",  "like", "log", "log10",  "log2",
+                                    "logn", "log1p", "mand", "max", "min", "mod", "mor",  "mul",
+                                    "ncdf",  "pow",  "root",  "round",  "roundn",  "sec", "sgn",
+                                    "sin", "sinc", "sinh", "sqrt", "sum", "swap", "tan", "tanh",
+                                    "trunc",  "not_equal",  "inrange",  "deg2grad",   "deg2rad",
+                                    "rad2deg", "grad2deg"
+                                  };
+
+      static const std::size_t base_function_list_size = sizeof(base_function_list) / sizeof(std::string);
+
+      static const std::string logic_ops_list[] =
+                                  {
+                                    "and", "nand", "nor", "not", "or",  "xnor", "xor", "&", "|"
+                                  };
+
+      static const std::size_t logic_ops_list_size = sizeof(logic_ops_list) / sizeof(std::string);
+
+      static const std::string cntrl_struct_list[] =
+                                  {
+                                     "if", "switch", "for", "while", "repeat", "return"
+                                  };
+
+      static const std::size_t cntrl_struct_list_size = sizeof(cntrl_struct_list) / sizeof(std::string);
+
+      static const std::string arithmetic_ops_list[] =
+                                  {
+                                    "+", "-", "*", "/", "%", "^"
+                                  };
+
+      static const std::size_t arithmetic_ops_list_size = sizeof(arithmetic_ops_list) / sizeof(std::string);
+
+      static const std::string assignment_ops_list[] =
+                                  {
+                                    ":=", "+=", "-=",
+                                    "*=", "/=", "%="
+                                  };
+
+      static const std::size_t assignment_ops_list_size = sizeof(assignment_ops_list) / sizeof(std::string);
+
+      static const std::string inequality_ops_list[] =
+                                  {
+                                     "<",  "<=", "==",
+                                     "=",  "!=", "<>",
+                                    ">=",  ">"
+                                  };
+
+      static const std::size_t inequality_ops_list_size = sizeof(inequality_ops_list) / sizeof(std::string);
+
+      inline bool is_reserved_word(const std::string& symbol)
+      {
+         for (std::size_t i = 0; i < reserved_words_size; ++i)
+         {
+            if (imatch(symbol, reserved_words[i]))
+            {
+               return true;
+            }
+         }
+
+         return false;
+      }
+
+      inline bool is_reserved_symbol(const std::string& symbol)
+      {
+         for (std::size_t i = 0; i < reserved_symbols_size; ++i)
+         {
+            if (imatch(symbol, reserved_symbols[i]))
+            {
+               return true;
+            }
+         }
+
+         return false;
+      }
+
+      inline bool is_base_function(const std::string& function_name)
+      {
+         for (std::size_t i = 0; i < base_function_list_size; ++i)
+         {
+            if (imatch(function_name, base_function_list[i]))
+            {
+               return true;
+            }
+         }
+
+         return false;
+      }
+
+      inline bool is_control_struct(const std::string& cntrl_strct)
+      {
+         for (std::size_t i = 0; i < cntrl_struct_list_size; ++i)
+         {
+            if (imatch(cntrl_strct, cntrl_struct_list[i]))
+            {
+               return true;
+            }
+         }
+
+         return false;
+      }
+
+      inline bool is_logic_opr(const std::string& lgc_opr)
+      {
+         for (std::size_t i = 0; i < logic_ops_list_size; ++i)
+         {
+            if (imatch(lgc_opr, logic_ops_list[i]))
+            {
+               return true;
+            }
+         }
+
+         return false;
+      }
+
+      struct cs_match
+      {
+         static inline bool cmp(const char_t c0, const char_t c1)
+         {
+            return (c0 == c1);
+         }
+      };
+
+      struct cis_match
+      {
+         static inline bool cmp(const char_t c0, const char_t c1)
+         {
+            return (std::tolower(c0) == std::tolower(c1));
+         }
+      };
+
+      template <typename Iterator, typename Compare>
+      inline bool match_impl(const Iterator pattern_begin,
+                             const Iterator pattern_end  ,
+                             const Iterator data_begin   ,
+                             const Iterator data_end     ,
+                             const typename std::iterator_traits<Iterator>::value_type& zero_or_more,
+                             const typename std::iterator_traits<Iterator>::value_type& zero_or_one )
+      {
+         const Iterator null_itr(0);
+
+         Iterator d_itr    = data_begin;
+         Iterator p_itr    = pattern_begin;
+         Iterator tb_p_itr = null_itr;
+         Iterator tb_d_itr = null_itr;
+
+         while (d_itr != data_end)
+         {
+            if (zero_or_more == *p_itr)
+            {
+               while ((pattern_end != p_itr) && ((zero_or_more == *p_itr) || (zero_or_one == *p_itr)))
+               {
+                  ++p_itr;
+               }
+
+               if (pattern_end == p_itr)
+                  return true;
+
+               const typename std::iterator_traits<Iterator>::value_type c = *(p_itr);
+
+               while ((data_end != d_itr) && !Compare::cmp(c,*d_itr))
+               {
+                  ++d_itr;
+               }
+
+               tb_p_itr = p_itr;
+               tb_d_itr = d_itr;
+
+               continue;
+            }
+            else if (!Compare::cmp(*p_itr, *d_itr) && (zero_or_one != *p_itr))
+            {
+               if (null_itr == tb_d_itr)
+                  return false;
+
+               d_itr = tb_d_itr++;
+               p_itr = tb_p_itr;
+
+               continue;
+            }
+
+            ++p_itr;
+            ++d_itr;
+         }
+
+         while ((pattern_end != p_itr) && ((zero_or_more == *p_itr) || (zero_or_one == *p_itr)))
+         {
+            ++p_itr;
+         }
+
+         return (pattern_end == p_itr);
+      }
+
+      inline bool wc_match(const std::string& wild_card,
+                           const std::string& str)
+      {
+         return match_impl<char_cptr,cs_match>(
+                   wild_card.data(), wild_card.data() + wild_card.size(),
+                   str.data(), str.data() + str.size(),
+                   '*', '?');
+      }
+
+      inline bool wc_imatch(const std::string& wild_card,
+                            const std::string& str)
+      {
+         return match_impl<char_cptr,cis_match>(
+                   wild_card.data(), wild_card.data() + wild_card.size(),
+                   str.data(), str.data() + str.size(),
+                   '*', '?');
+      }
+
+      inline bool sequence_match(const std::string& pattern,
+                                 const std::string& str,
+                                 std::size_t&       diff_index,
+                                 char_t&            diff_value)
+      {
+         if (str.empty())
+         {
+            return ("Z" == pattern);
+         }
+         else if ('*' == pattern[0])
+            return false;
+
+         typedef std::string::const_iterator itr_t;
+
+         itr_t p_itr = pattern.begin();
+         itr_t s_itr = str    .begin();
+
+         itr_t p_end = pattern.end();
+         itr_t s_end = str    .end();
+
+         while ((s_end != s_itr) && (p_end != p_itr))
+         {
+            if ('*' == (*p_itr))
+            {
+               const char_t target = static_cast<char_t>(std::toupper(*(p_itr - 1)));
+
+               if ('*' == target)
+               {
+                  diff_index = static_cast<std::size_t>(std::distance(str.begin(),s_itr));
+                  diff_value = static_cast<char_t>(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_t>(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 {};
+
+            #define exprtk_define_epsilon_type(Type, Epsilon)      \
+            template <> struct epsilon_type<Type>                  \
+            {                                                      \
+               static inline Type value()                          \
+               {                                                   \
+                  const Type epsilon = static_cast<Type>(Epsilon); \
+                  return epsilon;                                  \
+               }                                                   \
+            };                                                     \
+
+            exprtk_define_epsilon_type(float      ,      0.000001f)
+            exprtk_define_epsilon_type(double     ,   0.0000000001)
+            exprtk_define_epsilon_type(long double, 0.000000000001)
+
+            #undef exprtk_define_epsilon_type
+
+            template <typename T>
+            inline bool is_nan_impl(const T v, real_type_tag)
+            {
+               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, static_cast<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(const T v) { T v_5 = fast_exp<T,5>::result(v); return v_5 * v_5; } };
+         template <typename T> struct fast_exp<T, 9> { static inline T result(const T v) { return fast_exp<T,8>::result(v) * v; } };
+         template <typename T> struct fast_exp<T, 8> { static inline T result(const T v) { T v_4 = fast_exp<T,4>::result(v); return v_4 * v_4; } };
+         template <typename T> struct fast_exp<T, 7> { static inline T result(const T v) { return fast_exp<T,6>::result(v) * v; } };
+         template <typename T> struct fast_exp<T, 6> { static inline T result(const T v) { T v_3 = fast_exp<T,3>::result(v); return v_3 * v_3; } };
+         template <typename T> struct fast_exp<T, 5> { static inline T result(const T v) { return fast_exp<T,4>::result(v) * v; } };
+         template <typename T> struct fast_exp<T, 4> { static inline T result(const T v) { T v_2 = v * v; return v_2 * v_2; } };
+         template <typename T> struct fast_exp<T, 3> { static inline T result(const T v) { return v * v * v; } };
+         template <typename T> struct fast_exp<T, 2> { static inline T result(const T v) { return v * v;     } };
+         template <typename T> struct fast_exp<T, 1> { static inline T result(const T v) { return v;         } };
+         template <typename T> struct fast_exp<T, 0> { static inline T result(const T  ) { return T(1);      } };
+
+         #define exprtk_define_unary_function(FunctionName)        \
+         template <typename T>                                     \
+         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_t>(*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 T>
+      inline bool valid_exponent(const int exponent, numeric::details::real_type_tag)
+      {
+         using namespace details::numeric;
+         return (numeric_info<T>::min_exp <= exponent) && (exponent <= numeric_info<T>::max_exp);
+      }
+
+      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;
+
+            while (end != itr)
+            {
+               unsigned int digit;
+               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;
+               T tmp_d = T(0);
+
+               while (end != itr)
+               {
+                  unsigned int digit;
+                  parse_digit_1(tmp_d)
+                  parse_digit_1(tmp_d)
+                  parse_digit_2(tmp_d)
+               }
+
+               if (curr != itr)
+               {
+                  instate = true;
+
+                  const int frac_exponent = static_cast<int>(-std::distance(curr, itr));
+
+                  if (!valid_exponent<T>(frac_exponent, numeric::details::real_type_tag()))
+                     return false;
+
+                  d += compute_pow10(tmp_d, frac_exponent);
+               }
+
+               #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 (!valid_exponent<T>(exponent, numeric::details::real_type_tag()))
+            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
+
+   struct loop_runtime_check
+   {
+      enum loop_types
+      {
+         e_invalid           = 0,
+         e_for_loop          = 1,
+         e_while_loop        = 2,
+         e_repeat_until_loop = 4,
+         e_all_loops         = 7
+      };
+
+      enum violation_type
+      {
+          e_unknown         = 0,
+          e_iteration_count = 1,
+          e_timeout         = 2
+      };
+
+      loop_types loop_set;
+
+      loop_runtime_check()
+      : loop_set(e_invalid),
+        max_loop_iterations(0)
+      {}
+
+      details::_uint64_t max_loop_iterations;
+
+      struct violation_context
+      {
+         loop_types loop;
+         violation_type violation;
+         details::_uint64_t iteration_count;
+      };
+
+      virtual void handle_runtime_violation(const violation_context&)
+      {
+         throw std::runtime_error("ExprTk Loop run-time violation.");
+      }
+
+      virtual ~loop_runtime_check() {}
+   };
+
+   typedef loop_runtime_check* loop_runtime_check_ptr;
+
+   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 token_list_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);
+         }
+
+         #ifndef exprtk_disable_comments
+         inline bool is_comment_start(details::char_cptr itr)
+         {
+            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;
+            }
+            return false;
+         }
+         #else
+         inline bool is_comment_start(details::char_cptr)
+         {
+            return false;
+         }
+         #endif
+
+         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, std::min(initial_itr + 11, s_end_),
+                  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, std::min(initial_itr + 4, s_end_),
+                  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 (!details::is_valid_string_char(*s_itr_))
+               {
+                  t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_);
+                  token_list_.push_back(t);
+
+                  return;
+               }
+               else 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_)) &&
+                       ((s_itr_ + 4) <= s_end_)
+                     )
+                  {
+                     const bool x_seperator = ('X' == std::toupper(*(s_itr_ + 1)));
+
+                     const bool both_digits = details::is_hex_digit(*(s_itr_ + 2)) &&
+                                              details::is_hex_digit(*(s_itr_ + 3)) ;
+
+                     if (!(x_seperator && both_digits))
+                     {
+                        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_);
+
+               if (!details::cleanup_escapes(parsed_string))
+               {
+                  t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_);
+                  token_list_.push_back(t);
+
+                  return;
+               }
+
+               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) exprtk_override
+         {
+            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) exprtk_override
+         {
+            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) exprtk_override
+         {
+            if (g.token_list_.empty())
+               return 0;
+            else if (g.token_list_.size() < stride_)
+               return 0;
+
+            std::size_t changes = 0;
+
+            typedef std::pair<std::size_t, token> insert_t;
+            std::vector<insert_t> insert_list;
+            insert_list.reserve(10000);
+
+            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;
+               }
+
+               if ((insert_index >= 0) && (insert_index <= (static_cast<int>(stride_) + 1)))
+               {
+                  insert_list.push_back(insert_t(i, t));
+                  changes++;
+               }
+            }
+
+            if (!insert_list.empty())
+            {
+               generator::token_list_t token_list;
+
+               std::size_t insert_index = 0;
+
+               for (std::size_t i = 0; i < g.token_list_.size(); ++i)
+               {
+                  token_list.push_back(g.token_list_[i]);
+
+                  if (
+                       (insert_index < insert_list.size()) &&
+                       (insert_list[insert_index].first == i)
+                     )
+                  {
+                     token_list.push_back(insert_list[insert_index].second);
+                     insert_index++;
+                  }
+               }
+
+               std::swap(g.token_list_,token_list);
+            }
+
+            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) exprtk_override
+         {
+            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)
+         {
+            if (g.token_list_.size() < 2)
+               return 0;
+
+            std::size_t changes = 0;
+
+            generator::token_list_t token_list;
+            token_list.reserve(10000);
+
+            for (int i = 0;  i < static_cast<int>(g.token_list_.size() - 1); ++i)
+            {
+               token t;
+
+               for ( ; ; )
+               {
+                  if (!join(g[i], g[i + 1], t))
+                  {
+                     token_list.push_back(g[i]);
+                     break;
+                  }
+
+                  token_list.push_back(t);
+
+                  ++changes;
+
+                  i+=2;
+
+                  if (static_cast<std::size_t>(i) >= g.token_list_.size() - 1)
+                     break;
+               }
+            }
+
+            token_list.push_back(g.token_list_.back());
+
+            assert(token_list.size() <= g.token_list_.size());
+
+            std::swap(token_list, g.token_list_);
+
+            return changes;
+         }
+
+         inline std::size_t process_stride_3(generator& g)
+         {
+            if (g.token_list_.size() < 3)
+               return 0;
+
+            std::size_t changes = 0;
+
+            generator::token_list_t token_list;
+            token_list.reserve(10000);
+
+            for (int i = 0;  i < static_cast<int>(g.token_list_.size() - 2); ++i)
+            {
+               token t;
+
+               for ( ; ; )
+               {
+                  if (!join(g[i], g[i + 1], g[i + 2], t))
+                  {
+                     token_list.push_back(g[i]);
+                     break;
+                  }
+
+                  token_list.push_back(t);
+
+                  ++changes;
+
+                  i+=3;
+
+                  if (static_cast<std::size_t>(i) >= g.token_list_.size() - 2)
+                     break;
+               }
+            }
+
+            token_list.push_back(*(g.token_list_.begin() + g.token_list_.size() - 2));
+            token_list.push_back(*(g.token_list_.begin() + g.token_list_.size() - 1));
+
+            assert(token_list.size() <= g.token_list_.size());
+
+            std::swap(token_list, g.token_list_);
+
+            return changes;
+         }
+
+         const std::size_t stride_;
+      };
+
+      namespace helper
+      {
+
+         inline void dump(const lexer::generator& generator)
+         {
+            for (std::size_t i = 0; i < generator.size(); ++i)
+            {
+               const 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<details::char_t>(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<details::char_t>(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<details::char_t>(base)))
+               {
+                  if (details::is_right_bracket(static_cast<details::char_t>(t)))
+                     return false;
+                  else if (details::is_left_bracket(static_cast<details::char_t>(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<details::char_t>(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<details::char_t>(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:
+
+         explicit 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;
+
+         explicit type_view(type_store_t& ts)
+         : ts_(ts),
+           data_(reinterpret_cast<value_t*>(ts_.data))
+         {}
+
+         explicit type_view(const type_store_t& ts)
+         : ts_(const_cast<type_store_t&>(ts)),
+           data_(reinterpret_cast<value_t*>(ts_.data))
+         {}
+
+         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;
+
+         explicit scalar_view(type_store_t& ts)
+         : v_(*reinterpret_cast<value_t*>(ts.data))
+         {}
+
+         explicit 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
+         {
+            explicit details(const std::size_t& vsize,
+                             const unsigned int loop_batch_size = global_loop_batch_size)
+            : batch_size(loop_batch_size   ),
+              remainder (vsize % batch_size),
+              upper_bound(static_cast<int>(vsize - (remainder ? loop_batch_size : 0)))
+            {}
+
+            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)
+            {}
+
+            explicit 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))
+         {}
+
+         explicit vec_data_store(const std::size_t& size)
+         : control_block_(control_block::create(size,reinterpret_cast<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)
+         {
+            const 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 Node>
+      struct node_collector_interface
+      {
+         typedef Node* node_ptr_t;
+         typedef Node** node_pp_t;
+         typedef std::vector<node_pp_t> noderef_list_t;
+
+         virtual ~node_collector_interface() {}
+
+         virtual void collect_nodes(noderef_list_t&) {}
+      };
+
+      template <typename Node>
+      struct node_depth_base;
+
+      template <typename T>
+      class expression_node : public node_collector_interface<expression_node<T> >,
+                              public node_depth_base<expression_node<T> >
+      {
+      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;
+         typedef node_collector_interface<expression_node<T> > nci_t;
+         typedef typename nci_t::noderef_list_t noderef_list_t;
+         typedef node_depth_base<expression_node<T> > ndb_t;
+
+         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_true(const std::pair<expression_node<T>*,bool>& node)
+      {
+         return std::not_equal_to<T>()(T(0),node.first->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_false(const std::pair<expression_node<T>*,bool>& node)
+      {
+         return std::equal_to<T>()(T(0),node.first->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 (0 != node)             &&
+                !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 Node>
+      class node_collection_destructor
+      {
+      public:
+
+         typedef node_collector_interface<Node> nci_t;
+
+         typedef typename nci_t::node_ptr_t     node_ptr_t;
+         typedef typename nci_t::node_pp_t      node_pp_t;
+         typedef typename nci_t::noderef_list_t noderef_list_t;
+
+         static void delete_nodes(node_ptr_t& root)
+         {
+            std::vector<node_pp_t> node_delete_list;
+            node_delete_list.reserve(1000);
+
+            collect_nodes(root, node_delete_list);
+
+            for (std::size_t i = 0; i < node_delete_list.size(); ++i)
+            {
+               node_ptr_t& node = *node_delete_list[i];
+               exprtk_debug(("ncd::delete_nodes() - deleting: %p\n", static_cast<void*>(node)));
+               delete node;
+               node = reinterpret_cast<node_ptr_t>(0);
+            }
+         }
+
+      private:
+
+         static void collect_nodes(node_ptr_t& root, noderef_list_t& node_delete_list)
+         {
+            std::deque<node_ptr_t> node_list;
+            node_list.push_back(root);
+            node_delete_list.push_back(&root);
+
+            noderef_list_t child_node_delete_list;
+            child_node_delete_list.reserve(1000);
+
+            while (!node_list.empty())
+            {
+               node_list.front()->collect_nodes(child_node_delete_list);
+
+               if (!child_node_delete_list.empty())
+               {
+                  for (std::size_t i = 0; i < child_node_delete_list.size(); ++i)
+                  {
+                     node_pp_t& node = child_node_delete_list[i];
+
+                     if (0 == (*node))
+                     {
+                        exprtk_debug(("ncd::collect_nodes() - null node encountered.\n"));
+                     }
+
+                     node_list.push_back(*node);
+                  }
+
+                  node_delete_list.insert(
+                     node_delete_list.end(),
+                     child_node_delete_list.begin(), child_node_delete_list.end());
+
+                  child_node_delete_list.clear();
+               }
+
+               node_list.pop_front();
+            }
+
+            std::reverse(node_delete_list.begin(), node_delete_list.end());
+         }
+      };
+
+      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&, expression_node<T>*& node)
+      {
+         if ((0 == node) || is_variable_node(node) || is_string_node(node))
+         {
+            return;
+         }
+
+         node_collection_destructor<expression_node<T> >
+            ::delete_nodes(node);
+      }
+
+      template <typename T>
+      inline void destroy_node(expression_node<T>*& node)
+      {
+         if (0 != node)
+         {
+            node_collection_destructor<expression_node<T> >
+               ::delete_nodes(node);
+         }
+      }
+
+      template <typename Node>
+      struct node_depth_base
+      {
+         typedef Node* node_ptr_t;
+         typedef std::pair<node_ptr_t,bool> nb_pair_t;
+
+         node_depth_base()
+         : depth_set(false),
+           depth(0)
+         {}
+
+         virtual ~node_depth_base() {}
+
+         virtual std::size_t node_depth() const { return 1; }
+
+         std::size_t compute_node_depth(const Node* const& node) const
+         {
+            if (!depth_set)
+            {
+               depth = 1 + (node ? node->node_depth() : 0);
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         std::size_t compute_node_depth(const nb_pair_t& branch) const
+         {
+            if (!depth_set)
+            {
+               depth = 1 + (branch.first ? branch.first->node_depth() : 0);
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         template <std::size_t N>
+         std::size_t compute_node_depth(const nb_pair_t (&branch)[N]) const
+         {
+            if (!depth_set)
+            {
+               depth = 0;
+               for (std::size_t i = 0; i < N; ++i)
+               {
+                  if (branch[i].first)
+                  {
+                     depth = std::max(depth,branch[i].first->node_depth());
+                  }
+               }
+               depth += 1;
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         template <typename BranchType>
+         std::size_t compute_node_depth(const BranchType& n0, const BranchType& n1) const
+         {
+            if (!depth_set)
+            {
+               depth = 1 + std::max(compute_node_depth(n0), compute_node_depth(n1));
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         template <typename BranchType>
+         std::size_t compute_node_depth(const BranchType& n0, const BranchType& n1,
+                                        const BranchType& n2) const
+         {
+            if (!depth_set)
+            {
+               depth = 1 + std::max(
+                              std::max(compute_node_depth(n0), compute_node_depth(n1)),
+                              compute_node_depth(n2));
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         template <typename BranchType>
+         std::size_t compute_node_depth(const BranchType& n0, const BranchType& n1,
+                                        const BranchType& n2, const BranchType& n3) const
+         {
+            if (!depth_set)
+            {
+               depth = 1 + std::max(
+                           std::max(compute_node_depth(n0), compute_node_depth(n1)),
+                           std::max(compute_node_depth(n2), compute_node_depth(n3)));
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         std::size_t compute_node_depth(const Sequence<node_ptr_t, Allocator>& branch_list) const
+         {
+            if (!depth_set)
+            {
+               for (std::size_t i = 0; i < branch_list.size(); ++i)
+               {
+                  if (branch_list[i])
+                  {
+                     depth = std::max(depth, compute_node_depth(branch_list[i]));
+                  }
+               }
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         std::size_t compute_node_depth(const Sequence<nb_pair_t,Allocator>& branch_list) const
+         {
+            if (!depth_set)
+            {
+               for (std::size_t i = 0; i < branch_list.size(); ++i)
+               {
+                  if (branch_list[i].first)
+                  {
+                     depth = std::max(depth, compute_node_depth(branch_list[i].first));
+                  }
+               }
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         mutable bool depth_set;
+         mutable std::size_t depth;
+
+         template <typename NodeSequence>
+         void collect(node_ptr_t const& node,
+                      const bool deletable,
+                      NodeSequence& delete_node_list) const
+         {
+            if ((0 != node) && deletable)
+            {
+               delete_node_list.push_back(const_cast<node_ptr_t*>(&node));
+            }
+         }
+
+         template <typename NodeSequence>
+         void collect(const nb_pair_t& branch,
+                      NodeSequence& delete_node_list) const
+         {
+            collect(branch.first, branch.second, delete_node_list);
+         }
+
+         template <typename NodeSequence>
+         void collect(Node*& node,
+                      NodeSequence& delete_node_list) const
+         {
+            collect(node, branch_deletable(node), delete_node_list);
+         }
+
+         template <std::size_t N, typename NodeSequence>
+         void collect(const nb_pair_t(&branch)[N],
+                      NodeSequence& delete_node_list) const
+         {
+            for (std::size_t i = 0; i < N; ++i)
+            {
+               collect(branch[i].first, branch[i].second, delete_node_list);
+            }
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence,
+                   typename NodeSequence>
+         void collect(const Sequence<nb_pair_t, Allocator>& branch,
+                      NodeSequence& delete_node_list) const
+         {
+            for (std::size_t i = 0; i < branch.size(); ++i)
+            {
+               collect(branch[i].first, branch[i].second, delete_node_list);
+            }
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence,
+                   typename NodeSequence>
+         void collect(const Sequence<node_ptr_t, Allocator>& branch_list,
+                      NodeSequence& delete_node_list) const
+         {
+            for (std::size_t i = 0; i < branch_list.size(); ++i)
+            {
+               collect(branch_list[i], branch_deletable(branch_list[i]), delete_node_list);
+            }
+         }
+
+         template <typename Boolean,
+                   typename AllocatorT,
+                   typename AllocatorB,
+                   template <typename, typename> class Sequence,
+                   typename NodeSequence>
+         void collect(const Sequence<node_ptr_t, AllocatorT>& branch_list,
+                      const Sequence<Boolean, AllocatorB>& branch_deletable_list,
+                      NodeSequence& delete_node_list) const
+         {
+            for (std::size_t i = 0; i < branch_list.size(); ++i)
+            {
+               collect(branch_list[i], branch_deletable_list[i], delete_node_list);
+            }
+         }
+      };
+
+      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 exprtk_final : 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, std::size_t N>
+      inline void construct_branch_pair(std::pair<expression_node<T>*,bool> (&branch)[N],
+                                        expression_node<T>* b,
+                                        const std::size_t& index)
+      {
+         if (b && (index < N))
+         {
+            branch[index] = std::make_pair(b,branch_deletable(b));
+         }
+      }
+
+      template <typename T>
+      inline void construct_branch_pair(std::pair<expression_node<T>*,bool>& branch, expression_node<T>* b)
+      {
+         if (b)
+         {
+            branch = 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(branch, b0, 0);
+         construct_branch_pair(branch, b1, 1);
+         construct_branch_pair(branch, b2, 2);
+         construct_branch_pair(branch, b3, 3);
+         construct_branch_pair(branch, b4, 4);
+         construct_branch_pair(branch, b5, 5);
+         construct_branch_pair(branch, b6, 6);
+         construct_branch_pair(branch, b7, 7);
+         construct_branch_pair(branch, b8, 8);
+         construct_branch_pair(branch, b9, 9);
+      }
+
+      template <typename T>
+      class null_eq_node exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         explicit null_eq_node(expression_ptr branch, const bool equality = true)
+         : equality_(equality)
+         {
+            construct_branch_pair(branch_, branch);
+         }
+
+         inline T value() const
+         {
+            assert(branch_.first);
+
+            const T v = branch_.first->value();
+            const bool result = details::numeric::is_nan(v);
+
+            if (result)
+               return (equality_) ? T(1) : T(0);
+            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_.first;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
+      private:
+
+         bool equality_;
+         branch_t branch_;
+      };
+
+      template <typename T>
+      class literal_node exprtk_final : 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 exprtk_final
+                                : 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;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         unary_node(const operator_type& opr, expression_ptr branch)
+         : operation_(opr)
+         {
+            construct_branch_pair(branch_,branch);
+         }
+
+         inline T value() const
+         {
+            assert(branch_.first);
+
+            const T arg = branch_.first->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_.first;
+         }
+
+         inline void release()
+         {
+            branch_.second = false;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_final
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
+      protected:
+
+         operator_type operation_;
+         branch_t branch_;
+      };
+
+      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);
+         }
+
+         inline T value() const
+         {
+            assert(branch_[0].first);
+            assert(branch_[1].first);
+
+            const T arg0 = branch_[0].first->value();
+            const T arg1 = branch_[1].first->value();
+
+            return numeric::process<T>(operation_,arg0,arg1);
+         }
+
+         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);
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_final
+         {
+            return expression_node<T>::ndb_t::template compute_node_depth<2>(branch_);
+         }
+
+      protected:
+
+         operator_type operation_;
+         branch_t branch_[2];
+      };
+
+      template <typename T, typename Operation>
+      class binary_ext_node exprtk_final : 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);
+         }
+
+         inline T value() const
+         {
+            assert(branch_[0].first);
+            assert(branch_[1].first);
+
+            const T arg0 = branch_[0].first->value();
+            const T arg1 = branch_[1].first->value();
+
+            return Operation::process(arg0,arg1);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            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);
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::template compute_node_depth<2>(branch_);
+         }
+
+      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);
+         }
+
+         inline T value() const
+         {
+            assert(branch_[0].first);
+            assert(branch_[1].first);
+            assert(branch_[2].first);
+
+            const T arg0 = branch_[0].first->value();
+            const T arg1 = branch_[1].first->value();
+            const T arg2 = branch_[2].first->value();
+
+            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;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_final
+         {
+            return expression_node<T>::ndb_t::template compute_node_depth<3>(branch_);
+         }
+
+      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);
+         }
+
+         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;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_final
+         {
+            return expression_node<T>::ndb_t::template compute_node_depth<4>(branch_);
+         }
+
+      protected:
+
+         operator_type operation_;
+         branch_t branch_[4];
+      };
+
+      template <typename T>
+      class conditional_node exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         conditional_node(expression_ptr condition,
+                          expression_ptr consequent,
+                          expression_ptr alternative)
+         {
+            construct_branch_pair(condition_  , condition  );
+            construct_branch_pair(consequent_ , consequent );
+            construct_branch_pair(alternative_, alternative);
+         }
+
+         inline T value() const
+         {
+            assert(condition_  .first);
+            assert(consequent_ .first);
+            assert(alternative_.first);
+
+            if (is_true(condition_))
+               return consequent_.first->value();
+            else
+               return alternative_.first->value();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_conditional;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(condition_   , node_delete_list);
+            expression_node<T>::ndb_t::collect(consequent_  , node_delete_list);
+            expression_node<T>::ndb_t::collect(alternative_ , node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth
+               (condition_, consequent_, alternative_);
+         }
+
+      private:
+
+         branch_t condition_;
+         branch_t consequent_;
+         branch_t alternative_;
+      };
+
+      template <typename T>
+      class cons_conditional_node exprtk_final : public expression_node<T>
+      {
+      public:
+
+         // Consequent only conditional statement node
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         cons_conditional_node(expression_ptr condition,
+                               expression_ptr consequent)
+         {
+            construct_branch_pair(condition_ , condition );
+            construct_branch_pair(consequent_, consequent);
+         }
+
+         inline T value() const
+         {
+            assert(condition_ .first);
+            assert(consequent_.first);
+
+            if (is_true(condition_))
+               return consequent_.first->value();
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_conditional;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(condition_  , node_delete_list);
+            expression_node<T>::ndb_t::collect(consequent_ , node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::
+               compute_node_depth(condition_, consequent_);
+         }
+
+      private:
+
+         branch_t condition_;
+         branch_t consequent_;
+      };
+
+      #ifndef exprtk_disable_break_continue
+      template <typename T>
+      class break_exception
+      {
+      public:
+
+         explicit break_exception(const T& v)
+         : value(v)
+         {}
+
+         T value;
+      };
+
+      class continue_exception
+      {};
+
+      template <typename T>
+      class break_node exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         break_node(expression_ptr ret = expression_ptr(0))
+         {
+            construct_branch_pair(return_, ret);
+         }
+
+         inline T value() const
+         {
+            throw break_exception<T>(return_.first ? return_.first->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;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(return_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(return_);
+         }
+
+      private:
+
+         branch_t return_;
+      };
+
+      template <typename T>
+      class continue_node exprtk_final : 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
+
+      #ifdef exprtk_enable_runtime_checks
+      struct loop_runtime_checker
+      {
+         loop_runtime_checker(loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0),
+                              loop_runtime_check::loop_types lp_typ = loop_runtime_check::e_invalid)
+         : iteration_count_(0),
+           loop_runtime_check_(loop_rt_chk),
+           loop_type(lp_typ)
+         {}
+
+         inline void reset(const _uint64_t initial_value = 0) const
+         {
+            iteration_count_ = initial_value;
+         }
+
+         inline bool check() const
+         {
+            if (
+                 (0 == loop_runtime_check_) ||
+                 (++iteration_count_ <= loop_runtime_check_->max_loop_iterations)
+               )
+            {
+               return true;
+            }
+
+            loop_runtime_check::violation_context ctxt;
+            ctxt.loop      = loop_type;
+            ctxt.violation = loop_runtime_check::e_iteration_count;
+
+            loop_runtime_check_->handle_runtime_violation(ctxt);
+
+            return false;
+         }
+
+         mutable _uint64_t iteration_count_;
+         mutable loop_runtime_check_ptr loop_runtime_check_;
+         loop_runtime_check::loop_types loop_type;
+      };
+      #else
+      struct loop_runtime_checker
+      {
+         loop_runtime_checker(loop_runtime_check_ptr, loop_runtime_check::loop_types)
+         {}
+
+         inline void reset(const _uint64_t = 0) const
+         {}
+
+         inline bool check() const
+         {
+            return true;
+         }
+      };
+      #endif
+
+      template <typename T>
+      class while_loop_node exprtk_final
+                            : public expression_node<T>,
+                              public loop_runtime_checker
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         while_loop_node(expression_ptr condition,
+                         expression_ptr loop_body,
+                         loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
+         : loop_runtime_checker(loop_rt_chk,loop_runtime_check::e_while_loop)
+         {
+            construct_branch_pair(condition_, condition);
+            construct_branch_pair(loop_body_, loop_body);
+         }
+
+         inline T value() const
+         {
+            assert(condition_.first);
+            assert(loop_body_.first);
+
+            T result = T(0);
+
+            loop_runtime_checker::reset();
+
+            while (is_true(condition_) && loop_runtime_checker::check())
+            {
+               result = loop_body_.first->value();
+            }
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_while;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(condition_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_ , node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(condition_, loop_body_);
+         }
+
+      private:
+
+         branch_t condition_;
+         branch_t loop_body_;
+      };
+
+      template <typename T>
+      class repeat_until_loop_node exprtk_final
+                                   : public expression_node<T>,
+                                     public loop_runtime_checker
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         repeat_until_loop_node(expression_ptr condition,
+                                expression_ptr loop_body,
+                                loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
+         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_repeat_until_loop)
+         {
+            construct_branch_pair(condition_, condition);
+            construct_branch_pair(loop_body_, loop_body);
+         }
+
+         inline T value() const
+         {
+            assert(condition_.first);
+            assert(loop_body_.first);
+
+            T result = T(0);
+
+            loop_runtime_checker::reset(1);
+
+            do
+            {
+               result = loop_body_.first->value();
+            }
+            while (is_false(condition_.first) && loop_runtime_checker::check());
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_repeat;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(condition_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_ , node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(condition_, loop_body_);
+         }
+
+      private:
+
+         branch_t condition_;
+         branch_t loop_body_;
+      };
+
+      template <typename T>
+      class for_loop_node exprtk_final
+                          : public expression_node<T>,
+                            public loop_runtime_checker
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         for_loop_node(expression_ptr initialiser,
+                       expression_ptr condition,
+                       expression_ptr incrementor,
+                       expression_ptr loop_body,
+                       loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
+         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_for_loop)
+         {
+            construct_branch_pair(initialiser_, initialiser);
+            construct_branch_pair(condition_  , condition  );
+            construct_branch_pair(incrementor_, incrementor);
+            construct_branch_pair(loop_body_  , loop_body  );
+         }
+
+         inline T value() const
+         {
+            assert(condition_.first);
+            assert(loop_body_.first);
+
+            T result = T(0);
+
+            loop_runtime_checker::reset();
+
+            if (initialiser_.first)
+               initialiser_.first->value();
+
+            if (incrementor_.first)
+            {
+               while (is_true(condition_) && loop_runtime_checker::check())
+               {
+                  result = loop_body_.first->value();
+                  incrementor_.first->value();
+               }
+            }
+            else
+            {
+               while (is_true(condition_) && loop_runtime_checker::check())
+               {
+                  result = loop_body_.first->value();
+               }
+            }
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_for;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(initialiser_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(condition_   , node_delete_list);
+            expression_node<T>::ndb_t::collect(incrementor_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_   , node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth
+               (initialiser_, condition_, incrementor_, loop_body_);
+         }
+
+      private:
+
+         branch_t initialiser_;
+         branch_t condition_  ;
+         branch_t incrementor_;
+         branch_t loop_body_  ;
+      };
+
+      #ifndef exprtk_disable_break_continue
+      template <typename T>
+      class while_loop_bc_node exprtk_final
+                               : public expression_node<T>,
+                                 public loop_runtime_checker
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         while_loop_bc_node(expression_ptr condition,
+                            expression_ptr loop_body,
+                            loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
+         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_while_loop)
+         {
+            construct_branch_pair(condition_, condition);
+            construct_branch_pair(loop_body_, loop_body);
+         }
+
+         inline T value() const
+         {
+            assert(condition_.first);
+            assert(loop_body_.first);
+
+            T result = T(0);
+
+            loop_runtime_checker::reset();
+
+            while (is_true(condition_) && loop_runtime_checker::check())
+            {
+               try
+               {
+                  result = loop_body_.first->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;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(condition_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_ , node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(condition_, loop_body_);
+         }
+
+      private:
+
+         branch_t condition_;
+         branch_t loop_body_;
+      };
+
+      template <typename T>
+      class repeat_until_loop_bc_node exprtk_final
+                                      : public expression_node<T>,
+                                        public loop_runtime_checker
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         repeat_until_loop_bc_node(expression_ptr condition,
+                                   expression_ptr loop_body,
+                                   loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
+         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_repeat_until_loop)
+         {
+            construct_branch_pair(condition_, condition);
+            construct_branch_pair(loop_body_, loop_body);
+         }
+
+         inline T value() const
+         {
+            assert(condition_.first);
+            assert(loop_body_.first);
+
+            T result = T(0);
+
+            loop_runtime_checker::reset();
+
+            do
+            {
+               try
+               {
+                  result = loop_body_.first->value();
+               }
+               catch(const break_exception<T>& e)
+               {
+                  return e.value;
+               }
+               catch(const continue_exception&)
+               {}
+            }
+            while (is_false(condition_.first) && loop_runtime_checker::check());
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_repeat;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(condition_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_ , node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(condition_, loop_body_);
+         }
+
+      private:
+
+         branch_t condition_;
+         branch_t loop_body_;
+      };
+
+      template <typename T>
+      class for_loop_bc_node exprtk_final
+                             : public expression_node<T>,
+                               public loop_runtime_checker
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         for_loop_bc_node(expression_ptr initialiser,
+                          expression_ptr condition,
+                          expression_ptr incrementor,
+                          expression_ptr loop_body,
+                          loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
+         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_for_loop)
+         {
+            construct_branch_pair(initialiser_, initialiser);
+            construct_branch_pair(condition_  , condition  );
+            construct_branch_pair(incrementor_, incrementor);
+            construct_branch_pair(loop_body_  , loop_body  );
+         }
+
+         inline T value() const
+         {
+            assert(condition_.first);
+            assert(loop_body_.first);
+
+            T result = T(0);
+
+            loop_runtime_checker::reset();
+
+            if (initialiser_.first)
+               initialiser_.first->value();
+
+            if (incrementor_.first)
+            {
+               while (is_true(condition_) && loop_runtime_checker::check())
+               {
+                  try
+                  {
+                     result = loop_body_.first->value();
+                  }
+                  catch(const break_exception<T>& e)
+                  {
+                     return e.value;
+                  }
+                  catch(const continue_exception&)
+                  {}
+
+                  incrementor_.first->value();
+               }
+            }
+            else
+            {
+               while (is_true(condition_) && loop_runtime_checker::check())
+               {
+                  try
+                  {
+                     result = loop_body_.first->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;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(initialiser_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(condition_   , node_delete_list);
+            expression_node<T>::ndb_t::collect(incrementor_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_   , node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth
+               (initialiser_, condition_, incrementor_, loop_body_);
+         }
+
+      private:
+
+         branch_t initialiser_;
+         branch_t condition_  ;
+         branch_t incrementor_;
+         branch_t loop_body_  ;
+      };
+      #endif
+
+      template <typename T>
+      class switch_node : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         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());
+
+            for (std::size_t i = 0; i < arg_list.size(); ++i)
+            {
+               if (arg_list[i])
+               {
+                  construct_branch_pair(arg_list_[i], arg_list[i]);
+               }
+               else
+               {
+                  arg_list_.clear();
+                  return;
+               }
+            }
+         }
+
+         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    ].first;
+                  expression_ptr consequent = arg_list_[i + 1].first;
+
+                  if (is_true(condition))
+                  {
+                     return consequent->value();
+                  }
+               }
+
+               return arg_list_[upper_bound].first->value();
+            }
+            else
+               return std::numeric_limits<T>::quiet_NaN();
+         }
+
+         inline typename expression_node<T>::node_type type() const exprtk_final
+         {
+            return expression_node<T>::e_switch;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(arg_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_final
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(arg_list_);
+         }
+
+      protected:
+
+         std::vector<branch_t> arg_list_;
+      };
+
+      template <typename T, typename Switch_N>
+      class switch_n_node exprtk_final : 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 exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         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());
+
+            for (std::size_t i = 0; i < arg_list.size(); ++i)
+            {
+               if (arg_list[i])
+               {
+                  construct_branch_pair(arg_list_[i], arg_list[i]);
+               }
+               else
+               {
+                  arg_list_.clear();
+                  return;
+               }
+            }
+         }
+
+         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    ].first;
+               expression_ptr consequent = arg_list_[i + 1].first;
+
+               if (is_true(condition))
+               {
+                  result = consequent->value();
+               }
+            }
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_mswitch;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(arg_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_final
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(arg_list_);
+         }
+
+      private:
+
+         std::vector<branch_t> arg_list_;
+      };
+
+      template <typename T>
+      class ivariable
+      {
+      public:
+
+         virtual ~ivariable()
+         {}
+
+         virtual T& ref() = 0;
+         virtual const T& ref() const = 0;
+      };
+
+      template <typename T>
+      class variable_node exprtk_final
+                          : 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() const
+         {
+           return ( n0_c.first &&  n1_c.first) &&
+                  (!n0_e.first && !n1_e.first);
+         }
+
+         bool var_range() const
+         {
+           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)
+            {
+               r0 = static_cast<std::size_t>(details::numeric::to_int64(n0_e.second->value()));
+            }
+            else
+               return false;
+
+            if (n1_c.first)
+               r1 = n1_c.second;
+            else if (n1_e.first)
+            {
+               r1 = static_cast<std::size_t>(details::numeric::to_int64(n1_e.second->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;
+
+            #ifndef exprtk_enable_runtime_checks
+            return (r0 <= r1);
+            #else
+            return range_runtime_check(r0, r1, size);
+            #endif
+         }
+
+         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;
+
+         #ifdef exprtk_enable_runtime_checks
+         bool range_runtime_check(const std::size_t r0,
+                                  const std::size_t r1,
+                                  const std::size_t size) const
+         {
+            if (r0 >= size)
+            {
+               throw std::runtime_error("range error: (r0 < 0) || (r0 >= size)");
+               return false;
+            }
+
+            if (r1 >= size)
+            {
+               throw std::runtime_error("range error: (r1 < 0) || (r1 >= size)");
+               return false;
+            }
+
+            return (r0 <= r1);
+         }
+         #endif
+      };
+
+      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 exprtk_final
+                        : 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 exprtk_final
+                             : public expression_node<T>,
+                               public ivariable      <T>
+      {
+      public:
+
+         typedef expression_node<T>*            expression_ptr;
+         typedef vector_holder<T>               vector_holder_t;
+         typedef vector_holder_t*               vector_holder_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         vector_elem_node(expression_ptr index, vector_holder_ptr vec_holder)
+         : vec_holder_(vec_holder),
+           vector_base_((*vec_holder)[0])
+         {
+            construct_branch_pair(index_, index);
+         }
+
+         inline T value() const
+         {
+            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
+         }
+
+         inline T& ref()
+         {
+            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
+         }
+
+         inline const T& ref() const
+         {
+            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_.first->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_);
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(index_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(index_);
+         }
+
+      private:
+
+         vector_holder_ptr vec_holder_;
+         T* vector_base_;
+         branch_t index_;
+      };
+
+      template <typename T>
+      class rebasevector_elem_node exprtk_final
+                                   : public expression_node<T>,
+                                     public ivariable      <T>
+      {
+      public:
+
+         typedef expression_node<T>*            expression_ptr;
+         typedef vector_holder<T>               vector_holder_t;
+         typedef vector_holder_t*               vector_holder_ptr;
+         typedef vec_data_store<T>              vds_t;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         rebasevector_elem_node(expression_ptr index, vector_holder_ptr vec_holder)
+         : vector_holder_(vec_holder),
+           vds_((*vector_holder_).size(),(*vector_holder_)[0])
+         {
+            vector_holder_->set_ref(&vds_.ref());
+            construct_branch_pair(index_, index);
+         }
+
+         inline T value() const
+         {
+            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
+         }
+
+         inline T& ref()
+         {
+            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
+         }
+
+         inline const T& ref() const
+         {
+            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_.first->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_);
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(index_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(index_);
+         }
+
+      private:
+
+         vector_holder_ptr vector_holder_;
+         vds_t             vds_;
+         branch_t          index_;
+      };
+
+      template <typename T>
+      class rebasevector_celem_node exprtk_final
+                                    : public expression_node<T>,
+                                      public ivariable      <T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef vector_holder<T>    vector_holder_t;
+         typedef vector_holder_t*    vector_holder_ptr;
+         typedef vec_data_store<T>   vds_t;
+
+         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 exprtk_final : 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)
+         {}
+
+         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;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(initialiser_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(initialiser_list_);
+         }
+
+      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 exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef variable_node<T>*   variable_node_ptr;
+
+         swap_node(variable_node_ptr var0, variable_node_ptr var1)
+         : var0_(var0),
+           var1_(var1)
+         {}
+
+         inline T value() const
+         {
+            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 exprtk_final : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef ivariable<T>* ivariable_ptr;
+
+         swap_generic_node(expression_ptr var0, expression_ptr var1)
+         : binary_node<T>(details::e_swap, var0, var1),
+           var0_(dynamic_cast<ivariable_ptr>(var0)),
+           var1_(dynamic_cast<ivariable_ptr>(var1))
+         {}
+
+         inline T value() const
+         {
+            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 exprtk_final
+                             : public binary_node     <T>,
+                               public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node<T>*  expression_ptr;
+         typedef vector_node<T>*     vector_node_ptr;
+         typedef vec_data_store<T>             vds_t;
+
+         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
+         {
+            assert(binary_node<T>::branch_[0].first);
+            assert(binary_node<T>::branch_[1].first);
+
+            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 exprtk_final
+                           : 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 exprtk_final
+                              : 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 exprtk_final
+                                    : 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 exprtk_final
+                                      : public expression_node <T>,
+                                        public string_base_node<T>,
+                                        public range_interface <T>
+      {
+      public:
+
+         typedef expression_node <T>*      expression_ptr;
+         typedef stringvar_node  <T>*     strvar_node_ptr;
+         typedef string_base_node<T>*        str_base_ptr;
+         typedef range_pack      <T>              range_t;
+         typedef range_t*                       range_ptr;
+         typedef range_interface<T>              irange_t;
+         typedef irange_t*                     irange_ptr;
+         typedef std::pair<expression_ptr,bool>  branch_t;
+
+
+         generic_string_range_node(expression_ptr str_branch, const range_t& brange)
+         : initialised_(false),
+           str_base_ptr_ (0),
+           str_range_ptr_(0),
+           base_range_(brange)
+         {
+            range_.n0_c = std::make_pair<bool,std::size_t>(true,0);
+            range_.n1_c = std::make_pair<bool,std::size_t>(true,0);
+            range_.cache.first  = range_.n0_c.second;
+            range_.cache.second = range_.n1_c.second;
+
+            construct_branch_pair(branch_, str_branch);
+
+            if (is_generally_string_node(branch_.first))
+            {
+               str_base_ptr_ = dynamic_cast<str_base_ptr>(branch_.first);
+
+               if (0 == str_base_ptr_)
+                  return;
+
+               str_range_ptr_ = dynamic_cast<irange_ptr>(branch_.first);
+
+               if (0 == str_range_ptr_)
+                  return;
+            }
+
+            initialised_ = (str_base_ptr_ && str_range_ptr_);
+         }
+
+        ~generic_string_range_node()
+         {
+            base_range_.free();
+         }
+
+         inline T value() const
+         {
+            if (initialised_)
+            {
+               assert(branch_.first);
+
+               branch_.first->value();
+
+               std::size_t str_r0 = 0;
+               std::size_t str_r1 = 0;
+
+               std::size_t r0 = 0;
+               std::size_t r1 = 0;
+
+               const range_t& range = str_range_ptr_->range_ref();
+
+               const std::size_t base_str_size = str_base_ptr_->size();
+
+               if (
+                    range      (str_r0, str_r1, base_str_size) &&
+                    base_range_(    r0,     r1, base_str_size - str_r0)
+                  )
+               {
+                  const std::size_t size = (r1 - r0) + 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;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
+      private:
+
+         bool                initialised_;
+         branch_t                 branch_;
+         str_base_ptr       str_base_ptr_;
+         irange_ptr        str_range_ptr_;
+         mutable range_t      base_range_;
+         mutable range_t           range_;
+         mutable std::string       value_;
+      };
+
+      template <typename T>
+      class string_concat_node exprtk_final
+                               : public binary_node     <T>,
+                                 public string_base_node<T>,
+                                 public range_interface <T>
+      {
+      public:
+
+         typedef 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_)
+            {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
+               binary_node<T>::branch_[0].first->value();
+               binary_node<T>::branch_[1].first->value();
+
+               std::size_t str0_r0 = 0;
+               std::size_t str0_r1 = 0;
+
+               std::size_t str1_r0 = 0;
+               std::size_t str1_r1 = 0;
+
+               const range_t& range0 = str0_range_ptr_->range_ref();
+               const range_t& range1 = str1_range_ptr_->range_ref();
+
+               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 exprtk_final
+                             : public binary_node     <T>,
+                               public string_base_node<T>,
+                               public range_interface <T>
+      {
+      public:
+
+         typedef expression_node <T>*  expression_ptr;
+         typedef stringvar_node  <T>* strvar_node_ptr;
+         typedef 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_)
+            {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
+               binary_node<T>::branch_[0].first->value();
+               binary_node<T>::branch_[1].first->value();
+
+               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 exprtk_final : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node <T>* expression_ptr;
+         typedef string_base_node<T>*   str_base_ptr;
+         typedef range_pack      <T>         range_t;
+         typedef range_t*                  range_ptr;
+         typedef range_interface<T>         irange_t;
+         typedef irange_t*                irange_ptr;
+
+         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_)
+            {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
+               binary_node<T>::branch_[0].first->value();
+               binary_node<T>::branch_[1].first->value();
+
+               std::size_t str0_r0 = 0;
+               std::size_t str0_r1 = 0;
+
+               std::size_t str1_r0 = 0;
+               std::size_t str1_r1 = 0;
+
+               const range_t& range0 = (*str0_range_ptr_);
+               const range_t& range1 = (*str1_range_ptr_);
+
+               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 exprtk_final : 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 exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node <T>*      expression_ptr;
+         typedef string_base_node<T>*        str_base_ptr;
+         typedef std::pair<expression_ptr,bool>  branch_t;
+
+
+         explicit string_size_node(expression_ptr branch)
+         : str_base_ptr_(0)
+         {
+            construct_branch_pair(branch_, branch);
+
+            if (is_generally_string_node(branch_.first))
+            {
+               str_base_ptr_ = dynamic_cast<str_base_ptr>(branch_.first);
+
+               if (0 == str_base_ptr_)
+                  return;
+            }
+         }
+
+         inline T value() const
+         {
+            T result = std::numeric_limits<T>::quiet_NaN();
+
+            if (str_base_ptr_)
+            {
+               branch_.first->value();
+               result = T(str_base_ptr_->size());
+            }
+
+            return result;
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_stringsize;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
+      private:
+
+         branch_t           branch_;
+         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 exprtk_final
+                                   : public binary_node     <T>,
+                                     public string_base_node<T>,
+                                     public range_interface <T>
+      {
+      public:
+
+         typedef expression_node <T>*  expression_ptr;
+         typedef stringvar_node  <T>* strvar_node_ptr;
+         typedef 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_)
+            {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
+               binary_node<T>::branch_[1].first->value();
+
+               std::size_t r0 = 0;
+               std::size_t r1 = 0;
+
+               const range_t& range = (*str1_range_ptr_);
+
+               if (range(r0, r1, str1_base_ptr_->size()))
+               {
+                  AssignmentProcess::execute(str0_node_ptr_->ref(),
+                                             str1_base_ptr_->base() + r0,
+                                             (r1 - r0) + 1);
+
+                  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 exprtk_final
+                                         : public binary_node     <T>,
+                                           public string_base_node<T>,
+                                           public range_interface <T>
+      {
+      public:
+
+         typedef expression_node  <T>*   expression_ptr;
+         typedef stringvar_node   <T>*  strvar_node_ptr;
+         typedef string_range_node<T>* str_rng_node_ptr;
+         typedef string_base_node <T>*     str_base_ptr;
+         typedef range_pack       <T>           range_t;
+         typedef range_t*                     range_ptr;
+         typedef range_interface<T>            irange_t;
+         typedef irange_t*                   irange_ptr;
+
+         assignment_string_range_node(const operator_type& opr,
+                                      expression_ptr branch0,
+                                      expression_ptr branch1)
+         : binary_node<T>(opr, branch0, branch1),
+           initialised_(false),
+           str0_base_ptr_     (0),
+           str1_base_ptr_     (0),
+           str0_rng_node_ptr_ (0),
+           str0_range_ptr_    (0),
+           str1_range_ptr_    (0)
+         {
+            if (is_string_range_node(binary_node<T>::branch_[0].first))
+            {
+               str0_rng_node_ptr_ = static_cast<str_rng_node_ptr>(binary_node<T>::branch_[0].first);
+
+               str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
+
+               irange_ptr range = dynamic_cast<irange_ptr>(binary_node<T>::branch_[0].first);
+
+               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_rng_node_ptr_ &&
+                           str0_range_ptr_    &&
+                           str1_range_ptr_    ;
+         }
+
+         inline T value() const
+         {
+            if (initialised_)
+            {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
+               binary_node<T>::branch_[0].first->value();
+               binary_node<T>::branch_[1].first->value();
+
+               std::size_t s0_r0 = 0;
+               std::size_t s0_r1 = 0;
+
+               std::size_t s1_r0 = 0;
+               std::size_t s1_r1 = 0;
+
+               const range_t& range0 = (*str0_range_ptr_);
+               const range_t& range1 = (*str1_range_ptr_);
+
+               if (
+                    range0(s0_r0, s0_r1, str0_base_ptr_->size()) &&
+                    range1(s1_r0, s1_r1, str1_base_ptr_->size())
+                  )
+               {
+                  const std::size_t size = std::min((s0_r1 - s0_r0), (s1_r1 - s1_r0)) + 1;
+
+                  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_base_ptr_->str();
+         }
+
+         char_cptr base() const
+         {
+            return str0_base_ptr_->base();
+         }
+
+         std::size_t size() const
+         {
+            return str0_base_ptr_->size();
+         }
+
+         range_t& range_ref()
+         {
+            return str0_rng_node_ptr_->range_ref();
+         }
+
+         const range_t& range_ref() const
+         {
+            return str0_rng_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_;
+         str_rng_node_ptr str0_rng_node_ptr_;
+         range_ptr        str0_range_ptr_;
+         range_ptr        str1_range_ptr_;
+      };
+
+      template <typename T>
+      class conditional_string_node exprtk_final
+                                    : 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 condition,
+                                 expression_ptr consequent,
+                                 expression_ptr alternative)
+         : trinary_node<T>(details::e_default,consequent,alternative,condition),
+           initialised_(false),
+           str0_base_ptr_ (0),
+           str1_base_ptr_ (0),
+           str0_range_ptr_(0),
+           str1_range_ptr_(0),
+           condition_    (condition),
+           consequent_  (consequent),
+           alternative_(alternative)
+         {
+            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_)
+            {
+               assert(condition_  );
+               assert(consequent_ );
+               assert(alternative_);
+
+               std::size_t r0 = 0;
+               std::size_t r1 = 0;
+
+               if (is_true(condition_))
+               {
+                  consequent_->value();
+
+                  const 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();
+
+                  const range_t& range = str1_range_ptr_->range_ref();
+
+                  if (range(r0, r1, str1_base_ptr_->size()))
+                  {
+                     const std::size_t size = (r1 - r0) + 1;
+
+                     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 condition_;
+         expression_ptr consequent_;
+         expression_ptr alternative_;
+      };
+
+      template <typename T>
+      class cons_conditional_str_node exprtk_final
+                                      : 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 condition,
+                                   expression_ptr consequent)
+         : binary_node<T>(details::e_default, consequent, condition),
+           initialised_(false),
+           str0_base_ptr_ (0),
+           str0_range_ptr_(0),
+           condition_ (condition),
+           consequent_(consequent)
+         {
+            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_)
+            {
+               assert(condition_ );
+               assert(consequent_);
+
+               if (is_true(condition_))
+               {
+                  consequent_->value();
+
+                  const 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 condition_;
+         expression_ptr consequent_;
+      };
+
+      template <typename T, typename VarArgFunction>
+      class str_vararg_node exprtk_final
+                            : public expression_node <T>,
+                              public string_base_node<T>,
+                              public range_interface <T>
+      {
+      public:
+
+         typedef expression_node <T>*     expression_ptr;
+         typedef string_base_node<T>*       str_base_ptr;
+         typedef range_pack      <T>             range_t;
+         typedef range_t*                      range_ptr;
+         typedef range_interface<T>             irange_t;
+         typedef irange_t*                    irange_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         explicit str_vararg_node(const Sequence<expression_ptr,Allocator>& arg_list)
+         : initialised_(false),
+           str_base_ptr_ (0),
+           str_range_ptr_(0)
+         {
+            construct_branch_pair(final_node_, const_cast<expression_ptr>(arg_list.back()));
+
+            if (0 == final_node_.first)
+               return;
+            else if (!is_generally_string_node(final_node_.first))
+               return;
+
+            str_base_ptr_ = dynamic_cast<str_base_ptr>(final_node_.first);
+
+            if (0 == str_base_ptr_)
+               return;
+
+            str_range_ptr_ = dynamic_cast<irange_ptr>(final_node_.first);
+
+            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);
+
+               for (std::size_t i = 0; i < arg_list_size; ++i)
+               {
+                  if (arg_list[i])
+                  {
+                     construct_branch_pair(arg_list_[i], arg_list[i]);
+                  }
+                  else
+                  {
+                     arg_list_.clear();
+                     return;
+                  }
+               }
+            }
+         }
+
+         inline T value() const
+         {
+            if (!arg_list_.empty())
+            {
+               VarArgFunction::process(arg_list_);
+            }
+
+            final_node_.first->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;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(final_node_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(arg_list_   , node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return std::max(
+               expression_node<T>::ndb_t::compute_node_depth(final_node_),
+               expression_node<T>::ndb_t::compute_node_depth(arg_list_  ));
+         }
+
+      private:
+
+         bool                  initialised_;
+         branch_t              final_node_;
+         str_base_ptr          str_base_ptr_;
+         irange_ptr            str_range_ptr_;
+         std::vector<branch_t> arg_list_;
+      };
+      #endif
+
+      template <typename T, std::size_t N>
+      inline T axn(const T a, const T x)
+      {
+         // a*x^n
+         return a * exprtk::details::numeric::fast_exp<T,N>::result(x);
+      }
+
+      template <typename T, std::size_t N>
+      inline T axnb(const T a, const T x, const T b)
+      {
+         // a*x^n+b
+         return a * exprtk::details::numeric::fast_exp<T,N>::result(x) + b;
+      }
+
+      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 exprtk_final : 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
+         {
+            assert(trinary_node<T>::branch_[0].first);
+            assert(trinary_node<T>::branch_[1].first);
+            assert(trinary_node<T>::branch_[2].first);
+
+            const T x = trinary_node<T>::branch_[0].first->value();
+            const T y = trinary_node<T>::branch_[1].first->value();
+            const T z = trinary_node<T>::branch_[2].first->value();
+
+            return SpecialFunction::process(x, y, z);
+         }
+      };
+
+      template <typename T, typename SpecialFunction>
+      class sf4_node exprtk_final : 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
+         {
+            assert(quaternary_node<T>::branch_[0].first);
+            assert(quaternary_node<T>::branch_[1].first);
+            assert(quaternary_node<T>::branch_[2].first);
+            assert(quaternary_node<T>::branch_[3].first);
+
+            const T x = quaternary_node<T>::branch_[0].first->value();
+            const T y = quaternary_node<T>::branch_[1].first->value();
+            const T z = quaternary_node<T>::branch_[2].first->value();
+            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 exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         sf3_var_node(const T& v0, const T& v1, const T& v2)
+         : v0_(v0),
+           v1_(v1),
+           v2_(v2)
+         {}
+
+         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 exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+
+         sf4_var_node(const T& v0, const T& v1, const T& v2, const T& v3)
+         : v0_(v0),
+           v1_(v1),
+           v2_(v2),
+           v3_(v3)
+         {}
+
+         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 exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         explicit vararg_node(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])
+               {
+                  construct_branch_pair(arg_list_[i],arg_list[i]);
+               }
+               else
+               {
+                  arg_list_.clear();
+                  return;
+               }
+            }
+         }
+
+         inline T value() const
+         {
+            return VarArgFunction::process(arg_list_);
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_vararg;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(arg_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(arg_list_);
+         }
+
+      private:
+
+         std::vector<branch_t> arg_list_;
+      };
+
+      template <typename T, typename VarArgFunction>
+      class vararg_varnode exprtk_final : 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 exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         explicit vectorize_node(const expression_ptr v)
+         : ivec_ptr_(0)
+         {
+            construct_branch_pair(v_, v);
+
+            if (is_ivector_node(v_.first))
+            {
+               ivec_ptr_ = dynamic_cast<vector_interface<T>*>(v_.first);
+            }
+            else
+               ivec_ptr_ = 0;
+         }
+
+         inline T value() const
+         {
+            if (ivec_ptr_)
+            {
+               assert(v_.first);
+
+               v_.first->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;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(v_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(v_);
+         }
+
+      private:
+
+         vector_interface<T>* ivec_ptr_;
+         branch_t                    v_;
+      };
+
+      template <typename T>
+      class assignment_node exprtk_final : 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_)
+            {
+               assert(binary_node<T>::branch_[1].first);
+
+               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 exprtk_final : 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_)
+            {
+               assert(binary_node<T>::branch_[1].first);
+
+               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 exprtk_final : 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_)
+            {
+               assert(binary_node<T>::branch_[1].first);
+
+               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 exprtk_final : 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_)
+            {
+               assert(binary_node<T>::branch_[1].first);
+
+               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 exprtk_final
+                                : public binary_node     <T>,
+                                  public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef vector_node<T>*    vector_node_ptr;
+         typedef vec_data_store<T>            vds_t;
+
+         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_)
+            {
+               assert(binary_node<T>::branch_[1].first);
+
+               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 exprtk_final
+                                   : public binary_node     <T>,
+                                     public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node<T>*  expression_ptr;
+         typedef vector_node<T>*     vector_node_ptr;
+         typedef vec_data_store<T>             vds_t;
+
+         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_)
+            {
+               assert(binary_node<T>::branch_[1].first);
+
+               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 exprtk_final : 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_)
+            {
+               assert(binary_node<T>::branch_[1].first);
+
+               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 exprtk_final : 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_)
+            {
+               assert(binary_node<T>::branch_[1].first);
+
+               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 exprtk_final : 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_)
+            {
+               assert(binary_node<T>::branch_[1].first);
+
+               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 exprtk_final : 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_)
+            {
+               assert(binary_node<T>::branch_[1].first);
+
+               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 exprtk_final
+                                   : public binary_node     <T>,
+                                     public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node<T>*  expression_ptr;
+         typedef vector_node<T>*     vector_node_ptr;
+         typedef vec_data_store<T>             vds_t;
+
+         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_)
+            {
+               assert(binary_node<T>::branch_[1].first);
+
+               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 exprtk_final
+                                      : public binary_node     <T>,
+                                        public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node<T>*  expression_ptr;
+         typedef vector_node<T>*     vector_node_ptr;
+         typedef vec_data_store<T>             vds_t;
+
+         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_)
+            {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
+               binary_node<T>::branch_[0].first->value();
+               binary_node<T>::branch_[1].first->value();
+
+                     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 exprtk_final
+                                  : public binary_node     <T>,
+                                    public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node<T>*    expression_ptr;
+         typedef vector_node<T>*       vector_node_ptr;
+         typedef vector_holder<T>*   vector_holder_ptr;
+         typedef vec_data_store<T>               vds_t;
+
+         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_)
+            {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
+               binary_node<T>::branch_[0].first->value();
+               binary_node<T>::branch_[1].first->value();
+
+               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 exprtk_final
+                                  : public binary_node     <T>,
+                                    public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node<T>*    expression_ptr;
+         typedef vector_node<T>*       vector_node_ptr;
+         typedef vector_holder<T>*   vector_holder_ptr;
+         typedef vec_data_store<T>               vds_t;
+
+         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_)
+            {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
+                           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 exprtk_final
+                                  : public binary_node     <T>,
+                                    public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node<T>*    expression_ptr;
+         typedef vector_node<T>*       vector_node_ptr;
+         typedef vector_holder<T>*   vector_holder_ptr;
+         typedef vec_data_store<T>               vds_t;
+
+         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_)
+            {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
+               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 exprtk_final
+                              : public unary_node      <T>,
+                                public vector_interface<T>
+      {
+      public:
+
+         typedef expression_node<T>*    expression_ptr;
+         typedef vector_node<T>*       vector_node_ptr;
+         typedef vector_holder<T>*   vector_holder_ptr;
+         typedef vec_data_store<T>               vds_t;
+
+         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_.first))
+            {
+               vec0_node_ptr_ = static_cast<vector_node_ptr>(unary_node<T>::branch_.first);
+            }
+            else if (is_ivector_node(unary_node<T>::branch_.first))
+            {
+               vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
+
+               if (0 != (vi = dynamic_cast<vector_interface<T>*>(unary_node<T>::branch_.first)))
+               {
+                  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
+         {
+            assert(unary_node<T>::branch_.first);
+
+            unary_node<T>::branch_.first->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 exprtk_final : 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
+         {
+            assert(binary_node<T>::branch_[0].first);
+            assert(binary_node<T>::branch_[1].first);
+
+            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 exprtk_final : 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
+         {
+            assert(binary_node<T>::branch_[0].first);
+            assert(binary_node<T>::branch_[1].first);
+
+            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 exprtk_final : 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)
+         {}
+
+         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
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_function;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::template compute_node_depth<N>(branch_);
+         }
+
+         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]); }
+         };
+
+      private:
+
+         ifunction*  function_;
+         std::size_t parameter_count_;
+         branch_t    branch_[N];
+      };
+
+      template <typename T, typename IFunction>
+      class function_N_node<T,IFunction,0> exprtk_final : 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 exprtk_final : 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());
+         }
+
+         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;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            for (std::size_t i = 0; i < arg_list_.size(); ++i)
+            {
+               if (arg_list_[i] && !details::is_variable_node(arg_list_[i]))
+               {
+                  node_delete_list.push_back(&arg_list_[i]);
+               }
+            }
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(arg_list_);
+         }
+
+      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;
+
+         explicit generic_function_node(const std::vector<expression_ptr>& arg_list,
+                                        GenericFunction* func = reinterpret_cast<GenericFunction*>(0))
+         : function_(func),
+           arg_list_(arg_list)
+         {}
+
+         virtual ~generic_function_node()
+         {}
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const exprtk_final
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
+         virtual bool init_branches()
+         {
+            expr_as_vec1_store_.resize(arg_list_.size(),T(0)               );
+            typestore_list_    .resize(arg_list_.size(),type_store_t()     );
+            range_list_        .resize(arg_list_.size(),range_data_type_t());
+            branch_            .resize(arg_list_.size(),branch_t(reinterpret_cast<expression_ptr>(0),false));
+
+            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;
+
+                  const 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)
+               {
+                  const range_t& rp = (*rdt.range);
+                  std::size_t r0    = 0;
+                  std::size_t r1    = 0;
+
+                  if (rp(r0, r1, rdt.size))
+                  {
+                     type_store_t& ts = typestore_list_[i];
+
+                     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 exprtk_final
+         {
+            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 exprtk_final : 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 exprtk_final : 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 exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef results_context<T>  results_context_t;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         return_envelope_node(expression_ptr body, results_context_t& rc)
+         : results_context_(&rc  ),
+           return_invoked_ (false)
+         {
+            construct_branch_pair(body_, body);
+         }
+
+         inline T value() const
+         {
+            assert(body_.first);
+
+            try
+            {
+               return_invoked_ = false;
+               results_context_->clear();
+
+               return body_.first->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_;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(body_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(body_);
+         }
+
+      private:
+
+         results_context_t* results_context_;
+         mutable bool        return_invoked_;
+         branch_t                      body_;
+      };
+      #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(std::pair<details::expression_node<T>*,bool> n)
+      {
+         return n.first->value();
+      }
+
+      template <typename T>
+      inline T value(const T* t)
+      {
+         return (*t);
+      }
+
+      template <typename T>
+      inline T value(const 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)
+            {
+               const 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)
+            {
+               const 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 exprtk_final : 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 exprtk_final : public expression_node<T>
+      {
+      public:
+
+         // UOpr1(v0) Op UOpr2(v1)
+
+         typedef expression_node<T>* expression_ptr;
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::bfunc_t      bfunc_t;
+         typedef typename functor_t::ufunc_t      ufunc_t;
+
+         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 exprtk_final : public expression_node<T>
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+         typedef Operation operation_t;
+
+         explicit unary_branch_node(expression_ptr branch)
+         {
+            construct_branch_pair(branch_, branch);
+         }
+
+         inline T value() const
+         {
+            return Operation::process(branch_.first->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_.first;
+         }
+
+         inline void release()
+         {
+            branch_.second = false;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
+      private:
+
+         unary_branch_node(unary_branch_node<T,Operation>&);
+         unary_branch_node<T,Operation>& operator=(unary_branch_node<T,Operation>&);
+
+         branch_t branch_;
+      };
+
+      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 exprtk_final : 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 exprtk_final : public T0oT1oT2_base_node<T>
+      {
+      public:
+
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::bfunc_t      bfunc_t;
+         typedef 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 exprtk_final : public T0oT1oT2oT3_base_node<T>
+      {
+      public:
+
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::bfunc_t      bfunc_t;
+         typedef 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 exprtk_final : public T0oT1oT2_base_node<T>
+      {
+      public:
+
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::tfunc_t      tfunc_t;
+         typedef 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 exprtk_final : public sf3ext_type_node<T,T0,T1,T2>
+      {
+      public:
+
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::tfunc_t      tfunc_t;
+         typedef T value_type;
+         typedef T0oT1oT2_sf3ext<T,T0,T1,T2,SF3Operation> node_type;
+
+         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 exprtk_final : 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 exprtk_final : public T0oT1oT2oT3_base_node<T>
+      {
+      public:
+
+         typedef typename details::functor_t<T> functor_t;
+         typedef typename functor_t::tfunc_t      tfunc_t;
+         typedef T value_type;
+         typedef T0oT1oT2oT3_sf4ext<T,T0,T1,T2,T3,SF4Operation> node_type;
+
+         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 exprtk_final : 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 exprtk_final : 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 exprtk_final : 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 exprtk_final : 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 branch)
+         : v_(var)
+         {
+            construct_branch_pair(branch_, branch);
+         }
+
+         inline T value() const
+         {
+            assert(branch_.first);
+            return Operation::process(v_,branch_.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_.first;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
+      private:
+
+         vob_node(const vob_node<T,Operation>&);
+         vob_node<T,Operation>& operator=(const vob_node<T,Operation>&);
+
+         const T& v_;
+         branch_t branch_;
+      };
+
+      template <typename T, typename Operation>
+      class bov_node exprtk_final : 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 branch, const T& var)
+         : v_(var)
+         {
+            construct_branch_pair(branch_, branch);
+         }
+
+         inline T value() const
+         {
+            assert(branch_.first);
+            return Operation::process(branch_.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_.first;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
+      private:
+
+         bov_node(const bov_node<T,Operation>&);
+         bov_node<T,Operation>& operator=(const bov_node<T,Operation>&);
+
+         const T& v_;
+         branch_t branch_;
+      };
+
+      template <typename T, typename Operation>
+      class cob_node exprtk_final : 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 branch)
+         : c_(const_var)
+         {
+            construct_branch_pair(branch_, branch);
+         }
+
+         inline T value() const
+         {
+            assert(branch_.first);
+            return Operation::process(c_,branch_.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_.first;
+         }
+
+         inline expression_node<T>* move_branch(const std::size_t&)
+         {
+            branch_.second = false;
+            return branch_.first;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
+      private:
+
+         cob_node(const cob_node<T,Operation>&);
+         cob_node<T,Operation>& operator=(const cob_node<T,Operation>&);
+
+         const T  c_;
+         branch_t branch_;
+      };
+
+      template <typename T, typename Operation>
+      class boc_node exprtk_final : 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 branch, const T const_var)
+         : c_(const_var)
+         {
+            construct_branch_pair(branch_, branch);
+         }
+
+         inline T value() const
+         {
+            assert(branch_.first);
+            return Operation::process(branch_.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_.first;
+         }
+
+         inline expression_node<T>* move_branch(const std::size_t&)
+         {
+            branch_.second = false;
+            return branch_.first;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
+      private:
+
+         boc_node(const boc_node<T,Operation>&);
+         boc_node<T,Operation>& operator=(const boc_node<T,Operation>&);
+
+         const T  c_;
+         branch_t branch_;
+      };
+
+      #ifndef exprtk_disable_string_capabilities
+      template <typename T, typename SType0, typename SType1, typename Operation>
+      class sos_node exprtk_final : 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 exprtk_final : 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 exprtk_final : 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 exprtk_final : 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 exprtk_final : public binary_node<T>
+      {
+      public:
+
+         typedef expression_node <T>* expression_ptr;
+         typedef string_base_node<T>*   str_base_ptr;
+         typedef range_pack      <T>         range_t;
+         typedef range_t*                  range_ptr;
+         typedef range_interface<T>         irange_t;
+         typedef irange_t*                irange_ptr;
+
+         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;
+
+               const range_t& range0 = (*str0_range_ptr_);
+               const range_t& range1 = (*str1_range_ptr_);
+
+               if (
+                    range0(str0_r0, str0_r1, str0_base_ptr_->size()) &&
+                    range1(str1_r0, str1_r1, str1_base_ptr_->size())
+                  )
+               {
+                  return Operation::process(
+                                             str0_base_ptr_->str().substr(str0_r0,(str0_r1 - str0_r0) + 1),
+                                             str1_base_ptr_->str().substr(str1_r0,(str1_r1 - str1_r0) + 1)
+                                           );
+               }
+            }
+
+            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 exprtk_final : 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 exprtk_final: 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 exprtk_final : 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 branch)
+         {
+            construct_branch_pair(branch_, branch);
+         }
+
+         inline T value() const
+         {
+            assert(branch_.first);
+            return PowOp::result(branch_.first->value());
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_ipow;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
+      private:
+
+         bipow_node(const bipow_node<T,PowOp>&);
+         bipow_node<T,PowOp>& operator=(const bipow_node<T,PowOp>&);
+
+         branch_t branch_;
+      };
+
+      template <typename T, typename PowOp>
+      class ipowinv_node exprtk_final : 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 exprtk_final : 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 branch)
+         {
+            construct_branch_pair(branch_, branch);
+         }
+
+         inline T value() const
+         {
+            assert(branch_.first);
+            return (T(1) / PowOp::result(branch_.first->value()));
+         }
+
+         inline typename expression_node<T>::node_type type() const
+         {
+            return expression_node<T>::e_ipowinv;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
+      private:
+
+         bipowninv_node(const bipowninv_node<T,PowOp>&);
+         bipowninv_node<T,PowOp>& operator=(const bipowninv_node<T,PowOp>&);
+
+         branch_t branch_;
+      };
+
+      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])
+         {
+            expression_node<typename ResultNode::value_type>* result =
+               allocate<ResultNode>(operation, branch[0]);
+            result->node_depth();
+            return result;
+         }
+
+         template <typename ResultNode, typename OpType, typename ExprNode>
+         inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[2])
+         {
+            expression_node<typename ResultNode::value_type>* result =
+               allocate<ResultNode>(operation, branch[0], branch[1]);
+            result->node_depth();
+            return result;
+         }
+
+         template <typename ResultNode, typename OpType, typename ExprNode>
+         inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[3])
+         {
+            expression_node<typename ResultNode::value_type>* result =
+               allocate<ResultNode>(operation, branch[0], branch[1], branch[2]);
+            result->node_depth();
+            return result;
+         }
+
+         template <typename ResultNode, typename OpType, typename ExprNode>
+         inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[4])
+         {
+            expression_node<typename ResultNode::value_type>* result =
+               allocate<ResultNode>(operation, branch[0], branch[1], branch[2], branch[3]);
+            result->node_depth();
+            return result;
+         }
+
+         template <typename ResultNode, typename OpType, typename ExprNode>
+         inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[5])
+         {
+            expression_node<typename ResultNode::value_type>* result =
+               allocate<ResultNode>(operation, branch[0],branch[1], branch[2], branch[3], branch[4]);
+            result->node_depth();
+            return result;
+         }
+
+         template <typename ResultNode, typename OpType, typename ExprNode>
+         inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[6])
+         {
+            expression_node<typename ResultNode::value_type>* result =
+               allocate<ResultNode>(operation, branch[0], branch[1], branch[2], branch[3], branch[4], branch[5]);
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(seq));
+            result->node_depth();
+            return result;
+         }
+
+         template <typename node_type, typename T1>
+         inline expression_node<typename node_type::value_type>* allocate(T1& t1) const
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1));
+            result->node_depth();
+            return result;
+         }
+
+         template <typename node_type, typename T1>
+         inline expression_node<typename node_type::value_type>* allocate_c(const T1& t1) const
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1));
+            result->node_depth();
+            return result;
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2>
+         inline expression_node<typename node_type::value_type>* allocate(const T1& t1, const T2& t2) const
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2));
+            result->node_depth();
+            return result;
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2>
+         inline expression_node<typename node_type::value_type>* allocate_cr(const T1& t1, T2& t2) const
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2));
+            result->node_depth();
+            return result;
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2>
+         inline expression_node<typename node_type::value_type>* allocate_rc(T1& t1, const T2& t2) const
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2));
+            result->node_depth();
+            return result;
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2>
+         inline expression_node<typename node_type::value_type>* allocate_rr(T1& t1, T2& t2) const
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2));
+            result->node_depth();
+            return result;
+         }
+
+         template <typename node_type,
+                   typename T1, typename T2>
+         inline expression_node<typename node_type::value_type>* allocate_tt(T1 t1, T2 t2) const
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6, t7));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6, t7, t8));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6, t7, t8, t9));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6));
+            result->node_depth();
+            return result;
+         }
+
+         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
+         {
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6, t7));
+            result->node_depth();
+            return result;
+         }
+
+         template <typename T>
+         void inline free(expression_node<T>*& e) const
+         {
+            exprtk_debug(("node_allocator::free() - deleting expression_node "
+                          "type: %03d addr: %p\n",
+                          static_cast<int>(e->type()),
+                          reinterpret_cast<void*>(e)));
+            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(N)                   \
+      {                                              \
+         exprtk_debug(("ifunction::operator() - Operator(" #N ") has not been overridden\n")); \
+         return std::numeric_limits<T>::quiet_NaN(); \
+      }                                              \
+
+      inline virtual T operator() ()
+      empty_method_body(0)
+
+      inline virtual T operator() (const T&)
+      empty_method_body(1)
+
+      inline virtual T operator() (const T&,const T&)
+      empty_method_body(2)
+
+      inline virtual T operator() (const T&, const T&, const T&)
+      empty_method_body(3)
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&)
+      empty_method_body(4)
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&)
+      empty_method_body(5)
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&)
+      empty_method_body(6)
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&)
+      empty_method_body(7)
+
+      inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&)
+      empty_method_body(8)
+
+      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(9)
+
+      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(10)
+
+      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(11)
+
+      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(12)
+
+      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(13)
+
+      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(14)
+
+      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(15)
+
+      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(16)
+
+      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(17)
+
+      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(18)
+
+      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(19)
+
+      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(20)
+
+      #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(" #N ") has not been overridden\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)
+         {}
+
+         struct deleter
+         {
+            #define exprtk_define_process(Type)                  \
+            static inline void process(std::pair<bool,Type*>& n) \
+            {                                                    \
+               delete n.second;                                  \
+            }                                                    \
+
+            exprtk_define_process(variable_node_t )
+            exprtk_define_process(vector_t        )
+            #ifndef exprtk_disable_string_capabilities
+            exprtk_define_process(stringvar_node_t)
+            #endif
+
+            #undef exprtk_define_process
+
+            template <typename DeleteType>
+            static inline void process(std::pair<bool,DeleteType*>&)
+            {}
+         };
+
+         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)
+            {
+               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)
+         {
+            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<variable_t        , T                 > variable_store;
+            type_store<function_t        , function_t        > function_store;
+            type_store<vararg_function_t , vararg_function_t > vararg_function_store;
+            type_store<generic_function_t, generic_function_t> generic_function_store;
+            type_store<generic_function_t, generic_function_t> string_function_store;
+            type_store<generic_function_t, generic_function_t> overload_function_store;
+            type_store<vector_holder_t   , vector_holder_t   > vector_store;
+            #ifndef exprtk_disable_string_capabilities
+            type_store<stringvar_t       , std::string       > stringvar_store;
+            #endif
+
+            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
+         {
+            switch (function.rtrn_type)
+            {
+               case generic_function_t::e_rtrn_scalar :
+                  return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ?
+                         local_data().generic_function_store.add(function_name,function) : false;
+
+               case generic_function_t::e_rtrn_string :
+                  return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ?
+                         local_data().string_function_store.add(function_name,function)  : false;
+
+               case generic_function_t::e_rtrn_overload :
+                  return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|:")) ?
+                         local_data().overload_function_store.add(function_name,function) : false;
+            }
+         }
+
+         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
+         {
+            switch (function.rtrn_type)
+            {
+               case generic_function_t::e_rtrn_scalar :
+                  return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ?
+                         local_data().generic_function_store.add(function_name,function) : false;
+
+               case generic_function_t::e_rtrn_string :
+                  return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ?
+                         local_data().string_function_store.add(function_name,function)  : false;
+
+               case generic_function_t::e_rtrn_overload :
+                  return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|:")) ?
+                         local_data().overload_function_store.add(function_name,function) : false;
+            }
+         }
+
+         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 ((i < (symbol.size() - 1)) && ('.' == symbol[i]))
+                     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 ((i < (symbol.size() - 1)) && ('.' == symbol[i]))
+                     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 reinterpret_cast<T*>(local_data_list[i].pointer);
+                                        break;
+
+                     case e_vecdata   : delete [] reinterpret_cast<T*>(local_data_list[i].pointer);
+                                        break;
+
+                     case e_string    : delete reinterpret_cast<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
+      {
+         assert(control_block_      );
+         assert(control_block_->expr);
+
+         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,
+         e_parser  = 8
+      };
+
+      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" );
+            case e_parser  : return std::string("Parser 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)
+         {
+            exprtk_debug(("free_element() - se[%s]\n", se.name.c_str()));
+
+            switch (se.type)
+            {
+               case scope_element::e_variable   : delete reinterpret_cast<T*>(se.data);
+                                                  delete se.var_node;
+                                                  break;
+
+               case scope_element::e_vector     : delete[] reinterpret_cast<T*>(se.data);
+                                                  delete se.vec_node;
+                                                  break;
+
+               case scope_element::e_vecelem    : delete se.var_node;
+                                                  break;
+
+               #ifndef exprtk_disable_string_capabilities
+               case scope_element::e_string     : delete reinterpret_cast<std::string*>(se.data);
+                                                  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_;
+      };
+
+      class stack_limit_handler
+      {
+      public:
+
+         typedef parser<T> parser_t;
+
+         explicit stack_limit_handler(parser<T>& p)
+            : parser_(p),
+              limit_exceeded_(false)
+         {
+            if (++parser_.state_.stack_depth > parser_.settings_.max_stack_depth_)
+            {
+               limit_exceeded_ = true;
+               parser_.set_error(
+                  make_error(parser_error::e_parser,
+                     "ERR000 - Current stack depth " + details::to_str(parser_.state_.stack_depth) +
+                     " exceeds maximum allowed stack depth of " + details::to_str(parser_.settings_.max_stack_depth_),
+                     exprtk_error_location));
+            }
+         }
+
+        ~stack_limit_handler()
+         {
+            parser_.state_.stack_depth--;
+         }
+
+         bool operator!()
+         {
+            return limit_exceeded_;
+         }
+
+      private:
+
+         stack_limit_handler& operator=(const stack_limit_handler&);
+
+         parser_t& parser_;
+         bool limit_exceeded_;
+      };
+
+      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;
+            stack_depth             = 0;
+            parsing_loop_stmt_count = 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;
+         std::size_t stack_depth;
+         std::size_t parsing_loop_stmt_count;
+      };
+
+   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)
+         : max_stack_depth_(400),
+           max_node_depth_(10000)
+         {
+           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);
+         }
+
+         void set_max_stack_depth(const std::size_t mx_stack_depth)
+         {
+            max_stack_depth_ = mx_stack_depth;
+         }
+
+         void set_max_node_depth(const std::size_t max_node_depth)
+         {
+            max_node_depth_ = max_node_depth;
+         }
+
+      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_;
+
+         std::size_t max_stack_depth_;
+         std::size_t max_node_depth_;
+
+         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),
+        loop_runtime_check_(0)
+      {
+         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,
+                          "ERR001 - 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,
+                          "ERR002 - 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(),
+                             "ERR003 - 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 expression;
+
+         expression.register_symbol_table(symtab);
+
+         compile(expression_string,expression);
+
+         return expression;
+      }
+
+      void process_lexer_errors()
+      {
+         for (std::size_t i = 0; i < lexer().size(); ++i)
+         {
+            if (lexer()[i].is_error())
+            {
+               std::string diagnostic = "ERR004 - ";
+
+               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(),
+                                   "ERR005 - Mismatched brackets: '" + bracket_checker_ptr->error_token().value + "'",
+                                   exprtk_error_location));
+                  }
+                  else if (0 != (numeric_checker_ptr = dynamic_cast<lexer::helper::numeric_checker*>(helper_assembly_.error_token_scanner)))
+                  {
+                     for (std::size_t i = 0; i < numeric_checker_ptr->error_count(); ++i)
+                     {
+                        lexer::token error_token = lexer()[numeric_checker_ptr->error_index(i)];
+
+                        set_error(
+                           make_error(parser_error::e_token,
+                                      error_token,
+                                      "ERR006 - Invalid numeric token: '" + error_token.value + "'",
+                                      exprtk_error_location));
+                     }
+
+                     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,
+                                      "ERR007 - 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,
+                                      "ERR008 - 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_;
+      }
+
+      inline void register_loop_runtime_check(loop_runtime_check& lrtchk)
+      {
+         loop_runtime_check_ = &lrtchk;
+      }
+
+      inline void clear_loop_runtime_check()
+      {
+         loop_runtime_check_ = loop_runtime_check_ptr(0);
+      }
+
+   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;
+         const std::size_t ct_pos = current_token().position;
+         parser_helper::next_token();
+         const std::string depth(2 * state_.scope_depth,' ');
+         exprtk_debug(("%s"
+                       "prev[%s | %04d] --> curr[%s | %04d]  stack_level: %3d\n",
+                       depth.c_str(),
+                       ct_str.c_str(),
+                       static_cast<unsigned int>(ct_pos),
+                       current_token().value.c_str(),
+                       static_cast<unsigned int>(current_token().position),
+                       static_cast<unsigned int>(state_.stack_depth)));
+      }
+      #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(),
+                                "ERR009 - 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)
+      {
+         stack_limit_handler slh(*this);
+
+         if (!slh)
+         {
+            return error_node();
+         }
+
+         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,
+                             "ERR010 - 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,
+                             "ERR011 - 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,
+                             "ERR012 - 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,
+                             "ERR013 - 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,
+                                "ERR014 - 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_ :
+                                "ERR015 - 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);
+            }
+         }
+
+         if ((0 != expression) && (expression->node_depth() > settings_.max_node_depth_))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                  current_token(),
+                  "ERR016 - Expression depth of " + details::to_str(static_cast<int>(expression->node_depth())) +
+                  " exceeds maximum allowed expression depth of " + details::to_str(static_cast<int>(settings_.max_node_depth_)),
+                  exprtk_error_location));
+
+            free_node(node_allocator_,expression);
+
+            return error_node();
+         }
+
+         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(),
+                                "ERR017 - 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);
+      }
+
+      struct scoped_expression_delete
+      {
+         scoped_expression_delete(parser<T>& pr, expression_node_ptr& expression)
+         : delete_ptr(true),
+           parser_(pr),
+           expression_(expression)
+         {}
+
+        ~scoped_expression_delete()
+         {
+            if (delete_ptr)
+            {
+               free_node(parser_.node_allocator_, expression_);
+            }
+         }
+
+         bool delete_ptr;
+         parser<T>& parser_;
+         expression_node_ptr& expression_;
+
+      private:
+
+         scoped_expression_delete& operator=(const scoped_expression_delete&);
+      };
+
+      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_;
+      };
+
+      struct scoped_inc_dec
+      {
+         explicit scoped_inc_dec(std::size_t& v)
+         : v_(v)
+         { ++v_; }
+
+        ~scoped_inc_dec()
+         {
+           assert(v_ > 0);
+           --v_;
+         }
+
+         std::size_t& v_;
+      };
+
+      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(),
+                                       "ERR018 - 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(),
+                          "ERR019 - 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(),
+                          "ERR020 - 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(),
+                          "ERR021 - 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(),
+                             "ERR022 - 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(),
+                                "ERR023 - 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(),
+                          "ERR024 - Invalid number of arguments for function: '" + function_name + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else
+            result = expression_generator_.function(function,branch);
+
+         sd.delete_ptr = (0 == result);
+
+         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(),
+                          "ERR025 - 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(),
+                          "ERR026 - Expected a '(' at start of function call to '" + function_name  +
+                          "', instead got: '" + current_token().value + "'",
+                          exprtk_error_location));
+
+            return 0;
+         }
+
+         if (token_is(token_t::e_rbracket, e_hold))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR027 - Expected at least one input parameter for function call '" + function_name + "'",
+                          exprtk_error_location));
+
+            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(),
+                             "ERR028 - 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(),
+                          "ERR029 - 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,
+                          "ERR030 - 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,
+                       "ERR031 - 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(),
+                          "ERR032 - Expected ',' between if-statement condition and consequent",
+                          exprtk_error_location));
+            result = false;
+         }
+         else if (0 == (consequent = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR033 - Failed to parse consequent for if-statement",
+                          exprtk_error_location));
+            result = false;
+         }
+         else if (!token_is(token_t::e_comma))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR034 - Expected ',' between if-statement consequent and alternative",
+                          exprtk_error_location));
+            result = false;
+         }
+         else if (0 == (alternative = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR035 - Failed to parse alternative for if-statement",
+                          exprtk_error_location));
+            result = false;
+         }
+         else if (!token_is(token_t::e_rbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR036 - Expected ')' at the end of if-statement",
+                          exprtk_error_location));
+            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(),
+                             "ERR037 - 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(),
+                             "ERR038 - 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(),
+                                "ERR039 - 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(),
+                             "ERR040 - 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(),
+                                   "ERR041 - 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(),
+                                   "ERR042 - 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(),
+                                   "ERR043 - Expected ';' at the end of the 'else-if' for the if-statement",
+                                   exprtk_error_location));
+
+                     result = false;
+                  }
+               }
+               else
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR044 - Failed to parse body of the 'else' for if-statement",
+                                exprtk_error_location));
+
+                  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(),
+                             "ERR045 - 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(),
+                          "ERR046 - Expected '(' at start of if-statement, instead got: '" + current_token().value + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (0 == (condition = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR047 - Failed to parse condition for if-statement",
+                          exprtk_error_location));
+
+            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(),
+                       "ERR048 - 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(),
+                          "ERR049 - Encountered invalid condition branch for ternary if-statement",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!token_is(token_t::e_ternary))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR050 - Expected '?' after condition of ternary if-statement",
+                          exprtk_error_location));
+
+            result = false;
+         }
+         else if (0 == (consequent = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR051 - Failed to parse consequent for ternary if-statement",
+                          exprtk_error_location));
+
+            result = false;
+         }
+         else if (!token_is(token_t::e_colon))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR052 - Expected ':' between ternary if-statement consequent and alternative",
+                          exprtk_error_location));
+
+            result = false;
+         }
+         else if (0 == (alternative = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR053 - Failed to parse alternative for ternary if-statement",
+                          exprtk_error_location));
+
+            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(),
+                             "ERR054 - 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(),
+                          "ERR055 - 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(),
+                          "ERR056 - Expected '(' at start of while-loop condition statement",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (0 == (condition = parse_expression()))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR057 - Failed to parse condition for while-loop",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!token_is(token_t::e_rbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR058 - Expected ')' at end of while-loop condition statement",
+                          exprtk_error_location));
+
+            result = false;
+         }
+
+         brkcnt_list_.push_front(false);
+
+         if (result)
+         {
+            scoped_inc_dec sid(state_.parsing_loop_stmt_count);
+
+            if (0 == (branch = parse_multi_sequence("while-loop")))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR059 - Failed to parse body of while-loop"));
+               result = false;
+            }
+            else if (0 == (result_node = expression_generator_.while_loop(condition,
+                                                                          branch,
+                                                                          brkcnt_list_.front())))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR060 - Failed to synthesize while-loop",
+                             exprtk_error_location));
+
+               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);
+
+            scoped_inc_dec sid(state_.parsing_loop_stmt_count);
+
+            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(),
+                                "ERR061 - 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(),
+                             "ERR062 - 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(),
+                          "ERR063 - Expected '(' before condition statement of repeat until loop",
+                          exprtk_error_location));
+
+            free_node(node_allocator_,branch);
+
+            return error_node();
+         }
+         else if (0 == (condition = parse_expression()))
+         {
+            brkcnt_list_.pop_front();
+
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR064 - Failed to parse condition for repeat until loop",
+                          exprtk_error_location));
+
+            free_node(node_allocator_,branch);
+
+            return error_node();
+         }
+         else if (!token_is(token_t::e_rbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR065 - Expected ')' after condition of repeat until loop",
+                          exprtk_error_location));
+
+            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(),
+                          "ERR066 - 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(),
+                          "ERR067 - 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(),
+                                "ERR068 - Expected a variable at the start of initialiser section of for-loop",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+               else if (!peek_token_is(token_t::e_assign))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR069 - Expected variable assignment of initialiser section of for-loop",
+                                exprtk_error_location));
+
+                  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(),
+                                "ERR070 - 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>(*reinterpret_cast<T*>(nse.data));
+
+                     if (!sem_.add_element(nse))
+                     {
+                        set_error(
+                           make_error(parser_error::e_syntax,
+                                      current_token(),
+                                      "ERR071 - Failed to add new local variable '" + loop_counter_symbol + "' to SEM",
+                                      exprtk_error_location));
+
+                        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(),
+                             "ERR072 - Failed to parse initialiser of for-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+            else if (!token_is(token_t::e_eof))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR073 - Expected ';' after initialiser of for-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+         }
+
+         if (!token_is(token_t::e_eof))
+         {
+            if (0 == (condition = parse_expression()))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR074 - Failed to parse condition of for-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+            else if (!token_is(token_t::e_eof))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR075 - Expected ';' after condition section of for-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+         }
+
+         if (!token_is(token_t::e_rbracket))
+         {
+            if (0 == (incrementor = parse_expression()))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR076 - Failed to parse incrementor of for-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+            else if (!token_is(token_t::e_rbracket))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR077 - Expected ')' after incrementor section of for-loop",
+                             exprtk_error_location));
+
+               result = false;
+            }
+         }
+
+         if (result)
+         {
+            brkcnt_list_.push_front(false);
+
+            scoped_inc_dec sid(state_.parsing_loop_stmt_count);
+
+            if (0 == (loop_body = parse_multi_sequence("for-loop")))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR078 - 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(),
+                          "ERR079 - 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(),
+                          "ERR080 - Expected '{' for call to switch statement",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         expression_node_ptr default_statement = error_node();
+
+         scoped_expression_delete defstmt_delete((*this), default_statement);
+
+         for ( ; ; )
+         {
+            if (details::imatch("case",current_token().value))
+            {
+               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(),
+                                "ERR081 - Expected ':' for case of switch statement",
+                                exprtk_error_location));
+
+                  free_node(node_allocator_, condition);
+
+                  return error_node();
+               }
+
+               expression_node_ptr consequent = parse_expression();
+
+               if (0 == consequent)
+               {
+                  free_node(node_allocator_, condition);
+
+                  return error_node();
+               }
+               else if (!token_is(token_t::e_eof))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR082 - Expected ';' at end of case for switch statement",
+                                exprtk_error_location));
+
+                  free_node(node_allocator_, condition );
+                  free_node(node_allocator_, consequent);
+
+                  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);
+               }
+
+            }
+            else if (details::imatch("default",current_token().value))
+            {
+               if (0 != default_statement)
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR083 - Multiple default cases for switch statement",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+
+               next_token();
+
+               if (!token_is(token_t::e_colon))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR084 - Expected ':' for default of switch statement",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+
+               if (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold))
+                  default_statement = parse_multi_sequence("switch-default");
+               else
+                  default_statement = parse_expression();
+
+               if (0 == default_statement)
+                  return error_node();
+               else if (!token_is(token_t::e_eof))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR085 - Expected ';' at end of default for switch statement",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+            }
+            else if (token_is(token_t::e_rcrlbracket))
+               break;
+            else
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR086 - Expected '}' at end of switch statement",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+         }
+
+         const bool default_statement_present = (0 != default_statement);
+
+         if (default_statement_present)
+         {
+            arg_list.push_back(default_statement);
+         }
+
+         result = expression_generator_.switch_statement(arg_list, (0 != default_statement));
+
+         svd.delete_ptr = (0 == result);
+         defstmt_delete.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(),
+                          "ERR087 - 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(),
+                          "ERR088 - 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(),
+                             "ERR089 - 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(),
+                             "ERR090 - 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(),
+                             "ERR091 - 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(),
+                          "ERR092 - 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(),
+                          "ERR093 - 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(),
+                          "ERR094 - 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(),
+                             "ERR095 - 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(),
+                          "ERR096 - 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(),
+                          "ERR097 - Failed to generate string range node",
+                          exprtk_error_location));
+
+            free_node(node_allocator_,expression);
+            rp.free();
+         }
+
+         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(),
+                             "ERR098 - 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(),
+                             "ERR099 - 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(),
+                          "ERR100 - 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(),
+                             "ERR101 - 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(),
+                                "ERR102 - 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(),
+                             "ERR103 - 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(),
+                             "ERR104 - 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(),
+                                "ERR105 - Range upper bound less than zero! Constraint: r1 >= 0",
+                                exprtk_error_location));
+
+                  rp.free();
+
+                  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(),
+                             "ERR106 - 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;
+
+            bool rp_result = false;
+
+            try
+            {
+               rp_result = rp(r0, r1);
+            }
+            catch (std::runtime_error&)
+            {}
+
+            if (!rp_result || (r0 > r1))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR107 - 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(),
+                             "ERR108 - 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);
+               rp.free();
+
+               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(),
+                             "ERR109 - Overflow in range for string: '" + const_str + "'[" +
+                             (rp.n0_c.first ? details::to_str(static_cast<int>(rp.n0_c.second)) : "?") + ":" +
+                             (rp.n1_c.first ? details::to_str(static_cast<int>(rp.n1_c.second)) : "?") + "]",
+                             exprtk_error_location));
+
+               rp.free();
+
+               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(),
+                             "ERR110 - 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(),
+                          "ERR111 - Failed to parse index for vector: '" + symbol + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!token_is(token_t::e_rsqrbracket))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR112 - Expected ']' for index of vector: '" + symbol + "'",
+                          exprtk_error_location));
+
+            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(),
+                             "ERR113 - 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(),
+                                "ERR114 - 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(),
+                                   "ERR115 - 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(),
+                          "ERR116 - Zero parameter call to vararg function: "
+                          + vararg_function_name + " not allowed",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         if (arg_list.size() < vararg_function->min_num_args())
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR117 - Invalid number of parameters to call to vararg function: "
+                          + vararg_function_name + ", require at least "
+                          + details::to_str(static_cast<int>(vararg_function->min_num_args())) + " parameters",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (arg_list.size() > vararg_function->max_num_args())
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR118 - Invalid number of parameters to call to vararg function: "
+                          + vararg_function_name + ", require no more than "
+                          + details::to_str(static_cast<int>(vararg_function->max_num_args())) + " parameters",
+                          exprtk_error_location));
+
+            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);
+         }
+
+         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(),
+                                "ERR119 - 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(),
+                                "ERR120 - Failed parameter type check for function '" + function_name_ + "', "
+                                "Best match: '" + function_definition_list_[max_diff_index].param_seq +
+                                "'  call set: '" + param_seq + "'",
+                                exprtk_error_location));
+            }
+
+            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(),
+                                   "ERR121 - 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(),
+                                   "ERR122 - Function '" + function_name_ + "' has a parameter sequence conflict between " +
+                                   "pseq_idx[" + details::to_str(seq_itr->second) + "] and" +
+                                   "pseq_idx[" + details::to_str(i) + "] " +
+                                   "param seq: " + param_seq_list[i],
+                                   exprtk_error_location));
+                  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(),
+                          "ERR123 - 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(),
+                                "ERR124 - 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(),
+                                   "ERR125 - 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(),
+                          "ERR126 - 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(),
+                          "ERR127 - 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(),
+                                "ERR128 - 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(),
+                                   "ERR129 - 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(),
+                          "ERR130 - 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(),
+                          "ERR131 - 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(),
+                          "ERR132 - 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(),
+                               "ERR133 - 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(),
+                                     "ERR134 - 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(),
+                               "ERR135 - 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(),
+                          "ERR136 - 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(),
+                          "ERR137 - 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(),
+                          "ERR138 - Invoking 'break' within a break call is not allowed",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (0 == state_.parsing_loop_stmt_count)
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR139 - Invalid use of 'break', allowed only in the scope of a loop",
+                          exprtk_error_location));
+
+            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(),
+                                "ERR140 - Failed to parse return expression for 'break' statement",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
+               else if (!token_is(token_t::e_rsqrbracket))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR141 - Expected ']' at the completion of break's return expression",
+                                exprtk_error_location));
+
+                  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(),
+                          "ERR142 - 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 (0 == state_.parsing_loop_stmt_count)
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR143 - Invalid use of 'continue', allowed only in the scope of a loop",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else
+         {
+            next_token();
+
+            brkcnt_list_.front() = true;
+            state_.activate_side_effect("parse_continue_statement()");
+
+            return node_allocator_.allocate<details::continue_node<T> >();
+         }
+      }
+      #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(),
+                          "ERR144 - 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(),
+                          "ERR145 - 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(),
+                          "ERR146 - 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(),
+                          "ERR147 - Invalid vector size. Must be an integer in the range [0,2e9], size: " +
+                          details::to_str(details::numeric::to_int32(vector_size)),
+                          exprtk_error_location));
+
+            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(),
+                          "ERR148 - 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(),
+                             "ERR149 - 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(),
+                                "ERR150 - 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(),
+                                "ERR151 - 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?
+                  const 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(),
+                                   "ERR152 - 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(),
+                                   "ERR153 - 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(),
+                                   "ERR154 - 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(),
+                                "ERR155 - 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(),
+                             "ERR156 - 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(),
+                             "ERR157 - Illegal redefinition of local vector: '" + vec_name + "'",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+            else if (
+                      (se.size == vec_size) &&
+                      (scope_element::e_vector == se.type)
+                    )
+            {
+               vec_holder = se.vec_node;
+               se.active  = true;
+               se.depth   = state_.scope_depth;
+               se.ref_count++;
+            }
+         }
+
+         if (0 == vec_holder)
+         {
+            scope_element nse;
+            nse.name      = vec_name;
+            nse.active    = true;
+            nse.ref_count = 1;
+            nse.type      = scope_element::e_vector;
+            nse.depth     = state_.scope_depth;
+            nse.size      = vec_size;
+            nse.data      = new T[vec_size];
+            nse.vec_node  = new typename scope_element::vector_holder_t(reinterpret_cast<T*>(nse.data),nse.size);
+
+            if (!sem_.add_element(nse))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR158 - Failed to add new local vector '" + vec_name + "' to SEM",
+                             exprtk_error_location));
+
+               sem_.free_element(nse);
+
+               return error_node();
+            }
+
+            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(),
+                             "ERR159 - 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(*reinterpret_cast<std::string*>(nse.data));
+
+            if (!sem_.add_element(nse))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR160 - Failed to add new local string variable '" + str_name + "' to SEM",
+                             exprtk_error_location));
+
+               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(),
+                          "ERR161 - 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(),
+                          "ERR162 - Expected a symbol for variable definition",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (details::is_reserved_symbol(var_name))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR163 - Illegal redefinition of reserved keyword: '" + var_name + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (symtab_store_.symbol_exists(var_name))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR164 - Illegal redefinition of variable '" + var_name + "'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (local_variable_is_shadowed(var_name))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR165 - Illegal redefinition of local variable: '" + var_name + "'",
+                          exprtk_error_location));
+
+            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(),
+                             "ERR166 - 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(),
+                             "ERR167 - 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(),
+                             "ERR168 - 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>(*reinterpret_cast<T*>(nse.data));
+
+            if (!sem_.add_element(nse))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR169 - Failed to add new local variable '" + var_name + "' to SEM",
+                             exprtk_error_location));
+
+               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(),
+                          "ERR170 - Expected a '{}' for uninitialised var definition",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (!token_is(token_t::e_eof,prsrhlpr_t::e_hold))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR171 - Expected ';' after uninitialised variable definition",
+                          exprtk_error_location));
+
+            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(),
+                             "ERR172 - 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>(*reinterpret_cast<T*>(nse.data));
+
+            if (!sem_.add_element(nse))
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR173 - Failed to add new local variable '" + var_name + "' to SEM",
+                             exprtk_error_location));
+
+               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(),
+                          "ERR174 - 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(),
+                          "ERR175 - 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(),
+                             "ERR176 - 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);
+            }
+
+            const 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(),
+                             "ERR177 - 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(),
+                           "ERR178 - 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(),
+                          "ERR179 - 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(),
+                             "ERR180 - 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);
+            }
+
+            const 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(),
+                             "ERR181 - 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(),
+                          "ERR182 - 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(),
+                          "ERR183 - 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(),
+                          "ERR184 - 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(),
+                                "ERR185 - 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(),
+                          "ERR186 - 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,
+                             "ERR187 - 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(),
+                             "ERR188 - 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(),
+                             "ERR189 - 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(),
+                                "ERR190 - 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(),
+                                "ERR191 - 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(),
+                                "ERR192 - 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(),
+                                "ERR193 - 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(),
+                                "ERR194 - 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(),
+                                "ERR195 - 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(),
+                                "ERR196 - 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(),
+                                "ERR197 - Failed to resolve symbol: '" + symbol + "'" +
+                                (error_message.empty() ? "" : " - " + error_message),
+                                exprtk_error_location));
+               }
+
+               return error_node();
+            }
+         }
+
+         set_error(
+            make_error(parser_error::e_syntax,
+                       current_token(),
+                       "ERR198 - Undefined symbol: '" + symbol + "'",
+                       exprtk_error_location));
+
+         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(),
+                          "ERR199 - 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)
+      {
+         stack_limit_handler slh(*this);
+
+         if (!slh)
+         {
+            return error_node();
+         }
+
+         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(),
+                                "ERR200 - 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(),
+                             "ERR201 - 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(),
+                             "ERR202 - 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(),
+                             "ERR203 - 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(),
+                             "ERR204 - 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)
+                  )
+               )
+            {
+               expression_node_ptr result = expression_generator_(details::e_neg,branch);
+
+               if (0 == result)
+               {
+                  free_node(node_allocator_,branch);
+
+                  return error_node();
+               }
+               else
+                  branch = result;
+            }
+         }
+         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(),
+                          "ERR205 - Premature end of expression[1]",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR206 - Premature end of expression[2]",
+                          exprtk_error_location));
+
+            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 loop_runtime_check_ptr get_loop_runtime_check(const loop_runtime_check::loop_types loop_type) const
+         {
+            if (
+                 parser_->loop_runtime_check_ &&
+                 (loop_type == (parser_->loop_runtime_check_->loop_set & loop_type))
+               )
+            {
+               return parser_->loop_runtime_check_;
+            }
+
+            return loop_runtime_check_ptr(0);
+         }
+
+         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,
+                          get_loop_runtime_check(loop_runtime_check::e_while_loop)
+                        );
+            #ifndef exprtk_disable_break_continue
+            else
+               return node_allocator_->allocate<while_loop_bc_node_t>
+                        (
+                          condition,
+                          branch,
+                          get_loop_runtime_check(loop_runtime_check::e_while_loop)
+                        );
+            #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,
+                          get_loop_runtime_check(loop_runtime_check::e_repeat_until_loop)
+                        );
+            #ifndef exprtk_disable_break_continue
+            else
+               return node_allocator_->allocate<repeat_until_loop_bc_node_t>
+                        (
+                          condition,
+                          branch,
+                          get_loop_runtime_check(loop_runtime_check::e_repeat_until_loop)
+                        );
+            #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,
+                                         get_loop_runtime_check(loop_runtime_check::e_for_loop)
+                                       );
+
+            #ifndef exprtk_disable_break_continue
+            else
+               return node_allocator_->allocate<for_loop_bc_node_t>
+                                       (
+                                         initialiser,
+                                         condition,
+                                         incrementor,
+                                         loop_body,
+                                         get_loop_runtime_check(loop_runtime_check::e_for_loop)
+                                       );
+            #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<std::pair<expression_node_ptr,bool> > arg_list_t;
+
+            #define case_stmt(N)                                                         \
+            if (is_true(arg[(2 * N)].first)) { return arg[(2 * N) + 1].first->value(); } \
+
+            struct switch_impl_1
+            {
+               static inline T process(const arg_list_t& arg)
+               {
+                  case_stmt(0)
+
+                  assert(arg.size() == ((2 * 1) + 1));
+
+                  return arg.back().first->value();
+               }
+            };
+
+            struct switch_impl_2
+            {
+               static inline T process(const arg_list_t& arg)
+               {
+                  case_stmt(0) case_stmt(1)
+
+                  assert(arg.size() == ((2 * 2) + 1));
+
+                  return arg.back().first->value();
+               }
+            };
+
+            struct switch_impl_3
+            {
+               static inline T process(const arg_list_t& arg)
+               {
+                  case_stmt(0) case_stmt(1)
+                  case_stmt(2)
+
+                  assert(arg.size() == ((2 * 3) + 1));
+
+                  return arg.back().first->value();
+               }
+            };
+
+            struct switch_impl_4
+            {
+               static inline T process(const arg_list_t& arg)
+               {
+                  case_stmt(0) case_stmt(1)
+                  case_stmt(2) case_stmt(3)
+
+                  assert(arg.size() == ((2 * 4) + 1));
+
+                  return arg.back().first->value();
+               }
+            };
+
+            struct switch_impl_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)
+
+                  assert(arg.size() == ((2 * 5) + 1));
+
+                  return arg.back().first->value();
+               }
+            };
+
+            struct switch_impl_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)
+
+                  assert(arg.size() == ((2 * 6) + 1));
+
+                  return arg.back().first->value();
+               }
+            };
+
+            struct switch_impl_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)
+
+                  assert(arg.size() == ((2 * 7) + 1));
+
+                  return arg.back().first->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, const bool default_statement_present)
+         {
+            if (arg_list.empty())
+               return error_node();
+            else if (
+                      !all_nodes_valid(arg_list) ||
+                      (!default_statement_present && (arg_list.size() < 2))
+                    )
+            {
+               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_impl_##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))
+               {
+                  details::free_node(*node_allocator_,result);
+                  std::fill_n(b, N, reinterpret_cast<expression_node_ptr>(0));
+
+                  return error_node();
+               }
+               else if (N != f->param_count)
+               {
+                  details::free_node(*node_allocator_,result);
+                  std::fill_n(b, N, reinterpret_cast<expression_node_ptr>(0));
+
+                  return error_node();
+               }
+
+               function_N_node_t* func_node_ptr = reinterpret_cast<function_N_node_t*>(result);
+
+               if (!func_node_ptr->init_branches(b))
+               {
+                  details::free_node(*node_allocator_,result);
+                  std::fill_n(b, N, reinterpret_cast<expression_node_ptr>(0));
+
+                  return error_node();
+               }
+
+               return result;
+            }
+         }
+
+         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);
+               }
+
+               const 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];
+            }
+
+            details::free_node(*node_allocator_, branch[0]);
+
+            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));
+            }
+
+            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_;
+
+      loop_runtime_check_ptr loop_runtime_check_;
+
+      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();
+         const T x_original = x;
+         const 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();
+         const T x_original = x;
+         const 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;
+
+            const 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
+      {
+         explicit 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;
+
+         const 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 const symbol_table_t& symbol_table() const
+      {
+         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
+#   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);
+
+         const std::size_t dist  = r1 - r0 + 1;
+         const 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);
+
+         const 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);
+
+         const std::size_t dist  = r1 - r0 + 1;
+
+         if (n > dist)
+            return T(0);
+
+         const 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.718281828459045235360287471352"
+                                   "66249775724709369995957496696762"
+                                   "77240766303535475945713821785251"
+                                   "66427427466391932003059921817413";
+      static const char* date    = "20210101";
+
+      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
+
+   #ifdef exprtk_override
+   #undef exprtk_override
+   #endif
+
+   #ifdef exprtk_final
+   #undef exprtk_final
+   #endif
+
+} // namespace exprtk
+
+#endif
diff --git a/gui/source/file.cpp b/gui/source/file.cpp
new file mode 100644 (file)
index 0000000..9c1830e
--- /dev/null
@@ -0,0 +1,168 @@
+/**
+ * @file file.cpp
+ * @brief Contains code for file-management-related UI elements and logic.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "imgui.h"
+#include "backends/imgui_impl_sdl2.h"
+#include "backends/imgui_impl_opengl2.h"
+#include "ImGuiFileDialog.h"
+#include "TextEditor.h"
+
+#include "stmdsp_code.hpp"
+
+#include <algorithm>
+#include <cstdlib>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <SDL2/SDL.h>
+
+extern TextEditor editor;
+extern void log(const std::string& str);
+
+enum class FileAction {
+    None,
+    Open,
+    Save,
+    SaveAs
+};
+
+static FileAction fileAction = FileAction::None;
+static std::string fileCurrentPath;
+static std::vector<std::filesystem::path> fileExampleList;
+
+static void saveCurrentFile()
+{
+    if (std::ofstream ofs (fileCurrentPath, std::ios::binary); ofs.good()) {
+        const auto& text = editor.GetText();
+        ofs.write(text.data(), text.size());
+        log("Saved.");
+    }
+}
+
+static void openCurrentFile()
+{
+    if (std::ifstream ifs (fileCurrentPath); ifs.good()) {
+        std::ostringstream sstr;
+        sstr << ifs.rdbuf();
+        editor.SetText(sstr.str());
+    }
+}
+
+static void openNewFile()
+{
+    fileCurrentPath.clear();
+    editor.SetText(stmdsp::file_content);
+}
+
+static std::vector<std::filesystem::path> fileScanExamples()
+{
+    const auto path = std::filesystem::current_path() / "examples";
+    const std::filesystem::recursive_directory_iterator rdi (path);
+
+    std::vector<std::filesystem::path> list;
+    std::transform(
+        std::filesystem::begin(rdi),
+        std::filesystem::end(rdi),
+        std::back_inserter(list),
+        [](const auto& file) { return file.path(); });
+    std::sort(list.begin(), list.end());
+    return list;
+}
+
+void fileInit()
+{
+    fileExampleList = fileScanExamples();
+    openNewFile();
+}
+
+void fileRenderMenu()
+{
+    if (ImGui::BeginMenu("File")) {
+        if (ImGui::MenuItem("New")) {
+            // TODO modified?
+            openNewFile();
+            log("Ready.");
+        }
+
+        if (ImGui::MenuItem("Open")) {
+            fileAction = FileAction::Open;
+            ImGuiFileDialog::Instance()->OpenDialog(
+                "ChooseFileOpenSave", "Choose File", ".cpp", ".", 1, nullptr, ImGuiFileDialogFlags_Modal);
+        }
+
+        if (ImGui::BeginMenu("Open Example")) {
+            for (const auto& file : fileExampleList) {
+                if (ImGui::MenuItem(file.filename().string().c_str())) {
+                    fileCurrentPath = file.string();
+                    openCurrentFile();
+
+                    // Treat like new file.
+                    fileCurrentPath.clear();
+                    log("Ready.");
+                }
+            }
+
+            ImGui::EndMenu();
+        }
+
+        if (ImGui::MenuItem("Save")) {
+            if (fileCurrentPath.size() > 0) {
+                saveCurrentFile();
+            } else {
+                fileAction = FileAction::SaveAs;
+                ImGuiFileDialog::Instance()->OpenDialog(
+                    "ChooseFileOpenSave", "Choose File", ".cpp", ".", 1, nullptr, ImGuiFileDialogFlags_Modal);
+            }
+        }
+
+        if (ImGui::MenuItem("Save As")) {
+            fileAction = FileAction::SaveAs;
+            ImGuiFileDialog::Instance()->OpenDialog(
+                "ChooseFileOpenSave", "Choose File", ".cpp", ".", 1, nullptr, ImGuiFileDialogFlags_Modal);
+        }
+
+        ImGui::Separator();
+        if (ImGui::MenuItem("Quit")) {
+            SDL_Event quitEvent (SDL_QUIT);
+            SDL_PushEvent(&quitEvent);
+        }
+
+        ImGui::EndMenu();
+    }
+}
+
+void fileRenderDialog()
+{
+    if (ImGuiFileDialog::Instance()->Display("ChooseFileOpenSave",
+                                             ImGuiWindowFlags_NoCollapse,
+                                             ImVec2(460, 540)))
+    {
+        if (ImGuiFileDialog::Instance()->IsOk()) {
+            std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
+
+           if (fileAction == FileAction::Open) {
+                fileCurrentPath = filePathName;
+                openCurrentFile();
+                log("Ready.");
+           } else if (fileAction == FileAction::SaveAs) {
+                fileCurrentPath = filePathName;
+                saveCurrentFile();
+            }
+        }
+        
+        ImGuiFileDialog::Instance()->Close();
+    }
+}
+
diff --git a/gui/source/gui.cpp b/gui/source/gui.cpp
new file mode 100644 (file)
index 0000000..45bdf66
--- /dev/null
@@ -0,0 +1,151 @@
+/**
+ * @file gui.cpp
+ * @brief Contains code for GUI-related logic.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "imgui.h"
+#include "imgui_internal.h"
+#include "backends/imgui_impl_sdl2.h"
+#include "backends/imgui_impl_opengl2.h"
+
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_opengl.h>
+
+bool guiInitialize();
+void guiRender();
+bool guiHandleEvents();
+void guiShutdown();
+
+static SDL_Window *window = nullptr;
+static SDL_GLContext gl_context;
+
+bool guiInitialize()
+{
+    if (SDL_Init(0) != 0) {
+        printf("Error: %s\n", SDL_GetError());
+        return false;
+    }
+
+    // Setup window
+    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
+    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
+    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
+    window = SDL_CreateWindow("stmdsp gui",
+        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
+        640, 700,
+        SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE /*| SDL_WINDOW_ALLOW_HIGHDPI*/);
+
+    if (window == nullptr) {
+        puts("Error: Could not create the window!");
+        return false;
+    }
+
+    SDL_SetWindowMinimumSize(window, 320, 320);
+
+    gl_context = SDL_GL_CreateContext(window);
+    SDL_GL_MakeCurrent(window, gl_context);
+    SDL_GL_SetSwapInterval(1); // Enable vsync
+
+    // Setup Dear ImGui context
+    IMGUI_CHECKVERSION();
+    ImGui::CreateContext();
+    //io->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
+
+    ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
+    ImGui_ImplOpenGL2_Init();
+
+    ImGui::StyleColorsLight();
+    ImGuiStyle& style = ImGui::GetStyle();
+    style.WindowRounding = 5;
+    style.FrameRounding = 3;
+    style.ScrollbarRounding = 1;
+
+//#define ACCENT1 0.26f, 0.59f, 0.98f
+#define ACCENT1 0.6f, 0.6f, 0.6f
+#define ACCENT2 0.4f, 0.4f, 0.4f
+
+    style.Colors[ImGuiCol_FrameBgHovered]         = ImVec4(ACCENT1, 0.40f);
+    style.Colors[ImGuiCol_FrameBgActive]          = ImVec4(ACCENT1, 0.67f);
+    style.Colors[ImGuiCol_CheckMark]              = ImVec4(ACCENT1, 1.00f);
+    style.Colors[ImGuiCol_SliderGrab]             = ImVec4(ACCENT1, 0.78f);
+    style.Colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.46f, 0.54f, 0.80f, 0.60f);
+    style.Colors[ImGuiCol_Button]                 = ImVec4(ACCENT1, 0.40f);
+    style.Colors[ImGuiCol_ButtonHovered]          = ImVec4(ACCENT1, 1.00f);
+    style.Colors[ImGuiCol_ButtonActive]           = ImVec4(ACCENT2, 1.00f);
+    style.Colors[ImGuiCol_Header]                 = ImVec4(ACCENT1, 0.31f);
+    style.Colors[ImGuiCol_HeaderHovered]          = ImVec4(ACCENT1, 0.80f);
+    style.Colors[ImGuiCol_HeaderActive]           = ImVec4(ACCENT1, 1.00f);
+    style.Colors[ImGuiCol_Separator]              = ImVec4(0.39f, 0.39f, 0.39f, 0.62f);
+    style.Colors[ImGuiCol_SeparatorHovered]       = ImVec4(0.14f, 0.44f, 0.80f, 0.78f);
+    style.Colors[ImGuiCol_SeparatorActive]        = ImVec4(0.14f, 0.44f, 0.80f, 1.00f);
+    style.Colors[ImGuiCol_ResizeGripHovered]      = ImVec4(ACCENT1, 0.67f);
+    style.Colors[ImGuiCol_ResizeGripActive]       = ImVec4(ACCENT1, 0.95f);
+    style.Colors[ImGuiCol_TableHeaderBg]          = ImVec4(0.78f, 0.87f, 0.98f, 1.00f);
+    style.Colors[ImGuiCol_TableBorderStrong]      = ImVec4(0.57f, 0.57f, 0.64f, 1.00f);
+    style.Colors[ImGuiCol_TableBorderLight]       = ImVec4(0.68f, 0.68f, 0.74f, 1.00f);
+    style.Colors[ImGuiCol_TextSelectedBg]         = ImVec4(ACCENT1, 0.35f);
+    style.Colors[ImGuiCol_DragDropTarget]         = ImVec4(ACCENT1, 0.95f);
+
+    style.Colors[ImGuiCol_Tab]                    = ImLerp(style.Colors[ImGuiCol_Header],       style.Colors[ImGuiCol_TitleBgActive], 0.90f);
+    style.Colors[ImGuiCol_TabHovered]             = style.Colors[ImGuiCol_HeaderHovered];
+    style.Colors[ImGuiCol_TabActive]              = ImLerp(style.Colors[ImGuiCol_HeaderActive], style.Colors[ImGuiCol_TitleBgActive], 0.60f);
+    style.Colors[ImGuiCol_TabUnfocused]           = ImLerp(style.Colors[ImGuiCol_Tab],          style.Colors[ImGuiCol_TitleBg], 0.80f);
+    style.Colors[ImGuiCol_TabUnfocusedActive]     = ImLerp(style.Colors[ImGuiCol_TabActive],    style.Colors[ImGuiCol_TitleBg], 0.40f);
+    style.Colors[ImGuiCol_NavHighlight]           = style.Colors[ImGuiCol_HeaderHovered];
+
+    return true;
+}
+
+void guiRender()
+{
+    ImGui::Render();
+
+    const auto& displaySize = ImGui::GetIO().DisplaySize;
+    const int sizeX = static_cast<int>(displaySize.x);
+    const int sizeY = static_cast<int>(displaySize.y);
+
+    glViewport(0, 0, sizeX, sizeY);
+    glClearColor(1, 1, 1, 1);
+    glClear(GL_COLOR_BUFFER_BIT);
+    ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
+    SDL_GL_SwapWindow(window);
+}
+
+bool guiHandleEvents()
+{
+    bool done = false;
+
+    for (SDL_Event event; SDL_PollEvent(&event);) {
+        ImGui_ImplSDL2_ProcessEvent(&event);
+        if (event.type == SDL_QUIT) {
+            done = true;
+        } else if (event.type == SDL_WINDOWEVENT) {
+            const auto& ew = event.window;
+            const auto wid = SDL_GetWindowID(window);
+            if (ew.event == SDL_WINDOWEVENT_CLOSE && ew.windowID == wid)
+                done = true;
+        }
+    }
+
+    return done;
+}
+
+void guiShutdown()
+{
+    ImGui_ImplOpenGL2_Shutdown();
+    ImGui_ImplSDL2_Shutdown();
+    ImGui::DestroyContext();
+
+    SDL_GL_DeleteContext(gl_context);
+    SDL_DestroyWindow(window);
+    SDL_Quit();
+}
+
diff --git a/gui/source/gui_code.cpp b/gui/source/gui_code.cpp
new file mode 100644 (file)
index 0000000..b7b56dd
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * @file code.cpp
+ * @brief Contains code for algorithm-code-related UI elements and logic.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "code.hpp"
+#include "imgui.h"
+#include "backends/imgui_impl_sdl2.h"
+#include "backends/imgui_impl_opengl2.h"
+#include "TextEditor.h"
+
+#include <string>
+
+TextEditor editor; // file.cpp
+
+static std::string editorCompiled;
+
+static void codeCompile();
+static void codeDisassemble();
+
+void codeEditorInit()
+{
+    editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus());
+    editor.SetPalette(TextEditor::GetLightPalette());
+}
+
+void codeRenderMenu()
+{
+    if (ImGui::BeginMenu("Code")) {
+        if (ImGui::MenuItem("Compile"))
+            codeCompile();
+        if (ImGui::MenuItem("Disassemble"))
+            codeDisassemble();
+
+        ImGui::EndMenu();
+    }
+}
+
+void codeRenderToolbar()
+{
+    if (ImGui::Button("Compile"))
+        codeCompile();
+}
+
+void codeRenderWidgets(const ImVec2& size)
+{
+    editor.Render("code", size, true);
+}
+
+static void codeCompile()
+{
+    compileEditorCode(editor.GetText());
+    editorCompiled = editor.GetText().compare(editorCompiled);
+}
+
+static void codeDisassemble()
+{
+    if (editor.GetText().compare(editorCompiled) != 0)
+        codeCompile();
+    disassembleCode();
+}
+
diff --git a/gui/source/gui_device.cpp b/gui/source/gui_device.cpp
new file mode 100644 (file)
index 0000000..b048abf
--- /dev/null
@@ -0,0 +1,439 @@
+#include "circular.hpp"
+#include "imgui.h"
+#include "imgui_internal.h"
+#include "ImGuiFileDialog.h"
+
+#include "stmdsp.hpp"
+
+#include <array>
+#include <cstdio>
+#include <memory>
+#include <string>
+#include <string_view>
+
+namespace ImGui
+{
+    void PushDisabled()
+    {
+        ImGuiContext& g = *GImGui;
+        bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
+        if (!was_disabled)
+            PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.6f);
+        PushItemFlag(ImGuiItemFlags_Disabled, true);
+    }
+
+    void PopDisabled()
+    {
+        ImGuiContext& g = *GImGui;
+        bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;
+        PopItemFlag();
+        if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)
+            PopStyleVar();
+    }
+}
+
+// Used for status queries and buffer size configuration.
+extern std::shared_ptr<stmdsp::device> m_device;
+
+void deviceAlgorithmUnload();
+void deviceAlgorithmUpload();
+bool deviceConnect();
+void deviceGenLoadFormula(const std::string& list);
+void deviceGenLoadList(std::string_view list);
+bool deviceGenStartToggle();
+void deviceLoadAudioFile(const std::string& file);
+void deviceLoadLogFile(const std::string& file);
+void deviceSetSampleRate(unsigned int index);
+void deviceSetInputDrawing(bool enabled);
+void deviceStart(bool logResults, bool drawSamples);
+void deviceStartMeasurement();
+void deviceUpdateDrawBufferSize(double timeframe);
+std::size_t pullFromDrawQueue(
+    CircularBuffer<std::vector, stmdsp::dacsample_t>& circ);
+std::size_t pullFromInputDrawQueue(
+    CircularBuffer<std::vector, stmdsp::dacsample_t>& circ);
+
+static std::string sampleRatePreview = "?";
+static bool measureCodeTime = false;
+static bool logResults = false;
+static bool drawSamples = false;
+static bool popupRequestBuffer = false;
+static bool popupRequestSiggen = false;
+static bool popupRequestLog = false;
+static double drawSamplesTimeframe = 1.0; // seconds
+
+static std::string getSampleRatePreview(unsigned int rate)
+{
+    return std::to_string(rate / 1000) + " kHz";
+}
+
+static std::string connectLabel ("Connect");
+void deviceRenderDisconnect()
+{
+    connectLabel = "Connect";
+    measureCodeTime = false;
+    logResults = false;
+    drawSamples = false;
+}
+
+void deviceRenderMenu()
+{
+    auto addMenuItem = [](const std::string& label, bool enable, auto action) {
+        if (ImGui::MenuItem(label.c_str(), nullptr, false, enable)) {
+            action();
+        }
+    };
+
+    if (ImGui::BeginMenu("Device")) {
+        addMenuItem(connectLabel, !m_device || !m_device->is_running(), [&] {
+                if (deviceConnect()) {
+                    connectLabel = "Disconnect";
+                    sampleRatePreview =
+                        getSampleRatePreview(m_device->get_sample_rate());
+                    deviceUpdateDrawBufferSize(drawSamplesTimeframe);
+                } else {
+                    deviceRenderDisconnect();
+                }
+            });
+
+        const bool isConnected = m_device ? true : false;
+        const bool isRunning = isConnected && m_device->is_running();
+
+        ImGui::Separator();
+
+        static std::string startLabel ("Start");
+        addMenuItem(startLabel, isConnected, [&] {
+                startLabel = isRunning ? "Start" : "Stop";
+                deviceStart(logResults, drawSamples);
+                if (logResults && isRunning)
+                    logResults = false;
+            });
+        addMenuItem("Upload algorithm", isConnected && !isRunning,
+            deviceAlgorithmUpload);
+        addMenuItem("Unload algorithm", isConnected && !isRunning,
+            deviceAlgorithmUnload);
+        addMenuItem("Measure Code Time", isRunning, deviceStartMeasurement);
+
+        ImGui::Separator();
+        if (!isConnected || isRunning)
+            ImGui::PushDisabled(); // Hey, pushing disabled!
+
+        ImGui::Checkbox("Draw samples", &drawSamples);
+        if (ImGui::Checkbox("Log results...", &logResults)) {
+            if (logResults)
+                popupRequestLog = true;
+        }
+        addMenuItem("Set buffer size...", true, [] { popupRequestBuffer = true; });
+
+        if (!isConnected || isRunning)
+            ImGui::PopDisabled();
+        ImGui::Separator();
+
+        addMenuItem("Load signal generator",
+            isConnected && !m_device->is_siggening() && !m_device->is_running(),
+            [] { popupRequestSiggen = true; });
+
+        static std::string startSiggenLabel ("Start signal generator");
+        addMenuItem(startSiggenLabel, isConnected, [&] {
+                const bool running = deviceGenStartToggle();
+                startSiggenLabel = running ? "Stop signal generator"
+                                           : "Start signal generator";
+            });
+
+        ImGui::EndMenu();
+    }
+}
+
+void deviceRenderToolbar()
+{
+    ImGui::SameLine();
+    if (ImGui::Button("Upload"))
+        deviceAlgorithmUpload();
+    ImGui::SameLine();
+    ImGui::SetNextItemWidth(100);
+
+    const bool enable =
+        m_device && !m_device->is_running() && !m_device->is_siggening();
+    if (!enable)
+        ImGui::PushDisabled();
+
+    if (ImGui::BeginCombo("", sampleRatePreview.c_str())) {
+        extern std::array<unsigned int, 6> sampleRateInts;
+
+        for (const auto& r : sampleRateInts) {
+            const auto s = getSampleRatePreview(r);
+            if (ImGui::Selectable(s.c_str())) {
+                sampleRatePreview = s;
+                deviceSetSampleRate(r);
+                deviceUpdateDrawBufferSize(drawSamplesTimeframe);
+            }
+        }
+
+        ImGui::EndCombo();
+    }
+
+    if (!enable)
+        ImGui::PopDisabled();
+}
+
+void deviceRenderWidgets()
+{
+    static std::string siggenInput (32768, '\0');
+    static int siggenOption = 0;
+
+    if (popupRequestSiggen) {
+        popupRequestSiggen = false;
+        ImGui::OpenPopup("siggen");
+    } else if (popupRequestBuffer) {
+        popupRequestBuffer = false;
+        ImGui::OpenPopup("buffer");
+    } else if (popupRequestLog) {
+        popupRequestLog = false;
+        ImGuiFileDialog::Instance()->OpenDialog(
+            "ChooseFileLog", "Choose File", ".csv", ".", 1, nullptr, ImGuiFileDialogFlags_Modal);
+    }
+
+    if (ImGui::BeginPopup("siggen")) {
+        if (ImGui::RadioButton("List", &siggenOption, 0)) {
+            siggenInput.resize(32768);
+            siggenInput[0] = '\0';
+        }
+        ImGui::SameLine();
+        if (ImGui::RadioButton("Formula", &siggenOption, 1)) {
+            siggenInput.resize(1024);
+            siggenInput[0] = '\0';
+        }
+        ImGui::SameLine();
+        if (ImGui::RadioButton("Audio File", &siggenOption, 2))
+            siggenInput.clear();
+
+        if (siggenOption == 2) {
+            if (ImGui::Button("Choose File")) {
+                // This dialog will override the siggen popup, closing it.
+                ImGuiFileDialog::Instance()->OpenDialog(
+                    "ChooseFileGen", "Choose File", ".wav", ".", 1, nullptr, ImGuiFileDialogFlags_Modal);
+            }
+        } else {
+            ImGui::Text(siggenOption == 0 ? "Enter a list of numbers:"
+                                          : "Enter a formula. x = sample #, y = -1 to 1.\nf(x) = ");
+            ImGui::PushStyleColor(ImGuiCol_FrameBg, {.8, .8, .8, 1});
+            ImGui::InputText("", siggenInput.data(), siggenInput.size());
+            ImGui::PopStyleColor();
+        }
+
+        if (ImGui::Button("Save")) {
+            switch (siggenOption) {
+            case 0:
+                deviceGenLoadList(siggenInput.substr(0, siggenInput.find('\0')));
+                break;
+            case 1:
+                deviceGenLoadFormula(siggenInput.substr(0, siggenInput.find('\0')));
+                break;
+            case 2:
+                break;
+            }
+
+            ImGui::CloseCurrentPopup();
+        }
+
+        ImGui::SameLine();
+        if (ImGui::Button("Cancel")) {
+            siggenInput.clear();
+            ImGui::CloseCurrentPopup();
+        }
+
+        ImGui::EndPopup();
+    }
+
+    if (ImGui::BeginPopup("buffer")) {
+        static std::string bufferSizeInput ("4096");
+        ImGui::Text("Please enter a new sample buffer size (100-4096):");
+        ImGui::PushStyleColor(ImGuiCol_FrameBg, {.8, .8, .8, 1});
+        ImGui::InputText("",
+            bufferSizeInput.data(),
+            bufferSizeInput.size(),
+            ImGuiInputTextFlags_CharsDecimal);
+        ImGui::PopStyleColor();
+        if (ImGui::Button("Save")) {
+            if (m_device) {
+                int n = std::clamp(std::stoi(bufferSizeInput), 100, 4096);
+                m_device->continuous_set_buffer_size(n);
+            }
+            ImGui::CloseCurrentPopup();
+        }
+        ImGui::SameLine();
+        if (ImGui::Button("Cancel"))
+            ImGui::CloseCurrentPopup();
+        ImGui::EndPopup();
+    }
+
+    if (ImGuiFileDialog::Instance()->Display("ChooseFileLog",
+                                             ImGuiWindowFlags_NoCollapse,
+                                             ImVec2(460, 540)))
+    {
+        if (ImGuiFileDialog::Instance()->IsOk()) {
+            const auto filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
+            deviceLoadLogFile(filePathName);
+        } else {
+            logResults = false;
+        }
+
+        ImGuiFileDialog::Instance()->Close();
+    }
+
+    if (ImGuiFileDialog::Instance()->Display("ChooseFileGen",
+                                             ImGuiWindowFlags_NoCollapse,
+                                             ImVec2(460, 540)))
+    {
+        if (ImGuiFileDialog::Instance()->IsOk()) {
+            const auto filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
+            deviceLoadAudioFile(filePathName);
+        }
+
+        ImGuiFileDialog::Instance()->Close();
+    }
+}
+
+void deviceRenderDraw()
+{
+    if (drawSamples) {
+        static std::vector<stmdsp::dacsample_t> buffer;
+        static std::vector<stmdsp::dacsample_t> bufferInput;
+        static auto bufferCirc = CircularBuffer(buffer);
+        static auto bufferInputCirc = CircularBuffer(bufferInput);
+
+        static bool drawSamplesInput = false;
+        static unsigned int yMinMax = 4095;
+
+        ImGui::Begin("draw", &drawSamples);
+        ImGui::Text("Draw input ");
+        ImGui::SameLine();
+        if (ImGui::Checkbox("", &drawSamplesInput)) {
+            deviceSetInputDrawing(drawSamplesInput);
+            if (drawSamplesInput) {
+                bufferCirc.reset(2048);
+                bufferInputCirc.reset(2048);
+            }
+        }
+        ImGui::SameLine();
+        ImGui::Text("Time: %0.3f sec", drawSamplesTimeframe);
+        ImGui::SameLine();
+        if (ImGui::Button("-", {30, 0})) {
+            drawSamplesTimeframe = std::max(drawSamplesTimeframe / 2., 0.0078125);
+            deviceUpdateDrawBufferSize(drawSamplesTimeframe);
+        }
+        ImGui::SameLine();
+        if (ImGui::Button("+", {30, 0})) {
+            drawSamplesTimeframe = std::min(drawSamplesTimeframe * 2, 32.);
+            deviceUpdateDrawBufferSize(drawSamplesTimeframe);
+        }
+        ImGui::SameLine();
+        ImGui::Text("Y: +/-%1.2fV", 3.3f * (static_cast<float>(yMinMax) / 4095.f));
+        ImGui::SameLine();
+        if (ImGui::Button(" - ", {30, 0})) {
+            yMinMax = std::max(63u, yMinMax >> 1);
+        }
+        ImGui::SameLine();
+        if (ImGui::Button(" + ", {30, 0})) {
+            yMinMax = std::min(4095u, (yMinMax << 1) | 1);
+        }
+
+        auto newSize = pullFromDrawQueue(bufferCirc);
+        if (newSize > 0) {
+            buffer.resize(newSize);
+            bufferCirc = CircularBuffer(buffer);
+            pullFromDrawQueue(bufferCirc);
+        }
+
+        if (drawSamplesInput) {
+            auto newSize = pullFromInputDrawQueue(bufferInputCirc);
+            if (newSize > 0) {
+                bufferInput.resize(newSize);
+                bufferInputCirc = CircularBuffer(bufferInput);
+                pullFromInputDrawQueue(bufferInputCirc);
+            }
+        }
+
+        auto drawList = ImGui::GetWindowDrawList();
+        ImVec2 p0 = ImGui::GetWindowPos();
+        auto size = ImGui::GetWindowSize();
+        p0.y += 65;
+        size.y -= 70;
+        drawList->AddRectFilled(p0, {p0.x + size.x, p0.y + size.y}, IM_COL32_BLACK);
+
+        const auto lcMinor = ImGui::GetColorU32(IM_COL32(40, 40, 40, 255));
+        const auto lcMajor = ImGui::GetColorU32(IM_COL32(140, 140, 140, 255));
+
+        {
+            const float yinc = (3. / 3.3) * size.y / 12.f;
+            const float center = p0.y + size.y / 2;
+            drawList->AddLine({p0.x, center}, {p0.x + size.x, center}, ImGui::GetColorU32(IM_COL32_WHITE));
+            for (int i = 1; i < 7; ++i) {
+                drawList->AddLine({p0.x, center + i * yinc}, {p0.x + size.x, center + i * yinc}, (i % 2) ? lcMinor : lcMajor);
+                drawList->AddLine({p0.x, center - i * yinc}, {p0.x + size.x, center - i * yinc}, (i % 2) ? lcMinor : lcMajor);
+            }
+        }
+        {
+            const float xinc = size.x / 16.f;
+            const float center = p0.x + size.x / 2;
+            drawList->AddLine({center, p0.y}, {center, p0.y + size.y}, ImGui::GetColorU32(IM_COL32_WHITE));
+            for (int i = 1; i < 8; ++i) {
+                drawList->AddLine({center + i * xinc, p0.y}, {center + i * xinc, p0.y + size.y}, (i % 2) ? lcMinor : lcMajor);
+                drawList->AddLine({center - i * xinc, p0.y}, {center - i * xinc, p0.y + size.y}, (i % 2) ? lcMinor : lcMajor);
+            }
+        }
+
+        const float di = static_cast<float>(buffer.size()) / size.x;
+        const float dx = std::ceil(size.x / static_cast<float>(buffer.size()));
+        ImVec2 pp = p0;
+        float i = 0;
+        while (pp.x < p0.x + size.x) {
+            unsigned int idx = i;
+            float n = std::clamp((buffer[idx] - 2048.) / yMinMax, -0.5, 0.5);
+            i += di;
+
+            ImVec2 next (pp.x + dx, p0.y + size.y * (0.5 - n));
+            drawList->AddLine(pp, next, ImGui::GetColorU32(IM_COL32(255, 0, 0, 255)));
+            pp = next;
+        }
+
+        if (drawSamplesInput) {
+            ImVec2 pp = p0;
+            float i = 0;
+            while (pp.x < p0.x + size.x) {
+                unsigned int idx = i;
+                float n = std::clamp((bufferInput[idx] - 2048.) / yMinMax, -0.5, 0.5);
+                i += di;
+
+                ImVec2 next (pp.x + dx, p0.y + size.y * (0.5 - n));
+                drawList->AddLine(pp, next, ImGui::GetColorU32(IM_COL32(0, 0, 255, 255)));
+                pp = next;
+            }
+        }
+
+        const auto mouse = ImGui::GetMousePos();
+        if (mouse.x > p0.x && mouse.x < p0.x + size.x &&
+            mouse.y > p0.y && mouse.y < p0.y + size.y)
+        {
+            char buf[16];
+            drawList->AddLine({mouse.x, p0.y}, {mouse.x, p0.y + size.y}, IM_COL32(255, 255, 0, 255));
+
+            {
+                const std::size_t si = (mouse.x - p0.x) / size.x * buffer.size();
+                const float s = buffer[si] / 4095.f * 6.6f - 3.3f;
+                snprintf(buf, sizeof(buf), "   %1.3fV", s);
+                drawList->AddText(mouse, IM_COL32(255, 0, 0, 255), buf);
+            }
+
+            if (drawSamplesInput) {
+                const std::size_t si = (mouse.x - p0.x) / size.x * bufferInput.size();
+                const float s = bufferInput[si] / 4095.f * 6.6f - 3.3f;
+                snprintf(buf, sizeof(buf), "   %1.3fV", s);
+                drawList->AddText({mouse.x, mouse.y + 20}, IM_COL32(0, 0, 255, 255), buf);
+            }
+        }
+
+        ImGui::End();
+    }
+}
+
diff --git a/gui/source/gui_help.cpp b/gui/source/gui_help.cpp
new file mode 100644 (file)
index 0000000..66ca955
--- /dev/null
@@ -0,0 +1,124 @@
+/**
+ * @file gui_help.cpp
+ * @brief Defines the "Help" menu and provides its functionality.
+ *
+ * Copyright (C) 2022 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "imgui.h"
+#include "ImGuiFileDialog.h"
+
+#include <cstdlib>
+#include <iostream>
+#include <string>
+#include <thread>
+
+void log(const std::string& str);
+
+static bool showDownloadFirmware = false;
+static bool showHelp = false;
+static std::string firmwareFile;
+
+static void helpDownloadThread();
+
+void helpRenderMenu()
+{
+    if (ImGui::BeginMenu("Help")) {
+        if (ImGui::MenuItem("Open wiki...")) {
+#ifdef STMDSP_WIN32
+            system("start "
+#else
+            system("xdg-open "
+#endif
+                "https://code.bitgloo.com/clyne/stmdspgui/wiki");
+        }
+
+        if (ImGui::MenuItem("Download firmware...")) {
+            showDownloadFirmware = true;
+        }
+
+        ImGui::Separator();
+        if (ImGui::MenuItem("About")) {
+            showHelp = true;
+        }
+
+        ImGui::EndMenu();
+    }
+}
+
+void helpRenderDialog()
+{
+    if (showDownloadFirmware) {
+       showDownloadFirmware = false;
+        ImGuiFileDialog::Instance()->OpenDialog(
+            "ChooseFileFW", "Choose Firmware File", ".hex", ".", 1, nullptr, ImGuiFileDialogFlags_Modal);
+    }
+
+    if (ImGuiFileDialog::Instance()->Display("ChooseFileFW",
+                                             ImGuiWindowFlags_NoCollapse,
+                                             ImVec2(460, 540)))
+    {
+        if (ImGuiFileDialog::Instance()->IsOk()) {
+            firmwareFile = ImGuiFileDialog::Instance()->GetFilePathName();
+#ifdef STMDSP_WIN32
+            size_t i = 0;
+            while ((i = firmwareFile.find('\\', i)) != std::string::npos) {
+                firmwareFile.replace(i, 1, "\\\\");
+                i += 2;
+            }
+#endif
+
+           std::thread(helpDownloadThread).detach();
+        }
+
+        ImGuiFileDialog::Instance()->Close();
+    }
+
+    if (showHelp) {
+        ImGui::Begin("help");
+
+        ImGui::Text("stmdspgui\nCompiled on " __DATE__ ".\n\nWritten by Clyne Sullivan.\n");
+
+        if (ImGui::Button("Close")) {
+            showHelp = false;
+        }
+
+        ImGui::End();
+    }
+
+    if (!firmwareFile.empty()) {
+       ImGui::Begin("Downloading");
+
+       ImGui::Text("Downloading firmware to device...");
+
+       ImGui::End();
+    }
+}
+
+void helpDownloadThread()
+{
+    std::string command (
+#ifdef STMDSP_WIN32
+        "openocd\\bin\\openocd.exe"
+#else
+       "openocd"
+#endif
+       " -f openocd.cfg -c \"program $0 reset exit\"");
+
+    command.replace(command.find("$0"), 2, firmwareFile);
+
+    std::cout << "Run: " << command << std::endl;
+
+    if (system(command.c_str()) == 0) {
+       log("Programming finished.");    
+    } else {
+       log("Error while programming device!");
+    }
+
+    firmwareFile.clear();
+}
+
diff --git a/gui/source/gui_help.hpp b/gui/source/gui_help.hpp
new file mode 100644 (file)
index 0000000..89c00ec
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+ * @file gui_help.hpp
+ * @brief Defines the "Help" menu and provides its functionality.
+ *
+ * Copyright (C) 2022 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSPGUI_GUI_HELP
+#define STMDSPGUI_GUI_HELP
+
+void helpRenderMenu();
+void helpRenderDialog();
+
+#endif // STMDSPGUI_GUI_HELP
+
diff --git a/gui/source/imgui b/gui/source/imgui
new file mode 160000 (submodule)
index 0000000..bc3c0ce
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit bc3c0ce7728f39530020a979a19bfc176e4e8596
diff --git a/gui/source/logview.cpp b/gui/source/logview.cpp
new file mode 100644 (file)
index 0000000..5a771bf
--- /dev/null
@@ -0,0 +1,82 @@
+#include "logview.h"
+
+LogView::LogView()
+{
+    Clear();
+}
+
+void LogView::Clear()
+{
+    Buf.clear();
+    LineOffsets.clear();
+    LineOffsets.push_back(0);
+    updated = false;
+}
+
+void LogView::AddLog(const std::string& str)
+{
+    AddLog(str.c_str());
+}
+
+void LogView::AddLog(const char* fmt, ...)
+{
+    int old_size = Buf.size();
+    va_list args;
+    va_start(args, fmt);
+    Buf.appendfv(fmt, args);
+    Buf.appendfv("\n", args);
+    va_end(args);
+    for (int new_size = Buf.size(); old_size < new_size; old_size++)
+        if (Buf[old_size] == '\n')
+            LineOffsets.push_back(old_size + 1);
+    updated = true;
+}
+
+void LogView::Draw(const char* title, bool* p_open, ImGuiWindowFlags flags)
+{
+    if (!ImGui::Begin(title, p_open, flags))
+    {
+        ImGui::End();
+        return;
+    }
+
+    ImGui::Text("Log ");
+    ImGui::SameLine(ImGui::GetWindowWidth() - 120);
+    if (ImGui::Button("Clear"))
+        Clear();
+    ImGui::SameLine();
+    if (ImGui::Button("Copy"))
+        ImGui::LogToClipboard();
+    ImGui::Separator();
+    ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
+
+
+    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
+    const char* buf = Buf.begin();
+    const char* buf_end = Buf.end();
+
+    ImGuiListClipper clipper;
+    clipper.Begin(LineOffsets.Size);
+
+    while (clipper.Step())
+    {
+        for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)
+        {
+            const char* line_start = buf + LineOffsets[line_no];
+            const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;
+            ImGui::TextUnformatted(line_start, line_end);
+        }
+    }
+    clipper.End();
+
+    ImGui::PopStyleVar();
+
+    if (updated) {
+        ImGui::SetScrollHereY();
+        updated = false;
+    }
+
+    ImGui::EndChild();
+    ImGui::End();
+}
+
diff --git a/gui/source/logview.h b/gui/source/logview.h
new file mode 100644 (file)
index 0000000..3c6acf1
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef LOGVIEW_H
+#define LOGVIEW_H
+
+#include <string>
+
+#include "imgui.h"
+
+// Adapted from ExampleAppLog from imgui_demo.cpp
+class LogView
+{
+public:
+    LogView();
+
+    void Clear();
+    void AddLog(const std::string& str);
+    void AddLog(const char* fmt, ...) IM_FMTARGS(2);
+    void Draw(const char* title, bool* p_open = NULL, ImGuiWindowFlags flags = 0);
+
+private:
+    ImGuiTextBuffer Buf;
+    ImVector<int> LineOffsets; // Index to lines offset. We maintain this with AddLog() calls.
+    bool updated;
+};
+
+#endif // LOGVIEW_H
+
diff --git a/gui/source/main.cpp b/gui/source/main.cpp
new file mode 100644 (file)
index 0000000..83f40bd
--- /dev/null
@@ -0,0 +1,148 @@
+/**
+ * @file main.cpp
+ * @brief Program entry point and main loop.
+ *
+ * Copyright (C) 2022 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "imgui.h"
+#include "backends/imgui_impl_sdl2.h"
+#include "backends/imgui_impl_opengl2.h"
+
+#include "gui_help.hpp"
+#include "logview.h"
+#include "main.hpp"
+#include "stmdsp.hpp"
+
+#include <chrono>
+#include <cmath>
+#include <iostream>
+#include <string>
+#include <thread>
+
+void codeEditorInit();
+void codeRenderMenu();
+void codeRenderToolbar();
+void codeRenderWidgets(const ImVec2& size);
+void deviceRenderDraw();
+void deviceRenderMenu();
+void deviceRenderToolbar();
+void deviceRenderWidgets();
+void fileRenderMenu();
+void fileRenderDialog();
+void fileInit();
+bool guiInitialize();
+bool guiHandleEvents();
+void guiShutdown();
+void guiRender();
+
+static LogView logView;
+static ImFont *fontSans = nullptr;
+static ImFont *fontMono = nullptr;
+
+template<bool first = false>
+static void renderWindow();
+
+int main(int, char **)
+{
+    if (!guiInitialize())
+        return -1;
+
+    auto& io = ImGui::GetIO();
+    fontSans = io.Fonts->AddFontFromFileTTF("fonts/Roboto-Regular.ttf", 20);
+    fontMono = io.Fonts->AddFontFromFileTTF("fonts/RobotoMono-Regular.ttf", 20);
+    if (fontSans == nullptr || fontMono == nullptr) {
+        std::cout << "Failed to load fonts!" << std::endl;
+        return -1;
+    }
+
+    codeEditorInit();
+    fileInit();
+
+    renderWindow<true>();
+
+    while (1) {
+        constexpr std::chrono::duration<double> fpsDelay (1. / 60.);
+        const auto endTime = std::chrono::steady_clock::now() + fpsDelay;
+
+        const bool isDone = guiHandleEvents();
+        if (!isDone) {
+            renderWindow();
+            std::this_thread::sleep_until(endTime);
+        } else {
+            break;
+        }
+    }
+
+    guiShutdown();
+    return 0;
+}
+
+void log(const std::string& str)
+{
+    logView.AddLog(str);
+}
+
+template<bool first>
+void renderWindow()
+{
+    // Start the new window frame and render the menu bar.
+    ImGui_ImplOpenGL2_NewFrame();
+    ImGui_ImplSDL2_NewFrame();
+    ImGui::NewFrame();
+
+    if (ImGui::BeginMainMenuBar()) {
+        fileRenderMenu();
+        deviceRenderMenu();
+        codeRenderMenu();
+       helpRenderMenu();
+
+        ImGui::EndMainMenuBar();
+    }
+
+    if constexpr (first) {
+        ImGui::SetNextWindowSize({550, 440});
+    }
+
+    constexpr int MainTopMargin = 22;
+    const auto& displaySize = ImGui::GetIO().DisplaySize;
+
+    ImGui::SetNextWindowPos({0, MainTopMargin});
+    ImGui::SetNextWindowSizeConstraints({displaySize.x, 150}, {displaySize.x, displaySize.y - 150});
+    ImGui::Begin("main", nullptr,
+                 ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoTitleBar |
+                     ImGuiWindowFlags_NoBringToFrontOnFocus);
+
+    const float mainWindowHeight = ImGui::GetWindowHeight();
+
+    ImGui::PushFont(fontSans);
+    codeRenderToolbar();
+    deviceRenderToolbar();
+    fileRenderDialog();
+    helpRenderDialog();
+    deviceRenderWidgets();
+    ImGui::PopFont();
+
+    ImGui::PushFont(fontMono);
+    codeRenderWidgets({displaySize.x - 16, mainWindowHeight - MainTopMargin - 24});
+    ImGui::PopFont();
+
+    ImGui::End();
+
+    // The log window is kept separate from "main" to support panel resizing.
+    ImGui::PushFont(fontMono);
+    ImGui::SetNextWindowPos({0, mainWindowHeight + MainTopMargin});
+    ImGui::SetNextWindowSize({displaySize.x, displaySize.y - mainWindowHeight - MainTopMargin});
+    logView.Draw("log", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBringToFrontOnFocus);
+    ImGui::PopFont();
+
+    deviceRenderDraw();
+
+    // Draw everything to the screen.
+    guiRender();
+}
+
diff --git a/gui/source/main.hpp b/gui/source/main.hpp
new file mode 100644 (file)
index 0000000..921d1ef
--- /dev/null
@@ -0,0 +1,20 @@
+/**
+ * @file main.hpp
+ * @brief Common functions.
+ *
+ * Copyright (C) 2022 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSPGUI_MAIN_HPP
+#define STMDSPGUI_MAIN_HPP
+
+#include <string>
+
+void log(const std::string& str);
+
+#endif // STMDSPGUI_MAIN_HPP
+
diff --git a/gui/source/serial b/gui/source/serial
new file mode 160000 (submodule)
index 0000000..69e0372
--- /dev/null
@@ -0,0 +1 @@
+Subproject commit 69e0372cf0d3796e84ce9a09aff1d74496f68720
diff --git a/gui/source/stmdsp/stmdsp.cpp b/gui/source/stmdsp/stmdsp.cpp
new file mode 100644 (file)
index 0000000..c50845f
--- /dev/null
@@ -0,0 +1,331 @@
+/**
+ * @file stmdsp.cpp
+ * @brief Interface for communication with stmdsp device over serial.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "stmdsp.hpp"
+
+#include <serial/serial.h>
+
+#include <algorithm>
+#include <array>
+
+extern void log(const std::string& str);
+
+std::array<unsigned int, 6> sampleRateInts {{
+    8'000,
+    16'000,
+    20'000,
+    32'000,
+    48'000,
+    96'000
+}};
+
+namespace stmdsp
+{
+    const std::forward_list<std::string>& scanner::scan()
+    {
+        auto devices = serial::list_ports();
+        auto foundDevicesEnd = std::remove_if(
+            devices.begin(), devices.end(),
+            [](const auto& dev) {
+                return dev.hardware_id.find(STMDSP_USB_ID) == std::string::npos;
+            });
+        std::transform(devices.begin(), foundDevicesEnd,
+            std::front_inserter(m_available_devices),
+            [](const auto& dev) { return dev.port; });
+        return m_available_devices;
+    }
+
+    device::device(const std::string& file)
+    {
+        // This could throw!
+       // Note: Windows needs a not-simple, positive timeout like this to
+       // ensure that reads block.
+        m_serial.reset(new serial::Serial(file, 921'600 /*8'000'000*/, serial::Timeout(1000, 1000, 1, 1000, 1)));
+
+        // Test the ID command.
+        m_serial->flush();
+        m_serial->write("i");
+        auto id = m_serial->read(7);
+
+        if (id.starts_with("stmdsp")) {
+            if (id.back() == 'h')
+                m_platform = platform::H7;
+            else if (id.back() == 'l')
+                m_platform = platform::L4;
+            else
+                m_serial.release();
+        } else {
+            m_serial.release();
+        }
+    }
+
+    device::~device()
+    {
+        disconnect();
+    }
+
+    bool device::connected() {
+        if (m_serial && !m_serial->isOpen())
+            m_serial.release();
+
+        return m_serial ? true : false;
+    }
+
+    void device::disconnect() {
+        if (m_serial)
+            m_serial.release();
+    }
+
+    bool device::try_command(std::basic_string<uint8_t> cmd) {
+        bool success = false;
+
+        if (connected()) {
+            try {
+                std::scoped_lock lock (m_lock);
+                m_serial->write(cmd.data(), cmd.size());
+                success = true;
+            } catch (...) {
+                handle_disconnect();
+            }
+        }
+
+        return success;
+    }
+
+    bool device::try_read(std::basic_string<uint8_t> cmd, uint8_t *dest, unsigned int dest_size) {
+        bool success = false;
+
+        if (connected() && dest && dest_size > 0) {
+            try {
+                std::scoped_lock lock (m_lock);
+                m_serial->write(cmd.data(), cmd.size());
+                m_serial->read(dest, dest_size);
+                success = true;
+            } catch (...) {
+                handle_disconnect();
+            }
+        }
+
+        return success;
+    }
+
+    void device::continuous_set_buffer_size(unsigned int size) {
+        if (try_command({
+                'B',
+                static_cast<uint8_t>(size),
+                static_cast<uint8_t>(size >> 8)}))
+        {
+            m_buffer_size = size;
+        }
+    }
+
+    void device::set_sample_rate(unsigned int rate) {
+        auto it = std::find(
+            sampleRateInts.cbegin(),
+            sampleRateInts.cend(),
+            rate);
+
+        if (it != sampleRateInts.cend()) {
+            const auto i = std::distance(sampleRateInts.cbegin(), it);
+            try_command({
+                'r',
+                static_cast<uint8_t>(i)
+            });
+        }
+    }
+
+    unsigned int device::get_sample_rate() {
+        if (!is_running()) {
+            uint8_t result = 0xFF;
+            if (try_read({'r', 0xFF}, &result, 1))
+                m_sample_rate = result;
+        }
+
+        return m_sample_rate < sampleRateInts.size() ?
+            sampleRateInts[m_sample_rate] :
+            0;
+    }
+
+    void device::continuous_start() {
+        if (try_command({'R'}))
+            m_is_running = true;
+    }
+
+    void device::measurement_start() {
+        try_command({'M'});
+    }
+
+    uint32_t device::measurement_read() {
+        uint32_t count = 0;
+        try_read({'m'}, reinterpret_cast<uint8_t *>(&count), sizeof(uint32_t));
+        return count / 2;
+    }
+
+    std::vector<adcsample_t> device::continuous_read() {
+        if (connected()) {
+            try {
+                m_serial->write("s");
+                unsigned char sizebytes[2];
+                m_serial->read(sizebytes, 2);
+                unsigned int size = sizebytes[0] | (sizebytes[1] << 8);
+                if (size > 0) {
+                    std::vector<adcsample_t> data (size);
+                    unsigned int total = size * sizeof(adcsample_t);
+                    unsigned int offset = 0;
+
+                    while (total > 512) {
+                        m_serial->read(reinterpret_cast<uint8_t *>(&data[0]) + offset, 512);
+                        m_serial->write("n");
+                        offset += 512;
+                        total -= 512;
+                    }
+                    m_serial->read(reinterpret_cast<uint8_t *>(&data[0]) + offset, total);
+                    m_serial->write("n");
+                    return data;
+
+                }
+            } catch (...) {
+                handle_disconnect();
+            }
+        }
+
+        return {};
+    }
+
+    std::vector<adcsample_t> device::continuous_read_input() {
+        if (connected()) {
+            try {
+                m_serial->write("t");
+                unsigned char sizebytes[2];
+                m_serial->read(sizebytes, 2);
+                unsigned int size = sizebytes[0] | (sizebytes[1] << 8);
+                if (size > 0) {
+                    std::vector<adcsample_t> data (size);
+                    unsigned int total = size * sizeof(adcsample_t);
+                    unsigned int offset = 0;
+
+                    while (total > 512) {
+                        m_serial->read(reinterpret_cast<uint8_t *>(&data[0]) + offset, 512);
+                        m_serial->write("n");
+                        offset += 512;
+                        total -= 512;
+                    }
+                    m_serial->read(reinterpret_cast<uint8_t *>(&data[0]) + offset, total);
+                    m_serial->write("n");
+                    return data;
+
+                }
+            } catch (...) {
+                handle_disconnect();
+            }
+        }
+
+        return {};
+    }
+
+    void device::continuous_stop() {
+        if (try_command({'S'}))
+            m_is_running = false;
+    }
+
+    bool device::siggen_upload(dacsample_t *buffer, unsigned int size) {
+        if (connected()) {
+            uint8_t request[3] = {
+                'D',
+                static_cast<uint8_t>(size),
+                static_cast<uint8_t>(size >> 8)
+            };
+
+            if (!m_is_siggening) {
+                try {
+                    m_serial->write(request, 3);
+                    m_serial->write((uint8_t *)buffer, size * sizeof(dacsample_t));
+                } catch (...) {
+                    handle_disconnect();
+                }
+            } else {
+                try {
+                    m_serial->write(request, 3);
+                    if (m_serial->read(1)[0] == 0)
+                        return false;
+                    else
+                        m_serial->write((uint8_t *)buffer, size * sizeof(dacsample_t));
+                } catch (...) {
+                    handle_disconnect();
+                }
+            }
+
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    void device::siggen_start() {
+        if (try_command({'W'}))
+            m_is_siggening = true;
+    }
+
+    void device::siggen_stop() {
+        if (try_command({'w'}))
+            m_is_siggening = false;
+    }
+
+    void device::upload_filter(unsigned char *buffer, size_t size) {
+        if (connected()) {
+            uint8_t request[3] = {
+                'E',
+                static_cast<uint8_t>(size),
+                static_cast<uint8_t>(size >> 8)
+            };
+
+            try {
+                m_serial->write(request, 3);
+                m_serial->write(buffer, size);
+            } catch (...) {
+                handle_disconnect();
+            }
+        }
+    }
+
+    void device::unload_filter() {
+        try_command({'e'});
+    }
+
+    std::pair<RunStatus, Error> device::get_status() {
+        std::pair<RunStatus, Error> ret;
+
+        unsigned char buf[2];
+        if (try_read({'I'}, buf, 2)) {
+            ret = {
+                static_cast<RunStatus>(buf[0]),
+                static_cast<Error>(buf[1])
+            };
+
+            bool running = ret.first == RunStatus::Running;
+            if (m_is_running != running)
+                m_is_running = running;
+        } else if (m_disconnect_error_flag) {
+            m_disconnect_error_flag = false;
+            return {RunStatus::Idle, Error::GUIDisconnect};
+        }
+
+        return ret;
+    }
+
+    void device::handle_disconnect()
+    {
+        m_disconnect_error_flag = true;
+        m_serial.release();
+        log("Lost connection!");
+    }
+} // namespace stmdsp
+
diff --git a/gui/source/stmdsp/stmdsp.hpp b/gui/source/stmdsp/stmdsp.hpp
new file mode 100644 (file)
index 0000000..efed8a3
--- /dev/null
@@ -0,0 +1,166 @@
+/**
+ * @file stmdsp.hpp
+ * @brief Interface for communication with stmdsp device over serial.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_HPP_
+#define STMDSP_HPP_
+
+#include <serial/serial.h>
+
+#include <cstdint>
+#include <forward_list>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <tuple>
+
+namespace stmdsp
+{
+    /**
+     * The largest possible size of an ADC or DAC sample buffer, as a sample count.
+     * Maximum byte size would be `SAMPLES_MAX * sizeof(XXXsample_t)`.
+     */
+    constexpr unsigned int SAMPLES_MAX = 4096;
+
+    /**
+     * ADC samples on all platforms are stored as 16-bit unsigned integers.
+     */
+    using adcsample_t = uint16_t;
+    /**
+     * DAC samples on all platforms are stored as 16-bit unsigned integers.
+     */
+    using dacsample_t = uint16_t;
+
+    /**
+     * List of all available platforms.
+     * Note that some platforms in this list may not have complete support.
+     */
+    enum class platform {
+        Unknown,
+        H7, /* Some feature support */
+        L4, /* Complete feature support */
+        G4  /* Unsupported, but planned */
+    };
+
+    /**
+     * Run status states, valued to match what the stmdsp firmware reports.
+     */
+    enum class RunStatus : char {
+        Idle = '1', /* Device ready for commands or execution. */
+        Running,    /* Device currently executing its algorithm. */
+        Recovering  /* Device recovering from fault caused by algorithm. */
+    };
+
+    /**
+     * Error messages that are reported by the firmware.
+     */
+    enum class Error : char {
+        None = 0,
+        BadParam,            /* An invalid parameter was passed for a command. */
+        BadParamSize,        /* An invaild param. size was given for a command. */
+        BadUserCodeLoad,     /* Device failed to load the given algorithm. */
+        BadUserCodeSize,     /* The given algorithm is too large for the device. */
+        NotIdle,             /* An idle-only command was received while not Idle. */
+        ConversionAborted,   /* A conversion was aborted due to a fault. */
+        NotRunning,          /* A running-only command was received while not Running. */
+
+        GUIDisconnect = 100  /* The GUI lost connection with the device. */
+    };
+
+    /**
+     * Provides functionality to scan the system for stmdsp devices.
+     * A list of devices is returned, though the GUI only interacts with one
+     * device at a time.
+     */
+    class scanner
+    {
+    public:
+        /**
+         * Scans for connected devices, returning a list of ports with
+         * connected stmdsp devices.
+         */
+        const std::forward_list<std::string>& scan();
+
+        /**
+         * Retrieves the results of the last scan().
+         */
+        const std::forward_list<std::string>& devices() const noexcept {
+            return m_available_devices;
+        }
+
+    private:
+        constexpr static const char *STMDSP_USB_ID =
+#ifndef STMDSP_WIN32
+            "USB VID:PID=0483:5740";
+#else
+            "USB\\VID_0483&PID_5740";
+#endif
+
+        std::forward_list<std::string> m_available_devices;
+    };
+
+    class device
+    {
+    public:
+        device(const std::string& file);
+        ~device();
+
+        bool connected();
+        void disconnect();
+
+        auto get_platform() const { return m_platform; }
+
+        void continuous_set_buffer_size(unsigned int size);
+        unsigned int get_buffer_size() const { return m_buffer_size; }
+
+        void set_sample_rate(unsigned int rate);
+        unsigned int get_sample_rate();
+
+        void continuous_start();
+        void continuous_stop();
+
+        void measurement_start();
+        uint32_t measurement_read();
+
+        std::vector<adcsample_t> continuous_read();
+        std::vector<adcsample_t> continuous_read_input();
+
+        bool siggen_upload(dacsample_t *buffer, unsigned int size);
+        void siggen_start();
+        void siggen_stop();
+
+        bool is_siggening() const { return m_is_siggening; }
+        bool is_running() const { return m_is_running; }
+
+        // buffer is ELF binary
+        void upload_filter(unsigned char *buffer, size_t size);
+        void unload_filter();
+
+        std::pair<RunStatus, Error> get_status();
+
+    private:
+        std::unique_ptr<serial::Serial> m_serial;
+        platform m_platform = platform::Unknown;
+        unsigned int m_buffer_size = SAMPLES_MAX;
+        unsigned int m_sample_rate = 0;
+        bool m_is_siggening = false;
+        bool m_is_running = false;
+        bool m_disconnect_error_flag = false;
+
+        std::mutex m_lock;
+
+        bool try_command(std::basic_string<uint8_t> data);
+        bool try_read(std::basic_string<uint8_t> cmd, uint8_t *dest, unsigned int dest_size);
+        void handle_disconnect();
+    };
+}
+
+#endif // STMDSP_HPP_
+
diff --git a/gui/source/stmdsp/stmdsp_code.hpp b/gui/source/stmdsp/stmdsp_code.hpp
new file mode 100644 (file)
index 0000000..7ba0ed2
--- /dev/null
@@ -0,0 +1,199 @@
+/**
+ * @file stmdsp_code.hpp
+ * @brief Source code and build scripts for stmdsp device algorithms.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef STMDSP_CODE_HPP
+#define STMDSP_CODE_HPP
+
+#ifdef STMDSP_WIN32
+#define NEWLINE "\r\n"
+#define COPY "copy"
+#else
+#define NEWLINE "\n"
+#define COPY "cp"
+#endif
+
+namespace stmdsp {
+
+// $0 = temp file name
+// TODO try -ffunction-sections -fdata-sections -Wl,--gc-sections
+static std::string makefile_text_h7 =
+#ifdef STMDSP_WIN32
+       "echo off" NEWLINE
+#endif
+    "arm-none-eabi-g++ -x c++ -Os -std=c++20 -fno-exceptions -fno-rtti "
+        "-mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 -mtune=cortex-m7 "
+       "-nostartfiles "
+        "-Wl,-Ttext-segment=0x00000000 -Wl,-zmax-page-size=512 -Wl,-eprocess_data_entry "
+        "$0 -o $0.o" NEWLINE
+       COPY " $0.o $0.orig.o" NEWLINE
+       "arm-none-eabi-strip -s -S --strip-unneeded $0.o" NEWLINE
+       "arm-none-eabi-objcopy --remove-section .ARM.attributes "
+                          "--remove-section .comment "
+                          "--remove-section .noinit "
+                          "$0.o" NEWLINE
+       "arm-none-eabi-size $0.o" NEWLINE;
+static std::string makefile_text_l4 =
+#ifdef STMDSP_WIN32
+       "echo off" NEWLINE
+#endif
+    "arm-none-eabi-g++ -x c++ -Os -std=c++20 -fno-exceptions -fno-rtti "
+        "-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mtune=cortex-m4 "
+        "-nostartfiles -I$1/cmsis "
+        "-Wl,-Ttext-segment=0x10000000 -Wl,-zmax-page-size=512 -Wl,-eprocess_data_entry "
+        "$0 -o $0.o" NEWLINE
+    COPY " $0.o $0.orig.o" NEWLINE
+    "arm-none-eabi-strip -s -S --strip-unneeded $0.o" NEWLINE
+    "arm-none-eabi-objcopy --remove-section .ARM.attributes "
+                          "--remove-section .comment "
+                          "--remove-section .noinit "
+                          "$0.o" NEWLINE
+    "arm-none-eabi-size $0.o" NEWLINE;
+
+// $0 = buffer size
+static std::string file_header_h7 = R"cpp(
+#include <cstdint>
+#include <span>
+
+using Sample = uint16_t;
+using Samples = std::span<Sample, $0>;
+
+Sample *process_data(Samples samples);
+extern "C" void process_data_entry()
+{
+    Sample *samples;
+    asm("mov %0, r0" : "=r" (samples));
+    process_data(Samples(samples, $0));
+}
+
+static double PI = 3.14159265358979323846L;
+__attribute__((naked))
+auto sin(double x) {
+asm("vmov.f64 r1, r2, d0;"
+    "eor r0, r0;"
+    "svc 1;"
+    "vmov.f64 d0, r1, r2;"
+    "bx lr");
+return 0;
+}
+__attribute__((naked))
+auto cos(double x) {
+asm("vmov.f64 r1, r2, d0;"
+       "mov r0, #1;"
+       "svc 1;"
+       "vmov.f64 d0, r1, r2;"
+       "bx lr");
+return 0;
+}
+__attribute__((naked))
+auto tan(double x) {
+asm("vmov.f64 r1, r2, d0;"
+       "mov r0, #2;"
+       "svc 1;"
+       "vmov.f64 d0, r1, r2;"
+       "bx lr");
+return 0;
+}
+__attribute__((naked))
+auto sqrt(double x) {
+asm("vsqrt.f64 d0, d0; bx lr");
+return 0;
+}
+
+auto readalt() {
+Sample s;
+asm("svc 3; mov %0, r0" : "=&r"(s));
+return s;
+}
+
+// End stmdspgui header code
+
+)cpp";
+static std::string file_header_l4 = R"cpp(
+#include <cstdint>
+
+using Sample = uint16_t;
+using Samples = Sample[$0];
+constexpr unsigned int SIZE = $0;
+
+Sample *process_data(Samples samples);
+extern "C" void process_data_entry()
+{
+    Sample *samples;
+    asm("mov %0, r0" : "=r" (samples));
+    process_data(samples);
+}
+
+static inline float PI = 3.14159265358979L;
+__attribute__((naked))
+static inline auto sin(float x) {
+    asm("vmov.f32 r1, s0;"
+    "eor r0, r0;"
+    "svc 1;"
+    "vmov.f32 s0, r1;"
+    "bx lr");
+    return 0;
+}
+__attribute__((naked))
+static inline auto cos(float x) {
+    asm("vmov.f32 r1, s0;"
+       "mov r0, #1;"
+       "svc 1;"
+       "vmov.f32 s0, r1;"
+       "bx lr");
+    return 0;
+}
+__attribute__((naked))
+static inline auto tan(float x) {
+    asm("vmov.f32 r1, s0;"
+       "mov r0, #2;"
+       "svc 1;"
+       "vmov.f32 s0, r1;"
+       "bx lr");
+    return 0;
+}
+__attribute__((naked))
+static inline auto sqrt(float) {
+    asm("vsqrt.f32 s0, s0; bx lr");
+    return 0;
+}
+
+static inline auto param1() {
+    Sample s;
+    asm("eor r0, r0; svc 3; mov %0, r0" : "=r" (s) :: "r0");
+    return s;
+}
+static inline auto param2() {
+    Sample s;
+    asm("mov r0, #1; svc 3; mov %0, r0" : "=r" (s) :: "r0");
+    return s;
+}
+
+//static inline void puts(const char *s) {
+//    // 's' will already be in r0.
+//    asm("push {r4-r6}; svc 4; pop {r4-r6}");
+//}
+
+// End stmdspgui header code
+
+)cpp";
+
+
+static std::string file_content = 
+R"cpp(Sample* process_data(Samples samples)
+{
+    return samples;
+}
+)cpp";
+
+} // namespace stmdsp
+
+#endif // STMDSP_CODE_HPP
+
diff --git a/gui/source/wav.hpp b/gui/source/wav.hpp
new file mode 100644 (file)
index 0000000..e20776a
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef WAV_HPP_
+#define WAV_HPP_
+
+#include <cstdint>
+#include <cstring>
+#include <fstream>
+#include <string>
+#include <vector>
+
+namespace wav
+{
+    struct header {
+        char riff[4];      // "RIFF"
+        uint32_t filesize; // Total file size minus eight bytes
+        char wave[4];      // "WAVE"
+
+        bool valid() const {
+            return strncmp(riff, "RIFF", 4) == 0 && filesize > 8 && strncmp(wave, "WAVE", 4) == 0;
+        }
+    } __attribute__ ((packed));
+
+    struct format {
+        char fmt_[4]; // "fmt "
+        uint32_t size;
+        uint16_t type;
+        uint16_t channelcount;
+        uint32_t samplerate;
+        uint32_t byterate;
+        uint16_t unused;
+        uint16_t bps;
+
+        bool valid() const {
+            return strncmp(fmt_, "fmt ", 4) == 0;
+        }
+    } __attribute__ ((packed));
+
+    struct data {
+        char data[4]; // "data"
+        uint32_t size;
+
+        bool valid() const {
+            return strncmp(data, "data", 4) == 0;
+        }
+    } __attribute__ ((packed));
+
+    class clip {
+    public:
+        clip(const std::string& path) {
+            std::ifstream file (path);
+            if (!file.good())
+                return;
+            {
+                header h;
+                file.read(reinterpret_cast<char *>(&h), sizeof(header));
+                if (!h.valid())
+                    return;
+            }
+            {
+                format f;
+                file.read(reinterpret_cast<char *>(&f), sizeof(format));
+                if (!f.valid() || f.type != 1) // ensure PCM
+                    return;
+            }
+            {
+                wav::data d;
+                file.read(reinterpret_cast<char *>(&d), sizeof(wav::data));
+                if (!d.valid())
+                    return;
+                m_data.resize(d.size / sizeof(int16_t));
+                m_next = m_data.begin();
+                file.read(reinterpret_cast<char *>(m_data.data()), d.size);
+            }
+        }
+        clip() = default;
+
+        bool valid() const {
+            return !m_data.empty();
+        }
+        const int16_t *data() const {
+            return m_data.data();
+        }
+        void next(int16_t *buf, unsigned int size) {
+            for (unsigned int i = 0; i < size; ++i) {
+                if (m_next == m_data.end())
+                    m_next = m_data.begin();
+                else
+                    *buf++ = *m_next++;
+            }
+        }
+    private:
+        std::vector<int16_t> m_data;
+        decltype(m_data.begin()) m_next;
+    };
+}
+
+#endif // WAV_HPP_
+
diff --git a/hardware/DSP PAW add-on board.kicad_pro b/hardware/DSP PAW add-on board.kicad_pro
new file mode 100755 (executable)
index 0000000..bb1726d
--- /dev/null
@@ -0,0 +1,352 @@
+{
+  "board": {
+    "3dviewports": [],
+    "design_settings": {
+      "defaults": {
+        "board_outline_line_width": 0.1,
+        "copper_line_width": 0.2,
+        "copper_text_size_h": 1.5,
+        "copper_text_size_v": 1.5,
+        "copper_text_thickness": 0.3,
+        "other_line_width": 0.15,
+        "silk_line_width": 0.15,
+        "silk_text_size_h": 1.0,
+        "silk_text_size_v": 1.0,
+        "silk_text_thickness": 0.15
+      },
+      "diff_pair_dimensions": [],
+      "drc_exclusions": [],
+      "rules": {
+        "min_copper_edge_clearance": 0.0,
+        "solder_mask_clearance": 0.0,
+        "solder_mask_min_width": 0.0
+      },
+      "track_widths": [],
+      "via_dimensions": []
+    },
+    "layer_presets": [],
+    "viewports": []
+  },
+  "boards": [],
+  "cvpcb": {
+    "equivalence_files": []
+  },
+  "erc": {
+    "erc_exclusions": [
+      "power_pin_not_driven|2184400|939800|a4e547a7-29d8-439d-b01e-1a601ce2877f|00000000-0000-0000-0000-000000000000|||",
+      "power_pin_not_driven|2387600|1104900|d93ff3db-bdfb-4d71-83ae-9eb3adbc430b|00000000-0000-0000-0000-000000000000|||",
+      "power_pin_not_driven|838200|660400|17dc7a2c-d6c1-4236-91fa-2b37d621dec2|00000000-0000-0000-0000-000000000000|||"
+    ],
+    "meta": {
+      "version": 0
+    },
+    "pin_map": [
+      [
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        1,
+        0,
+        0,
+        0,
+        0,
+        2
+      ],
+      [
+        0,
+        2,
+        0,
+        1,
+        0,
+        0,
+        1,
+        0,
+        2,
+        2,
+        2,
+        2
+      ],
+      [
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        1,
+        0,
+        1,
+        0,
+        1,
+        2
+      ],
+      [
+        0,
+        1,
+        0,
+        0,
+        0,
+        0,
+        1,
+        1,
+        2,
+        1,
+        1,
+        2
+      ],
+      [
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        1,
+        0,
+        0,
+        0,
+        0,
+        2
+      ],
+      [
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        0,
+        2
+      ],
+      [
+        1,
+        1,
+        1,
+        1,
+        1,
+        0,
+        1,
+        1,
+        1,
+        1,
+        1,
+        2
+      ],
+      [
+        0,
+        0,
+        0,
+        1,
+        0,
+        0,
+        1,
+        0,
+        0,
+        0,
+        0,
+        2
+      ],
+      [
+        0,
+        2,
+        1,
+        2,
+        0,
+        0,
+        1,
+        0,
+        2,
+        2,
+        2,
+        2
+      ],
+      [
+        0,
+        2,
+        0,
+        1,
+        0,
+        0,
+        1,
+        0,
+        2,
+        0,
+        0,
+        2
+      ],
+      [
+        0,
+        2,
+        1,
+        1,
+        0,
+        0,
+        1,
+        0,
+        2,
+        0,
+        0,
+        2
+      ],
+      [
+        2,
+        2,
+        2,
+        2,
+        2,
+        2,
+        2,
+        2,
+        2,
+        2,
+        2,
+        2
+      ]
+    ],
+    "rule_severities": {
+      "bus_definition_conflict": "error",
+      "bus_entry_needed": "error",
+      "bus_to_bus_conflict": "error",
+      "bus_to_net_conflict": "error",
+      "conflicting_netclasses": "error",
+      "different_unit_footprint": "error",
+      "different_unit_net": "error",
+      "duplicate_reference": "error",
+      "duplicate_sheet_names": "error",
+      "endpoint_off_grid": "warning",
+      "extra_units": "error",
+      "global_label_dangling": "warning",
+      "hier_label_mismatch": "error",
+      "label_dangling": "error",
+      "lib_symbol_issues": "warning",
+      "missing_bidi_pin": "warning",
+      "missing_input_pin": "warning",
+      "missing_power_pin": "error",
+      "missing_unit": "warning",
+      "multiple_net_names": "warning",
+      "net_not_bus_member": "warning",
+      "no_connect_connected": "warning",
+      "no_connect_dangling": "warning",
+      "pin_not_connected": "error",
+      "pin_not_driven": "error",
+      "pin_to_pin": "error",
+      "power_pin_not_driven": "error",
+      "similar_labels": "warning",
+      "simulation_model_issue": "error",
+      "unannotated": "error",
+      "unit_value_mismatch": "error",
+      "unresolved_variable": "error",
+      "wire_dangling": "error"
+    }
+  },
+  "libraries": {
+    "pinned_footprint_libs": [],
+    "pinned_symbol_libs": []
+  },
+  "meta": {
+    "filename": "DSP PAW add-on board.kicad_pro",
+    "version": 1
+  },
+  "net_settings": {
+    "classes": [
+      {
+        "bus_width": 12,
+        "clearance": 0.2,
+        "diff_pair_gap": 0.25,
+        "diff_pair_via_gap": 0.25,
+        "diff_pair_width": 0.2,
+        "line_style": 0,
+        "microvia_diameter": 0.3,
+        "microvia_drill": 0.1,
+        "name": "Default",
+        "pcb_color": "rgba(0, 0, 0, 0.000)",
+        "schematic_color": "rgba(0, 0, 0, 0.000)",
+        "track_width": 0.25,
+        "via_diameter": 0.8,
+        "via_drill": 0.4,
+        "wire_width": 6
+      }
+    ],
+    "meta": {
+      "version": 3
+    },
+    "net_colors": null,
+    "netclass_assignments": null,
+    "netclass_patterns": []
+  },
+  "pcbnew": {
+    "last_paths": {
+      "gencad": "",
+      "idf": "",
+      "netlist": "",
+      "specctra_dsn": "",
+      "step": "",
+      "vrml": ""
+    },
+    "page_layout_descr_file": ""
+  },
+  "schematic": {
+    "annotate_start_num": 0,
+    "drawing": {
+      "dashed_lines_dash_length_ratio": 12.0,
+      "dashed_lines_gap_length_ratio": 3.0,
+      "default_line_thickness": 6.0,
+      "default_text_size": 50.0,
+      "field_names": [],
+      "intersheets_ref_own_page": false,
+      "intersheets_ref_prefix": "",
+      "intersheets_ref_short": false,
+      "intersheets_ref_show": false,
+      "intersheets_ref_suffix": "",
+      "junction_size_choice": 3,
+      "label_size_ratio": 0.375,
+      "pin_symbol_size": 25.0,
+      "text_offset_ratio": 0.15
+    },
+    "legacy_lib_dir": "",
+    "legacy_lib_list": [],
+    "meta": {
+      "version": 1
+    },
+    "net_format_name": "",
+    "page_layout_descr_file": "",
+    "plot_directory": "",
+    "spice_current_sheet_as_root": false,
+    "spice_external_command": "spice \"%I\"",
+    "spice_model_current_sheet_as_root": true,
+    "spice_save_all_currents": false,
+    "spice_save_all_voltages": false,
+    "subpart_first_id": 65,
+    "subpart_id_separator": 0
+  },
+  "sheets": [
+    [
+      "c291319b-d76e-4fda-91cc-061acff65f9f",
+      ""
+    ],
+    [
+      "684072e7-f538-4528-bfb8-b14c31b5b1f0",
+      "Analog IO"
+    ],
+    [
+      "97fc232a-dbc7-49da-a6a0-e33e9aacbabd",
+      "Power regulation"
+    ],
+    [
+      "270d19d2-3af2-41db-ad1c-33373417ec82",
+      "Board connectors"
+    ],
+    [
+      "7798c5d5-f9b1-4b2e-811c-a15abcd34bfa",
+      "User IO"
+    ]
+  ],
+  "text_variables": {}
+}
diff --git a/hardware/DSP PAW add-on board.kicad_sch b/hardware/DSP PAW add-on board.kicad_sch
new file mode 100755 (executable)
index 0000000..d6edd69
--- /dev/null
@@ -0,0 +1,98 @@
+(kicad_sch (version 20230121) (generator eeschema)
+
+  (uuid c291319b-d76e-4fda-91cc-061acff65f9f)
+
+  (paper "A4")
+
+  (title_block
+    (title "DSP PAW add-on board")
+    (date "2023-08-08")
+    (company "bitgloo")
+    (comment 1 "Released under the CERN Open Hardware Licence Version 2 - Strongly Reciprocal")
+  )
+
+  (lib_symbols
+  )
+
+
+  (sheet (at 25.4 76.2) (size 50.8 12.7) (fields_autoplaced)
+    (stroke (width 0.1524) (type solid))
+    (fill (color 0 0 0 0.0000))
+    (uuid 270d19d2-3af2-41db-ad1c-33373417ec82)
+    (property "Sheetname" "Board connectors" (at 25.4 75.4884 0)
+      (effects (font (size 1.27 1.27)) (justify left bottom))
+    )
+    (property "Sheetfile" "board_connectors.kicad_sch" (at 25.4 89.4846 0)
+      (effects (font (size 1.27 1.27)) (justify left top))
+    )
+    (property "Field2" "" (at 25.4 76.2 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f" (page "4"))
+      )
+    )
+  )
+
+  (sheet (at 25.4 25.4) (size 50.8 12.7) (fields_autoplaced)
+    (stroke (width 0.1524) (type solid))
+    (fill (color 0 0 0 0.0000))
+    (uuid 684072e7-f538-4528-bfb8-b14c31b5b1f0)
+    (property "Sheetname" "Analog IO" (at 25.4 24.6884 0)
+      (effects (font (size 1.27 1.27)) (justify left bottom))
+    )
+    (property "Sheetfile" "analog_io.kicad_sch" (at 25.4 38.6846 0)
+      (effects (font (size 1.27 1.27)) (justify left top))
+    )
+    (property "Field2" "" (at 25.4 25.4 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f" (page "2"))
+      )
+    )
+  )
+
+  (sheet (at 25.4 101.6) (size 50.8 12.7) (fields_autoplaced)
+    (stroke (width 0.1524) (type solid))
+    (fill (color 0 0 0 0.0000))
+    (uuid 7798c5d5-f9b1-4b2e-811c-a15abcd34bfa)
+    (property "Sheetname" "User IO" (at 25.4 100.8884 0)
+      (effects (font (size 1.27 1.27)) (justify left bottom))
+    )
+    (property "Sheetfile" "user_io.kicad_sch" (at 25.4 114.8846 0)
+      (effects (font (size 1.27 1.27)) (justify left top))
+    )
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f" (page "5"))
+      )
+    )
+  )
+
+  (sheet (at 25.4 50.8) (size 50.8 12.7) (fields_autoplaced)
+    (stroke (width 0.1524) (type solid))
+    (fill (color 0 0 0 0.0000))
+    (uuid 97fc232a-dbc7-49da-a6a0-e33e9aacbabd)
+    (property "Sheetname" "Power regulation" (at 25.4 50.0884 0)
+      (effects (font (size 1.27 1.27)) (justify left bottom))
+    )
+    (property "Sheetfile" "power_regulation.kicad_sch" (at 25.4 64.0846 0)
+      (effects (font (size 1.27 1.27)) (justify left top))
+    )
+    (property "Field2" "" (at 25.4 50.8 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f" (page "3"))
+      )
+    )
+  )
+
+  (sheet_instances
+    (path "/" (page "1"))
+  )
+)
diff --git a/hardware/DSP PAW add-on board.xml b/hardware/DSP PAW add-on board.xml
new file mode 100755 (executable)
index 0000000..5a26ecd
--- /dev/null
@@ -0,0 +1,1962 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<export version="E">
+  <design>
+    <source>/home/clyne/Documents/kicad/DSP PAW add-on board/DSP PAW add-on board.kicad_sch</source>
+    <date>Sat 22 Jul 2023 04:20:30 PM EDT</date>
+    <tool>Eeschema 7.0.1</tool>
+    <sheet number="1" name="/" tstamps="/">
+      <title_block>
+        <title/>
+        <company/>
+        <rev/>
+        <date/>
+        <source>DSP PAW add-on board.kicad_sch</source>
+        <comment number="1" value=""/>
+        <comment number="2" value=""/>
+        <comment number="3" value=""/>
+        <comment number="4" value=""/>
+        <comment number="5" value=""/>
+        <comment number="6" value=""/>
+        <comment number="7" value=""/>
+        <comment number="8" value=""/>
+        <comment number="9" value=""/>
+      </title_block>
+    </sheet>
+    <sheet number="2" name="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/">
+      <title_block>
+        <title>Analog signal conditioning</title>
+        <company/>
+        <rev/>
+        <date/>
+        <source>analog_io.kicad_sch</source>
+        <comment number="1" value=""/>
+        <comment number="2" value=""/>
+        <comment number="3" value=""/>
+        <comment number="4" value=""/>
+        <comment number="5" value=""/>
+        <comment number="6" value=""/>
+        <comment number="7" value=""/>
+        <comment number="8" value=""/>
+        <comment number="9" value=""/>
+      </title_block>
+    </sheet>
+    <sheet number="3" name="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/">
+      <title_block>
+        <title>Power regulation</title>
+        <company/>
+        <rev/>
+        <date/>
+        <source>power_regulation.kicad_sch</source>
+        <comment number="1" value=""/>
+        <comment number="2" value=""/>
+        <comment number="3" value=""/>
+        <comment number="4" value=""/>
+        <comment number="5" value=""/>
+        <comment number="6" value=""/>
+        <comment number="7" value=""/>
+        <comment number="8" value=""/>
+        <comment number="9" value=""/>
+      </title_block>
+    </sheet>
+    <sheet number="4" name="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/">
+      <title_block>
+        <title>Board connectors</title>
+        <company/>
+        <rev/>
+        <date/>
+        <source>board_connectors.kicad_sch</source>
+        <comment number="1" value=""/>
+        <comment number="2" value=""/>
+        <comment number="3" value=""/>
+        <comment number="4" value=""/>
+        <comment number="5" value=""/>
+        <comment number="6" value=""/>
+        <comment number="7" value=""/>
+        <comment number="8" value=""/>
+        <comment number="9" value=""/>
+      </title_block>
+    </sheet>
+    <sheet number="5" name="/User IO/" tstamps="/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa/">
+      <title_block>
+        <title>User I/O</title>
+        <company/>
+        <rev/>
+        <date/>
+        <source>user_io.kicad_sch</source>
+        <comment number="1" value=""/>
+        <comment number="2" value=""/>
+        <comment number="3" value=""/>
+        <comment number="4" value=""/>
+        <comment number="5" value=""/>
+        <comment number="6" value=""/>
+        <comment number="7" value=""/>
+        <comment number="8" value=""/>
+        <comment number="9" value=""/>
+      </title_block>
+    </sheet>
+  </design>
+  <components>
+    <comp ref="C1">
+      <value>100p</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CC0603KPX7R9BB101</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CC0603KPX7R9BB101"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>5f13d707-5314-4204-95ff-e7023dbf1390</tstamps>
+    </comp>
+    <comp ref="C2">
+      <value>10u</value>
+      <footprint>Capacitor_SMD:C_0805_2012Metric</footprint>
+      <fields>
+        <field name="Part Number">CL21A106KOQNNNE</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CL21A106KOQNNNE"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>b7446dbf-b5bf-4307-9edc-e98dcd42c78d</tstamps>
+    </comp>
+    <comp ref="C3">
+      <value>274p</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CC0603KRX7R9BB271</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CC0603KRX7R9BB271"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>12a9fbfe-dee3-47f7-8a33-6ece0a6a5e21</tstamps>
+    </comp>
+    <comp ref="C4">
+      <value>DNP</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>88e74efe-ba6a-4380-a952-50b4a27eb246</tstamps>
+    </comp>
+    <comp ref="C5">
+      <value>105p</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CC0603KPX7R9BB101</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CC0603KPX7R9BB101"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>63c2c207-40b2-47ce-b8a2-f9c77921a2b5</tstamps>
+    </comp>
+    <comp ref="C6">
+      <value>10u</value>
+      <footprint>Capacitor_SMD:C_0805_2012Metric</footprint>
+      <fields>
+        <field name="Part Number">CL21A106KOQNNNE</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CL21A106KOQNNNE"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>b0bfe7db-09da-4802-8a23-6a20984ec23f</tstamps>
+    </comp>
+    <comp ref="C7">
+      <value>100p</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CC0603KPX7R9BB101</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CC0603KPX7R9BB101"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>623d4de9-4003-498d-bb94-d973ca58f06f</tstamps>
+    </comp>
+    <comp ref="C8">
+      <value>DNP</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>4bd163bd-4e71-45f4-aa51-12c0c8894738</tstamps>
+    </comp>
+    <comp ref="C9">
+      <value>105p</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CC0603KPX7R9BB101</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CC0603KPX7R9BB101"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>eb3ad263-4383-4973-9b0c-7fde9947d384</tstamps>
+    </comp>
+    <comp ref="C10">
+      <value>10u</value>
+      <footprint>Capacitor_SMD:C_0805_2012Metric</footprint>
+      <fields>
+        <field name="Part Number">CL21A106KOQNNNE</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CL21A106KOQNNNE"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>99c91ccc-b2c3-4d6a-a11f-c11c2ab3496d</tstamps>
+    </comp>
+    <comp ref="C11">
+      <value>100p</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CC0603KPX7R9BB101</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CC0603KPX7R9BB101"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>77594052-576e-4ede-837c-0cea441b7b38</tstamps>
+    </comp>
+    <comp ref="C12">
+      <value>DNP</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>3ffe3182-5bf2-4df2-8625-80c9083156c0</tstamps>
+    </comp>
+    <comp ref="R1">
+      <value>9.09k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RT0603DRE079K09L</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RT0603DRE079K09L"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>f0ce9f99-0d2b-47ad-955e-a115d88f2423</tstamps>
+    </comp>
+    <comp ref="R2">
+      <value>10k/0.1%</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RN73R1JTTD1002B25</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RN73R1JTTD1002B25"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>77903c71-4695-45df-ae74-1ca7380bcb51</tstamps>
+    </comp>
+    <comp ref="R3">
+      <value>9.09k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RT0603DRE079K09L</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RT0603DRE079K09L"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>22077614-3367-4e4d-93bb-9d3cc32003bf</tstamps>
+    </comp>
+    <comp ref="R4">
+      <value>6.34k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RT0603DRE076K34L</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RT0603DRE076K34L"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>68ea83d2-5dc0-4430-a711-4c94c4e55ef4</tstamps>
+    </comp>
+    <comp ref="R5">
+      <value>20k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RT0603DRE0720KL</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RT0603DRE0720KL"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>09763a12-feda-48e1-afe9-d8a81a7439cb</tstamps>
+    </comp>
+    <comp ref="R6">
+      <value>0</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RMCF0603ZT0R00</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RMCF0603ZT0R00"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>b1cf4a1c-d6f7-46a0-8fae-ab2466b84cbc</tstamps>
+    </comp>
+    <comp ref="R7">
+      <value>10k/0.1%</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RN73R1JTTD1002B25</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RN73R1JTTD1002B25"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>c5790ffe-38a3-462e-88e9-7a0e67c0f3b4</tstamps>
+    </comp>
+    <comp ref="R8">
+      <value>22.1k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RT0603DRE0722K1L</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RT0603DRE0722K1L"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>95dedd7c-c752-4712-9aea-ee0143445b87</tstamps>
+    </comp>
+    <comp ref="R9">
+      <value>6.81k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RT0603DRE076K81L</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RT0603DRE076K81L"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>2d0a2ddc-1a7a-4738-b65d-75dfc8aed08c</tstamps>
+    </comp>
+    <comp ref="R10">
+      <value>0</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RMCF0603ZT0R00</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RMCF0603ZT0R00"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>6aab9e7e-b33f-47a1-8408-1806df557085</tstamps>
+    </comp>
+    <comp ref="R11">
+      <value>10k/0.1%</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RN73R1JTTD1002B25</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RN73R1JTTD1002B25"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>a97dfe8c-7bb6-43dd-9b12-e26f02e8d594</tstamps>
+    </comp>
+    <comp ref="R12">
+      <value>2.49k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RT0603DRE072K49L</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RT0603DRE072K49L"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>34e1c64c-0f60-433f-ae2a-8d2833c48e4f</tstamps>
+    </comp>
+    <comp ref="R13">
+      <value>2.49k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RT0603DRE072K49L</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RT0603DRE072K49L"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>e9a26256-ad58-4b4e-aeb9-27b97c5cf7a4</tstamps>
+    </comp>
+    <comp ref="R14">
+      <value>22.1k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RT0603DRE0722K1L</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RT0603DRE0722K1L"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>af2574b7-1459-4b58-a3e2-46c8e623030f</tstamps>
+    </comp>
+    <comp ref="R15">
+      <value>6.81k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RT0603DRE076K81L</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RT0603DRE076K81L"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>6de7c3e2-ccb1-4bda-b6b9-7e265de95076</tstamps>
+    </comp>
+    <comp ref="R16">
+      <value>0</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RMCF0603ZT0R00</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RMCF0603ZT0R00"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>95db5be2-27f2-47d0-bd88-021be9957d2d</tstamps>
+    </comp>
+    <comp ref="R17">
+      <value>2.49k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RT0603DRE072K49L</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RT0603DRE072K49L"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>0b777e25-6648-4258-86f0-5ac639e20017</tstamps>
+    </comp>
+    <comp ref="R18">
+      <value>2.49k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RT0603DRE072K49L</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RT0603DRE072K49L"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>b79fe011-5fd4-4b0f-bb62-51fff45eb70a</tstamps>
+    </comp>
+    <comp ref="U1">
+      <value>TLV9162</value>
+      <footprint>Package_SO:SOIC-8_3.9x4.9mm_P1.27mm</footprint>
+      <datasheet>https://www.ti.com/lit/ds/symlink/tlv9162.pdf</datasheet>
+      <fields>
+        <field name="Part Number">TLV9162IDR</field>
+      </fields>
+      <libsource lib="Amplifier_Operational" part="TLV9062xD" description="Dual operational amplifier, 300 uV Offset, SOIC-8"/>
+      <property name="Part Number" value="TLV9162IDR"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Dual operational amplifier, 300 uV Offset, SOIC-8"/>
+      <property name="ki_keywords" value="dual op-amp low power"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>b7a8ee36-8943-426a-b182-3c32ea14e286 ada410a1-682d-4a65-a2fe-bdbaa9ec75fc 8c9bfe4c-5a4e-45a1-8b6a-8480aa78343e</tstamps>
+    </comp>
+    <comp ref="U2">
+      <value>OPA1678</value>
+      <footprint>Package_SO:SOIC-8_3.9x4.9mm_P1.27mm</footprint>
+      <fields>
+        <field name="Part Number">ADA4001-2ARZ</field>
+      </fields>
+      <libsource lib="Amplifier_Operational" part="TLV9062xD" description="Dual operational amplifier, 300 uV Offset, SOIC-8"/>
+      <property name="Part Number" value="ADA4001-2ARZ"/>
+      <property name="Sheetname" value="Analog IO"/>
+      <property name="Sheetfile" value="analog_io.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Dual operational amplifier, 300 uV Offset, SOIC-8"/>
+      <property name="ki_keywords" value="dual op-amp low power"/>
+      <sheetpath names="/Analog IO/" tstamps="/684072e7-f538-4528-bfb8-b14c31b5b1f0/"/>
+      <tstamps>4014b31a-906d-4e54-9260-18b76b511e9b 0d35ce54-c1cf-4a8a-ba3b-4486213d9285 0bba421f-7d3e-4e1e-9f66-4037ea1e27ad</tstamps>
+    </comp>
+    <comp ref="C13">
+      <value>10n</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CC0603KRX7R9BB103</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CC0603KRX7R9BB103"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>f2d797f3-24bb-4aa5-ba81-171b8a49029e</tstamps>
+    </comp>
+    <comp ref="C14">
+      <value>100n</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CL10B104KB8NNWC</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CL10B104KB8NNWC"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>a3e00720-9b71-4fec-8b36-e7a5cdc4ca07</tstamps>
+    </comp>
+    <comp ref="C15">
+      <value>4.7u</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CL10A475KP8NNNC</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CL10A475KP8NNNC"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>98f2b9b9-6327-4bc8-b876-121d7ad6905e</tstamps>
+    </comp>
+    <comp ref="C16">
+      <value>4.7u</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CL10A475KP8NNNC</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CL10A475KP8NNNC"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>8f35c64f-1632-4c1b-b8fa-42e58819b677</tstamps>
+    </comp>
+    <comp ref="C17">
+      <value>1u</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CL10B105KP8NNNC</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CL10B105KP8NNNC"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>219a1fe6-d5bc-49e5-ba83-808476b87bcb</tstamps>
+    </comp>
+    <comp ref="C18">
+      <value>4.7u</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CL10A475KP8NNNC</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CL10A475KP8NNNC"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>17e9eb2e-3859-4b1d-b315-52d3f5671c2b</tstamps>
+    </comp>
+    <comp ref="C19">
+      <value>2.2u</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CL10B225KP8NNNC</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CL10B225KP8NNNC"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>75cccab0-1f63-406c-b256-08197905c2d8</tstamps>
+    </comp>
+    <comp ref="C20">
+      <value>1u</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CL10B105KP8NNNC</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CL10B105KP8NNNC"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>1dcdc424-fb22-42bf-8874-89ad7acc92ec</tstamps>
+    </comp>
+    <comp ref="C21">
+      <value>4.7u</value>
+      <footprint>Capacitor_SMD:C_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">CL10A475KP8NNNC</field>
+      </fields>
+      <libsource lib="Device" part="C" description="Unpolarized capacitor"/>
+      <property name="Part Number" value="CL10A475KP8NNNC"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Unpolarized capacitor"/>
+      <property name="ki_keywords" value="cap capacitor"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>5a538b70-0a4a-472f-85a3-5d86c644f91a</tstamps>
+    </comp>
+    <comp ref="L1">
+      <value>33@100MHz</value>
+      <footprint>Inductor_SMD:L_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">BLM18PG330SN1D</field>
+      </fields>
+      <libsource lib="Device" part="L" description="Inductor"/>
+      <property name="Part Number" value="BLM18PG330SN1D"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Inductor"/>
+      <property name="ki_keywords" value="inductor choke coil reactor magnetic"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>e1dbf950-0e9a-4d00-a47a-b30cf1090893</tstamps>
+    </comp>
+    <comp ref="R19">
+      <value>174k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RMCF0603FT174K</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RMCF0603FT174K"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>4928232b-2208-4ba3-94a3-4f551edb95e6</tstamps>
+    </comp>
+    <comp ref="R20">
+      <value>0</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RMCF0603ZT0R00</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RMCF0603ZT0R00"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>8e04a020-c831-4020-a547-acddae80ca1b</tstamps>
+    </comp>
+    <comp ref="R21">
+      <value>56k</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RMCF0603FT56K0</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RMCF0603FT56K0"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>a1ea9e3c-6800-4e35-b5e2-cc7438c29b4e</tstamps>
+    </comp>
+    <comp ref="TP1">
+      <value>TestPoint</value>
+      <footprint>TestPoint:TestPoint_Pad_D1.0mm</footprint>
+      <libsource lib="Connector" part="TestPoint" description="test point"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="test point"/>
+      <property name="ki_keywords" value="test point tp"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>1362f62f-0013-4579-bfe3-657f3da88a4c</tstamps>
+    </comp>
+    <comp ref="TP2">
+      <value>TestPoint</value>
+      <footprint>TestPoint:TestPoint_Pad_D1.0mm</footprint>
+      <libsource lib="Connector" part="TestPoint" description="test point"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="test point"/>
+      <property name="ki_keywords" value="test point tp"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>6c19d262-a0fe-4278-9e4c-98da07eea5c9</tstamps>
+    </comp>
+    <comp ref="TP3">
+      <value>TestPoint</value>
+      <footprint>TestPoint:TestPoint_Pad_D1.0mm</footprint>
+      <libsource lib="Connector" part="TestPoint" description="test point"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="test point"/>
+      <property name="ki_keywords" value="test point tp"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>9563d063-75b0-44ce-b3ce-3dcaef0a8a7b</tstamps>
+    </comp>
+    <comp ref="TP4">
+      <value>TestPoint</value>
+      <footprint>TestPoint:TestPoint_Pad_D1.0mm</footprint>
+      <libsource lib="Connector" part="TestPoint" description="test point"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="test point"/>
+      <property name="ki_keywords" value="test point tp"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>1e9bd7fc-0998-4613-aa5b-48cd00451144</tstamps>
+    </comp>
+    <comp ref="U3">
+      <value>MAX6106</value>
+      <footprint>Package_TO_SOT_SMD:SOT-23</footprint>
+      <datasheet>http://datasheets.maximintegrated.com/en/ds/MAX6100-MAX6107.pdf</datasheet>
+      <fields>
+        <field name="Part Number">MAX6106EUR+T</field>
+      </fields>
+      <libsource lib="Reference_Voltage" part="MAX6106" description="Low-dropout high current voltage reference, 2.048V, Â±0.4% accuracy, SOT-23"/>
+      <property name="Part Number" value="MAX6106EUR+T"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Low-dropout high current voltage reference, 2.048V, Â±0.4% accuracy, SOT-23"/>
+      <property name="ki_keywords" value="voltage reference ldo"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>2721a4bf-a486-4e4a-95f3-7833d023f203</tstamps>
+    </comp>
+    <comp ref="U4">
+      <value>LM27761</value>
+      <footprint>Package_SON:WSON-8-1EP_2x2mm_P0.5mm_EP0.9x1.6mm</footprint>
+      <datasheet>http://www.ti.com/lit/ds/symlink/lm27761.pdf</datasheet>
+      <fields>
+        <field name="Part Number">LM27761DSGR</field>
+      </fields>
+      <libsource lib="Regulator_SwitchedCapacitor" part="LM27761" description="low-noise regulated switched-capacitor voltage inverter with 2.7V-5.5V input to -1.5 to -5V Output Voltage, WSON-8"/>
+      <property name="Part Number" value="LM27761DSGR"/>
+      <property name="Sheetname" value="Power regulation"/>
+      <property name="Sheetfile" value="power_regulation.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="low-noise regulated switched-capacitor voltage inverter with 2.7V-5.5V input to -1.5 to -5V Output Voltage, WSON-8"/>
+      <property name="ki_keywords" value="low-noise switched capacitor voltage converter invert"/>
+      <sheetpath names="/Power regulation/" tstamps="/97fc232a-dbc7-49da-a6a0-e33e9aacbabd/"/>
+      <tstamps>ab5ba760-1b9a-48fc-8a36-c943d7f45f9f</tstamps>
+    </comp>
+    <comp ref="D1">
+      <value>ESD5Zxx</value>
+      <footprint>Diode_SMD:D_SOD-523</footprint>
+      <datasheet>https://www.onsemi.com/pdf/datasheet/esd5z2.5t1-d.pdf</datasheet>
+      <fields>
+        <field name="Part Number">ESD5Z2.5T1G</field>
+      </fields>
+      <libsource lib="Diode" part="ESD5Zxx" description="ESD Protection Diode, SOD-523"/>
+      <property name="Part Number" value="ESD5Z2.5T1G"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="ESD Protection Diode, SOD-523"/>
+      <property name="ki_keywords" value="esd tvs unidirectional diode"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>50abfadf-fb6c-48a1-9415-dba8320ad18e</tstamps>
+    </comp>
+    <comp ref="D2">
+      <value>ESD5Zxx</value>
+      <footprint>Diode_SMD:D_SOD-523</footprint>
+      <datasheet>https://www.onsemi.com/pdf/datasheet/esd5z2.5t1-d.pdf</datasheet>
+      <fields>
+        <field name="Part Number">ESD5Z2.5T1G</field>
+      </fields>
+      <libsource lib="Diode" part="ESD5Zxx" description="ESD Protection Diode, SOD-523"/>
+      <property name="Part Number" value="ESD5Z2.5T1G"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="ESD Protection Diode, SOD-523"/>
+      <property name="ki_keywords" value="esd tvs unidirectional diode"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>5dffc28c-19cd-4431-a7ba-b33bafb430ee</tstamps>
+    </comp>
+    <comp ref="D3">
+      <value>ESD5Zxx</value>
+      <footprint>Diode_SMD:D_SOD-523</footprint>
+      <datasheet>https://www.onsemi.com/pdf/datasheet/esd5z2.5t1-d.pdf</datasheet>
+      <fields>
+        <field name="Part Number">ESD5Z2.5T1G</field>
+      </fields>
+      <libsource lib="Diode" part="ESD5Zxx" description="ESD Protection Diode, SOD-523"/>
+      <property name="Part Number" value="ESD5Z2.5T1G"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="ESD Protection Diode, SOD-523"/>
+      <property name="ki_keywords" value="esd tvs unidirectional diode"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>2fddc873-d43d-46d7-9c1a-d0acddc1a966</tstamps>
+    </comp>
+    <comp ref="D4">
+      <value>DF2B7AFS,L3M</value>
+      <footprint>Diode_SMD:D_SOD-923</footprint>
+      <datasheet>https://www.onsemi.com/pub/Collateral/ESD9B-D.PDF</datasheet>
+      <fields>
+        <field name="Part Number">DF2B7AFS,L3M</field>
+      </fields>
+      <libsource lib="Diode" part="ESD9B5.0ST5G" description="ESD protection diode, 5.0Vrwm, SOD-923"/>
+      <property name="Part Number" value="DF2B7AFS,L3M"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="ESD protection diode, 5.0Vrwm, SOD-923"/>
+      <property name="ki_keywords" value="diode TVS ESD"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>0dee484c-3850-4bfd-ac52-a058e497e313</tstamps>
+    </comp>
+    <comp ref="D5">
+      <value>DF2B7AFS,L3M</value>
+      <footprint>Diode_SMD:D_SOD-923</footprint>
+      <datasheet>https://www.onsemi.com/pub/Collateral/ESD9B-D.PDF</datasheet>
+      <fields>
+        <field name="Part Number">DF2B7AFS,L3M</field>
+      </fields>
+      <libsource lib="Diode" part="ESD9B5.0ST5G" description="ESD protection diode, 5.0Vrwm, SOD-923"/>
+      <property name="Part Number" value="DF2B7AFS,L3M"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="ESD protection diode, 5.0Vrwm, SOD-923"/>
+      <property name="ki_keywords" value="diode TVS ESD"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>2f36a1b8-dd88-4fdd-80cc-b6a7d1336b74</tstamps>
+    </comp>
+    <comp ref="D6">
+      <value>DF2B7AFS,L3M</value>
+      <footprint>Diode_SMD:D_SOD-923</footprint>
+      <datasheet>https://www.onsemi.com/pub/Collateral/ESD9B-D.PDF</datasheet>
+      <fields>
+        <field name="Part Number">DF2B7AFS,L3M</field>
+      </fields>
+      <libsource lib="Diode" part="ESD9B5.0ST5G" description="ESD protection diode, 5.0Vrwm, SOD-923"/>
+      <property name="Part Number" value="DF2B7AFS,L3M"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="ESD protection diode, 5.0Vrwm, SOD-923"/>
+      <property name="ki_keywords" value="diode TVS ESD"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>d707892a-1c20-45ac-bb77-11891c2394e4</tstamps>
+    </comp>
+    <comp ref="J1">
+      <value>AudioJack2_Ground</value>
+      <footprint>Connector_Audio:Jack_3.5mm_CUI_SJ-3523-SMT_Horizontal</footprint>
+      <fields>
+        <field name="Part Number">SJ-3523-SMT-TR</field>
+      </fields>
+      <libsource lib="Connector_Audio" part="AudioJack2_Ground" description="Audio Jack, 2 Poles (Mono / TS), Grounded Sleeve"/>
+      <property name="Part Number" value="SJ-3523-SMT-TR"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Audio Jack, 2 Poles (Mono / TS), Grounded Sleeve"/>
+      <property name="ki_keywords" value="audio jack receptacle mono phone headphone TS connector"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>7694e2a6-3372-4fab-895b-9744296f2da7</tstamps>
+    </comp>
+    <comp ref="J2">
+      <value>Conn_02x10_Odd_Even</value>
+      <footprint>Connector_PinHeader_2.54mm:PinHeader_2x10_P2.54mm_Vertical</footprint>
+      <fields>
+        <field name="Part Number">PPPC102LFBN-RC</field>
+      </fields>
+      <libsource lib="Connector_Generic" part="Conn_02x10_Odd_Even" description="Generic connector, double row, 02x10, odd/even pin numbering scheme (row 1 odd numbers, row 2 even numbers), script generated (kicad-library-utils/schlib/autogen/connector/)"/>
+      <property name="Part Number" value="PPPC102LFBN-RC"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Generic connector, double row, 02x10, odd/even pin numbering scheme (row 1 odd numbers, row 2 even numbers), script generated (kicad-library-utils/schlib/autogen/connector/)"/>
+      <property name="ki_keywords" value="connector"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>8475bff6-cb36-4a34-84ec-774f48f03d90</tstamps>
+    </comp>
+    <comp ref="J3">
+      <value>Conn_02x10_Odd_Even</value>
+      <footprint>Connector_PinHeader_2.54mm:PinHeader_2x10_P2.54mm_Vertical</footprint>
+      <fields>
+        <field name="Part Number">PPPC102LFBN-RC</field>
+      </fields>
+      <libsource lib="Connector_Generic" part="Conn_02x10_Odd_Even" description="Generic connector, double row, 02x10, odd/even pin numbering scheme (row 1 odd numbers, row 2 even numbers), script generated (kicad-library-utils/schlib/autogen/connector/)"/>
+      <property name="Part Number" value="PPPC102LFBN-RC"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Generic connector, double row, 02x10, odd/even pin numbering scheme (row 1 odd numbers, row 2 even numbers), script generated (kicad-library-utils/schlib/autogen/connector/)"/>
+      <property name="ki_keywords" value="connector"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>ec63f71a-7153-47f9-b86f-860fe7e45e10</tstamps>
+    </comp>
+    <comp ref="J4">
+      <value>Conn_02x03_Odd_Even</value>
+      <footprint>Connector_PinHeader_2.54mm:PinHeader_2x03_P2.54mm_Horizontal</footprint>
+      <fields>
+        <field name="Part Number">PH2RA-06-UA</field>
+      </fields>
+      <libsource lib="Connector_Generic" part="Conn_02x03_Odd_Even" description="Generic connector, double row, 02x03, odd/even pin numbering scheme (row 1 odd numbers, row 2 even numbers), script generated (kicad-library-utils/schlib/autogen/connector/)"/>
+      <property name="Part Number" value="PH2RA-06-UA"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Generic connector, double row, 02x03, odd/even pin numbering scheme (row 1 odd numbers, row 2 even numbers), script generated (kicad-library-utils/schlib/autogen/connector/)"/>
+      <property name="ki_keywords" value="connector"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>de87bdea-3e5b-416f-bfb4-ea751dfca070</tstamps>
+    </comp>
+    <comp ref="J5">
+      <value>AudioJack2_Ground</value>
+      <footprint>Connector_Audio:Jack_3.5mm_CUI_SJ-3523-SMT_Horizontal</footprint>
+      <fields>
+        <field name="Part Number">SJ-3523-SMT-TR</field>
+      </fields>
+      <libsource lib="Connector_Audio" part="AudioJack2_Ground" description="Audio Jack, 2 Poles (Mono / TS), Grounded Sleeve"/>
+      <property name="Part Number" value="SJ-3523-SMT-TR"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Audio Jack, 2 Poles (Mono / TS), Grounded Sleeve"/>
+      <property name="ki_keywords" value="audio jack receptacle mono phone headphone TS connector"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>2f551abf-6373-4e81-b42b-ccb46e78a8d3</tstamps>
+    </comp>
+    <comp ref="J6">
+      <value>Conn_02x10_Odd_Even</value>
+      <footprint>Connector_PinHeader_2.54mm:PinHeader_2x10_P2.54mm_Vertical</footprint>
+      <fields>
+        <field name="Part Number">PPPC102LFBN-RC</field>
+      </fields>
+      <libsource lib="Connector_Generic" part="Conn_02x10_Odd_Even" description="Generic connector, double row, 02x10, odd/even pin numbering scheme (row 1 odd numbers, row 2 even numbers), script generated (kicad-library-utils/schlib/autogen/connector/)"/>
+      <property name="Part Number" value="PPPC102LFBN-RC"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Generic connector, double row, 02x10, odd/even pin numbering scheme (row 1 odd numbers, row 2 even numbers), script generated (kicad-library-utils/schlib/autogen/connector/)"/>
+      <property name="ki_keywords" value="connector"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>3c409af7-d0f8-44c1-a8e3-ea5a071f1b52</tstamps>
+    </comp>
+    <comp ref="J7">
+      <value>AudioJack2_Ground</value>
+      <footprint>Connector_Audio:Jack_3.5mm_CUI_SJ-3523-SMT_Horizontal</footprint>
+      <fields>
+        <field name="Part Number">SJ-3523-SMT-TR</field>
+      </fields>
+      <libsource lib="Connector_Audio" part="AudioJack2_Ground" description="Audio Jack, 2 Poles (Mono / TS), Grounded Sleeve"/>
+      <property name="Part Number" value="SJ-3523-SMT-TR"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Audio Jack, 2 Poles (Mono / TS), Grounded Sleeve"/>
+      <property name="ki_keywords" value="audio jack receptacle mono phone headphone TS connector"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>8ca59ac3-e703-4c21-a6a5-9119fbd2e6c2</tstamps>
+    </comp>
+    <comp ref="J8">
+      <value>USB_B_Micro</value>
+      <footprint>Connector_USB:USB_Micro-B_Amphenol_10104110_Horizontal</footprint>
+      <fields>
+        <field name="Part Number">10104110-0001LF</field>
+      </fields>
+      <libsource lib="Connector" part="USB_B_Micro" description="USB Micro Type B connector"/>
+      <property name="Part Number" value="10104110-0001LF"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="USB Micro Type B connector"/>
+      <property name="ki_keywords" value="connector USB micro"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>78555264-1178-42da-8282-385afceb94d8</tstamps>
+    </comp>
+    <comp ref="R22">
+      <value>0</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RMCF0603ZT0R00</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RMCF0603ZT0R00"/>
+      <property name="Sheetname" value="Board connectors"/>
+      <property name="Sheetfile" value="board_connectors.kicad_sch"/>
+      <property name="Field2" value=""/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/Board connectors/" tstamps="/270d19d2-3af2-41db-ad1c-33373417ec82/"/>
+      <tstamps>9f0f3415-176c-4496-afca-6588b7686c1e</tstamps>
+    </comp>
+    <comp ref="D7">
+      <value>LED_RAGB</value>
+      <footprint>LED_SMD:LED_Kingbright_AAA3528ESGCT</footprint>
+      <fields>
+        <field name="Part Number">AAA3528SEEZGKQBKS</field>
+      </fields>
+      <libsource lib="Device" part="LED_RAGB" description="RGB LED, red/anode/green/blue"/>
+      <property name="Part Number" value="AAA3528SEEZGKQBKS"/>
+      <property name="Sheetname" value="User IO"/>
+      <property name="Sheetfile" value="user_io.kicad_sch"/>
+      <property name="ki_description" value="RGB LED, red/anode/green/blue"/>
+      <property name="ki_keywords" value="LED RGB diode"/>
+      <sheetpath names="/User IO/" tstamps="/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa/"/>
+      <tstamps>9ec9653f-c3a4-41f7-9b1c-80cc24251ffe</tstamps>
+    </comp>
+    <comp ref="R23">
+      <value>560</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RC0603JR-07560RL</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RC0603JR-07560RL"/>
+      <property name="Sheetname" value="User IO"/>
+      <property name="Sheetfile" value="user_io.kicad_sch"/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/User IO/" tstamps="/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa/"/>
+      <tstamps>63c4f82c-3aee-47eb-aaf9-4ff013fc8a40</tstamps>
+    </comp>
+    <comp ref="R24">
+      <value>240</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RC0603JR-07240RL</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RC0603JR-07240RL"/>
+      <property name="Sheetname" value="User IO"/>
+      <property name="Sheetfile" value="user_io.kicad_sch"/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/User IO/" tstamps="/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa/"/>
+      <tstamps>29f7e662-f721-4c3d-848d-61fc5171ba42</tstamps>
+    </comp>
+    <comp ref="R25">
+      <value>240</value>
+      <footprint>Resistor_SMD:R_0603_1608Metric</footprint>
+      <fields>
+        <field name="Part Number">RC0603JR-07240RL</field>
+      </fields>
+      <libsource lib="Device" part="R_US" description="Resistor, US symbol"/>
+      <property name="Part Number" value="RC0603JR-07240RL"/>
+      <property name="Sheetname" value="User IO"/>
+      <property name="Sheetfile" value="user_io.kicad_sch"/>
+      <property name="ki_description" value="Resistor, US symbol"/>
+      <property name="ki_keywords" value="R res resistor"/>
+      <sheetpath names="/User IO/" tstamps="/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa/"/>
+      <tstamps>d04657bd-c6dc-4346-b2bd-fdac6c2c8be6</tstamps>
+    </comp>
+    <comp ref="RV1">
+      <value>10K</value>
+      <footprint>Potentiometer_THT:Potentiometer_Bourns_3386P_Vertical</footprint>
+      <fields>
+        <field name="Part Number">3386P-1-103TLF</field>
+      </fields>
+      <libsource lib="Device" part="R_Potentiometer_US" description="Potentiometer, US symbol"/>
+      <property name="Part Number" value="3386P-1-103TLF"/>
+      <property name="Sheetname" value="User IO"/>
+      <property name="Sheetfile" value="user_io.kicad_sch"/>
+      <property name="ki_description" value="Potentiometer, US symbol"/>
+      <property name="ki_keywords" value="resistor variable"/>
+      <sheetpath names="/User IO/" tstamps="/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa/"/>
+      <tstamps>f4a975b9-7021-410d-9761-44f0c502022e</tstamps>
+    </comp>
+    <comp ref="RV2">
+      <value>10K</value>
+      <footprint>Potentiometer_THT:Potentiometer_Bourns_3386P_Vertical</footprint>
+      <fields>
+        <field name="Part Number">3386P-1-103TLF</field>
+      </fields>
+      <libsource lib="Device" part="R_Potentiometer_US" description="Potentiometer, US symbol"/>
+      <property name="Part Number" value="3386P-1-103TLF"/>
+      <property name="Sheetname" value="User IO"/>
+      <property name="Sheetfile" value="user_io.kicad_sch"/>
+      <property name="ki_description" value="Potentiometer, US symbol"/>
+      <property name="ki_keywords" value="resistor variable"/>
+      <sheetpath names="/User IO/" tstamps="/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa/"/>
+      <tstamps>04ebdf1c-ac9d-479f-992a-1ea0aeec5c23</tstamps>
+    </comp>
+  </components>
+  <libparts>
+    <libpart lib="Amplifier_Operational" part="TLV9062xD">
+      <description>Dual operational amplifier, 300 uV Offset, SOIC-8</description>
+      <docs>https://www.ti.com/lit/ds/symlink/tlv9062.pdf</docs>
+      <footprints>
+        <fp>SOIC*3.9x4.9mm*P1.27mm*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">U</field>
+        <field name="Value">TLV9062xD</field>
+        <field name="Footprint">Package_SO:SOIC-8_3.9x4.9mm_P1.27mm</field>
+        <field name="Datasheet">https://www.ti.com/lit/ds/symlink/tlv9062.pdf</field>
+      </fields>
+      <pins>
+        <pin num="1" name="" type="output"/>
+        <pin num="2" name="-" type="input"/>
+        <pin num="3" name="+" type="input"/>
+        <pin num="4" name="V-" type="power_in"/>
+        <pin num="5" name="+" type="input"/>
+        <pin num="6" name="-" type="input"/>
+        <pin num="7" name="" type="output"/>
+        <pin num="8" name="V+" type="power_in"/>
+      </pins>
+    </libpart>
+    <libpart lib="Connector" part="TestPoint">
+      <description>test point</description>
+      <docs>~</docs>
+      <footprints>
+        <fp>Pin*</fp>
+        <fp>Test*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">TP</field>
+        <field name="Value">TestPoint</field>
+        <field name="Datasheet">~</field>
+      </fields>
+      <pins>
+        <pin num="1" name="1" type="passive"/>
+      </pins>
+    </libpart>
+    <libpart lib="Connector" part="USB_B_Micro">
+      <description>USB Micro Type B connector</description>
+      <docs>~</docs>
+      <footprints>
+        <fp>USB*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">J</field>
+        <field name="Value">USB_B_Micro</field>
+        <field name="Datasheet">~</field>
+      </fields>
+      <pins>
+        <pin num="1" name="VBUS" type="power_out"/>
+        <pin num="2" name="D-" type="bidirectional"/>
+        <pin num="3" name="D+" type="bidirectional"/>
+        <pin num="4" name="ID" type="passive"/>
+        <pin num="5" name="GND" type="power_out"/>
+        <pin num="6" name="Shield" type="passive"/>
+      </pins>
+    </libpart>
+    <libpart lib="Connector_Audio" part="AudioJack2_Ground">
+      <description>Audio Jack, 2 Poles (Mono / TS), Grounded Sleeve</description>
+      <docs>~</docs>
+      <footprints>
+        <fp>Jack*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">J</field>
+        <field name="Value">AudioJack2_Ground</field>
+        <field name="Datasheet">~</field>
+      </fields>
+      <pins>
+        <pin num="G" name="" type="passive"/>
+        <pin num="S" name="" type="passive"/>
+        <pin num="T" name="" type="passive"/>
+      </pins>
+    </libpart>
+    <libpart lib="Connector_Generic" part="Conn_02x03_Odd_Even">
+      <description>Generic connector, double row, 02x03, odd/even pin numbering scheme (row 1 odd numbers, row 2 even numbers), script generated (kicad-library-utils/schlib/autogen/connector/)</description>
+      <docs>~</docs>
+      <footprints>
+        <fp>Connector*:*_2x??_*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">J</field>
+        <field name="Value">Conn_02x03_Odd_Even</field>
+        <field name="Datasheet">~</field>
+      </fields>
+      <pins>
+        <pin num="1" name="Pin_1" type="passive"/>
+        <pin num="2" name="Pin_2" type="passive"/>
+        <pin num="3" name="Pin_3" type="passive"/>
+        <pin num="4" name="Pin_4" type="passive"/>
+        <pin num="5" name="Pin_5" type="passive"/>
+        <pin num="6" name="Pin_6" type="passive"/>
+      </pins>
+    </libpart>
+    <libpart lib="Connector_Generic" part="Conn_02x10_Odd_Even">
+      <description>Generic connector, double row, 02x10, odd/even pin numbering scheme (row 1 odd numbers, row 2 even numbers), script generated (kicad-library-utils/schlib/autogen/connector/)</description>
+      <docs>~</docs>
+      <footprints>
+        <fp>Connector*:*_2x??_*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">J</field>
+        <field name="Value">Conn_02x10_Odd_Even</field>
+        <field name="Datasheet">~</field>
+      </fields>
+      <pins>
+        <pin num="1" name="Pin_1" type="passive"/>
+        <pin num="2" name="Pin_2" type="passive"/>
+        <pin num="3" name="Pin_3" type="passive"/>
+        <pin num="4" name="Pin_4" type="passive"/>
+        <pin num="5" name="Pin_5" type="passive"/>
+        <pin num="6" name="Pin_6" type="passive"/>
+        <pin num="7" name="Pin_7" type="passive"/>
+        <pin num="8" name="Pin_8" type="passive"/>
+        <pin num="9" name="Pin_9" type="passive"/>
+        <pin num="10" name="Pin_10" type="passive"/>
+        <pin num="11" name="Pin_11" type="passive"/>
+        <pin num="12" name="Pin_12" type="passive"/>
+        <pin num="13" name="Pin_13" type="passive"/>
+        <pin num="14" name="Pin_14" type="passive"/>
+        <pin num="15" name="Pin_15" type="passive"/>
+        <pin num="16" name="Pin_16" type="passive"/>
+        <pin num="17" name="Pin_17" type="passive"/>
+        <pin num="18" name="Pin_18" type="passive"/>
+        <pin num="19" name="Pin_19" type="passive"/>
+        <pin num="20" name="Pin_20" type="passive"/>
+      </pins>
+    </libpart>
+    <libpart lib="Device" part="C">
+      <description>Unpolarized capacitor</description>
+      <docs>~</docs>
+      <footprints>
+        <fp>C_*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">C</field>
+        <field name="Value">C</field>
+        <field name="Datasheet">~</field>
+      </fields>
+      <pins>
+        <pin num="1" name="" type="passive"/>
+        <pin num="2" name="" type="passive"/>
+      </pins>
+    </libpart>
+    <libpart lib="Device" part="L">
+      <description>Inductor</description>
+      <docs>~</docs>
+      <footprints>
+        <fp>Choke_*</fp>
+        <fp>*Coil*</fp>
+        <fp>Inductor_*</fp>
+        <fp>L_*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">L</field>
+        <field name="Value">L</field>
+        <field name="Datasheet">~</field>
+      </fields>
+      <pins>
+        <pin num="1" name="1" type="passive"/>
+        <pin num="2" name="2" type="passive"/>
+      </pins>
+    </libpart>
+    <libpart lib="Device" part="LED_RAGB">
+      <description>RGB LED, red/anode/green/blue</description>
+      <docs>~</docs>
+      <footprints>
+        <fp>LED*</fp>
+        <fp>LED_SMD:*</fp>
+        <fp>LED_THT:*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">D</field>
+        <field name="Value">LED_RAGB</field>
+        <field name="Datasheet">~</field>
+      </fields>
+      <pins>
+        <pin num="1" name="RK" type="passive"/>
+        <pin num="2" name="A" type="passive"/>
+        <pin num="3" name="GK" type="passive"/>
+        <pin num="4" name="BK" type="passive"/>
+      </pins>
+    </libpart>
+    <libpart lib="Device" part="R_Potentiometer_US">
+      <description>Potentiometer, US symbol</description>
+      <docs>~</docs>
+      <footprints>
+        <fp>Potentiometer*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">RV</field>
+        <field name="Value">R_Potentiometer_US</field>
+        <field name="Datasheet">~</field>
+      </fields>
+      <pins>
+        <pin num="1" name="1" type="passive"/>
+        <pin num="2" name="2" type="passive"/>
+        <pin num="3" name="3" type="passive"/>
+      </pins>
+    </libpart>
+    <libpart lib="Device" part="R_US">
+      <description>Resistor, US symbol</description>
+      <docs>~</docs>
+      <footprints>
+        <fp>R_*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">R</field>
+        <field name="Value">R_US</field>
+        <field name="Datasheet">~</field>
+      </fields>
+      <pins>
+        <pin num="1" name="" type="passive"/>
+        <pin num="2" name="" type="passive"/>
+      </pins>
+    </libpart>
+    <libpart lib="Diode" part="ESD5Zxx">
+      <description>ESD Protection Diode, SOD-523</description>
+      <docs>https://www.onsemi.com/pdf/datasheet/esd5z2.5t1-d.pdf</docs>
+      <footprints>
+        <fp>D?SOD?523*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">D</field>
+        <field name="Value">ESD5Zxx</field>
+        <field name="Footprint">Diode_SMD:D_SOD-523</field>
+        <field name="Datasheet">https://www.onsemi.com/pdf/datasheet/esd5z2.5t1-d.pdf</field>
+      </fields>
+      <pins>
+        <pin num="1" name="K" type="passive"/>
+        <pin num="2" name="A" type="passive"/>
+      </pins>
+    </libpart>
+    <libpart lib="Diode" part="ESD9B5.0ST5G">
+      <description>ESD protection diode, 5.0Vrwm, SOD-923</description>
+      <docs>https://www.onsemi.com/pub/Collateral/ESD9B-D.PDF</docs>
+      <footprints>
+        <fp>D*SOD?923*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">D</field>
+        <field name="Value">ESD9B5.0ST5G</field>
+        <field name="Footprint">Diode_SMD:D_SOD-923</field>
+        <field name="Datasheet">https://www.onsemi.com/pub/Collateral/ESD9B-D.PDF</field>
+      </fields>
+      <pins>
+        <pin num="1" name="A1" type="passive"/>
+        <pin num="2" name="A2" type="passive"/>
+      </pins>
+    </libpart>
+    <libpart lib="Reference_Voltage" part="MAX6106">
+      <description>Low-dropout high current voltage reference, 2.048V, Â±0.4% accuracy, SOT-23</description>
+      <docs>http://datasheets.maximintegrated.com/en/ds/MAX6100-MAX6107.pdf</docs>
+      <footprints>
+        <fp>SOT?23*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">U</field>
+        <field name="Value">MAX6106</field>
+        <field name="Footprint">Package_TO_SOT_SMD:SOT-23</field>
+        <field name="Datasheet">http://datasheets.maximintegrated.com/en/ds/MAX6100-MAX6107.pdf</field>
+      </fields>
+      <pins>
+        <pin num="1" name="IN" type="power_in"/>
+        <pin num="2" name="OUT" type="power_out"/>
+        <pin num="3" name="GND" type="power_in"/>
+      </pins>
+    </libpart>
+    <libpart lib="Regulator_SwitchedCapacitor" part="LM27761">
+      <description>low-noise regulated switched-capacitor voltage inverter with 2.7V-5.5V input to -1.5 to -5V Output Voltage, WSON-8</description>
+      <docs>http://www.ti.com/lit/ds/symlink/lm27761.pdf</docs>
+      <footprints>
+        <fp>WSON*1EP?2x2mm*P0.5mm*</fp>
+      </footprints>
+      <fields>
+        <field name="Reference">U</field>
+        <field name="Value">LM27761</field>
+        <field name="Footprint">Package_SON:WSON-8-1EP_2x2mm_P0.5mm_EP0.9x1.6mm</field>
+        <field name="Datasheet">http://www.ti.com/lit/ds/symlink/lm27761.pdf</field>
+      </fields>
+      <pins>
+        <pin num="1" name="VIN" type="power_in"/>
+        <pin num="2" name="GND" type="power_in"/>
+        <pin num="3" name="CPOUT" type="power_out"/>
+        <pin num="4" name="VOUT" type="power_out"/>
+        <pin num="5" name="VFB" type="input"/>
+        <pin num="6" name="EN" type="input"/>
+        <pin num="7" name="C-" type="passive"/>
+        <pin num="8" name="C+" type="passive"/>
+        <pin num="9" name="PAD" type="power_in"/>
+      </pins>
+    </libpart>
+  </libparts>
+  <libraries>
+    <library logical="Amplifier_Operational">
+      <uri>/usr/share/kicad/symbols//Amplifier_Operational.kicad_sym</uri>
+    </library>
+    <library logical="Connector">
+      <uri>/usr/share/kicad/symbols//Connector.kicad_sym</uri>
+    </library>
+    <library logical="Connector_Audio">
+      <uri>/usr/share/kicad/symbols//Connector_Audio.kicad_sym</uri>
+    </library>
+    <library logical="Connector_Generic">
+      <uri>/usr/share/kicad/symbols//Connector_Generic.kicad_sym</uri>
+    </library>
+    <library logical="Device">
+      <uri>/usr/share/kicad/symbols//Device.kicad_sym</uri>
+    </library>
+    <library logical="Diode">
+      <uri>/usr/share/kicad/symbols//Diode.kicad_sym</uri>
+    </library>
+    <library logical="Reference_Voltage">
+      <uri>/usr/share/kicad/symbols//Reference_Voltage.kicad_sym</uri>
+    </library>
+    <library logical="Regulator_SwitchedCapacitor">
+      <uri>/usr/share/kicad/symbols//Regulator_SwitchedCapacitor.kicad_sym</uri>
+    </library>
+  </libraries>
+  <nets>
+    <net code="1" name="+5V">
+      <node ref="C14" pin="1" pintype="passive"/>
+      <node ref="C15" pin="1" pintype="passive"/>
+      <node ref="C16" pin="1" pintype="passive"/>
+      <node ref="C18" pin="1" pintype="passive"/>
+      <node ref="L1" pin="2" pinfunction="2" pintype="passive"/>
+      <node ref="R20" pin="1" pintype="passive"/>
+      <node ref="TP1" pin="1" pinfunction="1" pintype="passive"/>
+      <node ref="U1" pin="8" pinfunction="V+" pintype="power_in"/>
+      <node ref="U2" pin="8" pinfunction="V+" pintype="power_in"/>
+      <node ref="U3" pin="1" pinfunction="IN" pintype="power_in"/>
+      <node ref="U4" pin="1" pinfunction="VIN" pintype="power_in"/>
+    </net>
+    <net code="2" name="-5V">
+      <node ref="C19" pin="1" pintype="passive"/>
+      <node ref="R19" pin="1" pintype="passive"/>
+      <node ref="TP2" pin="1" pinfunction="1" pintype="passive"/>
+      <node ref="U1" pin="4" pinfunction="V-" pintype="power_in"/>
+      <node ref="U2" pin="4" pinfunction="V-" pintype="power_in"/>
+      <node ref="U4" pin="4" pinfunction="VOUT" pintype="power_out"/>
+    </net>
+    <net code="3" name="GENERATOR">
+      <node ref="C12" pin="2" pintype="passive"/>
+      <node ref="D1" pin="1" pinfunction="K" pintype="passive"/>
+      <node ref="J1" pin="T" pintype="passive"/>
+      <node ref="J4" pin="1" pinfunction="Pin_1" pintype="passive"/>
+      <node ref="R16" pin="1" pintype="passive"/>
+    </net>
+    <net code="4" name="GENERATOR_MCU">
+      <node ref="C10" pin="2" pintype="passive"/>
+      <node ref="J2" pin="12" pinfunction="Pin_12" pintype="passive"/>
+    </net>
+    <net code="5" name="GND">
+      <node ref="C11" pin="2" pintype="passive"/>
+      <node ref="C12" pin="1" pintype="passive"/>
+      <node ref="C13" pin="2" pintype="passive"/>
+      <node ref="C14" pin="2" pintype="passive"/>
+      <node ref="C15" pin="2" pintype="passive"/>
+      <node ref="C16" pin="2" pintype="passive"/>
+      <node ref="C17" pin="2" pintype="passive"/>
+      <node ref="C18" pin="2" pintype="passive"/>
+      <node ref="C19" pin="2" pintype="passive"/>
+      <node ref="C21" pin="2" pintype="passive"/>
+      <node ref="C3" pin="2" pintype="passive"/>
+      <node ref="C4" pin="2" pintype="passive"/>
+      <node ref="C7" pin="2" pintype="passive"/>
+      <node ref="C8" pin="1" pintype="passive"/>
+      <node ref="D1" pin="2" pinfunction="A" pintype="passive"/>
+      <node ref="D2" pin="2" pinfunction="A" pintype="passive"/>
+      <node ref="D3" pin="2" pinfunction="A" pintype="passive"/>
+      <node ref="D4" pin="1" pinfunction="A1" pintype="passive"/>
+      <node ref="D5" pin="1" pinfunction="A1" pintype="passive"/>
+      <node ref="D6" pin="1" pinfunction="A1" pintype="passive"/>
+      <node ref="J1" pin="G" pintype="passive"/>
+      <node ref="J1" pin="S" pintype="passive"/>
+      <node ref="J2" pin="10" pinfunction="Pin_10" pintype="passive"/>
+      <node ref="J3" pin="7" pinfunction="Pin_7" pintype="passive"/>
+      <node ref="J4" pin="2" pinfunction="Pin_2" pintype="passive"/>
+      <node ref="J4" pin="4" pinfunction="Pin_4" pintype="passive"/>
+      <node ref="J4" pin="6" pinfunction="Pin_6" pintype="passive"/>
+      <node ref="J5" pin="G" pintype="passive"/>
+      <node ref="J5" pin="S" pintype="passive"/>
+      <node ref="J7" pin="G" pintype="passive"/>
+      <node ref="J7" pin="S" pintype="passive"/>
+      <node ref="J8" pin="5" pinfunction="GND" pintype="power_out"/>
+      <node ref="R11" pin="1" pintype="passive"/>
+      <node ref="R13" pin="2" pintype="passive"/>
+      <node ref="R18" pin="2" pintype="passive"/>
+      <node ref="R21" pin="2" pintype="passive"/>
+      <node ref="R22" pin="2" pintype="passive"/>
+      <node ref="RV1" pin="3" pinfunction="3" pintype="passive"/>
+      <node ref="RV2" pin="3" pinfunction="3" pintype="passive"/>
+      <node ref="TP4" pin="1" pinfunction="1" pintype="passive"/>
+      <node ref="U1" pin="3" pinfunction="+" pintype="input"/>
+      <node ref="U3" pin="3" pinfunction="GND" pintype="power_in"/>
+      <node ref="U4" pin="2" pinfunction="GND" pintype="power_in"/>
+      <node ref="U4" pin="9" pinfunction="PAD" pintype="power_in"/>
+    </net>
+    <net code="6" name="Net-(C1-Pad2)">
+      <node ref="C1" pin="2" pintype="passive"/>
+      <node ref="R1" pin="2" pintype="passive"/>
+      <node ref="R5" pin="1" pintype="passive"/>
+      <node ref="U1" pin="1" pintype="output"/>
+    </net>
+    <net code="7" name="Net-(C2-Pad2)">
+      <node ref="C2" pin="2" pintype="passive"/>
+      <node ref="R3" pin="1" pintype="passive"/>
+    </net>
+    <net code="8" name="Net-(C3-Pad1)">
+      <node ref="C3" pin="1" pintype="passive"/>
+      <node ref="R3" pin="2" pintype="passive"/>
+      <node ref="R4" pin="1" pintype="passive"/>
+    </net>
+    <net code="9" name="Net-(C5-Pad1)">
+      <node ref="C5" pin="1" pintype="passive"/>
+      <node ref="R10" pin="2" pintype="passive"/>
+      <node ref="R12" pin="1" pintype="passive"/>
+      <node ref="U2" pin="1" pintype="output"/>
+    </net>
+    <net code="10" name="Net-(C6-Pad1)">
+      <node ref="C6" pin="1" pintype="passive"/>
+      <node ref="R9" pin="2" pintype="passive"/>
+    </net>
+    <net code="11" name="Net-(C9-Pad1)">
+      <node ref="C9" pin="1" pintype="passive"/>
+      <node ref="R16" pin="2" pintype="passive"/>
+      <node ref="R17" pin="1" pintype="passive"/>
+      <node ref="U2" pin="7" pintype="output"/>
+    </net>
+    <net code="12" name="Net-(C10-Pad1)">
+      <node ref="C10" pin="1" pintype="passive"/>
+      <node ref="R15" pin="2" pintype="passive"/>
+    </net>
+    <net code="13" name="Net-(D7-BK)">
+      <node ref="D7" pin="4" pinfunction="BK" pintype="passive"/>
+      <node ref="R25" pin="2" pintype="passive"/>
+    </net>
+    <net code="14" name="Net-(D7-GK)">
+      <node ref="D7" pin="3" pinfunction="GK" pintype="passive"/>
+      <node ref="R24" pin="2" pintype="passive"/>
+    </net>
+    <net code="15" name="Net-(D7-RK)">
+      <node ref="D7" pin="1" pinfunction="RK" pintype="passive"/>
+      <node ref="R23" pin="2" pintype="passive"/>
+    </net>
+    <net code="16" name="Net-(J8-Shield)">
+      <node ref="J8" pin="6" pinfunction="Shield" pintype="passive"/>
+      <node ref="R22" pin="1" pintype="passive"/>
+    </net>
+    <net code="17" name="Net-(R2-Pad2)">
+      <node ref="R2" pin="2" pintype="passive"/>
+      <node ref="R6" pin="1" pintype="passive"/>
+      <node ref="U1" pin="7" pintype="output"/>
+    </net>
+    <net code="18" name="Net-(R8-Pad2)">
+      <node ref="R8" pin="2" pintype="passive"/>
+      <node ref="R9" pin="1" pintype="passive"/>
+    </net>
+    <net code="19" name="Net-(R14-Pad2)">
+      <node ref="R14" pin="2" pintype="passive"/>
+      <node ref="R15" pin="1" pintype="passive"/>
+    </net>
+    <net code="20" name="Net-(U1A--)">
+      <node ref="C1" pin="1" pintype="passive"/>
+      <node ref="R1" pin="1" pintype="passive"/>
+      <node ref="R4" pin="2" pintype="passive"/>
+      <node ref="U1" pin="2" pinfunction="-" pintype="input"/>
+    </net>
+    <net code="21" name="Net-(U1B--)">
+      <node ref="R2" pin="1" pintype="passive"/>
+      <node ref="R5" pin="2" pintype="passive"/>
+      <node ref="U1" pin="6" pinfunction="-" pintype="input"/>
+    </net>
+    <net code="22" name="Net-(U2A-+)">
+      <node ref="C5" pin="2" pintype="passive"/>
+      <node ref="C7" pin="1" pintype="passive"/>
+      <node ref="R8" pin="1" pintype="passive"/>
+      <node ref="U2" pin="3" pinfunction="+" pintype="input"/>
+    </net>
+    <net code="23" name="Net-(U2A--)">
+      <node ref="R12" pin="2" pintype="passive"/>
+      <node ref="R13" pin="1" pintype="passive"/>
+      <node ref="U2" pin="2" pinfunction="-" pintype="input"/>
+    </net>
+    <net code="24" name="Net-(U2B-+)">
+      <node ref="C11" pin="1" pintype="passive"/>
+      <node ref="C9" pin="2" pintype="passive"/>
+      <node ref="R14" pin="1" pintype="passive"/>
+      <node ref="U2" pin="5" pinfunction="+" pintype="input"/>
+    </net>
+    <net code="25" name="Net-(U2B--)">
+      <node ref="R17" pin="2" pintype="passive"/>
+      <node ref="R18" pin="1" pintype="passive"/>
+      <node ref="U2" pin="6" pinfunction="-" pintype="input"/>
+    </net>
+    <net code="26" name="Net-(U4-C+)">
+      <node ref="C20" pin="1" pintype="passive"/>
+      <node ref="U4" pin="8" pinfunction="C+" pintype="passive"/>
+    </net>
+    <net code="27" name="Net-(U4-C-)">
+      <node ref="C20" pin="2" pintype="passive"/>
+      <node ref="U4" pin="7" pinfunction="C-" pintype="passive"/>
+    </net>
+    <net code="28" name="Net-(U4-CPOUT)">
+      <node ref="C21" pin="1" pintype="passive"/>
+      <node ref="U4" pin="3" pinfunction="CPOUT" pintype="power_out"/>
+    </net>
+    <net code="29" name="Net-(U4-EN)">
+      <node ref="R20" pin="2" pintype="passive"/>
+      <node ref="U4" pin="6" pinfunction="EN" pintype="input"/>
+    </net>
+    <net code="30" name="Net-(U4-VFB)">
+      <node ref="R19" pin="2" pintype="passive"/>
+      <node ref="R21" pin="1" pintype="passive"/>
+      <node ref="U4" pin="5" pinfunction="VFB" pintype="input"/>
+    </net>
+    <net code="31" name="PC0{slash}POT1">
+      <node ref="J6" pin="17" pinfunction="Pin_17" pintype="passive"/>
+      <node ref="RV1" pin="2" pinfunction="2" pintype="passive"/>
+    </net>
+    <net code="32" name="PC1{slash}POT2">
+      <node ref="J6" pin="15" pinfunction="Pin_15" pintype="passive"/>
+      <node ref="RV2" pin="2" pinfunction="2" pintype="passive"/>
+    </net>
+    <net code="33" name="PC10{slash}LED_R">
+      <node ref="J3" pin="2" pinfunction="Pin_2" pintype="passive"/>
+      <node ref="R23" pin="1" pintype="passive"/>
+    </net>
+    <net code="34" name="PC11{slash}LED_G">
+      <node ref="J3" pin="1" pinfunction="Pin_1" pintype="passive"/>
+      <node ref="R24" pin="1" pintype="passive"/>
+    </net>
+    <net code="35" name="PC12{slash}LED_B">
+      <node ref="J3" pin="4" pinfunction="Pin_4" pintype="passive"/>
+      <node ref="R25" pin="1" pintype="passive"/>
+    </net>
+    <net code="36" name="SIGNAL_IN">
+      <node ref="C2" pin="1" pintype="passive"/>
+      <node ref="D3" pin="1" pinfunction="K" pintype="passive"/>
+      <node ref="J4" pin="5" pinfunction="Pin_5" pintype="passive"/>
+      <node ref="J7" pin="T" pintype="passive"/>
+    </net>
+    <net code="37" name="SIGNAL_IN_MCU">
+      <node ref="C4" pin="1" pintype="passive"/>
+      <node ref="J6" pin="7" pinfunction="Pin_7" pintype="passive"/>
+      <node ref="R6" pin="2" pintype="passive"/>
+    </net>
+    <net code="38" name="SIGNAL_OUT">
+      <node ref="C8" pin="2" pintype="passive"/>
+      <node ref="D2" pin="1" pinfunction="K" pintype="passive"/>
+      <node ref="J4" pin="3" pinfunction="Pin_3" pintype="passive"/>
+      <node ref="J5" pin="T" pintype="passive"/>
+      <node ref="R10" pin="1" pintype="passive"/>
+    </net>
+    <net code="39" name="SIGNAL_OUT_MCU">
+      <node ref="C6" pin="2" pintype="passive"/>
+      <node ref="J6" pin="11" pinfunction="Pin_11" pintype="passive"/>
+    </net>
+    <net code="40" name="USB_D_N">
+      <node ref="D6" pin="2" pinfunction="A2" pintype="passive"/>
+      <node ref="J2" pin="13" pinfunction="Pin_13" pintype="passive"/>
+      <node ref="J8" pin="2" pinfunction="D-" pintype="bidirectional"/>
+    </net>
+    <net code="41" name="USB_D_P">
+      <node ref="D5" pin="2" pinfunction="A2" pintype="passive"/>
+      <node ref="J2" pin="11" pinfunction="Pin_11" pintype="passive"/>
+      <node ref="J8" pin="3" pinfunction="D+" pintype="bidirectional"/>
+    </net>
+    <net code="42" name="VBUS">
+      <node ref="C13" pin="1" pintype="passive"/>
+      <node ref="D4" pin="2" pinfunction="A2" pintype="passive"/>
+      <node ref="J3" pin="5" pinfunction="Pin_5" pintype="passive"/>
+      <node ref="J8" pin="1" pinfunction="VBUS" pintype="power_out"/>
+      <node ref="L1" pin="1" pinfunction="1" pintype="passive"/>
+    </net>
+    <net code="43" name="VCC">
+      <node ref="D7" pin="2" pinfunction="A" pintype="passive"/>
+      <node ref="J3" pin="15" pinfunction="Pin_15" pintype="passive"/>
+      <node ref="RV1" pin="1" pinfunction="1" pintype="passive"/>
+      <node ref="RV2" pin="1" pinfunction="1" pintype="passive"/>
+    </net>
+    <net code="44" name="VDDA">
+      <node ref="C17" pin="1" pintype="passive"/>
+      <node ref="J2" pin="8" pinfunction="Pin_8" pintype="passive"/>
+      <node ref="R7" pin="2" pintype="passive"/>
+      <node ref="TP3" pin="1" pinfunction="1" pintype="passive"/>
+      <node ref="U3" pin="2" pinfunction="OUT" pintype="power_out"/>
+    </net>
+    <net code="45" name="VDDA{slash}2">
+      <node ref="R11" pin="2" pintype="passive"/>
+      <node ref="R7" pin="1" pintype="passive"/>
+      <node ref="U1" pin="5" pinfunction="+" pintype="input"/>
+    </net>
+    <net code="46" name="unconnected-(J2-Pin_1-Pad1)">
+      <node ref="J2" pin="1" pinfunction="Pin_1" pintype="passive+no_connect"/>
+    </net>
+    <net code="47" name="unconnected-(J2-Pin_2-Pad2)">
+      <node ref="J2" pin="2" pinfunction="Pin_2" pintype="passive+no_connect"/>
+    </net>
+    <net code="48" name="unconnected-(J2-Pin_3-Pad3)">
+      <node ref="J2" pin="3" pinfunction="Pin_3" pintype="passive+no_connect"/>
+    </net>
+    <net code="49" name="unconnected-(J2-Pin_4-Pad4)">
+      <node ref="J2" pin="4" pinfunction="Pin_4" pintype="passive+no_connect"/>
+    </net>
+    <net code="50" name="unconnected-(J2-Pin_5-Pad5)">
+      <node ref="J2" pin="5" pinfunction="Pin_5" pintype="passive+no_connect"/>
+    </net>
+    <net code="51" name="unconnected-(J2-Pin_6-Pad6)">
+      <node ref="J2" pin="6" pinfunction="Pin_6" pintype="passive+no_connect"/>
+    </net>
+    <net code="52" name="unconnected-(J2-Pin_7-Pad7)">
+      <node ref="J2" pin="7" pinfunction="Pin_7" pintype="passive+no_connect"/>
+    </net>
+    <net code="53" name="unconnected-(J2-Pin_9-Pad9)">
+      <node ref="J2" pin="9" pinfunction="Pin_9" pintype="passive+no_connect"/>
+    </net>
+    <net code="54" name="unconnected-(J2-Pin_14-Pad14)">
+      <node ref="J2" pin="14" pinfunction="Pin_14" pintype="passive+no_connect"/>
+    </net>
+    <net code="55" name="unconnected-(J2-Pin_15-Pad15)">
+      <node ref="J2" pin="15" pinfunction="Pin_15" pintype="passive+no_connect"/>
+    </net>
+    <net code="56" name="unconnected-(J2-Pin_16-Pad16)">
+      <node ref="J2" pin="16" pinfunction="Pin_16" pintype="passive+no_connect"/>
+    </net>
+    <net code="57" name="unconnected-(J2-Pin_17-Pad17)">
+      <node ref="J2" pin="17" pinfunction="Pin_17" pintype="passive+no_connect"/>
+    </net>
+    <net code="58" name="unconnected-(J2-Pin_18-Pad18)">
+      <node ref="J2" pin="18" pinfunction="Pin_18" pintype="passive+no_connect"/>
+    </net>
+    <net code="59" name="unconnected-(J2-Pin_19-Pad19)">
+      <node ref="J2" pin="19" pinfunction="Pin_19" pintype="passive+no_connect"/>
+    </net>
+    <net code="60" name="unconnected-(J2-Pin_20-Pad20)">
+      <node ref="J2" pin="20" pinfunction="Pin_20" pintype="passive+no_connect"/>
+    </net>
+    <net code="61" name="unconnected-(J3-Pin_3-Pad3)">
+      <node ref="J3" pin="3" pinfunction="Pin_3" pintype="passive+no_connect"/>
+    </net>
+    <net code="62" name="unconnected-(J3-Pin_6-Pad6)">
+      <node ref="J3" pin="6" pinfunction="Pin_6" pintype="passive+no_connect"/>
+    </net>
+    <net code="63" name="unconnected-(J3-Pin_8-Pad8)">
+      <node ref="J3" pin="8" pinfunction="Pin_8" pintype="passive+no_connect"/>
+    </net>
+    <net code="64" name="unconnected-(J3-Pin_9-Pad9)">
+      <node ref="J3" pin="9" pinfunction="Pin_9" pintype="passive+no_connect"/>
+    </net>
+    <net code="65" name="unconnected-(J3-Pin_10-Pad10)">
+      <node ref="J3" pin="10" pinfunction="Pin_10" pintype="passive+no_connect"/>
+    </net>
+    <net code="66" name="unconnected-(J3-Pin_11-Pad11)">
+      <node ref="J3" pin="11" pinfunction="Pin_11" pintype="passive+no_connect"/>
+    </net>
+    <net code="67" name="unconnected-(J3-Pin_12-Pad12)">
+      <node ref="J3" pin="12" pinfunction="Pin_12" pintype="passive+no_connect"/>
+    </net>
+    <net code="68" name="unconnected-(J3-Pin_13-Pad13)">
+      <node ref="J3" pin="13" pinfunction="Pin_13" pintype="passive+no_connect"/>
+    </net>
+    <net code="69" name="unconnected-(J3-Pin_14-Pad14)">
+      <node ref="J3" pin="14" pinfunction="Pin_14" pintype="passive+no_connect"/>
+    </net>
+    <net code="70" name="unconnected-(J3-Pin_16-Pad16)">
+      <node ref="J3" pin="16" pinfunction="Pin_16" pintype="passive+no_connect"/>
+    </net>
+    <net code="71" name="unconnected-(J3-Pin_17-Pad17)">
+      <node ref="J3" pin="17" pinfunction="Pin_17" pintype="passive+no_connect"/>
+    </net>
+    <net code="72" name="unconnected-(J3-Pin_18-Pad18)">
+      <node ref="J3" pin="18" pinfunction="Pin_18" pintype="passive+no_connect"/>
+    </net>
+    <net code="73" name="unconnected-(J3-Pin_19-Pad19)">
+      <node ref="J3" pin="19" pinfunction="Pin_19" pintype="passive+no_connect"/>
+    </net>
+    <net code="74" name="unconnected-(J3-Pin_20-Pad20)">
+      <node ref="J3" pin="20" pinfunction="Pin_20" pintype="passive+no_connect"/>
+    </net>
+    <net code="75" name="unconnected-(J6-Pin_1-Pad1)">
+      <node ref="J6" pin="1" pinfunction="Pin_1" pintype="passive+no_connect"/>
+    </net>
+    <net code="76" name="unconnected-(J6-Pin_2-Pad2)">
+      <node ref="J6" pin="2" pinfunction="Pin_2" pintype="passive+no_connect"/>
+    </net>
+    <net code="77" name="unconnected-(J6-Pin_3-Pad3)">
+      <node ref="J6" pin="3" pinfunction="Pin_3" pintype="passive+no_connect"/>
+    </net>
+    <net code="78" name="unconnected-(J6-Pin_4-Pad4)">
+      <node ref="J6" pin="4" pinfunction="Pin_4" pintype="passive+no_connect"/>
+    </net>
+    <net code="79" name="unconnected-(J6-Pin_5-Pad5)">
+      <node ref="J6" pin="5" pinfunction="Pin_5" pintype="passive+no_connect"/>
+    </net>
+    <net code="80" name="unconnected-(J6-Pin_6-Pad6)">
+      <node ref="J6" pin="6" pinfunction="Pin_6" pintype="passive+no_connect"/>
+    </net>
+    <net code="81" name="unconnected-(J6-Pin_8-Pad8)">
+      <node ref="J6" pin="8" pinfunction="Pin_8" pintype="passive+no_connect"/>
+    </net>
+    <net code="82" name="unconnected-(J6-Pin_9-Pad9)">
+      <node ref="J6" pin="9" pinfunction="Pin_9" pintype="passive+no_connect"/>
+    </net>
+    <net code="83" name="unconnected-(J6-Pin_10-Pad10)">
+      <node ref="J6" pin="10" pinfunction="Pin_10" pintype="passive+no_connect"/>
+    </net>
+    <net code="84" name="unconnected-(J6-Pin_12-Pad12)">
+      <node ref="J6" pin="12" pinfunction="Pin_12" pintype="passive+no_connect"/>
+    </net>
+    <net code="85" name="unconnected-(J6-Pin_13-Pad13)">
+      <node ref="J6" pin="13" pinfunction="Pin_13" pintype="passive+no_connect"/>
+    </net>
+    <net code="86" name="unconnected-(J6-Pin_14-Pad14)">
+      <node ref="J6" pin="14" pinfunction="Pin_14" pintype="passive+no_connect"/>
+    </net>
+    <net code="87" name="unconnected-(J6-Pin_16-Pad16)">
+      <node ref="J6" pin="16" pinfunction="Pin_16" pintype="passive+no_connect"/>
+    </net>
+    <net code="88" name="unconnected-(J6-Pin_18-Pad18)">
+      <node ref="J6" pin="18" pinfunction="Pin_18" pintype="passive+no_connect"/>
+    </net>
+    <net code="89" name="unconnected-(J6-Pin_19-Pad19)">
+      <node ref="J6" pin="19" pinfunction="Pin_19" pintype="passive+no_connect"/>
+    </net>
+    <net code="90" name="unconnected-(J6-Pin_20-Pad20)">
+      <node ref="J6" pin="20" pinfunction="Pin_20" pintype="passive+no_connect"/>
+    </net>
+    <net code="91" name="unconnected-(J8-ID-Pad4)">
+      <node ref="J8" pin="4" pinfunction="ID" pintype="passive+no_connect"/>
+    </net>
+  </nets>
+</export>
diff --git a/hardware/LICENSE b/hardware/LICENSE
new file mode 100644 (file)
index 0000000..364c873
--- /dev/null
@@ -0,0 +1,290 @@
+CERN Open Hardware Licence Version 2 - Strongly Reciprocal
+
+
+Preamble
+
+CERN has developed this licence to promote collaboration among
+hardware designers and to provide a legal tool which supports the
+freedom to use, study, modify, share and distribute hardware designs
+and products based on those designs. Version 2 of the CERN Open
+Hardware Licence comes in three variants: CERN-OHL-P (permissive); and
+two reciprocal licences: CERN-OHL-W (weakly reciprocal) and this
+licence, CERN-OHL-S (strongly reciprocal).
+
+The CERN-OHL-S is copyright CERN 2020. Anyone is welcome to use it, in
+unmodified form only.
+
+Use of this Licence does not imply any endorsement by CERN of any
+Licensor or their designs nor does it imply any involvement by CERN in
+their development.
+
+
+1 Definitions
+
+  1.1 'Licence' means this CERN-OHL-S.
+
+  1.2 'Compatible Licence' means
+
+       a) any earlier version of the CERN Open Hardware licence, or
+
+       b) any version of the CERN-OHL-S, or
+
+       c) any licence which permits You to treat the Source to which
+          it applies as licensed under CERN-OHL-S provided that on
+          Conveyance of any such Source, or any associated Product You
+          treat the Source in question as being licensed under
+          CERN-OHL-S.
+
+  1.3 'Source' means information such as design materials or digital
+      code which can be applied to Make or test a Product or to
+      prepare a Product for use, Conveyance or sale, regardless of its
+      medium or how it is expressed. It may include Notices.
+
+  1.4 'Covered Source' means Source that is explicitly made available
+      under this Licence.
+
+  1.5 'Product' means any device, component, work or physical object,
+      whether in finished or intermediate form, arising from the use,
+      application or processing of Covered Source.
+
+  1.6 'Make' means to create or configure something, whether by
+      manufacture, assembly, compiling, loading or applying Covered
+      Source or another Product or otherwise.
+
+  1.7 'Available Component' means any part, sub-assembly, library or
+      code which:
+
+       a) is licensed to You as Complete Source under a Compatible
+          Licence; or
+
+       b) is available, at the time a Product or the Source containing
+          it is first Conveyed, to You and any other prospective
+          licensees
+
+            i) as a physical part with sufficient rights and
+               information (including any configuration and
+               programming files and information about its
+               characteristics and interfaces) to enable it either to
+               be Made itself, or to be sourced and used to Make the
+               Product; or
+           ii) as part of the normal distribution of a tool used to
+               design or Make the Product.
+
+  1.8 'Complete Source' means the set of all Source necessary to Make
+      a Product, in the preferred form for making modifications,
+      including necessary installation and interfacing information
+      both for the Product, and for any included Available Components.
+      If the format is proprietary, it must also be made available in
+      a format (if the proprietary tool can create it) which is
+      viewable with a tool available to potential licensees and
+      licensed under a licence approved by the Free Software
+      Foundation or the Open Source Initiative. Complete Source need
+      not include the Source of any Available Component, provided that
+      You include in the Complete Source sufficient information to
+      enable a recipient to Make or source and use the Available
+      Component to Make the Product.
+
+  1.9 'Source Location' means a location where a Licensor has placed
+      Covered Source, and which that Licensor reasonably believes will
+      remain easily accessible for at least three years for anyone to
+      obtain a digital copy.
+
+ 1.10 'Notice' means copyright, acknowledgement and trademark notices,
+      Source Location references, modification notices (subsection
+      3.3(b)) and all notices that refer to this Licence and to the
+      disclaimer of warranties that are included in the Covered
+      Source.
+
+ 1.11 'Licensee' or 'You' means any person exercising rights under
+      this Licence.
+
+ 1.12 'Licensor' means a natural or legal person who creates or
+      modifies Covered Source. A person may be a Licensee and a
+      Licensor at the same time.
+
+ 1.13 'Convey' means to communicate to the public or distribute.
+
+
+2 Applicability
+
+  2.1 This Licence governs the use, copying, modification, Conveying
+      of Covered Source and Products, and the Making of Products. By
+      exercising any right granted under this Licence, You irrevocably
+      accept these terms and conditions.
+
+  2.2 This Licence is granted by the Licensor directly to You, and
+      shall apply worldwide and without limitation in time.
+
+  2.3 You shall not attempt to restrict by contract or otherwise the
+      rights granted under this Licence to other Licensees.
+
+  2.4 This Licence is not intended to restrict fair use, fair dealing,
+      or any other similar right.
+
+
+3 Copying, Modifying and Conveying Covered Source
+
+  3.1 You may copy and Convey verbatim copies of Covered Source, in
+      any medium, provided You retain all Notices.
+
+  3.2 You may modify Covered Source, other than Notices, provided that
+      You irrevocably undertake to make that modified Covered Source
+      available from a Source Location should You Convey a Product in
+      circumstances where the recipient does not otherwise receive a
+      copy of the modified Covered Source. In each case subsection 3.3
+      shall apply.
+
+      You may only delete Notices if they are no longer applicable to
+      the corresponding Covered Source as modified by You and You may
+      add additional Notices applicable to Your modifications.
+      Including Covered Source in a larger work is modifying the
+      Covered Source, and the larger work becomes modified Covered
+      Source.
+
+  3.3 You may Convey modified Covered Source (with the effect that You
+      shall also become a Licensor) provided that You:
+
+       a) retain Notices as required in subsection 3.2;
+
+       b) add a Notice to the modified Covered Source stating that You
+          have modified it, with the date and brief description of how
+          You have modified it;
+
+       c) add a Source Location Notice for the modified Covered Source
+          if You Convey in circumstances where the recipient does not
+          otherwise receive a copy of the modified Covered Source; and
+
+       d) license the modified Covered Source under the terms and
+          conditions of this Licence (or, as set out in subsection
+          8.3, a later version, if permitted by the licence of the
+          original Covered Source). Such modified Covered Source must
+          be licensed as a whole, but excluding Available Components
+          contained in it, which remain licensed under their own
+          applicable licences.
+
+
+4 Making and Conveying Products
+
+You may Make Products, and/or Convey them, provided that You either
+provide each recipient with a copy of the Complete Source or ensure
+that each recipient is notified of the Source Location of the Complete
+Source. That Complete Source is Covered Source, and You must
+accordingly satisfy Your obligations set out in subsection 3.3. If
+specified in a Notice, the Product must visibly and securely display
+the Source Location on it or its packaging or documentation in the
+manner specified in that Notice.
+
+
+5 Research and Development
+
+You may Convey Covered Source, modified Covered Source or Products to
+a legal entity carrying out development, testing or quality assurance
+work on Your behalf provided that the work is performed on terms which
+prevent the entity from both using the Source or Products for its own
+internal purposes and Conveying the Source or Products or any
+modifications to them to any person other than You. Any modifications
+made by the entity shall be deemed to be made by You pursuant to
+subsection 3.2.
+
+
+6 DISCLAIMER AND LIABILITY
+
+  6.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products
+      are provided 'as is' and any express or implied warranties,
+      including, but not limited to, implied warranties of
+      merchantability, of satisfactory quality, non-infringement of
+      third party rights, and fitness for a particular purpose or use
+      are disclaimed in respect of any Source or Product to the
+      maximum extent permitted by law. The Licensor makes no
+      representation that any Source or Product does not or will not
+      infringe any patent, copyright, trade secret or other
+      proprietary right. The entire risk as to the use, quality, and
+      performance of any Source or Product shall be with You and not
+      the Licensor. This disclaimer of warranty is an essential part
+      of this Licence and a condition for the grant of any rights
+      granted under this Licence.
+
+  6.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to
+      the maximum extent permitted by law, have no liability for
+      direct, indirect, special, incidental, consequential, exemplary,
+      punitive or other damages of any character including, without
+      limitation, procurement of substitute goods or services, loss of
+      use, data or profits, or business interruption, however caused
+      and on any theory of contract, warranty, tort (including
+      negligence), product liability or otherwise, arising in any way
+      in relation to the Covered Source, modified Covered Source
+      and/or the Making or Conveyance of a Product, even if advised of
+      the possibility of such damages, and You shall hold the
+      Licensor(s) free and harmless from any liability, costs,
+      damages, fees and expenses, including claims by third parties,
+      in relation to such use.
+
+
+7 Patents
+
+  7.1 Subject to the terms and conditions of this Licence, each
+      Licensor hereby grants to You a perpetual, worldwide,
+      non-exclusive, no-charge, royalty-free, irrevocable (except as
+      stated in subsections 7.2 and 8.4) patent licence to Make, have
+      Made, use, offer to sell, sell, import, and otherwise transfer
+      the Covered Source and Products, where such licence applies only
+      to those patent claims licensable by such Licensor that are
+      necessarily infringed by exercising rights under the Covered
+      Source as Conveyed by that Licensor.
+
+  7.2 If You institute patent litigation against any entity (including
+      a cross-claim or counterclaim in a lawsuit) alleging that the
+      Covered Source or a Product constitutes direct or contributory
+      patent infringement, or You seek any declaration that a patent
+      licensed to You under this Licence is invalid or unenforceable
+      then any rights granted to You under this Licence shall
+      terminate as of the date such process is initiated.
+
+
+8 General
+
+  8.1 If any provisions of this Licence are or subsequently become
+      invalid or unenforceable for any reason, the remaining
+      provisions shall remain effective.
+
+  8.2 You shall not use any of the name (including acronyms and
+      abbreviations), image, or logo by which the Licensor or CERN is
+      known, except where needed to comply with section 3, or where
+      the use is otherwise allowed by law. Any such permitted use
+      shall be factual and shall not be made so as to suggest any kind
+      of endorsement or implication of involvement by the Licensor or
+      its personnel.
+
+  8.3 CERN may publish updated versions and variants of this Licence
+      which it considers to be in the spirit of this version, but may
+      differ in detail to address new problems or concerns. New
+      versions will be published with a unique version number and a
+      variant identifier specifying the variant. If the Licensor has
+      specified that a given variant applies to the Covered Source
+      without specifying a version, You may treat that Covered Source
+      as being released under any version of the CERN-OHL with that
+      variant. If no variant is specified, the Covered Source shall be
+      treated as being released under CERN-OHL-S. The Licensor may
+      also specify that the Covered Source is subject to a specific
+      version of the CERN-OHL or any later version in which case You
+      may apply this or any later version of CERN-OHL with the same
+      variant identifier published by CERN.
+
+  8.4 This Licence shall terminate with immediate effect if You fail
+      to comply with any of its terms and conditions.
+
+  8.5 However, if You cease all breaches of this Licence, then Your
+      Licence from any Licensor is reinstated unless such Licensor has
+      terminated this Licence by giving You, while You remain in
+      breach, a notice specifying the breach and requiring You to cure
+      it within 30 days, and You have failed to come into compliance
+      in all material respects by the end of the 30 day period. Should
+      You repeat the breach after receipt of a cure notice and
+      subsequent reinstatement, this Licence will terminate
+      immediately and permanently. Section 6 shall continue to apply
+      after any termination.
+
+  8.6 This Licence shall not be enforceable except by a Licensor
+      acting as such, and third party beneficiary rights are
+      specifically excluded.
+
diff --git a/hardware/analog_io.kicad_sch b/hardware/analog_io.kicad_sch
new file mode 100755 (executable)
index 0000000..1320a55
--- /dev/null
@@ -0,0 +1,2464 @@
+(kicad_sch (version 20230121) (generator eeschema)
+
+  (uuid 441a9488-1fb7-4b51-b2c7-6d1e46b0a32c)
+
+  (paper "A4")
+
+  (title_block
+    (title "Analog signal transformation")
+    (date "2023-08-08")
+    (company "bitgloo")
+    (comment 1 "Released under the CERN Open Hardware Licence Version 2 - Strongly Reciprocal")
+  )
+
+  (lib_symbols
+    (symbol "Amplifier_Operational:TLV9062xD" (pin_names (offset 0.127)) (in_bom yes) (on_board yes)
+      (property "Reference" "U" (at 2.54 6.35 0)
+        (effects (font (size 1.27 1.27)) (justify left))
+      )
+      (property "Value" "TLV9062xD" (at 2.54 3.81 0)
+        (effects (font (size 1.27 1.27)) (justify left))
+      )
+      (property "Footprint" "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" (at 2.54 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "https://www.ti.com/lit/ds/symlink/tlv9062.pdf" (at 6.35 3.81 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_locked" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "ki_keywords" "dual op-amp low power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Dual operational amplifier, 300 uV Offset, SOIC-8" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "SOIC*3.9x4.9mm*P1.27mm*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "TLV9062xD_1_1"
+        (polyline
+          (pts
+            (xy 5.08 0)
+            (xy -5.08 5.08)
+            (xy -5.08 -5.08)
+            (xy 5.08 0)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type background))
+        )
+        (pin output line (at 7.62 0 180) (length 2.54)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin input line (at -7.62 -2.54 0) (length 2.54)
+          (name "-" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+        (pin input line (at -7.62 2.54 0) (length 2.54)
+          (name "+" (effects (font (size 1.27 1.27))))
+          (number "3" (effects (font (size 1.27 1.27))))
+        )
+      )
+      (symbol "TLV9062xD_2_1"
+        (polyline
+          (pts
+            (xy 5.08 0)
+            (xy -5.08 5.08)
+            (xy -5.08 -5.08)
+            (xy 5.08 0)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type background))
+        )
+        (pin input line (at -7.62 2.54 0) (length 2.54)
+          (name "+" (effects (font (size 1.27 1.27))))
+          (number "5" (effects (font (size 1.27 1.27))))
+        )
+        (pin input line (at -7.62 -2.54 0) (length 2.54)
+          (name "-" (effects (font (size 1.27 1.27))))
+          (number "6" (effects (font (size 1.27 1.27))))
+        )
+        (pin output line (at 7.62 0 180) (length 2.54)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "7" (effects (font (size 1.27 1.27))))
+        )
+      )
+      (symbol "TLV9062xD_3_1"
+        (pin power_in line (at 0 -7.62 90) (length 3.81)
+          (name "V-" (effects (font (size 1.27 1.27))))
+          (number "4" (effects (font (size 1.27 1.27))))
+        )
+        (pin power_in line (at 0 7.62 270) (length 3.81)
+          (name "V+" (effects (font (size 1.27 1.27))))
+          (number "8" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Device:C" (pin_numbers hide) (pin_names (offset 0.254)) (in_bom yes) (on_board yes)
+      (property "Reference" "C" (at 0.635 2.54 0)
+        (effects (font (size 1.27 1.27)) (justify left))
+      )
+      (property "Value" "C" (at 0.635 -2.54 0)
+        (effects (font (size 1.27 1.27)) (justify left))
+      )
+      (property "Footprint" "" (at 0.9652 -3.81 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "cap capacitor" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Unpolarized capacitor" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "C_*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "C_0_1"
+        (polyline
+          (pts
+            (xy -2.032 -0.762)
+            (xy 2.032 -0.762)
+          )
+          (stroke (width 0.508) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -2.032 0.762)
+            (xy 2.032 0.762)
+          )
+          (stroke (width 0.508) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "C_1_1"
+        (pin passive line (at 0 3.81 270) (length 2.794)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 0 -3.81 90) (length 2.794)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Device:R_US" (pin_numbers hide) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "R" (at 2.54 0 90)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "R_US" (at -2.54 0 90)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 1.016 -0.254 90)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "R res resistor" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Resistor, US symbol" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "R_*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "R_US_0_1"
+        (polyline
+          (pts
+            (xy 0 -2.286)
+            (xy 0 -2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.286)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 -0.762)
+            (xy 1.016 -1.143)
+            (xy 0 -1.524)
+            (xy -1.016 -1.905)
+            (xy 0 -2.286)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0.762)
+            (xy 1.016 0.381)
+            (xy 0 0)
+            (xy -1.016 -0.381)
+            (xy 0 -0.762)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.286)
+            (xy 1.016 1.905)
+            (xy 0 1.524)
+            (xy -1.016 1.143)
+            (xy 0 0.762)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "R_US_1_1"
+        (pin passive line (at 0 3.81 270) (length 1.27)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 0 -3.81 90) (length 1.27)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "power:+5V" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 -3.81 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "+5V" (at 0 3.556 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"+5V\"" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "+5V_0_1"
+        (polyline
+          (pts
+            (xy -0.762 1.27)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.54)
+            (xy 0.762 1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "+5V_1_1"
+        (pin power_in line (at 0 0 90) (length 0) hide
+          (name "+5V" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "power:-5V" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 2.54 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "-5V" (at 0 3.81 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"-5V\"" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "-5V_0_0"
+        (pin power_in line (at 0 0 90) (length 0) hide
+          (name "-5V" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+      (symbol "-5V_0_1"
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 1.27)
+            (xy 0.762 1.27)
+            (xy 0 2.54)
+            (xy -0.762 1.27)
+            (xy 0 1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type outline))
+        )
+      )
+    )
+    (symbol "power:GND" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 -6.35 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "GND" (at 0 -3.81 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"GND\" , ground" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "GND_0_1"
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 -1.27)
+            (xy 1.27 -1.27)
+            (xy 0 -2.54)
+            (xy -1.27 -1.27)
+            (xy 0 -1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "GND_1_1"
+        (pin power_in line (at 0 0 270) (length 0) hide
+          (name "GND" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "power:VDDA" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 -3.81 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "VDDA" (at 0 3.81 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"VDDA\"" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "VDDA_0_1"
+        (polyline
+          (pts
+            (xy -0.762 1.27)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.54)
+            (xy 0.762 1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "VDDA_1_1"
+        (pin power_in line (at 0 0 90) (length 0) hide
+          (name "VDDA" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+  )
+
+
+  (junction (at 76.2 160.02) (diameter 0) (color 0 0 0 0)
+    (uuid 13ca57e4-cb64-4610-8602-f6b3b79b4e4d)
+  )
+  (junction (at 96.52 173.99) (diameter 0) (color 0 0 0 0)
+    (uuid 14baa294-32e6-4d79-933f-f598a3401be8)
+  )
+  (junction (at 254 101.6) (diameter 0) (color 0 0 0 0)
+    (uuid 1cec0460-226d-417d-8a64-e91e92531e84)
+  )
+  (junction (at 77.47 45.72) (diameter 0) (color 0 0 0 0)
+    (uuid 2a686120-7c2b-40a8-94b4-ff352bbed22c)
+  )
+  (junction (at 97.79 104.14) (diameter 0) (color 0 0 0 0)
+    (uuid 544a1e6d-0eba-425e-a387-613fa5a3e4ba)
+  )
+  (junction (at 102.87 104.14) (diameter 0) (color 0 0 0 0)
+    (uuid 555b2ed8-b23d-4699-90af-9ec06e3f7c9d)
+  )
+  (junction (at 77.47 106.68) (diameter 0) (color 0 0 0 0)
+    (uuid 58b50289-d890-4170-b5db-23a006184ec8)
+  )
+  (junction (at 100.33 45.72) (diameter 0) (color 0 0 0 0)
+    (uuid 643fd8e9-3057-4bb3-beab-7b64665cb158)
+  )
+  (junction (at 149.86 57.15) (diameter 0) (color 0 0 0 0)
+    (uuid 6a4a19a7-5df2-44f4-ae40-75dd293319a0)
+  )
+  (junction (at 54.61 106.68) (diameter 0) (color 0 0 0 0)
+    (uuid 6a4bb706-dcc5-4872-b86b-74860967c0eb)
+  )
+  (junction (at 128.27 54.61) (diameter 0) (color 0 0 0 0)
+    (uuid 6d4eb889-ecae-423b-aa28-b01041e96c3e)
+  )
+  (junction (at 97.79 120.65) (diameter 0) (color 0 0 0 0)
+    (uuid 7505d28d-42d4-4141-ba06-8adb7fd6091d)
+  )
+  (junction (at 101.6 157.48) (diameter 0) (color 0 0 0 0)
+    (uuid 771dbe8c-e2bc-40e6-b3f9-d246824bbc97)
+  )
+  (junction (at 100.33 57.15) (diameter 0) (color 0 0 0 0)
+    (uuid 792fbb65-a6d1-41e2-9ca9-0934048113bc)
+  )
+  (junction (at 77.47 54.61) (diameter 0) (color 0 0 0 0)
+    (uuid bb0f3e97-91ef-4c38-b0d6-996ae4d77785)
+  )
+  (junction (at 53.34 160.02) (diameter 0) (color 0 0 0 0)
+    (uuid cd0dc2f5-2bbf-4407-89b7-12f64f275e27)
+  )
+  (junction (at 96.52 157.48) (diameter 0) (color 0 0 0 0)
+    (uuid dd48d0dc-1a30-4855-b672-b884e88b6f39)
+  )
+  (junction (at 59.69 54.61) (diameter 0) (color 0 0 0 0)
+    (uuid e8de15ad-a3c1-43d2-99e8-9736cc4fc0a7)
+  )
+
+  (wire (pts (xy 128.27 54.61) (xy 115.57 54.61))
+    (stroke (width 0) (type default))
+    (uuid 01cbda64-51ea-4b25-ac59-1d6840aad4d0)
+  )
+  (wire (pts (xy 57.15 160.02) (xy 53.34 160.02))
+    (stroke (width 0) (type default))
+    (uuid 079a36ff-c903-46d1-8586-80db49aeb9e6)
+  )
+  (wire (pts (xy 161.29 57.15) (xy 167.64 57.15))
+    (stroke (width 0) (type default))
+    (uuid 0907e7f6-3ca3-4104-92e1-e21d7d590578)
+  )
+  (wire (pts (xy 129.54 157.48) (xy 133.35 157.48))
+    (stroke (width 0) (type default))
+    (uuid 0b8ac979-0d2f-4d8e-876a-825807fd8b0d)
+  )
+  (wire (pts (xy 140.97 157.48) (xy 147.32 157.48))
+    (stroke (width 0) (type default))
+    (uuid 0cdab281-29ae-4e6d-a0db-cfce77096b35)
+  )
+  (wire (pts (xy 55.88 54.61) (xy 59.69 54.61))
+    (stroke (width 0) (type default))
+    (uuid 0e00d3ac-ddd2-4c17-96ae-a390318f8524)
+  )
+  (wire (pts (xy 147.32 57.15) (xy 149.86 57.15))
+    (stroke (width 0) (type default))
+    (uuid 11a2874e-6279-4dbb-a9f4-e378690f94f9)
+  )
+  (wire (pts (xy 77.47 106.68) (xy 77.47 120.65))
+    (stroke (width 0) (type default))
+    (uuid 172bca92-63fe-4e66-b337-8afca1343ca7)
+  )
+  (wire (pts (xy 119.38 176.53) (xy 119.38 173.99))
+    (stroke (width 0) (type default))
+    (uuid 18a12969-71e9-4591-ab64-9501b7c7aea4)
+  )
+  (wire (pts (xy 100.33 34.29) (xy 100.33 45.72))
+    (stroke (width 0) (type default))
+    (uuid 199b0b32-87e1-41fa-8a26-ab433d22b54c)
+  )
+  (wire (pts (xy 43.18 54.61) (xy 48.26 54.61))
+    (stroke (width 0) (type default))
+    (uuid 1deb107f-8ee6-4f20-a7c8-042735521459)
+  )
+  (wire (pts (xy 142.24 104.14) (xy 148.59 104.14))
+    (stroke (width 0) (type default))
+    (uuid 1eaa98a8-10c5-4362-a75a-a97f64524926)
+  )
+  (wire (pts (xy 77.47 106.68) (xy 77.47 91.44))
+    (stroke (width 0) (type default))
+    (uuid 20cbc10b-171f-4139-8623-6865fae119fb)
+  )
+  (wire (pts (xy 97.79 104.14) (xy 102.87 104.14))
+    (stroke (width 0) (type default))
+    (uuid 2343068f-d79f-46b4-a982-0ae2aa0b6572)
+  )
+  (wire (pts (xy 81.28 59.69) (xy 77.47 59.69))
+    (stroke (width 0) (type default))
+    (uuid 27ed1140-35fa-4731-8aec-ac4042413c80)
+  )
+  (wire (pts (xy 96.52 157.48) (xy 96.52 144.78))
+    (stroke (width 0) (type default))
+    (uuid 2b43fe7c-7845-416f-8391-db903b4d546f)
+  )
+  (wire (pts (xy 85.09 45.72) (xy 77.47 45.72))
+    (stroke (width 0) (type default))
+    (uuid 30e1940d-ac9d-43c2-81ac-84544cdb94f8)
+  )
+  (wire (pts (xy 254 101.6) (xy 254 105.41))
+    (stroke (width 0) (type default))
+    (uuid 3a7a3d64-783e-4d0f-b8a7-8c496426d4b0)
+  )
+  (wire (pts (xy 102.87 104.14) (xy 102.87 105.41))
+    (stroke (width 0) (type default))
+    (uuid 43cc69c6-d118-438d-96a2-1216d8d001c0)
+  )
+  (wire (pts (xy 58.42 106.68) (xy 54.61 106.68))
+    (stroke (width 0) (type default))
+    (uuid 45dbe678-c722-4305-a559-ff5e89421c47)
+  )
+  (wire (pts (xy 53.34 160.02) (xy 53.34 162.56))
+    (stroke (width 0) (type default))
+    (uuid 4ae0d717-a858-4543-bfc5-8c7b7900bf45)
+  )
+  (wire (pts (xy 50.8 106.68) (xy 54.61 106.68))
+    (stroke (width 0) (type default))
+    (uuid 4bb92711-010f-4110-b584-dbb65656d05c)
+  )
+  (wire (pts (xy 101.6 157.48) (xy 109.22 157.48))
+    (stroke (width 0) (type default))
+    (uuid 4f1cbd2b-3712-4e94-908b-3c622c1d113d)
+  )
+  (wire (pts (xy 54.61 106.68) (xy 54.61 109.22))
+    (stroke (width 0) (type default))
+    (uuid 50d53083-32d0-4e43-bb1c-db7ffa29e205)
+  )
+  (wire (pts (xy 115.57 54.61) (xy 115.57 57.15))
+    (stroke (width 0) (type default))
+    (uuid 53357090-105c-4c23-8dd4-63f08b386d13)
+  )
+  (wire (pts (xy 97.79 120.65) (xy 110.49 120.65))
+    (stroke (width 0) (type default))
+    (uuid 5ab35d18-5d5b-4512-94bd-a49e2e37830b)
+  )
+  (wire (pts (xy 64.77 160.02) (xy 76.2 160.02))
+    (stroke (width 0) (type default))
+    (uuid 5c49ebf4-8114-4eb0-a400-cdac65114eee)
+  )
+  (wire (pts (xy 77.47 120.65) (xy 83.82 120.65))
+    (stroke (width 0) (type default))
+    (uuid 6077e6f2-98f5-4c34-808b-f6b0dd2c0444)
+  )
+  (wire (pts (xy 76.2 173.99) (xy 82.55 173.99))
+    (stroke (width 0) (type default))
+    (uuid 650ed807-9c2c-4baa-b8ea-e63fc4119580)
+  )
+  (wire (pts (xy 76.2 160.02) (xy 76.2 144.78))
+    (stroke (width 0) (type default))
+    (uuid 67dc7098-e276-4138-849e-4b96f0b1965c)
+  )
+  (wire (pts (xy 101.6 157.48) (xy 101.6 158.75))
+    (stroke (width 0) (type default))
+    (uuid 67ea9d8e-8189-4ba9-afce-bf2c8312a8cc)
+  )
+  (wire (pts (xy 130.81 104.14) (xy 134.62 104.14))
+    (stroke (width 0) (type default))
+    (uuid 6a560e8c-0e2b-490c-9653-6c5a6fdf1375)
+  )
+  (wire (pts (xy 66.04 106.68) (xy 77.47 106.68))
+    (stroke (width 0) (type default))
+    (uuid 6c98525b-07a3-45f7-9586-6b91565c4d74)
+  )
+  (wire (pts (xy 120.65 123.19) (xy 120.65 120.65))
+    (stroke (width 0) (type default))
+    (uuid 6f1ddcec-373c-4ff2-88dd-04c55517ae10)
+  )
+  (wire (pts (xy 76.2 144.78) (xy 82.55 144.78))
+    (stroke (width 0) (type default))
+    (uuid 74feeb8c-fdbc-41c1-b6e4-36ac971e1557)
+  )
+  (wire (pts (xy 77.47 34.29) (xy 77.47 45.72))
+    (stroke (width 0) (type default))
+    (uuid 75de3747-8d55-43a5-9329-d60e1ab39780)
+  )
+  (wire (pts (xy 96.52 173.99) (xy 109.22 173.99))
+    (stroke (width 0) (type default))
+    (uuid 796ceed9-5299-4e1b-aa41-b75e2a020bdd)
+  )
+  (wire (pts (xy 167.64 60.96) (xy 167.64 57.15))
+    (stroke (width 0) (type default))
+    (uuid 7cc7f718-00da-4907-a8d4-eb46ec038edd)
+  )
+  (wire (pts (xy 49.53 160.02) (xy 53.34 160.02))
+    (stroke (width 0) (type default))
+    (uuid 824b6317-5ee3-4183-b962-d579a52e061c)
+  )
+  (wire (pts (xy 96.52 162.56) (xy 96.52 173.99))
+    (stroke (width 0) (type default))
+    (uuid 884a34e6-2bea-4fdc-958d-5138593702ff)
+  )
+  (wire (pts (xy 93.98 157.48) (xy 96.52 157.48))
+    (stroke (width 0) (type default))
+    (uuid 885f3c93-e1e0-4b2b-87a7-c08fa48987bf)
+  )
+  (wire (pts (xy 119.38 173.99) (xy 116.84 173.99))
+    (stroke (width 0) (type default))
+    (uuid 8a2cb018-f384-4d62-bc58-f2683c0d87c6)
+  )
+  (wire (pts (xy 149.86 57.15) (xy 153.67 57.15))
+    (stroke (width 0) (type default))
+    (uuid 9096db32-8a77-44b2-97f3-b225f83e7059)
+  )
+  (wire (pts (xy 100.33 45.72) (xy 100.33 57.15))
+    (stroke (width 0) (type default))
+    (uuid 9103dbf4-8ff6-4fca-a83f-418fe22abf47)
+  )
+  (wire (pts (xy 149.86 45.72) (xy 149.86 57.15))
+    (stroke (width 0) (type default))
+    (uuid 9129b90c-9c2a-4842-9dbd-455d7423abb8)
+  )
+  (wire (pts (xy 254 101.6) (xy 257.81 101.6))
+    (stroke (width 0) (type default))
+    (uuid 9270bc15-18a9-43e0-82b5-a8011bc79f6b)
+  )
+  (wire (pts (xy 95.25 104.14) (xy 97.79 104.14))
+    (stroke (width 0) (type default))
+    (uuid 93633cde-6fce-4d19-8109-d32c96972d19)
+  )
+  (wire (pts (xy 96.52 57.15) (xy 100.33 57.15))
+    (stroke (width 0) (type default))
+    (uuid 9586f1e7-8a81-40e2-9b64-ccae75882ffd)
+  )
+  (wire (pts (xy 59.69 54.61) (xy 59.69 58.42))
+    (stroke (width 0) (type default))
+    (uuid 958f6745-de05-4545-baa6-d06abc9ea9f4)
+  )
+  (wire (pts (xy 96.52 157.48) (xy 101.6 157.48))
+    (stroke (width 0) (type default))
+    (uuid 96340d5e-a21a-4826-88b9-e6d475d8fe6f)
+  )
+  (wire (pts (xy 120.65 120.65) (xy 118.11 120.65))
+    (stroke (width 0) (type default))
+    (uuid 97985406-fa08-49fb-b550-7287a3f657f2)
+  )
+  (wire (pts (xy 76.2 160.02) (xy 78.74 160.02))
+    (stroke (width 0) (type default))
+    (uuid 9816a452-9e75-4958-bf35-daf6efb3e319)
+  )
+  (wire (pts (xy 96.52 144.78) (xy 90.17 144.78))
+    (stroke (width 0) (type default))
+    (uuid a6ed5323-2af3-45c5-9615-75e24477cbcd)
+  )
+  (wire (pts (xy 77.47 54.61) (xy 81.28 54.61))
+    (stroke (width 0) (type default))
+    (uuid a82847a6-0562-4e43-a56f-20758a032215)
+  )
+  (wire (pts (xy 132.08 54.61) (xy 128.27 54.61))
+    (stroke (width 0) (type default))
+    (uuid ae499b69-7960-47f4-94b1-868442379983)
+  )
+  (wire (pts (xy 77.47 106.68) (xy 80.01 106.68))
+    (stroke (width 0) (type default))
+    (uuid b008c5f1-2061-4c18-8d4b-e961b69c6221)
+  )
+  (wire (pts (xy 77.47 59.69) (xy 77.47 66.04))
+    (stroke (width 0) (type default))
+    (uuid b5179481-c40d-4aba-81ac-d8bc98db2d31)
+  )
+  (wire (pts (xy 254 97.79) (xy 254 101.6))
+    (stroke (width 0) (type default))
+    (uuid b6e25c05-a4e6-43d1-bf57-78c397010090)
+  )
+  (wire (pts (xy 111.76 57.15) (xy 115.57 57.15))
+    (stroke (width 0) (type default))
+    (uuid b7b2cf01-8da6-449f-b4bc-514b9df7d393)
+  )
+  (wire (pts (xy 118.11 104.14) (xy 123.19 104.14))
+    (stroke (width 0) (type default))
+    (uuid b7f886b9-0859-40d9-a3f6-a6683c341740)
+  )
+  (wire (pts (xy 95.25 109.22) (xy 97.79 109.22))
+    (stroke (width 0) (type default))
+    (uuid b87dbe6c-b5ab-47e7-894b-ab3611a3eefa)
+  )
+  (wire (pts (xy 100.33 57.15) (xy 104.14 57.15))
+    (stroke (width 0) (type default))
+    (uuid b914df1b-aa28-44c7-8708-9ca845805cb6)
+  )
+  (wire (pts (xy 85.09 34.29) (xy 77.47 34.29))
+    (stroke (width 0) (type default))
+    (uuid bb98d78f-4922-43c1-a016-24830e3d6d64)
+  )
+  (wire (pts (xy 128.27 45.72) (xy 128.27 54.61))
+    (stroke (width 0) (type default))
+    (uuid bdb7fa6e-cbee-42f3-b15c-5af443eb5580)
+  )
+  (wire (pts (xy 77.47 91.44) (xy 83.82 91.44))
+    (stroke (width 0) (type default))
+    (uuid c51f3e45-8fee-4484-87df-75ac220f37c5)
+  )
+  (wire (pts (xy 116.84 157.48) (xy 121.92 157.48))
+    (stroke (width 0) (type default))
+    (uuid c804f2eb-852b-4d46-91ae-670d149a2864)
+  )
+  (wire (pts (xy 93.98 162.56) (xy 96.52 162.56))
+    (stroke (width 0) (type default))
+    (uuid cf31c0ab-f87c-4f7d-85c3-0c7e17c95ace)
+  )
+  (wire (pts (xy 71.12 54.61) (xy 77.47 54.61))
+    (stroke (width 0) (type default))
+    (uuid d032f367-c762-4ba9-969e-73f235ff53ea)
+  )
+  (wire (pts (xy 102.87 104.14) (xy 110.49 104.14))
+    (stroke (width 0) (type default))
+    (uuid d636a217-cad9-421a-9f13-39961341ebe9)
+  )
+  (wire (pts (xy 77.47 45.72) (xy 77.47 54.61))
+    (stroke (width 0) (type default))
+    (uuid df123dd8-4923-452a-b8da-4665d45f61ca)
+  )
+  (wire (pts (xy 92.71 34.29) (xy 100.33 34.29))
+    (stroke (width 0) (type default))
+    (uuid e7407b15-7fe3-441d-a571-d716631a2b1d)
+  )
+  (wire (pts (xy 91.44 120.65) (xy 97.79 120.65))
+    (stroke (width 0) (type default))
+    (uuid e75622b7-18c9-4e5b-82b3-f0cf29de6416)
+  )
+  (wire (pts (xy 97.79 104.14) (xy 97.79 91.44))
+    (stroke (width 0) (type default))
+    (uuid efaa7f19-342f-4d8c-aee7-b09fdc7dad93)
+  )
+  (wire (pts (xy 97.79 109.22) (xy 97.79 120.65))
+    (stroke (width 0) (type default))
+    (uuid efed7f9a-c25b-4b12-8502-09c4399ba710)
+  )
+  (wire (pts (xy 90.17 173.99) (xy 96.52 173.99))
+    (stroke (width 0) (type default))
+    (uuid f0f48c9a-12ee-4367-aa73-0abefbe73bf0)
+  )
+  (wire (pts (xy 135.89 45.72) (xy 128.27 45.72))
+    (stroke (width 0) (type default))
+    (uuid f1b8a59d-2d22-4be3-b9aa-ef59f0e4cf6f)
+  )
+  (wire (pts (xy 59.69 54.61) (xy 63.5 54.61))
+    (stroke (width 0) (type default))
+    (uuid f328d5ec-77b5-43e3-81c3-1bc3673a2a4e)
+  )
+  (wire (pts (xy 143.51 45.72) (xy 149.86 45.72))
+    (stroke (width 0) (type default))
+    (uuid f33d5a85-b58e-4315-b793-98019259497c)
+  )
+  (wire (pts (xy 97.79 91.44) (xy 91.44 91.44))
+    (stroke (width 0) (type default))
+    (uuid fe09fbec-243b-4a4a-8ce9-d5dbb2f125ae)
+  )
+  (wire (pts (xy 76.2 160.02) (xy 76.2 173.99))
+    (stroke (width 0) (type default))
+    (uuid fed811b4-edf5-42fd-b3c2-d12f670345bb)
+  )
+  (wire (pts (xy 92.71 45.72) (xy 100.33 45.72))
+    (stroke (width 0) (type default))
+    (uuid ffb2af50-256e-4a32-82aa-87c6bfb25395)
+  )
+
+  (text "Cancel DC offset" (at 31.75 60.96 0)
+    (effects (font (size 1.27 1.27)) (justify left bottom))
+    (uuid 0c255578-cbec-4af6-8a8f-914b2a4af8a6)
+  )
+  (text "2nd-order low-pass Bessel filter\nFc = 100kHz, G = 2\nnon-inverting (Sallen-Key)"
+    (at 102.87 91.44 0)
+    (effects (font (size 1.27 1.27)) (justify left bottom))
+    (uuid 11759ad9-6dd7-41a3-871a-342ee2b5fb5b)
+  )
+  (text "Operational amplifier options:\n - TLV9162: Good for input, low GBW for output\n - OPA1678: Good for output, low stock\n - TL972: Good balance as choice for both (12MHz)\n - MC33078D: Also a good choice for both (15MHz)\n\nInput requires GBW between 8-15MHz; output requires >14.7MHz"
+    (at 209.55 53.34 0)
+    (effects (font (size 1.27 1.27)) (justify left bottom))
+    (uuid 1298cbaa-a693-47ae-987b-082155ee1893)
+  )
+  (text "Low-pass RC filter\nto increase drop beyond Fc" (at 154.94 48.26 0)
+    (effects (font (size 1.27 1.27)) (justify left bottom))
+    (uuid 187aacc2-0fff-498f-ae6b-86d2f27b6642)
+  )
+  (text "2nd-order low-pass Bessel filter\nFc = 100kHz, G = 1\ninverting (MFB)"
+    (at 38.1 38.1 0)
+    (effects (font (size 1.27 1.27)) (justify left bottom))
+    (uuid 2db69c83-1c30-4290-876e-1ab4e087452c)
+  )
+  (text "Cancel DC offset" (at 129.54 162.56 0)
+    (effects (font (size 1.27 1.27)) (justify left bottom))
+    (uuid 2ddab52d-e5d5-4b55-99a7-4b82b0d74488)
+  )
+  (text "Cancel DC offset" (at 130.81 109.22 0)
+    (effects (font (size 1.27 1.27)) (justify left bottom))
+    (uuid 4d37f2a9-553f-44c3-88e8-f0b7f0eb294f)
+  )
+  (text "Inverting amplifier\nG = 0.5, Offset = 1V\n0-2V output for MCU"
+    (at 110.49 38.1 0)
+    (effects (font (size 1.27 1.27)) (justify left bottom))
+    (uuid 7ff24ca8-45ac-404e-b354-ab2d4f4a4a56)
+  )
+  (text "Signal generator output" (at 15.875 134.62 0)
+    (effects (font (size 2.54 2.54)) (justify left bottom))
+    (uuid a8262b1a-df9c-4ba8-9de2-e1e787ef16d3)
+  )
+  (text "2nd-order low-pass Bessel filter\nFc = 100kHz, G = 2\nnon-inverting (Sallen-Key)"
+    (at 101.6 144.78 0)
+    (effects (font (size 1.27 1.27)) (justify left bottom))
+    (uuid b339f83c-48d6-4510-8f56-d7e7635e2b35)
+  )
+  (text "Low-pass RC filter\nto increase drop beyond Fc" (at 66.04 97.79 0)
+    (effects (font (size 1.27 1.27)) (justify right bottom))
+    (uuid b359bad7-cd76-42ef-96c0-69d394af2379)
+  )
+  (text "Input signal" (at 15.875 26.67 0)
+    (effects (font (size 2.54 2.54)) (justify left bottom))
+    (uuid b564d06f-c9fd-49a8-a95e-a877b22ac096)
+  )
+  (text "Low-pass RC filter\nto increase drop beyond Fc" (at 64.77 151.13 0)
+    (effects (font (size 1.27 1.27)) (justify right bottom))
+    (uuid bf8bd8cd-d4f7-44a1-ad1b-31e7ccf62047)
+  )
+  (text "Output signal" (at 15.875 82.55 0)
+    (effects (font (size 2.54 2.54)) (justify left bottom))
+    (uuid e8957e1d-c031-4004-9296-84b44028970b)
+  )
+
+  (global_label "GENERATOR" (shape input) (at 49.53 160.02 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid 0446309d-a8ba-4da1-acb0-acccff8f98c9)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 35.799 160.02 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "SIGNAL_IN_MCU" (shape input) (at 167.64 57.15 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid 0f8f9aed-d237-4cff-a02a-edbc5f8dbbf4)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 184.9997 57.15 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+  (global_label "VDDA{slash}2" (shape input) (at 257.81 101.6 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid 4fb7b295-e678-4a16-b835-5b5ab5e6ef90)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 267.973 101.6 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+  (global_label "GENERATOR_MCU" (shape input) (at 147.32 157.48 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid 7fd78e2d-c200-4df3-9413-8f9a4cfa81ce)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 166.0705 157.48 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+  (global_label "SIGNAL_OUT_MCU" (shape input) (at 148.59 104.14 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid b5a5cc41-eed0-4ce9-9e3c-1e43cb81d7a7)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 167.643 104.14 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+  (global_label "VDDA{slash}2" (shape input) (at 132.08 59.69 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid ccd7ea87-758e-4bb4-a684-bd62b838741d)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 121.917 59.69 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "SIGNAL_IN" (shape input) (at 35.56 54.61 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid d8e7c267-f8b7-40a7-822f-600a8a7b2e2e)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 23.2198 54.61 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "SIGNAL_OUT" (shape input) (at 50.8 106.68 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid fe685e56-e4e9-4f3f-9e67-0eaa629e96c4)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 36.7665 106.68 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 102.87 113.03 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 0644252a-1b0b-4fcc-80c7-a4a62f93d012)
+    (property "Reference" "#PWR02" (at 102.87 119.38 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 102.87 118.11 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 102.87 113.03 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 102.87 113.03 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 49ed74ec-3a30-44e9-bc32-74708afa0d6f))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR02") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR09") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 107.95 57.15 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 09763a12-feda-48e1-afe9-d8a81a7439cb)
+    (property "Reference" "R4" (at 107.95 52.07 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "20k" (at 107.95 54.61 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 108.204 56.134 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 107.95 57.15 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RT0603DRE0720KL" (at 107.95 57.15 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 6611f1b9-8e1b-492e-955d-247b29c6d327))
+    (pin "2" (uuid 7c771bcf-0adb-46f1-a6aa-7e8c78837392))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "R4") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R5") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 86.36 173.99 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 0b777e25-6648-4258-86f0-5ac639e20017)
+    (property "Reference" "R17" (at 86.36 168.91 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "2.49k" (at 86.36 171.45 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 86.614 172.974 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 86.36 173.99 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RT0603DRE072K49L" (at 86.36 173.99 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid c93944d4-c30c-42f9-bf5b-391c69879cbf))
+    (pin "2" (uuid 69bda8ee-4704-47e0-84ff-d3df01165524))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R17") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Amplifier_Operational:TLV9062xD") (at 86.36 160.02 0) (mirror y) (unit 2)
+    (in_bom yes) (on_board yes) (dnp no)
+    (uuid 0bba421f-7d3e-4e1e-9f66-4037ea1e27ad)
+    (property "Reference" "U2" (at 86.36 151.13 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "OPA1678" (at 86.36 153.67 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" (at 83.82 160.02 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 80.01 156.21 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "OPA1652AID" (at 86.36 160.02 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 1f61a1ea-98b2-4f14-aef5-263de62de786))
+    (pin "2" (uuid d1835242-f5eb-4194-bf86-2922798a3626))
+    (pin "3" (uuid 3e29f86f-c9b3-48d3-b9ee-7a04e7402329))
+    (pin "5" (uuid 05f0d4ef-1c6c-4305-9974-02b11d54e7a1))
+    (pin "6" (uuid 1eee25d9-41e6-435a-8282-5677e1ab7994))
+    (pin "7" (uuid 7e13f8b1-8471-49c0-a7c4-f430eb568801))
+    (pin "4" (uuid 6bf340e5-1823-4308-a64e-18f1c6f01bc2))
+    (pin "8" (uuid 73c818e5-90ee-45d8-a985-168e76b95c95))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "U2") (unit 2)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "U2") (unit 2)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Amplifier_Operational:TLV9062xD") (at 218.44 101.6 0) (unit 3)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 0d35ce54-c1cf-4a8a-ba3b-4486213d9285)
+    (property "Reference" "U2" (at 219.71 100.965 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "OPA1678" (at 219.71 103.505 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" (at 220.98 101.6 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 224.79 97.79 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "OPA1652AID" (at 218.44 101.6 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 66d39042-8a71-462a-b9a9-1ca367067f3d))
+    (pin "2" (uuid 1c9a0b39-dfe4-460c-a72a-9e1182732a31))
+    (pin "3" (uuid c823d86f-b632-4b28-b0fa-8e92be1e3edf))
+    (pin "5" (uuid d9181019-6f28-4b66-aba4-ee3297e161e3))
+    (pin "6" (uuid caf50f42-6307-4795-9bdb-553383434304))
+    (pin "7" (uuid 5731784d-1008-4ea7-b598-23e49c572152))
+    (pin "4" (uuid d33767b3-df7d-4842-877e-c255ad77ccc8))
+    (pin "8" (uuid a4e547a7-29d8-439d-b01e-1a601ce2877f))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "U2") (unit 3)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "U2") (unit 3)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 59.69 66.04 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 1063e76b-79dd-42d8-a5b9-3a1dc5ab4ffd)
+    (property "Reference" "#PWR01" (at 59.69 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 59.69 71.12 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 59.69 66.04 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 59.69 66.04 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 71a3f27d-3333-465a-8fe8-e3dfb6c45173))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR01") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR01") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 59.69 62.23 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 12a9fbfe-dee3-47f7-8a33-6ece0a6a5e21)
+    (property "Reference" "C1" (at 63.5 61.595 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "274p" (at 63.5 64.135 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 60.6552 66.04 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 59.69 62.23 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CC0603KRX7R9BB271" (at 59.69 62.23 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 6fdc265c-2317-4a7e-a795-ce750f52008f))
+    (pin "2" (uuid 42b60269-a437-4d44-af42-e7d52bd8670a))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "C1") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "C3") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 167.64 68.58 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 15d86f1c-6e1d-4e83-bc8c-152584093027)
+    (property "Reference" "#PWR02" (at 167.64 74.93 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 167.64 73.66 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 167.64 68.58 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 167.64 68.58 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid a4764405-0495-4c52-8224-19e3d8bfd178))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR02") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR03") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:-5V") (at 237.49 109.22 180) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 1af02c4c-1c94-4791-8ac6-070fe9a60b9c)
+    (property "Reference" "#PWR08" (at 237.49 111.76 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "-5V" (at 237.49 114.3 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 237.49 109.22 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 237.49 109.22 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid e092b399-178a-4593-b707-e805c3bff0c7))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR08") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR08") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 101.6 166.37 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 1f3d7225-9a9b-486a-b3c1-2613dae4d720)
+    (property "Reference" "#PWR02" (at 101.6 172.72 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 101.6 171.45 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 101.6 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 101.6 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 3e9e0c47-e269-4be9-9f01-2905d74f0424))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR02") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR013") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 52.07 54.61 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 22077614-3367-4e4d-93bb-9d3cc32003bf)
+    (property "Reference" "R1" (at 52.07 49.53 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "9.09k" (at 52.07 52.07 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 52.324 53.594 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 52.07 54.61 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RT0603DRE079K09L" (at 52.07 54.61 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 96e0b48e-77bd-47d6-8390-29484650cea3))
+    (pin "2" (uuid fe349b81-f850-437a-87de-9a284c706f1b))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "R1") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R3") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 127 104.14 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 2d0a2ddc-1a7a-4738-b65d-75dfc8aed08c)
+    (property "Reference" "R9" (at 127 99.06 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "6.81k" (at 127 101.6 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 127.254 103.124 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 127 104.14 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RT0603DRE076K81L" (at 127 104.14 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid a65e8c4e-375b-4814-add8-7ccf7111819a))
+    (pin "2" (uuid 64090c02-5cb4-4571-b15d-9b44b284ae3f))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R9") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 87.63 120.65 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 34e1c64c-0f60-433f-ae2a-8d2833c48e4f)
+    (property "Reference" "R12" (at 87.63 115.57 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "2.49k" (at 87.63 118.11 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 87.884 119.634 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 87.63 120.65 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RT0603DRE072K49L" (at 87.63 120.65 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 32bb3e32-35bf-47f1-bde9-eb411cf66f0d))
+    (pin "2" (uuid b1f25ba7-a26e-4e64-b1fe-8e88f089afec))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R12") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 254 113.03 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 3e30a194-251d-470d-a890-0baee49de02b)
+    (property "Reference" "#PWR05" (at 254 119.38 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 254 118.11 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 254 113.03 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 254 113.03 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 6cff9016-7c95-4818-96c4-cc8f70542aa6))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR05") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR010") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 53.34 166.37 180) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 3ffe3182-5bf2-4df2-8625-80c9083156c0)
+    (property "Reference" "C12" (at 57.15 165.735 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Value" "DNP" (at 57.15 168.275 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 52.3748 162.56 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 53.34 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid e7f74963-292e-45b9-ab8c-91a5764b765e))
+    (pin "2" (uuid 9729df06-5e8e-4dad-84d0-f0f9dc2598fd))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "C12") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Amplifier_Operational:TLV9062xD") (at 87.63 106.68 0) (mirror y) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no)
+    (uuid 4014b31a-906d-4e54-9260-18b76b511e9b)
+    (property "Reference" "U2" (at 87.63 97.79 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "OPA1678" (at 87.63 100.33 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" (at 85.09 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 81.28 102.87 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "OPA1652AID" (at 87.63 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid c8cffe73-6aa8-4d3c-93e2-a94535394a28))
+    (pin "2" (uuid bbaf461e-ae38-4e0e-878f-9652294d8158))
+    (pin "3" (uuid ba25295c-435b-400c-9bf1-ecd183fca919))
+    (pin "5" (uuid e4ab79d0-22b3-4a21-806d-e49b748eabe4))
+    (pin "6" (uuid 7ca522ff-b1ba-4a83-9a7e-37f8d4e9b39f))
+    (pin "7" (uuid 119d118c-ad1d-4ec6-a78c-ce77a4a8a638))
+    (pin "4" (uuid 7e4b0450-b24a-43cd-aa72-83851fcc4c80))
+    (pin "8" (uuid 152e8a15-6676-49e0-9c91-5d1da146a15d))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "U2") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "U2") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 54.61 113.03 180) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 4bd163bd-4e71-45f4-aa51-12c0c8894738)
+    (property "Reference" "C8" (at 58.42 112.395 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Value" "DNP" (at 58.42 114.935 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 53.6448 109.22 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 54.61 113.03 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 9be65c66-1afa-4765-b611-db1cb0114523))
+    (pin "2" (uuid e506969b-3ff3-4998-b48d-8b0875e0fef3))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "C8") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 88.9 45.72 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 5f13d707-5314-4204-95ff-e7023dbf1390)
+    (property "Reference" "C2" (at 88.9 39.37 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "100p" (at 88.9 41.91 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 92.71 44.7548 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 88.9 45.72 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CC0603KPX7R9BB101" (at 88.9 45.72 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 45a25271-bcc0-47e3-a8e7-7d256aadbca0))
+    (pin "2" (uuid 6829677c-0d98-4f83-a3f3-95946cce1178))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "C2") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "C1") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 102.87 109.22 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 623d4de9-4003-498d-bb94-d973ca58f06f)
+    (property "Reference" "C7" (at 106.68 108.585 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "100p" (at 106.68 111.125 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 103.8352 113.03 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 102.87 109.22 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CC0603KPX7R9BB101" (at 102.87 109.22 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 5ca17ef7-5119-4aea-b2fc-5df05367a0d8))
+    (pin "2" (uuid 468fc0bb-223d-4282-a793-4cd2a25d836b))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "C7") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 77.47 66.04 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 62a7acc6-bac9-44e6-89a6-1cab2d22ca59)
+    (property "Reference" "#PWR02" (at 77.47 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 77.47 71.12 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 77.47 66.04 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 77.47 66.04 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 2b2a5e9c-adb3-461c-8665-30285382f988))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR02") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR02") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 87.63 91.44 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 63c2c207-40b2-47ce-b8a2-f9c77921a2b5)
+    (property "Reference" "C5" (at 87.63 85.09 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "105p" (at 87.63 87.63 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 91.44 90.4748 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 87.63 91.44 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CC0603KPX7R9BB101" (at 87.63 91.44 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid d6c1ad4e-4363-4a74-92ff-ca08e6d4ec5f))
+    (pin "2" (uuid 1fc9d465-8fad-4fd3-a830-30dc6adbf3b2))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "C5") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 67.31 54.61 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 68ea83d2-5dc0-4430-a711-4c94c4e55ef4)
+    (property "Reference" "R2" (at 67.31 49.53 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "6.34k" (at 67.31 52.07 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 67.564 53.594 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 67.31 54.61 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RT0603DRE076K34L" (at 67.31 54.61 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 388ac2ff-e1b0-46fe-bb18-df208c66e7f1))
+    (pin "2" (uuid 2467a834-c99b-4539-8e40-186d5ddb860a))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "R2") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R4") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 62.23 106.68 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 6aab9e7e-b33f-47a1-8408-1806df557085)
+    (property "Reference" "R10" (at 62.23 101.6 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "0" (at 62.23 104.14 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 62.484 105.664 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 62.23 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RMCF0603ZT0R00" (at 62.23 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 749c90dc-eeb5-4ea9-b923-187074b37738))
+    (pin "2" (uuid 100de8b7-e2be-4464-aa8a-86b4ddc88662))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R10") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:VDDA") (at 254 90.17 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 6bb37a30-601d-4b78-add7-b6eaf0d233fb)
+    (property "Reference" "#PWR06" (at 254 93.98 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "VDDA" (at 254 86.36 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 254 90.17 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 254 90.17 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid a83c3b8e-afa5-444a-94f6-7a76312d9888))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR06") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR04") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 125.73 157.48 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 6de7c3e2-ccb1-4bda-b6b9-7e265de95076)
+    (property "Reference" "R15" (at 125.73 152.4 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "6.81k" (at 125.73 154.94 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 125.984 156.464 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 125.73 157.48 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RT0603DRE076K81L" (at 125.73 157.48 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid c0ce4fe5-e3ff-40cf-ab00-8557ad4fc7d5))
+    (pin "2" (uuid 21c31c37-7300-47fd-8c86-f07c4a4f3541))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R15") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:+5V") (at 218.44 93.98 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 754c3c87-dd31-495a-8b6e-875194a6f038)
+    (property "Reference" "#PWR07" (at 218.44 97.79 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "+5V" (at 218.44 90.17 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 218.44 93.98 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 218.44 93.98 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid ffb94f6b-43c6-46a5-a336-3d3e4d3aa679))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR07") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR05") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 101.6 162.56 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 77594052-576e-4ede-837c-0cea441b7b38)
+    (property "Reference" "C11" (at 105.41 161.925 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "100p" (at 105.41 164.465 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 102.5652 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 101.6 162.56 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CC0603KPX7R9BB101" (at 101.6 162.56 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 7f966cbf-d036-471d-84a3-c48685acc885))
+    (pin "2" (uuid 7db36809-9cc3-4266-b9c7-15900e3691fc))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "C11") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 139.7 45.72 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 77903c71-4695-45df-ae74-1ca7380bcb51)
+    (property "Reference" "R7" (at 139.7 40.64 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "10k/0.1%" (at 139.7 43.18 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 139.954 44.704 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 139.7 45.72 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RN73R1JTTD1002B25" (at 139.7 45.72 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid d1ae68a4-d545-4e9e-b67c-dea4c1ecd866))
+    (pin "2" (uuid 2732cc5c-562f-427b-bb0c-e7fab4426e08))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "R7") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R2") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:+5V") (at 237.49 93.98 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 81ccd8b5-9d95-4261-b85a-7edd690df830)
+    (property "Reference" "#PWR07" (at 237.49 97.79 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "+5V" (at 237.49 90.17 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 237.49 93.98 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 237.49 93.98 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 5641c513-3df3-40ae-a91f-5cab931cd3be))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR07") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR06") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 167.64 64.77 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 88e74efe-ba6a-4380-a952-50b4a27eb246)
+    (property "Reference" "C4" (at 171.45 64.135 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "DNP" (at 171.45 66.675 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 168.6052 68.58 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 167.64 64.77 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 1ce2fc37-267c-44a5-81f6-9df6887c66ce))
+    (pin "2" (uuid 7f866abb-41f7-46b0-8b7e-cffe216766c1))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "C4") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Amplifier_Operational:TLV9062xD") (at 237.49 101.6 0) (unit 3)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 8c9bfe4c-5a4e-45a1-8b6a-8480aa78343e)
+    (property "Reference" "U1" (at 238.76 100.965 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "TLV9162" (at 238.76 103.505 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" (at 240.03 101.6 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "https://www.ti.com/lit/ds/symlink/tlv9162.pdf" (at 243.84 97.79 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "TLV9162IDR" (at 237.49 101.6 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid e1d7395e-fa07-43ca-983f-4cb0f447e8c2))
+    (pin "2" (uuid 1443d506-f486-4d8e-a73f-ab2ee559ec37))
+    (pin "3" (uuid 304101b1-2173-4b4a-8359-86b3843e4309))
+    (pin "5" (uuid 7fd0783b-4c9a-46ec-a645-9b3483f6c033))
+    (pin "6" (uuid 8614b269-5bed-417b-8b7d-79a068767925))
+    (pin "7" (uuid 78247ffa-6f2f-40b3-8af9-36d937ce8754))
+    (pin "4" (uuid 29f98b05-419f-47bf-9552-3973c7a0e813))
+    (pin "8" (uuid d93ff3db-bdfb-4d71-83ae-9eb3adbc430b))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "U1") (unit 3)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "U1") (unit 3)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 53.34 170.18 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 8d9071cf-171c-4b0a-8acf-30c3a1a0fcd2)
+    (property "Reference" "#PWR01" (at 53.34 176.53 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 53.34 175.26 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 53.34 170.18 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 53.34 170.18 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid de2da121-0adb-42fe-b814-77a6c2f676d1))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR01") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR014") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:-5V") (at 218.44 109.22 180) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 9330e2e5-e962-4235-8327-3a89a62ddcd2)
+    (property "Reference" "#PWR08" (at 218.44 111.76 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "-5V" (at 218.44 114.3 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 218.44 109.22 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 218.44 109.22 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 9b21a1d6-dee7-4d1e-be71-b86425f0627e))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR08") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR07") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 60.96 160.02 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 95db5be2-27f2-47d0-bd88-021be9957d2d)
+    (property "Reference" "R16" (at 60.96 154.94 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "0" (at 60.96 157.48 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 61.214 159.004 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 60.96 160.02 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RMCF0603ZT0R00" (at 60.96 160.02 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid c2a70843-85f3-4f54-a0b3-79626011b504))
+    (pin "2" (uuid 5a8cf6ad-d3ac-4065-bb84-f08cc9a4e556))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R16") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 114.3 104.14 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 95dedd7c-c752-4712-9aea-ee0143445b87)
+    (property "Reference" "R8" (at 114.3 99.06 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "22.1k" (at 114.3 101.6 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 114.554 103.124 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 114.3 104.14 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RT0603DRE0722K1L" (at 114.3 104.14 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid f9aa0b93-68bc-4919-aaa9-1cfee0e256c0))
+    (pin "2" (uuid 718d5583-4741-49c0-a741-632e7c73c064))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R8") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 137.16 157.48 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 99c91ccc-b2c3-4d6a-a11f-c11c2ab3496d)
+    (property "Reference" "C5" (at 137.16 151.13 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "10u" (at 137.16 153.67 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0805_2012Metric" (at 140.97 156.5148 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 137.16 157.48 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CL21A106KOQNNNE" (at 137.16 157.48 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid b930ea1c-9b17-4f90-b3d2-75acb31181fd))
+    (pin "2" (uuid 8d69e19a-6474-4f45-9edc-2aeab636e721))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "C5") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "C10") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 120.65 123.19 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid a31f3ed6-e728-4293-b760-25b5c2bdcb46)
+    (property "Reference" "#PWR02" (at 120.65 129.54 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 120.65 128.27 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 120.65 123.19 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 120.65 123.19 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 55ebe991-a6fe-4845-86b6-cba590785800))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR02") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR012") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 119.38 176.53 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid a96847c8-e767-4fad-9b42-191f8c5416e9)
+    (property "Reference" "#PWR02" (at 119.38 182.88 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 119.38 181.61 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 119.38 176.53 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 119.38 176.53 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid d71e15a3-92d4-4e9d-9a64-d80381b82b20))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR02") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR015") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 254 109.22 180) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid a97dfe8c-7bb6-43dd-9b12-e26f02e8d594)
+    (property "Reference" "R6" (at 256.54 108.585 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Value" "10k/0.1%" (at 256.54 111.125 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 252.984 108.966 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 254 109.22 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RN73R1JTTD1002B25" (at 254 109.22 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 230676bd-f9d7-4135-bf94-5bd32a6c6817))
+    (pin "2" (uuid 9c2995b3-c3fd-4325-a38a-02d453a8d712))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "R6") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R11") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Amplifier_Operational:TLV9062xD") (at 139.7 57.15 0) (mirror x) (unit 2)
+    (in_bom yes) (on_board yes) (dnp no)
+    (uuid ada410a1-682d-4a65-a2fe-bdbaa9ec75fc)
+    (property "Reference" "U1" (at 139.7 66.04 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "TLV9162" (at 139.7 63.5 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" (at 142.24 57.15 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "https://www.ti.com/lit/ds/symlink/tlv9162.pdf" (at 146.05 60.96 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "TLV9162IDR" (at 139.7 57.15 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 70cbb8b0-b842-4326-aada-dae0717f5282))
+    (pin "2" (uuid e5340e6e-6714-4211-9a29-b399cdd97942))
+    (pin "3" (uuid a1871b0d-f1f1-4ee5-a636-483e7fd30b10))
+    (pin "5" (uuid 34830b8e-7271-4349-a750-2834597d6bfa))
+    (pin "6" (uuid c17cc6f8-3293-4f37-99af-22100f912684))
+    (pin "7" (uuid 167a70b1-9f97-4d3e-9a04-030bee9b9f21))
+    (pin "4" (uuid 91412848-38fa-48ee-8d47-f24df08baf30))
+    (pin "8" (uuid 66822d18-13db-4d1a-8c58-63a9a2ab2bee))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "U1") (unit 2)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "U1") (unit 2)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 113.03 157.48 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid af2574b7-1459-4b58-a3e2-46c8e623030f)
+    (property "Reference" "R14" (at 113.03 152.4 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "22.1k" (at 113.03 154.94 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 113.284 156.464 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 113.03 157.48 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RT0603DRE0722K1L" (at 113.03 157.48 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 9155210f-f1db-403a-b9f1-e1400b8030cc))
+    (pin "2" (uuid cb24e826-7310-439a-8775-626d726466f1))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R14") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 138.43 104.14 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid b0bfe7db-09da-4802-8a23-6a20984ec23f)
+    (property "Reference" "C5" (at 138.43 97.79 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "10u" (at 138.43 100.33 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0805_2012Metric" (at 142.24 103.1748 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 138.43 104.14 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CL21A106KOQNNNE" (at 138.43 104.14 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 1bfbf1bf-e80b-4a39-b2a7-f400e35b7470))
+    (pin "2" (uuid 49533bad-0162-4c94-8789-d89aac72e635))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "C5") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "C6") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 157.48 57.15 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid b1cf4a1c-d6f7-46a0-8fae-ab2466b84cbc)
+    (property "Reference" "R6" (at 157.48 52.07 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "0" (at 157.48 54.61 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 157.734 56.134 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 157.48 57.15 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RMCF0603ZT0R00" (at 157.48 57.15 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 8c757545-9373-43a0-85a5-bbbda107cd9e))
+    (pin "2" (uuid 450a2be7-651f-4979-9655-ae6450471d9b))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R6") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 54.61 116.84 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid b220f110-d3c5-4af7-b47d-d29abe2f4aed)
+    (property "Reference" "#PWR01" (at 54.61 123.19 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 54.61 121.92 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 54.61 116.84 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 54.61 116.84 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid c57fd86a-ca36-40a3-bbfe-9346fdfce204))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "#PWR01") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "#PWR011") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 39.37 54.61 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid b7446dbf-b5bf-4307-9edc-e98dcd42c78d)
+    (property "Reference" "C5" (at 39.37 48.26 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "10u" (at 39.37 50.8 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0805_2012Metric" (at 43.18 53.6448 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 39.37 54.61 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CL21A106KOQNNNE" (at 39.37 54.61 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid ac1fe170-2eb1-42f3-ac4e-7c4ffceba4f6))
+    (pin "2" (uuid 14a894dc-f8fa-46d9-a8e0-57a2289ada05))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "C5") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "C2") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 113.03 173.99 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid b79fe011-5fd4-4b0f-bb62-51fff45eb70a)
+    (property "Reference" "R18" (at 113.03 168.91 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "2.49k" (at 113.03 171.45 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 113.284 172.974 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 113.03 173.99 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RT0603DRE072K49L" (at 113.03 173.99 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid f9e943f6-0226-49f5-9611-99036b23f957))
+    (pin "2" (uuid 2d8b2997-5a40-4d79-be80-3371ed7c49e3))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R18") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Amplifier_Operational:TLV9062xD") (at 88.9 57.15 0) (mirror x) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no)
+    (uuid b7a8ee36-8943-426a-b182-3c32ea14e286)
+    (property "Reference" "U1" (at 88.9 66.04 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "TLV9162" (at 88.9 63.5 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" (at 91.44 57.15 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "https://www.ti.com/lit/ds/symlink/tlv9162.pdf" (at 95.25 60.96 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "TLV9162IDR" (at 88.9 57.15 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 14db3c56-fa76-48fa-823e-cc555eaa2472))
+    (pin "2" (uuid 0500565d-dcc2-4e13-9e37-d38fe95b83f0))
+    (pin "3" (uuid 02e1b336-e9a6-4186-a02a-3f2eaf52dd8b))
+    (pin "5" (uuid 7b01a7a7-d2bc-486f-a8e3-e57b57dd8762))
+    (pin "6" (uuid 760dfb5f-385e-4704-856c-525e1b4098f3))
+    (pin "7" (uuid 06251b44-55dd-4579-b6f2-1ef0134bd4d2))
+    (pin "4" (uuid 8de3edc6-dd14-459f-b596-3c091eb0dfe7))
+    (pin "8" (uuid eb256e96-db70-450f-b88a-892c9bf0748e))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "U1") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "U1") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 254 93.98 180) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid c5790ffe-38a3-462e-88e9-7a0e67c0f3b4)
+    (property "Reference" "R5" (at 256.54 93.345 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Value" "10k/0.1%" (at 256.54 95.885 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 252.984 93.726 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 254 93.98 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RN73R1JTTD1002B25" (at 254 93.98 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 1b22a07d-8dd1-4067-ac70-790d7b7ebe81))
+    (pin "2" (uuid b25739ce-c4df-41f6-9b5c-e304d523b167))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "R5") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R7") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 114.3 120.65 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid e9a26256-ad58-4b4e-aeb9-27b97c5cf7a4)
+    (property "Reference" "R13" (at 114.3 115.57 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "2.49k" (at 114.3 118.11 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 114.554 119.634 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 114.3 120.65 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RT0603DRE072K49L" (at 114.3 120.65 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 40064330-1af5-4413-a1ec-6af3a0586598))
+    (pin "2" (uuid 462ea222-73ad-4847-9b24-844ec39d56cc))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R13") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 86.36 144.78 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid eb3ad263-4383-4973-9b0c-7fde9947d384)
+    (property "Reference" "C9" (at 86.36 138.43 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "105p" (at 86.36 140.97 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 90.17 143.8148 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 86.36 144.78 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CC0603KPX7R9BB101" (at 86.36 144.78 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid c0d232bf-caa5-496c-be11-29715961f4b8))
+    (pin "2" (uuid 043e1f6d-0813-4628-afef-ffc4fcbccf1f))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "C9") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 88.9 34.29 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid f0ce9f99-0d2b-47ad-955e-a115d88f2423)
+    (property "Reference" "R3" (at 88.9 29.21 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "9.09k" (at 88.9 31.75 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 89.154 33.274 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 88.9 34.29 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RT0603DRE079K09L" (at 88.9 34.29 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 1eee0348-dd93-450c-aca6-b1e8b82b5960))
+    (pin "2" (uuid afd92469-5138-4218-8ce8-ca192ddbe89a))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f"
+          (reference "R3") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/684072e7-f538-4528-bfb8-b14c31b5b1f0"
+          (reference "R1") (unit 1)
+        )
+      )
+    )
+  )
+)
diff --git a/hardware/board_connectors.kicad_sch b/hardware/board_connectors.kicad_sch
new file mode 100755 (executable)
index 0000000..867e8da
--- /dev/null
@@ -0,0 +1,2475 @@
+(kicad_sch (version 20230121) (generator eeschema)
+
+  (uuid db8d8453-0abf-41fc-8f7a-e13f9795dae1)
+
+  (paper "A4")
+
+  (title_block
+    (title "Board and I/O connectors")
+    (date "2023-08-08")
+    (company "bitgloo")
+    (comment 1 "Released under the CERN Open Hardware Licence Version 2 - Strongly Reciprocal")
+  )
+
+  (lib_symbols
+    (symbol "Connector:USB_B_Micro" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)
+      (property "Reference" "J" (at -5.08 11.43 0)
+        (effects (font (size 1.27 1.27)) (justify left))
+      )
+      (property "Value" "USB_B_Micro" (at -5.08 8.89 0)
+        (effects (font (size 1.27 1.27)) (justify left))
+      )
+      (property "Footprint" "" (at 3.81 -1.27 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 3.81 -1.27 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "connector USB micro" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "USB Micro Type B connector" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "USB*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "USB_B_Micro_0_1"
+        (rectangle (start -5.08 -7.62) (end 5.08 7.62)
+          (stroke (width 0.254) (type default))
+          (fill (type background))
+        )
+        (circle (center -3.81 2.159) (radius 0.635)
+          (stroke (width 0.254) (type default))
+          (fill (type outline))
+        )
+        (circle (center -0.635 3.429) (radius 0.381)
+          (stroke (width 0.254) (type default))
+          (fill (type outline))
+        )
+        (rectangle (start -0.127 -7.62) (end 0.127 -6.858)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.905 2.159)
+            (xy 0.635 2.159)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -3.175 2.159)
+            (xy -2.54 2.159)
+            (xy -1.27 3.429)
+            (xy -0.635 3.429)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -2.54 2.159)
+            (xy -1.905 2.159)
+            (xy -1.27 0.889)
+            (xy 0 0.889)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0.635 2.794)
+            (xy 0.635 1.524)
+            (xy 1.905 2.159)
+            (xy 0.635 2.794)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type outline))
+        )
+        (polyline
+          (pts
+            (xy -4.318 5.588)
+            (xy -1.778 5.588)
+            (xy -2.032 4.826)
+            (xy -4.064 4.826)
+            (xy -4.318 5.588)
+          )
+          (stroke (width 0) (type default))
+          (fill (type outline))
+        )
+        (polyline
+          (pts
+            (xy -4.699 5.842)
+            (xy -4.699 5.588)
+            (xy -4.445 4.826)
+            (xy -4.445 4.572)
+            (xy -1.651 4.572)
+            (xy -1.651 4.826)
+            (xy -1.397 5.588)
+            (xy -1.397 5.842)
+            (xy -4.699 5.842)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 0.254 1.27) (end -0.508 0.508)
+          (stroke (width 0.254) (type default))
+          (fill (type outline))
+        )
+        (rectangle (start 5.08 -5.207) (end 4.318 -4.953)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 5.08 -2.667) (end 4.318 -2.413)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 5.08 -0.127) (end 4.318 0.127)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 5.08 4.953) (end 4.318 5.207)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "USB_B_Micro_1_1"
+        (pin power_out line (at 7.62 5.08 180) (length 2.54)
+          (name "VBUS" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin bidirectional line (at 7.62 -2.54 180) (length 2.54)
+          (name "D-" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+        (pin bidirectional line (at 7.62 0 180) (length 2.54)
+          (name "D+" (effects (font (size 1.27 1.27))))
+          (number "3" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 -5.08 180) (length 2.54)
+          (name "ID" (effects (font (size 1.27 1.27))))
+          (number "4" (effects (font (size 1.27 1.27))))
+        )
+        (pin power_out line (at 0 -10.16 90) (length 2.54)
+          (name "GND" (effects (font (size 1.27 1.27))))
+          (number "5" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -2.54 -10.16 90) (length 2.54)
+          (name "Shield" (effects (font (size 1.27 1.27))))
+          (number "6" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Connector_Audio:AudioJack2_Ground" (in_bom yes) (on_board yes)
+      (property "Reference" "J" (at 0 8.89 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "AudioJack2_Ground" (at 0 6.35 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "audio jack receptacle mono phone headphone TS connector" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Audio Jack, 2 Poles (Mono / TS), Grounded Sleeve" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "Jack*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "AudioJack2_Ground_0_1"
+        (rectangle (start -2.54 -2.54) (end -3.81 0)
+          (stroke (width 0.254) (type default))
+          (fill (type outline))
+        )
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0.635 -0.635)
+            (xy 1.27 0)
+            (xy 2.54 0)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 2.54 2.54)
+            (xy -0.635 2.54)
+            (xy -0.635 0)
+            (xy -1.27 -0.635)
+            (xy -1.905 0)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 2.54 3.81) (end -2.54 -2.54)
+          (stroke (width 0.254) (type default))
+          (fill (type background))
+        )
+      )
+      (symbol "AudioJack2_Ground_1_1"
+        (pin passive line (at 0 -5.08 90) (length 2.54)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "G" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 5.08 2.54 180) (length 2.54)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "S" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 5.08 0 180) (length 2.54)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "T" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Connector_Generic:Conn_02x03_Odd_Even" (pin_names (offset 1.016) hide) (in_bom yes) (on_board yes)
+      (property "Reference" "J" (at 1.27 5.08 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "Conn_02x03_Odd_Even" (at 1.27 -5.08 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "connector" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Generic connector, double row, 02x03, odd/even pin numbering scheme (row 1 odd numbers, row 2 even numbers), script generated (kicad-library-utils/schlib/autogen/connector/)" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "Connector*:*_2x??_*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "Conn_02x03_Odd_Even_1_1"
+        (rectangle (start -1.27 -2.413) (end 0 -2.667)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start -1.27 0.127) (end 0 -0.127)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start -1.27 2.667) (end 0 2.413)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start -1.27 3.81) (end 3.81 -3.81)
+          (stroke (width 0.254) (type default))
+          (fill (type background))
+        )
+        (rectangle (start 3.81 -2.413) (end 2.54 -2.667)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 3.81 0.127) (end 2.54 -0.127)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 3.81 2.667) (end 2.54 2.413)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (pin passive line (at -5.08 2.54 0) (length 3.81)
+          (name "Pin_1" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 2.54 180) (length 3.81)
+          (name "Pin_2" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -5.08 0 0) (length 3.81)
+          (name "Pin_3" (effects (font (size 1.27 1.27))))
+          (number "3" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 0 180) (length 3.81)
+          (name "Pin_4" (effects (font (size 1.27 1.27))))
+          (number "4" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -5.08 -2.54 0) (length 3.81)
+          (name "Pin_5" (effects (font (size 1.27 1.27))))
+          (number "5" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 -2.54 180) (length 3.81)
+          (name "Pin_6" (effects (font (size 1.27 1.27))))
+          (number "6" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Connector_Generic:Conn_02x10_Odd_Even" (pin_names (offset 1.016) hide) (in_bom yes) (on_board yes)
+      (property "Reference" "J" (at 1.27 12.7 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "Conn_02x10_Odd_Even" (at 1.27 -15.24 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "connector" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Generic connector, double row, 02x10, odd/even pin numbering scheme (row 1 odd numbers, row 2 even numbers), script generated (kicad-library-utils/schlib/autogen/connector/)" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "Connector*:*_2x??_*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "Conn_02x10_Odd_Even_1_1"
+        (rectangle (start -1.27 -12.573) (end 0 -12.827)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start -1.27 -10.033) (end 0 -10.287)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start -1.27 -7.493) (end 0 -7.747)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start -1.27 -4.953) (end 0 -5.207)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start -1.27 -2.413) (end 0 -2.667)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start -1.27 0.127) (end 0 -0.127)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start -1.27 2.667) (end 0 2.413)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start -1.27 5.207) (end 0 4.953)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start -1.27 7.747) (end 0 7.493)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start -1.27 10.287) (end 0 10.033)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start -1.27 11.43) (end 3.81 -13.97)
+          (stroke (width 0.254) (type default))
+          (fill (type background))
+        )
+        (rectangle (start 3.81 -12.573) (end 2.54 -12.827)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 3.81 -10.033) (end 2.54 -10.287)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 3.81 -7.493) (end 2.54 -7.747)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 3.81 -4.953) (end 2.54 -5.207)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 3.81 -2.413) (end 2.54 -2.667)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 3.81 0.127) (end 2.54 -0.127)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 3.81 2.667) (end 2.54 2.413)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 3.81 5.207) (end 2.54 4.953)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 3.81 7.747) (end 2.54 7.493)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 3.81 10.287) (end 2.54 10.033)
+          (stroke (width 0.1524) (type default))
+          (fill (type none))
+        )
+        (pin passive line (at -5.08 10.16 0) (length 3.81)
+          (name "Pin_1" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 0 180) (length 3.81)
+          (name "Pin_10" (effects (font (size 1.27 1.27))))
+          (number "10" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -5.08 -2.54 0) (length 3.81)
+          (name "Pin_11" (effects (font (size 1.27 1.27))))
+          (number "11" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 -2.54 180) (length 3.81)
+          (name "Pin_12" (effects (font (size 1.27 1.27))))
+          (number "12" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -5.08 -5.08 0) (length 3.81)
+          (name "Pin_13" (effects (font (size 1.27 1.27))))
+          (number "13" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 -5.08 180) (length 3.81)
+          (name "Pin_14" (effects (font (size 1.27 1.27))))
+          (number "14" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -5.08 -7.62 0) (length 3.81)
+          (name "Pin_15" (effects (font (size 1.27 1.27))))
+          (number "15" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 -7.62 180) (length 3.81)
+          (name "Pin_16" (effects (font (size 1.27 1.27))))
+          (number "16" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -5.08 -10.16 0) (length 3.81)
+          (name "Pin_17" (effects (font (size 1.27 1.27))))
+          (number "17" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 -10.16 180) (length 3.81)
+          (name "Pin_18" (effects (font (size 1.27 1.27))))
+          (number "18" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -5.08 -12.7 0) (length 3.81)
+          (name "Pin_19" (effects (font (size 1.27 1.27))))
+          (number "19" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 10.16 180) (length 3.81)
+          (name "Pin_2" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 -12.7 180) (length 3.81)
+          (name "Pin_20" (effects (font (size 1.27 1.27))))
+          (number "20" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -5.08 7.62 0) (length 3.81)
+          (name "Pin_3" (effects (font (size 1.27 1.27))))
+          (number "3" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 7.62 180) (length 3.81)
+          (name "Pin_4" (effects (font (size 1.27 1.27))))
+          (number "4" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -5.08 5.08 0) (length 3.81)
+          (name "Pin_5" (effects (font (size 1.27 1.27))))
+          (number "5" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 5.08 180) (length 3.81)
+          (name "Pin_6" (effects (font (size 1.27 1.27))))
+          (number "6" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -5.08 2.54 0) (length 3.81)
+          (name "Pin_7" (effects (font (size 1.27 1.27))))
+          (number "7" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 7.62 2.54 180) (length 3.81)
+          (name "Pin_8" (effects (font (size 1.27 1.27))))
+          (number "8" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -5.08 0 0) (length 3.81)
+          (name "Pin_9" (effects (font (size 1.27 1.27))))
+          (number "9" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Device:R_US" (pin_numbers hide) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "R" (at 2.54 0 90)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "R_US" (at -2.54 0 90)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 1.016 -0.254 90)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "R res resistor" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Resistor, US symbol" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "R_*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "R_US_0_1"
+        (polyline
+          (pts
+            (xy 0 -2.286)
+            (xy 0 -2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.286)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 -0.762)
+            (xy 1.016 -1.143)
+            (xy 0 -1.524)
+            (xy -1.016 -1.905)
+            (xy 0 -2.286)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0.762)
+            (xy 1.016 0.381)
+            (xy 0 0)
+            (xy -1.016 -0.381)
+            (xy 0 -0.762)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.286)
+            (xy 1.016 1.905)
+            (xy 0 1.524)
+            (xy -1.016 1.143)
+            (xy 0 0.762)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "R_US_1_1"
+        (pin passive line (at 0 3.81 270) (length 1.27)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 0 -3.81 90) (length 1.27)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Diode:ESD5Zxx" (pin_numbers hide) (pin_names hide) (in_bom yes) (on_board yes)
+      (property "Reference" "D" (at 0 2.54 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "ESD5Zxx" (at 0 -2.54 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "Diode_SMD:D_SOD-523" (at 0 -4.445 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "https://www.onsemi.com/pdf/datasheet/esd5z2.5t1-d.pdf" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "esd tvs unidirectional diode" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "ESD Protection Diode, SOD-523" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "D?SOD?523*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "ESD5Zxx_0_1"
+        (polyline
+          (pts
+            (xy 1.27 0)
+            (xy -1.27 0)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.27 -1.27)
+            (xy -1.27 1.27)
+            (xy -0.762 1.27)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 1.27 -1.27)
+            (xy 1.27 1.27)
+            (xy -1.27 0)
+            (xy 1.27 -1.27)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "ESD5Zxx_1_1"
+        (pin passive line (at -3.81 0 0) (length 2.54)
+          (name "K" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 3.81 0 180) (length 2.54)
+          (name "A" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Diode:ESD9B5.0ST5G" (pin_numbers hide) (pin_names (offset 1.016) hide) (in_bom yes) (on_board yes)
+      (property "Reference" "D" (at 0 2.54 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "ESD9B5.0ST5G" (at 0 -2.54 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "Diode_SMD:D_SOD-923" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "https://www.onsemi.com/pub/Collateral/ESD9B-D.PDF" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "diode TVS ESD" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "ESD protection diode, 5.0Vrwm, SOD-923" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "D*SOD?923*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "ESD9B5.0ST5G_0_1"
+        (polyline
+          (pts
+            (xy 1.27 0)
+            (xy -1.27 0)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -2.54 -1.27)
+            (xy 0 0)
+            (xy -2.54 1.27)
+            (xy -2.54 -1.27)
+          )
+          (stroke (width 0.2032) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0.508 1.27)
+            (xy 0 1.27)
+            (xy 0 -1.27)
+            (xy -0.508 -1.27)
+          )
+          (stroke (width 0.2032) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 2.54 1.27)
+            (xy 2.54 -1.27)
+            (xy 0 0)
+            (xy 2.54 1.27)
+          )
+          (stroke (width 0.2032) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "ESD9B5.0ST5G_1_1"
+        (pin passive line (at -3.81 0 0) (length 2.54)
+          (name "A1" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 3.81 0 180) (length 2.54)
+          (name "A2" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "power:GND" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 -6.35 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "GND" (at 0 -3.81 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"GND\" , ground" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "GND_0_1"
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 -1.27)
+            (xy 1.27 -1.27)
+            (xy 0 -2.54)
+            (xy -1.27 -1.27)
+            (xy 0 -1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "GND_1_1"
+        (pin power_in line (at 0 0 270) (length 0) hide
+          (name "GND" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "power:VBUS" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 -3.81 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "VBUS" (at 0 3.81 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"VBUS\"" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "VBUS_0_1"
+        (polyline
+          (pts
+            (xy -0.762 1.27)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.54)
+            (xy 0.762 1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "VBUS_1_1"
+        (pin power_in line (at 0 0 90) (length 0) hide
+          (name "VBUS" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "power:VCC" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 -3.81 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "VCC" (at 0 3.81 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"VCC\"" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "VCC_0_1"
+        (polyline
+          (pts
+            (xy -0.762 1.27)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.54)
+            (xy 0.762 1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "VCC_1_1"
+        (pin power_in line (at 0 0 90) (length 0) hide
+          (name "VCC" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "power:VDDA" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 -3.81 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "VDDA" (at 0 3.81 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"VDDA\"" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "VDDA_0_1"
+        (polyline
+          (pts
+            (xy -0.762 1.27)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.54)
+            (xy 0.762 1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "VDDA_1_1"
+        (pin power_in line (at 0 0 90) (length 0) hide
+          (name "VDDA" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+  )
+
+
+  (junction (at 64.77 153.67) (diameter 0) (color 0 0 0 0)
+    (uuid 341d7753-7745-4e80-b0d0-3726c431b28c)
+  )
+  (junction (at 194.31 50.8) (diameter 0) (color 0 0 0 0)
+    (uuid 626c56db-b0b0-4ac0-9ec0-63491613099f)
+  )
+  (junction (at 73.66 156.21) (diameter 0) (color 0 0 0 0)
+    (uuid 6c193457-5f43-4341-87b8-71d0ba6cac19)
+  )
+  (junction (at 194.31 81.28) (diameter 0) (color 0 0 0 0)
+    (uuid 6e53c4d1-157e-4543-982e-4f7ac4a3c3bf)
+  )
+  (junction (at 257.81 68.58) (diameter 0) (color 0 0 0 0)
+    (uuid 8172661d-4213-4d2c-9ece-dadb6d46dd4d)
+  )
+  (junction (at 55.88 148.59) (diameter 0) (color 0 0 0 0)
+    (uuid d21c0f1c-b9d1-4039-b9b1-8a0ef306ea0b)
+  )
+  (junction (at 194.31 113.03) (diameter 0) (color 0 0 0 0)
+    (uuid d92d6a8f-280b-46e1-8666-fc542978c136)
+  )
+  (junction (at 257.81 71.12) (diameter 0) (color 0 0 0 0)
+    (uuid f29364eb-6f9a-44ee-9ada-b9530a4818e5)
+  )
+
+  (no_connect (at 105.41 60.96) (uuid 02e14ecb-281f-4f3d-8f5b-f03c2be2774a))
+  (no_connect (at 52.07 68.58) (uuid 10837e33-4e68-49e7-8045-56d254f760c3))
+  (no_connect (at 39.37 53.34) (uuid 1276f9f6-f1d7-4db1-8b1a-ed942a730c6f))
+  (no_connect (at 92.71 87.63) (uuid 141fb05e-0fc8-4331-94fb-6e594633c365))
+  (no_connect (at 105.41 92.71) (uuid 175f6a1e-b8a8-4cb2-9876-72aa853a9c21))
+  (no_connect (at 105.41 107.95) (uuid 19a07d42-bbb8-4ed1-b026-dd44dbce7745))
+  (no_connect (at 105.41 95.25) (uuid 1ab53dd2-9b40-44f6-8a0b-f4ff48f61a95))
+  (no_connect (at 105.41 63.5) (uuid 2946a5ed-dea8-436d-a403-f12e42cffda9))
+  (no_connect (at 92.71 60.96) (uuid 2c37091f-0b69-4f11-b40b-a8f2b99fdfef))
+  (no_connect (at 39.37 58.42) (uuid 2e6c9d78-135a-4139-881c-2e15369bbe4e))
+  (no_connect (at 52.07 50.8) (uuid 340f5bad-722f-4dd7-ad02-e0296963f516))
+  (no_connect (at 105.41 85.09) (uuid 342f6dff-72d6-42c2-9862-e253d4c4fee3))
+  (no_connect (at 105.41 105.41) (uuid 37641353-8510-4784-bcd9-6d0af8a34902))
+  (no_connect (at 105.41 71.12) (uuid 446b82c6-eaf6-4c61-93a4-7025f35cadf3))
+  (no_connect (at 92.71 71.12) (uuid 4ba37343-3f89-4d5d-9e27-f7e4e5789126))
+  (no_connect (at 52.07 48.26) (uuid 56e11a62-be7d-49fd-a1ee-aea7ccccbcb1))
+  (no_connect (at 52.07 71.12) (uuid 57b40ce3-bcb7-40a8-a790-90c07b8d6424))
+  (no_connect (at 92.71 63.5) (uuid 5fe6fbc7-cc4b-49fb-8dd1-41c4b1db3ebe))
+  (no_connect (at 92.71 58.42) (uuid 690fdb80-122c-499d-9875-61e86269071e))
+  (no_connect (at 92.71 95.25) (uuid 799a0e24-3068-4766-b6fe-f2226ac69c95))
+  (no_connect (at 39.37 71.12) (uuid 7a9c0c96-0d24-4f7a-86ae-39e74f01a86c))
+  (no_connect (at 52.07 66.04) (uuid 86d141df-a196-4d3e-ad74-24246dc70d09))
+  (no_connect (at 39.37 66.04) (uuid 9546d86c-4f8e-4a63-8f83-08b4bfa49a3d))
+  (no_connect (at 52.07 53.34) (uuid 9aecb32b-1505-4873-8f4b-724bc72199bc))
+  (no_connect (at 105.41 53.34) (uuid 9b4d6cea-7131-4481-a956-5aa03b6d3247))
+  (no_connect (at 39.37 50.8) (uuid a0863bea-b3bd-4ffd-b3ff-73675473c622))
+  (no_connect (at 105.41 55.88) (uuid a1bf8dbe-78db-4ebe-afec-5180968da477))
+  (no_connect (at 105.41 58.42) (uuid a43bcfe3-77fd-4209-a3ac-b34ec193a4f8))
+  (no_connect (at 105.41 100.33) (uuid a728d9e4-163a-4f3a-9c16-110f3fe011ea))
+  (no_connect (at 39.37 55.88) (uuid a8867314-4c7e-4773-9990-39e904f0e4e5))
+  (no_connect (at 52.07 63.5) (uuid ab373b53-cc53-406d-ad4f-496e243b78a8))
+  (no_connect (at 92.71 50.8) (uuid ba8b447b-c057-4be3-a5ec-c2f04676a218))
+  (no_connect (at 49.53 158.75) (uuid bdbcbbc5-096b-4cb4-b887-0651b9672f1c))
+  (no_connect (at 92.71 85.09) (uuid bf00cb17-b71b-4181-b6b5-10dfce6d0abb))
+  (no_connect (at 105.41 90.17) (uuid c04ce474-d6e3-459c-b2e9-39dff966ac76))
+  (no_connect (at 105.41 97.79) (uuid c195b8d1-0d6c-425c-9278-3f7cf59f372b))
+  (no_connect (at 39.37 68.58) (uuid cc070b88-f5a4-431c-af7e-214f9d773271))
+  (no_connect (at 105.41 87.63) (uuid cd428b6a-8153-4a89-aeac-4e71c52a517b))
+  (no_connect (at 39.37 48.26) (uuid d21adaaa-7a9f-4901-87bd-e27c382495d9))
+  (no_connect (at 105.41 102.87) (uuid d8deda2a-71eb-4ce6-9ae1-49df15c8dea2))
+  (no_connect (at 92.71 90.17) (uuid ddc360d4-d4a8-4189-8b10-d6b9392e045b))
+  (no_connect (at 105.41 68.58) (uuid e8703331-4f08-466b-bc7c-cafd62064081))
+  (no_connect (at 92.71 100.33) (uuid e87f31be-867e-4128-a625-5d1ddf924da5))
+  (no_connect (at 105.41 66.04) (uuid ef1e1548-d018-4bfe-bc4f-9ca3264693d5))
+  (no_connect (at 92.71 68.58) (uuid f3395491-fcf5-4736-a8ee-cbace5525f0e))
+  (no_connect (at 92.71 107.95) (uuid f3fd5de1-4ed9-439d-9dc7-ceb87d290f48))
+
+  (wire (pts (xy 257.81 68.58) (xy 251.46 68.58))
+    (stroke (width 0) (type default))
+    (uuid 01138170-73f9-4823-9506-1738d24bf2a6)
+  )
+  (wire (pts (xy 182.88 110.49) (xy 186.69 110.49))
+    (stroke (width 0) (type default))
+    (uuid 11a34e7f-e14e-457d-b0c7-c16335ff1669)
+  )
+  (wire (pts (xy 257.81 71.12) (xy 257.81 73.66))
+    (stroke (width 0) (type default))
+    (uuid 17c5a835-0904-4d1c-b2f2-e4ae4a6c73df)
+  )
+  (wire (pts (xy 55.88 147.32) (xy 55.88 148.59))
+    (stroke (width 0) (type default))
+    (uuid 1a304211-dc86-4443-9db3-54bb237bf277)
+  )
+  (wire (pts (xy 186.69 78.74) (xy 186.69 91.44))
+    (stroke (width 0) (type default))
+    (uuid 1ba98c24-1533-4438-9210-c16715cabd31)
+  )
+  (wire (pts (xy 86.36 92.71) (xy 92.71 92.71))
+    (stroke (width 0) (type default))
+    (uuid 2160dd6d-268f-46f0-9ba6-785df017111d)
+  )
+  (wire (pts (xy 52.07 55.88) (xy 55.88 55.88))
+    (stroke (width 0) (type default))
+    (uuid 25f541ae-3e1c-4bbb-8af6-684e35c590ec)
+  )
+  (wire (pts (xy 194.31 113.03) (xy 198.12 113.03))
+    (stroke (width 0) (type default))
+    (uuid 271ad314-beff-4448-8bfa-83b95756b2fc)
+  )
+  (wire (pts (xy 257.81 68.58) (xy 257.81 71.12))
+    (stroke (width 0) (type default))
+    (uuid 2a3a9bba-40e4-4f83-a857-2d364199c688)
+  )
+  (wire (pts (xy 194.31 50.8) (xy 198.12 50.8))
+    (stroke (width 0) (type default))
+    (uuid 2c938643-5232-413b-8485-dea4ededa847)
+  )
+  (wire (pts (xy 52.07 58.42) (xy 55.88 58.42))
+    (stroke (width 0) (type default))
+    (uuid 30195f84-a019-40ee-badd-009ad4355ad9)
+  )
+  (wire (pts (xy 88.9 74.93) (xy 88.9 55.88))
+    (stroke (width 0) (type default))
+    (uuid 3960e16a-7fe8-4649-85fc-c52bbb1347a3)
+  )
+  (wire (pts (xy 186.69 110.49) (xy 186.69 123.19))
+    (stroke (width 0) (type default))
+    (uuid 3e933706-f819-47e7-a77a-70b1cc24e18c)
+  )
+  (wire (pts (xy 73.66 156.21) (xy 77.47 156.21))
+    (stroke (width 0) (type default))
+    (uuid 4cec1375-8ab8-4479-9227-a4b3064b5121)
+  )
+  (wire (pts (xy 88.9 53.34) (xy 88.9 44.45))
+    (stroke (width 0) (type default))
+    (uuid 5009ab8f-15ae-4ef9-883e-151f8e97ec07)
+  )
+  (wire (pts (xy 194.31 50.8) (xy 194.31 53.34))
+    (stroke (width 0) (type default))
+    (uuid 5420cddc-ff30-4142-adbb-79d9b2e6ffb1)
+  )
+  (wire (pts (xy 92.71 53.34) (xy 88.9 53.34))
+    (stroke (width 0) (type default))
+    (uuid 562f4726-10c4-40c8-91cc-d9346788ae5a)
+  )
+  (wire (pts (xy 49.53 153.67) (xy 64.77 153.67))
+    (stroke (width 0) (type default))
+    (uuid 56e90dd9-8312-4290-ad05-bb760b9a76e5)
+  )
+  (wire (pts (xy 86.36 48.26) (xy 92.71 48.26))
+    (stroke (width 0) (type default))
+    (uuid 57ff3361-b0f6-4b30-a36d-1df4890f5f08)
+  )
+  (wire (pts (xy 186.69 48.26) (xy 186.69 60.96))
+    (stroke (width 0) (type default))
+    (uuid 59fe13b0-e8f7-4bed-9fc0-30b7c0ce3a14)
+  )
+  (wire (pts (xy 49.53 156.21) (xy 73.66 156.21))
+    (stroke (width 0) (type default))
+    (uuid 5dd8066c-24b3-4f94-b7d6-ff6f778fab5b)
+  )
+  (wire (pts (xy 194.31 81.28) (xy 194.31 83.82))
+    (stroke (width 0) (type default))
+    (uuid 63e5ef50-ebff-405d-a4b1-c89364ac9493)
+  )
+  (wire (pts (xy 49.53 148.59) (xy 55.88 148.59))
+    (stroke (width 0) (type default))
+    (uuid 67898f17-331d-4fd9-8cf3-8cfaae2f4f29)
+  )
+  (wire (pts (xy 194.31 81.28) (xy 198.12 81.28))
+    (stroke (width 0) (type default))
+    (uuid 6b65ae4d-cfd7-4271-b6d9-ebb2d8a2942e)
+  )
+  (wire (pts (xy 44.45 165.1) (xy 41.91 165.1))
+    (stroke (width 0) (type default))
+    (uuid 91824ee3-2983-424e-9c63-5d5c1cd56dd7)
+  )
+  (wire (pts (xy 182.88 48.26) (xy 186.69 48.26))
+    (stroke (width 0) (type default))
+    (uuid 94be8937-f540-41ed-8cc3-984047c8cc62)
+  )
+  (wire (pts (xy 194.31 113.03) (xy 194.31 115.57))
+    (stroke (width 0) (type default))
+    (uuid 94ee4d1a-3e5b-4eeb-848b-5d84e6300b27)
+  )
+  (wire (pts (xy 257.81 66.04) (xy 257.81 68.58))
+    (stroke (width 0) (type default))
+    (uuid 9772b1fc-e148-4b2d-9844-4d80b227df7a)
+  )
+  (wire (pts (xy 39.37 163.83) (xy 39.37 165.1))
+    (stroke (width 0) (type default))
+    (uuid 9aa8dbd0-d333-4d47-a312-48f4ff8bf29e)
+  )
+  (wire (pts (xy 55.88 148.59) (xy 55.88 162.56))
+    (stroke (width 0) (type default))
+    (uuid 9eaf94c6-4a3c-41bf-b91d-690f57d4b0c6)
+  )
+  (wire (pts (xy 257.81 66.04) (xy 251.46 66.04))
+    (stroke (width 0) (type default))
+    (uuid b1266ec8-3985-494d-96f0-283ff2d6bd6d)
+  )
+  (wire (pts (xy 44.45 166.37) (xy 44.45 165.1))
+    (stroke (width 0) (type default))
+    (uuid b1d7b782-2212-44d4-a34a-e41b80e37307)
+  )
+  (wire (pts (xy 41.91 165.1) (xy 41.91 163.83))
+    (stroke (width 0) (type default))
+    (uuid b8f10e73-35e2-4840-85fe-e875b97f9c85)
+  )
+  (wire (pts (xy 64.77 153.67) (xy 64.77 162.56))
+    (stroke (width 0) (type default))
+    (uuid bbd6bee0-d345-4bca-82f0-a75fe857d8b5)
+  )
+  (wire (pts (xy 52.07 60.96) (xy 59.69 60.96))
+    (stroke (width 0) (type default))
+    (uuid c092cdd3-feef-47ef-8d61-8f353c03443f)
+  )
+  (wire (pts (xy 251.46 71.12) (xy 257.81 71.12))
+    (stroke (width 0) (type default))
+    (uuid c206ebf0-12f3-43fe-81a2-9074704a3577)
+  )
+  (wire (pts (xy 83.82 66.04) (xy 92.71 66.04))
+    (stroke (width 0) (type default))
+    (uuid c2becea2-9330-4c82-8464-0fa054673117)
+  )
+  (wire (pts (xy 55.88 58.42) (xy 55.88 74.93))
+    (stroke (width 0) (type default))
+    (uuid c7566461-9a25-492b-a37e-d1ec5a366f02)
+  )
+  (wire (pts (xy 86.36 97.79) (xy 92.71 97.79))
+    (stroke (width 0) (type default))
+    (uuid c9200dbc-f5e9-414c-b83b-ff0c88df91c9)
+  )
+  (wire (pts (xy 182.88 81.28) (xy 194.31 81.28))
+    (stroke (width 0) (type default))
+    (uuid c9941a1b-b9e3-48a9-9b29-e20195bb0bbf)
+  )
+  (wire (pts (xy 182.88 50.8) (xy 194.31 50.8))
+    (stroke (width 0) (type default))
+    (uuid d790cd63-8757-4a83-abf9-f94ef01f8125)
+  )
+  (wire (pts (xy 182.88 113.03) (xy 194.31 113.03))
+    (stroke (width 0) (type default))
+    (uuid e4ba08dc-824b-4666-89d5-d26151c1e18e)
+  )
+  (wire (pts (xy 88.9 55.88) (xy 92.71 55.88))
+    (stroke (width 0) (type default))
+    (uuid e58cfdb7-876b-40f8-80e8-d0b645634cf8)
+  )
+  (wire (pts (xy 64.77 153.67) (xy 77.47 153.67))
+    (stroke (width 0) (type default))
+    (uuid ef4a0796-a016-4a30-86f1-eff3ba5a7480)
+  )
+  (wire (pts (xy 182.88 78.74) (xy 186.69 78.74))
+    (stroke (width 0) (type default))
+    (uuid f0474fd5-8895-4177-81b4-c9fefc3d8571)
+  )
+  (wire (pts (xy 73.66 156.21) (xy 73.66 162.56))
+    (stroke (width 0) (type default))
+    (uuid fa4c2aa9-82a5-46bc-a149-c4d6c9dda92a)
+  )
+
+  (text "USB to host computer" (at 49.53 125.73 0)
+    (effects (font (size 2.54 2.54) (thickness 0.254) bold) (justify left bottom))
+    (uuid 0db574be-a011-4b48-808c-f57996c7a78d)
+  )
+  (text "PA4" (at 86.36 97.79 0)
+    (effects (font (size 1.27 1.27)) (justify left bottom))
+    (uuid 1477f792-83df-4152-8e8b-7586d1e4836f)
+  )
+  (text "ST Morpho connector to NUCLEO" (at 40.64 30.48 0)
+    (effects (font (size 2.54 2.54) (thickness 0.254) bold) (justify left bottom))
+    (uuid 2502b80e-2d58-4f05-9c42-ec05876180ed)
+  )
+  (text "PA5" (at 59.69 60.96 0)
+    (effects (font (size 1.27 1.27)) (justify right bottom))
+    (uuid 43c6f8c7-26c2-4658-b3be-4ca33fffb425)
+  )
+  (text "PA0" (at 86.36 92.71 0)
+    (effects (font (size 1.27 1.27)) (justify left bottom))
+    (uuid 4ed82dd3-23b9-4d09-9068-6c7e0bb936a8)
+  )
+  (text "PA12\nPA11" (at 22.86 64.77 0)
+    (effects (font (size 1.27 1.27)) (justify left bottom))
+    (uuid 93bd8bd3-5680-417a-b52a-42fafd50a3ad)
+  )
+  (text "External signal connectors" (at 186.69 30.48 0)
+    (effects (font (size 2.54 2.54) (thickness 0.254) bold) (justify left bottom))
+    (uuid ce7bd745-4a1d-45e1-8c00-36ffa40fc6f2)
+  )
+
+  (global_label "SIGNAL_OUT" (shape input) (at 198.12 81.28 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid 10d89912-e360-4ee2-9758-91137e1628f1)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 212.1535 81.28 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+  (global_label "PC1{slash}POT2" (shape input) (at 92.71 102.87 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid 1f63dc8c-8b72-4fbf-911f-67600cb72c69)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 80.4393 102.7906 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "SIGNAL_OUT_MCU" (shape input) (at 86.36 97.79 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid 2c4389f5-c05b-46ec-bcf2-697a8b6668fc)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 67.307 97.79 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "SIGNAL_IN" (shape input) (at 198.12 113.03 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid 32d30b1c-b7c1-4411-8e76-b9502fe536db)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 210.4602 113.03 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+  (global_label "GENERATOR" (shape input) (at 198.12 50.8 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid 48ac5c15-d529-48d1-b279-d5574d115fbc)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 211.851 50.8 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+  (global_label "GENERATOR_MCU" (shape input) (at 59.69 60.96 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid 4bb74090-1e71-4ca0-8b6b-b61fb5a02492)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 78.4405 60.96 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+  (global_label "PC10{slash}LED_R" (shape input) (at 105.41 48.26 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid 57cd2906-d9ab-4fca-999f-94b7fa14fb20)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 119.7974 48.1806 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+  (global_label "SIGNAL_OUT" (shape input) (at 238.76 68.58 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid 660918bc-91c1-4bfa-a6fa-4e80f52f503a)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 224.7265 68.58 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "PC12{slash}LED_B" (shape input) (at 105.41 50.8 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid 68048f48-82b3-4e13-bbd3-31d79d1afe3e)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 119.7974 50.7206 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+  (global_label "PC11{slash}LED_G" (shape input) (at 86.36 48.26 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid 7f283649-04c7-43a8-ac25-96e75dac14ec)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 71.9726 48.1806 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "USB_D_P" (shape input) (at 77.47 153.67 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid 80ed805e-29a0-4561-ad7e-28956861b9ec)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 88.1683 153.5906 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+  (global_label "PC0{slash}POT1" (shape input) (at 92.71 105.41 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid 8492e551-add7-4c91-91ab-521e53e1e74d)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 80.4393 105.3306 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "GENERATOR" (shape input) (at 238.76 66.04 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid 89067483-99cd-4587-8e11-8f7db1996941)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 225.029 66.04 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "USB_D_N" (shape input) (at 39.37 63.5 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid af2aef39-3174-4b72-85b8-71cb0a568e2e)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 28.6112 63.4206 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "SIGNAL_IN" (shape input) (at 238.76 71.12 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid b34a2611-f563-4cec-b5fc-edfa38c6e9be)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 226.4198 71.12 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "SIGNAL_IN_MCU" (shape input) (at 86.36 92.71 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid e3973062-506c-431f-92b9-9cb618502756)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 69.0003 92.71 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "USB_D_P" (shape input) (at 39.37 60.96 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid eaeb3048-ee91-4825-b6fa-5cc8b40a6ff8)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 28.6717 60.8806 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "USB_D_N" (shape input) (at 77.47 156.21 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid f9fe0c63-fae4-43d0-9dd0-e7229fbaf9a5)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 88.2288 156.1306 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+
+  (symbol (lib_id "Diode:ESD9B5.0ST5G") (at 55.88 166.37 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 0dee484c-3850-4bfd-ac52-a058e497e313)
+    (property "Reference" "D4" (at 58.42 165.735 90)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Value" "DF2B7AFS,L3M" (at 58.42 168.275 90)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Footprint" "Diode_SMD:D_SOD-923" (at 55.88 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "https://www.onsemi.com/pub/Collateral/ESD9B-D.PDF" (at 55.88 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "DF2B7AFS,L3M" (at 55.88 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 8eb33409-faa8-40d4-9d7a-18638942d26f))
+    (pin "2" (uuid 5ec6112b-76d7-490e-bb18-3bc29d4108df))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "D4") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 64.77 170.18 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 1a32f1bc-ab0d-4d13-a98a-8d0cda7ca98f)
+    (property "Reference" "#PWR038" (at 64.77 176.53 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 64.77 175.26 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 64.77 170.18 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 64.77 170.18 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid e396a3f6-dccc-40db-93ad-733b024d4813))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR038") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR064") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 186.69 91.44 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 202c75b6-1292-4802-8ef4-b35ec29dbc4f)
+    (property "Reference" "#PWR034" (at 186.69 97.79 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 186.69 96.52 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 186.69 91.44 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 186.69 91.44 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 284ad767-8db3-4db9-a2fe-1e95fd1f704c))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR034") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR056") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 257.81 73.66 0) (mirror y) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 27814364-c174-4bf5-8304-524845c1fb46)
+    (property "Reference" "#PWR036" (at 257.81 80.01 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 257.81 78.74 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 257.81 73.66 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 257.81 73.66 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 7c310584-25a5-4ff3-8943-9974f60654b9))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR036") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR052") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 186.69 60.96 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 2ae9fd91-8ea4-49c9-8c30-b20f94310ca0)
+    (property "Reference" "#PWR032" (at 186.69 67.31 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 186.69 66.04 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 186.69 60.96 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 186.69 60.96 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 8d71c6d1-8221-4843-846e-8f0e278eb339))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR032") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR049") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Diode:ESD9B5.0ST5G") (at 64.77 166.37 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 2f36a1b8-dd88-4fdd-80cc-b6a7d1336b74)
+    (property "Reference" "D5" (at 67.31 165.735 90)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Value" "DF2B7AFS,L3M" (at 67.31 168.275 90)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Footprint" "Diode_SMD:D_SOD-923" (at 64.77 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "https://www.onsemi.com/pub/Collateral/ESD9B-D.PDF" (at 64.77 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "DF2B7AFS,L3M" (at 64.77 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid c8365dfe-a49f-480a-ac00-dbd50c440bcb))
+    (pin "2" (uuid 64e434b0-e31c-4a55-8804-dcbc6cfc24ad))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "D5") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Connector_Audio:AudioJack2_Ground") (at 177.8 81.28 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 2f551abf-6373-4e81-b42b-ccb46e78a8d3)
+    (property "Reference" "J5" (at 177.165 73.66 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "AudioJack2_Ground" (at 177.165 76.2 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Connector_Audio:Jack_3.5mm_CUI_SJ-3523-SMT_Horizontal" (at 177.8 81.28 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 177.8 81.28 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "SJ-3523-SMT-TR" (at 177.8 81.28 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "G" (uuid 64e15694-9c93-43bc-9414-b7987c45d427))
+    (pin "S" (uuid 7d01e566-80c6-4eac-aacf-ef6f563b8f09))
+    (pin "T" (uuid 56e1b378-8db4-4e6f-9dc6-a57b418bb3c3))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "J5") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Diode:ESD5Zxx") (at 194.31 119.38 270) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 2fddc873-d43d-46d7-9c1a-d0acddc1a966)
+    (property "Reference" "D3" (at 196.85 118.745 90)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "ESD5Zxx" (at 196.85 121.285 90)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Diode_SMD:D_SOD-523" (at 189.865 119.38 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "https://www.onsemi.com/pdf/datasheet/esd5z2.5t1-d.pdf" (at 194.31 119.38 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "ESD5Z2.5T1G" (at 194.31 119.38 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid b3573e45-9382-43b1-8a68-7846ffae5f73))
+    (pin "2" (uuid 5da17751-d15c-4093-a954-a0db73824f5b))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "D3") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 186.69 123.19 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 354ed183-c965-45c7-b7a5-eea091706c94)
+    (property "Reference" "#PWR034" (at 186.69 129.54 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 186.69 128.27 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 186.69 123.19 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 186.69 123.19 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid b7469cda-ddbd-4179-bd91-61557e068358))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR034") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR059") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Connector_Generic:Conn_02x10_Odd_Even") (at 97.79 95.25 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 3c409af7-d0f8-44c1-a8e3-ea5a071f1b52)
+    (property "Reference" "J3" (at 99.06 78.74 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "Conn_02x10_Odd_Even" (at 99.06 81.28 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Footprint" "Connector_PinHeader_2.54mm:PinHeader_2x10_P2.54mm_Vertical" (at 97.79 95.25 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 97.79 95.25 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "PPPC102LFBN-RC" (at 97.79 95.25 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 0eaaccb3-4b12-44db-83b1-5795c548f312))
+    (pin "10" (uuid 8420daf5-6660-46ff-b47f-d0a303cde603))
+    (pin "11" (uuid 0ab8500b-cc2a-4ae9-a58c-d44e801ce050))
+    (pin "12" (uuid 5fbc71d4-6912-4646-b0c8-4215639357b2))
+    (pin "13" (uuid 99d675e1-3c2b-4166-a68d-b7f713e99b1b))
+    (pin "14" (uuid 7c75078d-26ce-4e95-aef4-d5a9f894ba06))
+    (pin "15" (uuid 65e269db-f019-4159-baa3-df49b6a67013))
+    (pin "16" (uuid cb136bb4-f8ee-4a36-b940-67d90d7ac3c3))
+    (pin "17" (uuid 542d92fe-07c6-4847-b692-9b61c5a4b399))
+    (pin "18" (uuid a4b31083-5779-4119-9876-ca34df3ee275))
+    (pin "19" (uuid a901d116-cddd-49ac-b209-8cae1d337d62))
+    (pin "2" (uuid a1401b6a-48c7-424b-95da-6d50c6441453))
+    (pin "20" (uuid c03be84e-b08e-4bff-aab1-97fefeea019a))
+    (pin "3" (uuid 1c87293d-1af2-4b32-9cd4-4035572955bb))
+    (pin "4" (uuid 9effd2fb-426e-4603-88e7-970d67013313))
+    (pin "5" (uuid 0a3c6c04-c97f-4254-aae8-e6650395030d))
+    (pin "6" (uuid 1b12880f-0c62-44a8-bae8-fe1fd95c803b))
+    (pin "7" (uuid 807a616b-4a4f-4c01-9032-c6ec9acc3108))
+    (pin "8" (uuid 782d5277-e656-4ca1-8362-b913ee4a59b2))
+    (pin "9" (uuid d5dfdb19-cc97-4ffe-a210-0335b2104f0b))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "J3") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "J6") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 44.45 166.37 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 3c44300d-4194-4273-ac1c-8e16d11cb43d)
+    (property "Reference" "#PWR038" (at 44.45 172.72 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 44.45 171.45 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 44.45 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 44.45 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 443d6396-1ec5-432d-a5b2-633d93cdeb5b))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR038") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR062") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 55.88 170.18 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 3d2f6da2-64d6-4af2-8092-72a600be7d57)
+    (property "Reference" "#PWR038" (at 55.88 176.53 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 55.88 175.26 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 55.88 170.18 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 55.88 170.18 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid e65c94de-6862-4a12-a15c-4e41fce7374f))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR038") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR063") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 177.8 86.36 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no)
+    (uuid 3d4813c6-9f98-46fc-aee5-70acb4705268)
+    (property "Reference" "#PWR032" (at 177.8 92.71 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 177.8 91.44 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 177.8 86.36 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 177.8 86.36 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 38543618-7202-42f9-a573-e07a171d2113))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR032") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR055") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:VBUS") (at 55.88 147.32 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 3f05b989-e02f-490f-a033-8a49f2e32908)
+    (property "Reference" "#PWR061" (at 55.88 151.13 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "VBUS" (at 55.88 143.51 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 55.88 147.32 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 55.88 147.32 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid d273aedf-7245-41a4-941c-3554a2176f5e))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR061") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Diode:ESD5Zxx") (at 194.31 57.15 270) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 50abfadf-fb6c-48a1-9415-dba8320ad18e)
+    (property "Reference" "D1" (at 196.85 56.515 90)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "ESD5Zxx" (at 196.85 59.055 90)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Diode_SMD:D_SOD-523" (at 189.865 57.15 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "https://www.onsemi.com/pdf/datasheet/esd5z2.5t1-d.pdf" (at 194.31 57.15 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "ESD5Z2.5T1G" (at 194.31 57.15 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid ca8fb78d-320c-46ed-8ee2-d3e4017ea044))
+    (pin "2" (uuid 5098f8fa-78e2-498c-a17d-5416fd0472c0))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "D1") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 177.8 55.88 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 53812d1e-6cb4-4564-944c-12ca50f5498b)
+    (property "Reference" "#PWR032" (at 177.8 62.23 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 177.8 60.96 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 177.8 55.88 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 177.8 55.88 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 7e626491-6adc-4d63-b0ac-d9146bbb898b))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR032") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR048") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Diode:ESD5Zxx") (at 194.31 87.63 270) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 5dffc28c-19cd-4431-a7ba-b33bafb430ee)
+    (property "Reference" "D2" (at 196.85 86.995 90)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "ESD5Zxx" (at 196.85 89.535 90)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Diode_SMD:D_SOD-523" (at 189.865 87.63 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "https://www.onsemi.com/pdf/datasheet/esd5z2.5t1-d.pdf" (at 194.31 87.63 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "ESD5Z2.5T1G" (at 194.31 87.63 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid a344bd0f-f193-4fa8-b038-a83c4f395d1e))
+    (pin "2" (uuid c4d6819b-49d3-40ae-9dac-6aef5fdcbee3))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "D2") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:VDDA") (at 55.88 55.88 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 6e9d5039-1f23-42de-af49-829ced31aae6)
+    (property "Reference" "#PWR04" (at 55.88 59.69 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "VDDA" (at 55.88 50.8 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 55.88 55.88 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 55.88 55.88 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid c6232a67-c758-4f86-8e7b-b79c0211c586))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR04") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR047") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Connector_Audio:AudioJack2_Ground") (at 177.8 50.8 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 7694e2a6-3372-4fab-895b-9744296f2da7)
+    (property "Reference" "J1" (at 177.165 43.18 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "AudioJack2_Ground" (at 177.165 45.72 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Connector_Audio:Jack_3.5mm_CUI_SJ-3523-SMT_Horizontal" (at 177.8 50.8 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 177.8 50.8 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "SJ-3523-SMT-TR" (at 177.8 50.8 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "G" (uuid 0f05d796-6150-45cf-94da-2c8798cc6f86))
+    (pin "S" (uuid bbd4f69e-1bbc-4633-bc2b-9086a9729329))
+    (pin "T" (uuid f0a9fef1-a57e-4888-a226-d3bf9724656e))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "J1") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Connector:USB_B_Micro") (at 41.91 153.67 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 78555264-1178-42da-8282-385afceb94d8)
+    (property "Reference" "J7" (at 41.91 140.97 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "USB_B_Micro" (at 41.91 143.51 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Connector_USB:USB_Micro-B_Amphenol_10104110_Horizontal" (at 45.72 154.94 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 45.72 154.94 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "10104110-0001LF" (at 41.91 153.67 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 11a09a66-afe0-497b-8f7b-213919809f31))
+    (pin "2" (uuid ed1359c5-8991-4aa7-bbed-563d9ca0fd76))
+    (pin "3" (uuid be5f2ca5-ff80-4355-823b-5493b1d5a1d0))
+    (pin "4" (uuid bfd1a233-2c74-4306-8f44-87c4c8b61cf6))
+    (pin "5" (uuid 7aaca5cb-84b1-4e7f-8640-8dc917a9ce5b))
+    (pin "6" (uuid cabc509a-7bb6-408b-8f4b-241d298cd5ee))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "J7") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "J8") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 194.31 91.44 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 79b31dce-cbe5-427b-b46e-fcc62c592022)
+    (property "Reference" "#PWR032" (at 194.31 97.79 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 194.31 96.52 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 194.31 91.44 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 194.31 91.44 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid b2c463fc-0143-439a-8e24-bae8b22f5f87))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR032") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR057") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Connector_Generic:Conn_02x10_Odd_Even") (at 44.45 58.42 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 8475bff6-cb36-4a34-84ec-774f48f03d90)
+    (property "Reference" "J1" (at 45.72 41.91 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "Conn_02x10_Odd_Even" (at 45.72 44.45 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Footprint" "Connector_PinHeader_2.54mm:PinHeader_2x10_P2.54mm_Vertical" (at 44.45 58.42 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 44.45 58.42 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "PPPC102LFBN-RC" (at 44.45 58.42 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 4c47940c-98bb-44d3-939e-29b2c5c45971))
+    (pin "10" (uuid a35c423f-d6b4-47fc-b807-daa86f6a73c2))
+    (pin "11" (uuid d8e76a4c-7993-4f37-b339-9d4b02131f3e))
+    (pin "12" (uuid 74374593-853b-4040-b66a-c0d4d1a57786))
+    (pin "13" (uuid 956c2fb9-525a-4948-a30e-e98c5399d3b6))
+    (pin "14" (uuid 605dafb7-a42d-4a78-a475-b585d0e62b49))
+    (pin "15" (uuid 0b86922c-178f-470b-aaca-954c9f96bda1))
+    (pin "16" (uuid fa27b227-7528-4fa2-9d7a-594c6297c26e))
+    (pin "17" (uuid 17ddbd7f-f691-4a34-9895-ab71d5c47926))
+    (pin "18" (uuid 6c252ffd-a4e7-4764-8b0f-f80223e1601e))
+    (pin "19" (uuid 0b7e0856-149b-432d-9251-52d95d266a41))
+    (pin "2" (uuid 9c33d1ec-9f6a-4eb6-b9fe-d6ccb6fdf565))
+    (pin "20" (uuid 540ba65e-f1d1-4d5f-ba32-62673667da76))
+    (pin "3" (uuid bbe6f617-5bae-486c-8a74-ca1f6eea356c))
+    (pin "4" (uuid 2988955b-81e4-4b52-b89b-e7a06ba97abd))
+    (pin "5" (uuid 76ebf496-0b49-42e6-8b30-8030c4c7ffc7))
+    (pin "6" (uuid 5337e377-0dac-42be-9f63-545482f94793))
+    (pin "7" (uuid 1ecfde4b-c3df-4972-accd-e00d2604cad6))
+    (pin "8" (uuid e84b46fe-2581-4f5f-93c3-13b01de1bbe3))
+    (pin "9" (uuid 249176b3-4cc0-4008-9cb0-11cca57f158c))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "J1") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "J2") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Connector_Audio:AudioJack2_Ground") (at 177.8 113.03 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 8ca59ac3-e703-4c21-a6a5-9119fbd2e6c2)
+    (property "Reference" "J7" (at 177.165 105.41 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "AudioJack2_Ground" (at 177.165 107.95 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Connector_Audio:Jack_3.5mm_CUI_SJ-3523-SMT_Horizontal" (at 177.8 113.03 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 177.8 113.03 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "SJ-3523-SMT-TR" (at 177.8 113.03 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "G" (uuid a80cd669-8ef2-4632-9728-28a2b9eacfdf))
+    (pin "S" (uuid 879ca810-33c3-4be3-9d19-01414f43b355))
+    (pin "T" (uuid 142f9914-dafd-47bc-bc98-5d8aaa505a93))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "J7") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 194.31 123.19 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 9e4a32e2-b480-45f8-9942-42d21a488813)
+    (property "Reference" "#PWR032" (at 194.31 129.54 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 194.31 128.27 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 194.31 123.19 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 194.31 123.19 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid e0046b0d-9f80-42d0-90cc-a34c2e2bfa9c))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR032") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR060") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 39.37 168.91 0) (mirror y) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no)
+    (uuid 9f0f3415-176c-4496-afca-6588b7686c1e)
+    (property "Reference" "R22" (at 36.83 168.275 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "0" (at 36.83 170.815 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 38.354 169.164 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 39.37 168.91 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RMCF0603ZT0R00" (at 39.37 168.91 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 449ea0e2-47fe-40e6-b0ea-f36520e1b0d4))
+    (pin "2" (uuid feebb300-b386-445a-8727-e8a9747d6dbd))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "R22") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:VCC") (at 83.82 66.04 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid a075c7b2-6b75-48b7-ba62-feffb2e8424d)
+    (property "Reference" "#PWR015" (at 83.82 69.85 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "VCC" (at 83.82 60.96 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 83.82 66.04 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 83.82 66.04 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 17dc7a2c-d6c1-4236-91fa-2b37d621dec2))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR015") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR051") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 177.8 118.11 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid b150da0e-7b6f-4b96-b4b8-58828104dc0c)
+    (property "Reference" "#PWR032" (at 177.8 124.46 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 177.8 123.19 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 177.8 118.11 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 177.8 118.11 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid f16cebc1-550a-404b-a1d8-3bcfac8dea9c))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR032") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR058") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 55.88 74.93 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid c29cd35b-b63c-4c61-baeb-7950ba97a481)
+    (property "Reference" "#PWR023" (at 55.88 81.28 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 55.88 80.01 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 55.88 74.93 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 55.88 74.93 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 167403de-dcb3-4b78-b5af-37ef33b1ddaf))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR023") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR053") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 73.66 170.18 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid cfd353e4-0331-4ed6-9450-7c1ebb5ef956)
+    (property "Reference" "#PWR038" (at 73.66 176.53 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 73.66 175.26 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 73.66 170.18 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 73.66 170.18 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 6b7d8f8c-5565-4036-bb9a-b1834a7c90f0))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR038") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR065") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Diode:ESD9B5.0ST5G") (at 73.66 166.37 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid d707892a-1c20-45ac-bb77-11891c2394e4)
+    (property "Reference" "D6" (at 76.2 165.735 90)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Value" "DF2B7AFS,L3M" (at 76.2 168.275 90)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Footprint" "Diode_SMD:D_SOD-923" (at 73.66 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "https://www.onsemi.com/pub/Collateral/ESD9B-D.PDF" (at 73.66 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "DF2B7AFS,L3M" (at 73.66 166.37 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 1badecf4-422b-4d5d-8437-f04a2f25d1e8))
+    (pin "2" (uuid 77b2372d-097a-4747-8b15-07f0a66045db))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "D6") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:VBUS") (at 88.9 44.45 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid d8958fa9-595e-4eea-b400-f6cbaf16d851)
+    (property "Reference" "#PWR046" (at 88.9 48.26 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "VBUS" (at 88.9 40.64 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 88.9 44.45 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 88.9 44.45 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 023e4509-34a0-4227-b4e4-1d81d00aac18))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR046") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Connector_Generic:Conn_02x03_Odd_Even") (at 243.84 68.58 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid de87bdea-3e5b-416f-bfb4-ea751dfca070)
+    (property "Reference" "J6" (at 245.11 59.69 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "Conn_02x03_Odd_Even" (at 245.11 62.23 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Connector_PinHeader_2.54mm:PinHeader_2x03_P2.54mm_Horizontal" (at 243.84 68.58 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 243.84 68.58 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "PH2RA-06-UA" (at 243.84 68.58 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 0d0b164f-a07e-416d-97b5-231df3d7b14c))
+    (pin "2" (uuid 176e7f06-8e36-4716-a845-b9657880ee9e))
+    (pin "3" (uuid 6e268613-0dde-4f5e-890e-1d9c3b61f29e))
+    (pin "4" (uuid f336bc2d-12ba-4cd9-9c94-278c9dcc4661))
+    (pin "5" (uuid 7a2c4018-7434-44de-bae6-b7e90b816fde))
+    (pin "6" (uuid fe457608-883a-4c19-9511-9d20606bd4a8))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "J6") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "J4") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 194.31 60.96 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid e3aa6d05-70bd-4cde-bf05-6c7655d5d84b)
+    (property "Reference" "#PWR032" (at 194.31 67.31 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 194.31 66.04 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 194.31 60.96 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 194.31 60.96 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid c0ed9697-37a9-405d-813c-af9db23ccce3))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR032") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR050") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Connector_Generic:Conn_02x10_Odd_Even") (at 97.79 58.42 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid ec63f71a-7153-47f9-b86f-860fe7e45e10)
+    (property "Reference" "J2" (at 99.06 41.91 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "Conn_02x10_Odd_Even" (at 99.06 44.45 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Footprint" "Connector_PinHeader_2.54mm:PinHeader_2x10_P2.54mm_Vertical" (at 97.79 58.42 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 97.79 58.42 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "PPPC102LFBN-RC" (at 97.79 58.42 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 392a853d-04fc-443f-871d-a69644d936d8))
+    (pin "10" (uuid 242b105e-3167-472e-8d80-e6308ea335a2))
+    (pin "11" (uuid 0f2274fd-0b27-4b22-9d63-7fca06a8d067))
+    (pin "12" (uuid a8b98539-e862-4ddb-8bea-e67519deadf9))
+    (pin "13" (uuid c8606985-2245-4c63-9d43-019e8c742fb6))
+    (pin "14" (uuid 473964c0-e3ac-4c3f-9584-befe7cf69216))
+    (pin "15" (uuid 863dc14f-e1f4-4baa-8c7f-00a739bbd00f))
+    (pin "16" (uuid ea80c488-cddb-46fe-a3e2-0fac75c185ce))
+    (pin "17" (uuid e3aa0362-9e46-43c6-a055-bd9b58705da7))
+    (pin "18" (uuid 32551ce7-cb9c-45f6-8d6f-471c93426176))
+    (pin "19" (uuid 23daedb3-522c-48b6-a787-fadeea602b64))
+    (pin "2" (uuid f7546210-f6c7-4205-bf4c-a4e6de1e6013))
+    (pin "20" (uuid 3c04cda5-c51f-4eef-a743-b99a01a0b658))
+    (pin "3" (uuid 1a1916ec-30d2-45f3-be28-a46f614eb449))
+    (pin "4" (uuid f9c1ad21-2427-4315-8f0e-1737a603e262))
+    (pin "5" (uuid 260af566-7606-4ab8-96a9-97ad307004ca))
+    (pin "6" (uuid b4ed07a4-fcf9-4c3f-8b58-6b58b30ff0cc))
+    (pin "7" (uuid f664e483-9587-4dbf-b40a-e86e0ef170c9))
+    (pin "8" (uuid 6ac8f5e0-6a25-4976-ba18-50f3e81eb160))
+    (pin "9" (uuid 6c69fb36-24ef-4cb1-85a0-5736f84173ab))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "J2") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "J3") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 39.37 172.72 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid f18f5482-3c4d-44ab-935a-46c400a9b2b4)
+    (property "Reference" "#PWR038" (at 39.37 179.07 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 39.37 177.8 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 39.37 172.72 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 39.37 172.72 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid a964d6d9-c115-49a0-8460-0ea0e4ad135c))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR038") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR066") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 88.9 74.93 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid ffe29d7b-7789-4faa-9fb7-cdb8b1797bec)
+    (property "Reference" "#PWR024" (at 88.9 81.28 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 88.9 80.01 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 88.9 74.93 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 88.9 74.93 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 8a764283-4bfb-4d5f-878f-f01468f09d41))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR024") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR054") (unit 1)
+        )
+      )
+    )
+  )
+)
diff --git a/hardware/power_regulation.kicad_sch b/hardware/power_regulation.kicad_sch
new file mode 100755 (executable)
index 0000000..01a445c
--- /dev/null
@@ -0,0 +1,2236 @@
+(kicad_sch (version 20230121) (generator eeschema)
+
+  (uuid c6aeebbe-489b-412e-bd61-4ab6df7b25be)
+
+  (paper "A4")
+
+  (title_block
+    (title "Power regulation")
+    (date "2023-08-08")
+    (company "bitgloo")
+    (comment 1 "Released under the CERN Open Hardware Licence Version 2 - Strongly Reciprocal")
+  )
+
+  (lib_symbols
+    (symbol "Connector:TestPoint" (pin_numbers hide) (pin_names (offset 0.762) hide) (in_bom yes) (on_board yes)
+      (property "Reference" "TP" (at 0 6.858 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "TestPoint" (at 0 5.08 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 5.08 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 5.08 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "test point tp" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "test point" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "Pin* Test*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "TestPoint_0_1"
+        (circle (center 0 3.302) (radius 0.762)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "TestPoint_1_1"
+        (pin passive line (at 0 0 90) (length 2.54)
+          (name "1" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Device:C" (pin_numbers hide) (pin_names (offset 0.254)) (in_bom yes) (on_board yes)
+      (property "Reference" "C" (at 0.635 2.54 0)
+        (effects (font (size 1.27 1.27)) (justify left))
+      )
+      (property "Value" "C" (at 0.635 -2.54 0)
+        (effects (font (size 1.27 1.27)) (justify left))
+      )
+      (property "Footprint" "" (at 0.9652 -3.81 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "cap capacitor" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Unpolarized capacitor" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "C_*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "C_0_1"
+        (polyline
+          (pts
+            (xy -2.032 -0.762)
+            (xy 2.032 -0.762)
+          )
+          (stroke (width 0.508) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -2.032 0.762)
+            (xy 2.032 0.762)
+          )
+          (stroke (width 0.508) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "C_1_1"
+        (pin passive line (at 0 3.81 270) (length 2.794)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 0 -3.81 90) (length 2.794)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Device:L" (pin_numbers hide) (pin_names (offset 1.016) hide) (in_bom yes) (on_board yes)
+      (property "Reference" "L" (at -1.27 0 90)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "L" (at 1.905 0 90)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "inductor choke coil reactor magnetic" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Inductor" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "Choke_* *Coil* Inductor_* L_*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "L_0_1"
+        (arc (start 0 -2.54) (mid 0.6323 -1.905) (end 0 -1.27)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (arc (start 0 -1.27) (mid 0.6323 -0.635) (end 0 0)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (arc (start 0 0) (mid 0.6323 0.635) (end 0 1.27)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (arc (start 0 1.27) (mid 0.6323 1.905) (end 0 2.54)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "L_1_1"
+        (pin passive line (at 0 3.81 270) (length 1.27)
+          (name "1" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 0 -3.81 90) (length 1.27)
+          (name "2" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Device:R_US" (pin_numbers hide) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "R" (at 2.54 0 90)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "R_US" (at -2.54 0 90)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 1.016 -0.254 90)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "R res resistor" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Resistor, US symbol" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "R_*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "R_US_0_1"
+        (polyline
+          (pts
+            (xy 0 -2.286)
+            (xy 0 -2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.286)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 -0.762)
+            (xy 1.016 -1.143)
+            (xy 0 -1.524)
+            (xy -1.016 -1.905)
+            (xy 0 -2.286)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0.762)
+            (xy 1.016 0.381)
+            (xy 0 0)
+            (xy -1.016 -0.381)
+            (xy 0 -0.762)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.286)
+            (xy 1.016 1.905)
+            (xy 0 1.524)
+            (xy -1.016 1.143)
+            (xy 0 0.762)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "R_US_1_1"
+        (pin passive line (at 0 3.81 270) (length 1.27)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 0 -3.81 90) (length 1.27)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Reference_Voltage:MAX6106" (in_bom yes) (on_board yes)
+      (property "Reference" "U" (at 0 6.35 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "MAX6106" (at 2.54 -6.35 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "Package_TO_SOT_SMD:SOT-23" (at 2.54 -7.62 0)
+        (effects (font (size 1.27 1.27) italic) hide)
+      )
+      (property "Datasheet" "http://datasheets.maximintegrated.com/en/ds/MAX6100-MAX6107.pdf" (at 2.54 -8.89 0)
+        (effects (font (size 1.27 1.27) italic) hide)
+      )
+      (property "ki_keywords" "voltage reference ldo" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Low-dropout high current voltage reference, 2.048V, Â±0.4% accuracy, SOT-23" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "SOT?23*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "MAX6106_0_1"
+        (rectangle (start -5.08 5.08) (end 5.08 -5.08)
+          (stroke (width 0.254) (type default))
+          (fill (type background))
+        )
+      )
+      (symbol "MAX6106_1_1"
+        (pin power_in line (at -2.54 7.62 270) (length 2.54)
+          (name "IN" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin power_out line (at 7.62 0 180) (length 2.54)
+          (name "OUT" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+        (pin power_in line (at -2.54 -7.62 90) (length 2.54)
+          (name "GND" (effects (font (size 1.27 1.27))))
+          (number "3" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Regulator_SwitchedCapacitor:LM27761" (in_bom yes) (on_board yes)
+      (property "Reference" "U" (at -6.35 11.43 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "LM27761" (at 3.302 11.43 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "Package_SON:WSON-8-1EP_2x2mm_P0.5mm_EP0.9x1.6mm" (at 3.81 -12.7 0)
+        (effects (font (size 1.27 1.27)) (justify left) hide)
+      )
+      (property "Datasheet" "http://www.ti.com/lit/ds/symlink/lm27761.pdf" (at 63.5 -10.16 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "low-noise switched capacitor voltage converter invert" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "low-noise regulated switched-capacitor voltage inverter with 2.7V-5.5V input to -1.5 to -5V Output Voltage, WSON-8" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "WSON*1EP?2x2mm*P0.5mm*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "LM27761_0_1"
+        (rectangle (start -7.62 10.16) (end 7.62 -10.16)
+          (stroke (width 0.254) (type default))
+          (fill (type background))
+        )
+      )
+      (symbol "LM27761_1_1"
+        (pin power_in line (at -10.16 7.62 0) (length 2.54)
+          (name "VIN" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin power_in line (at 0 -12.7 90) (length 2.54)
+          (name "GND" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+        (pin power_out line (at 10.16 -2.54 180) (length 2.54)
+          (name "CPOUT" (effects (font (size 1.27 1.27))))
+          (number "3" (effects (font (size 1.27 1.27))))
+        )
+        (pin power_out line (at 10.16 7.62 180) (length 2.54)
+          (name "VOUT" (effects (font (size 1.27 1.27))))
+          (number "4" (effects (font (size 1.27 1.27))))
+        )
+        (pin input line (at 10.16 2.54 180) (length 2.54)
+          (name "VFB" (effects (font (size 1.27 1.27))))
+          (number "5" (effects (font (size 1.27 1.27))))
+        )
+        (pin input line (at -10.16 5.08 0) (length 2.54)
+          (name "EN" (effects (font (size 1.27 1.27))))
+          (number "6" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -10.16 -7.62 0) (length 2.54)
+          (name "C-" (effects (font (size 1.27 1.27))))
+          (number "7" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -10.16 0 0) (length 2.54)
+          (name "C+" (effects (font (size 1.27 1.27))))
+          (number "8" (effects (font (size 1.27 1.27))))
+        )
+        (pin power_in line (at 2.54 -12.7 90) (length 2.54)
+          (name "PAD" (effects (font (size 1.27 1.27))))
+          (number "9" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "power:+5V" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 -3.81 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "+5V" (at 0 3.556 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"+5V\"" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "+5V_0_1"
+        (polyline
+          (pts
+            (xy -0.762 1.27)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.54)
+            (xy 0.762 1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "+5V_1_1"
+        (pin power_in line (at 0 0 90) (length 0) hide
+          (name "+5V" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "power:-5V" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 2.54 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "-5V" (at 0 3.81 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"-5V\"" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "-5V_0_0"
+        (pin power_in line (at 0 0 90) (length 0) hide
+          (name "-5V" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+      (symbol "-5V_0_1"
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 1.27)
+            (xy 0.762 1.27)
+            (xy 0 2.54)
+            (xy -0.762 1.27)
+            (xy 0 1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type outline))
+        )
+      )
+    )
+    (symbol "power:GND" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 -6.35 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "GND" (at 0 -3.81 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"GND\" , ground" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "GND_0_1"
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 -1.27)
+            (xy 1.27 -1.27)
+            (xy 0 -2.54)
+            (xy -1.27 -1.27)
+            (xy 0 -1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "GND_1_1"
+        (pin power_in line (at 0 0 270) (length 0) hide
+          (name "GND" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "power:VBUS" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 -3.81 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "VBUS" (at 0 3.81 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"VBUS\"" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "VBUS_0_1"
+        (polyline
+          (pts
+            (xy -0.762 1.27)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.54)
+            (xy 0.762 1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "VBUS_1_1"
+        (pin power_in line (at 0 0 90) (length 0) hide
+          (name "VBUS" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "power:VDDA" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 -3.81 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "VDDA" (at 0 3.81 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"VDDA\"" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "VDDA_0_1"
+        (polyline
+          (pts
+            (xy -0.762 1.27)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.54)
+            (xy 0.762 1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "VDDA_1_1"
+        (pin power_in line (at 0 0 90) (length 0) hide
+          (name "VDDA" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+  )
+
+  (junction (at 102.87 135.89) (diameter 0) (color 0 0 0 0)
+    (uuid 2b0a7097-d58e-48f5-b1a9-557408f83890)
+  )
+  (junction (at 77.47 151.13) (diameter 0) (color 0 0 0 0)
+    (uuid f0c3cd13-cf0e-4352-8da8-28cf5041eb05)
+  )
+
+  (wire (pts (xy 87.63 130.81) (xy 91.44 130.81))
+    (stroke (width 0) (type default))
+    (uuid 0941c4c7-5558-4fc0-bf47-2aaa34586146)
+  )
+  (wire (pts (xy 59.69 71.12) (xy 59.69 72.39))
+    (stroke (width 0) (type default))
+    (uuid 24337116-ae1a-4aa2-9f89-77b0a194aa7b)
+  )
+  (wire (pts (xy 59.69 133.35) (xy 67.31 133.35))
+    (stroke (width 0) (type default))
+    (uuid 259048b4-459b-4a53-9a4d-4f37995be0c7)
+  )
+  (wire (pts (xy 102.87 135.89) (xy 102.87 137.16))
+    (stroke (width 0) (type default))
+    (uuid 355c07f9-a55c-4467-be61-b5c7e66bf130)
+  )
+  (wire (pts (xy 102.87 135.89) (xy 102.87 134.62))
+    (stroke (width 0) (type default))
+    (uuid 475964ab-ce70-4f9c-aee4-45c1b2f0b4d6)
+  )
+  (wire (pts (xy 48.26 133.35) (xy 52.07 133.35))
+    (stroke (width 0) (type default))
+    (uuid 556536a6-4230-44c2-a32d-6d2fca3f6ea0)
+  )
+  (wire (pts (xy 80.01 71.12) (xy 80.01 72.39))
+    (stroke (width 0) (type default))
+    (uuid 63b8704f-625f-4014-8864-9ea515dcf5e2)
+  )
+  (wire (pts (xy 87.63 135.89) (xy 102.87 135.89))
+    (stroke (width 0) (type default))
+    (uuid 73bf79cc-52eb-4d3e-8902-3bca69d9b345)
+  )
+  (wire (pts (xy 63.5 138.43) (xy 67.31 138.43))
+    (stroke (width 0) (type default))
+    (uuid ae462b14-232e-49da-aca3-2ec258e00648)
+  )
+  (wire (pts (xy 63.5 130.81) (xy 67.31 130.81))
+    (stroke (width 0) (type default))
+    (uuid af9639c6-1088-48da-97df-b1fa7835072a)
+  )
+  (wire (pts (xy 208.28 72.39) (xy 212.09 72.39))
+    (stroke (width 0) (type default))
+    (uuid c9875964-8070-4e31-ac03-a067eb0f07c3)
+  )
+  (wire (pts (xy 63.5 146.05) (xy 67.31 146.05))
+    (stroke (width 0) (type default))
+    (uuid cac804d7-b44d-496d-950d-d0c4d8a260ab)
+  )
+  (wire (pts (xy 59.69 72.39) (xy 66.04 72.39))
+    (stroke (width 0) (type default))
+    (uuid ccad4a4b-fdb7-421d-a11f-d4d544c25aa0)
+  )
+  (wire (pts (xy 87.63 140.97) (xy 91.44 140.97))
+    (stroke (width 0) (type default))
+    (uuid d5247eca-6402-4a11-8810-c55c35a6fe00)
+  )
+  (wire (pts (xy 73.66 72.39) (xy 80.01 72.39))
+    (stroke (width 0) (type default))
+    (uuid e629ff71-f7b5-4684-84ef-100ee8c2426a)
+  )
+  (wire (pts (xy 77.47 151.13) (xy 80.01 151.13))
+    (stroke (width 0) (type default))
+    (uuid e73c5c82-c125-4861-8f10-4f799cb4ab6b)
+  )
+
+  (text "Analog voltage reference" (at 195.58 53.34 0)
+    (effects (font (size 2.54 2.54) (thickness 0.254) bold) (justify left bottom))
+    (uuid 2b9ac247-ad34-4af4-9f1f-81ad5cac97a2)
+  )
+  (text "+5V inverter" (at 80.01 114.3 0)
+    (effects (font (size 2.54 2.54) (thickness 0.254) bold) (justify left bottom))
+    (uuid 2bf0fe52-ea5c-44a4-a994-2b774ae0c185)
+  )
+  (text "Supply test points" (at 201.93 114.3 0)
+    (effects (font (size 2.54 2.54) (thickness 0.254) bold) (justify left bottom))
+    (uuid 4e9e691e-e51c-49c5-9490-5c283eed198e)
+  )
+  (text "Noise filtering for +5V from VBUS" (at 57.15 54.61 0)
+    (effects (font (size 2.54 2.54) (thickness 0.254) bold) (justify left bottom))
+    (uuid ddf750d9-baac-4dbc-88dc-0d7e3b1b3e53)
+  )
+
+  (symbol (lib_id "power:GND") (at 231.14 135.89 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 046decc0-a39e-4aa3-832a-2417ed5faec3)
+    (property "Reference" "#PWR014" (at 231.14 142.24 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 231.14 140.97 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 231.14 135.89 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 231.14 135.89 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid a7900495-903c-4b9e-9a6c-dc133799e5f6))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR014") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR034") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR040") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:-5V") (at 102.87 127 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 0fe8caa4-7926-43c0-8da8-836c298f3377)
+    (property "Reference" "#PWR012" (at 102.87 124.46 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "-5V" (at 102.87 121.92 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 102.87 127 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 102.87 127 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid c91dff7c-18ec-45f6-afd0-1aa6e8749947))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR012") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR032") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR031") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Connector:TestPoint") (at 204.47 134.62 180) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 1362f62f-0013-4579-bfe3-657f3da88a4c)
+    (property "Reference" "TP1" (at 207.01 136.6519 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Value" "TestPoint" (at 207.01 139.1919 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+    (property "Footprint" "TestPoint:TestPoint_Pad_D1.0mm" (at 199.39 134.62 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 199.39 134.62 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid c61524bf-721c-4283-bb99-5b3940bcef7e))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "TP1") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "TP1") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "TP1") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 118.11 135.89 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 17e9eb2e-3859-4b1d-b315-52d3f5671c2b)
+    (property "Reference" "C18" (at 121.92 135.255 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "4.7u" (at 121.92 137.795 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 119.0752 139.7 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 118.11 135.89 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CL10A475KP8NNNC" (at 118.11 135.89 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 0cda965f-94fd-4ace-b335-096456ad673d))
+    (pin "2" (uuid cbcda09c-636a-4082-b3fb-7d121927b9e7))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "C18") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:+5V") (at 63.5 130.81 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 1861e3f2-cef4-4cde-9405-5cfacef90eed)
+    (property "Reference" "#PWR011" (at 63.5 134.62 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "+5V" (at 63.5 125.73 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 63.5 130.81 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 63.5 130.81 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid d26031c0-e4dc-4bcd-836d-469b08f659a0))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR011") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR030") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR032") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:+5V") (at 48.26 133.35 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 1ba35b23-4f63-4c21-abcc-c8fdc06bea89)
+    (property "Reference" "#PWR011" (at 48.26 137.16 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "+5V" (at 48.26 128.27 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 48.26 133.35 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 48.26 133.35 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 51f13e1c-169b-4716-b1f9-e8bb4416ab37))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR011") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR030") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR036") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 63.5 142.24 0) (mirror y) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no)
+    (uuid 1dcdc424-fb22-42bf-8874-89ad7acc92ec)
+    (property "Reference" "C20" (at 59.69 141.605 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "1u" (at 59.69 144.145 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 62.5348 146.05 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 63.5 142.24 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CL10B105KP8NNNC" (at 63.5 142.24 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 4481173a-a6d6-4807-b57a-bce35d3f144d))
+    (pin "2" (uuid 69e8e48f-d1f9-42ed-a9bf-a6d69c02a29c))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "C20") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Connector:TestPoint") (at 231.14 135.89 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 1e9bd7fc-0998-4613-aa5b-48cd00451144)
+    (property "Reference" "TP4" (at 233.68 132.5879 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "TestPoint" (at 228.6 131.3181 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+    (property "Footprint" "TestPoint:TestPoint_Pad_D1.0mm" (at 236.22 135.89 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 236.22 135.89 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 24676cf7-af6a-426e-bb6f-4b5f913fdeeb))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "TP4") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "TP4") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "TP4") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 242.57 72.39 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 219a1fe6-d5bc-49e5-ba83-808476b87bcb)
+    (property "Reference" "C17" (at 246.38 71.755 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "1u" (at 246.38 74.295 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 243.5352 76.2 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 242.57 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CL10B105KP8NNNC" (at 242.57 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 6b153cc1-b2f8-4a53-9d48-5689b1c084eb))
+    (pin "2" (uuid 39542967-46f6-4cd9-b39b-c564e60e7ff2))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "C17") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Reference_Voltage:MAX6106") (at 200.66 72.39 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 2721a4bf-a486-4e4a-95f3-7833d023f203)
+    (property "Reference" "U3" (at 194.31 71.755 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Value" "MAX6106" (at 194.31 74.295 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Footprint" "Package_TO_SOT_SMD:SOT-23" (at 203.2 80.01 0)
+      (effects (font (size 1.27 1.27) italic) hide)
+    )
+    (property "Datasheet" "http://datasheets.maximintegrated.com/en/ds/MAX6100-MAX6107.pdf" (at 203.2 81.28 0)
+      (effects (font (size 1.27 1.27) italic) hide)
+    )
+    (property "Part Number" "MAX6106EUR+T" (at 200.66 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 14a2220b-3f94-4b6c-8253-0292c079e977))
+    (pin "2" (uuid 6bec615b-28e8-4874-b9bd-ad456dde0db0))
+    (pin "3" (uuid 16a57cde-52a3-4ad6-b3e2-482b1c3d1c91))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "U3") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 119.38 73.66 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 2c4be438-6cdd-4af1-896c-80967810d9d7)
+    (property "Reference" "#PWR038" (at 119.38 80.01 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 119.38 78.74 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 119.38 73.66 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 119.38 73.66 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid ce28dbda-92cb-422a-80d3-2d1509d298ec))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR038") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR060") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR027") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 77.47 151.13 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 3734b5dc-7757-4164-aaf4-0394d5a5e4e8)
+    (property "Reference" "#PWR014" (at 77.47 157.48 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 77.47 156.21 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 77.47 151.13 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 77.47 151.13 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 81690a4b-9ea5-43e4-9d13-527d1fb0c182))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR014") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR034") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR045") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 102.87 130.81 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 4928232b-2208-4ba3-94a3-4f551edb95e6)
+    (property "Reference" "R19" (at 105.41 130.175 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "174k" (at 105.41 132.715 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 103.886 131.064 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 102.87 130.81 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RMCF0603FT174K" (at 102.87 130.81 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 0b3d36cd-f02a-40eb-b4e2-ee8a34c11526))
+    (pin "2" (uuid 4c42b571-51b2-40c3-917c-5c4dd2d5dfaa))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "R19") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 91.44 144.78 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 5a538b70-0a4a-472f-85a3-5d86c644f91a)
+    (property "Reference" "C21" (at 95.25 144.145 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "4.7u" (at 95.25 146.685 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 92.4052 148.59 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 91.44 144.78 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CL10A475KP8NNNC" (at 91.44 144.78 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 8321bc94-c842-4d5b-a8c7-619af0ca5738))
+    (pin "2" (uuid 35c8151c-4c66-420a-99e6-9b4f7bc6341f))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "C21") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:VBUS") (at 96.52 66.04 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 63c1a517-0671-47d3-812d-286119ebbe57)
+    (property "Reference" "#PWR061" (at 96.52 69.85 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "VBUS" (at 96.52 62.23 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 96.52 66.04 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 96.52 66.04 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 2af5903d-2193-42bc-bea6-926b80d80d0b))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR061") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR017") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:VDDA") (at 222.25 134.62 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 69a0f71b-0607-484e-b263-de0bffc9a9b5)
+    (property "Reference" "#PWR013" (at 222.25 138.43 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "VDDA" (at 222.25 129.54 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 222.25 134.62 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 222.25 134.62 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 28de77ff-87c7-4ee1-8a34-6eeb63355182))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR013") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR033") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR039") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Connector:TestPoint") (at 213.36 134.62 180) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 6c19d262-a0fe-4278-9e4c-98da07eea5c9)
+    (property "Reference" "TP2" (at 215.9 136.6519 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Value" "TestPoint" (at 215.9 139.1919 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+    (property "Footprint" "TestPoint:TestPoint_Pad_D1.0mm" (at 208.28 134.62 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 208.28 134.62 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 11ab48b4-00c1-4420-b776-913031772c78))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "TP2") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "TP2") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "TP2") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 130.81 135.89 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 75cccab0-1f63-406c-b256-08197905c2d8)
+    (property "Reference" "C19" (at 134.62 135.255 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "2.2u" (at 134.62 137.795 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 131.7752 139.7 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 130.81 135.89 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CL10B225KP8NNNC" (at 130.81 135.89 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 12198c25-2660-4a1a-9ddc-8d1629dc760f))
+    (pin "2" (uuid b09f302c-5356-4fb4-9ee6-d5ad6a3375f5))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "C19") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:+5V") (at 80.01 71.12 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 7b324404-991b-4e7b-ac91-f04cb7cea286)
+    (property "Reference" "#PWR033" (at 80.01 74.93 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "+5V" (at 80.01 66.04 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 80.01 71.12 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 80.01 71.12 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid b7bf0572-26a7-49cd-b90d-2da89242ab16))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR033") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR045") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR023") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 96.52 73.66 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 7bb6ce0d-1d2f-41b2-b395-b56ba1cc97b4)
+    (property "Reference" "#PWR038" (at 96.52 80.01 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 96.52 78.74 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 96.52 73.66 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 96.52 73.66 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 97b7c62d-a150-4314-8c2b-3c8d7b5e80bb))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR038") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR058") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR025") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:+5V") (at 229.87 68.58 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 85dd6cb0-3ec6-4fd3-a7f3-5c00d59ddd52)
+    (property "Reference" "#PWR011" (at 229.87 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "+5V" (at 229.87 63.5 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 229.87 68.58 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 229.87 68.58 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid f700b420-c6dc-403d-b658-a9646ac6db33))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR011") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR030") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR020") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 55.88 133.35 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 8e04a020-c831-4020-a547-acddae80ca1b)
+    (property "Reference" "R20" (at 55.88 128.27 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "0" (at 55.88 130.81 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 56.134 132.334 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 55.88 133.35 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RMCF0603ZT0R00" (at 55.88 133.35 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 6d436e5c-547e-41ff-86fc-7de02c55160f))
+    (pin "2" (uuid 7305b306-ad5b-4a93-b739-b99626eb6e8b))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "R20") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:+5V") (at 118.11 132.08 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 8e6ccf1c-f269-42c1-9cb6-a06e667980bd)
+    (property "Reference" "#PWR011" (at 118.11 135.89 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "+5V" (at 118.11 127 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 118.11 132.08 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 118.11 132.08 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 0851bb54-ee23-4a4b-a81c-ef250286d779))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR011") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR030") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR034") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 229.87 72.39 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 8f35c64f-1632-4c1b-b8fa-42e58819b677)
+    (property "Reference" "C16" (at 233.68 71.755 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "4.7u" (at 233.68 74.295 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 230.8352 76.2 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 229.87 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CL10A475KP8NNNC" (at 229.87 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 645d0c68-e6c9-493f-88d7-69007e5f25ee))
+    (pin "2" (uuid 945e9a39-9c28-4800-a167-a87858c16bbc))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "C16") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:VDDA") (at 212.09 72.39 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 8f3f4cc2-fe28-4d27-a2cc-01741fec1dff)
+    (property "Reference" "#PWR013" (at 212.09 76.2 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "VDDA" (at 212.09 67.31 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 212.09 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 212.09 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 3eee085b-c189-432c-897c-8563c3779154))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR013") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR033") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR024") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Connector:TestPoint") (at 222.25 134.62 180) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 9563d063-75b0-44ce-b3ce-3dcaef0a8a7b)
+    (property "Reference" "TP3" (at 224.79 136.6519 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Value" "TestPoint" (at 224.79 139.1919 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+    (property "Footprint" "TestPoint:TestPoint_Pad_D1.0mm" (at 217.17 134.62 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 217.17 134.62 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid c28177d7-0d6f-42f8-9859-669225cfd629))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "TP3") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "TP3") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "TP3") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:VBUS") (at 59.69 71.12 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no)
+    (uuid 980e1d26-93bc-4d43-9367-6ac9398bd11f)
+    (property "Reference" "#PWR022" (at 59.69 74.93 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "VBUS" (at 59.69 66.04 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 59.69 71.12 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 59.69 71.12 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 95a97132-2d2e-42de-810e-996d6d2d517c))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR022") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 119.38 69.85 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 98f2b9b9-6327-4bc8-b876-121d7ad6905e)
+    (property "Reference" "C15" (at 123.19 69.215 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "4.7u" (at 123.19 71.755 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 120.3452 73.66 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 119.38 69.85 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CL10A475KP8NNNC" (at 119.38 69.85 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 4f07604e-e771-44a6-967e-9aed9515d3f5))
+    (pin "2" (uuid 93c96f5c-4843-4ac7-8da8-ad1f21690ddf))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "C15") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "C15") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 102.87 140.97 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid a1ea9e3c-6800-4e35-b5e2-cc7438c29b4e)
+    (property "Reference" "R21" (at 105.41 140.335 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "56k" (at 105.41 142.875 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 103.886 141.224 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 102.87 140.97 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RMCF0603FT56K0" (at 102.87 140.97 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 11a0ad8e-f178-4da7-9911-1f0675d8445a))
+    (pin "2" (uuid 4eb73d7c-564b-4629-bd8e-f7c360497562))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "R21") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 107.95 69.85 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid a3e00720-9b71-4fec-8b36-e7a5cdc4ca07)
+    (property "Reference" "C14" (at 111.76 69.215 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "100n" (at 111.76 71.755 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 108.9152 73.66 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 107.95 69.85 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CL10B104KB8NNWC" (at 107.95 69.85 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 5ed47f8c-e7db-4bae-941d-46f4c03222e6))
+    (pin "2" (uuid 9fe98691-dd94-43c3-8cbb-a358b32112fa))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "C14") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "C14") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Regulator_SwitchedCapacitor:LM27761") (at 77.47 138.43 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid ab5ba760-1b9a-48fc-8a36-c943d7f45f9f)
+    (property "Reference" "U4" (at 77.47 124.46 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "LM27761" (at 77.47 127 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Package_SON:WSON-8-1EP_2x2mm_P0.5mm_EP0.9x1.6mm" (at 81.28 151.13 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+    (property "Datasheet" "http://www.ti.com/lit/ds/symlink/lm27761.pdf" (at 140.97 148.59 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "LM27761DSGR" (at 77.47 138.43 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 21545055-5b80-4ed0-9ac9-2935855f79d1))
+    (pin "2" (uuid 72213785-c384-447a-b2f7-3dce8f3a8ff4))
+    (pin "3" (uuid 8a7b92d4-9dd9-4f6a-8d34-b0348a67ba66))
+    (pin "4" (uuid 546e6335-ed48-4c12-bf5c-107851086860))
+    (pin "5" (uuid 30d73505-7266-43ea-b473-c8efc129d173))
+    (pin "6" (uuid 898c90d9-2d6f-49b9-afa3-0c2d7b7d5086))
+    (pin "7" (uuid 2294023e-d221-41a5-9169-7c17fe4f596e))
+    (pin "8" (uuid 8cbc5d5b-0e83-4f83-9cb4-c0c29c762dda))
+    (pin "9" (uuid a4fc70c8-4d07-4810-8975-be14e976391e))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "U4") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:-5V") (at 130.81 132.08 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid ace022e9-6578-4042-b901-b11d04f4ab5f)
+    (property "Reference" "#PWR012" (at 130.81 129.54 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "-5V" (at 130.81 127 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 130.81 132.08 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 130.81 132.08 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 30cd4546-33d5-4a5a-89cc-e513583d5aa3))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR012") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR032") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR035") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:+5V") (at 119.38 66.04 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid b0636f3a-4271-4dba-9f14-192c19f32b8a)
+    (property "Reference" "#PWR033" (at 119.38 69.85 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "+5V" (at 119.38 60.96 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 119.38 66.04 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 119.38 66.04 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 42d0f091-2f24-49c7-9bda-875a505d02b5))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR033") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR063") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR019") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 91.44 148.59 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid b20c9d7c-2a79-48cd-ab76-376a4e5f7ba1)
+    (property "Reference" "#PWR014" (at 91.44 154.94 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 91.44 153.67 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 91.44 148.59 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 91.44 148.59 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 601197e0-9832-4c3d-b1a7-2bd03987bf47))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR014") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR034") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR044") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 130.81 139.7 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid b4dcadca-f631-4d3b-b1fa-841604f4c562)
+    (property "Reference" "#PWR014" (at 130.81 146.05 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 130.81 144.78 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 130.81 139.7 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 130.81 139.7 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 228640c4-a133-4c5d-81e9-857b4dc26614))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR014") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR034") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR042") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:+5V") (at 198.12 64.77 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid be093599-523e-4c2e-b272-abe5809dc134)
+    (property "Reference" "#PWR011" (at 198.12 68.58 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "+5V" (at 198.12 59.69 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 198.12 64.77 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 198.12 64.77 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 937eebdd-5301-49e5-b841-531e6acd0fa8))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR011") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR030") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR016") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 198.12 80.01 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid c0a6ce2c-2abe-4806-9b33-6aad4b43fed8)
+    (property "Reference" "#PWR014" (at 198.12 86.36 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 198.12 85.09 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 198.12 80.01 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 198.12 80.01 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 49c43164-09dd-4dfc-bd48-fa37f6aa7f2c))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR014") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR034") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR030") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:+5V") (at 204.47 134.62 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid c9bf1559-a362-435c-9b54-e97ab8f624e0)
+    (property "Reference" "#PWR011" (at 204.47 138.43 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "+5V" (at 204.47 129.54 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 204.47 134.62 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 204.47 134.62 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid ca17ace4-2639-4fd3-89bf-252e20ca7191))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR011") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR030") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR037") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 102.87 144.78 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid d150fb34-ff0f-4fb8-9bd5-5ac333ff1158)
+    (property "Reference" "#PWR014" (at 102.87 151.13 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 102.87 149.86 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 102.87 144.78 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 102.87 144.78 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 4650ff88-aff8-4a30-b27d-5975c17453e8))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR014") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR034") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR043") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 118.11 139.7 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid d493e9eb-0593-4b90-9c8f-2a800dff4a27)
+    (property "Reference" "#PWR014" (at 118.11 146.05 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 118.11 144.78 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 118.11 139.7 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 118.11 139.7 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid f217387a-3b05-4051-a8ff-9644a4c1b9a3))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR014") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR034") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR041") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 242.57 76.2 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid e1d27700-2b11-401d-86e1-e30a724dc96b)
+    (property "Reference" "#PWR014" (at 242.57 82.55 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 242.57 81.28 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 242.57 76.2 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 242.57 76.2 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 9df71c6c-f1d2-4978-9fb0-0de42b2d1cc7))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR014") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR034") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR029") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:L") (at 69.85 72.39 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no)
+    (uuid e1dbf950-0e9a-4d00-a47a-b30cf1090893)
+    (property "Reference" "L1" (at 69.85 68.58 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "33@100MHz" (at 69.85 73.66 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Inductor_SMD:L_0603_1608Metric" (at 69.85 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 69.85 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "BLM18PG330SN1D" (at 69.85 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 6dd83468-e806-4464-8c39-cb81c509c81e))
+    (pin "2" (uuid 603c57a9-3abe-4b53-87cf-8b24f5f5440a))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "L1") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "L1") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:VDDA") (at 242.57 68.58 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid e8f88fa3-a7ad-45e2-9e3b-660c8ab36936)
+    (property "Reference" "#PWR013" (at 242.57 72.39 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "VDDA" (at 242.57 63.5 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 242.57 68.58 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 242.57 68.58 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid ba62c885-7793-4bef-9fbd-126a83cdb25f))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR013") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR033") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR021") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:-5V") (at 91.44 130.81 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid ea02a04a-7e35-43d1-b94b-f59b5022f4ac)
+    (property "Reference" "#PWR012" (at 91.44 128.27 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "-5V" (at 91.44 125.73 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 91.44 130.81 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 91.44 130.81 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 203ec968-cb96-4dba-a4f6-d28f29187f97))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR012") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR032") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR033") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:+5V") (at 107.95 66.04 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid ed2e49b0-c494-4ae8-83e5-7979b9638228)
+    (property "Reference" "#PWR033" (at 107.95 69.85 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "+5V" (at 107.95 60.96 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 107.95 66.04 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 107.95 66.04 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 8ddada4c-8473-4576-af2a-46b6ec6af1ce))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR033") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR062") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR018") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:C") (at 96.52 69.85 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid f2d797f3-24bb-4aa5-ba81-171b8a49029e)
+    (property "Reference" "C13" (at 100.33 69.215 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Value" "10n" (at 100.33 71.755 0)
+      (effects (font (size 1.27 1.27)) (justify left))
+    )
+    (property "Footprint" "Capacitor_SMD:C_0603_1608Metric" (at 97.4852 73.66 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 96.52 69.85 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "CC0603KRX7R9BB103" (at 96.52 69.85 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 3889c5b5-ada8-466e-9da0-08b1be88f996))
+    (pin "2" (uuid 5b82a078-7e33-472d-912b-078f7710f2c6))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "C13") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "C13") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:-5V") (at 213.36 134.62 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid f352ed29-9dc0-4d41-90d6-e4145d389395)
+    (property "Reference" "#PWR012" (at 213.36 132.08 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "-5V" (at 213.36 129.54 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 213.36 134.62 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 213.36 134.62 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 850323b0-c815-47aa-939f-03c7add6b113))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR012") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR032") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR038") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 229.87 76.2 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid f3b53079-fa57-432c-82e9-af900d77ad7d)
+    (property "Reference" "#PWR014" (at 229.87 82.55 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 229.87 81.28 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 229.87 76.2 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 229.87 76.2 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 8a97012a-5db7-42be-b21c-f1397422a5e3))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR014") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR034") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR028") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 107.95 73.66 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid fc312680-4e84-43d8-abad-8d9ca7cd36c6)
+    (property "Reference" "#PWR038" (at 107.95 80.01 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 107.95 78.74 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 107.95 73.66 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 107.95 73.66 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 759805d4-114e-490e-9113-f8a3c4e71b42))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR038") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR059") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/97fc232a-dbc7-49da-a6a0-e33e9aacbabd"
+          (reference "#PWR026") (unit 1)
+        )
+      )
+    )
+  )
+)
diff --git a/hardware/user_io.kicad_sch b/hardware/user_io.kicad_sch
new file mode 100755 (executable)
index 0000000..f0e3e60
--- /dev/null
@@ -0,0 +1,1030 @@
+(kicad_sch (version 20230121) (generator eeschema)
+
+  (uuid 0edcf05c-5355-4e2c-8fe9-25fab7c199bc)
+
+  (paper "A4")
+
+  (title_block
+    (title "User I/O")
+    (date "2023-08-08")
+    (company "bitgloo")
+    (comment 1 "Released under the CERN Open Hardware Licence Version 2 - Strongly Reciprocal")
+  )
+
+  (lib_symbols
+    (symbol "Device:LED_RAGB" (pin_names (offset 0) hide) (in_bom yes) (on_board yes)
+      (property "Reference" "D" (at 0 9.398 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "LED_RAGB" (at 0 -8.89 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 -1.27 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 0 -1.27 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "LED RGB diode" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "RGB LED, red/anode/green/blue" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "LED* LED_SMD:* LED_THT:*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "LED_RAGB_0_0"
+        (text "B" (at -1.905 -6.35 0)
+          (effects (font (size 1.27 1.27)))
+        )
+        (text "G" (at -1.905 -1.27 0)
+          (effects (font (size 1.27 1.27)))
+        )
+        (text "R" (at -1.905 3.81 0)
+          (effects (font (size 1.27 1.27)))
+        )
+      )
+      (symbol "LED_RAGB_0_1"
+        (polyline
+          (pts
+            (xy -1.27 -5.08)
+            (xy -2.54 -5.08)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.27 -5.08)
+            (xy 1.27 -5.08)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.27 -3.81)
+            (xy -1.27 -6.35)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.27 0)
+            (xy -2.54 0)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.27 1.27)
+            (xy -1.27 -1.27)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.27 5.08)
+            (xy -2.54 5.08)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.27 5.08)
+            (xy 1.27 5.08)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.27 6.35)
+            (xy -1.27 3.81)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 1.27 0)
+            (xy -1.27 0)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 1.27 0)
+            (xy 2.54 0)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.27 1.27)
+            (xy -1.27 -1.27)
+            (xy -1.27 -1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.27 6.35)
+            (xy -1.27 3.81)
+            (xy -1.27 3.81)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 1.27 -5.08)
+            (xy 2.032 -5.08)
+            (xy 2.032 5.08)
+            (xy 1.27 5.08)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 1.27 -3.81)
+            (xy 1.27 -6.35)
+            (xy -1.27 -5.08)
+            (xy 1.27 -3.81)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 1.27 1.27)
+            (xy 1.27 -1.27)
+            (xy -1.27 0)
+            (xy 1.27 1.27)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 1.27 6.35)
+            (xy 1.27 3.81)
+            (xy -1.27 5.08)
+            (xy 1.27 6.35)
+          )
+          (stroke (width 0.254) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.016 -3.81)
+            (xy 0.508 -2.286)
+            (xy -0.254 -2.286)
+            (xy 0.508 -2.286)
+            (xy 0.508 -3.048)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.016 1.27)
+            (xy 0.508 2.794)
+            (xy -0.254 2.794)
+            (xy 0.508 2.794)
+            (xy 0.508 2.032)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy -1.016 6.35)
+            (xy 0.508 7.874)
+            (xy -0.254 7.874)
+            (xy 0.508 7.874)
+            (xy 0.508 7.112)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 -3.81)
+            (xy 1.524 -2.286)
+            (xy 0.762 -2.286)
+            (xy 1.524 -2.286)
+            (xy 1.524 -3.048)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 1.27)
+            (xy 1.524 2.794)
+            (xy 0.762 2.794)
+            (xy 1.524 2.794)
+            (xy 1.524 2.032)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 6.35)
+            (xy 1.524 7.874)
+            (xy 0.762 7.874)
+            (xy 1.524 7.874)
+            (xy 1.524 7.112)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 1.27 -1.27) (end 1.27 1.27)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 1.27 1.27) (end 1.27 1.27)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 1.27 3.81) (end 1.27 6.35)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (rectangle (start 1.27 6.35) (end 1.27 6.35)
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (circle (center 2.032 0) (radius 0.254)
+          (stroke (width 0) (type default))
+          (fill (type outline))
+        )
+        (rectangle (start 2.794 8.382) (end -2.794 -7.62)
+          (stroke (width 0.254) (type default))
+          (fill (type background))
+        )
+      )
+      (symbol "LED_RAGB_1_1"
+        (pin passive line (at -5.08 5.08 0) (length 2.54)
+          (name "RK" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 5.08 0 180) (length 2.54)
+          (name "A" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -5.08 0 0) (length 2.54)
+          (name "GK" (effects (font (size 1.27 1.27))))
+          (number "3" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at -5.08 -5.08 0) (length 2.54)
+          (name "BK" (effects (font (size 1.27 1.27))))
+          (number "4" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Device:R_Potentiometer_US" (pin_names (offset 1.016) hide) (in_bom yes) (on_board yes)
+      (property "Reference" "RV" (at -4.445 0 90)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "R_Potentiometer_US" (at -2.54 0 90)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "resistor variable" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Potentiometer, US symbol" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "Potentiometer*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "R_Potentiometer_US_0_1"
+        (polyline
+          (pts
+            (xy 0 -2.286)
+            (xy 0 -2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.54)
+            (xy 0 2.286)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 2.54 0)
+            (xy 1.524 0)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 1.143 0)
+            (xy 2.286 0.508)
+            (xy 2.286 -0.508)
+            (xy 1.143 0)
+          )
+          (stroke (width 0) (type default))
+          (fill (type outline))
+        )
+        (polyline
+          (pts
+            (xy 0 -0.762)
+            (xy 1.016 -1.143)
+            (xy 0 -1.524)
+            (xy -1.016 -1.905)
+            (xy 0 -2.286)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0.762)
+            (xy 1.016 0.381)
+            (xy 0 0)
+            (xy -1.016 -0.381)
+            (xy 0 -0.762)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.286)
+            (xy 1.016 1.905)
+            (xy 0 1.524)
+            (xy -1.016 1.143)
+            (xy 0 0.762)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "R_Potentiometer_US_1_1"
+        (pin passive line (at 0 3.81 270) (length 1.27)
+          (name "1" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 3.81 0 180) (length 1.27)
+          (name "2" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 0 -3.81 90) (length 1.27)
+          (name "3" (effects (font (size 1.27 1.27))))
+          (number "3" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "Device:R_US" (pin_numbers hide) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "R" (at 2.54 0 90)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Value" "R_US" (at -2.54 0 90)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 1.016 -0.254 90)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "~" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "R res resistor" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Resistor, US symbol" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_fp_filters" "R_*" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "R_US_0_1"
+        (polyline
+          (pts
+            (xy 0 -2.286)
+            (xy 0 -2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.286)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 -0.762)
+            (xy 1.016 -1.143)
+            (xy 0 -1.524)
+            (xy -1.016 -1.905)
+            (xy 0 -2.286)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0.762)
+            (xy 1.016 0.381)
+            (xy 0 0)
+            (xy -1.016 -0.381)
+            (xy 0 -0.762)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.286)
+            (xy 1.016 1.905)
+            (xy 0 1.524)
+            (xy -1.016 1.143)
+            (xy 0 0.762)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "R_US_1_1"
+        (pin passive line (at 0 3.81 270) (length 1.27)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+        (pin passive line (at 0 -3.81 90) (length 1.27)
+          (name "~" (effects (font (size 1.27 1.27))))
+          (number "2" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "power:GND" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 -6.35 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "GND" (at 0 -3.81 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"GND\" , ground" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "GND_0_1"
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 -1.27)
+            (xy 1.27 -1.27)
+            (xy 0 -2.54)
+            (xy -1.27 -1.27)
+            (xy 0 -1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "GND_1_1"
+        (pin power_in line (at 0 0 270) (length 0) hide
+          (name "GND" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+    (symbol "power:VCC" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes)
+      (property "Reference" "#PWR" (at 0 -3.81 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Value" "VCC" (at 0 3.81 0)
+        (effects (font (size 1.27 1.27)))
+      )
+      (property "Footprint" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "Datasheet" "" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_keywords" "global power" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (property "ki_description" "Power symbol creates a global label with name \"VCC\"" (at 0 0 0)
+        (effects (font (size 1.27 1.27)) hide)
+      )
+      (symbol "VCC_0_1"
+        (polyline
+          (pts
+            (xy -0.762 1.27)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 0)
+            (xy 0 2.54)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+        (polyline
+          (pts
+            (xy 0 2.54)
+            (xy 0.762 1.27)
+          )
+          (stroke (width 0) (type default))
+          (fill (type none))
+        )
+      )
+      (symbol "VCC_1_1"
+        (pin power_in line (at 0 0 90) (length 0) hide
+          (name "VCC" (effects (font (size 1.27 1.27))))
+          (number "1" (effects (font (size 1.27 1.27))))
+        )
+      )
+    )
+  )
+
+
+  (wire (pts (xy 96.52 106.68) (xy 100.33 106.68))
+    (stroke (width 0) (type default))
+    (uuid 222aa4be-b44f-43d8-9f1d-36f978346489)
+  )
+  (wire (pts (xy 96.52 111.76) (xy 100.33 111.76))
+    (stroke (width 0) (type default))
+    (uuid 36caa1f7-82cf-4e39-998d-7cccd6c68c00)
+  )
+  (wire (pts (xy 110.49 106.68) (xy 114.3 106.68))
+    (stroke (width 0) (type default))
+    (uuid b3676852-c578-405f-a793-f6668e22af7a)
+  )
+  (wire (pts (xy 96.52 115.57) (xy 96.52 111.76))
+    (stroke (width 0) (type default))
+    (uuid cfee4500-40f6-4487-b611-6717f3037bb0)
+  )
+  (wire (pts (xy 96.52 97.79) (xy 96.52 101.6))
+    (stroke (width 0) (type default))
+    (uuid d26e9002-6368-445c-9889-91050d5d6bde)
+  )
+  (wire (pts (xy 96.52 101.6) (xy 100.33 101.6))
+    (stroke (width 0) (type default))
+    (uuid e9c734d6-1417-42bf-93f2-87e45d7558bc)
+  )
+
+  (text "Status LEDs and parameter knobs" (at 115.57 69.85 0)
+    (effects (font (size 2.54 2.54) (thickness 0.254) bold) (justify left bottom))
+    (uuid c13dc8fd-4ee1-4031-b224-1a84272505b9)
+  )
+
+  (global_label "PC0{slash}POT1" (shape input) (at 153.67 106.68 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid 00398924-6e31-4c1f-8993-22b891a1f27b)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 165.9407 106.6006 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+  (global_label "PC12{slash}LED_B" (shape input) (at 88.9 115.57 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid 0fd3b223-092e-4a18-9e3b-460e3e4605d7)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 74.5126 115.4906 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "PC10{slash}LED_R" (shape input) (at 88.9 97.79 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid 2074dbb5-c735-483e-a108-ecde670d15b4)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 74.5126 97.7106 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+  (global_label "PC1{slash}POT2" (shape input) (at 196.85 106.68 0) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify left))
+    (uuid 85823cba-5162-4cff-9c65-741e34b8bc70)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 209.1207 106.6006 0)
+      (effects (font (size 1.27 1.27)) (justify left) hide)
+    )
+  )
+  (global_label "PC11{slash}LED_G" (shape input) (at 88.9 106.68 180) (fields_autoplaced)
+    (effects (font (size 1.27 1.27)) (justify right))
+    (uuid cdea714e-c064-473d-a6c3-973f5beb0d4e)
+    (property "Intersheetrefs" "${INTERSHEET_REFS}" (at 74.5126 106.6006 0)
+      (effects (font (size 1.27 1.27)) (justify right) hide)
+    )
+  )
+
+  (symbol (lib_id "Device:R_Potentiometer_US") (at 193.04 106.68 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 04ebdf1c-ac9d-479f-992a-1ea0aeec5c23)
+    (property "Reference" "RV2" (at 190.5 105.4099 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Value" "10K" (at 190.5 107.9499 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Footprint" "Potentiometer_THT:Potentiometer_Bourns_3386P_Vertical" (at 193.04 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 193.04 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "3386P-1-103TLF" (at 193.04 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 5468099e-cb95-4972-9078-30e43b094d04))
+    (pin "2" (uuid 0de265c9-fa60-41cd-b5c8-3b2982f1e4cf))
+    (pin "3" (uuid 8d22e474-522c-442d-84f9-4d3795fa75e1))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "RV2") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "RV2") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa"
+          (reference "RV2") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:VCC") (at 193.04 102.87 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 0b7ce666-6cbf-4100-957e-48b86848c1c6)
+    (property "Reference" "#PWR056" (at 193.04 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "VCC" (at 193.04 97.79 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 193.04 102.87 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 193.04 102.87 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 4d67f819-24dd-4ed0-ae73-b63ed362a7b4))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR056") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR027") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa"
+          (reference "#PWR068") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 92.71 106.68 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 29f7e662-f721-4c3d-848d-61fc5171ba42)
+    (property "Reference" "R18" (at 92.71 100.33 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "240" (at 92.71 102.87 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 92.964 105.664 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 92.71 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RC0603JR-07240RL" (at 92.71 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid bc61001a-9375-4494-bd53-b146b8cbbfcd))
+    (pin "2" (uuid 0608467e-09dc-44ae-a62b-e571005c919b))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "R18") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "R16") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa"
+          (reference "R24") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 149.86 110.49 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 33bbd370-c8f7-49bf-97f1-713e8b674cb2)
+    (property "Reference" "#PWR058" (at 149.86 116.84 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 149.86 115.57 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 149.86 110.49 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 149.86 110.49 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid a8bd6d9a-853c-4922-aefa-69fd558f0efa))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR058") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR024") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa"
+          (reference "#PWR070") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:GND") (at 193.04 110.49 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 50eee7b6-f7e8-49f5-a03a-3e57c0517f9c)
+    (property "Reference" "#PWR059" (at 193.04 116.84 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "GND" (at 193.04 115.57 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 193.04 110.49 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 193.04 110.49 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 3b179701-3be9-44fd-b148-1cca65b5261f))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR059") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR028") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa"
+          (reference "#PWR071") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 92.71 97.79 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 63c4f82c-3aee-47eb-aaf9-4ff013fc8a40)
+    (property "Reference" "R17" (at 92.71 91.44 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "560" (at 92.71 93.98 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 92.964 96.774 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 92.71 97.79 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RC0603JR-07560RL" (at 92.71 97.79 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 261550e6-b14d-4948-b24c-2c241f5f4547))
+    (pin "2" (uuid dd48f3e0-3f5d-46bd-85da-f4476fc2bdae))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "R17") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "R15") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa"
+          (reference "R23") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:VCC") (at 149.86 102.87 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 881d1958-8c2a-4d5a-827d-7379482ba809)
+    (property "Reference" "#PWR055" (at 149.86 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "VCC" (at 149.86 97.79 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 149.86 102.87 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 149.86 102.87 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 3f92816b-1cb9-4f48-9c32-7731fb5014e6))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR055") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR023") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa"
+          (reference "#PWR067") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:LED_RAGB") (at 105.41 106.68 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid 9ec9653f-c3a4-41f7-9b1c-80cc24251ffe)
+    (property "Reference" "D7" (at 105.41 93.98 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "LED_RAGB" (at 105.41 96.52 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "LED_SMD:LED_Kingbright_AAA3528ESGCT" (at 105.41 107.95 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "~" (at 105.41 107.95 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "AAA3528SEEZGKQBKS" (at 105.41 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 363fad31-16fa-4be8-b5d2-c004f7616847))
+    (pin "2" (uuid c958bf86-e891-47cb-9056-d1b81e2b51c9))
+    (pin "3" (uuid 42dd1fb6-acbd-4b4d-9b66-616c7b1fcb01))
+    (pin "4" (uuid 0998c988-3a4a-4a56-b61e-668bef55cafd))
+    (instances
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa"
+          (reference "D7") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_US") (at 92.71 115.57 90) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid d04657bd-c6dc-4346-b2bd-fdac6c2c8be6)
+    (property "Reference" "R19" (at 92.71 109.22 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Value" "240" (at 92.71 111.76 90)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "Resistor_SMD:R_0603_1608Metric" (at 92.964 114.554 90)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 92.71 115.57 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "RC0603JR-07240RL" (at 92.71 115.57 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 58518da9-c923-45a5-928d-33b164e619f0))
+    (pin "2" (uuid 55ad7eab-6366-4c1f-aa56-1782827f3e8e))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "R19") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "R17") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa"
+          (reference "R25") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "power:VCC") (at 114.3 106.68 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid f28364df-0228-4487-bd82-798aa123dfd6)
+    (property "Reference" "#PWR057" (at 114.3 110.49 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Value" "VCC" (at 114.3 101.6 0)
+      (effects (font (size 1.27 1.27)))
+    )
+    (property "Footprint" "" (at 114.3 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 114.3 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid e5e3bf25-e96c-47aa-b8ea-b90319a51b17))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "#PWR057") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "#PWR019") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa"
+          (reference "#PWR069") (unit 1)
+        )
+      )
+    )
+  )
+
+  (symbol (lib_id "Device:R_Potentiometer_US") (at 149.86 106.68 0) (unit 1)
+    (in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
+    (uuid f4a975b9-7021-410d-9761-44f0c502022e)
+    (property "Reference" "RV1" (at 147.32 105.4099 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Value" "10K" (at 147.32 107.9499 0)
+      (effects (font (size 1.27 1.27)) (justify right))
+    )
+    (property "Footprint" "Potentiometer_THT:Potentiometer_Bourns_3386P_Vertical" (at 149.86 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Datasheet" "" (at 149.86 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (property "Part Number" "3386P-1-103TLF" (at 149.86 106.68 0)
+      (effects (font (size 1.27 1.27)) hide)
+    )
+    (pin "1" (uuid 1a649be3-376f-4b5d-81c4-a51773e11622))
+    (pin "2" (uuid 2642011f-a119-4c97-a567-49296afdd169))
+    (pin "3" (uuid 402bf8e9-23dc-4da7-aabf-504b3b356586))
+    (instances
+      (project "stmdsp_rev3"
+        (path "/975c3983-57e7-4e06-a697-831e5209dd80"
+          (reference "RV1") (unit 1)
+        )
+      )
+      (project "DSP PAW add-on board"
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/270d19d2-3af2-41db-ad1c-33373417ec82"
+          (reference "RV1") (unit 1)
+        )
+        (path "/c291319b-d76e-4fda-91cc-061acff65f9f/7798c5d5-f9b1-4b2e-811c-a15abcd34bfa"
+          (reference "RV1") (unit 1)
+        )
+      )
+    )
+  )
+)