diff --git a/BlockBuster/BlockBuster.sln b/BlockBuster/BlockBuster.sln
new file mode 100644
index 0000000..bad6803
--- /dev/null
+++ b/BlockBuster/BlockBuster.sln
@@ -0,0 +1,51 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28010.2036
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlockBusterEngineUnitTests", "BlockBusterEngineUnitTests\BlockBusterEngineUnitTests.vcxproj", "{CC80ED32-1BE1-4950-85C7-AC8825FD569C}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlockBusterEngine", "BlockBusterEngine\BlockBusterEngine.vcxproj", "{03D655DF-395E-4EF1-A67D-33F39C2579DC}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlockBuster", "BlockBuster\BlockBuster.vcxproj", "{DC18B2C9-FD56-4949-BC6F-93905F5C1D62}"
+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
+ {CC80ED32-1BE1-4950-85C7-AC8825FD569C}.Debug|x64.ActiveCfg = Debug|x64
+ {CC80ED32-1BE1-4950-85C7-AC8825FD569C}.Debug|x64.Build.0 = Debug|x64
+ {CC80ED32-1BE1-4950-85C7-AC8825FD569C}.Debug|x86.ActiveCfg = Debug|Win32
+ {CC80ED32-1BE1-4950-85C7-AC8825FD569C}.Debug|x86.Build.0 = Debug|Win32
+ {CC80ED32-1BE1-4950-85C7-AC8825FD569C}.Release|x64.ActiveCfg = Release|x64
+ {CC80ED32-1BE1-4950-85C7-AC8825FD569C}.Release|x64.Build.0 = Release|x64
+ {CC80ED32-1BE1-4950-85C7-AC8825FD569C}.Release|x86.ActiveCfg = Release|Win32
+ {CC80ED32-1BE1-4950-85C7-AC8825FD569C}.Release|x86.Build.0 = Release|Win32
+ {03D655DF-395E-4EF1-A67D-33F39C2579DC}.Debug|x64.ActiveCfg = Debug|x64
+ {03D655DF-395E-4EF1-A67D-33F39C2579DC}.Debug|x64.Build.0 = Debug|x64
+ {03D655DF-395E-4EF1-A67D-33F39C2579DC}.Debug|x86.ActiveCfg = Debug|Win32
+ {03D655DF-395E-4EF1-A67D-33F39C2579DC}.Debug|x86.Build.0 = Debug|Win32
+ {03D655DF-395E-4EF1-A67D-33F39C2579DC}.Release|x64.ActiveCfg = Release|x64
+ {03D655DF-395E-4EF1-A67D-33F39C2579DC}.Release|x64.Build.0 = Release|x64
+ {03D655DF-395E-4EF1-A67D-33F39C2579DC}.Release|x86.ActiveCfg = Release|Win32
+ {03D655DF-395E-4EF1-A67D-33F39C2579DC}.Release|x86.Build.0 = Release|Win32
+ {DC18B2C9-FD56-4949-BC6F-93905F5C1D62}.Debug|x64.ActiveCfg = Debug|x64
+ {DC18B2C9-FD56-4949-BC6F-93905F5C1D62}.Debug|x64.Build.0 = Debug|x64
+ {DC18B2C9-FD56-4949-BC6F-93905F5C1D62}.Debug|x86.ActiveCfg = Debug|Win32
+ {DC18B2C9-FD56-4949-BC6F-93905F5C1D62}.Debug|x86.Build.0 = Debug|Win32
+ {DC18B2C9-FD56-4949-BC6F-93905F5C1D62}.Release|x64.ActiveCfg = Release|x64
+ {DC18B2C9-FD56-4949-BC6F-93905F5C1D62}.Release|x64.Build.0 = Release|x64
+ {DC18B2C9-FD56-4949-BC6F-93905F5C1D62}.Release|x86.ActiveCfg = Release|Win32
+ {DC18B2C9-FD56-4949-BC6F-93905F5C1D62}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {30C0DFDE-7623-4FD8-8CC8-EA430CA639D6}
+ EndGlobalSection
+EndGlobal
diff --git a/BlockBuster/BlockBuster/BlockBuster.vcxproj b/BlockBuster/BlockBuster/BlockBuster.vcxproj
new file mode 100644
index 0000000..a0df3f2
--- /dev/null
+++ b/BlockBuster/BlockBuster/BlockBuster.vcxproj
@@ -0,0 +1,172 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 15.0
+ {DC18B2C9-FD56-4949-BC6F-93905F5C1D62}
+ Win32Proj
+ BlockBuster
+ 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
+
+
+
+ Use
+ Level3
+ Disabled
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ pch.h
+ stdcpp17
+
+
+ Console
+ true
+
+
+
+
+ Use
+ Level3
+ Disabled
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ pch.h
+ stdcpp17
+
+
+ Console
+ true
+
+
+
+
+ Use
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ pch.h
+ stdcpp17
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Use
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ pch.h
+ stdcpp17
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BlockBuster/BlockBuster/BlockBuster.vcxproj.filters b/BlockBuster/BlockBuster/BlockBuster.vcxproj.filters
new file mode 100644
index 0000000..b3d517b
--- /dev/null
+++ b/BlockBuster/BlockBuster/BlockBuster.vcxproj.filters
@@ -0,0 +1,30 @@
+
+
+
+
+ {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
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/BlockBuster/BlockBuster/main.cpp b/BlockBuster/BlockBuster/main.cpp
new file mode 100644
index 0000000..8896916
Binary files /dev/null and b/BlockBuster/BlockBuster/main.cpp differ
diff --git a/BlockBuster/BlockBuster/pch.cpp b/BlockBuster/BlockBuster/pch.cpp
new file mode 100644
index 0000000..a38f8a3
Binary files /dev/null and b/BlockBuster/BlockBuster/pch.cpp differ
diff --git a/BlockBuster/BlockBuster/pch.h b/BlockBuster/BlockBuster/pch.h
new file mode 100644
index 0000000..beb6e4c
Binary files /dev/null and b/BlockBuster/BlockBuster/pch.h differ
diff --git a/BlockBuster/BlockBusterEngine/BlockBusterEngine.vcxproj b/BlockBuster/BlockBusterEngine/BlockBusterEngine.vcxproj
new file mode 100644
index 0000000..14e0621
--- /dev/null
+++ b/BlockBuster/BlockBusterEngine/BlockBusterEngine.vcxproj
@@ -0,0 +1,178 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 15.0
+ {03D655DF-395E-4EF1-A67D-33F39C2579DC}
+ Win32Proj
+ BlockBusterEngine
+ 10.0.17134.0
+
+
+
+ StaticLibrary
+ true
+ v141
+ Unicode
+
+
+ StaticLibrary
+ false
+ v141
+ true
+ Unicode
+
+
+ StaticLibrary
+ true
+ v141
+ Unicode
+
+
+ StaticLibrary
+ false
+ v141
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+ false
+
+
+
+ Use
+ Level3
+ Disabled
+ true
+ WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ pch.h
+ stdcpp17
+
+
+ Windows
+ true
+
+
+
+
+ Use
+ Level3
+ Disabled
+ true
+ _DEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ pch.h
+ stdcpp17
+
+
+ Windows
+ true
+
+
+
+
+ Use
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ pch.h
+ stdcpp17
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+ Use
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ NDEBUG;_LIB;%(PreprocessorDefinitions)
+ true
+ pch.h
+ stdcpp17
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BlockBuster/BlockBusterEngine/BlockBusterEngine.vcxproj.filters b/BlockBuster/BlockBusterEngine/BlockBusterEngine.vcxproj.filters
new file mode 100644
index 0000000..682fb27
--- /dev/null
+++ b/BlockBuster/BlockBusterEngine/BlockBusterEngine.vcxproj.filters
@@ -0,0 +1,48 @@
+
+
+
+
+ {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
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/BlockBuster/BlockBusterEngine/block.h b/BlockBuster/BlockBusterEngine/block.h
new file mode 100644
index 0000000..273a385
--- /dev/null
+++ b/BlockBuster/BlockBusterEngine/block.h
@@ -0,0 +1,27 @@
+#pragma once
+#include "pch.h"
+
+namespace blockbuster::engine
+{
+ template
+ using block = std::function;
+
+ template
+ using block_unique_ptr = std::unique_ptr>;
+
+ class block_id final
+ {
+ public:
+ inline static const std::string identity = "identity";
+ inline static const std::string addition = "addition";
+ inline static const std::string multiplication = "multiplication";
+ inline static const std::string power = "power";
+ inline static const std::string limit = "limit";
+ inline static const std::string condition = "condition";
+
+ inline static const std::map blocks =
+ {
+ {identity, 0}, {addition, 1}, {multiplication, 1}, {power, 1}, {condition, 1}, {limit,2}
+ };
+ };
+}
diff --git a/BlockBuster/BlockBusterEngine/block_editor.h b/BlockBuster/BlockBusterEngine/block_editor.h
new file mode 100644
index 0000000..455fc9d
--- /dev/null
+++ b/BlockBuster/BlockBusterEngine/block_editor.h
@@ -0,0 +1,46 @@
+#pragma once
+#include "pch.h"
+#include "utils.h"
+
+namespace
+{
+ using namespace blockbuster::engine;
+
+ template
+ std::string read_block_params(std::istream& is, unsigned count)
+ {
+ if (count == 0)
+ return "";
+
+ T param;
+ is >> param;
+ return param + " " +
+ }
+
+ std::string read_block(std::istream& is)
+ {
+ std::string block_name;
+ is >> block_name;
+
+ }
+}
+
+namespace blockbuster::engine
+{
+ template
+ std::vector block_stream_reader(std::istream& is)
+ {
+ std::vector result;
+
+ std::transform(
+ std::istream_iterator(is),
+ std::istream_iterator(),
+ std::back_inserter(result),
+ [&is](const std::string& block_id)
+ {
+ return "";
+ });
+
+ return result;
+ }
+}
diff --git a/BlockBuster/BlockBusterEngine/block_editor_factory.h b/BlockBuster/BlockBusterEngine/block_editor_factory.h
new file mode 100644
index 0000000..ed52119
--- /dev/null
+++ b/BlockBuster/BlockBusterEngine/block_editor_factory.h
@@ -0,0 +1,25 @@
+#pragma once
+#include "pch.h"
+#include "utils.h"
+
+namespace blockbuster::engine
+{
+ template
+ std::vector blockchain_editor_factory(std::istream& is)
+ {
+ std::vector blocks_strings;
+ std::copy(
+ std::istream_iterator(is),
+ std::istream_iterator(),
+ std::back_inserter(blocks_strings));
+
+ return blocks_strings;
+ }
+
+ template
+ std::vector blockchain_editor_factory(const std::string& str)
+ {
+ auto iss = std::istringstream(str);
+ return blockchain_editor_factory(iss);
+ }
+}
\ No newline at end of file
diff --git a/BlockBuster/BlockBusterEngine/block_factory.h b/BlockBuster/BlockBusterEngine/block_factory.h
new file mode 100644
index 0000000..a9d6d3b
--- /dev/null
+++ b/BlockBuster/BlockBusterEngine/block_factory.h
@@ -0,0 +1,110 @@
+#pragma once
+#include "pch.h"
+#include "block.h"
+#include "utils.h"
+
+namespace blockbuster::engine
+{
+ template
+ using block_factory = std::function(std::istream&)>;
+}
+
+namespace
+{
+ using namespace blockbuster::engine;
+ using namespace blockbuster::engine::utils;
+
+ template
+ block_unique_ptr identity_factory(std::istream&)
+ {
+ return std::make_unique>([](const T& input) { return input; });
+ }
+
+ template
+ block_unique_ptr addition_factory(std::istream& is)
+ {
+ return std::make_unique>([n = read_param(is, 0)](const T& input) { return input + n; });
+ }
+
+ template
+ block_unique_ptr multiplication_factory(std::istream& is)
+ {
+ return std::make_unique>([n = read_param(is, 1)](const T& input) { return input * n; });
+ }
+
+ template
+ block_unique_ptr power_factory(std::istream& is)
+ {
+ return std::make_unique>([n = read_param(is, 1)](const T& input)
+ {
+ return std::pow(input, n);
+ });
+ }
+
+ template
+ block_unique_ptr condition_factory(std::istream& is)
+ {
+ return std::make_unique>([n = read_param(is, 0)](const T& input)
+ {
+ return input < n ? -1 : n < input ? 1 : 0;
+ });
+ }
+
+ template
+ block_unique_ptr limit_factory(std::istream& is)
+ {
+ T a = read_param(is, std::numeric_limits::min());
+ T b = read_param(is, std::numeric_limits::max());;
+ return std::make_unique>([min = std::min(a, b), max = std::max(a, b)](const T& input)
+ {
+ return
+ input < min ? min :
+ max < input ? max :
+ input;
+ });
+ }
+
+ template
+ const block_factory* get_block_factory(const std::string& id)
+ {
+ static const std::map> map =
+ {
+ {block_id::identity, identity_factory },
+ {block_id::addition, addition_factory},
+ {block_id::multiplication, multiplication_factory},
+ {block_id::power, power_factory},
+ {block_id::limit, limit_factory},
+ {block_id::condition, condition_factory}
+ };
+ const auto it = map.find(id);
+ return
+ it != map.end() ?
+ &it->second :
+ nullptr;
+ }
+}
+
+namespace blockbuster::engine
+{
+ template
+ block_unique_ptr get_block(const std::string& id, std::istream& is)
+ {
+ const auto block_factory = get_block_factory(id);
+ return block_factory != nullptr ? (*block_factory)(is) : block_unique_ptr();
+ }
+
+ template
+ block_unique_ptr get_block(std::istream& is)
+ {
+ std::string block_id;
+ is >> block_id;
+ return get_block(block_id, is);
+ }
+
+ template
+ block_unique_ptr get_block(const std::string& block_definition)
+ {
+ std::istringstream iss(block_definition);
+ return get_block(iss);
+ }
+}
diff --git a/BlockBuster/BlockBusterEngine/blockchain_cruncher.h b/BlockBuster/BlockBusterEngine/blockchain_cruncher.h
new file mode 100644
index 0000000..ef9963c
--- /dev/null
+++ b/BlockBuster/BlockBusterEngine/blockchain_cruncher.h
@@ -0,0 +1,15 @@
+#pragma once
+#include "pch.h"
+#include "block.h"
+
+namespace blockbuster::engine
+{
+ template
+ T blockchain_cruncher(const iterator begin, const iterator end, const T input)
+ {
+ return std::accumulate(begin, end, input, [](const T& input, const std::unique_ptr>& func)
+ {
+ return func != nullptr ? (*func)(input) : input;
+ });
+ }
+}
diff --git a/BlockBuster/BlockBusterEngine/blockchain_factory.h b/BlockBuster/BlockBusterEngine/blockchain_factory.h
new file mode 100644
index 0000000..1e67757
--- /dev/null
+++ b/BlockBuster/BlockBusterEngine/blockchain_factory.h
@@ -0,0 +1,30 @@
+#pragma once
+#include "pch.h"
+#include "block_factory.h"
+
+namespace blockbuster::engine
+{
+ template
+ std::vector> blockchain_factory(std::istream& is)
+ {
+ std::vector> result;
+ std::transform(
+ std::istream_iterator(is),
+ std::istream_iterator(),
+ std::back_inserter(result),
+ [&is](const std::string& block_id) { return get_block(block_id, is); });
+
+ result.erase(
+ std::remove_if(result.begin(), result.end(), [](const block_unique_ptr& ptr) { return ptr == nullptr; }),
+ result.end());
+
+ return result;
+ }
+
+ template
+ std::vector> blockchain_factory(const std::string& str)
+ {
+ auto iss = std::istringstream(str);
+ return blockchain_factory(iss);
+ }
+}
diff --git a/BlockBuster/BlockBusterEngine/pch.cpp b/BlockBuster/BlockBusterEngine/pch.cpp
new file mode 100644
index 0000000..a38f8a3
Binary files /dev/null and b/BlockBuster/BlockBusterEngine/pch.cpp differ
diff --git a/BlockBuster/BlockBusterEngine/pch.h b/BlockBuster/BlockBusterEngine/pch.h
new file mode 100644
index 0000000..1be903e
Binary files /dev/null and b/BlockBuster/BlockBusterEngine/pch.h differ
diff --git a/BlockBuster/BlockBusterEngine/utils.cpp b/BlockBuster/BlockBusterEngine/utils.cpp
new file mode 100644
index 0000000..a725b5d
--- /dev/null
+++ b/BlockBuster/BlockBusterEngine/utils.cpp
@@ -0,0 +1,13 @@
+#include "pch.h"
+#include "utils.h"
+
+namespace blockbuster::engine::utils
+{
+ std::string trim(const std::string &s)
+ {
+ auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) {return std::isspace(c); });
+ auto wsback = std::find_if_not(s.rbegin(), s.rend(), [](int c) {return std::isspace(c); }).base();
+ return (wsback <= wsfront ? std::string() : std::string(wsfront, wsback));
+ }
+}
+
diff --git a/BlockBuster/BlockBusterEngine/utils.h b/BlockBuster/BlockBusterEngine/utils.h
new file mode 100644
index 0000000..db924d7
--- /dev/null
+++ b/BlockBuster/BlockBusterEngine/utils.h
@@ -0,0 +1,101 @@
+#pragma once
+#include "pch.h"
+#include
+
+namespace blockbuster::engine::utils
+{
+ template
+ std::string join(
+ iterator begin,
+ iterator end,
+ const std::string& separator)
+ {
+ using value_type = typename std::iterator_traits::value_type;
+
+ std::ostringstream oss;
+ std::copy(begin, end, std::ostream_iterator(oss, separator.c_str()));
+ auto res = oss.str();
+ auto size = static_cast(res.size()) - static_cast(separator.size());
+ if (0 <= size)
+ res.erase(res.begin() + size, res.end());
+ return res;
+ }
+ template
+ T read_param(std::istream& is, const T& default_val)
+ {
+ T n = default_val;
+ is >> n;
+ return n;
+ }
+
+ template
+ std::string get_params_as_string(std::istream& is, unsigned count)
+ {
+ if (count == 0)
+ return "";
+ std::ostringstream oss;
+ oss << read_param(is, 0);
+ if (1 < count)
+ oss << ' ' << get_params_as_string(is, count - 1);
+ return oss.str();
+ }
+
+ template
+ std::string stringify(const T& t)
+ {
+ std::ostringstream string_stream;
+ string_stream << t;
+ return string_stream.str();
+ }
+
+ template
+ std::string stringify(const T& first, Args ... args)
+ {
+ return stringify(first) + " " + stringify(args...);
+ }
+
+ std::string trim(const std::string &);
+
+ class string_separated_by_char
+ {
+ protected:
+ string_separated_by_char(char separator) : separator_(separator) {}
+
+ public:
+ virtual ~string_separated_by_char() = default;
+ operator std::string() const { return str_; }
+ const char separator_;
+
+ private:
+ std::string str_;
+
+ friend std::istream& operator>>(std::istream& is, string_separated_by_char& result)
+ {
+ return std::getline(is, result.str_, result.separator_);
+ }
+ };
+
+ class string_separated_by_newline final : public string_separated_by_char
+ {
+ public:
+ explicit string_separated_by_newline()
+ : string_separated_by_char('\n')
+ { }
+ };
+
+ class string_separated_by_semicolon final : public string_separated_by_char
+ {
+ public:
+ explicit string_separated_by_semicolon()
+ : string_separated_by_char(';')
+ { }
+ };
+
+ class string_separated_by_comma final : public string_separated_by_char
+ {
+ public:
+ explicit string_separated_by_comma()
+ : string_separated_by_char(',')
+ { }
+ };
+}
diff --git a/BlockBuster/BlockBusterEngineUnitTests/BlockBusterEngineUnitTests.vcxproj b/BlockBuster/BlockBusterEngineUnitTests/BlockBusterEngineUnitTests.vcxproj
new file mode 100644
index 0000000..1da3487
--- /dev/null
+++ b/BlockBuster/BlockBusterEngineUnitTests/BlockBusterEngineUnitTests.vcxproj
@@ -0,0 +1,186 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 15.0
+ {CC80ED32-1BE1-4950-85C7-AC8825FD569C}
+ Win32Proj
+ BlockBusterEngineUnitTests
+ 10.0.17134.0
+ BlockBusterEngineUnitTests
+
+
+
+ Application
+ true
+ v141
+ Unicode
+
+
+ Application
+ false
+ v141
+ true
+ Unicode
+
+
+ Application
+ true
+ v141
+ Unicode
+
+
+ Application
+ false
+ v141
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ ..\BlockBuster\BlockBusterEngine;$(IncludePath)
+
+
+ true
+ ..\BlockBuster\BlockBusterEngine;$(IncludePath)
+
+
+ false
+ ..\BlockBuster\BlockBusterEngine;$(IncludePath)
+
+
+ false
+ ..\BlockBuster\BlockBusterEngine;$(IncludePath)
+
+
+
+ NotUsing
+ Level3
+ Disabled
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ pch.h
+ stdcpp17
+
+
+ Console
+ true
+ BlockBusterEngine.lib;%(AdditionalDependencies)
+ $(OutDir)
+
+
+
+
+ NotUsing
+ Level3
+ Disabled
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ pch.h
+ stdcpp17
+
+
+ Console
+ true
+ BlockBusterEngine.lib;%(AdditionalDependencies)
+ $(OutDir)
+
+
+
+
+ NotUsing
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ pch.h
+ stdcpp17
+
+
+ Console
+ true
+ true
+ true
+ BlockBusterEngine.lib;%(AdditionalDependencies)
+ $(OutDir)
+
+
+
+
+ NotUsing
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ pch.h
+ stdcpp17
+
+
+ Console
+ true
+ true
+ true
+ BlockBusterEngine.lib;%(AdditionalDependencies)
+ $(OutDir)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BlockBuster/BlockBusterEngineUnitTests/BlockBusterEngineUnitTests.vcxproj.filters b/BlockBuster/BlockBusterEngineUnitTests/BlockBusterEngineUnitTests.vcxproj.filters
new file mode 100644
index 0000000..a57d4c9
--- /dev/null
+++ b/BlockBuster/BlockBusterEngineUnitTests/BlockBusterEngineUnitTests.vcxproj.filters
@@ -0,0 +1,42 @@
+
+
+
+
+ {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
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BlockBuster/BlockBusterEngineUnitTests/block_factory_unit_tests.cpp b/BlockBuster/BlockBusterEngineUnitTests/block_factory_unit_tests.cpp
new file mode 100644
index 0000000..44059e7
--- /dev/null
+++ b/BlockBuster/BlockBusterEngineUnitTests/block_factory_unit_tests.cpp
@@ -0,0 +1,154 @@
+#include "catch.hpp"
+#include "random.h"
+#include "../BlockBusterEngine/utils.h"
+#include "../BlockBusterEngine/block.h"
+#include "../BlockBusterEngine/blockchain_factory.h"
+
+using namespace blockbuster::engine;
+using namespace blockbuster::engine::utils;
+
+namespace blockbuster::engine::tests
+{
+ TEST_CASE("block_factory - create block with empty string", "[blockbuster][engine][block_factory]")
+ {
+ const auto block = get_block<>("");
+ REQUIRE(block == nullptr);
+ }
+
+ TEST_CASE("block_factory - create block with random garbage string", "[blockbuster][engine][block_factory]")
+ {
+ const auto rand_str = std::to_string(random());
+ const auto block = get_block(rand_str);
+ REQUIRE(block == nullptr);
+ }
+
+ TEST_CASE("block_factory - create identity block", "[blockbuster][engine][block_factory]")
+ {
+ const auto input = random();
+ const auto block = get_block(block_id::identity);
+ REQUIRE(block != nullptr);
+ const auto result = (*block)(input);
+ REQUIRE(result == Approx(input));
+ }
+
+ TEST_CASE("block_factory - create addition block with incomplete string", "[blockbuster][engine][block_factory]")
+ {
+ const auto input = random();
+ const auto block = get_block(block_id::addition);
+ REQUIRE(block != nullptr);
+ const auto result = (*block)(input);
+ REQUIRE(result == Approx(input));
+ }
+
+ TEST_CASE("block_factory - create multiplication block with incomplete string", "[blockbuster][engine][block_factory]")
+ {
+ const auto input = random();
+ const auto block = get_block(block_id::multiplication);
+ REQUIRE(block != nullptr);
+ const auto result = (*block)(input);
+ REQUIRE(result == Approx(input));
+ }
+
+ TEST_CASE("block_factory - create condition block with incomplete string", "[blockbuster][engine][block_factory]")
+ {
+ const auto input = random();
+ const auto block = get_block(block_id::condition);
+ REQUIRE(block != nullptr);
+ const auto result = (*block)(input);
+ REQUIRE(result == (input < 0 ? -1 : input > 0 ? 1 : 0));
+ }
+
+ TEST_CASE("block_factory - create power block with incomplete parameter string", "[blockbuster][engine][block_factory]")
+ {
+ const auto input = random();
+ const auto block = get_block(block_id::power);
+ REQUIRE(block != nullptr);
+ const auto result = (*block)(input);
+ REQUIRE(result == Approx(input));
+ }
+
+ TEST_CASE("block_factory - create identity block with too many parameters", "[blockbuster][engine][block_factory]")
+ {
+ const auto input = random();
+ const auto block_str = stringify(block_id::identity, random(), random(), random());
+ const auto block = get_block(block_str);
+ REQUIRE(block != nullptr);
+ const auto result = (*block)(input);
+ REQUIRE(result == Approx(input));
+ }
+
+ TEST_CASE("block_factory - create addition block with too many parameters", "[blockbuster][engine][block_factory]")
+ {
+ const auto input = random();
+ const auto param = random();
+ const auto block_str = stringify(block_id::addition, param, random(), random(), random());
+ const auto block = get_block(block_str);
+ REQUIRE(block != nullptr);
+ const auto result = (*block)(input);
+ REQUIRE(result == Approx(input + param));
+ }
+
+ TEST_CASE("block_factory - create multiplication block with too many parameters", "[blockbuster][engine][block_factory]")
+ {
+ const auto input = random();
+ const auto param = random();
+ const auto block_str = stringify(block_id::multiplication, param, random(), random(), random());
+ const auto block = get_block(block_str);
+ REQUIRE(block != nullptr);
+ const auto result = (*block)(input);
+ REQUIRE(result == Approx(input * param));
+ }
+
+ TEST_CASE("block_factory - create power block with too many parameters", "[blockbuster][engine][block_factory]")
+ {
+ const auto input = random();
+ const auto param = random();
+ const auto block_str = stringify(block_id::power, param, random(), random(), random());
+
+ const auto block = get_block(block_str);
+ REQUIRE(block != nullptr);
+
+ const auto result = static_cast((*block)(input) * 10);
+ const auto expected = static_cast(std::pow(input, param) * 10);
+ REQUIRE(result == expected);
+ }
+
+ TEST_CASE("block_factory - create condition block with too many parameters", "[blockbuster][engine][block_factory]")
+ {
+ const auto param = static_cast(random());
+ const auto block_str = stringify(block_id::condition, param, random(), random(), random());
+ const auto block = get_block(block_str);
+ REQUIRE(block != nullptr);
+
+ const auto result_less = (*block)(param - 1);
+ REQUIRE(result_less == -1);
+
+ const auto result_more = (*block)(param + 1);
+ REQUIRE(result_more == 1);
+
+ const auto result = (*block)(param);
+ REQUIRE(result == 0);
+ }
+
+ TEST_CASE("block_factory - create limit block with too many parameters", "[blockbuster][engine][block_factory]")
+ {
+ auto param1 = random();
+ auto param2 = random();
+ auto input = (param1 + param2) / 2;
+
+ auto block_str = stringify(block_id::limit, param1, param2, random(), random(), random());
+ auto block = get_block(block_str);
+ REQUIRE(block != nullptr);
+
+ auto result = (*block)(input);
+ REQUIRE(result == Approx(input));
+
+ auto min = std::min(param1, param2);
+ auto result_min = (*block)(min - 1);
+ REQUIRE(min == Approx(result_min));
+
+ auto max = std::max(param1, param2);
+ auto result_max = (*block)(max + 1);
+ REQUIRE(max == Approx(result_max));
+ }
+}
diff --git a/BlockBuster/BlockBusterEngineUnitTests/blockchain_cruncher_unit_tests.cpp b/BlockBuster/BlockBusterEngineUnitTests/blockchain_cruncher_unit_tests.cpp
new file mode 100644
index 0000000..bb262de
--- /dev/null
+++ b/BlockBuster/BlockBusterEngineUnitTests/blockchain_cruncher_unit_tests.cpp
@@ -0,0 +1,48 @@
+#include "catch.hpp"
+#include
+#include "random.h"
+#include "../BlockBusterEngine/block.h"
+#include "../BlockBusterEngine/blockchain_factory.h"
+#include "../BlockBusterEngine/blockchain_cruncher.h"
+
+using namespace blockbuster::engine;
+
+namespace blockbuster::engine::tests
+{
+ TEST_CASE("blockchain_cruncher - empty block chain", "[blockbuster][engine][blockchain_cruncher]")
+ {
+ const auto input = random();
+ std::vector>> v;
+ auto res = blockchain_cruncher(v.begin(), v.end(), input);
+ REQUIRE(res == input);
+ }
+
+ TEST_CASE("blockchain_cruncher - block chain with nulls", "[blockbuster][engine][blockchain_cruncher]")
+ {
+ const auto input = random();
+ std::vector>> v;
+ v.push_back(std::unique_ptr>());
+ v.push_back(std::unique_ptr>());
+ v.push_back(std::unique_ptr>());
+ auto res = blockchain_cruncher(v.begin(), v.end(), input);
+ REQUIRE(res == input);
+ }
+
+ TEST_CASE("blockchain_cruncher - block chain with single addition block", "[blockbuster][engine][blockchain_cruncher]")
+ {
+ const auto input = random();
+ auto chain = blockchain_factory("limit 5 5 addition 2 addition 3 multiplication 2 power 2");
+ auto res = blockchain_cruncher(chain.begin(), chain.end(), input);
+ REQUIRE(res == std::pow((5 + 2 + 3) * 2, 2));
+ }
+
+ TEST_CASE("blockchain_cruncher - block chain with single addition block and some garbage", "[blockbuster][engine][blockchain_cruncher]")
+ {
+ const auto input = random();
+
+ auto chain = blockchain_factory("limit 5 5 garbage 2 addition garbage 2 addition 3 garbage multiplication 2");
+ auto res = blockchain_cruncher(chain.begin(), chain.end(), input);
+
+ REQUIRE(res == 5);
+ }
+}
\ No newline at end of file
diff --git a/BlockBuster/BlockBusterEngineUnitTests/catch.hpp b/BlockBuster/BlockBusterEngineUnitTests/catch.hpp
new file mode 100644
index 0000000..b324e56
--- /dev/null
+++ b/BlockBuster/BlockBusterEngineUnitTests/catch.hpp
@@ -0,0 +1,14075 @@
+/*
+ * Catch v2.4.2
+ * Generated: 2018-10-26 21:12:29.223927
+ * ----------------------------------------------------------
+ * This file has been merged from multiple headers. Please don't edit it directly
+ * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved.
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+// start catch.hpp
+
+
+#define CATCH_VERSION_MAJOR 2
+#define CATCH_VERSION_MINOR 4
+#define CATCH_VERSION_PATCH 2
+
+#ifdef __clang__
+# pragma clang system_header
+#elif defined __GNUC__
+# pragma GCC system_header
+#endif
+
+// start catch_suppress_warnings.h
+
+#ifdef __clang__
+# ifdef __ICC // icpc defines the __clang__ macro
+# pragma warning(push)
+# pragma warning(disable: 161 1682)
+# else // __ICC
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wpadded"
+# pragma clang diagnostic ignored "-Wswitch-enum"
+# pragma clang diagnostic ignored "-Wcovered-switch-default"
+# endif
+#elif defined __GNUC__
+ // GCC likes to warn on REQUIREs, and we cannot suppress them
+ // locally because g++'s support for _Pragma is lacking in older,
+ // still supported, versions
+# pragma GCC diagnostic ignored "-Wparentheses"
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wunused-variable"
+# pragma GCC diagnostic ignored "-Wpadded"
+#endif
+// end catch_suppress_warnings.h
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+# define CATCH_IMPL
+# define CATCH_CONFIG_ALL_PARTS
+#endif
+
+// In the impl file, we want to have access to all parts of the headers
+// Can also be used to sanely support PCHs
+#if defined(CATCH_CONFIG_ALL_PARTS)
+# define CATCH_CONFIG_EXTERNAL_INTERFACES
+# if defined(CATCH_CONFIG_DISABLE_MATCHERS)
+# undef CATCH_CONFIG_DISABLE_MATCHERS
+# endif
+# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+# endif
+#endif
+
+#if !defined(CATCH_CONFIG_IMPL_ONLY)
+// start catch_platform.h
+
+#ifdef __APPLE__
+# include
+# if TARGET_OS_OSX == 1
+# define CATCH_PLATFORM_MAC
+# elif TARGET_OS_IPHONE == 1
+# define CATCH_PLATFORM_IPHONE
+# endif
+
+#elif defined(linux) || defined(__linux) || defined(__linux__)
+# define CATCH_PLATFORM_LINUX
+
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
+# define CATCH_PLATFORM_WINDOWS
+#endif
+
+// end catch_platform.h
+
+#ifdef CATCH_IMPL
+# ifndef CLARA_CONFIG_MAIN
+# define CLARA_CONFIG_MAIN_NOT_DEFINED
+# define CLARA_CONFIG_MAIN
+# endif
+#endif
+
+// start catch_user_interfaces.h
+
+namespace Catch {
+ unsigned int rngSeed();
+}
+
+// end catch_user_interfaces.h
+// start catch_tag_alias_autoregistrar.h
+
+// start catch_common.h
+
+// start catch_compiler_capabilities.h
+
+// Detect a number of compiler features - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled?
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_ form
+// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+#ifdef __cplusplus
+
+# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
+# define CATCH_CPP14_OR_GREATER
+# endif
+
+# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+# define CATCH_CPP17_OR_GREATER
+# endif
+
+#endif
+
+#if defined(CATCH_CPP17_OR_GREATER)
+# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#endif
+
+#ifdef __clang__
+
+# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
+ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
+# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+ _Pragma( "clang diagnostic push" ) \
+ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
+# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \
+ _Pragma( "clang diagnostic pop" )
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// Assume that non-Windows platforms support posix signals by default
+#if !defined(CATCH_PLATFORM_WINDOWS)
+ #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// We know some environments not to support full POSIX signals
+#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__)
+ #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#endif
+
+#ifdef __OS400__
+# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+# define CATCH_CONFIG_COLOUR_NONE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Android somehow still does not support std::to_string
+#if defined(__ANDROID__)
+# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Not all Windows environments support SEH properly
+#if defined(__MINGW32__)
+# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// PS4
+#if defined(__ORBIS__)
+# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+# define _BSD_SOURCE
+// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
+// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
+# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
+ && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
+
+# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+
+# endif
+#endif // __CYGWIN__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
+# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+# endif
+
+// Universal Windows platform does not support SEH
+// Or console colours (or console at all...)
+# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+# define CATCH_CONFIG_COLOUR_NONE
+# else
+# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+# endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+// Check if we are compiled with -fno-exceptions or equivalent
+#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)
+# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// DJGPP
+#ifdef __DJGPP__
+# define CATCH_INTERNAL_CONFIG_NO_WCHAR
+#endif // __DJGPP__
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Use of __COUNTER__ is suppressed during code analysis in
+// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly
+// handled by it.
+// Otherwise all supported compilers support COUNTER macro,
+// but user still might want to turn it off
+#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
+ #define CATCH_INTERNAL_CONFIG_COUNTER
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Check if string_view is available and usable
+// The check is split apart to work around v140 (VS2015) preprocessor issue...
+#if defined(__has_include)
+#if __has_include() && defined(CATCH_CPP17_OR_GREATER)
+# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
+#endif
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Check if variant is available and usable
+#if defined(__has_include)
+# if __has_include() && defined(CATCH_CPP17_OR_GREATER)
+# if defined(__clang__) && (__clang_major__ < 8)
+ // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
+ // fix should be in clang 8, workaround in libstdc++ 8.2
+# include
+# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+# define CATCH_CONFIG_NO_CPP17_VARIANT
+# else
+# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+# endif // defined(__clang__) && (__clang_major__ < 8)
+# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
+#endif // __has_include
+
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+# define CATCH_CONFIG_COUNTER
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH)
+# define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+# define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR)
+# define CATCH_CONFIG_WCHAR
+#endif
+
+#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING)
+# define CATCH_CONFIG_CPP11_TO_STRING
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
+# define CATCH_CONFIG_CPP17_STRING_VIEW
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT)
+# define CATCH_CONFIG_CPP17_VARIANT
+#endif
+
+#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE)
+# define CATCH_CONFIG_NEW_CAPTURE
+#endif
+
+#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+# define CATCH_CONFIG_DISABLE_EXCEPTIONS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
+# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
+#endif
+
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+#define CATCH_TRY if ((true))
+#define CATCH_CATCH_ALL if ((false))
+#define CATCH_CATCH_ANON(type) if ((false))
+#else
+#define CATCH_TRY try
+#define CATCH_CATCH_ALL catch (...)
+#define CATCH_CATCH_ANON(type) catch (type)
+#endif
+
+// end catch_compiler_capabilities.h
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#include
+#include
+#include
+
+// We need a dummy global operator<< so we can bring it into Catch namespace later
+struct Catch_global_namespace_dummy {};
+std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
+
+namespace Catch {
+
+ struct CaseSensitive { enum Choice {
+ Yes,
+ No
+ }; };
+
+ class NonCopyable {
+ NonCopyable( NonCopyable const& ) = delete;
+ NonCopyable( NonCopyable && ) = delete;
+ NonCopyable& operator = ( NonCopyable const& ) = delete;
+ NonCopyable& operator = ( NonCopyable && ) = delete;
+
+ protected:
+ NonCopyable();
+ virtual ~NonCopyable();
+ };
+
+ struct SourceLineInfo {
+
+ SourceLineInfo() = delete;
+ SourceLineInfo( char const* _file, std::size_t _line ) noexcept
+ : file( _file ),
+ line( _line )
+ {}
+
+ SourceLineInfo( SourceLineInfo const& other ) = default;
+ SourceLineInfo( SourceLineInfo && ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo && ) = default;
+
+ bool empty() const noexcept;
+ bool operator == ( SourceLineInfo const& other ) const noexcept;
+ bool operator < ( SourceLineInfo const& other ) const noexcept;
+
+ char const* file;
+ std::size_t line;
+ };
+
+ std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+ // Bring in operator<< from global namespace into Catch namespace
+ // This is necessary because the overload of operator<< above makes
+ // lookup stop at namespace Catch
+ using ::operator<<;
+
+ // Use this in variadic streaming macros to allow
+ // >> +StreamEndStop
+ // as well as
+ // >> stuff +StreamEndStop
+ struct StreamEndStop {
+ std::string operator+() const;
+ };
+ template
+ T const& operator + ( T const& value, StreamEndStop ) {
+ return value;
+ }
+}
+
+#define CATCH_INTERNAL_LINEINFO \
+ ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) )
+
+// end catch_common.h
+namespace Catch {
+
+ struct RegistrarForTagAliases {
+ RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+ };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+
+// end catch_tag_alias_autoregistrar.h
+// start catch_test_registry.h
+
+// start catch_interfaces_testcase.h
+
+#include
+#include
+
+namespace Catch {
+
+ class TestSpec;
+
+ struct ITestInvoker {
+ virtual void invoke () const = 0;
+ virtual ~ITestInvoker();
+ };
+
+ using ITestCasePtr = std::shared_ptr;
+
+ class TestCase;
+ struct IConfig;
+
+ struct ITestCaseRegistry {
+ virtual ~ITestCaseRegistry();
+ virtual std::vector const& getAllTests() const = 0;
+ virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0;
+ };
+
+ bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+ std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config );
+ std::vector const& getAllTestCasesSorted( IConfig const& config );
+
+}
+
+// end catch_interfaces_testcase.h
+// start catch_stringref.h
+
+#include
+#include
+#include
+
+namespace Catch {
+
+ class StringData;
+
+ /// A non-owning string class (similar to the forthcoming std::string_view)
+ /// Note that, because a StringRef may be a substring of another string,
+ /// it may not be null terminated. c_str() must return a null terminated
+ /// string, however, and so the StringRef will internally take ownership
+ /// (taking a copy), if necessary. In theory this ownership is not externally
+ /// visible - but it does mean (substring) StringRefs should not be shared between
+ /// threads.
+ class StringRef {
+ public:
+ using size_type = std::size_t;
+
+ private:
+ friend struct StringRefTestAccess;
+
+ char const* m_start;
+ size_type m_size;
+
+ char* m_data = nullptr;
+
+ void takeOwnership();
+
+ static constexpr char const* const s_empty = "";
+
+ public: // construction/ assignment
+ StringRef() noexcept
+ : StringRef( s_empty, 0 )
+ {}
+
+ StringRef( StringRef const& other ) noexcept
+ : m_start( other.m_start ),
+ m_size( other.m_size )
+ {}
+
+ StringRef( StringRef&& other ) noexcept
+ : m_start( other.m_start ),
+ m_size( other.m_size ),
+ m_data( other.m_data )
+ {
+ other.m_data = nullptr;
+ }
+
+ StringRef( char const* rawChars ) noexcept;
+
+ StringRef( char const* rawChars, size_type size ) noexcept
+ : m_start( rawChars ),
+ m_size( size )
+ {}
+
+ StringRef( std::string const& stdString ) noexcept
+ : m_start( stdString.c_str() ),
+ m_size( stdString.size() )
+ {}
+
+ ~StringRef() noexcept {
+ delete[] m_data;
+ }
+
+ auto operator = ( StringRef const &other ) noexcept -> StringRef& {
+ delete[] m_data;
+ m_data = nullptr;
+ m_start = other.m_start;
+ m_size = other.m_size;
+ return *this;
+ }
+
+ operator std::string() const;
+
+ void swap( StringRef& other ) noexcept;
+
+ public: // operators
+ auto operator == ( StringRef const& other ) const noexcept -> bool;
+ auto operator != ( StringRef const& other ) const noexcept -> bool;
+
+ auto operator[] ( size_type index ) const noexcept -> char;
+
+ public: // named queries
+ auto empty() const noexcept -> bool {
+ return m_size == 0;
+ }
+ auto size() const noexcept -> size_type {
+ return m_size;
+ }
+
+ auto numberOfCharacters() const noexcept -> size_type;
+ auto c_str() const -> char const*;
+
+ public: // substrings and searches
+ auto substr( size_type start, size_type size ) const noexcept -> StringRef;
+
+ // Returns the current start pointer.
+ // Note that the pointer can change when if the StringRef is a substring
+ auto currentData() const noexcept -> char const*;
+
+ private: // ownership queries - may not be consistent between calls
+ auto isOwned() const noexcept -> bool;
+ auto isSubstring() const noexcept -> bool;
+ };
+
+ auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string;
+ auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string;
+ auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string;
+
+ auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
+ auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
+
+ inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
+ return StringRef( rawChars, size );
+ }
+
+} // namespace Catch
+
+inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
+ return Catch::StringRef( rawChars, size );
+}
+
+// end catch_stringref.h
+namespace Catch {
+
+template
+class TestInvokerAsMethod : public ITestInvoker {
+ void (C::*m_testAsMethod)();
+public:
+ TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {}
+
+ void invoke() const override {
+ C obj;
+ (obj.*m_testAsMethod)();
+ }
+};
+
+auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*;
+
+template
+auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {
+ return new(std::nothrow) TestInvokerAsMethod( testAsMethod );
+}
+
+struct NameAndTags {
+ NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept;
+ StringRef name;
+ StringRef tags;
+};
+
+struct AutoReg : NonCopyable {
+ AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;
+ ~AutoReg();
+};
+
+} // end namespace Catch
+
+#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param)
+#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
+#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
+#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+
+#if defined(CATCH_CONFIG_DISABLE)
+ #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
+ namespace{ \
+ struct TestName : INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF ClassName) { \
+ void test(); \
+ }; \
+ } \
+ void TestName::test()
+
+#endif
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+ static void TestName(); \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ static void TestName()
+ #define INTERNAL_CATCH_TESTCASE( ... ) \
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ namespace{ \
+ struct TestName : INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF ClassName) { \
+ void test(); \
+ }; \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ } \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ void TestName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+
+// end catch_test_registry.h
+// start catch_capture.hpp
+
+// start catch_assertionhandler.h
+
+// start catch_assertioninfo.h
+
+// start catch_result_type.h
+
+namespace Catch {
+
+ // ResultWas::OfType enum
+ struct ResultWas { enum OfType {
+ Unknown = -1,
+ Ok = 0,
+ Info = 1,
+ Warning = 2,
+
+ FailureBit = 0x10,
+
+ ExpressionFailed = FailureBit | 1,
+ ExplicitFailure = FailureBit | 2,
+
+ Exception = 0x100 | FailureBit,
+
+ ThrewException = Exception | 1,
+ DidntThrowException = Exception | 2,
+
+ FatalErrorCondition = 0x200 | FailureBit
+
+ }; };
+
+ bool isOk( ResultWas::OfType resultType );
+ bool isJustInfo( int flags );
+
+ // ResultDisposition::Flags enum
+ struct ResultDisposition { enum Flags {
+ Normal = 0x01,
+
+ ContinueOnFailure = 0x02, // Failures fail test, but execution continues
+ FalseTest = 0x04, // Prefix expression with !
+ SuppressFail = 0x08 // Failures are reported but do not fail the test
+ }; };
+
+ ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
+
+ bool shouldContinueOnFailure( int flags );
+ inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+ bool shouldSuppressFailure( int flags );
+
+} // end namespace Catch
+
+// end catch_result_type.h
+namespace Catch {
+
+ struct AssertionInfo
+ {
+ StringRef macroName;
+ SourceLineInfo lineInfo;
+ StringRef capturedExpression;
+ ResultDisposition::Flags resultDisposition;
+
+ // We want to delete this constructor but a compiler bug in 4.8 means
+ // the struct is then treated as non-aggregate
+ //AssertionInfo() = delete;
+ };
+
+} // end namespace Catch
+
+// end catch_assertioninfo.h
+// start catch_decomposer.h
+
+// start catch_tostring.h
+
+#include
+#include
+#include
+#include
+// start catch_stream.h
+
+#include
+#include
+#include
+
+namespace Catch {
+
+ std::ostream& cout();
+ std::ostream& cerr();
+ std::ostream& clog();
+
+ class StringRef;
+
+ struct IStream {
+ virtual ~IStream();
+ virtual std::ostream& stream() const = 0;
+ };
+
+ auto makeStream( StringRef const &filename ) -> IStream const*;
+
+ class ReusableStringStream {
+ std::size_t m_index;
+ std::ostream* m_oss;
+ public:
+ ReusableStringStream();
+ ~ReusableStringStream();
+
+ auto str() const -> std::string;
+
+ template
+ auto operator << ( T const& value ) -> ReusableStringStream& {
+ *m_oss << value;
+ return *this;
+ }
+ auto get() -> std::ostream& { return *m_oss; }
+ };
+}
+
+// end catch_stream.h
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+#include
+#endif
+
+#ifdef __OBJC__
+// start catch_objc_arc.hpp
+
+#import
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+ [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+ if( [obj respondsToSelector: sel] )
+ return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+// end catch_objc_arc.hpp
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
+#endif
+
+namespace Catch {
+ namespace Detail {
+
+ extern const std::string unprintableString;
+
+ std::string rawMemoryToString( const void *object, std::size_t size );
+
+ template
+ std::string rawMemoryToString( const T& object ) {
+ return rawMemoryToString( &object, sizeof(object) );
+ }
+
+ template
+ class IsStreamInsertable {
+ template
+ static auto test(int)
+ -> decltype(std::declval() << std::declval(), std::true_type());
+
+ template
+ static auto test(...)->std::false_type;
+
+ public:
+ static const bool value = decltype(test(0))::value;
+ };
+
+ template
+ std::string convertUnknownEnumToString( E e );
+
+ template
+ typename std::enable_if<
+ !std::is_enum::value && !std::is_base_of::value,
+ std::string>::type convertUnstreamable( T const& ) {
+ return Detail::unprintableString;
+ }
+ template
+ typename std::enable_if<
+ !std::is_enum::value && std::is_base_of::value,
+ std::string>::type convertUnstreamable(T const& ex) {
+ return ex.what();
+ }
+
+ template
+ typename std::enable_if<
+ std::is_enum::value
+ , std::string>::type convertUnstreamable( T const& value ) {
+ return convertUnknownEnumToString( value );
+ }
+
+#if defined(_MANAGED)
+ //! Convert a CLR string to a utf8 std::string
+ template
+ std::string clrReferenceToString( T^ ref ) {
+ if (ref == nullptr)
+ return std::string("null");
+ auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString());
+ cli::pin_ptr p = &bytes[0];
+ return std::string(reinterpret_cast(p), bytes->Length);
+ }
+#endif
+
+ } // namespace Detail
+
+ // If we decide for C++14, change these to enable_if_ts
+ template
+ struct StringMaker {
+ template
+ static
+ typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type
+ convert(const Fake& value) {
+ ReusableStringStream rss;
+ // NB: call using the function-like syntax to avoid ambiguity with
+ // user-defined templated operator<< under clang.
+ rss.operator<<(value);
+ return rss.str();
+ }
+
+ template
+ static
+ typename std::enable_if::value, std::string>::type
+ convert( const Fake& value ) {
+#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
+ return Detail::convertUnstreamable(value);
+#else
+ return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
+#endif
+ }
+ };
+
+ namespace Detail {
+
+ // This function dispatches all stringification requests inside of Catch.
+ // Should be preferably called fully qualified, like ::Catch::Detail::stringify
+ template
+ std::string stringify(const T& e) {
+ return ::Catch::StringMaker::type>::type>::convert(e);
+ }
+
+ template
+ std::string convertUnknownEnumToString( E e ) {
+ return ::Catch::Detail::stringify(static_cast::type>(e));
+ }
+
+#if defined(_MANAGED)
+ template
+ std::string stringify( T^ e ) {
+ return ::Catch::StringMaker::convert(e);
+ }
+#endif
+
+ } // namespace Detail
+
+ // Some predefined specializations
+
+ template<>
+ struct StringMaker {
+ static std::string convert(const std::string& str);
+ };
+
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+ template<>
+ struct StringMaker {
+ static std::string convert(std::string_view str);
+ };
+#endif
+
+ template<>
+ struct StringMaker {
+ static std::string convert(char const * str);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(char * str);
+ };
+
+#ifdef CATCH_CONFIG_WCHAR
+ template<>
+ struct StringMaker {
+ static std::string convert(const std::wstring& wstr);
+ };
+
+# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+ template<>
+ struct StringMaker {
+ static std::string convert(std::wstring_view str);
+ };
+# endif
+
+ template<>
+ struct StringMaker {
+ static std::string convert(wchar_t const * str);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(wchar_t * str);
+ };
+#endif
+
+ // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer,
+ // while keeping string semantics?
+ template
+ struct StringMaker {
+ static std::string convert(char const* str) {
+ return ::Catch::Detail::stringify(std::string{ str });
+ }
+ };
+ template
+ struct StringMaker {
+ static std::string convert(signed char const* str) {
+ return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) });
+ }
+ };
+ template
+ struct StringMaker {
+ static std::string convert(unsigned char const* str) {
+ return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) });
+ }
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(int value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(long value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(long long value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(unsigned int value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(unsigned long value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(unsigned long long value);
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(bool b);
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(char c);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(signed char c);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(unsigned char c);
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(std::nullptr_t);
+ };
+
+ template<>
+ struct StringMaker {
+ static std::string convert(float value);
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(double value);
+ };
+
+ template
+ struct StringMaker {
+ template
+ static std::string convert(U* p) {
+ if (p) {
+ return ::Catch::Detail::rawMemoryToString(p);
+ } else {
+ return "nullptr";
+ }
+ }
+ };
+
+ template
+ struct StringMaker {
+ static std::string convert(R C::* p) {
+ if (p) {
+ return ::Catch::Detail::rawMemoryToString(p);
+ } else {
+ return "nullptr";
+ }
+ }
+ };
+
+#if defined(_MANAGED)
+ template
+ struct StringMaker {
+ static std::string convert( T^ ref ) {
+ return ::Catch::Detail::clrReferenceToString(ref);
+ }
+ };
+#endif
+
+ namespace Detail {
+ template
+ std::string rangeToString(InputIterator first, InputIterator last) {
+ ReusableStringStream rss;
+ rss << "{ ";
+ if (first != last) {
+ rss << ::Catch::Detail::stringify(*first);
+ for (++first; first != last; ++first)
+ rss << ", " << ::Catch::Detail::stringify(*first);
+ }
+ rss << " }";
+ return rss.str();
+ }
+ }
+
+#ifdef __OBJC__
+ template<>
+ struct StringMaker {
+ static std::string convert(NSString * nsstring) {
+ if (!nsstring)
+ return "nil";
+ return std::string("@") + [nsstring UTF8String];
+ }
+ };
+ template<>
+ struct StringMaker {
+ static std::string convert(NSObject* nsObject) {
+ return ::Catch::Detail::stringify([nsObject description]);
+ }
+
+ };
+ namespace Detail {
+ inline std::string stringify( NSString* nsstring ) {
+ return StringMaker::convert( nsstring );
+ }
+
+ } // namespace Detail
+#endif // __OBJC__
+
+} // namespace Catch
+
+//////////////////////////////////////////////////////
+// Separate std-lib types stringification, so it can be selectively enabled
+// This means that we do not bring in
+
+#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)
+# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+#endif
+
+// Separate std::pair specialization
+#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER)
+#include
+namespace Catch {
+ template
+ struct StringMaker > {
+ static std::string convert(const std::pair& pair) {
+ ReusableStringStream rss;
+ rss << "{ "
+ << ::Catch::Detail::stringify(pair.first)
+ << ", "
+ << ::Catch::Detail::stringify(pair.second)
+ << " }";
+ return rss.str();
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+
+// Separate std::tuple specialization
+#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
+#include
+namespace Catch {
+ namespace Detail {
+ template<
+ typename Tuple,
+ std::size_t N = 0,
+ bool = (N < std::tuple_size::value)
+ >
+ struct TupleElementPrinter {
+ static void print(const Tuple& tuple, std::ostream& os) {
+ os << (N ? ", " : " ")
+ << ::Catch::Detail::stringify(std::get(tuple));
+ TupleElementPrinter::print(tuple, os);
+ }
+ };
+
+ template<
+ typename Tuple,
+ std::size_t N
+ >
+ struct TupleElementPrinter {
+ static void print(const Tuple&, std::ostream&) {}
+ };
+
+ }
+
+ template
+ struct StringMaker> {
+ static std::string convert(const std::tuple& tuple) {
+ ReusableStringStream rss;
+ rss << '{';
+ Detail::TupleElementPrinter>::print(tuple, rss.get());
+ rss << " }";
+ return rss.str();
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+
+#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
+#include
+namespace Catch {
+ template<>
+ struct StringMaker {
+ static std::string convert(const std::monostate&) {
+ return "{ }";
+ }
+ };
+
+ template
+ struct StringMaker> {
+ static std::string convert(const std::variant& variant) {
+ if (variant.valueless_by_exception()) {
+ return "{valueless variant}";
+ } else {
+ return std::visit(
+ [](const auto& value) {
+ return ::Catch::Detail::stringify(value);
+ },
+ variant
+ );
+ }
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+
+namespace Catch {
+ struct not_this_one {}; // Tag type for detecting which begin/ end are being selected
+
+ // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace
+ using std::begin;
+ using std::end;
+
+ not_this_one begin( ... );
+ not_this_one end( ... );
+
+ template
+ struct is_range {
+ static const bool value =
+ !std::is_same())), not_this_one>::value &&
+ !std::is_same())), not_this_one>::value;
+ };
+
+#if defined(_MANAGED) // Managed types are never ranges
+ template
+ struct is_range {
+ static const bool value = false;
+ };
+#endif
+
+ template
+ std::string rangeToString( Range const& range ) {
+ return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
+ }
+
+ // Handle vector specially
+ template
+ std::string rangeToString( std::vector const& v ) {
+ ReusableStringStream rss;
+ rss << "{ ";
+ bool first = true;
+ for( bool b : v ) {
+ if( first )
+ first = false;
+ else
+ rss << ", ";
+ rss << ::Catch::Detail::stringify( b );
+ }
+ rss << " }";
+ return rss.str();
+ }
+
+ template
+ struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> {
+ static std::string convert( R const& range ) {
+ return rangeToString( range );
+ }
+ };
+
+ template
+ struct StringMaker {
+ static std::string convert(T const(&arr)[SZ]) {
+ return rangeToString(arr);
+ }
+ };
+
+} // namespace Catch
+
+// Separate std::chrono::duration specialization
+#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+#include
+#include
+#include
+
+namespace Catch {
+
+template
+struct ratio_string {
+ static std::string symbol();
+};
+
+template
+std::string ratio_string::symbol() {
+ Catch::ReusableStringStream rss;
+ rss << '[' << Ratio::num << '/'
+ << Ratio::den << ']';
+ return rss.str();
+}
+template <>
+struct ratio_string {
+ static std::string symbol();
+};
+template <>
+struct ratio_string {
+ static std::string symbol();
+};
+template <>
+struct ratio_string {
+ static std::string symbol();
+};
+template <>
+struct ratio_string {
+ static std::string symbol();
+};
+template <>
+struct ratio_string {
+ static std::string symbol();
+};
+template <>
+struct ratio_string {
+ static std::string symbol();
+};
+
+ ////////////
+ // std::chrono::duration specializations
+ template
+ struct StringMaker> {
+ static std::string convert(std::chrono::duration const& duration) {
+ ReusableStringStream rss;
+ rss << duration.count() << ' ' << ratio_string::symbol() << 's';
+ return rss.str();
+ }
+ };
+ template
+ struct StringMaker