From 6f1b3e5098bcce0f1b0d8a4965de85c3451d2fb9 Mon Sep 17 00:00:00 2001 From: jadranko lucic Date: Sat, 24 Nov 2018 17:34:49 +0100 Subject: [PATCH 1/3] Final: first version. --- final/final.sln | 42 ++++++ final/tests/tests.cpp | 8 + final/tests/tests.h | 5 + final/tests/tests.vcxproj | 101 +++++++++++++ final/tests/tool_test.cpp | 22 +++ final/tool/block.cpp | 128 ++++++++++++++++ final/tool/block.h | 72 +++++++++ final/tool/block_add.cpp | 19 +++ final/tool/block_add.h | 22 +++ final/tool/block_lim.cpp | 24 +++ final/tool/block_lim.h | 19 +++ final/tool/block_mult.cpp | 18 +++ final/tool/block_mult.h | 18 +++ final/tool/block_neg.cpp | 17 +++ final/tool/block_neg.h | 18 +++ final/tool/block_queq.cpp | 19 +++ final/tool/block_queq.h | 20 +++ final/tool/menus.cpp | 192 ++++++++++++++++++++++++ final/tool/menus.h | 29 ++++ final/tool/self_registered_in_factory.h | 26 ++++ final/tool/stdafx.cpp | Bin 0 -> 584 bytes final/tool/stdafx.h | Bin 0 -> 642 bytes final/tool/targetver.h | Bin 0 -> 630 bytes final/tool/tool.cpp | Bin 0 -> 9730 bytes final/tool/tool.vcxproj | 187 +++++++++++++++++++++++ final/tool/tool.vcxproj.filters | 78 ++++++++++ 26 files changed, 1084 insertions(+) create mode 100644 final/final.sln create mode 100644 final/tests/tests.cpp create mode 100644 final/tests/tests.h create mode 100644 final/tests/tests.vcxproj create mode 100644 final/tests/tool_test.cpp create mode 100644 final/tool/block.cpp create mode 100644 final/tool/block.h create mode 100644 final/tool/block_add.cpp create mode 100644 final/tool/block_add.h create mode 100644 final/tool/block_lim.cpp create mode 100644 final/tool/block_lim.h create mode 100644 final/tool/block_mult.cpp create mode 100644 final/tool/block_mult.h create mode 100644 final/tool/block_neg.cpp create mode 100644 final/tool/block_neg.h create mode 100644 final/tool/block_queq.cpp create mode 100644 final/tool/block_queq.h create mode 100644 final/tool/menus.cpp create mode 100644 final/tool/menus.h create mode 100644 final/tool/self_registered_in_factory.h create mode 100644 final/tool/stdafx.cpp create mode 100644 final/tool/stdafx.h create mode 100644 final/tool/targetver.h create mode 100644 final/tool/tool.cpp create mode 100644 final/tool/tool.vcxproj create mode 100644 final/tool/tool.vcxproj.filters diff --git a/final/final.sln b/final/final.sln new file mode 100644 index 0000000..a3929dd --- /dev/null +++ b/final/final.sln @@ -0,0 +1,42 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2026 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "tests\tests.vcxproj", "{DD752318-EA08-4316-9EFE-C1A42F5526F4}" + ProjectSection(ProjectDependencies) = postProject + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF} = {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tool", "tool\tool.vcxproj", "{6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}" +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 + {DD752318-EA08-4316-9EFE-C1A42F5526F4}.Debug|x64.ActiveCfg = Debug|Win32 + {DD752318-EA08-4316-9EFE-C1A42F5526F4}.Debug|x86.ActiveCfg = Debug|Win32 + {DD752318-EA08-4316-9EFE-C1A42F5526F4}.Debug|x86.Build.0 = Debug|Win32 + {DD752318-EA08-4316-9EFE-C1A42F5526F4}.Release|x64.ActiveCfg = Release|Win32 + {DD752318-EA08-4316-9EFE-C1A42F5526F4}.Release|x86.ActiveCfg = Release|Win32 + {DD752318-EA08-4316-9EFE-C1A42F5526F4}.Release|x86.Build.0 = Release|Win32 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Debug|x64.ActiveCfg = Debug|x64 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Debug|x64.Build.0 = Debug|x64 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Debug|x86.ActiveCfg = Debug|Win32 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Debug|x86.Build.0 = Debug|Win32 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Release|x64.ActiveCfg = Release|x64 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Release|x64.Build.0 = Release|x64 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Release|x86.ActiveCfg = Release|Win32 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FDE21BA4-D7CE-47C7-96EA-E00553A83F88} + EndGlobalSection +EndGlobal diff --git a/final/tests/tests.cpp b/final/tests/tests.cpp new file mode 100644 index 0000000..d6d2c25 --- /dev/null +++ b/final/tests/tests.cpp @@ -0,0 +1,8 @@ +#include +#include +#include "tests.h" +#include +#include +#include +#include + diff --git a/final/tests/tests.h b/final/tests/tests.h new file mode 100644 index 0000000..60bea48 --- /dev/null +++ b/final/tests/tests.h @@ -0,0 +1,5 @@ +#pragma once +#include +#include +#include +#include diff --git a/final/tests/tests.vcxproj b/final/tests/tests.vcxproj new file mode 100644 index 0000000..5df0842 --- /dev/null +++ b/final/tests/tests.vcxproj @@ -0,0 +1,101 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {DD752318-EA08-4316-9EFE-C1A42F5526F4} + Win32Proj + e01 + 10.0.17134.0 + + + + DynamicLibrary + true + v141 + Unicode + false + + + DynamicLibrary + false + v141 + true + Unicode + false + + + + + + + + + + + + + true + ..\$(Configuration)\$(Platform) + + + true + ..\$(Configuration)\$(Platform) + + + + NotUsing + Level3 + Disabled + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + Level3 + NotUsing + MaxSpeed + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + stdcpp17 + + + Windows + true + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/final/tests/tool_test.cpp b/final/tests/tool_test.cpp new file mode 100644 index 0000000..b684b76 --- /dev/null +++ b/final/tests/tool_test.cpp @@ -0,0 +1,22 @@ +#include "CppUnitTest.h" +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +#include "tests.h" +#include +#include +#include "..\tool\block.h" + +TEST_CLASS(test_tool) +{ +public: + TEST_METHOD(test_add) + { + block_processor bp; + std::vector p; + p.push_back(5); + std::unique_ptr block_add = block_factory::Create("add", p); + bp.add_block(block_add); + double res = bp.calc_all(5.); + Assert::AreEqual(10., res); + } +}; diff --git a/final/tool/block.cpp b/final/tool/block.cpp new file mode 100644 index 0000000..ef5ab67 --- /dev/null +++ b/final/tool/block.cpp @@ -0,0 +1,128 @@ +#include "stdafx.h" +#include "block.h" +#include +#include +#include + +#pragma region block +block::block(std::string block_type_name, int number_of_parameters, const std::vector& parameters) +{ + if (parameters.size() != number_of_parameters) + throw std::invalid_argument("Wrong number of arguments"); + parameters_ = parameters; + block_type_name_ = block_type_name; +} + +std::string block::get_command() +{ + std::string arguments; + + std::for_each(parameters_.begin(), parameters_.end(), [&](double const &par) { + arguments += std::to_string(par) + " "; + }); + + return block_type_name_ + " " + arguments; +} + +//block::~block() {}; +#pragma endregion block + +#pragma region block_processor +block_processor::block_processor() +{ + +} + +void block_processor::add_block(std::unique_ptr &b) +{ + blocks_.push_back(std::move(b)); +} + +double block_processor::calc_all(double input) const +{ + double calculated_value = input; + for (auto const& b : blocks_) + { + calculated_value = (*b).calc(calculated_value); + } + return calculated_value; +} + +std::vector block_processor::get_sequence() +{ + std::vector temp_vector; + temp_vector.reserve(blocks_.size()); + std::transform(blocks_.begin(), blocks_.end(), std::back_inserter(temp_vector), [](std::unique_ptr const &b) + { + return (*b).get_command(); + }); + return temp_vector; +} + +void block_processor::load(sequence_list sl) +{ + blocks_.clear(); + for (sequence_list::iterator it = sl.begin(); it != sl.end(); ++it) { + const std::string block_name = (*it).first; + const std::vector parameter_values = it->second; + std::unique_ptr bl = block_factory::Create(block_name, parameter_values); + blocks_.push_back(std::move(bl)); + } +} + +bool block_processor::remove_block(unsigned int index) +{ + if (index > blocks_.size()) + return false; + blocks_.erase(blocks_.begin() + index - 1); + return true; +} +#pragma endregion block_processor + +#pragma region block_factory +using TBlockCreateMethod = std::unique_ptr(*)(const std::vector& parameter); + +std::map block_factory::create_methods_; + +bool block_factory::Register(const std::string block_name, const std::string block_description, TBlockCreateMethod funcCreateBlock) +{ + if (auto it = create_methods_.find(block_name); it == create_methods_.end()) + { + create_methods_[block_name] = block_meta(block_description, funcCreateBlock); + //std::cout << "LOG: block_factory::Register for block: " + block_name << std::endl; + return true; + } + return false; +} + +std::unique_ptr block_factory::Create(const std::string& block_name, const std::vector& parameters) +{ + if (auto it = create_methods_.find(block_name); it != create_methods_.end()) + { + //std::cout << "LOG: block_factory::Create for block: " + block_name << std::endl; + return it->second.GetCreateMethod()(parameters); // call the createFunc + } + return nullptr; +} + +std::vector block_factory::GetBlockTypes() +{ + //Vector of int to store values + std::vector temp_vector; + temp_vector.reserve(create_methods_.size()); + + //First solution: Copy all value fields from map to a vector using transform() & Lambda function + //std::transform(create_methods_.begin(), create_methods_.end(), std::back_inserter(temp_vector), [](std::pair const &m) + //{ + // return m.first + " : " + m.second.Getdescription() + "\n"; + //}); + + //Second solution: Copy all value fields from map to a vector using Lambda function + std::for_each(create_methods_.begin(), create_methods_.end(), [&](std::pair const &m) { + temp_vector.push_back(m.first + "\t" + m.second.Getdescription() + "\n"); + }); + + return temp_vector; +} + +#pragma endregion block_factory diff --git a/final/tool/block.h b/final/tool/block.h new file mode 100644 index 0000000..6457c23 --- /dev/null +++ b/final/tool/block.h @@ -0,0 +1,72 @@ +#pragma once +#include +#include +#include +#include "self_registered_in_factory.h" + +class block +{ +public: + block(std::string block_type_name, int number_of_parameters, const std::vector& parameters); + virtual ~block() = default; + virtual double calc(double input) = 0; + std::string get_command(); +protected: + std::vector parameters_; +private: + std::string block_type_name_; +}; + +//holder for block meta data: description and create method +class block_meta +{ +public: + using TBlockCreateMethod = std::unique_ptr(*)(const std::vector& parameter); + block_meta(std::string description = "", TBlockCreateMethod block_create_method = nullptr) + { + description_ = description; + block_create_method_ = block_create_method; + } + TBlockCreateMethod GetCreateMethod() const + { + return block_create_method_; + } + std::string Getdescription() const + { + return description_; + } + +private: + std::string description_; + TBlockCreateMethod block_create_method_; +}; + +class block_processor +{ +private: + std::vector> blocks_; +public: + block_processor(); + void add_block(std::unique_ptr &b); + double calc_all(double input) const; + using sequence_list = std::vector>>; + void load(sequence_list sl); + bool remove_block(unsigned int index); + std::vector get_sequence(); +}; + +//prevent class derivation with 'final' +class block_factory final +{ +public: + using TBlockCreateMethod = std::unique_ptr(*)(const std::vector& parameter); + //prevent class instantiation + block_factory() = delete; + //all members are static + //wrap static methods in this separate class because I want to have it in one place and 'create_methods_' has to be hidden + static bool Register(const std::string block_name, const std::string block_description, TBlockCreateMethod funcCreateBlock); + static std::unique_ptr Create(const std::string& block_name, const std::vector& parameters); + static std::vector GetBlockTypes(); +private: + static std::map create_methods_; +}; \ No newline at end of file diff --git a/final/tool/block_add.cpp b/final/tool/block_add.cpp new file mode 100644 index 0000000..f9aef36 --- /dev/null +++ b/final/tool/block_add.cpp @@ -0,0 +1,19 @@ +#include "stdafx.h" +#include "block_add.h" +#include "block.h" +#include "self_registered_in_factory.h" + +#pragma region add +add::add(const std::vector& parameters) : block(GetFactoryName(), 1, parameters) +{ + initial_value_ = parameters[0]; +} + +double add::calc(double input) +{ + is_blocktype_registered_; + return initial_value_ + input; +}; + +std::string self_registered_in_factory::description_ = "Summarize input with parameter value. Example: add 5"; +#pragma endregion add \ No newline at end of file diff --git a/final/tool/block_add.h b/final/tool/block_add.h new file mode 100644 index 0000000..e19b10f --- /dev/null +++ b/final/tool/block_add.h @@ -0,0 +1,22 @@ +#pragma once +#include "stdafx.h" +#include "block.h" +#include + +class add : + //derived from base block class + public block, + //derived from base block class + public self_registered_in_factory +{ +private: + double initial_value_; +public: + add(const std::vector& parameters); + double calc(double input) override; + + static std::unique_ptr CreateMethod(const std::vector& parameters) { + return std::make_unique(parameters); + } + static std::string GetFactoryName() { return "add"; } +}; diff --git a/final/tool/block_lim.cpp b/final/tool/block_lim.cpp new file mode 100644 index 0000000..b1bb467 --- /dev/null +++ b/final/tool/block_lim.cpp @@ -0,0 +1,24 @@ +#include "stdafx.h" +#include "block_lim.h" +#include "block.h" +#include "self_registered_in_factory.h" + +lim::lim(const std::vector& parameters) : block(GetFactoryName(), 2, parameters) +{ + initial_value_1_ = parameters[0]; + initial_value_2_ = parameters[1]; +} + +double lim::calc(double input) +{ + is_blocktype_registered_; + double ret = input; + if (input < initial_value_1_) + ret = initial_value_1_; + + if (input > initial_value_2_) + ret = initial_value_2_; + return ret; +} + +std::string self_registered_in_factory::description_ = "Limit between numbers. Example: lim 0 100"; diff --git a/final/tool/block_lim.h b/final/tool/block_lim.h new file mode 100644 index 0000000..c6852b2 --- /dev/null +++ b/final/tool/block_lim.h @@ -0,0 +1,19 @@ +#pragma +#include "stdafx.h" +#include "block.h" +#include + +class lim : public block, public self_registered_in_factory +{ +private: + double initial_value_1_; + double initial_value_2_; +public: + lim(const std::vector& parameters); + double calc(double input) override; + + static std::unique_ptr CreateMethod(const std::vector& parameters) { + return std::make_unique(parameters); + } + static std::string GetFactoryName() { return "lim"; } +}; diff --git a/final/tool/block_mult.cpp b/final/tool/block_mult.cpp new file mode 100644 index 0000000..329fe38 --- /dev/null +++ b/final/tool/block_mult.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" +#include "block_mult.h" +#include "block.h" +#include "self_registered_in_factory.h" + +#pragma region mult +mult::mult(const std::vector& parameters) : block(GetFactoryName(), 1, parameters) +{ + initial_value_ = parameters[0]; +} +double mult::calc(double input) +{ + is_blocktype_registered_; + return initial_value_ * input; +} +std::string self_registered_in_factory::description_ = "Multiply input value with parameter value. Example: mult 5"; + +#pragma endregion mult diff --git a/final/tool/block_mult.h b/final/tool/block_mult.h new file mode 100644 index 0000000..3cd9de6 --- /dev/null +++ b/final/tool/block_mult.h @@ -0,0 +1,18 @@ +#pragma once +#include "stdafx.h" +#include "block.h" +#include + +class mult : public block, public self_registered_in_factory +{ +private: + double initial_value_; +public: + mult(const std::vector& parameters); + double calc(double input) override; + + static std::unique_ptr CreateMethod(const std::vector& parameters) { + return std::make_unique(parameters); + } + static std::string GetFactoryName() { return "mult"; } +}; \ No newline at end of file diff --git a/final/tool/block_neg.cpp b/final/tool/block_neg.cpp new file mode 100644 index 0000000..0d02ed7 --- /dev/null +++ b/final/tool/block_neg.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#include "block_neg.h" +#include "block.h" +//#include +#include "self_registered_in_factory.h" + +neg::neg(const std::vector& parameters) : block(GetFactoryName(), 0, parameters) +{ +} + +double neg::calc(double input) +{ + is_blocktype_registered_; + return -1 * input; +} + +std::string self_registered_in_factory::description_ = "Negation of input value. Example: neg"; diff --git a/final/tool/block_neg.h b/final/tool/block_neg.h new file mode 100644 index 0000000..996cf61 --- /dev/null +++ b/final/tool/block_neg.h @@ -0,0 +1,18 @@ +#pragma +#include "stdafx.h" +#include "block.h" +#include + +class neg : public block, public self_registered_in_factory +{ +private: + //double initial_value_; +public: + neg(const std::vector& parameters); + double calc(double input) override; + + static std::unique_ptr CreateMethod(const std::vector& parameters) { + return std::make_unique(parameters); + } + static std::string GetFactoryName() { return "neg"; } +}; diff --git a/final/tool/block_queq.cpp b/final/tool/block_queq.cpp new file mode 100644 index 0000000..f4904cf --- /dev/null +++ b/final/tool/block_queq.cpp @@ -0,0 +1,19 @@ +#include "stdafx.h" +#include "block_queq.h" +#include "block.h" +#include "self_registered_in_factory.h" + +queq::queq(const std::vector& parameters) : block(GetFactoryName(), 3, parameters) +{ + initial_value_a_ = parameters[0]; + initial_value_b_ = parameters[1]; + initial_value_c_ = parameters[2]; +} + +double queq::calc(double input) +{ + is_blocktype_registered_; + return initial_value_a_ * input * input + initial_value_b_ * input + initial_value_c_; +} + +std::string self_registered_in_factory::description_ = "Quadratic equation (ax^2 + bx + c). Example: queq 1 2 3"; diff --git a/final/tool/block_queq.h b/final/tool/block_queq.h new file mode 100644 index 0000000..7e355dd --- /dev/null +++ b/final/tool/block_queq.h @@ -0,0 +1,20 @@ +#pragma +#include "stdafx.h" +#include "block.h" +#include + +class queq : public block, public self_registered_in_factory +{ +private: + double initial_value_a_; + double initial_value_b_; + double initial_value_c_; +public: + queq(const std::vector& parameters); + double calc(double input) override; + + static std::unique_ptr CreateMethod(const std::vector& parameters) { + return std::make_unique(parameters); + } + static std::string GetFactoryName() { return "queq"; } +}; diff --git a/final/tool/menus.cpp b/final/tool/menus.cpp new file mode 100644 index 0000000..157bc0d --- /dev/null +++ b/final/tool/menus.cpp @@ -0,0 +1,192 @@ +#include "stdafx.h" +#include +#include +#include +#include +#include +#include +#include +#include "block.h" +#include +#include "menus.h" + +void press_enter_to_go_back() +{ + std::cout << "\nPress ENTER to go back...\n"; + std::cin.ignore(); + system("cls"); +} + +unsigned int get_number_from_console(std::string caption) +{ + unsigned int user_number = 0; + std::string input = ""; + while (true) { + std::cout << caption; + getline(std::cin, input); + // This code converts from string to number safely. + std::stringstream input_ss(input); + if (input_ss >> user_number) + break; + std::cout << "Invalid number, please try again..." << std::endl; + } + return user_number; +} + +double get_input_from_console() +{ + double input_value = 0.; + std::string input; + while (true) { + std::cout << "Please enter a valid input number: "; + getline(std::cin, input); + // This code converts from string to number safely. + std::stringstream input_ss(input); + if (input_ss >> input_value) + break; + std::cout << "Invalid input value, please try again..." << std::endl; + } + return input_value; +} + +void show_available_block_types() +{ + //show available block types + system("cls"); + std::string block_list_menu = "Available block types:\n"; + std::vector block_list = block_factory::GetBlockTypes(); + for (std::vector::iterator it = block_list.begin(); it != block_list.end(); ++it) + block_list_menu += *it; + std::cout << block_list_menu << std::endl; +} + +void show_block_sequence(block_processor &bp) +{ + //show block sequence + system("cls"); + std::cout << "Block sequence:\n" << std::endl; + int index = 0; + std::vector temp_v = bp.get_sequence(); + std::for_each(temp_v.begin(), temp_v.end(), [&](std::string const &c) + { + index++; + std::cout << index << ":\t" << c << std::endl; + }); +} +std::pair> get_block_command_from_text(std::string command_text) +{ + bool block_name_taken = false; + std::string block_name; + std::vector parameters; + std::istringstream line_ss(command_text); + do + { + std::string value; + line_ss >> value; + if (value == "") break; + if (!block_name_taken) + { + block_name = value; + block_name_taken = true; + } + else + parameters.push_back(atof(value.c_str())); + } while (!line_ss.eof()); + return std::pair>(block_name, parameters); +} + +bool add_new_block(block_processor &bp) +{ + //add new block + system("cls"); + std::string block_command; + std::string input = ""; + while (true) { + std::cout << "Enter new block (or just ENTER to go back):\ "; + getline(std::cin, input); + if (input == "") + return false; + try + { + std::pair> p = get_block_command_from_text(input); + std::unique_ptr bl = block_factory::Create(p.first, p.second); + if (bl == nullptr) return false; + bp.add_block(bl); + return true; + } + catch (...) + { + std::cout << "Invalid entry, please try again..." << std::endl; + return false; + } + } +} +void remove_block_from_sequence(block_processor &bp) +{ + system("cls"); + const unsigned int block_index = get_number_from_console("Enter index of block to be removed:"); + if (bp.remove_block(block_index)) + std::cout << "Block on index" << block_index << " is removed." << std::endl; + else + std::cout << "ERROR: Block on index" << block_index << " is NOT removed." << std::endl; +} +// +void calc_sequence(const block_processor &bp) +{ + system("cls"); + const double input_value = get_input_from_console(); + const double result = bp.calc_all(input_value); + std::cout << "Result: " << result << std::endl; +} + +void load_from_file(block_processor &bp, std::string &file_name) +{ + system("cls"); + block_processor::sequence_list stored_blocks; + if (get_blocks_from_file(file_name, stored_blocks)) + { + try + { + bp.load(stored_blocks); + std::cout << "Block sequence is loaded from '" << file_name << "' file.\n" << std::endl; + } + catch (const std::exception&) + { + std::cout << "ERROR: Block sequence cannot be loaded from '" << file_name << "' file.\n" << std::endl; + } + } + else + { + std::cout << "File '" << file_name << "' is missing.\n" << std::endl; + } +} + +bool get_blocks_from_file(std::string &file_name, block_processor::sequence_list &stored_blocks) +{ + std::ifstream parameters_stream(file_name, std::ios::in); + std::string block_line; + + if (!parameters_stream.is_open()) { return false; } + + std::istringstream line_stream; + while (getline(parameters_stream, block_line)) { + std::pair> block_command = get_block_command_from_text(block_line); + stored_blocks.push_back(block_command); + } + parameters_stream.close(); + return true; +} + +void save_sequence_to_file(block_processor &bp, std::string file_name) +{ + system("cls"); + + std::ofstream parameters_stream(file_name, std::ios::out); + if (!parameters_stream.is_open()) { std::cout << "\nERROR: Block sequence is NOT saved to file: " << file_name; } + + auto command_sequence = bp.get_sequence(); + for (std::vector::iterator it = command_sequence.begin(); it != command_sequence.end(); ++it) + parameters_stream << *it << std::endl; + parameters_stream.close(); + std::cout << "\nBlock sequence is saved to file: " << file_name; +} \ No newline at end of file diff --git a/final/tool/menus.h b/final/tool/menus.h new file mode 100644 index 0000000..9ee078e --- /dev/null +++ b/final/tool/menus.h @@ -0,0 +1,29 @@ +#pragma once +#include + +inline const std::string MAIN_MENU = +"MAIN MENU:\n\n" +"Chose command (enter number):\n" +"1:\tShow available block types.\n" +"2:\tShow block sequence.\n" +"3:\tAdd new block.\n" +"4:\tRun block sequence.\n" +"5:\tRemove block from sequence.\n" +"6:\tSave block sequence to file.\n" +"7:\tLoad block sequence from file.\n" +"0:\tExit.\n"; + +inline const std::string BLOCK_SEQUENCE_FILE = "block_sequence.txt"; + +void press_enter_to_go_back(); +void show_available_block_types(); +unsigned int get_number_from_console(std::string caption); +double get_input_from_console(); +void show_block_sequence(block_processor &bp); +std::pair> get_block_command_from_text(std::string command_text); +bool add_new_block(block_processor &bp); +void calc_sequence(const block_processor &bp); +void remove_block_from_sequence(block_processor &bp); +void load_from_file(block_processor &bp, std::string &file_name); +bool get_blocks_from_file(std::string &file_name, block_processor::sequence_list &stored_blocks); +void save_sequence_to_file(block_processor &bp, std::string file_name); \ No newline at end of file diff --git a/final/tool/self_registered_in_factory.h b/final/tool/self_registered_in_factory.h new file mode 100644 index 0000000..9475cc3 --- /dev/null +++ b/final/tool/self_registered_in_factory.h @@ -0,0 +1,26 @@ +#pragma once + +//with below line we promise that definition of class WILL BE included in some files +//this is 'Forward declaration': https://en.wikipedia.org/wiki/Forward_declaration +class block_factory; + +//template +template +class self_registered_in_factory +{ +protected: + //because it is static member, it will be initialized in the moment when template is compiled + //template is compiled when concrete class (which is derived from SelfRegisteredInFactory class) is instantiated (before main method) + static bool is_blocktype_registered_; + static std::string description_; +public: + static std::string GetDescription() + { + return description_; + }; +}; + +//this is implementation of assignment of static variable is_blocktype_registered_ which is triggered before 'main'. +template +bool self_registered_in_factory::is_blocktype_registered_ = +block_factory::Register(TBlock::GetFactoryName(), TBlock::GetDescription(), TBlock::CreateMethod); diff --git a/final/tool/stdafx.cpp b/final/tool/stdafx.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5a8cdb07ed7eec4d390d55c7b8b4080edfcd9f78 GIT binary patch literal 584 zcmZ`%L2kk@5S%ko|6tK`%bgoiRdM4`4qQW%C`M@<1tZAkp)+fdG({*fw%5Bev$Nc; z3oPJpL`04ZB`$c!H(GuUOM3}8p<}Gzt%OHyr-+LAkui^kuQjuBz8o1j@GL~fRav%f z8S})^@%ORn1v4sI8KeDQ`QZmm~)A6;^o|MR?JI>V}Q z8vEtLe;CsQ`f9DHxFP?Ev!}!l=AXA(tbaPXB)?e2)ZxZ=WBxB^dUob-#=8i1_Gje& z#NH7xP7*QWxk;u;fEfw0WxN;+co0fK2Vs4B9U*sm0{`t1wOo&Foy0~*EXI^K{ z&F{}p2USW{Xp2p>*G`#oJ!s%(q!H-M(Ty4fmG}kNtEQTB%)V1m=}BwwfWPvrT#@e@ zH0NG}74Ao{glS)#QXA|NYdIfY7hrMp+Ji@H`t9kzWx_SD6;~Ri;cvXH)6CO{-I1p_IJPQ#X=r zigZeSqQguJz35q;zt9^Q_C^^>*mmuXUCp&pw^fPoGX+dho4RCrySs7j@9_UipWkA5 OQDt4mH~x-^Z~X_?9czaG literal 0 HcmV?d00001 diff --git a/final/tool/tool.cpp b/final/tool/tool.cpp new file mode 100644 index 0000000000000000000000000000000000000000..10eb0270238c306feef5cf260fa1994021e65172 GIT binary patch literal 9730 zcmds7ZBH9V5Z=#~`X4T?l$fM4p|4V)kxELWstIZH@~N#XV-8~lW7n7v6!F)SKF^Nl zX7~2aK9lmI$^zTR?Ci`luRF`9kLzyTWp3uicpbTp>$?eFnR|qvUtI1E+|V84{lrrp z;!}p7W2~6Eb9|m)9Vuq`e1Ol>fUb}4$5=_uT-Wowk3iqY=NaDl_m2D0eE})f{X0K@ zeTJ~l`U|Y;d2jryvK?|*YeaU9~6{N}-~bg9ZFf8ge^m zNH_9$)yw}58upjS>f^P?q zUT4id1)iDJ*k;t7Oz<5W%WMR4fd(?h;Z-J7AbO>`w3ESyJzl&d*XhBrCzxm_lJ9f z-@ACSgC|ejulW1Ue0O53ni56khqrh#0gj9$#s;&r#Hdma@g##S)CL){N#-^JyvbAf zX6`jOGww2X<~cDhFk;oZGrzO!GX)(hgYZ6`XBj?;Wuhl$s3&8@H!Uw2V1Uoe5GpBU zxEzYIKmBL}o@72jj%F{`t5YxUmfON#TJHBsctplJzJEyB`>dP z1I^{tHKdDZFx;<3f^Y|oCGoH+BArhrbMItcco@h-RLoDxUi{qsj4ZJW9?yN|cvJYP ztx0{0JnMJYK7bv=2>zZUQo}wV?)=(5lGp<>4_ODvRm=?;GJFqqmC79QWMs7p?9~hQ zeYR-oCwIUyIMT{3aaIwV(@|Pl;((#G-*C9|p!#DO5 ztlvlY%_yTiE!udNIESaQcMG|S%)+Bv!6WS1x&e=NVSRGY7wdy`hh4n$^dTN79nAoe zl3&l)ziJM9oPMq-vSMK1@ka_ z3Z4+@8zz6KW9)gv)p`!DXSc}pp%19Gu@9^LEpwH#UCYxRV9IRO}n6Z4T>i6T)&@xH@7T3{|Nq1y>nz2J#{U%tFF4; z-uwMr$CuD*gj~V%QRutuUFAe8+SkQy>etLl$}#ljur`*nfqHuCjn{AeALhusg7s5W z%96|6C_PayT2gFQJBePdiwk=wjos@<@y9ue&){2(^4Pah6wTYT`(7Mn%)D3AgRasA zE527F$xT|4RpJ1alQU7=9Z4lP_tk*0&fb=j%9Ef^Rb+%#=RM8- zPHWO#l7@Q!zo(I^rBnjy+az|BPCG>P0P8I4CeE5WZT7f0o2|iSDkCL}<>V8&;|OW0 z>G+hrd8f$AGXJigC1_?-O}Uz{Mg8Tr=Pc8_w0=lmsd-qZeA>Z;lc^LVomsUo!Lmx; zbP5&fAif>{~-2tblM5hiv z%6W{}7&6e3>;_{rZwA8{(eP}dD5tm>vT40}>QC*|ZWVpRyGB^c88YWkgT_-zmD4n_ zPa0QtSJ6DyO-p`~+4BH;-NfyZ>Ji(*BxEKNDdk2|X5TW|W{u$Ff;lauX=9B{>C$81v&e#~31dt1?;S)Yr{{80S1oM^)YM=MTAmX* zagp1a3@gJr$gWBz)tlHO^@TX9$)YW&2w&>}-1yA|vfBBgatJSDs*-oj~wcN~-@$2|{k zdP2D<^9V2IL!MhWX-=cEi%c5Qg{ND3$a`3gr&`UqA#$;WRIlnv*@2DN8y@<}kWTYL zzOfwTC@}xxK27|Xojm_5Aoo5T%Nlz!eU4KRqp{u8FRZ^??d)r~vAd6N!kgSZr@6yA zoexE{3-*goGW0*yMr|ctHNN7kCiwEEziie2Dl=Kkl_OPVh}(t#rc1!VE5Q>1sJmEnhELQrA9k5B%=t<@8m)mdCtW=KfrM exrsH+tMl=4zWh(B`dniCFYkMw<8vC*7w%tfn)D+8 literal 0 HcmV?d00001 diff --git a/final/tool/tool.vcxproj b/final/tool/tool.vcxproj new file mode 100644 index 0000000..bc6ffd1 --- /dev/null +++ b/final/tool/tool.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {6E9AF5D3-751C-446F-B351-8C0DEE50AFAF} + Win32Proj + tool + 10.0.17134.0 + + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + Application + true + v141 + Unicode + + + Application + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + ..\$(Configuration)\$(Platform) + + + true + ..\$(Configuration)\$(Platform) + + + false + ..\$(Configuration)\$(Platform) + + + false + + + + Use + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + + + + + Use + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + + + + + Use + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + + + + + Use + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp17 + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + \ No newline at end of file diff --git a/final/tool/tool.vcxproj.filters b/final/tool/tool.vcxproj.filters new file mode 100644 index 0000000..8740c95 --- /dev/null +++ b/final/tool/tool.vcxproj.filters @@ -0,0 +1,78 @@ + + + + + {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 + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file From 5524ccb8ce3941c309069007010991f729b0494a Mon Sep 17 00:00:00 2001 From: jadranko lucic Date: Wed, 28 Nov 2018 15:11:22 +0100 Subject: [PATCH 2/3] Final: - process inputs from file - more tests - some refactoring - new methods on block_processor: load_from_stream, bulk_calc, get_block_command_from_text --- final/tests/tests.vcxproj | 4 ++ final/tests/tool_test.cpp | 65 ++++++++++++++++++++++-- final/tool/block.cpp | 79 ++++++++++++++++++++++++++--- final/tool/block.h | 3 ++ final/tool/menus.cpp | 101 ++++++++++++++++++++------------------ final/tool/menus.h | 9 ++-- final/tool/tool.cpp | Bin 9730 -> 9896 bytes 7 files changed, 200 insertions(+), 61 deletions(-) diff --git a/final/tests/tests.vcxproj b/final/tests/tests.vcxproj index 5df0842..704ffb4 100644 --- a/final/tests/tests.vcxproj +++ b/final/tests/tests.vcxproj @@ -89,6 +89,10 @@ + + + + diff --git a/final/tests/tool_test.cpp b/final/tests/tool_test.cpp index b684b76..4e71c42 100644 --- a/final/tests/tool_test.cpp +++ b/final/tests/tool_test.cpp @@ -1,4 +1,5 @@ #include "CppUnitTest.h" +#include using namespace Microsoft::VisualStudio::CppUnitTestFramework; #include "tests.h" @@ -12,11 +13,69 @@ TEST_CLASS(test_tool) TEST_METHOD(test_add) { block_processor bp; - std::vector p; - p.push_back(5); - std::unique_ptr block_add = block_factory::Create("add", p); + //std::vector p; + //p.push_back(5); + std::unique_ptr block_add = block_factory::Create("add", { 5 }); bp.add_block(block_add); double res = bp.calc_all(5.); Assert::AreEqual(10., res); } + + TEST_METHOD(test_bulk_calc_with_add_block) + { + //create block processor with 'add' block + const double add_parameter = 5.; + block_processor bp; + bp.add_block(block_factory::Create("add", { add_parameter })); + + //prepare input values + std::vector inputs{1,2,3}; + std::stringstream input_ss; + for (auto it = inputs.begin(); it != inputs.end(); ++it) + input_ss << *it << std::endl; + + //validate return value from bulk_calc + std::stringstream output_ss; + Assert::AreEqual(bp.bulk_calc(input_ss, output_ss), true); + + //validate output values + double output_value; + std::vector outputs; + std::string output_line; + + //check that all output values are doubles + std::istringstream line_stream; + while (std::getline(output_ss, output_line)) { + std::stringstream output_value_ss(output_line); + if (output_value_ss >> output_value) + outputs.push_back(output_value); + else + Assert::Fail(L"output is wrong"); + } + + //check input and output sizes + Assert::AreEqual(outputs.size() == 3, true); + + //compare and validate each value + const bool is_equal = std::equal(outputs.begin(), outputs.end(), inputs.begin(), + [add_parameter](double &vo, double &vi) + { + return add_parameter + vi == vo; + }); + + Assert::AreEqual(is_equal, true); + } + + TEST_METHOD(test_load_sequence) + { + const double add_parameter = 5.; + block_processor bp; + + std::stringstream input_ss("add 5\nmult 2"); + Assert::AreEqual(bp.load_from_stream(input_ss), true); + + //check bp size + auto seq = bp.get_sequence(); + Assert::AreEqual(seq.size() == 2, true); + } }; diff --git a/final/tool/block.cpp b/final/tool/block.cpp index ef5ab67..4692d2b 100644 --- a/final/tool/block.cpp +++ b/final/tool/block.cpp @@ -1,8 +1,11 @@ #include "stdafx.h" #include "block.h" #include -#include +//#include #include +#include +#include +#include "menus.h" #pragma region block block::block(std::string block_type_name, int number_of_parameters, const std::vector& parameters) @@ -15,13 +18,13 @@ block::block(std::string block_type_name, int number_of_parameters, const std::v std::string block::get_command() { - std::string arguments; - - std::for_each(parameters_.begin(), parameters_.end(), [&](double const &par) { - arguments += std::to_string(par) + " "; + std::string arguments = std::accumulate(parameters_.begin(), parameters_.end(), std::string(), + [](std::string &args, double &par) + { + return args.empty() ? std::to_string(par) : args + " " + std::to_string(par); }); - return block_type_name_ + " " + arguments; + return block_type_name_ + (arguments.empty() ? "" : " " + arguments); } //block::~block() {}; @@ -70,6 +73,22 @@ void block_processor::load(sequence_list sl) } } +bool block_processor::load_from_stream(std::istream &is) +{ + //check streams + if (!is.good()) { return false; } + + sequence_list stored_blocks; + std::string block_line; + std::istringstream line_stream; + while (std::getline(is, block_line)) { + const std::pair> block_command = get_block_command_from_text(block_line); + stored_blocks.push_back(block_command); + } + load(stored_blocks); + return true; +} + bool block_processor::remove_block(unsigned int index) { if (index > blocks_.size()) @@ -77,6 +96,54 @@ bool block_processor::remove_block(unsigned int index) blocks_.erase(blocks_.begin() + index - 1); return true; } + +bool block_processor::bulk_calc(std::istream &is, std::ostream &os) const +{ + //check streams + if (!os.good() || !is.good()) { return false; } + + //load inputs from input stream + double input_value; + std::vector inputs_for_calc; + std::string input_line; + + std::istringstream line_stream; + while (getline(is, input_line)) { + std::stringstream input_ss(input_line); + if (input_ss >> input_value) + inputs_for_calc.push_back(input_value); + else + return false; + } + + //calculate each input value and send to output stream + for (std::vector::iterator it = inputs_for_calc.begin(); it != inputs_for_calc.end(); ++it) + os << calc_all(*it) << std::endl; + + return true; +} + +std::pair> block_processor::get_block_command_from_text(std::string command_text) +{ + bool block_name_taken = false; + std::string block_name; + std::vector parameters; + std::istringstream line_ss(command_text); + do + { + std::string value; + line_ss >> value; + if (value == "") break; + if (!block_name_taken) + { + block_name = value; + block_name_taken = true; + } + else + parameters.push_back(atof(value.c_str())); + } while (!line_ss.eof()); + return std::pair>(block_name, parameters); +} #pragma endregion block_processor #pragma region block_factory diff --git a/final/tool/block.h b/final/tool/block.h index 6457c23..5708d18 100644 --- a/final/tool/block.h +++ b/final/tool/block.h @@ -51,8 +51,11 @@ class block_processor double calc_all(double input) const; using sequence_list = std::vector>>; void load(sequence_list sl); + bool load_from_stream(std::istream &is); bool remove_block(unsigned int index); std::vector get_sequence(); + bool bulk_calc(std::istream &is, std::ostream &os) const; + static std::pair> get_block_command_from_text(std::string command_text); }; //prevent class derivation with 'final' diff --git a/final/tool/menus.cpp b/final/tool/menus.cpp index 157bc0d..02de8cc 100644 --- a/final/tool/menus.cpp +++ b/final/tool/menus.cpp @@ -9,6 +9,7 @@ #include "block.h" #include #include "menus.h" +#include void press_enter_to_go_back() { @@ -33,12 +34,12 @@ unsigned int get_number_from_console(std::string caption) return user_number; } -double get_input_from_console() +double get_input_from_console(std::string caption) { double input_value = 0.; std::string input; while (true) { - std::cout << "Please enter a valid input number: "; + std::cout << caption; getline(std::cin, input); // This code converts from string to number safely. std::stringstream input_ss(input); @@ -49,6 +50,14 @@ double get_input_from_console() return input_value; } +std::string get_text_from_console(std::string caption) +{ + std::string text_input; + std::cout << caption << "\n"; + getline(std::cin, text_input); + return text_input; +} + void show_available_block_types() { //show available block types @@ -73,27 +82,6 @@ void show_block_sequence(block_processor &bp) std::cout << index << ":\t" << c << std::endl; }); } -std::pair> get_block_command_from_text(std::string command_text) -{ - bool block_name_taken = false; - std::string block_name; - std::vector parameters; - std::istringstream line_ss(command_text); - do - { - std::string value; - line_ss >> value; - if (value == "") break; - if (!block_name_taken) - { - block_name = value; - block_name_taken = true; - } - else - parameters.push_back(atof(value.c_str())); - } while (!line_ss.eof()); - return std::pair>(block_name, parameters); -} bool add_new_block(block_processor &bp) { @@ -108,7 +96,7 @@ bool add_new_block(block_processor &bp) return false; try { - std::pair> p = get_block_command_from_text(input); + std::pair> p = block_processor::get_block_command_from_text(input); std::unique_ptr bl = block_factory::Create(p.first, p.second); if (bl == nullptr) return false; bp.add_block(bl); @@ -134,7 +122,7 @@ void remove_block_from_sequence(block_processor &bp) void calc_sequence(const block_processor &bp) { system("cls"); - const double input_value = get_input_from_console(); + const double input_value = get_input_from_console("Please enter a valid number: "); const double result = bp.calc_all(input_value); std::cout << "Result: " << result << std::endl; } @@ -143,38 +131,55 @@ void load_from_file(block_processor &bp, std::string &file_name) { system("cls"); block_processor::sequence_list stored_blocks; - if (get_blocks_from_file(file_name, stored_blocks)) + + std::ifstream parameters_stream(file_name, std::ios::in); + std::string block_line; + + if (!parameters_stream.is_open()) { - try - { - bp.load(stored_blocks); - std::cout << "Block sequence is loaded from '" << file_name << "' file.\n" << std::endl; - } - catch (const std::exception&) - { - std::cout << "ERROR: Block sequence cannot be loaded from '" << file_name << "' file.\n" << std::endl; - } + std::cout << "ERROR: Block sequence cannot be loaded from '" << file_name << "' file.\n" << std::endl; + return; } - else + try { - std::cout << "File '" << file_name << "' is missing.\n" << std::endl; + bp.load_from_stream(parameters_stream); + std::cout << "Block sequence is loaded from '" << file_name << "' file.\n" << std::endl; } + catch (const std::exception&) + { + std::cout << "ERROR: Block sequence cannot be loaded from '" << file_name << "' file.\n" << std::endl; + } + parameters_stream.close(); } -bool get_blocks_from_file(std::string &file_name, block_processor::sequence_list &stored_blocks) +void load_and_calc_inputs_from_file(block_processor &bp) { - std::ifstream parameters_stream(file_name, std::ios::in); - std::string block_line; + system("cls"); - if (!parameters_stream.is_open()) { return false; } + //Load inputs from file + double input_value; + std::vector inputs_for_calc; + const std::string file_with_inputs = get_text_from_console("Please enter file name with input values (each value in new row): "); + std::ifstream inputs_is(file_with_inputs); + std::string input_line; + if (!inputs_is.is_open()) { std::cout << "\nERROR: Cannot open input file: " << file_with_inputs; return; } - std::istringstream line_stream; - while (getline(parameters_stream, block_line)) { - std::pair> block_command = get_block_command_from_text(block_line); - stored_blocks.push_back(block_command); - } - parameters_stream.close(); - return true; + //Prepare result stream + const std::size_t lastindex = file_with_inputs.find_last_of("."); + const std::string raw_file_name = file_with_inputs.substr(0, lastindex); + const std::string result_file_name = raw_file_name + "_results.txt"; + std::ofstream results_os(result_file_name, std::ios::out); + if (!results_os.is_open()) { std::cout << "\nERROR: Cannot open output file:: " << result_file_name; return; } + + // Calculate and save results + if (!bp.bulk_calc(inputs_is, results_os)) + std::cout << "\nERROR: Input file: '" << file_with_inputs << "' has one or more invalid values."; + else + std::cout << "\nResults are saved to file: " << result_file_name; + + //close streams + inputs_is.close(); + results_os.close(); } void save_sequence_to_file(block_processor &bp, std::string file_name) diff --git a/final/tool/menus.h b/final/tool/menus.h index 9ee078e..8a20106 100644 --- a/final/tool/menus.h +++ b/final/tool/menus.h @@ -11,6 +11,7 @@ inline const std::string MAIN_MENU = "5:\tRemove block from sequence.\n" "6:\tSave block sequence to file.\n" "7:\tLoad block sequence from file.\n" +"8:\tLoad and calculate inputs from file.\n" "0:\tExit.\n"; inline const std::string BLOCK_SEQUENCE_FILE = "block_sequence.txt"; @@ -18,12 +19,12 @@ inline const std::string BLOCK_SEQUENCE_FILE = "block_sequence.txt"; void press_enter_to_go_back(); void show_available_block_types(); unsigned int get_number_from_console(std::string caption); -double get_input_from_console(); +double get_input_from_console(std::string caption); +std::string get_text_from_console(std::string caption); void show_block_sequence(block_processor &bp); -std::pair> get_block_command_from_text(std::string command_text); bool add_new_block(block_processor &bp); void calc_sequence(const block_processor &bp); void remove_block_from_sequence(block_processor &bp); void load_from_file(block_processor &bp, std::string &file_name); -bool get_blocks_from_file(std::string &file_name, block_processor::sequence_list &stored_blocks); -void save_sequence_to_file(block_processor &bp, std::string file_name); \ No newline at end of file +void save_sequence_to_file(block_processor &bp, std::string file_name); +void load_and_calc_inputs_from_file(block_processor &bp); \ No newline at end of file diff --git a/final/tool/tool.cpp b/final/tool/tool.cpp index 10eb0270238c306feef5cf260fa1994021e65172..719732a549f7ec3707ee7e6fb1130d15b1774758 100644 GIT binary patch delta 142 zcmZqjS>d~3oA6{a&ZNovc&nT(7_1odfzXE`pCOSUg+T#G=7H&CAS;I0swVOA=dx^ delta 28 mcmV+%0OS9tO@d6YwiA=E2!@ls4G5F|6bh5T6Uei?73>hX^$gJf From 21d7a99fb7d7a475569e5f0b2315ab92df48f39c Mon Sep 17 00:00:00 2001 From: jadranko lucic Date: Fri, 30 Nov 2018 13:36:33 +0100 Subject: [PATCH 3/3] Jan's comments are resolved. New tests Refactoring --- final/tests/tool_test.cpp | 54 ++++++++++++++++++++++-- final/tool/block.cpp | 45 +++++++++++--------- final/tool/block.h | 23 +++++----- final/tool/block_add.cpp | 4 +- final/tool/block_add.h | 6 ++- final/tool/block_lim.cpp | 15 +++---- final/tool/block_lim.h | 4 +- final/tool/block_mult.cpp | 4 +- final/tool/block_mult.h | 6 ++- final/tool/block_neg.cpp | 5 +-- final/tool/block_neg.h | 6 +-- final/tool/block_queq.cpp | 4 +- final/tool/block_queq.h | 4 +- final/tool/menus.cpp | 36 ++++++++++------ final/tool/self_registered_in_factory.h | 9 ++-- final/tool/tool.cpp | Bin 9896 -> 6268 bytes 16 files changed, 144 insertions(+), 81 deletions(-) diff --git a/final/tests/tool_test.cpp b/final/tests/tool_test.cpp index 4e71c42..705b7f1 100644 --- a/final/tests/tool_test.cpp +++ b/final/tests/tool_test.cpp @@ -15,18 +15,66 @@ TEST_CLASS(test_tool) block_processor bp; //std::vector p; //p.push_back(5); - std::unique_ptr block_add = block_factory::Create("add", { 5 }); - bp.add_block(block_add); + std::unique_ptr block_add = block_factory::create("add", { 5 }); + bp.add_block(std::move(block_add)); double res = bp.calc_all(5.); Assert::AreEqual(10., res); } + TEST_METHOD(test_lim_lower) + { + block_processor bp; + std::unique_ptr block_lim = block_factory::create("lim", { 5, 10 }); + bp.add_block(std::move(block_lim)); + double res = bp.calc_all(4.); + Assert::AreEqual(5., res); + } + + TEST_METHOD(test_lim_between) + { + block_processor bp; + std::unique_ptr block_lim = block_factory::create("lim", { 5, 10 }); + bp.add_block(std::move(block_lim)); + double res = bp.calc_all(7.); + Assert::AreEqual(7., res); + } + + TEST_METHOD(test_lim_upper) + { + block_processor bp; + std::unique_ptr block_lim = block_factory::create("lim", { 5, 10 }); + bp.add_block(std::move(block_lim)); + double res = bp.calc_all(17.); + Assert::AreEqual(10., res); + } + TEST_METHOD(test_calc_all) + { + block_processor bp; + std::unique_ptr block_add = block_factory::create("add", { 2 }); + std::unique_ptr block_mult = block_factory::create("mult", { 2 }); + bp.add_block(std::move(block_add)); + bp.add_block(std::move(block_mult)); + double res = bp.calc_all(1.); + Assert::AreEqual(6., res); + } + + TEST_METHOD(test_get_block_infos) + { + std::vector abl = block_factory::get_block_infos(); + Assert::IsTrue(std::any_of(abl.begin(), abl.end(), + [](std::string const& s) + { + return s.compare(0, 3, "add") == 0; + })); + } + TEST_METHOD(test_bulk_calc_with_add_block) { //create block processor with 'add' block const double add_parameter = 5.; block_processor bp; - bp.add_block(block_factory::Create("add", { add_parameter })); + std::unique_ptr bl = block_factory::create("add", { add_parameter }); + bp.add_block(std::move(bl)); //prepare input values std::vector inputs{1,2,3}; diff --git a/final/tool/block.cpp b/final/tool/block.cpp index 4692d2b..604805c 100644 --- a/final/tool/block.cpp +++ b/final/tool/block.cpp @@ -36,18 +36,20 @@ block_processor::block_processor() } -void block_processor::add_block(std::unique_ptr &b) +void block_processor::add_block(std::unique_ptr b) { blocks_.push_back(std::move(b)); } double block_processor::calc_all(double input) const { - double calculated_value = input; - for (auto const& b : blocks_) + const double calculated_value = std::accumulate(blocks_.begin(), blocks_.end(), input, + //with below 'const' we are promising that variable 'b' will not (and cannot) be changed. + [](double intermediate_value, const std::unique_ptr &b) { - calculated_value = (*b).calc(calculated_value); - } + return intermediate_value = (*b).calc(intermediate_value); + }); + return calculated_value; } @@ -62,14 +64,14 @@ std::vector block_processor::get_sequence() return temp_vector; } -void block_processor::load(sequence_list sl) +void block_processor::load(const sequence_list& sl) { blocks_.clear(); - for (sequence_list::iterator it = sl.begin(); it != sl.end(); ++it) { + for (sequence_list::const_iterator it = sl.begin(); it != sl.end(); ++it) { const std::string block_name = (*it).first; const std::vector parameter_values = it->second; - std::unique_ptr bl = block_factory::Create(block_name, parameter_values); - blocks_.push_back(std::move(bl)); + std::unique_ptr bl = block_factory::create(block_name, parameter_values); + add_block(std::move(bl)); } } @@ -140,7 +142,8 @@ std::pair> block_processor::get_block_command_f block_name_taken = true; } else - parameters.push_back(atof(value.c_str())); + parameters.push_back(std::stof(value)); + //parameters.push_back(atof(value.c_str())); } while (!line_ss.eof()); return std::pair>(block_name, parameters); } @@ -151,18 +154,18 @@ using TBlockCreateMethod = std::unique_ptr(*)(const std::vector& std::map block_factory::create_methods_; -bool block_factory::Register(const std::string block_name, const std::string block_description, TBlockCreateMethod funcCreateBlock) +bool block_factory::register_block(const std::string block_name, const std::string block_description, TBlockCreateMethod funcCreateBlock) { if (auto it = create_methods_.find(block_name); it == create_methods_.end()) { create_methods_[block_name] = block_meta(block_description, funcCreateBlock); - //std::cout << "LOG: block_factory::Register for block: " + block_name << std::endl; + //std::cout << "LOG: block_factory::register_block for block: " + block_name << std::endl; return true; } return false; } -std::unique_ptr block_factory::Create(const std::string& block_name, const std::vector& parameters) +std::unique_ptr block_factory::create(const std::string& block_name, const std::vector& parameters) { if (auto it = create_methods_.find(block_name); it != create_methods_.end()) { @@ -172,22 +175,22 @@ std::unique_ptr block_factory::Create(const std::string& block_name, cons return nullptr; } -std::vector block_factory::GetBlockTypes() +std::vector block_factory::get_block_infos() { //Vector of int to store values std::vector temp_vector; temp_vector.reserve(create_methods_.size()); //First solution: Copy all value fields from map to a vector using transform() & Lambda function - //std::transform(create_methods_.begin(), create_methods_.end(), std::back_inserter(temp_vector), [](std::pair const &m) - //{ - // return m.first + " : " + m.second.Getdescription() + "\n"; - //}); + std::transform(create_methods_.begin(), create_methods_.end(), std::back_inserter(temp_vector), [](std::pair const &m) + { + return m.first + "\t" + m.second.Getdescription() + "\n"; + }); //Second solution: Copy all value fields from map to a vector using Lambda function - std::for_each(create_methods_.begin(), create_methods_.end(), [&](std::pair const &m) { - temp_vector.push_back(m.first + "\t" + m.second.Getdescription() + "\n"); - }); + //std::for_each(create_methods_.begin(), create_methods_.end(), [&](std::pair const &m) { + // temp_vector.push_back(m.first + "\t" + m.second.Getdescription() + "\n"); + //}); return temp_vector; } diff --git a/final/tool/block.h b/final/tool/block.h index 5708d18..a40cd19 100644 --- a/final/tool/block.h +++ b/final/tool/block.h @@ -22,11 +22,12 @@ class block_meta { public: using TBlockCreateMethod = std::unique_ptr(*)(const std::vector& parameter); - block_meta(std::string description = "", TBlockCreateMethod block_create_method = nullptr) - { - description_ = description; - block_create_method_ = block_create_method; - } + explicit block_meta(std::string description = "", TBlockCreateMethod block_create_method = nullptr) + : description_{ std::move(description) }, block_create_method_{ block_create_method } { } + //{ + // description_ = description; + // block_create_method_ = block_create_method; + //} TBlockCreateMethod GetCreateMethod() const { return block_create_method_; @@ -38,7 +39,7 @@ class block_meta private: std::string description_; - TBlockCreateMethod block_create_method_; + TBlockCreateMethod block_create_method_{}; }; class block_processor @@ -47,10 +48,10 @@ class block_processor std::vector> blocks_; public: block_processor(); - void add_block(std::unique_ptr &b); + void add_block(std::unique_ptr b); double calc_all(double input) const; using sequence_list = std::vector>>; - void load(sequence_list sl); + void load(const sequence_list& sl); bool load_from_stream(std::istream &is); bool remove_block(unsigned int index); std::vector get_sequence(); @@ -67,9 +68,9 @@ class block_factory final block_factory() = delete; //all members are static //wrap static methods in this separate class because I want to have it in one place and 'create_methods_' has to be hidden - static bool Register(const std::string block_name, const std::string block_description, TBlockCreateMethod funcCreateBlock); - static std::unique_ptr Create(const std::string& block_name, const std::vector& parameters); - static std::vector GetBlockTypes(); + static bool register_block(const std::string block_name, const std::string block_description, TBlockCreateMethod funcCreateBlock); + static std::unique_ptr create(const std::string& block_name, const std::vector& parameters); + static std::vector get_block_infos(); private: static std::map create_methods_; }; \ No newline at end of file diff --git a/final/tool/block_add.cpp b/final/tool/block_add.cpp index f9aef36..3953759 100644 --- a/final/tool/block_add.cpp +++ b/final/tool/block_add.cpp @@ -4,16 +4,16 @@ #include "self_registered_in_factory.h" #pragma region add -add::add(const std::vector& parameters) : block(GetFactoryName(), 1, parameters) +add::add(const std::vector& parameters) : block(GetBlockTypeName(), 1, parameters) { initial_value_ = parameters[0]; } double add::calc(double input) { + //we have to have line below in order to trigger registration during class instantiation (before main) is_blocktype_registered_; return initial_value_ + input; }; -std::string self_registered_in_factory::description_ = "Summarize input with parameter value. Example: add 5"; #pragma endregion add \ No newline at end of file diff --git a/final/tool/block_add.h b/final/tool/block_add.h index e19b10f..dbdeb5a 100644 --- a/final/tool/block_add.h +++ b/final/tool/block_add.h @@ -10,7 +10,7 @@ class add : public self_registered_in_factory { private: - double initial_value_; + double initial_value_{}; public: add(const std::vector& parameters); double calc(double input) override; @@ -18,5 +18,7 @@ class add : static std::unique_ptr CreateMethod(const std::vector& parameters) { return std::make_unique(parameters); } - static std::string GetFactoryName() { return "add"; } + + inline static const char* name = "add"; + inline static const char* description = "Summarize input with parameter value. Example: add 5"; }; diff --git a/final/tool/block_lim.cpp b/final/tool/block_lim.cpp index b1bb467..ac8ae7f 100644 --- a/final/tool/block_lim.cpp +++ b/final/tool/block_lim.cpp @@ -2,8 +2,9 @@ #include "block_lim.h" #include "block.h" #include "self_registered_in_factory.h" +#include -lim::lim(const std::vector& parameters) : block(GetFactoryName(), 2, parameters) +lim::lim(const std::vector& parameters) : block(GetBlockTypeName(), 2, parameters) { initial_value_1_ = parameters[0]; initial_value_2_ = parameters[1]; @@ -12,13 +13,11 @@ lim::lim(const std::vector& parameters) : block(GetFactoryName(), 2, par double lim::calc(double input) { is_blocktype_registered_; - double ret = input; - if (input < initial_value_1_) - ret = initial_value_1_; + double ret = std::clamp(input, initial_value_1_, initial_value_2_); + //if (input < initial_value_1_) + // ret = initial_value_1_; - if (input > initial_value_2_) - ret = initial_value_2_; + //if (input > initial_value_2_) + // ret = initial_value_2_; return ret; } - -std::string self_registered_in_factory::description_ = "Limit between numbers. Example: lim 0 100"; diff --git a/final/tool/block_lim.h b/final/tool/block_lim.h index c6852b2..a2a1fdf 100644 --- a/final/tool/block_lim.h +++ b/final/tool/block_lim.h @@ -15,5 +15,7 @@ class lim : public block, public self_registered_in_factory static std::unique_ptr CreateMethod(const std::vector& parameters) { return std::make_unique(parameters); } - static std::string GetFactoryName() { return "lim"; } + + inline static const char* name = "lim"; + inline static const char* description = "Limit between numbers. Example: lim 0 100"; }; diff --git a/final/tool/block_mult.cpp b/final/tool/block_mult.cpp index 329fe38..ac1844c 100644 --- a/final/tool/block_mult.cpp +++ b/final/tool/block_mult.cpp @@ -4,7 +4,7 @@ #include "self_registered_in_factory.h" #pragma region mult -mult::mult(const std::vector& parameters) : block(GetFactoryName(), 1, parameters) +mult::mult(const std::vector& parameters) : block(GetBlockTypeName(), 1, parameters) { initial_value_ = parameters[0]; } @@ -13,6 +13,4 @@ double mult::calc(double input) is_blocktype_registered_; return initial_value_ * input; } -std::string self_registered_in_factory::description_ = "Multiply input value with parameter value. Example: mult 5"; - #pragma endregion mult diff --git a/final/tool/block_mult.h b/final/tool/block_mult.h index 3cd9de6..62b1891 100644 --- a/final/tool/block_mult.h +++ b/final/tool/block_mult.h @@ -6,7 +6,7 @@ class mult : public block, public self_registered_in_factory { private: - double initial_value_; + double initial_value_{}; public: mult(const std::vector& parameters); double calc(double input) override; @@ -14,5 +14,7 @@ class mult : public block, public self_registered_in_factory static std::unique_ptr CreateMethod(const std::vector& parameters) { return std::make_unique(parameters); } - static std::string GetFactoryName() { return "mult"; } + + inline static const char* name = "mult"; + inline static const char* description = "Multiply input value with parameter value. Example: mult 5"; }; \ No newline at end of file diff --git a/final/tool/block_neg.cpp b/final/tool/block_neg.cpp index 0d02ed7..b76f259 100644 --- a/final/tool/block_neg.cpp +++ b/final/tool/block_neg.cpp @@ -1,10 +1,9 @@ #include "stdafx.h" #include "block_neg.h" #include "block.h" -//#include #include "self_registered_in_factory.h" -neg::neg(const std::vector& parameters) : block(GetFactoryName(), 0, parameters) +neg::neg(const std::vector& parameters) : block(GetBlockTypeName(), 0, parameters) { } @@ -13,5 +12,3 @@ double neg::calc(double input) is_blocktype_registered_; return -1 * input; } - -std::string self_registered_in_factory::description_ = "Negation of input value. Example: neg"; diff --git a/final/tool/block_neg.h b/final/tool/block_neg.h index 996cf61..cd48c5d 100644 --- a/final/tool/block_neg.h +++ b/final/tool/block_neg.h @@ -5,8 +5,6 @@ class neg : public block, public self_registered_in_factory { -private: - //double initial_value_; public: neg(const std::vector& parameters); double calc(double input) override; @@ -14,5 +12,7 @@ class neg : public block, public self_registered_in_factory static std::unique_ptr CreateMethod(const std::vector& parameters) { return std::make_unique(parameters); } - static std::string GetFactoryName() { return "neg"; } + + inline static const char* name = "neg"; + inline static const char* description = "Negation of input value. Example: neg"; }; diff --git a/final/tool/block_queq.cpp b/final/tool/block_queq.cpp index f4904cf..1ac0d7a 100644 --- a/final/tool/block_queq.cpp +++ b/final/tool/block_queq.cpp @@ -3,7 +3,7 @@ #include "block.h" #include "self_registered_in_factory.h" -queq::queq(const std::vector& parameters) : block(GetFactoryName(), 3, parameters) +queq::queq(const std::vector& parameters) : block(GetBlockTypeName(), 3, parameters) { initial_value_a_ = parameters[0]; initial_value_b_ = parameters[1]; @@ -16,4 +16,4 @@ double queq::calc(double input) return initial_value_a_ * input * input + initial_value_b_ * input + initial_value_c_; } -std::string self_registered_in_factory::description_ = "Quadratic equation (ax^2 + bx + c). Example: queq 1 2 3"; +//std::string self_registered_in_factory::description_ = "Quadratic equation (ax^2 + bx + c). Example: queq 1 2 3"; diff --git a/final/tool/block_queq.h b/final/tool/block_queq.h index 7e355dd..6b9a545 100644 --- a/final/tool/block_queq.h +++ b/final/tool/block_queq.h @@ -16,5 +16,7 @@ class queq : public block, public self_registered_in_factory static std::unique_ptr CreateMethod(const std::vector& parameters) { return std::make_unique(parameters); } - static std::string GetFactoryName() { return "queq"; } + + inline static const char* name = "queq"; + inline static const char* description = "Quadratic equation (ax^2 + bx + c). Example: queq 1 2 3"; }; diff --git a/final/tool/menus.cpp b/final/tool/menus.cpp index 02de8cc..8168db2 100644 --- a/final/tool/menus.cpp +++ b/final/tool/menus.cpp @@ -10,6 +10,7 @@ #include #include "menus.h" #include +#include void press_enter_to_go_back() { @@ -27,6 +28,11 @@ unsigned int get_number_from_console(std::string caption) getline(std::cin, input); // This code converts from string to number safely. std::stringstream input_ss(input); + //instead, we could use: + //https://en.cppreference.com/w/cpp/string/basic_string/stol + //https://en.cppreference.com/w/cpp/string/basic_string/stof + //but in that case we would have to catch exception. + //with current code we have 'try' approach if (input_ss >> user_number) break; std::cout << "Invalid number, please try again..." << std::endl; @@ -63,9 +69,11 @@ void show_available_block_types() //show available block types system("cls"); std::string block_list_menu = "Available block types:\n"; - std::vector block_list = block_factory::GetBlockTypes(); - for (std::vector::iterator it = block_list.begin(); it != block_list.end(); ++it) - block_list_menu += *it; + std::vector block_list = block_factory::get_block_infos(); + //for (std::vector::iterator it = block_list.begin(); it != block_list.end(); ++it) + // block_list_menu += *it; + block_list_menu = std::accumulate(block_list.begin(), block_list.end(), block_list_menu); + std::cout << block_list_menu << std::endl; } @@ -74,13 +82,17 @@ void show_block_sequence(block_processor &bp) //show block sequence system("cls"); std::cout << "Block sequence:\n" << std::endl; - int index = 0; + std::vector temp_v = bp.get_sequence(); - std::for_each(temp_v.begin(), temp_v.end(), [&](std::string const &c) - { - index++; - std::cout << index << ":\t" << c << std::endl; - }); + std::for_each(temp_v.begin(), temp_v.end(), [index = 0](std::string const &c) mutable { index++; std::cout << index << ":\t" << c << std::endl; }); + + //int index = 0; + //std::vector temp_v = bp.get_sequence(); + //std::for_each(temp_v.begin(), temp_v.end(), [&](std::string const &c) + //{ + // index++; + // std::cout << index << ":\t" << c << std::endl; + //}); } bool add_new_block(block_processor &bp) @@ -97,9 +109,9 @@ bool add_new_block(block_processor &bp) try { std::pair> p = block_processor::get_block_command_from_text(input); - std::unique_ptr bl = block_factory::Create(p.first, p.second); + std::unique_ptr bl = block_factory::create(p.first, p.second); if (bl == nullptr) return false; - bp.add_block(bl); + bp.add_block(std::move(bl)); return true; } catch (...) @@ -137,7 +149,7 @@ void load_from_file(block_processor &bp, std::string &file_name) if (!parameters_stream.is_open()) { - std::cout << "ERROR: Block sequence cannot be loaded from '" << file_name << "' file.\n" << std::endl; + std::cout << "INFO: Block sequence cannot be loaded from '" << file_name << "' file.\n" << std::endl; return; } try diff --git a/final/tool/self_registered_in_factory.h b/final/tool/self_registered_in_factory.h index 9475cc3..b6176d6 100644 --- a/final/tool/self_registered_in_factory.h +++ b/final/tool/self_registered_in_factory.h @@ -12,15 +12,12 @@ class self_registered_in_factory //because it is static member, it will be initialized in the moment when template is compiled //template is compiled when concrete class (which is derived from SelfRegisteredInFactory class) is instantiated (before main method) static bool is_blocktype_registered_; - static std::string description_; public: - static std::string GetDescription() - { - return description_; - }; + static std::string GetBlockTypeName() { return TBlock::name; } + static std::string GetDescription() { return TBlock::description; } }; //this is implementation of assignment of static variable is_blocktype_registered_ which is triggered before 'main'. template bool self_registered_in_factory::is_blocktype_registered_ = -block_factory::Register(TBlock::GetFactoryName(), TBlock::GetDescription(), TBlock::CreateMethod); +block_factory::register_block(TBlock::GetBlockTypeName(), TBlock::GetDescription(), TBlock::CreateMethod); diff --git a/final/tool/tool.cpp b/final/tool/tool.cpp index 719732a549f7ec3707ee7e6fb1130d15b1774758..e2d9888a758afd126d5717e7a3f9557f8d216580 100644 GIT binary patch delta 44 ycmZ4C`^R9z9?{7Pyj=VS3|0*KK)1@0%9#}PB!AL4!*jO*k331*Tq=k+}8V~p?Peuhu}-f^G0Pr${d|K$B= zo?;)f&fI~Y*#ear?t56{J!TxamLHvt+y+gKonl_kjog#kaa?`5kur)>4jSm4E=lch z$#El(S3UplAYmVp67#xIEw>L8#~Amv5w#YOqE^qqo1v?E*#`ycFoXO#R_q2;Px0*# z9E?GMcSEo1wwLh$R`h5tt)Q2=yTzC;)}w}U_cGkmoF{OM$CwG5=L-NVr zx>#xGsgo*o#F06yB<63!JuWQl9mWhXBJ-&9{Mah)o4Mh7`{0s%bKUim(=>JpjA`g zBQktj#4mLuy60#{nC2OatgPO~y1_bi77?zU>LNuhbKisO9rw(=a6h_Vps81G*Zt<+ z;C&BIcJbt?`x(FQ&Q~YKDk)K9e0Yl|6W~ZsqHi!ti;wE)A)aK=h3X(fG|AjnfH!F> z-ORlPW%^y_&OIf@1$wM%cji}?b*31{%pkPS=23=EVxDM;8S=>({!PtG1Q_6+5kfg7 z4_8A`)~6k9!II1-NYV7ge0Ape-FDmfrR9FDL@t}l&uB?ZnJe#MbcR`+w>poy-aSO$ zKj0Zg%eZ6QJ3!nr?k@{uay%z5ij???Xsua7$?-kNee5My>sT)tRWAB4^I22mmbkpC z4m6fm=a4SE!EnDG4#E}Gm&C%R@N_i|{^BiMTmPYvsUxbkazPht(oIAj$hXE8Qpi10n=RWfr(lb+QouvaTM z@X=yf8^4jpI1h4x87FXjiS4!ii|?n5k4Dw+W} zCB2@{f7KNBJ%t|fqxm(dTTJCQL+OhZQ3??u&SB+RkBiMp>$Nr{nyd*LUjhwU1>-Pl z3hoeT8^(VqV{CcE)oKpTXSK-rp$(|Cu??&KEpnBkUCZasy@VgyE!SecW{zcCi8W8! z-~Bz<4Z_2LagBaD70PHuCk*Q>FazqwQEKtr5IXsm>bK{KrKDB#+%pvH*;iOLHj8x zWzMB9bZ9b3b}@9A%Aeu|`;L*LApVI~y~X9Rdm2xy3b{t! zyW@LF4~yzi;|ufE3>GwnkJ+A5YG96`-2d;MsY)sNit>s-H2aQ@(>hl7!&d*+MGcMc z)*)@~JZwN`$|E_8`Q#Hhi3!J4kKXzHUW0>|D;TE z`etX+CJQOW`CF_@=e3uRi}}Oyjv=Pfg?Mp2TDu`%_P;7h8hve6+%H!DiE*JuhCp^Z zSS>_K^PX5rc$&S9dY>mTyhtYY0ojF8}m{20<$l!vy2_HlIMRHa&N;iudyZ5=GeQjF}9l8 zh1GYf?a&Q3R`>CF{W2%yY3#5{=R@J`g8kxZOr=p>iB*lSIjRZ1{69ijtN&GGGM_7Z zs&qPc3j0laRpp(U>X*A|IdN4T$T^Gpxjwb1ULOa!{}fuWm8Ts*InR~~RK2xaC0egu zV~YN#DEvoB94ic=FuFWJ&SBP(T)+yC-b+MSJuzY|;