diff --git a/MathLab/MathLab.cpp b/MathLab/MathLab.cpp
new file mode 100644
index 0000000..eac9488
Binary files /dev/null and b/MathLab/MathLab.cpp differ
diff --git a/MathLab/MathLab.sln b/MathLab/MathLab.sln
new file mode 100644
index 0000000..077bff1
--- /dev/null
+++ b/MathLab/MathLab.sln
@@ -0,0 +1,41 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28010.2041
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MathLab", "MathLab.vcxproj", "{3314C41D-F478-4CB2-A408-F0B113C7AF2C}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MathLabTests", "..\MathLabTests\MathLabTests.vcxproj", "{5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Debug|x64.ActiveCfg = Debug|x64
+ {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Debug|x64.Build.0 = Debug|x64
+ {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Debug|x86.ActiveCfg = Debug|Win32
+ {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Debug|x86.Build.0 = Debug|Win32
+ {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Release|x64.ActiveCfg = Release|x64
+ {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Release|x64.Build.0 = Release|x64
+ {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Release|x86.ActiveCfg = Release|Win32
+ {3314C41D-F478-4CB2-A408-F0B113C7AF2C}.Release|x86.Build.0 = Release|Win32
+ {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Debug|x64.ActiveCfg = Debug|x64
+ {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Debug|x64.Build.0 = Debug|x64
+ {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Debug|x86.ActiveCfg = Debug|Win32
+ {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Debug|x86.Build.0 = Debug|Win32
+ {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Release|x64.ActiveCfg = Release|x64
+ {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Release|x64.Build.0 = Release|x64
+ {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Release|x86.ActiveCfg = Release|Win32
+ {5DBEC0A4-DD81-4B5D-81E7-22940933E8F3}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {9A5CE5FA-B1B1-4AF3-9E24-6C95A4696F5F}
+ EndGlobalSection
+EndGlobal
diff --git a/MathLab/MathLab.vcxproj b/MathLab/MathLab.vcxproj
new file mode 100644
index 0000000..4c761d5
--- /dev/null
+++ b/MathLab/MathLab.vcxproj
@@ -0,0 +1,176 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 15.0
+ {3314C41D-F478-4CB2-A408-F0B113C7AF2C}
+ Win32Proj
+ MathLab
+ 10.0.17134.0
+
+
+
+ Application
+ true
+ v141
+ Unicode
+
+
+ Application
+ false
+ v141
+ true
+ Unicode
+
+
+ Application
+ true
+ v141
+ Unicode
+
+
+ Application
+ false
+ v141
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+ false
+
+
+
+ NotUsing
+ Level3
+ Disabled
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ stdcpp17
+
+
+ Console
+ true
+
+
+
+
+ NotUsing
+ Level3
+ Disabled
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ stdcpp17
+
+
+ Console
+ true
+
+
+
+
+ NotUsing
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ stdcpp17
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ NotUsing
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ stdcpp17
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MathLab/MathLab.vcxproj.filters b/MathLab/MathLab.vcxproj.filters
new file mode 100644
index 0000000..8495eb4
--- /dev/null
+++ b/MathLab/MathLab.vcxproj.filters
@@ -0,0 +1,45 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/MathLab/block_sequence.cpp b/MathLab/block_sequence.cpp
new file mode 100644
index 0000000..6f90347
--- /dev/null
+++ b/MathLab/block_sequence.cpp
@@ -0,0 +1,77 @@
+#include "block_sequence.h"
+#include
+#include
+
+namespace {
+ std::pair extract_type(const std::string& line)
+ {
+ std::string block_type;
+ std::istringstream line_stream(line);
+ line_stream >> block_type;
+ return std::make_pair(std::move(block_type), std::move(line_stream));
+ }
+}
+
+namespace mathlab {
+
+ block_sequence::block_sequence(factory& factory) : factory_(factory) {}
+
+ std::string block_sequence::append_from(std::istream& input_stream) {
+ std::string block_type;
+ std::istringstream constants;
+ std::string invalid_lines;
+ std::string line;
+ while (!input_stream.eof()) {
+ std::getline(input_stream, line);
+ std::tie(block_type, constants) = extract_type(line);
+ if (!block_type.empty()) {
+ std::unique_ptr block;
+ try {
+ block = factory_.create(block_type, constants);
+ }
+ catch (std::invalid_argument&) {
+ // Deliberately empty - handle same as unknown command
+ }
+ if (block != nullptr)
+ blocks_.push_back(std::make_pair(std::move(block_type), std::move(block)));
+ else
+ invalid_lines += line + "\n";
+ }
+ }
+ return invalid_lines;
+ }
+
+ std::string block_sequence::load_from(std::istream& input_stream) {
+ blocks_.clear();
+ return append_from(input_stream);
+ }
+
+ std::ostream& block_sequence::dump(std::ostream& to_stream, bool with_line_numbers) const {
+ int position = 1;
+ for (auto& name_with_block : blocks_) {
+ if (with_line_numbers)
+ to_stream << position++ << ": ";
+ to_stream << name_with_block.first << ' ' << *name_with_block.second << std::endl;
+ }
+ return to_stream;
+ }
+
+ double block_sequence::eval(double input) const {
+ return std::accumulate(
+ blocks_.cbegin(),
+ blocks_.cend(),
+ input,
+ [](double val, const std::pair> &name_with_block) { return name_with_block.second->eval(val); });
+ }
+
+ void block_sequence::remove_at(unsigned index) {
+ if (index < blocks_.size())
+ blocks_.erase(blocks_.begin() + index);
+ }
+
+ void block_sequence::move_to_beginning(unsigned index) {
+ if (index < blocks_.size() && index > 0) {
+ std::swap(blocks_[0], blocks_[index]);
+ }
+ }
+}
diff --git a/MathLab/block_sequence.h b/MathLab/block_sequence.h
new file mode 100644
index 0000000..ce810a2
--- /dev/null
+++ b/MathLab/block_sequence.h
@@ -0,0 +1,24 @@
+#pragma once
+#include "factory.h"
+#include
+#include
+#include
+
+namespace mathlab {
+ class block_sequence {
+ std::vector>> blocks_;
+ factory& factory_;
+ public:
+ block_sequence(factory& factory);
+ // Appends one or more block from supplied stream
+ // Returns invalid lines
+ std::string append_from(std::istream& input_stream);
+ // Load sequence from supplied stream
+ // Returns invalid lines
+ std::string load_from(std::istream& input_stream);
+ std::ostream& dump(std::ostream& to_stream, bool with_line_numbers) const;
+ double eval(double input) const;
+ void remove_at(unsigned index);
+ void move_to_beginning(unsigned index);
+ };
+}
diff --git a/MathLab/blocks.cpp b/MathLab/blocks.cpp
new file mode 100644
index 0000000..46892ad
--- /dev/null
+++ b/MathLab/blocks.cpp
@@ -0,0 +1,9 @@
+#include "blocks.h"
+
+namespace mathlab
+{
+ std::ostream& operator<<(std::ostream& stream, const block& block) {
+ block.dump(stream);
+ return stream;
+ }
+}
\ No newline at end of file
diff --git a/MathLab/blocks.h b/MathLab/blocks.h
new file mode 100644
index 0000000..a200c30
--- /dev/null
+++ b/MathLab/blocks.h
@@ -0,0 +1,69 @@
+#pragma once
+#include
+#include
+#include "tuple_serialization.h"
+
+namespace mathlab
+{
+ // block interface
+ struct block {
+ virtual double eval(double input) const = 0;
+ virtual void dump(std::ostream& to_stream) const = 0;
+ virtual ~block() = default;
+ };
+
+ template
+ struct block_with_constants : block {
+ double eval(double input) const override {
+ auto input_with_constants = std::tuple_cat(std::tuple(input), constants_);
+ return std::apply(callable_, input_with_constants);
+ }
+ // Serializes all constants to stream
+ void dump(std::ostream& to) const override {
+ tuple_serialization::serialize(to, constants_);
+ }
+ // Creates new block from stream
+ // throws invalid_argument if stream cannot be converted
+ template
+ static std::unique_ptr create_from_stream(std::istream& input_stream) {
+ std::tuple to;
+ tuple_serialization::deserialize(input_stream, to);
+ TBlock* block = std::apply(create, to);
+ return std::unique_ptr(block);
+ }
+ protected:
+ block_with_constants(TCallable callable, TArgs... args) : callable_(std::move(callable)), constants_(std::tie(args...)) {}
+ private:
+ std::tuple constants_;
+ TCallable callable_;
+ };
+
+ std::ostream& operator<<(std::ostream& stream, const block& block);
+
+ // Supported blocks:
+
+ struct identity final : block_with_constants> {
+ identity() : block_with_constants([](auto input) { return input; }) {}
+ };
+
+ struct addition final : block_with_constants, double> {
+ addition(double constant) : block_with_constants(std::plus(), constant) {}
+ };
+
+ struct multiplication final : block_with_constants, double> {
+ multiplication(double constant) : block_with_constants(std::multiplies(), constant) {}
+ };
+
+ struct power final : block_with_constants, double> {
+ power(double constant) : block_with_constants([](double input, double other) { return std::pow(input, other); }, constant) {}
+ };
+
+ struct condition final : block_with_constants, double> {
+ condition(double constant) :
+ block_with_constants([](double input, double other) { return input < other ? -1 : (input == other ? 0 : 1); }, constant) {}
+ };
+
+ struct limit final : block_with_constants, double, double> {
+ limit(double lower, double upper) : block_with_constants(std::clamp, lower, upper) {}
+ };
+}
diff --git a/MathLab/factory.cpp b/MathLab/factory.cpp
new file mode 100644
index 0000000..10c9810
--- /dev/null
+++ b/MathLab/factory.cpp
@@ -0,0 +1,32 @@
+#include "factory.h"
+
+namespace mathlab
+{
+ template
+ void factory::register_block(const std::string& type_name) {
+ types_[type_name] = TBlock::template create_from_stream;
+ }
+
+ std::unique_ptr factory::create(const std::string& type_name, std::istream& stream) const {
+ const auto found = types_.find(type_name);
+ if (found != types_.end())
+ return found->second(stream);
+ return std::unique_ptr();
+ }
+
+ std::ostream& factory::dump_registered(std::ostream& to_stream) {
+ for (auto pair : types_)
+ to_stream << pair.first << ' ';
+ return to_stream;
+ }
+
+ void register_all_blocks(factory& factory) {
+ factory.register_block("identity");
+ factory.register_block("addition");
+ factory.register_block("multiplication");
+ factory.register_block("power");
+ factory.register_block("condition");
+ factory.register_block("addition");
+ factory.register_block("limit");
+ }
+}
diff --git a/MathLab/factory.h b/MathLab/factory.h
new file mode 100644
index 0000000..5ced743
--- /dev/null
+++ b/MathLab/factory.h
@@ -0,0 +1,20 @@
+#pragma once
+#include "blocks.h"
+#include
+#include