From 7c3400f2734410477efa4a4d8ffafd1c22e0496a Mon Sep 17 00:00:00 2001 From: WickedJack99 Date: Sat, 6 Dec 2025 21:59:19 +0100 Subject: [PATCH] Added all exercises worked on. --- a1_ln2_openmp/.DS_Store | Bin 0 -> 6148 bytes .../a1_ln2_openmp.cpp | 0 ln2 => a1_ln2_openmp/ln2 | Bin makefile => a1_ln2_openmp/makefile | 0 test.h => a1_ln2_openmp/test.h | 0 a2_ln2_mpi/.DS_Store | Bin 0 -> 6148 bytes a2_ln2_mpi/a2_ln2_mpi.cpp | 62 ++++ a2_ln2_mpi/ln2 | Bin 0 -> 133704 bytes a2_ln2_mpi/makefile | 5 + a2_ln2_mpi/test.h | 290 ++++++++++++++++ a3_ln2_hybrid/a3_ln2_hybrid.cpp | 69 ++++ a3_ln2_hybrid/ln2 | Bin 0 -> 134336 bytes a3_ln2_hybrid/makefile | 5 + a3_ln2_hybrid/test.h | 290 ++++++++++++++++ a4_minimum_search/.DS_Store | Bin 0 -> 6148 bytes a4_minimum_search/a4_min.cpp | 73 ++++ a4_minimum_search/makefile | 5 + a4_minimum_search/min | Bin 0 -> 31456 bytes a4_minimum_search/test.h | 290 ++++++++++++++++ a5_mpi_deadlock/.DS_Store | Bin 0 -> 6148 bytes a5_mpi_deadlock/a5_deadlock.cpp | 49 +++ a5_mpi_deadlock/deadlock | Bin 0 -> 129144 bytes a5_mpi_deadlock/makefile | 5 + a5_mpi_deadlock/test.h | 290 ++++++++++++++++ a6_send_column/.DS_Store | Bin 0 -> 6148 bytes a6_send_column/a6_send_column.cpp | 98 ++++++ a6_send_column/makefile | 5 + a6_send_column/matrix.h | 324 ++++++++++++++++++ a6_send_column/send-column | Bin 0 -> 142568 bytes a6_send_column/test.h | 290 ++++++++++++++++ 30 files changed, 2150 insertions(+) create mode 100644 a1_ln2_openmp/.DS_Store rename a1_ln2_openmp.cpp => a1_ln2_openmp/a1_ln2_openmp.cpp (100%) rename ln2 => a1_ln2_openmp/ln2 (100%) rename makefile => a1_ln2_openmp/makefile (100%) rename test.h => a1_ln2_openmp/test.h (100%) create mode 100644 a2_ln2_mpi/.DS_Store create mode 100644 a2_ln2_mpi/a2_ln2_mpi.cpp create mode 100644 a2_ln2_mpi/ln2 create mode 100644 a2_ln2_mpi/makefile create mode 100644 a2_ln2_mpi/test.h create mode 100644 a3_ln2_hybrid/a3_ln2_hybrid.cpp create mode 100644 a3_ln2_hybrid/ln2 create mode 100644 a3_ln2_hybrid/makefile create mode 100644 a3_ln2_hybrid/test.h create mode 100644 a4_minimum_search/.DS_Store create mode 100644 a4_minimum_search/a4_min.cpp create mode 100644 a4_minimum_search/makefile create mode 100644 a4_minimum_search/min create mode 100644 a4_minimum_search/test.h create mode 100644 a5_mpi_deadlock/.DS_Store create mode 100644 a5_mpi_deadlock/a5_deadlock.cpp create mode 100644 a5_mpi_deadlock/deadlock create mode 100644 a5_mpi_deadlock/makefile create mode 100644 a5_mpi_deadlock/test.h create mode 100644 a6_send_column/.DS_Store create mode 100644 a6_send_column/a6_send_column.cpp create mode 100644 a6_send_column/makefile create mode 100644 a6_send_column/matrix.h create mode 100644 a6_send_column/send-column create mode 100644 a6_send_column/test.h diff --git a/a1_ln2_openmp/.DS_Store b/a1_ln2_openmp/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 +#include +#include "test.h" + +void runningWithMPITest(); +void computeLn2Test(); + +// **************************************************** +// TODO: parallelize this function with MPI +// **************************************************** + +double ln2(int numTerms) { + int rank, numProcs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &numProcs); + + int start = 1 + (numTerms / numProcs) * rank; + int end = (numTerms / numProcs) * (rank + 1); + + double localSum = 0; + + for (int i = start; i < end + 1; ++i) { + int sign = 1; + if (i % 2 == 0) { + sign = -1; + } + localSum += sign / static_cast(i); + } + + double globalSum; + MPI_Allreduce(&localSum, &globalSum, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + + return globalSum; +} + +int main(int argc, char* argv[]) { + MPI_Init(&argc, &argv); + runningWithMPITest(); + computeLn2Test(); + MPI_Finalize(); +} + +// Do not change anything below this line! +// You may use the tests below to verify that your +// program works correctly. +// **************************************************** + +// Verify that the program actually runs with MPI +void runningWithMPITest() { + int numProc; + MPI_Comm_size(MPI_COMM_WORLD, &numProc); + check(numProc, 4); +} + +// Verify correct functionality of ln2() +void computeLn2Test() { + check(ln2(4), 0.5833333333333333); + check(ln2(8), 0.6345238095238095); + check(ln2(12), 0.6532106782106781); + check(ln2(100), 0.688172179310195); + check(ln2(1e6), 0.6931466805602525); +} diff --git a/a2_ln2_mpi/ln2 b/a2_ln2_mpi/ln2 new file mode 100644 index 0000000000000000000000000000000000000000..e82b1112cd78850e3c6378ab38c9cdb573a94b53 GIT binary patch literal 133704 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%0A=-V2;2pzzE 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

#(^l&LZkhSha+9| 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 ZE*j>msC@slRQkOf5p%VmOyE7n{s-EEwMPH| literal 0 HcmV?d00001 diff --git a/a2_ln2_mpi/makefile b/a2_ln2_mpi/makefile new file mode 100644 index 0000000..0b82dc5 --- /dev/null +++ b/a2_ln2_mpi/makefile @@ -0,0 +1,5 @@ +all: a2_ln2_mpi.cpp + mpicxx a2_ln2_mpi.cpp -std=c++17 -o ln2 + +run: all + mpirun -np 4 ./ln2 \ No newline at end of file diff --git a/a2_ln2_mpi/test.h b/a2_ln2_mpi/test.h new file mode 100644 index 0000000..232be71 --- /dev/null +++ b/a2_ln2_mpi/test.h @@ -0,0 +1,290 @@ +/** test.h, an extremly simple test framework. + * Version 1.4 + * Copyright (C) 2022-2023 Tobias Kreilos, Offenburg University of Applied + * Sciences + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * + * The framework defines a function check(a,b) that can be called with + * parameters of different types. The function asserts + * that the two paramters are equal (within a certain, predefined range for + * floating point numbers) and prints the result of the comparison on the + * command line. Additionally a summary of all tests is printed at the end of + * the program. + * Additionally there is TEST macro, which you can place outside main to group + * tests together. Code in the macro is automatically executed at the beginning + * of the program. + * The file also defines a class InstanceCount, that can be used to + * count how many instances of an object are still alive at the end of a + * program. To use it, derive your class from InstanceCount and the + * message is automatically printed at the end of the program. + */ + +#ifndef VERY_SIMPLE_TEST_H + #define VERY_SIMPLE_TEST_H + + #include + #include + #include + #include + + /** Simple macro to execute the code that follows the macro (without call from + * main) + * + * Define a class, that is directly instantiated + * and contains the test code in the constructor. + * + * Usage: + * TEST(MyTest) + * { + * // test code + * } + */ + #define TEST(name) \ + struct _TestClass##name { \ + _TestClass##name(); \ + } _TestClass##name##Instance; \ + _TestClass##name::_TestClass##name() + +// Use a namespace to hide implementation details +namespace Test::Detail { +/** + * Make it possible to print the underlying value of class enums with ostream + * + * The expression typename std::enable_if::value, + * std::ostream>::type decays to ostream if the type T is an enum. Otherwise, + * the function is not generated. + */ +template +std::ostream& operator<<( + typename std::enable_if::value, std::ostream>::type& stream, + const T& e) { + return stream << static_cast::type>(e); +} + +/** + * Convert anything to a string. + */ +template +std::string toString(const T& t) { + std::ostringstream ss; + ss << t; + return "\"" + ss.str() + "\""; +} + +/** + * Convert bools to string "true" or "false" instead of 0 and 1 + */ +template <> +inline std::string toString(const bool& b) { + return b ? "\"true\"" : "\"false\""; +} + +/** + * Comparison function for different types + */ +template +bool isEqual(const T& t1, const T& t2) { + return t1 == t2; +} + +/** + * Double values are equal if they differ no more than 1e-12 + */ +template <> +inline bool isEqual(const double& expectedValue, + const double& actualValue) { + const double epsilon = 1e-12; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * Float values are equal if they differ no more than 1e-6 + */ +template <> +inline bool isEqual(const float& expectedValue, + const float& actualValue) { + const double epsilon = 1e-6; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * This class realizes some basics of the test framework. + * Test summary is printed in the destructor. + * Apart from that, the class implements counting of total and failed tests, + * comparison of floating point numbers within sensible boundaries and prints + * the result of each test on the command line. + */ +class Test { + public: + /** + * Test class is a Singleton + */ + static Test& instance() { + static Test test; + return test; + } + + /** + * the main entry point for tests. Test two values for equality and output the + * result. + */ + template + bool check(const T& expectedValue, const T& actualValue) { + bool testResult = isEqual(expectedValue, actualValue); + if (testResult == true) { + registerPassingTest(); + #pragma omp critical + std::cout << "Test successful! Expected value == actual value (=" + << toString(expectedValue) << ")" << std::endl; + } else { + registerFailingTest(); + #pragma omp critical + std::cout << "Error in test: expected value " << toString(expectedValue) + << ", but actual value was " << toString(actualValue) + << std::endl; + } + + return testResult; + } + + private: + /** + * On destruction, print a summary of all tests. + */ + ~Test() { + std::cout << "\n--------------------------------------" << std::endl; + std::cout << "Test summary:" << std::endl; + std::cout << "Executed tests: " << numTests_ << std::endl; + std::cout << "Failed tests: " << numFailedTests_ << std::endl; + } + + void registerPassingTest() { numTests_++; } + + void registerFailingTest() { + numTests_++; + numFailedTests_++; + } + + /** + * For statistics + */ + std::atomic numTests_ = 0; + + /** + * For statistics + */ + std::atomic numFailedTests_ = 0; +}; + +template +class InstanceCounterHelper { + public: + ~InstanceCounterHelper() { + std::cout << "The remaining number of objects of type " << typeid(T).name() + << " at the end of the program is " << count; + if (count > 0) + std::cout << " (NOT zero!)"; + std::cout << "\nThe total number of objects created was " << total + << std::endl; + } + + void increment() { + count++; + total++; + } + + void decrement() { count--; } + + private: + std::atomic count = 0; + std::atomic total = 0; +}; + +} // namespace Test::Detail + +/** + * Count the instances of a class T. + * Result gets printed automatically at the end of the program. + * To use it, inherit T from InstanceCounter, e.g. + * class MyClass : InstanceCounter + */ +template +class InstanceCounter { + public: + InstanceCounter() { counter().increment(); } + + InstanceCounter(const InstanceCounter&) { counter().increment(); } + + InstanceCounter(const InstanceCounter&&) { counter().increment(); } + + virtual ~InstanceCounter() { counter().decrement(); } + + Test::Detail::InstanceCounterHelper& counter() { + static Test::Detail::InstanceCounterHelper c; + return c; + } +}; + +/** + * Check if the expected value is equal to the actual value. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +template +void check(const T1& actualValue, const T2& expectedValue) { + const T1& expectedValueCasted{ + expectedValue}; // allows conversion in general, but avoids narrowing + // conversion + Test::Detail::Test::instance().check(expectedValueCasted, actualValue); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const int& actualValue, const double& expectedValue) { + Test::Detail::Test::instance().check(expectedValue, + static_cast(actualValue)); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const double& actualValue, const int& expectedValue) { + Test::Detail::Test::instance().check(static_cast(expectedValue), + actualValue); +} + +/** + * Check if the entered value is true. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +inline void check(bool a) { + Test::Detail::Test::instance().check(true, a); +} + +#endif // VERY_SIMPLE_TEST_H + +/** + * V1.0: Creation of franework + * V1.1: make check(bool) inline, automatically convert expected value type to + * actual value type + * V1.2: added possibilty to count constructions and destructions of some type + * V1.3: tweaks on check for int and double types + * V1.4: Adding thread safety in OpenMP programs (not general thread safety, as + * OpenMP and std::thread might not play along) + */ \ No newline at end of file diff --git a/a3_ln2_hybrid/a3_ln2_hybrid.cpp b/a3_ln2_hybrid/a3_ln2_hybrid.cpp new file mode 100644 index 0000000..d6fc076 --- /dev/null +++ b/a3_ln2_hybrid/a3_ln2_hybrid.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include "test.h" + +void runningHybridTest(); +void computeLn2Test(); + +// **************************************************** +// TODO: parallelize this function with MPI and OpenMP +// **************************************************** + +double ln2(int numTerms) { + int rank, numProcs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &numProcs); + + int start = 1 + (numTerms / numProcs) * rank; + int end = (numTerms / numProcs) * (rank + 1); + + double localSum = 0; + + #pragma omp parallel for reduction(+ : localSum) + for (int i = start; i < end + 1; ++i) { + int sign = 1; + if (i % 2 == 0) { + sign = -1; + } + localSum += sign / static_cast(i); + } + + double globalSum; + MPI_Allreduce(&localSum, &globalSum, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + + return globalSum; +} + +int main(int argc, char* argv[]) { + MPI_Init(&argc, &argv); + runningHybridTest(); + computeLn2Test(); + MPI_Finalize(); +} + +// Do not change anything below this line! +// You may use the tests below to verify that your +// program works correctly. +// **************************************************** + +// Verify that the program actually runs with MPI and OpenMP +void runningHybridTest() { + int numProc; + MPI_Comm_size(MPI_COMM_WORLD, &numProc); +#pragma omp parallel + { + int numThreads = omp_get_num_threads(); + check(numProc, 2); + check(numThreads, 2); + } +} + +// Verify correct functionality of ln2() +void computeLn2Test() { + check(ln2(4), 0.5833333333333333); + check(ln2(8), 0.6345238095238095); + check(ln2(12), 0.6532106782106781); + check(ln2(100), 0.688172179310195); + check(ln2(1e6), 0.6931466805602525); +} diff --git a/a3_ln2_hybrid/ln2 b/a3_ln2_hybrid/ln2 new file mode 100644 index 0000000000000000000000000000000000000000..02064986d614a48fb84d88d5a2e63f5c50dcf445 GIT binary patch literal 134336 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. + * + * + * + * The framework defines a function check(a,b) that can be called with + * parameters of different types. The function asserts + * that the two paramters are equal (within a certain, predefined range for + * floating point numbers) and prints the result of the comparison on the + * command line. Additionally a summary of all tests is printed at the end of + * the program. + * Additionally there is TEST macro, which you can place outside main to group + * tests together. Code in the macro is automatically executed at the beginning + * of the program. + * The file also defines a class InstanceCount, that can be used to + * count how many instances of an object are still alive at the end of a + * program. To use it, derive your class from InstanceCount and the + * message is automatically printed at the end of the program. + */ + +#ifndef VERY_SIMPLE_TEST_H + #define VERY_SIMPLE_TEST_H + + #include + #include + #include + #include + + /** Simple macro to execute the code that follows the macro (without call from + * main) + * + * Define a class, that is directly instantiated + * and contains the test code in the constructor. + * + * Usage: + * TEST(MyTest) + * { + * // test code + * } + */ + #define TEST(name) \ + struct _TestClass##name { \ + _TestClass##name(); \ + } _TestClass##name##Instance; \ + _TestClass##name::_TestClass##name() + +// Use a namespace to hide implementation details +namespace Test::Detail { +/** + * Make it possible to print the underlying value of class enums with ostream + * + * The expression typename std::enable_if::value, + * std::ostream>::type decays to ostream if the type T is an enum. Otherwise, + * the function is not generated. + */ +template +std::ostream& operator<<( + typename std::enable_if::value, std::ostream>::type& stream, + const T& e) { + return stream << static_cast::type>(e); +} + +/** + * Convert anything to a string. + */ +template +std::string toString(const T& t) { + std::ostringstream ss; + ss << t; + return "\"" + ss.str() + "\""; +} + +/** + * Convert bools to string "true" or "false" instead of 0 and 1 + */ +template <> +inline std::string toString(const bool& b) { + return b ? "\"true\"" : "\"false\""; +} + +/** + * Comparison function for different types + */ +template +bool isEqual(const T& t1, const T& t2) { + return t1 == t2; +} + +/** + * Double values are equal if they differ no more than 1e-12 + */ +template <> +inline bool isEqual(const double& expectedValue, + const double& actualValue) { + const double epsilon = 1e-12; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * Float values are equal if they differ no more than 1e-6 + */ +template <> +inline bool isEqual(const float& expectedValue, + const float& actualValue) { + const double epsilon = 1e-6; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * This class realizes some basics of the test framework. + * Test summary is printed in the destructor. + * Apart from that, the class implements counting of total and failed tests, + * comparison of floating point numbers within sensible boundaries and prints + * the result of each test on the command line. + */ +class Test { + public: + /** + * Test class is a Singleton + */ + static Test& instance() { + static Test test; + return test; + } + + /** + * the main entry point for tests. Test two values for equality and output the + * result. + */ + template + bool check(const T& expectedValue, const T& actualValue) { + bool testResult = isEqual(expectedValue, actualValue); + if (testResult == true) { + registerPassingTest(); + #pragma omp critical + std::cout << "Test successful! Expected value == actual value (=" + << toString(expectedValue) << ")" << std::endl; + } else { + registerFailingTest(); + #pragma omp critical + std::cout << "Error in test: expected value " << toString(expectedValue) + << ", but actual value was " << toString(actualValue) + << std::endl; + } + + return testResult; + } + + private: + /** + * On destruction, print a summary of all tests. + */ + ~Test() { + std::cout << "\n--------------------------------------" << std::endl; + std::cout << "Test summary:" << std::endl; + std::cout << "Executed tests: " << numTests_ << std::endl; + std::cout << "Failed tests: " << numFailedTests_ << std::endl; + } + + void registerPassingTest() { numTests_++; } + + void registerFailingTest() { + numTests_++; + numFailedTests_++; + } + + /** + * For statistics + */ + std::atomic numTests_ = 0; + + /** + * For statistics + */ + std::atomic numFailedTests_ = 0; +}; + +template +class InstanceCounterHelper { + public: + ~InstanceCounterHelper() { + std::cout << "The remaining number of objects of type " << typeid(T).name() + << " at the end of the program is " << count; + if (count > 0) + std::cout << " (NOT zero!)"; + std::cout << "\nThe total number of objects created was " << total + << std::endl; + } + + void increment() { + count++; + total++; + } + + void decrement() { count--; } + + private: + std::atomic count = 0; + std::atomic total = 0; +}; + +} // namespace Test::Detail + +/** + * Count the instances of a class T. + * Result gets printed automatically at the end of the program. + * To use it, inherit T from InstanceCounter, e.g. + * class MyClass : InstanceCounter + */ +template +class InstanceCounter { + public: + InstanceCounter() { counter().increment(); } + + InstanceCounter(const InstanceCounter&) { counter().increment(); } + + InstanceCounter(const InstanceCounter&&) { counter().increment(); } + + virtual ~InstanceCounter() { counter().decrement(); } + + Test::Detail::InstanceCounterHelper& counter() { + static Test::Detail::InstanceCounterHelper c; + return c; + } +}; + +/** + * Check if the expected value is equal to the actual value. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +template +void check(const T1& actualValue, const T2& expectedValue) { + const T1& expectedValueCasted{ + expectedValue}; // allows conversion in general, but avoids narrowing + // conversion + Test::Detail::Test::instance().check(expectedValueCasted, actualValue); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const int& actualValue, const double& expectedValue) { + Test::Detail::Test::instance().check(expectedValue, + static_cast(actualValue)); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const double& actualValue, const int& expectedValue) { + Test::Detail::Test::instance().check(static_cast(expectedValue), + actualValue); +} + +/** + * Check if the entered value is true. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +inline void check(bool a) { + Test::Detail::Test::instance().check(true, a); +} + +#endif // VERY_SIMPLE_TEST_H + +/** + * V1.0: Creation of franework + * V1.1: make check(bool) inline, automatically convert expected value type to + * actual value type + * V1.2: added possibilty to count constructions and destructions of some type + * V1.3: tweaks on check for int and double types + * V1.4: Adding thread safety in OpenMP programs (not general thread safety, as + * OpenMP and std::thread might not play along) + */ \ No newline at end of file diff --git a/a4_minimum_search/.DS_Store b/a4_minimum_search/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 +#include +#include +#include +#include "test.h" + +void findMinimumTest(); + +// **************************************************** +// TODO: find and fix the data race in this function +// **************************************************** +double findMinimum(std::function f, + double xMin, + double xMax, + int n) { + + double globalMin = f(xMax); + double localMin = f(xMax); + +#pragma omp parallel firstprivate(localMin) shared(globalMin) + { + double range = xMax - xMin; + double step = range / n; + +#pragma omp for schedule(static) + for (int i = 0; i < n; ++i) { + double x = xMin + step * i; + double fx = f(x); + if (fx < localMin) { + localMin = fx; + } + } + + #pragma omp critical + if (localMin < globalMin) { + globalMin = localMin; + } + } + + return globalMin; +} + +int main() { + findMinimumTest(); +} + +// Do not change anything below this line! +// You may use the tests below to verify that your +// program works correctly. +// **************************************************** + +double f0(double x) { + return (x - 2) * (x - 2); +} + +double f1(double x) { + return sin(x) + 1. / 5. * x * x * x * x - 3. * (x - 3) * (x - 3); +}; + +double f2(double x) { + return sin(x) + x * x; +} + +double f3(double x) { + return x*x*x*x - x*x; +} + +void findMinimumTest() { + check(findMinimum(f0, -5, 5, 1e6), 0); + check(findMinimum(f1, -5, 5, 1e6), -96.68321722021884); + check(findMinimum(f2, -5, 5, 1e6), -0.2324655751423368); + check(findMinimum(f3, -2, 2, 1e7), -0.25); +} \ No newline at end of file diff --git a/a4_minimum_search/makefile b/a4_minimum_search/makefile new file mode 100644 index 0000000..5bd1b96 --- /dev/null +++ b/a4_minimum_search/makefile @@ -0,0 +1,5 @@ +build: a4_min.cpp + g++ -std=c++17 a4_min.cpp -fopenmp -o min + +run: build + ./min \ No newline at end of file diff --git a/a4_minimum_search/min b/a4_minimum_search/min new file mode 100644 index 0000000000000000000000000000000000000000..eaa3b31aa690cbd4cb3deb4b53eb10b5683aff58 GIT binary patch literal 31456 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. + * + * + * + * The framework defines a function check(a,b) that can be called with + * parameters of different types. The function asserts + * that the two paramters are equal (within a certain, predefined range for + * floating point numbers) and prints the result of the comparison on the + * command line. Additionally a summary of all tests is printed at the end of + * the program. + * Additionally there is TEST macro, which you can place outside main to group + * tests together. Code in the macro is automatically executed at the beginning + * of the program. + * The file also defines a class InstanceCount, that can be used to + * count how many instances of an object are still alive at the end of a + * program. To use it, derive your class from InstanceCount and the + * message is automatically printed at the end of the program. + */ + +#ifndef VERY_SIMPLE_TEST_H + #define VERY_SIMPLE_TEST_H + + #include + #include + #include + #include + + /** Simple macro to execute the code that follows the macro (without call from + * main) + * + * Define a class, that is directly instantiated + * and contains the test code in the constructor. + * + * Usage: + * TEST(MyTest) + * { + * // test code + * } + */ + #define TEST(name) \ + struct _TestClass##name { \ + _TestClass##name(); \ + } _TestClass##name##Instance; \ + _TestClass##name::_TestClass##name() + +// Use a namespace to hide implementation details +namespace Test::Detail { +/** + * Make it possible to print the underlying value of class enums with ostream + * + * The expression typename std::enable_if::value, + * std::ostream>::type decays to ostream if the type T is an enum. Otherwise, + * the function is not generated. + */ +template +std::ostream& operator<<( + typename std::enable_if::value, std::ostream>::type& stream, + const T& e) { + return stream << static_cast::type>(e); +} + +/** + * Convert anything to a string. + */ +template +std::string toString(const T& t) { + std::ostringstream ss; + ss << t; + return "\"" + ss.str() + "\""; +} + +/** + * Convert bools to string "true" or "false" instead of 0 and 1 + */ +template <> +inline std::string toString(const bool& b) { + return b ? "\"true\"" : "\"false\""; +} + +/** + * Comparison function for different types + */ +template +bool isEqual(const T& t1, const T& t2) { + return t1 == t2; +} + +/** + * Double values are equal if they differ no more than 1e-12 + */ +template <> +inline bool isEqual(const double& expectedValue, + const double& actualValue) { + const double epsilon = 1e-12; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * Float values are equal if they differ no more than 1e-6 + */ +template <> +inline bool isEqual(const float& expectedValue, + const float& actualValue) { + const double epsilon = 1e-6; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * This class realizes some basics of the test framework. + * Test summary is printed in the destructor. + * Apart from that, the class implements counting of total and failed tests, + * comparison of floating point numbers within sensible boundaries and prints + * the result of each test on the command line. + */ +class Test { + public: + /** + * Test class is a Singleton + */ + static Test& instance() { + static Test test; + return test; + } + + /** + * the main entry point for tests. Test two values for equality and output the + * result. + */ + template + bool check(const T& expectedValue, const T& actualValue) { + bool testResult = isEqual(expectedValue, actualValue); + if (testResult == true) { + registerPassingTest(); + #pragma omp critical + std::cout << "Test successful! Expected value == actual value (=" + << toString(expectedValue) << ")" << std::endl; + } else { + registerFailingTest(); + #pragma omp critical + std::cout << "Error in test: expected value " << toString(expectedValue) + << ", but actual value was " << toString(actualValue) + << std::endl; + } + + return testResult; + } + + private: + /** + * On destruction, print a summary of all tests. + */ + ~Test() { + std::cout << "\n--------------------------------------" << std::endl; + std::cout << "Test summary:" << std::endl; + std::cout << "Executed tests: " << numTests_ << std::endl; + std::cout << "Failed tests: " << numFailedTests_ << std::endl; + } + + void registerPassingTest() { numTests_++; } + + void registerFailingTest() { + numTests_++; + numFailedTests_++; + } + + /** + * For statistics + */ + std::atomic numTests_ = 0; + + /** + * For statistics + */ + std::atomic numFailedTests_ = 0; +}; + +template +class InstanceCounterHelper { + public: + ~InstanceCounterHelper() { + std::cout << "The remaining number of objects of type " << typeid(T).name() + << " at the end of the program is " << count; + if (count > 0) + std::cout << " (NOT zero!)"; + std::cout << "\nThe total number of objects created was " << total + << std::endl; + } + + void increment() { + count++; + total++; + } + + void decrement() { count--; } + + private: + std::atomic count = 0; + std::atomic total = 0; +}; + +} // namespace Test::Detail + +/** + * Count the instances of a class T. + * Result gets printed automatically at the end of the program. + * To use it, inherit T from InstanceCounter, e.g. + * class MyClass : InstanceCounter + */ +template +class InstanceCounter { + public: + InstanceCounter() { counter().increment(); } + + InstanceCounter(const InstanceCounter&) { counter().increment(); } + + InstanceCounter(const InstanceCounter&&) { counter().increment(); } + + virtual ~InstanceCounter() { counter().decrement(); } + + Test::Detail::InstanceCounterHelper& counter() { + static Test::Detail::InstanceCounterHelper c; + return c; + } +}; + +/** + * Check if the expected value is equal to the actual value. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +template +void check(const T1& actualValue, const T2& expectedValue) { + const T1& expectedValueCasted{ + expectedValue}; // allows conversion in general, but avoids narrowing + // conversion + Test::Detail::Test::instance().check(expectedValueCasted, actualValue); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const int& actualValue, const double& expectedValue) { + Test::Detail::Test::instance().check(expectedValue, + static_cast(actualValue)); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const double& actualValue, const int& expectedValue) { + Test::Detail::Test::instance().check(static_cast(expectedValue), + actualValue); +} + +/** + * Check if the entered value is true. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +inline void check(bool a) { + Test::Detail::Test::instance().check(true, a); +} + +#endif // VERY_SIMPLE_TEST_H + +/** + * V1.0: Creation of franework + * V1.1: make check(bool) inline, automatically convert expected value type to + * actual value type + * V1.2: added possibilty to count constructions and destructions of some type + * V1.3: tweaks on check for int and double types + * V1.4: Adding thread safety in OpenMP programs (not general thread safety, as + * OpenMP and std::thread might not play along) + */ \ No newline at end of file diff --git a/a5_mpi_deadlock/.DS_Store b/a5_mpi_deadlock/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 +#include +#include "test.h" + +int main(int argc, char* argv[]) { + MPI_Init(&argc, &argv); + + int rank, numProc; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &numProc); + + int sendBuf; + int recvBuf; + int val = 0; + MPI_Status status; + + // send and receive message on rank 0 + if (rank == 0) { + sendBuf = 123; + MPI_Recv(&recvBuf, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &status); + std::cout << "Rank 0 received " << recvBuf << "\n"; + + MPI_Send(&sendBuf, 1, MPI_INT, 1, 0, MPI_COMM_WORLD); + std::cout << "Rank 0 sent " << sendBuf << " to rank 1\n"; + + check(recvBuf, 456); + } + + // send and receive message on rank 1 + if (rank == 1) { + sendBuf = 456; + MPI_Send(&sendBuf, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + std::cout << "Rank 1 sent " << sendBuf << " to rank 0\n"; + + MPI_Recv(&recvBuf, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status); + std::cout << "Rank 1 received " << recvBuf << "\n"; + + check(recvBuf, 123); + } + + // Broadcast value from rank 0 to all others + if (rank == 0) val = 789; + MPI_Bcast(&val, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + + // Verify that broadcast message arrived correctly everywhere + check(val, 789); + + MPI_Finalize(); +} \ No newline at end of file diff --git a/a5_mpi_deadlock/deadlock b/a5_mpi_deadlock/deadlock new file mode 100644 index 0000000000000000000000000000000000000000..dd7608d5881c8f2dc08621c6f8aa0c79c8559cf0 GIT binary patch literal 129144 zcmeFadt6mz_CJ2aOK6%W)1tCE*^R6eykc451v}=YlV~w!0U`>9KwJzkIgL0P2P_ zStoU%%`rMbXe{TW2B};ew`|jaoKL8Y zRC|8zfki(*O_vjDmLu6TOx5LV+8U?JB@NP4%8|IJT*bw@oJ|8PPoW}D>LZ;=EKi#T zkVZ#9{p<7D<wqfOx)9Wz0^TvC=VT!pQl*y z2Ox11{tDnc5T`#FH{jrLymvjl%c$9zuay|#&7O6 z$rtPQ^*&*{@A2T}$=hq@b@BUpbo*1UlV6;7@}Rq?WctdJ{pINWpqX82=LTo?it)|O z@n3iFH9^1MUo#{S8}c9fw-G%Pj>}7cxW4b=tN&ZW_*DmNjytqGzQ6C-lTYmCTay!q zp04UPDE68zF=3Jg9h-5!fb&ISa5Un473XU>U&m=5Zvbe*xgF}w9E$JJQQaf&LeOh ziIa|jphxrH#26p(n2Q-)e)+s*ULhyMw^DVp(d=X>COiMs7$61DR z31d=^(51ka8EIGJ?{b{i;9QP#1x`6C@OLH7l{l+#uEHtDjrjXd22c2EgTEPn-(v8T z{%7#FGPVZvFZ}oIpm*`#ly5Emz8B|xIRB2b4(I(i<#+&pKZx@Y1FOg1|1kJR`R~U; zpTzkT^XoyM#<_v{XFxaNd=}^PI2&+o!P$s&8_w5pzJZgDCeSx=zQ60V+iy8>_6tA7 zz4^gqw@m$h$fnxHwU@l|@PH3qIQPTQp!06qk}^5xvFR@_7&T+qLyyIUHeECO*cIhJ zt}EU1OwZ3st|>U+ro#r^^M2c{51iZm(({h0`1|~>4}3jh#g8xT=smac>6eNRx#5|_ zdnTXW{mqZhZkh7Y2gMiXynJBFH$z5zK6>Sx_B}Tz9Cg~dAC~OA<&dGT#Pv?SA71?RX}9 zdw9z^1Nxji{gzUtkFFMId2b;bK0`}WI`|9Qm!=7e*OYS_E6@?W!OUB2e=OD~+|Kkkd!i*HD2 zJ@wO1|B%07=F4Z*jp(;!MABs^6kWP=S4{C;N4=Hx%mY0x{cKhF+*N~bnEel zxBghybp3(9tlMz^_LW^uZs;*_!ebZiDeRv2V)=~c-z`1tg1Zjfyz{b|@A+;X>U-sn z#~t%T_nL3+yZV5GD@)HiXYzf2Z<%=1qP}%+?>l?S=wJ8Vwff>Mdp9mDduz_}lkU3q zgf}m_^p9tLFmuB6r?UHhw_@!R^PA#7UU%M(xoOwk&~w&NF}Z*J&)W~DE;;6&=O5^C z(`8Tn!T;fgM{fCH#nAO-1u=Jaeec9?^Z$C>Ro_kcPwwrTU5L*+O34u9>Q zUl+gi#=Yk~_SCUAXCM9XN3CUteEsoVje9pfJ89V+dloju&-!u2+AB{yFZt6K9_Z1l z=eQgHeSCPwj(@Kh`j6VwMdz-3E2DMK^hbU6uqR&H`Fhef zz5{+qe&dF1uiccp@Pc=*JbY~SnZs|IbmpPU&+mHs^PhZlLet|1o_)bh*FJMx`IHOq zAGvtU2H(fqui1CspAJm>X==}{lX`4<;@hT=S0DVBYwmpIpp#TV(q=R4_$r3eP~mVKavHI4Aa# zqb{n+8L+1Jg!cmj<31i!ef$}xp8dtfi~O(Ml78iR=X^Ca_1^m)yZZ9Y#fNl=kAQ3|LY%~xp4Vi>pr{VPlpVx=-)ea$Hus!qo4or?N|Qzz&{qf zxT4D$rfj{VhKjVQ<84_I%OCX?V@}amd8s6f8f8&97 z$3#Y}=Wq|4VxVaGQ#^2rSEA*oI3*gM?SWH#5iS4q9(auh-hhjZX!U$`N_3oJg=qQ7 zpGU)A^}s*zz`ymt_j=&C(1_aZVIKIO4vn6FkVpOz9{4y9{B#ey5%S2N>wzEaq5qe8 z4DdK;Gbi>xL)jVJnxbJbr1YK4?OIFzvUsHy&m~{c8xyoaUS?d z9{9foN3UmuNB$HKJmi7T_rRBV;43}wn?3Nqdf*Ru;2S*f_dM{IJm%$dkNmA3^8DE& ze{XE2M6)wTc;Kga;AeT@Q#|ksJn&~c*4Ganc)CYDGd=9>d=I?I!_E|Y)W6IFU+sb4 z?SVh)fxqH`@ASaG^T69Z@P62Ui#G2AJ@5f1MbCeNNB-d+_*4)4Vh_B~17GHWS9##K zdElEp@a-OWk%u1k0Uph6zvod;*aN3c>}c~6=Yb#Rfv@w>^GP20&-1|Nc;J_L;8%L! zl^*!*9{AH9_!}PhDi1sPu}A**JnZDR9{Jln@IKggk2YUNdf-Dn@JSx{MIQLi9(tJL zk-yvnukyhE;(_1mfj{YiZ}Gt2^1yd{;N5WJC7PTs_K-s#kNn4Z;0YdhvIl;y2R_#W zFZRG!c;GjA;B!3I^;(boj{y&Kh$gWI=#l>^k9G99NB$o@@Dn}!Y4@Jd=kZVve6R<8 zwg*1N1E1}I=X>BQfJc+t%^vwjK@SH-k@Gzs^*rlQ&*dI+d&47t*aMI06wq8YJIohHHzjbS>15w6oPYCH z4a774sO0DSzkHk6_Y&tX=bK4GIsYz||6pI!A-aC?-9K~w0N0<&`A<4f_x~WeF@;0w zAJ2G?cQsD?J9HEO?)3PzI2NQPG{_tE3p{+$M7TtgZ6f2{FEoPQDHMRem4hvdJ3@wF`q_Fbxp?>WX38Z;pD_$K4$ zexmVYO?=-mK8(jJ@{B!5_j4lazl`f>#;;)gm-UJgn3BalUBP{ukWPCf_%*L@y6W;>Hx4fbOx(`mrO^nxW*SzS}bBu@nrE#84 z-xrL>GcI!O4nsu!sb_n2x-RBB2{`pX$FdJ284p?OE`{rfryJ!sL=Q6|s#on&q{CEGP+dYl@a}VS8I(nM%R~vQ4i#h-2 zjN5wtZ^mbE{UOeOQh(i_@;~VHLidX47_IPweYO2H0C%a56yVCgvz+r7U&{EpH#AVf zcs=8vv45P(`1_2{X1^o;tLFe+{}H=&M$wZK7=LGn#)on}V}X-ATP%4_<@^I#Z<9Iy zrHrqAT>}>|{wWMsw7BjT&R?=sXPn0QH!yzH#~L_>@wXY@&GM1{f5Z4$dvr#zp9dVO z`(Mv~U-BOfoa8*0*Q4meFvfkn-o>6wWBhTBTavjynT%imwg$vr-OYGtlg7n9KLz|) z>~J)2yae;5j#n5@V12;yS4RuuLB{(q{+-m%cJ&&@;}6s0TFLz!%y=^6P23+@*RvSk z)uc0C!uhXe{PArXC}jK}jOVl6xPbAG81M0p21E{hu>eWV*Yd+nGdcfg#;1R%f#r-B zGrpeZMdtkm;G~B&mLA^D`Dc8uE11spJjwXRPc<-^@$HOH=XEXh?`C|96<2pToaM>! z$w;mzk@0-4N9@DJjBnnoGm4yx8PDPVq;fsCG2ZJ74P3?eHsH!ng}NvtBSt5D#QEp4 zJf;8t=KM`8AJMBGN9g{q=5=~2*FT=|K|g9h)=M_yXT7Fz(VyjvpW34F%Qf+>2TuB2 z!}@s;Pm}I!iv=Ie#+izpU$BjN9vCFXKly>2`B8@eLZJ!Qo0b3NB{{g-h4m5jHt{wFhjH{*5ekEH+qVEi4nOILFK zj~UPB{4!rZF<$$Y&UiKFKjt_+u804vfjGv;GJYrP`4Yyj22SH`WjllLULAkt`~j=} zO)5X^20iGDBPm7^-#eWD_*XQatcmX%#GLp$1H_^*Rc z(EZP7i_D+Mcplr^;X2tjlkouC)rpLkF>bG;O5ijv8J3;-E9bxOd0m0%&!ddn>+U74 zr{1dPJyCAd53}smM#k-Z(N`G%$?DJNjDN`T=jG&kXb8{yrF!1|JpEnJ0n(o`%a6wcC;c4s ziSD@c^GwF4vi+3xKA-VdU()$gdAzF_AIx?@*6&)zx3Iq$!TH}{yq5bfdiyPK>SwdH zkJl9ohx&Qn7F~hp!{LlKv0q-s^`FalGy65^PbP4xKWNFJg!9kiI8*Z9$asGiu;6#A zdJguLvptdPyQet+LENsyZ|^WZ<9*%1Yq_7jPuAl)w?P9ku491Hx+rIT?#uayasHLR zYCzWS6vp$d>zz!lCzk6ukn4G*JL`%35Fth7UrbqXXh@+nOjh#v6)K> zv$^r~oW-cWK+9lOej!zvT~?A^l%J`k6e=p1tNWBHLsUaA&epwBby2sdRKDBO!U7h< zWJe6TdM&U*Rc*nd!rXK^vq-*|7Zl}bAxIM7YWUzt3PU)LP+WPmu4@~rPR!;&O;ORa8yN(5iut-CpUjyb{R`! zM)thYtn75yQnm{WKrFPfRGlPM$JksO+&b$x34RrM#qdcOXR8 zqfn`_5}i(`Dr=_%*HK0!fNW=`RA+S8)t_qgMcIoAAV$3qou1^1F%u=$BA2zwizdA& zyD%>^ODoxAXBlC<$}nB7T5&Yg@gAm}S$fB^MWs1lVx$2>KKb3>5wN-NRTrxDOZjpzc zeymSt#i+ujn^Qn!a&ciED}X7t;?lXUxzT+fW0}6FG_NEV_DKst#_x=Hab{jAgrv2f z#_WhlRc^RL=oK$!%0}za9-%5>E)z!-&P-F$`PuMHj*PRQVvhCHD?tr6ee%p{>GQIS zvKQnQV@b`NHX*MdKYM1T7(H|BMcFhJ>@3L`BDm06pn8y*m6ctn=UryiQ5phl!=b9S zL2dc11uLp1#%)n{(E@j9)?!OfU$CfvJym+TR)nmqQcMuVRcgGQk2WF%FqV0wrM5yG zTg@F^itv+e;G_mmEje?hE40Hu(i2!$vUcgSrw>PfIc73(n9oHM6H*onIvf&ae&z)# z-$*sfDU@Lfl}nnK36q5&EQwYn&7>+lbF#{vB!1Q{FoLu=-3U>X;fSMOrDL+|bo8-H%%R9X`vD|j0j1M zn1p~tOG(R=r0kAX2H;q^6swQaLw7id32uax5}M*MqR6?@;@44b%8Wcc2qk`Qd^Aa~ zBy)iwJ6AzQrjiZQqT-Q{A;G3khp%&o8{!aaq_RDRBx$%KCy^b|)r-BO;wUNhq{9WKX-QTqaA)isBiX?dbK6ffvv*fv&OB3Tc2W z*oe__4{4%ZccVu@MMiS~JAxuQ7j}m%l~LxzgcK(-FmXq4LLRNNiloh|~zLkKTu-WQ9_Pr^CL5$WSTc37F)>9n%om1nRyr*yak- zCJ)-GW7cTXswdU4Dvrh^4$~$`$88t{+70D}QWMDJ85U^NWOWr9t!metx}%(sm=a1H zqheE9U#0oEh`S4l(o19vdRZh+bgu{uGo;;z>X}48XgF(zl883LypGv4x$;qSMgmd> zoIGSoNEcM1lZhFjbVIp7XO^`_@FZ({Y&$}#c+@PlLt}JTsqqN*MSO{+%bN?eBvs9gt}~m?%#Tswg<80# z8p{MFiD{QmLCHyL8udV3eHcAR5?eZaT4teYHZ&zltBE6=MWnpDbkOOaI}*4cgvT`T zA&h|gNP&gxQN-Y~kks0COpkc>4wh5r7O&>ycP9wSl{jK@v3XH$m}WPKGUvvTEA*of zm%(bKQ0x&+xM~HPrK!bX?8VeID;4h;xj;%{5y+5Jk2$1@@N0T;jS@32*L+b!A-#It z$%QY?S{yZJG(|&Tn+fK_jvv1vSXd?C9``rG-Y5+)sB;O)07seYz%HWuIWIFV`-x>(n8= zGhK#GM!Hj0RNJJg29kKA5sC-3hs7F^ZuN-#*XVQ=I?#GnmT;8z9wEUdr^cJCVjLMS26p=Ne2JI3N*K5=(3??_UIB$kh5|x{!uezdkzJ8-W zD*s5eM;~oXI=xW&C|*^&IGu?RqNV>wyZ&7%mE#Y2DMieu9r|?$mm`d}bKRh4L_)Yx zWU*i=oa;`f37IQ3v6L(EcQ{jLZH^%ED{*>RT~{BaMKvdG=R5WNZnjPwlNq$RF}!P% z)I5#mt#Ks^mN!j;n@uh1gmKQ^LsTuvqcFYWMNMRYOuLmZEfE2-BQjhF=v@R-)<|6% zrJWHq+|dm=jjDi58vyf4LHS`P;)V6_js@*C#H&v4YSF5*x~2E%qRPX~szqQKTV}yu$eLDNyztMkZf)Yow zJlvZ!cBLcL{VMMY)v#rUMlKp7rLF`;`)yE^$dFjapra?~yLxCOOHIA9zD7sbjiqQ^ zQ&(cF)*aj!b%(TDsUEjE#W4vo6S6)jw`xoP*N7$|K2tMd_%?TmCMJsDaD}g2E22>f zMPvsmBI@JL+OEl&hUnK4uKhBv#T-->D#4yMn$Hc*2umpf>M)hZGz|o3Bsd_1M5xiwNP!O3bFunkY`2Q zthxUCya*SVhH$hSgv}a=z!ihH=fCfygS1N!;Az(28x6^>=*U!hEDO~uQPav|$0^&a&S>4GA!_CtwGJJ&d=dd9oeG}5s#*Lqxo;YOqkR+XumWKQz zh722Jrj0R}$ZUxwOAqVE;1w;M{nr)WOzAA$ySy%^|Lx9o2-E(*^5cD5obN{qaBj8NXcW;Ty+T=@Gd-=?hj< zo%}s~{WuNTePwSxg8F*;(imIZb}N1X73<4peu+vyz*oZjTaR3dUrcrPUBmokDt}ks zD&|X0UL8xe<#zCN$oIJbsw1>f(?)*JlHiv8M1KseO;P`%lqImEL?u)mSf@a{+&V#m*0z)S-AW@x!l6#_q`PsF7K7Av2gkQ z=Nb!__wLnN_=)`cv^ooy-|?=uaCr|!gN4icm>Mlye%IGz;qp8D77Lg6bA>Hje(&09 z;qtrjHVc>cBB_ruVX8%M<@dC){JV9*<^4^53zzps1uR_Na}jUhCvMhym0;oW`}`ye zm*0CQTe!UUBxvFCo}-Y3%X>r8Ec~54x<45fF7JQIv2giaeVK*Jdl||tT;88mVd3(g z${Guo-wCg=aCyH~t%b{b3hFFe-Y-yZ;qpGC^%gF_=WMWWd7n$8g$K54xiwjME#u7= zF7E|sv2b}GLD<6OckithF7Fv>v+!E}oxJ+Mn4!<|-mX~skcWZGdu{v{F7K;~vv7HT zQ^3Nn=l43rTX>ImRdc=s3%{Q6Bny}Ki6mRNyni8R;c@@e^@l86-b0sW;qo4n3=5a{ zh2>bdyf>=Q!sYkdWfm^)$0@gPc^^}Sh0A-dYAjsdXR*e@<@esT7B0UluCwsaPA#8$ z3zzpTuD5V`A5DXW%kSYEEnMEu)nwuF-il@mKea{or^Uk8G9I>YKmQ)P)xzbyZ*3MX z?@5u5>xtc#_fy33`?3U=-+TKlT;5+0XW=XPci;gFm-my#Te$pgJi)@9_s&?jyr(kR z!sWf9K?|4n0fj7F-UpOs;quyeF~D!sUI7w`O)optc8m`_glEw^Ee9^dmgZGvFGs?F7`aZ!o{8^S-9BqWD6I29<*?= z=OGIhd!A>azp}={<^67JEL`3z zU2Ea;KKD8cm-kiGTe!T>alM7h`?wn{T;7-0Wa08&(Pj&m_ph~BxV$GcY~kVuTPY3l~3_WZ~imlPz5QV9>(F z4~8sU{9u}eiyzFeaJjC@v2gK&g%&P;u*|~650+cF_`wPb7e82I;o=9^Sh)DXS_>CH zSZCql2kR|d{NQ>E7eClw;qqSoMhh1|*ks}22b(Qi{9ucPiysVIxcI?V3l~4wX5r!o zeKRBVU;JRKg^M5bTe$eaI13j)7_e~hgYgzFelWqp#SbP~xcI?j3l~2av~cl*Aqy8j zm}cSP2Qw^Ou4{5ET>N06g^M37vvBc)nl1_`yaC7eCl!;o=9IEnNIyi-n6H3|qMP!Bz_wKiFpB;s@0S;t>K! zoFngNkLCAC3NG)%^;@{S$2QKw<$a3*3zzrk#ap<%A27kf<^BH27B2504O+N-pCDx6 z@;<&a3m3ncVd3I8b1Yo^W}$_P-z>9m@tfrqE`GDZ!o_daSh)DjH5M*@v)01JZ`N73 z_|19?7r(jQ!o_bkSh)DjMhh3e*<|73H=8Y7{AP=Vi{A`excJRh3m3oHX5r#DeHTR9 zdGVXE7A}6%Z{gxM<1AeKX28P5Z^m1=_{{_hm-h)KS-AMkWD6I+8MJWmn;{DqznNy? z;x{uaT>NH^g^S-Tv~cm8Wfm@ev)sbPZ&p~i_{|y%7r(j2!o_daTDbVlItv%SS#ROu zH`iOZ_{|0j7r)tP;o>)&EL{9%vxSS_Y_V|hn_&wVzu9Wx;y2qYT>Pf*!btrWzZq-c z;y3*kE`Br4!o_a}EL{9%yoHP3Ot5hAn@JWfelyv^#cu{JT>NIp!o_c( znPcJNH_I$M?qV(9atpt2lfJ&Lu<$0vYb;#8x3tE><$FK17B25Yud{IZepS7N%X?W=&*%{SPrcgeg)%g7B2Gf@pq#nJ`wrE zTDZu^Z{Z@JI13l~1T0+S6K~-np9BjR`6OBRbu7=Ih0A+>Ll$1wpyion;hP!HuyB!2 zj)jYS3N2jZQ)b~JpK=Qq`BYfA$fw4_MLugRT;x+{;qv{tdJC8L$gj6>`QAyRh0FIy znk-ztpVVyO(_dh{wQ%`9R@lPj`$Mf3F5f$8vv7IewC@jHR70Y-^8Gx&h0Avu;w)U= zUmvh=`3^z6h0AvW5-eQa51wS<@_oK!3zzQ|1ua~@D;BbF`HoJSh0Ay8GAvxadz540 z^8K?y3zzSXlv%jEZ@R+5Tll*LwH7Y%f1QO({9kY3692EaaEbpLEL`IMMhlntzsbTS z{%^K$iT_(HT;l(*g-iV3YT*+9w^_Kv|GwFg`YG{$tc6Sb@3(M?|KluN;{Sk!OZ*>i z;S&ERSh&RhNfs{gf3k&3{2#P%iT^_uF7bbwg-iUOVc`=0=UBMJ|AiJV@qf96OPpU} z;S%T9Sh&RbYb;#i{8|f_IKR%qCC;z6aEbHRTe!se4HhnOexrp;oZn>O66ZHtxWxG_ z7A|pq*uo{wZ?$lV^V=-k|FO37KK>4-*x{=fkG1eS8TVUw6XS6fE^&Uq!X?g+w{VH` z6D(Zf{3HvPI6v9KCC(38xWxHs7B1gc$gpsE-+hjS%l9n`EnL1+Rc7Jxovm^Um+vxG zSh##QsK&zO`weR>T)yvHYvJ-ezd8$-?+w>mxO_Kvy@ktnI2tTmzTehl;qpDUW($|^ zIJQ{0eE%|R;rTCX{cN>x`7TA9g|~5jANx1aXZbEvtcA;W_52nt--(H{aQQAsz{2Ib zV(}Kfcf~3! zypH)b7T$137d7Bo3zz)$7T(19*IT&sr_sV&Ie)W-%Xc!u7B1gOYPIk-=6!sfDtaQ{ zee_$neD^V6;qv{Pcng>7mm~|9?{Xzuxa1F6xO_(>!@_I&YdIHMcrD}Q7B1h{spanq z$#@-};~%OuSF5c0w~YM-;ZGQNBd^K2>qgh ze%0U}^#*>l&Y}|68~Cwqc3*>mpK0KY27aG`HyQY22HtGoPa1fOfj1a<9qT7`@iqf* zU>rZ@@ZDzgU%n?uc$SIt_i+e+$;A2lN`yaR;PTxZ!nYc@FGDj205{=S1MiA7Is69R zO+e5%1E)6R2pBlcnH=#3PHoGPVBoTbDJ{vs@dFy?NH*|Z4(js-4g5d@4;i?8TaJ>` z47`t#Kf}NeGVmM&?`zGVoIz)aOe!@KX&uXyEcqEJ_X;_z)w1nt}6A#Z+pBfz$8V!zPGT*>sIoKk6fr0xCJjcM}41A%12MqjD1CKZGJOfWK@I?lmWZ?M* zo^0R+1|BrCL$%*8u(Hpf1QELw>c@f-oURi@~=1Ws|~!tz{?H1(ZH`U@FoMl*1($$ ze7S+Q82EJt9yah52HtAm*Bf}7f!|=@zIm?wuQ2df1Ftl2zk#nb@HhjnGVp+buQKp> z1Ftsl1Ova(z>^I8j|QG>;57ywH1Izec*ww48+e+5-(=t!27a@F=NR}c23~03e>U(k z1HaY4%ME;ufmay#Uktp)z;8G3H3oi%f!7-NUk$v@!0$BhdIP`9z}Fjit${Zf_}>h? z(ZKIE@FoMl$H1Ene64}E82G&g9yaj58+faM*BN-5f!}Z7KECcIJHO7rV-5TP1NR&F zg9aXF;13yiz`!3i@OT4%#K02_yxzc*4E!Gko^0Tc8hFsaA2;xjfj?#7X$HRDz%vZ| zX#>wO@C^oDXyDHnc$tB3H1Ki*f7ZY&4E#9*uQBlF4g9zM3@+at8?5U7B4iz0(NGf8 z-V|KECH4iUSo_FrNNPXfRs7rcm}LA#yz=m0w6xW({zB}kX`el_u=hF=saekJj-#Ahh}TH<>WA5#1k#P=dT zS@A{0A4q(H;ujL%oA`j@=MvwCc)#Lj6Mqo#KE=-# zKb!a=#QPLKgZQDux9(H(PkaLLEs7scd?N8pia(wBVZ=8mei-q?iLY1uVB$v*U#s{* z#M7nx7d47Mg7{Iymn*&>@uP_^RD3VulZekyd<^kphz}`#FU)Mt>BJ{1{(IujAU;9y zUlKo-_<-U+A^uF_{fd8&__K)jDSkWgXA|H0i<*DplZkIp{AS|E5#OZv4aAQpzCrPi z5dd1B#zZ{50bIil0sVbmD!ApF#Y2#JBEM^G|$;_!h;FC;oim zn-qUK@iT~TQ2a3BQ;Dxv{9xi|5?`zMLB!Lg?-wsj zCB8=SM-ac5_;SVfBmQ#Y3l-ms_%h-%6dyzU65>OO-}?agD~L~4{P)CPNqmChza)Ms z@d3quLi{q~{fd8&_^XKbDSkWgR}7UF9Ze+2P=Cca$p{fNJn_(H|^B7P0=8H$e~ z{x8Ib6u);J_}hq2R{ZzG-%fmj;=d&R4&nof|AhFz67N_1d&J*Kyif7liNA~Z)*sdU z6JJYwi{dvE|2N{B6u*J^yNPd5{G-I*LwvpB*Ac&#_*%u^P5iyY*C_rr;_oBAT=A=k z|2y%8ieE{59q}28zn1v>i4Q6M3gU5M{E9Ck9x}rC6~B;ptQm}7@pFlX4aN8sKbv^? zRE%HoGl+kL_|`pY{)w+AzD4oliT?-jO^QFA_(zFv(D^ae`J^xENc?(o<(roTD?bS? z-_;tLnUdI$_;RrN%-50FK42)a?wLQN?=iGIbi9qUo~STb-93iLn6{Du2-gonMhe$^ zwzu>>rkwJ=z!dra31I}QM{Xx_@~^?l*5IaZ&JJ#B>l*CxQt-`ROZuaN4=^EAL2P@= z{JzIbbj$zH_vpRlXTC`NFFj>eaQT@lY3hQNpO*9vR-KuTI>RSJ3gLP9=cVp{MZPW< zqiv^sD*wghNSuW-xVQ*aF54KaEUgb#&%&94zuyT~ZVlfH*9x{ZSoso$Hk!H$^lFXn z*66W}J`FRxHKhgJ@KvR>u4w4H;#d5+H6=_&4VCOt*D%$y8fHwb?z0=3F>XEOeY%TE z9N^0qFHBy=v-sjsk9SO6r2VD*5X(X1nYjX$VPZAAd02mcIIp9Vj{>eLG~7i#cz zoEL*Xnlf1uzoJ8f6=s9KU=gvzFLfH7#ErfV3u8)ry3^=^Zlm`j)7F$WJ=?R|3`yKG zo>ijs04T_St~A_~mZ{a`zSkWSfhm+h-8ZCk! zqicMQ%0Exq7``2gnOQ&?8uNL2R&c9M1F6lSvrvxH!8f)dT(lXQ=zR z|FXFS6WdWX=eaeh3T@el#I=0TOUSOOA1h2f$-jEQJ#f|18)-qxRS#IWJ|tvhk8zTdUpVALw}Up?S(1Vua&MQmwM&gua_ zR5`gE9kNEzK{F4t+N04%YLnL0V=QS!@4?UR==Zw^w|wu)v7H(uy{(hOh081vC;m+aLl}=5ba~LUo8GH4BYuvGjR$c#lv?|f) zk<`k1ed0H2t-Omch&8N(u2v6t+D(=AXaAnH@{d+mBGP`x89fj~f*N1v3d)>Ot%q;m za)bM+NOV2C-_5n(df3P4V>CTXfL?`Rk6S8d?L^Zp!Kz>^+<#kD%FbZr>oD?jz9tI+ zFNo-Y4bg$POea>fVfk*emda_(6Dw18xRotytD3c=snId})(Y>BNqeW;vCL?fc!eKKE^huJ-9rt2KIbwaMyglR5k= zzU)xv(-h-%Ceh!jb1|mb5^1(~ox#dk%|QrN_c9q1P9AX<_)_3o!)Y)WR&9rMs4bvo zh_nKh6)Lljwc`|stSV)n6VOmT?eQyTA+NT?s2zV#YlV(cgHL_o1T2WH_ou`y5TdLj zS$kCX@&-8bt>ML3|Hg`^8B%?yoP}6_S_>33JQa$H?)WMDEYj%8t-5!i*r`>}VKuU8 zEmftv0k=}fe<5XEx*J&$(EpCw|4kaBYoChcB{f3^&35e{hNIRVt}NZ9Cao$(P0+ng zjgVrYl0GgXf|YLuEBB(0;m>&rMDLyPqmpptv|UiAW{jT{023d?fJtSSHCN5r1s$MS zQUh);^-rtZghfjO{}<+n2hMAhYBg*JiXo1JDpi$gQ-VT6V4|rJM;jt~LQ@ghl`=?Y znqPN^re_umlG`6O0aGxz$bLjy)fim^8g5~M`=M;$9h;%A;Fh&rh(4|)UqP2>ktXQ` zx{ioglV&??<S~ZKBYIH2#)asPBaiI&Vmc?J#UK*HI*@Om9){+cZ z4L(knZ>m9-FRcS5U)_RQWta+eJ)6?eESK2j{iOAkrF&2uvz#1THEWM99PYR}3=P9P zDwD7e{eyx+t+s}Hpvzd)xE8*p!I?`X4`^oPtd`1YJIO?mILLFX32uNRrK4|f%;usg z!A&h)shXj?FvLm<{I%J@L_>VbnyRMl#8gvTSZ3{|t*+KR^p&0*TtgwE_kDFn_!%JR z3fidd{~}?itTosLSGR<1YUviN+yuxU?9#}xS^?LvJY`=O zglfQ+>Z!OM)~`da+;Q+S<8i3E&XT&!b>{YyN}!+3m`l-yR4kb!F%%TU!K}l*AqJcF zD!KxlON-TA+8Vx-qJ+w=RnuNoD_dFV$(8>cSJ|jM2UXn%9jFObUCb7H+)S!J!MlFw zm#RNJwJO7DXH=ASXl18PtNhq(EJWsjd}**cyY-@RbM)2z?-|jn6rWimqLvo=tZ{vc zsn<<8WBWbr%!|^F)j}5809SIqr}iGdUHdH`{nxeEk$d_LN7RO@>Tf&DDBk^&iJ zWBy4MsFg)Zr&jy#X@46z!pg1t(f)W?qA2bE;s4hDx%=JzwI1WA3jPn{r+9R~#y{+X z=;QytwSVq@w|_S;LG3%;`KJmZ#}D;UCP&8>uxK#5$X{JX76!Vb!pwbDr7Z}0^(Ez# z7+Z8p%XM@pr?;%3z*j~THN6eRPL*k^N4c{da5mE_kN>o8^ryJj>N8_K;QfSBj$|rTlwV*}VO3f468^Wcx?`-`d|r zA@hD|`94Tkt@6nB|4IY+Ka79B+kZ8B`$zrX+TTUjh5I%B-v5j~ei}gJ_{YuC-aSby zedVlIq3aEZ9vWfkHzIq|)ao2sbIt7C5xZb_K=tJXzzxJs)?LHZj~_;OBd)QkN-6Sa zqWY_*C7fS1E&lveTpOM5c!K8lRUZ?`?}y>{$6c&F|8GVc5@o1$h!&0YkiR;ySCR>6&}#_Q(k@)cq*nkQd~DkB7N!-q-$0XOh* zECS7jy6F3jSZ+IcGt!7??Je|0ws)A*o)i6`TRIZO-XBDWQw6qd?3Les57kFr5T8X8 z4Lj&u|LO9|vZ}HOh?PfQf+YZB{G0i@pjT!-KZcI08GU_0Q`&?K66ZSo?bzPWFnO-= z7Ypvz#!+(&R)*9@wl6-4q-lKStmYx8Lr=_bx!FmmrnneW7dS{1N4C} zIX5U)tsGpCBf@jq*ScFZEpUFS+l38|GMQ1kcMqbCzCW5YM&Dic;5SF#{?7i49uaFZ z44K+9jyC*TDG*VHqmiUhp;)z&c=%1Yw5JJci5UOSl=&gdIplAz+}}6)L(!r!`U7+a zu<2-tp0Af&(I4~H{*0e32Weik;jclkZ`jRB#bWlcWx|_etbG`i+K7RaXk(Fv|7!>J zqsZs?jXn+y8l&&2+ux7TciFK&qwi$)mD~8s$1;31`zsZ@Ne_P+DZe%QsqLk4?tWAs zvWp$(ze6lVj+8E$Fah<@B+RTY*u7cpRzb`Z{BCXEUYQbxRp`=S@0SGx*;-Uw}NeZ!1}a@N zz=tb;eGLlsA-d$M+*Gv;*QU55u-`VtYL{ao)$ZQ@*oSmY%1Cb4 zZ4^d)awW}r_yOfLmTyT=8y;R`^sry5vFgai8p$5ho~Nt1Zfi$%Yz;Hv`bN25@`mdh zkseT6AJ6d=8qh+D*+hV+1!xNQ7pe5C&s?$Nu9ulN`$5TnD zvmM(JkrGXyQM-3>c<|Umbobb|qqA-pW6Cv}6t--4`yKEfa4THv|{JeF78eF&a=4US1^Dmh?1JtOp0clBrx631XGlWu%9aZGm$<`C;2 zPw*YAYQu9Gc!ZW7?D=Yx0!aIai;v)r(2GKK=vTEU)Sm~?np2^um;QIM)^0pFVy(SQ zK|I!Yw6*rpn-CJrb?0lXlePv$wsmnQZKc9U+imd`Ry-!Z5puz))Ki37h&gRd-2@1*7(#zrmum7R>GcA*hqP{Yd0$aO;om7-0{3YxsBy zRU(((`5kIXR$us{kWX7sCxSfNI(Vq9<*qg{x*!YUAWeL|gx)v{Rt;Xd5FZ zJEfZ0K+{aDb9K7kEgjcMOV`k%ifk#g|1ELkCgZB7zCWS$5!WdQC@`+l1{&K!JO!%v zx`UPKBB5d%8rFA|&0)Sp*25X5TA`lfjMn<-sI4QSvYPK1z4@6D&9i@rrZf7YVU}M# z1N07ty^s{TlH4Jwkh(`tce=rMxT(I-2d!?!9dCWB`z%shgv-$8HXYl4_~8wVT?hZ4tg}F~>4_P&O0UJF zAq2o<;H4A;s0vhz@cLs_+tmX;#P51k7;uW6!;Azw4((_;~MsF6nJK3 zhZ+UkBaLDi!g8f?ja8*PSo&MZ3`VNV&055K#UgwCUXkc$$gLyMjmn}*Bw9@u04fr_ z@fRA5XC!(rig6@5Si7HyNHnUQnm{3I^?wgnI21|+GrXqgZ%4@ z6WfDFE~}>P(o6n6F^&@V2e1N-w%oNV>z3Ah%kvRyJ^^J^3-05g7>v@neHav0k$N7C zb?!Ly&aub8rC--WJ+yw!QD(TKe!W8%2j2RXf?}*++c3XymLB@0;wxV5+Hdu8+HO&1 zh4#H+DwS3zQ!rY2F@{M7-SGG%KSot$a&O~VROXD@@mA4yA2P|rL|&5YRo7P%<)FNh zIkc#yvZ*3vXWu8M?Nq(f_Nq-!ZY~WtvYsBT1VyU{+6Y6T1A%?iTGZ>j9%Py#s~m}I z8eOI6klVLeJM|q57*LXid7U>F0bi#XI|Ti<`nE*H8)o13v#y(;=}^~c|7~5(9qam7Ma2I_zm|5K$y++qr{?(STk4V~0+ev6B6x;X7l-*c< zRo&O*k`>1DY#Ws7i3M%tS>L41lerUSj$t)cv+T%`e@RkHmX@NsM~wVf#p=Qo*PmO% zcaRy6*ev2**-xph{jvZ@KBsiidOER#gs($3>0Jjqfu8hJJ%JR^LFe(u&Gh)=V9cm8 zF|rQb)kDuq%5#$MhBrMMttUFSSR}MmM_~=gyF*KbdMNG;DvO5+tW4R59vHvJ;~hwP zf>`~OPt6Hi$g1Sto#yY2AFD*>_jj6~eq_gUPGf;WmbTJ!`~0}>Kgk-Z;Xs*!u?Qn8 zmDpa;0ynj#vb7>*53~(WK;vR{`Q}(M`tOEwwc?mLsSPw>Yv9!p8W<^AHqorBFFr*o z9I<&4=PbjBmW);CGc-8D7wE@0)r_fTmMQ;R1sD%KvH#6_+I#jpTF@t=SXzn??BKDA7xX6bDRE~G?WT-4>Fl^j{YZDKC(h3ejyvujHa86XJN1%vK~FbGcf8|7>s9Y zxEG>feV-|g*c5Vt>h@0qv7;2Lo=Lfa$ay8bsGh>_Lr zS{9NxYF`;SGb-J=Lx4xqpzf8lYcs>h7yS+`JtvIOHIkz7-7Gbn#T2->Pu46@tPH1- z_joZksMQ+CL9`4a9#1eK z`%#5H-*pXrZ*IWl>bclY>jR#2U=|utb`UuIdjH<}gwv}X{#8JK(OW2vr5z#U{R?yt z>~@s^+w!<#p8?ArnDhY)6Bi`a-S_pA;zfD7_+BbrHQ*w;jMhdn3Ef84pPj7daMm|; z)&w_eADwlllXW0xT}xS&4Ztee8t#c3Q?yb(3LmM0wE?@+o#tNF{lDL-qxn+Zvkaq- z2D$(>yQH7Zcd?%5rqO0 zRA`5!w^yCKtLkaoTf^T_MU~oTXsPytno-rxUJzBJA|~4P#j7*AqX?`o%r3^bHT)8; zXtCd@>@~EY5B6uemRk2&!;6rc6T8`o)L4~I))v~5tAE`bW&|%KX;rPxQa|!)?Yqfe z$+`*Y=>CZxMWOIH=#si1lxemm0KdXruFdOd_;{`C9s-wCv4ON)XFP@6*$Fn2{yphG} z$BEpJhgJR5kJ`w7oTmFxOWnpI=;8FE*zLzX-j@F!3YQ35smsN#d{s&sG8nAAT z)88AYq{^^0{Qe{AZ?v>E{09CeS$>rw!U4E}GU#2Y=V|pSv7zp*;aa48h5I79{{a5z zX1$rR+B+O=u4Ppk(JJyIkBstPD19*(K_9BoT?8DSD^2y|-_|PMn9Z<;uKqNW;3}Vu zrqj{=YL1-23Mkrj1>(V3Ncokx&x4LmnuNFj2s+np$a#cOx`-7asUZk#63{5V5SpBUWb)ov(>CH6{LVRay@u|Ea zq>DUNfwNt}DK!P9csdfO)7*)Oy+Xe-In@KMo{T<`b*dy`f#A2g&qeq zQ8N6Xm0M!=fHN6qIZo>I6{=LJdt7oQ&RP0Gy8GuRoK`IiO~0!7(b*}02>L%kR(&dx6h z_yQ#bfg;M2$O(x7Dzh7&T4?f!$@%jO zQWP)$D16N1{F3aVtb#?0A`_+Dm~)CU3v;~FlO|*qmFQ%rzM@R;{D}z|PEKb2yu9op zZc&vReqnCD$&Z-Op-Z}c!qcah=H)s4RrttriV8{#9gN1P@T7B!vM(#eRHrZ>isVPn zkYt@cDzzlDq_mhSQssx8=Txfeo0y3VOA517d|i(0dd5x|qP@tQaeI4vJQTb3zV`MI zXgz2f(*E*y;4m0}Sl8a31cUJaXc}k(Xb$qd3R(gB8E747zX#ge8$ri{hCwd@jfIgb z1dRt>2^s`_5|q9?5(cdRjltGK9q16yM$k0SFlY^EEDYoZ(0I_#K!c$Ehfp4LENBJj ze9$`3a?nQ52SLN2&w<9mxQ0RFL1Sp21at@}ec2=#v;wpQv<~z}&_>V=pkdG*ps{3_ zu>}whI+!+%K*xg8mr&+|R)DSstpnW%+6ej?Xc)8~wmD*9)F*+)gBF4YL05v(ms6eu ztpIHVtphy>+d_??8K7ZG2aSb8=#6c)c+d>mUITp`GzT;W+fEgrgF)*+=YTeX-Uu27 zeHb(r&ZIYX;^RRV(vCdn!=UtKmTuVRsQ^s^tpi;J+6cM_Gz>Zqdu_2Wc56Z7L0_f4 zH_&d_R?7i>53~Yw;Chq?oetUvdKG9Gv<@^DPV6Pnc+fqdLC`pCnbVhFl0Yj!i$Lo@ z*Mc^JZUGI0?gEX4^E(||_3@y2v}F&v3N#1wOVCxIm*6HvJ?KlIJ3x=ey_Yu7Ye5IX zfwq7qgJwM2-ae0b&6lbS-FqINTP{5uk_SX6;PSNuVX5FM+Nh z9#1%IBp$Sdc-#l?1}FUzXgp{dZlHxgKLjly9ycJ@fNlZZ2-*$zzjlJ23c3$;Cg?yo z_!7{upm&4L0o@F`6!cI$K2!_38gvV2e>_4E2CV_@ji8_(o{&fYtpS}0>c{=!GSC}A z@1XQ;?d=Vqd9Sp$?*iQg8jC<8^lE$isi4n+hCn|AEd=$yhTS*N(?RP&=YlqYt^{ob z-3IEXAmnwl3z`5L0?h#}1g!zB2MxW^-rfwl5p*ADd{cXS0D(&#=v2^spn0H^-o$)@ zJ_-6D=*`>P+g}B31KkZe_bs%CV27S(907Xn4%j2mM$o08bKk}|LB9cQ0A2YG<^^>2 zyBH?|q-#OrL6iuztpGjxpU^|lHqb`UN8f{dL5IB$eL!#(vlH_IIs$YK zXc6dA&|1(tKzD#{1nu_$<`r}`Xe*{~)Y;&;-yCyHFl?Q!uwb-)~{qstXDd|k@?U5@O1Kx|Ey1N?+TrG52xwzosX&JjJoj?pQ9 z!cC9?e4_6l|D=QB&h2~P<+0_yvyV7q@bKeRHB^2g&LHZA>vs++I|paz?)G*n?i+WI z|N58-eR@nnhrm+$Qk-Lv4%6VK&veu806Yrmrnb+FLK-8hxGDJ$`6deTI;0!u}J@OC*|iLy|$C`OOgIyC*|)z z`jbfalK)1e*LPC?PNZ){y4U>fL;Ay=v_G(`&$qsl_QxXq(N5Z*gY@S*X@4owssAI) z`6+Pc=MJQA>7@OQT)&t8?L_*vPRj4&`n}2z>;_wj@?QEi7U}OH-Alf6kp5~X<(DG; z9i)4;e+SY(>7@Kdr0?z|eJ9d?>ZJaCNRPS4TmA#P!}lWHbNomTAl*wp=OFzEq31R>A(3m}XS?P1ar(t(dQ6(rzkvr}{3!30J{IXm+}qwh!7P83TmKxS zpM~@b%=DP^o%)v|y#(o=>mTVANS_%|{yeArMx<{<`htk`xlZ~{q<6ayF;_(TrB3=j zq|ZcpvYCFRN>=h82uEFl^pnhVUA;;li}a;PPcqYs-STsgelybFHq&F8oc5O@{oKE| zw~sf|m$~KdKzbh1J?9_kS0R0qr}W}&Z@46f|bL8Na$dbG6|K$#q*Z$o-#V_k(jCqCGDo_gfD_BZnEK%NhOBTpOh zoc@q^TePMJ_QCo`p4H}`YNOuA-`-+lR7Ss+VdvJ z3_-_x-8^i2n&?>c+y|LGkhylMjImmgc?vS~wok7|4&63F=7Ur9n9(eH=0WC3$h>c= zj2QtUvkWqI*tgv@RmOO9ktssvM#xO3=SIjp51Buos%O30(hkTh$6oMdSignqYP8`I zT{BE0N?uPuW)t>-*H6`B0sxVD88VMTX5Ca7GmD8#74~ya$G-94RGFM)CN|LunKh7! zvzdPKv*i=z^`}t zBi0_Je)GV82>iFXd?(Im1^)@~WUe;)kdGQMqG;co?hCe9mvSjPWen0^xc zN$~s1_%<-80`@fcvvB@!rOTIgTOGCAEbRR+|2;AbAaf&R%EqqfVZ1AObwK8M$h_ar z%ZcemAk&KTjayuqnV&F)5gj*z{}u3WaQTwYwYI#{COaTAA7>z+c4cO6F#=*M-PqT- z9sKX$?^*u~6P>oZ==pl2=VjiB;9unOua5Y)f8wfR+-EQNadjF2{{`^l z=Fc0!e;NF^Hro#VoChoGmpZ3sVc!^hQ|JGUfH$VjRgjglr)weG>DEQ+Ylo-l!z!FJ z-2<688)*W62l!2{4#&5y2LDm;m%03zEny#S0{><3vt|5fk6|nL)syl5V-oz!!H+B7 z)8O|`!{Zj@0MKR^8LYgM>({6gPA!nbz zA2L@$N1V-H2mXebJfsiqAU|$xvK#!HrqTZ___u=}mwpx+_bcF|siHQ@m?ki7G!Oip z;6qKs&qn-K@b8@_eLwiyrb&Mt__s}y{tocBOp|^$_#XxT0=InQQTd()|1R+3;;UJw zVh)~$KM(u|z;AN(J7c^R{HMTwlgoGZ+4{+Earw?Z*>&K{yX0|W_zv)o0zWSP+713( z@Go%nyY&aZ2K+dmJ?k{g$>2A+>7Dw`1HT{qdY8}mQ09(S@UH>?W|yyX%wIdTe>C55^yDl=PIx<%FLZ;(u@pU->ei8h*IeQM2Yy`h-PY&aG z1F9iqs0RNw@TV(7H)IaQ=#Vm&@Xx8cE5#FM{7V zjea@vZvsEgUh2TlfFEa%-Qf3vA2;4g;1|J1_!9XOyRB>d%}wC127j%~=O%#ITOC5W zyTCt)zh@nfz(wbH@w-2Z>`m?>One<@*{+^c}>Z@3xMCEI@ z^^quvY%IT`NmHX2Fo|{>H1XE_LmIap4xhDB=_{~*fnW94EwY^S!-wqi4$>D%Px`IJ zFD2bXx}EfSQY(e~?(e4lNsBBeJs_J|_?`bBmR?S}h4fz1J)}P*JxF>S%QK&JHL2Bx zGh){3ny*>pCeppkSLFs+zK{OKB6pGQC#|7;hV*tX{ZULmf0s?)MEb(7?eiO7wDcm< z50maAJ&XAaky>f&2eSMx%(Cq_S2mmQ>-dh9xSRBhz4m!C>9eFu{?+1JNvj^=GilX# z`Ak~%sD17y-AcNTboOJGznOIQK0cGqew@#wbDyx!w~@~Mo_)TKbT{eGNHf2({F9!v zw2riw^jgw=q?zwqxkpLUPub^3NYg*C&wEKbe@H&*`A^&DJ4oj|W1rtodK>9p(w9ij z`Zp{0VbbNKlcWbo7yQ`DZDqY4B0Y+BktV%^)Jo%AjQ!rq{vP@h@ApT3X>0zc*6(wk zwe{LaI`F^k^8=)__uJ)pnYj0{da~W4{`>I9ul5YOCecnNOh_vQkEdB(a`}uqa z=}V;NGj7s+s+muk`mg!Fl+Sg{PxD#EeD0dG$S%_VZhch$A&#@Vc2Pg6^7oQo$2ho? z^op0P#Ob6TCq4H+Exw#o^^EfQ0BQBhmb04lcG9ZfS-h9@M$-F8pCC=VV&xW)UQYU9 zQqA{imS3Nh{;B#At>2%TAJKYk;CQ%!>(Xtc*K_^(FQip_Y>Elehe@lxVet~_F4E^o zPk+erJ4rR)AwEA$dhS14&JgKt(nF*j-?aQ4r1QRIpZiH~Al*%R%fpuc6zOsFM;DP= zF5VA0afbeB=q5hmLy(7dNS$U&)7c{KWflEYq{st*yq=(Jn4V(S`QwL=qr3)R$D;PgM*HXX}9F|CZ0c(T=n5|1Fj^7Ilqx~K4}AK8|f<2HKY@yn@P8jZX?}Ex`%Wh>3-6Kq_aQI{7L7NHjuWF zt|DDSIzhUbbPMS=(w(GxNcWNMCp}0y`yZJ<>3q@#(l*jnq-#hgNH>#iA>Bs0lXMU1 zKGOZ92T5n|VE&}@NgGJpNLP`rA)O%IOuB`18|hBcJ*4|c_mdtZo&5#oPdcBpfwYZu z73mt%3DV7^TS&K&?j+qqx{q`}=|R%jJDES}e9{KeHquq3Ye*+ZH+L!(k-OhNOzL%A>Bv1pY$N3q@#(l*jn zq-#hgNH>#iA>Bs0lXMU1KGOZ92T5mtk@=I(Cv6~YBV9$hhIE2-GwBx6ZKOL%_mJ)* z-A{UuboQ5+Kk0nZ2GTatRitZ3CrCGwZXw-9x|4Jd=|0l^qz6f7-_QI>=aV*&wvnzP zwbGbh^5qlC(w3GBsur~7dkeXts$|W=nuXPGP1c>)pYKi9j3?hhNK;RxqHR{C8YHfQA{>FsH zA9yssjrdpe>$wl%#P#@N2EHt-@r>ek;*Uz@mti6KKI7#p;rEQh@rk46*pkemq8|}I z*251GpX1@jBf01~$-~beKG(y~BYwJvHxjS%@a4qkdH8DL3p~6;{Cp4p5bT08rYq!^7tjPkZ=c;@uv;g7~E#-b?%n z55I=^Y7hS?@qQ1#gZO}l-%q^g;g1p@@$lz~mpuFx;`kJru}rnklQ3?jeKvad`NTJQ z_{GGp^YF`vU+>{X;x~Bshlt*F{0GEe^zdI0 zKjh)B!Gcrj`?80hMm!-789y|a`6Y?Z^6)0&M|pUL_^}?ohWH#0-$eW*55I-@To1p8 z_~{=0b>dYX{tWSX9{vjP1s;A17R1s%=X>~K;?*9$ig=xej}UM4@V_E{p@)B#c$0^J zgLtcl|A_c95C0G19UeXht97YY+QZKy-tFNj;+J~(yNO@n;Twss_VAmD_j~yL#0Nb5 zapFY}|26Ru51)&GOzKtg@Ot7KJp2;k8$J9Q;+s7D6U49c@Xr&!-ow95{00wyp7@O( zel#|Gr2IE|__@SC>EUg}Z}sqi_*M_UhWH&GelzjAJpA*-@A2?m#J79+KH@t(T)xLG z^}5f)Un2g1haZEDE|H(~@KcHJ_V9CvKjh&{h(GM%S>k&=d>!#eJ^VW2Pk8vv#Gmr; zyNN&T;a?;EtcQP>_;ViqZ^VD*;lCw*z{BTY15?`VMGrrR_#qE}JMou2{9VKoCzSjD z^~7g+_(zBz<>9vzKi0#)Oni=q|A6>O9)5uMTn|42`=nCe(>?r+#H&2Kj`%ze?;yUw z!`Bc$-@`W&ulDeph}U`ey~Gw)-TSI&saXr_aNBqk%`X8sf-b?!c<&S*5&G+T6THr0j-%MQ3gWg2^ zV&Xe^-gpx6tB9w6Yyoyp;#T05>ib#B>%F`8P|st;^*l@a_a)+buW>HrPn=U(|5?DL zJ@p>OOO$T|erDq2gq{zno_EFQDa7FGssEAPR&gEm-%ecbL2A40h|we8W~$Ua`(p4P z$KWpjm-^~G(YLcabC0+6o!e>c{8Hj?G5GNbJ)gp`GQSIeiypln_(9@VQBV3^94{n) z0lZTA|2hW$R1AJEaLHHiZ@rKCK1E#bg{>q024pJz_{2(EkS5}16W9B!XcqIk0J!Ma z`-tZd?;@_}obM+7UgCE=Z2`^q^ThSO=4Q(Ogt*=VK9{(BSVZ#Gd#O6U-T=oWxSq>q zDc=GdNv8bXNqM~|r{$6VUPC4Mn<=mNgw-Ct9wYxf%3phgZO^4F!8sV2ZloVeaE{s8sNePiYF z&j&8$)cd>FQeOW17lP}3*#*R}jnVTd%D;HP3TpYk93%g)l-K($H&g%XPOeXJUTW^J|2==J!tEQZK#d`9tE@5Z8O7H_a5aU3cr_0@Z` zw-9e9uJ>!S-QG=H-xE-L1982#ou!`75PyUndl~1E?*p$?{^uyK?-z7X&xxnna_T+# zBJt(K{WxTtxZbN$fAtZAGqFRq0k2e^FHw))qglx`KO(O8h--<@KCQBz6M;)R>peZ~ z#|6awIPiiPJ@RdxO6BaLp66*lDkJ|r7|A!oxJB*i<1u>fro6r%u!;5hG4Wsh!UEqR zF5kxy{rVn<`lWM;>w7ckR`Yv1aGA&TULnlV{JMzi{cFX~F!Al{6PG<;6?X#A-oA`3# zTHm$AcO0_vO_n6yOI+XMQT%q`s*|FI+#iE~4Y-UWz5l55_~XE(oC%J*I-@0#_#JV5 z4+_;czc-v&Ip6mH7d>^flWNLe6C?jo%IiJtD=B{;aI+rq#{0vRfB!R9P|NcZ;`%<{ zTdC(YXW8=T`*3PMX9BO(ZVM@|_j=z$J(mEN_)6~&qNU95${0O=Nj;k%w{jn+{5Of~ zz0F4AKQ;PKP8{XMS1(cigZ zPwt|BK8nh=Qr@4ZzAf^wD}8TJ=a+w@eD$kV!9nWzPvUdGYJo+>&%(xw*w1{x~tN)-##Nj-kt z)gPm0jPj2>XcefR{}ge3A9e-xKR{gHn^pX=82!)2;J=K)U!i_|?^oOHbujEo?Qlj6 z{+1a0V&Kv*`ktSTi?JB_4^w{Eq-_8lcelmJe>MjH0&wZC?c7IF`+PV?{xRTEPJQ1( z_YGbk{?FV$Q9C&S3M;iw6>!m`?<3Z*AD0l<`}Ez!M~QEuJsd;)V=?--#^Bq5OMRDl ze*0^{O?bvO5ZCvAn<&4GxV}%YiueR^ zeQzGwnct1TE4Am}Q(oUo!lU_pB}RU44Bmncm3ebD_xaUtTmoF`rSE}jeXk|1@AYW> z@JZ^?_YPFgJ(Pc${fIEY{2rye-rraG=P0l5scM{X0t{aIaUgB`Yc>1p9N{N0q-_u91`o{W+Icgp+c3&)&axjbjY;OE8Q4Ket|z@;7Z z{X#A0<-nzX^?j0KS>G!uukY1&68{V0kN(C2+J;++-!j9tKbEEDcO*JU^40gmv_0#I z>vtM7-pCL?>9;n$&f^auz&HVVM((#b!YcDy2YD%fHSJU9?oGh8Y!o$QcMQId`RaRQ zHB9tN;`%OBWQbGNPOE@t)Py(cLJ|eo_q{`4fXhO*Z&}{@6R={JogaS z_h~O8{xETWet(9zzGtNMo$di1*<7f}CEa72~be=cw-r@qgw{q@!u`8LYGFliO5{FO2C?~TE4q@Fg$ zy*e%)Ag=FqqMOX`hcSA7MtS{yiq`iyY>ZT@@0l_9c`g8V6ZiY&Xc)GXXWqALhE2@(EaLh- zjb+5&L0rGHvV!<3;=Vl>fuBGbms~@6{T|K9WPVchOjr48zy_?egC9Si1H6)+M#}5=Pu|B&3Ni8E$?;`;p@ZJ$?S^t=umAeHKM7I0|?{hmga`rCn<# z&y2zAfJ=S#z4Y@~o_65J^KX~DFGkNts7Jpu(#J$!050~a?}yJJzMJy;JsaI8`B9Ai z|A@g~-(btH-#2;__0I!dseRrSgQtK?J3n14@81md>-)pnK5L2Vd-$5~CgS?tqyd)a zF5s2Qb0`L%Vd5DkcF1wSrN4H6%Yu)QcR`GN7v=T)RceRBz@=ZBxb9v}J=aoRzc-}g z^fu!9Uca`(y++URi4iaU-vwOGo&9sh?@*7v&#&>)FNo`V)9+?^{tSj8_1*ei3#gxO zBd*_PSWNi=;Fa2WGzQ;9JR_f93L#bWePyD54 zZ2K%=JAaRO{|sv&7`EnjfVh5d0^1tqH|OoPocf(DjRWTcmwG+H_qtb8z5#fpc3Vz8 z54~aq*HZpUqv!a9zK^fI`U8~L@26?KJ{O~ZkI{qk0=Dy9>i>R>`~m9M?;&XWyzW9f zUi>)i6yVZd`dyGysQ)d%E7iAw^7Wa$LAj*uHO+t7+`+)64&n#>bQF_ zM*riK*YCUiIrW_Qj>_df1Gv;zzhi_j*!)_E>vy-VBt8_QXCvkHyA(Z?zcWVuA1SZj zS!tsDBgFMPd+N`B8l&eW;9{Tpod~suqp@HV{8Dcp>~!Fj`te-g(hmB4zN=ZD!5I0C z)IZ7jXp-_Dp&tEC-8XU{q85G z5A%B*coqM4$*+M|D*rLq=&OXE1zhTNBjeBGsH_gS)JwlVsD8ek_#MBtf@&xE82w|E zziZOU>-_bh82OuH@SQREH)8Pb$KVHI@Rxy0JLva(G#)q}2bY!FtqQobkAC-2{YDzN z=+W=>UG6w%%4F0Pa{Ma_z{`&nVjqBbHyi)nwDes@B zj}X`I(5+(SZi&(J4>9=Hs7Jq_+eAG-Ab$SS7FbXGgr$|sc{XsdEB~HGx`gg;Rl%S$719Y*}lU$bUmU!p%hnqONO zE9FOn(qPb693IM#L9%~1SX&(K%@u?G((ve5kQ?8S=o=m!DdtQ0{)N?*C4x1Dp+b-w z9nD=G#INY~xVc*C|BDg$T>Szpt z!mxMzVXq@De^=8!f4EB&S7sm@kB zbk?p+w|6Yc_O!I5ve{IKB&$=IOt7SIO;N< z%OXKjnAY}{PIXO*n=?*{ZCQFcS9Eq=(ix?t&P-2dOLMnVWmBN8?qzOk**0uPV`W?o zrjltie|x8!U#%3jy)(#mH+T0qMYCF(z5X%zHLvVyb;>|VbYsS8DqG?u&8kisP(m?&$H=OOhx@V=?UxU|SFhkv0;u zrReBtSy680rof%eE1gWNg3hjBWy&#itDv*1JIIzx8v})OHr?FfYB2>9yr*SZrEW`i zxjk%b;hjBQ-OWy!jX7mf==QG84!7xy?##+&>F2hN_LeE8BE0Nnoh^Cl#&M>(yUYg77`>$3u}XGKbGEys zY#=O7XS!N~&Yljp6RVp$FAK6=JsGzOb>(hlk;rtrj%#sGXKShrnYKF3QMV+E;Tv>e zm@GpJyBupf7{{=nc89a5E7O|7*m7iRE!2j-gd21i4Xqe6nf4_;-Kk)Sn4QC06)EfUji>1fVoQTFcC(lS3*7cpT`ZlgNM;N8<5 zsY|0glZ`8zFAbzCFtc=J0&k==2p28{9I0)YFRT$>g<&7zH2vpB>tbh_J@_H>o`>H3hp3fXlz(`HSS*D}ki(8vv8 za>_V4N4$2Js7W&^@nNn3)OIg3o-+%p zbf?T@JsE91ot>$UvNVe{O)ArlpfRr<4w$$C-obmu8yN%ba?& zG`n+MGAS`n(1QMP!!N>_R&TPi(wAh);l)Nrru7o1No%SN*otn2+wJNMS~|Mi=p@-- z_$Vx*QwcL2k8Sm(E^TpLqs@zLoHL;@?p3J|Pk%FG}P z=p$_wf}{_X=V=@Pt7TcLY^td9<#xgkVs>FCMAmYg(6!^tj#(VNDrO9ogJ12&jy;jN>7;Tb$n5IFrni9x5 zG&htjC2I!rgTtd&2g9RSGiOURwL#z_!QgOzVND?~3s19fT-=T&a1M*+!Im22XBV&X zI6hS9%VAZGY>{PfNnt1$ACh%%d7bJrW2jVoeN1bH1y0tsjOKHtd@zykgKpG0taJk^ zP#Pawltp`E^`9!3Y)#Ju`3-%=Fx$qK+-ONk*eG378Xg%=71D)tZ#F4i+}-X~D`oUF z7mI6ir2*7iDwE0gO_+XgTLC>Ltst#lu@%^0HC-chZKL@-x?NOBVS+wWDf>ItsHg zS$GvH6r|DXgUHxT%QUp+Wupoy`lN|uWJ?QKJ1p}?VJ#37+3`~t>d$Y;_XoW)A~B56 z)yBTMM${OmHhLXw5irWmUk5HbMz3uAr5Lqizk z(2y;S6x;jYo05G4Irx)NIL)#4zEn1ce^Q;<(xN~dXEK?r>CKH{e!x^v7+Tv7$6XzC zCxcX~E87^PSD*#8onQ~G)sa80g?CCOZF2^&e>j$~vzYiRGCW(tu~%g_zV%+Zv2(Ng2lD-OA|aej?~jJmMzqS2MC z7dsxsREfDrG}D`N);Db=!OA#xnK^?;RdD7VOgu8}*g=DK35N28wFB@$Q95Ir)y;i< z0mnNT{p%H10{02CRpCxd{8LONb=q;^# zikxmTN$1grIm`51dPQ$)}7Ifa6|jp zbcJ+d!g|wp@Zgmk(SM-@%L=W53M=A{PKRaMR)fNL!~A zP!!^al~2}joQ>wiwe+WoQScghkz}p7pwM5)9B!v@@zEogKeQ(smyX)Ch`LV41jU@i z1lz^j%wN_$NLo`fx6At`(#b_;J%LG$;Zq>Xmnq{FEf%6-wS0@Q-BKM!*1yjWhuMTP z2v!`K74gxwsem;=<8l#yu8YcLJO)Q49a3YOEhRI#V!B!oEL*BWJ5>xuunkg5)<%=J zOxq>^No5-XcPiCtB`DayT7WQB!S-W9PnLWW2YCcuY)@Q%mF~ z!sKotTh$<=K{7OvFAk5`rI7j!W2rWPlIAoW9oiXVOr}d^WQMi>Z%rLugH2u`tqwiL z$5DI|^p6b(*61afP78MF8x~tvPb#9(o@4t{=8gKkfqdV(_ChMvn^}SLAw;QmNSZOp zyv$&My+=C?1~GPR!>+zend{;zfI+TpAvNVi;yL;YgU}{9nel|jF$r7PMp$yngEp?o zY!Pc$DFWdFMeA(_m)Q_V8ha?n8rJGI3*NDT!WyIfj}K8{$6c5h+#zM6-U>5vaKQmmwk>Oy_D?}X>p_Qt)?piUx#N=9vh0Xi-n$#7d{dS5%98|_CI7xQR} zCTs{cFKG{w3v0`V43sI4g<~ za(tT})s>#H-mp^PhzVm9E-es#6ceqNEF8_RSr`Osv5h&B9~~Q(5U6x@FoCNy^qJA* zjT7bg$(wbIhLo8urua_dc&R#^y)jNibDbH`oYAArPg8R@6pHFpVF#d+)%Q&loR*A| zFVf{vI1V^E%tlzaUs&F`i^Iif%HLFyIY!OS)ZEF{)S{u?L;zERe+;Q)wb>NGU8sp< zQZ`76aDjnwZQ_hmuE|Q@+v#5R;Iyl3fK)c+NE9x{7uY$keND-NKqV*l&sE#yPyaznrj1Wyp@Zf-TR6r@GwO+ z%T-%cDY-fn!)xOjL+guGk!c%KMH}?vI$GWaPj?Vx%LfD_ldT*~pCNC^DiTxJe+8%{gN^YK7XeamqHpjx?G;Ee@fGG2VrX6dYID+M zW=S-~hC(SEnX&{%5;JOax({RLFgB8@&a`e3yS#DRzVXYgMP?%`Zc|wH6zc7+@YE2J zbA9MGjO(!#l)h_dH>WT0Ep9~3PF9EARu?N#r5b%t^=D?~RMB;4+OmMqIC?gb zxihw9vq^@2AuVT3Y>@I9Gh4!VojP1AZH)Ax*cgS-=Z--r9$G);fIS>jQ`fp7t3E4H z0HQwZ1T!p-W>sUR^=5}F#=_Jk>9iQmq0up+okoMp`{DWro!DvNVrg`DE;4_YxmxKB zGe&hKh!m#i;YoCTR@TE3$C={*Esu_RjdE=wMJu$2V_1LOIZ`bSnkt+mVbwQW#QfNn z$qr|OR9R=qC^Dy|(g0>V#7u9pZN=KtN~HRc&kNnYY!Ql%aNk%D9tD)Kim6%mAVj?y zX^rtWF(-)DU73;IH;gL`<`~6HH|l*&Tbh*t2Bx?axpINSiefL_fJp0{Y@Ca~j*#-1 zX_IBJ&PDd#`ptQ)ZkSEAifBKg4TlD0`w?!g_86pWO(=Ov~@#L9mw4r zpkpIQV@pQ z=?2&X+?7O@x@n?j9Styb52kJWO)b;+?ipQJdgCPArU4N3*-zl)d>FnhbqwG)T|YR(f1ETl;hAvdaQeFH@}0C)*uPZ}*@EI|*aB zn)Z7(5KNwB10rf@+^4on#Ieq(M1axtr`ckbKlAuf0WMMq>WZ!So5li(2bPs8EKO-^6<9hjUipu zfeULI?C8=}wP^{tDyBXuEWDj1WQW#VypbrskS?aP)pnh%y9#EpmZ~vQCBwFkVqf&WAoi^=!6w8b5nX4+xC@%yRd>dejULK zZib2+?E0H&QPJw#91B>D`lPYl$qElP*-LTvL&S(1vZw3v-TDe&UM@#{{t`L zpt{{rJRjE5I#+veoXQmS?uZ$CW~$dqgYL9ZcGpX*7{%_cT}MM!QPXZ=pc>xU!OaiV z4AaQG?w-tK^II@XOQ^_eleD`$L|@ zhnahsQdiTI(PDhcs?lU)2ddmkEe@}}+AKfyfDpURViouwMy=|T0g|k5K>+KxKf8Jp z-yUVh_=$l7Yba%m;zToNi)Po7&EJjD-4L6WB1wn1plRDy)AvG~9jXAn3(rx7}$*b)+GT>(SuNj1JdNMpeN*R2*4gDJD5& zOO1u$F>~651}l_&2P`sW6MI>B7mgY`YVCpqnxjifrWedela4TToYEjpG!WjKF;^vG zZs&UUW_o3#FlsH0K%q6pdxL5nd40Q7a87rpRYbUXB5kWow7q zA9^il$|PWWON>Qww3DIKBpM6M-<-C~jr6FYY*->EDP=oXRwA@kUZOF5LS1GX0pkT% zO2bLj8REL8cD+AdfUx`Wk%`GEPk5Iu>~_;0jpkAcHvFMq63B^pC?VBzPf{u!^f`_< z@~`6cb%k1{trn4KMGp}M{n8q?r@YfSDV=zb(ASzu^P(firAcTOLY0m~%Suw;Ci`iT z<1|U#HK(b3c~m1sFgs78smu=`&)5K83oe@u<-YH~f6>`oa@MUtDN*Fu%3YJLQ<9Bk zyZiPotL*dH-j|yYy6BX0hb~GtVvTW{wyHN5|0{)6VLOKou{Jy2i*Aq3;T_K*Q>Jq} z!ijREx7SE7nX<`KpL2X=cB%?fwy2hxS6?{rIdQoWiy5w_rA*z-t*h7r{OJ?vGGCNKWu5i%Sv(r&+3PM(YM;Nm`^N++uPxkXiPxo3^jTUmd#)ZjU;ET zdz4+4$n{+{UKlOAEFt33kuvL|n60@{d79HB-eUesVaUnaQF2BQHn z%^N3XhNjN2_Hfnv%viW zmpcu)krBixG8)_wEEl_^f#76umELaG>SZ6pu%}AlC0H-kcDxGX5uFTm3nDz>GC{D0 z2ga4_!j!H&ZkJK5zwAZ7DZ#>2Cu@pttXf)bsynTCU|?*q!FIgu@Yw#g4&1g+pB_1z zNh%uHX0;YGGt$~4VJD1|DHqcM+(S65Lf7uDYW(n7rXaU_6N!amR}YqQy`ZI0O9%8X zqkkl^a0sthEL=M@z7Tg^aHptrwFC8z7mEFFE%Xz@6|A?)hkuOtKyGXxv9SN@A!K1G zPL&M@`_*H3Z?xkDtuGk^{a7i)-VzR-CCb19` zp1e85Zz276$Z1F{pJ19vC3BCke<(ejxDtM@Ngoqu6KQ6lJ<|Iyl44- zs+;~&rdK-8lb_{1%Xiz{^c$I8X_Y7Mr@sXJgRj0itmWUz^h!6p)+(4yJ%0Ifz>mTI zH2vJySwW>w^Mi9zMrk`uuiw)w0Fx{={h>xY;HUIX{g9f4jSl{4$W{3FRk!>Dmb;bo zF4jlOulXpI@4da^q+eFF>6K2}N7KflO3P0;@mKgm+En!&#SgP7ol|2aHGfU7^}m_v zmsqjH+|SrQls?1{{!02&@jpNP-vX0%RsG?AXJ9)({Pg>t_Q{r2{}y^H{^zEzVmhTw z(z(u$#lbhB?e9(yH~s4SEv{6{OYyM$ic5BGdj0Q~DE+{4O=+p-tLRQIz5e&Sl+t`v z-1JrWfIl8!dhI{`@9!#|^ahNd|86h+B)`9?^eHiX=jW+QJOZB7U-RF3x;!NkN^h+) zkKsSsZVEhwKiu>ezQd+h`dJkuwcH4N0e{N3A~{SFll&gDQop{MztWeG_VrHsy-crE z6E_NORwLFSE_zOwP-qhJ`d>y)B5XwXI8&I z-RKQ~CQV3M&HSr9T+^T9;Zj%mxm0b}a_qlP@V}=x(rlmK1oA)eKP|u3ONysux03eX hWp~;fzOd9qqkI*W?|W8C|GO7#`o&sMCh#5;{|7L)KA`{r literal 0 HcmV?d00001 diff --git a/a5_mpi_deadlock/makefile b/a5_mpi_deadlock/makefile new file mode 100644 index 0000000..e56bc19 --- /dev/null +++ b/a5_mpi_deadlock/makefile @@ -0,0 +1,5 @@ +all: a5_deadlock.cpp + mpicxx a5_deadlock.cpp -std=c++17 -o deadlock + +run: all + mpirun -np 3 ./deadlock \ No newline at end of file diff --git a/a5_mpi_deadlock/test.h b/a5_mpi_deadlock/test.h new file mode 100644 index 0000000..232be71 --- /dev/null +++ b/a5_mpi_deadlock/test.h @@ -0,0 +1,290 @@ +/** test.h, an extremly simple test framework. + * Version 1.4 + * Copyright (C) 2022-2023 Tobias Kreilos, Offenburg University of Applied + * Sciences + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * + * The framework defines a function check(a,b) that can be called with + * parameters of different types. The function asserts + * that the two paramters are equal (within a certain, predefined range for + * floating point numbers) and prints the result of the comparison on the + * command line. Additionally a summary of all tests is printed at the end of + * the program. + * Additionally there is TEST macro, which you can place outside main to group + * tests together. Code in the macro is automatically executed at the beginning + * of the program. + * The file also defines a class InstanceCount, that can be used to + * count how many instances of an object are still alive at the end of a + * program. To use it, derive your class from InstanceCount and the + * message is automatically printed at the end of the program. + */ + +#ifndef VERY_SIMPLE_TEST_H + #define VERY_SIMPLE_TEST_H + + #include + #include + #include + #include + + /** Simple macro to execute the code that follows the macro (without call from + * main) + * + * Define a class, that is directly instantiated + * and contains the test code in the constructor. + * + * Usage: + * TEST(MyTest) + * { + * // test code + * } + */ + #define TEST(name) \ + struct _TestClass##name { \ + _TestClass##name(); \ + } _TestClass##name##Instance; \ + _TestClass##name::_TestClass##name() + +// Use a namespace to hide implementation details +namespace Test::Detail { +/** + * Make it possible to print the underlying value of class enums with ostream + * + * The expression typename std::enable_if::value, + * std::ostream>::type decays to ostream if the type T is an enum. Otherwise, + * the function is not generated. + */ +template +std::ostream& operator<<( + typename std::enable_if::value, std::ostream>::type& stream, + const T& e) { + return stream << static_cast::type>(e); +} + +/** + * Convert anything to a string. + */ +template +std::string toString(const T& t) { + std::ostringstream ss; + ss << t; + return "\"" + ss.str() + "\""; +} + +/** + * Convert bools to string "true" or "false" instead of 0 and 1 + */ +template <> +inline std::string toString(const bool& b) { + return b ? "\"true\"" : "\"false\""; +} + +/** + * Comparison function for different types + */ +template +bool isEqual(const T& t1, const T& t2) { + return t1 == t2; +} + +/** + * Double values are equal if they differ no more than 1e-12 + */ +template <> +inline bool isEqual(const double& expectedValue, + const double& actualValue) { + const double epsilon = 1e-12; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * Float values are equal if they differ no more than 1e-6 + */ +template <> +inline bool isEqual(const float& expectedValue, + const float& actualValue) { + const double epsilon = 1e-6; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * This class realizes some basics of the test framework. + * Test summary is printed in the destructor. + * Apart from that, the class implements counting of total and failed tests, + * comparison of floating point numbers within sensible boundaries and prints + * the result of each test on the command line. + */ +class Test { + public: + /** + * Test class is a Singleton + */ + static Test& instance() { + static Test test; + return test; + } + + /** + * the main entry point for tests. Test two values for equality and output the + * result. + */ + template + bool check(const T& expectedValue, const T& actualValue) { + bool testResult = isEqual(expectedValue, actualValue); + if (testResult == true) { + registerPassingTest(); + #pragma omp critical + std::cout << "Test successful! Expected value == actual value (=" + << toString(expectedValue) << ")" << std::endl; + } else { + registerFailingTest(); + #pragma omp critical + std::cout << "Error in test: expected value " << toString(expectedValue) + << ", but actual value was " << toString(actualValue) + << std::endl; + } + + return testResult; + } + + private: + /** + * On destruction, print a summary of all tests. + */ + ~Test() { + std::cout << "\n--------------------------------------" << std::endl; + std::cout << "Test summary:" << std::endl; + std::cout << "Executed tests: " << numTests_ << std::endl; + std::cout << "Failed tests: " << numFailedTests_ << std::endl; + } + + void registerPassingTest() { numTests_++; } + + void registerFailingTest() { + numTests_++; + numFailedTests_++; + } + + /** + * For statistics + */ + std::atomic numTests_ = 0; + + /** + * For statistics + */ + std::atomic numFailedTests_ = 0; +}; + +template +class InstanceCounterHelper { + public: + ~InstanceCounterHelper() { + std::cout << "The remaining number of objects of type " << typeid(T).name() + << " at the end of the program is " << count; + if (count > 0) + std::cout << " (NOT zero!)"; + std::cout << "\nThe total number of objects created was " << total + << std::endl; + } + + void increment() { + count++; + total++; + } + + void decrement() { count--; } + + private: + std::atomic count = 0; + std::atomic total = 0; +}; + +} // namespace Test::Detail + +/** + * Count the instances of a class T. + * Result gets printed automatically at the end of the program. + * To use it, inherit T from InstanceCounter, e.g. + * class MyClass : InstanceCounter + */ +template +class InstanceCounter { + public: + InstanceCounter() { counter().increment(); } + + InstanceCounter(const InstanceCounter&) { counter().increment(); } + + InstanceCounter(const InstanceCounter&&) { counter().increment(); } + + virtual ~InstanceCounter() { counter().decrement(); } + + Test::Detail::InstanceCounterHelper& counter() { + static Test::Detail::InstanceCounterHelper c; + return c; + } +}; + +/** + * Check if the expected value is equal to the actual value. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +template +void check(const T1& actualValue, const T2& expectedValue) { + const T1& expectedValueCasted{ + expectedValue}; // allows conversion in general, but avoids narrowing + // conversion + Test::Detail::Test::instance().check(expectedValueCasted, actualValue); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const int& actualValue, const double& expectedValue) { + Test::Detail::Test::instance().check(expectedValue, + static_cast(actualValue)); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const double& actualValue, const int& expectedValue) { + Test::Detail::Test::instance().check(static_cast(expectedValue), + actualValue); +} + +/** + * Check if the entered value is true. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +inline void check(bool a) { + Test::Detail::Test::instance().check(true, a); +} + +#endif // VERY_SIMPLE_TEST_H + +/** + * V1.0: Creation of franework + * V1.1: make check(bool) inline, automatically convert expected value type to + * actual value type + * V1.2: added possibilty to count constructions and destructions of some type + * V1.3: tweaks on check for int and double types + * V1.4: Adding thread safety in OpenMP programs (not general thread safety, as + * OpenMP and std::thread might not play along) + */ \ No newline at end of file diff --git a/a6_send_column/.DS_Store b/a6_send_column/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 +#include +#include +#include +#include "test.h" +#include "matrix.h" + +using namespace HPC; + +// For simplicity, store rank in a global variable +int rank; + +// **************************************************** +// TODO: modify this function to use a custom data +// type instead of sending each entry separately. +// **************************************************** + +// Send one column of the matrix from rank 0 to rank 1 +void sendColumn(Matrix& m, int col) { + + // Store MPI_Reqeuests + std::vector req(m.dim1()); + + // Send/receive each entry of the column separately + for (int i=0; i stat(m.dim1()); + MPI_Waitall(req.size(), req.data(), stat.data()); +} + +// Do not change anything below this line! +// You may use the tests below to verify that your +// program works correctly. +// **************************************************** + +// Create a matrix with some contents +Matrix createMatrix(int n) { + Matrix m(n); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + m(i, j) = 2 * i + i * j + 1; + } + } + return m; +} + +void sendColumnTest() { + int n = 3; + + Matrix m(n); + if (rank == 0) { + m = createMatrix(n); + } + + sendColumn(m, 1); + + if (rank == 0) { + check(m(0, 0), 1); + check(m(0, 1), 1); + check(m(0, 2), 1); + check(m(1, 0), 3); + check(m(1, 1), 4); + check(m(1, 2), 5); + check(m(2, 0), 5); + check(m(2, 1), 7); + check(m(2, 2), 9); + } + + if (rank == 1) { + check(m(0, 0), 0); + check(m(0, 1), 1); + check(m(0, 2), 0); + check(m(1, 0), 0); + check(m(1, 1), 4); + check(m(1, 2), 0); + check(m(2, 0), 0); + check(m(2, 1), 7); + check(m(2, 2), 0); + } +} + + +int main(int argc, char* argv[]) { + MPI_Init(&argc, &argv); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + sendColumnTest(); + + MPI_Finalize(); +} \ No newline at end of file diff --git a/a6_send_column/makefile b/a6_send_column/makefile new file mode 100644 index 0000000..8e69866 --- /dev/null +++ b/a6_send_column/makefile @@ -0,0 +1,5 @@ +build: a6_send_column.cpp test.h matrix.h + mpicxx -std=c++17 a6_send_column.cpp -o send-column + +run: build + mpirun -np 2 -x OMP_NUM_THREADS=2 ./send-column \ No newline at end of file diff --git a/a6_send_column/matrix.h b/a6_send_column/matrix.h new file mode 100644 index 0000000..b8caacc --- /dev/null +++ b/a6_send_column/matrix.h @@ -0,0 +1,324 @@ +/** + * matrix.h a very simplistic class for m times n matrices. + */ + +#ifndef MATRIX_H +#define MATRIX_H + +#include +#include +#include +#include + +namespace HPC { + +// A very simplistic vector class for vectors of size n +class Vector { + public: + // constructors + Vector(int n) : n_(n), data_(n_, 0) {} + Vector(const Vector& other) = default; + Vector(Vector&& other) = default; + ~Vector() = default; + + // assignment operators + Vector& operator=(const Vector& other) = default; + Vector& operator=(Vector&& other) = default; + + // element access + double& operator()(int i) { return data_[i]; } + const double& operator()(int i) const { return data_[i]; } + + // getter functions for the dimensions + int dim() const { return n_; } + + // comparison operators + bool operator==(const Vector& b) { return (data_ == b.data_); } + bool operator!=(const Vector& b) { return (data_ != b.data_); } + + // addition, substraction, multiplication with scalars + Vector& operator+=(const Vector& b) { + for (int i = 0; i < n_; ++i) { + operator()(i) += b(i); + } + return *this; + } + + Vector& operator-=(const Vector& b) { + for (int i = 0; i < n_; ++i) { + operator()(i) -= b(i); + } + return *this; + } + + Vector& operator*=(double x) { + for (int i = 0; i < n_; ++i) { + operator()(i) *= x; + } + return *this; + } + + Vector& operator/=(double x) { + for (int i = 0; i < n_; ++i) { + operator()(i) /= x; + } + + return *this; + } + + double dot(const Vector& other) const { + double sum = 0; + for (int i = 0; i < n_; ++i) { + sum += operator()(i) * other(i); + } + return sum; + } + + private: + int n_; // vector dimension + std::vector data_; // the vectors entries +}; + +inline double dot(const Vector& v1, const Vector& v2) { + return v1.dot(v2); +} + +// Print the vector as a table +inline std::ostream& operator<<(std::ostream& os, const Vector& a) { + const int width = 10; + const int precision = 4; + + const auto originalPrecision = os.precision(); + os << std::setprecision(precision); + + for (int i = 0; i < a.dim(); ++i) { + os << std::setw(width) << a(i) << " "; + } + + os << "\n"; + + os << std::setprecision(originalPrecision); + return os; +} + +// A very simple class for m times n matrices +class Matrix { + public: + // constructors + Matrix() : Matrix (0, 0) {} + Matrix(int m, int n) : m_(m), n_(n), data_(m_ * n_, 0) {} + Matrix(std::pair dim) : Matrix(dim.first, dim.second) {} + Matrix(int n) : Matrix(n, n) {} + Matrix(const Matrix& other) = default; + Matrix(Matrix&& other) = default; + ~Matrix() = default; + + // assignment operators + Matrix& operator=(const Matrix& other) = default; + Matrix& operator=(Matrix&& other) = default; + + // element access + double& operator()(int i, int j) { return data_[i * n_ + j]; } + const double& operator()(int i, int j) const { return data_[i * n_ + j]; } + + // getter functions for the dimensions + std::pair dim() const { return std::pair(m_, n_); } + int dim1() const { return m_; } + int dim2() const { return n_; } + int numEntries() const { return data_.size(); } + + // comparison operators + bool operator==(const Matrix& b) { return (data_ == b.data_); } + bool operator!=(const Matrix& b) { return (data_ != b.data_); } + + // addition, substraction, multiplication with scalars + Matrix& operator+=(const Matrix& b) { + for (int i = 0; i < m_; ++i) { + for (int j = 0; j < n_; ++j) { + operator()(i, j) += b(i, j); + } + } + return *this; + } + + Matrix& operator-=(const Matrix& b) { + for (int i = 0; i < m_; ++i) { + for (int j = 0; j < n_; ++j) { + operator()(i, j) -= b(i, j); + } + } + return *this; + } + + Matrix& operator*=(double x) { + for (int i = 0; i < m_; ++i) { + for (int j = 0; j < n_; ++j) { + operator()(i, j) *= x; + } + } + return *this; + } + + Matrix& operator/=(double x) { + for (int i = 0; i < m_; ++i) { + for (int j = 0; j < n_; ++j) { + operator()(i, j) /= x; + } + } + return *this; + } + + public: + int m_; // first dimension + int n_; // second dimension + std::vector data_; // the matrix' entries +}; + +// Print the matrix as a table +inline std::ostream& operator<<(std::ostream& os, const Matrix& a) { + const int width = 10; + const int precision = 4; + + const auto originalPrecision = os.precision(); + os << std::setprecision(precision); + + for (int i = 0; i < a.dim1(); ++i) { + for (int j = 0; j < a.dim2(); ++j) { + os << std::setw(width) << a(i, j) << " "; + } + if (i != a.dim1() - 1) + os << "\n"; + } + + os << std::setprecision(originalPrecision); + return os; +} + +inline Matrix operator*(const Matrix& a, const Matrix& b) { + if (a.dim2() == b.dim1()) { + int m = a.dim1(); + int n = a.dim2(); + int p = b.dim2(); + Matrix c(m, p); + for (int i = 0; i < m; ++i) { + for (int j = 0; j < p; ++j) { + for (int k = 0; k < n; ++k) { + c(i, j) += a(i, k) * b(k, j); + } + } + } + return c; + } else { + return Matrix(0, 0); + } +} + +inline bool equalWithinRange(const Matrix& a, const Matrix& b, double eps = 1e-12) { + if (a.dim2() != b.dim1()) + return false; + + int m = a.dim1(); + int n = a.dim2(); + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (fabs(a(i, j) - b(i, j)) > eps) { + return false; + } + } + } + + return true; +} + +// A very simple class for "3D-Matrices" (tensors) with dimension l x m x n +class Matrix3D { + public: + // constructors + Matrix3D(int l, int m, int n) : l_(l), m_(m), n_(n), data_(l) { + for (int i = 0; i < l_; ++i) { + data_[i] = std::vector>(m_); + for (int j = 0; j < m_; ++j) { + data_[i][j] = std::vector(n_, 0); + } + } + } + Matrix3D(int n) : Matrix3D(n, n, n) {} + Matrix3D(const Matrix3D& other) = default; + Matrix3D(Matrix3D&& other) = default; + ~Matrix3D() = default; + + // assignment operators + Matrix3D& operator=(const Matrix3D& other) = default; + Matrix3D& operator=(Matrix3D&& other) = default; + + // element access + double& operator()(int i, int j, int k) { return data_[i][j][k]; } + const double& operator()(int i, int j, int k) const { return data_[i][j][k]; } + + // getter functions for the dimensions + int dim1() const { return l_; } + int dim2() const { return m_; } + int dim3() const { return n_; } + + // comparison operators + bool operator==(const Matrix3D& b) { return (data_ == b.data_); } + bool operator!=(const Matrix3D& b) { return (data_ != b.data_); } + + // addition + Matrix3D& operator+=(const Matrix3D& b) { + for (int i = 0; i < l_; ++i) { + for (int j = 0; j < m_; ++j) { + for (int k = 0; k < n_; ++k) { + operator()(i, j, k) += b(i, j, k); + } + } + } + return *this; + } + + // substraction + Matrix3D& operator-=(const Matrix3D& b) { + for (int i = 0; i < l_; ++i) { + for (int j = 0; j < m_; ++j) { + for (int k = 0; k < n_; ++k) { + operator()(i, j, k) -= b(i, j, k); + } + } + } + return *this; + } + + // scalar multiplication + Matrix3D& operator*=(double x) { + for (int i = 0; i < l_; ++i) { + for (int j = 0; j < m_; ++j) { + for (int k = 0; k < n_; ++k) { + operator()(i, j, k) *= x; + } + } + } + return *this; + } + + // scalar division + Matrix3D& operator/=(double x) { + for (int i = 0; i < l_; ++i) { + for (int j = 0; j < m_; ++j) { + for (int k = 0; k < n_; ++k) { + operator()(i, j, k) /= x; + } + } + } + return *this; + } + + private: + int l_; // first dimension + int m_; // second dimension + int n_; // third dimension + std::vector>> data_; // the tensors' entries +}; + +} // namespace HPC + +#endif // MATRIX_H \ No newline at end of file diff --git a/a6_send_column/send-column b/a6_send_column/send-column new file mode 100644 index 0000000000000000000000000000000000000000..fac05944d93845fe82ee652c42299f226d7ddf28 GIT binary patch literal 142568 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%0D!PTCp zJGed{D#O9`xo;r{*XL1YIk-MIbAf~F^P_VeT%QkE=-~R?;W7u;=SEdKxIPcD#=-S@ z!L<&q&&_ObaDA@aS_jwXSvNYkK1Y6?gX?p$njBo8BiZcW`W)~U2iNEHwL7>z&$PqA z^|^1I4zAB9jX1da!7c|^KiKWy>IVa>9DDweiR%&^T>W68gR37*a&Yy7$quf5FvY>u z52iY}`oVMuS3j8H;OYlM4z7MM%fZzTE^u)5gSif_ez4HN)en|Axcb3r2UkB>IWMgT>aoW2UkDXW6XgR39xaB%g5 zoer*kFyi3q2fG|x{b09)s~-$p9;^TA2NN7z{a~Vls~=2qaP@=94z7MM#lh7NraHL# z!E^^#KbYa*>IXv(u6{7f!PO5gaB%g5xel&=u+YKP50*K&`oU@kS3g+e;OYl!9bElj zgM+IdT_b1XH^(dY0cI{3kTO#UPXpC@>-gX{DAQXE{L zOPK25`d)zy2iNDHh8$eqi;(5u`W(Oo4z7MP*TL0q7CN~4%`ykq^<%YztKY0~aP^zD z4z7N)!NJvUu61zrn~e^xesi6JtKV#LaP^zb4z7N)#lh8YwmG=^&2|S@zuDp7>Nh(b zT>WOm!PReeIk@`GZUNjg0T>WN) zgR9?M>)`4)8y#Hz<~j#gzuDyA>NlGmT>WN?gR9?cb8z*W?GCPfv%|sFZ+1Gk`pt-g ztKaN$aP^zr4z7MPAkRltd#-*n!NJvUCOWwK%_Ij`znSdd>NisyT>WOMgR9?6cX0Ka z84j*~GvwgvH?tgE{pJD(SHGF-;OaLE9bElpwSyZoT%Q|W>)`qxjs^!W*~;!W zu-3u#JwuHS-YNOlIk>(ztI5GzB!9Dm>vL>d99-Yy(&pg$euZ`iA1TlO?R0Q`4t2!A z_5BT94zBMX>UMCIPe9&RrTVP$NpSE^>2IQgt9+6iyhZXSJGjax#lclRsSd94Nq6vK zd0ueH!S(sVSq`qxtzO{Z`d)`z2UqzNI=ITG%)wPY)ef%msc~?XPpyNid>R~F<+Iko zRX*z+yj!jdGFzrNYQ^*t+X4zBM(X?JjaZ&QbZ>w6PB9bDhb6>)HV?@^b7>w8wZ z9bBJt9SHX^@wJ)e0ex>@qJ!)EA(9+ipPQfT;QIc96bIM$6Qnx0K9@Y*!S%g>84j-R zNeVf*zRxVn!S($;3mjbEhnMT%`o5(?2iNzml{vV+@1)wn^*PtI4&Etx)#%_F|F3g! zjsKe*T;u;{2iN$&#lbcHZ*y>s|Jxm0);yaH#oS)`D-0q-&0}9bDi4(c<9x-ne!L*Z076IJmw)veUu!y_*pS*Z1mmIk>(Lquaqd-(q@99-Y$m+0X7ewri)*Y}YmJGj2jEXBd^|G>yO)xj_8Giue*j2`h^Aksv-JmvhagU7CzZ*;iIG311%PwVc~5SzTU#y zE&Oo{@38PEExgmhU$F3Y(NALWHVY5Pd${mpj==2}|N0(7!V7I&-pfPyD>i zKElFlEqqT4Z?N!@7QWWPM_G8Ih3{qI>nvQ~6-TF=EL`6eM|iV^>pRZ~Z?W)w36M{l zh5tf9r0o`tw{d!(4h!GkLj!?M3;(5sM=V_5`9!C?EIirD-)-RsS$N<#QT@lO?7UBc zg&*RfuzwbgSE+fQBn!u@$h=Rog{OFEAdq6=c!ibsNwsjiO3M4BTe!X}i%w=(_>o?2 z=)Z;QySV6dmW7Y8@-MJ({AA4g&cY{Kc$0;D@9x6+W(z;g%HLw)$6I)th3h-&=ybb< zpJ?Upu<$7s-f7`eEj(i3(=5Ep!u1`Bbh_KZgI4~)wNd?_Vc`iDKGVVzEj(o5NftiK z!jmohBnwZm@YxogYT@P3lCX%nT2Or_<0t- zz{1NdJlDc2EWFUdD=oat!mBL2+QL^@c#VahZ{f8TUTxtG7Ji|HueI=tEWFXeFShV? z7QWKLn=Jg-7T#>(msog!V4|@dJ8YJ@Ea_=+QM(N@EQwWYvHvP{yPhAu<)BK ze65AwY~hU-ev5^#v+!Fjyvf3ExA0~QZ?y0h3%|p{+bsM}3vajZyDYrJ!tb{5P7A-s z!Xp-buZ4G6`0p*e+rrmbctFMpoOPc_#Z7i z)xw)BJl(?oWZ@YW{*Z--Ec{^$&$94GEPR25KWgE*7XFxp7g~6;g_l|Q6Bb@=;ZIq3 zjfMZ&!fP%3X$x<#@MkQ1t%bK(c%y|sYvJoGe4~XoS@?4n-fZE|TllW8yCkqn0=p!z zO9HzjuuB5FB(O^YyCkqn0=p!zO9Hzj@c*I&LaV+=2-OXE;dNX>T-{RHx2HX{>iLA| zO1Njj%Q)3@$R_+9c0dNc5zRZ{pLX`(bI3-bX;bmjcBVHF-IwSVrXM1@AJI)ruP3@c z(Tz;sN%R1s8<@U{=z&DnFnul2v|aOQ8PjWsP9Qp$>C1>7M06I@7ZN>~=nSUIiQb*) zRHl~^J%s3FrWX-Cl;}jJ7ZSY((E+CC57COVnu zPNp{!eIU{8Om851G|??gKScCFL^m19M8Npv#Pi-47^24#-NN)(qQ?>4#Ps1rk0-j3>Cr?_Ai9C+eTkk( zbPdxZh@M1r8PkJ_PA58->AplyCOV7hZLfkphUg5YzajcqqEngvjOgQtPGfao#;lU z?<6`%bOX~j5j}(G8m6x$dM43jOs^q2M076Gmk~XS=q#o$B>E(xGng(XdN$FiOfMsP z4$;X>FCsdV=tQO$5`8k!0jB2?eG1WC+j#tm&LX;#>8V7YN_0EZ#}GZ2=oY5O5}i$S z6VrziJ&)){rbiP!pXdgr_a*u?qHCBQLGHTk6PbRO=yQk;F#RUcIYf8;hsU4jT%tRf-bi#F(d|rc zAbJteElfW|bUx8dOs^+;G0}}o-$`@<(G5)BMD!A(YnZ;4=%qxLF};RpazLNvGJP4* zMMP&YeIe1yh|XZToal3jPGx!-(Zxh3GrfrDia zqN|C{Wx6lX7Z9Dr^tM+(Ur2NY)87z%5z(nke@67hL?<)-A<-*|PGtIBqJK?vfay1h zzJ%zmfAjbgT|;yy(;JDtl<0P*HxPXp(Jf3rMD!}6o0wit^yNf1GJPk}tBG!4`X-{U zAi9R>Yl*&+=rX3)5M4`jF4LD09VR-9=?jToLv#ky!ZfAM}(RUKv!t_H# z-$is2)9Z=8o9ITS?zL4nk zL}xHvPV{|5r!u{a==+IIW_l6P4-lQm^g^N^Bs#$KT%!L-bl0~${zNws-O2P+qW?s6 zJJZJy{SeVDOphh{VWOLuKAh-Bh;C$hG|`U|-N5v|L_bD!4bvltew^qsrnfy82rMD} z<4@X`v%+tj6AFJATJ=#^*1TX^OWI4J`r}_6h@gGc2gv&Ek}<;$py_7fcbwb$5@P-O z0q;^Wxw~={!vCj{k;4B$J)Of2sG+>iNs9b`;`|fVQ*zjkp>S8|*{@CvJ=@(c)aS*} z8$VW#LR}hr+FqnX>|caPSKy zLrg|O^_iV@^CNY^FJ^_?LiIyOVUj%E2Q8z-69{WU^}$Xmy+2%B-GJTE?CO@PS)sbK zQHra68hOEHYvewJ8c2DaR3Gffno~cttt}9k(o7kipejS*ce&~ajW1SDiJ#A{8_@iE zAh4>tPu0wLb)Me4&dO+=RIyj*abS?@4AswX0n;6!@L!0l@8FuW4=+JY8_-PmOV5#z zz-cOE6bV;-R5$-4$R%QmJcSslHMpH>|9pOHu$#YR)CITKj||}Nus;U7`?RlW>9b_m z*Dc=+I~>RGU`O5j?z+tFT*+(H?yacgn_-E;4xA0FLW#Oy_p!l`E^b~G+>YHA$^ zCKxfyWQQ*Of^1c5@QZ#(>gInDs$Wg(Hp5d2s;e7t)oU#Nap+JDa+p(T$b?SUNPg+} zjVM6bh?fa7=sVZO@+a&s2-)if9Lp6BTfGs((;B>+2gzjIK)v?H_I0Wuyb%@N9SXk~ z3cnL+L?GH4TxXY{?_P;py%OuB1YSLc68GCB`r0MV^-A0?C640~O(YLt-*ClMpbwM1 z5=~NKFqe3wzg2>{(h|FQB_5Fy9hicx!R7&03Fb;myp0-7&oPO(=l61nr|c3a9C(_F zk!0`kN<1YcD!D|~kkiqhXhndt8H+d!6rNp^h zVzXU>zI#eZs~yADW+^e5OLX){E>!{MirGXzCVC|>;dyv=;}To!Hkd0d(cdevMM}Iq zm3rQ3m!R+7fWCdXFjZ_z` z#DGuWuBiUH0rxy*>VE*WHkcZprt;6wctmc7?K+!{F}G;G!X|fzU@k4He$?OK*a-}U z6C2Kcu0iZ~qUNP8yher)S)NWr)dURsrl;p6$xC&zI7a<8S`$}T2LXk%61-Y9XI-!> zRF`Pl%)-_mF=W~m{z7#b2{Ak#_=lb6FoU=Q3|-y8P~B+>q3{$Or-G9i z= za-%)da`mHbjh;1`S|ejo4yqA%Ora~WV>-d&J6h`p2rH#ct#u3b;Q^{M+Mf z49J;gkbiA8DR#n0@+WV4lnhiPPx~U{7%>M3#S$%!2!{lSTo{DFn-_bNBu6^ z7uBjOevVd!zl_RQB`O+m&SzwYM}3XumFR6RU9BJWM)a((Zsqf21r zmV0bg*uJVItRMBsUEWDwDb|mAHkxavN3GzCsEm5sRf*7HxjV!2x1j0HP+ceiKCQbh z2>1OOjQn+=Po9n7NQ01&} zH>!LVv(u@vnU)b=u#S~OhE-!IoY_7n9Bi|zM0wCv?Ovs~^sY4Dt5o^?*|qKk@j0vx z?NzJOd$8K>u-a~S|Jr}_s`Cl>eA9j9^k>%jT5moJ{Ob&b=Oe1g6fQF`;N%hKgRbIe zrwLQt>Gj^dYD?x3BCUX*hRSFNwCZ!Le1hA(=!Wu{*dRO~dC99GKi0&dy38)<7)25v zeC$Op82g%w1N(oI2<*_Y8~oDq7Nap^=2sDeLA4vSJkE|wGV_K z;?y3&8kz^Kjuzgnk$b!vA;mJ5eq9hb-Ux-a;dJ0583J3IyfFx8egqZmK>tYrFdQLt zm{fKpR@qpmSJ5bOhkL3LGsDkf(o&m8klLD#+pGJi?LaZaaZshYDq~7e=mwZbYQ)n9 zZzRwiSd7fhSXQbboy(04h?PS#ZPZMtu9~zaX-?gIYO2k%cysE5-BYqouUnaN zdQVkyX1E;yq9v(>XYq!9(yIiGlw%_6 zGQTwj@<$A}HDWxnn0X{f;R`DTGq~f7>GM`_yf|%*><1Qmji6l*ma%3TW^5>H+1~zE z*4v5tSB&j8H4;~1i5Gtu!+ESun-(LXdI~yd5sfW|*CXdaHc?h}Tuez)9Ldd_RZ`@H zm?B=^HR2Io5}N8>$2xrRHjq{8k=g=jrpXl=yWv{6vV--!@VH}%iIK3 zpmPfn3`?z%+pxGp`MS(aJhRzK&kFx_O1Op+`r}lwAUHb#?{m-?pqiD{Nk;D724|>%eQsswDY71vR5A)s( zooZoSGCI{v3UiXF9YIZE-Taois^^iRjfMa-ByuX+@l3{A&MgBY0|C9b2 zxBWBzZ|$G7)9r7AlEv#kFI{5BUrmmQEnv|g9Av1kCJTeH<1ll3T~#N7UbCcpAAO5& zX{C-{<;-<6DDYh|Mb-AR4~It%lV8mweKCHA{cTnPTz_5gTa^B|12e~qh}cp~shFHk zP@sbBkD2U0TgyM974+NrVx83Tv#oDvCVX zss6gm)Klv+Q%=ps+UQiz6C`66z!IdA-;coWPdVFo{-2CCG|FH;+7YeM1sjgHu&cym ztMFk(ILiWle5`#Z%11Q*_(?93=hSnogZUMWt)`mhY558~#y^?w3~*)Vi^jPUucuJZ6TNcb*Iph>Uex%j1&`JyQS(Wd8Cn}f zXDQl!E2^db*`r!aOLfFfv=EkXxx-WAL2t%HP4V>xaTCoZ@?cssIBSiJK+Utl6fLzA z+0Qft)j>|0LfjgUB7$bKvqSj~k?HMdj(^*-5u!6Ei7L4P`$N{uxspkqIarV*!t>fU zx?7i-d}?--g%Ku>v6PoqHvSyM>-|VHY4v`9;ov8GKj|$n9;77bgQIpc%p^u1TPCteCfWzF@r6L{nL7>-_IZWBs6IC zevoN@Cwf1*eP?>VgVATV@jI;3tkGY?(vk@S&;(7w$Ob|KI(WAVVy3XHwdc)nFaoR4r^VebOIB32%@SWFmV=$Rd5Pl4 z&sRkDz&PNZG8E(uuZ^g(In<|}ZfnYaGgLq1ZOVtfV4q^Y;WTh}@y=U6r%W=H$@~JF zS$$siDY(oOTowERw^e;{Y%p^1m8L({31rdiUAWFU_5J&j|D97eAD!l1G6=M)4F&LP z9&Dgb$zox$#rwg`3DRz~Q5f-A=2nt^ z>@^Vm@P#+k3=EiWJoZVO(wrqIMGpf1K)~aBy8YtF^wuX_2`bP1zSHBYVjX0kS zRYoL${Y7U((%Qj%?`19$v)2}XM{@XPJ15l*Pe|S{CTh3{a_JQf#M`~z2qUO|TzHLu z*G|-lBPNR3o2+$g6muNSceL(BCq@>AbM&`0GMy~~O{s3K&g|vQp<#=SMY?yiV|!Mt zL{q8P(W^Mp`ItTS!@+DcjIre!PYU~4QqX7yR;8*Pe3NyFoCyYS-T4dLQEs3_F&~kw zSj=4Bg4<>52V8L{&Q1=tSMJtKHw=9~fbSE+$;sHtqzhl|5)b|tvjp=Wx8@G#+UiHm zrlPp9=ktjSkliFIC5GD9ObTY`N1hb?H3!1{S##W;YgZ4CQU8}H9KjrqH`gY;hCz$f z{-3rdc3Y!k+d6v(ZQZx2cU!W;O2Oc_K`uC0Jyob}VDA~an<{b}3PV&={>-=0vDmQ{ z{z2rIKN5a61tT4wC&RJ@FaNS~sN8||LF7sln`6w_5EGcB$Z|@fVAeaH9dylCRO2Js ziQ}sz)`a2x=NY6lu@mNI+{uc9m@vKSM4(|JTd4+@{!E|@^}CotmDrk2?Nw6-+XA`! zAa>&#GHQ+dnHi<)vfKh)O!GFc;gjvXs@==bTI(d1eD82KzCpVa7lCEDs>3a@bM2QtE*^7Wr73EYH^ecUJq+Xb{$ zfFAK;xP>}UPn%G5|6^vPZa$rDqr2b3f2DJ)p!8@PJtsTG&1|4yCemA-?sQ8B?VzQ< zlFVXT%G&vsBy!V!H4*QR8hyk%1px*6Rn2|&G%I8G1{Ww$x58DM+>jS)hxcn z#|@BaYVrl;@1I2}D>6Pl@-3=%qTKc!M#fvI;c!1Mz}<2&4J!I+{f=R$q%<9dV<)AA zTLEIkREk(Cj+j!~0Fxn!is>5KUSu(O`a|A@jT&&It&z`Zd#!%dWvh&Rz%9M%`9DEu z1LwXkazd{Y5df^d37IBu?B)M6OcrQ1-7&+n^j&ZR$hm(+RZ$yt6VyPg^*;bJ8o{T`*<|&a9vgJ`6|J|-1?k~D1!DK#C{~SL%fQS^y* z?{N=!+8wL?Apy_hw(ZoyQ8Q%FPqoT_U7SEodTE%1?kR=sR^3%(Kp~-3|z( zCwuN$xC+0YFl+oe-0A42acBJ5?ZxRvhfUnjvU9z_PnkAzFW?@j7b_8#v&Oa6Rc#jO zAF#1kXTErjMJ!R9?)Cdcq9>s1CK4S*($cd%Hor6XkT)b?P~*JXZW zru-gedPHM%r!5qUGv~XXjG6PPWLGSU8f^)`b*an5EaLmYMCUF7cb+}|DgAmM>S6Tj z5jMlR(uwL9{a|hf`gJRciGGcyX}aopAN?ZT(%Ek2@FK76bkjEa)hSw?K?~9F*%8xK z%j1*$1ZC5q*EU9BdU(C|yjA?&hbPFy#4drhaqw&HKCsN8MV;aH+TfO9k7aIQ?o1E5 z&EP(ZsqEARxYUt2-*QOsF}pYsjaqg?>A{T|ir@F}&?$UH3oUtFEK| z*SflT*R_?`Dt^+(ZZc*cxnNSpm_BaqJ(Ab=sw3MrPS3fGhlFcwo z+Rp#VX}70#{Oba?LS!RJyJX5x!8fsaXV*QCIhZY_&n?t!dv$q42IKj8y|-tCVY>GsFVFrsW?wC*S;bibtDC;3ifpSYZu+-i}~ z(mGm2ki2`f)MhGBuOSu>Iu;IY2M5;g@ni>*?jYu$^0C$N@(Y%*jNY}8 z^kmnYyR(8KtmqZurA%sf+M+i@pjM|NGiVybT%eaR+0mGu*L&`~$>FvYCsBEcox_>N zli+tNxZdgAiFY%ci0F{6HX@|3)K@ZnW_z-eB=`}J&$GP0^)A_j*CWgS6l4E?p1fvI zfEJb4Bqn!CUK7dl^^(_5@qch{{#QN}!@uWoxaTnu?6_uolE!G6cb&=7qPrhtvBu?Wg8vaf|+0O z?f>sh=cs1$8+~l@qwfc{TOJK6h6j-hxGI?*NHUxR6C@x&N{2Mzn_*`?`>$cY9(oZn zEEzWMS<2K!B^EMpbg*mKjV-3WVUG>ie@!58dCRctTCn^*3Hx(HL6gd!4e_yE1x}9! zxc)N2=~)1O57Re#*1&<-VxqivK$k(j$tPMP-#|Pdp9Co$o%9P69SfZL0h@lQ#V@Qi z#rvYup}J8A(X!kGVi|-}EVgC*vqGzmRW10T5AJL5>Uj_T5WEye>j@)!Y)t3b z(x)LRmepG*FIGcsDQUNN{$ZSFbw_(1cw0=NWR@hzg^^tyk)LDnIjy}EyN`ZX= zT)m=!g0-oSslek(e>3wVp2wkcKy5(>Hl4zBg`b{vdd+gb$xo97etOiA}Nb86h+&~Q*+=bt1t!25q6PX2O)RxGKr4CBV zGh6A5{u-Cs_c5T~W5BFR%^ORJ0$2pyEVZsW7FoBET$gfW{pu-=L;@zgO+Ol844rQE za91LpHk7ZYQO9!@s94xU8D`!j-A^)eDxx956bN>=2EQEuErh>D7|3^rUx%myJJgZc z2y!sbhhlrOUzKxZJ~AeHvcG$hMQcegqWeXrIl8#q8tmd$=kt(t4SO~*EY?gPkrWF5 zxf){_3O`!L$@PVl)Jd}1fV?}}y{)IGHuyCn?63K$IGE}X+-g(0HFD*4l&c@GabJ=P z8K|Vo>04{0@d2tHk6OX=lj^RwD*P|aHh!uSn}%+ZA0%2o;7?KqqW63%TsP|S^QkUo z;|@aPSxj$ZFg~S+lv#WnVE8Ea_-LRjBj6*+ zpA`M<4?R7f<0=@jz~%o6KxXTYP59btwvu{KMUBS%^!DjE*I=Df+rx2G$otVaZI`hn z>XZ&Gr5>~0Yp3C&U8@fw#&Xh42XrGCsXX6dunl>QjbW@cQbmJhY)oi}Tn}VpBvCwm zFi4%jU3I3~e-jJRy@ODr+H{?`l9?{1J^LXZ7^fcGwjp&{WGK1P!_bR%ezY9=!4TD;B<~McK2#B;z$&}N)S!1EiET5Mj^<$prYh!P3OkfX z>G{Z^`qlK2 zyy_+wI6ew^r4BbjP<~z9pRZC2Ys&-bz`ry|h*nLQFOtwaH zi1}l2_rtX`MUVr&5xdNnzgIa7CgrZZNCc1TDAEC? zDG;O93;*MF?mV_w-tSH%Hpfgsp*#^NdSDgg8y6@?Yb4jShZ0j0JY9>?ncj7> zlR^m|{VY_VGeR|gRtA2#AJqy9sQK&2K^WW&9z3}W%7Oc zdakLLweTLU*9u}{JINxaAB8hRH?e_ljVvVh5w`t$#@(zku>nFPgmndfhRIz3(2dr} z?$kW}4BMhX6(8jp`FkI0OuZk6l!#k5NtI zT$uR~peGEA7c(0+u&m9Lm~Q8cokP8qyP7k5UM*@jynZ`15YMfVO%#S7i|vAITX0(; zo|21a1n@<2wL1sU*@^*?t&MohVDW7{A28F+ol?i$85Nm3FuX8rjT{WVSa4|UEs7#I zq@%e5CE%GqG}=vwU4`gYNBUJo3fukpF@Oa~OfdI;%cvi1x&ca{Z_;@gRG{ zqv%X)8o64)hyT@u(Ofn5^VC4pTM`2RryeFE6Ga6VXCdT6}w z6#3(#OMotD48%bXjHeawARL2n?2cmy4qCSCfnyjBS{36*eStl3jKG24(gtV+g5AIX z_B{hhIQGGTjsCzdaO{U;e;mKWaR82F90%eUje|CtY0-2D4%)r@6^;}fhv7IJ#}PP= z#BmgkF*uIKk%}V?$50moT5u-C&MtZw>p`zjkP(i67;O^3k}_*d*h~z)^#PGF;gD+;;iOx{5^%*a$$=SpMa32w(ER!N1r-%b zs)`Rx4px^H&#| zsw!hko}X8d%<4tm4&>*Rl$2H`=a(1cRTd;yR4zW|m=$Ojbr$E9FD)oft}M(eNnV~; zol{YCVL=M19)E^^cGRW6yYal^l_p)m=Mtq)9+#OlYy7N|C8a@zn4UbVq_UtqzjXO> z18QcZ=KRd^ys|?7^Xb#_$}7!jufFm;|NLpG5D)lYTwG8tEpoYWrx%sj^!RDLSTgk! zo^x_lak0lQ;}d3)#Jc4b~=RRvYV<;R}l zRch*+o`(zAcNMA#` z0BJYUYNQ9>f$~UCM%s+D5NQX}>yUOMy%%W`jBPv8bfljlU4V2i>^)T@O+mUA=~+md zk*+}6f%HzK-AJE8ngl}~L7I+q1hxhjAUzmqHPVxju0?tw(q^Q0Bke%?8q#j0+mI%~ z$dAGPd^*wvNEaY2M_P^aHl%BjHX&_B`aRMPq#;}a>ZbEZlVF6tMVgK@bT7&yy#;AC z($A2tMLGf(vzn1kN7{jO1=4P$Hy}-dbNLo&I?}o8P#)4fpjg>WL&6iMp}uq z1L+o|-AL!)B3;smK;SB*=|~?zx&Y}HNUM=Ryy(j+*x z2a%>D-GX!hIXYZ`sz#cObS=__NSl#fg|q|d{Ybl!ZbF&_hd3Hn~4@TOCbUM->kuF9$2KSs?j`TsK>xjlJKAVU}+C?;O9U2Lz{UFkGq#3wLDi`UC zNG~TEJEQB6-j8$>(l3yHfpjEph8+S&e;m>yk)DNg4$?J9i;=EJdKJw)S=Y+SL#@XI@H{E?ftIr+h^x| zaeC)I_qn%EPb=%3cdfnl-`Z=h{qudf8+7cCVTYiHKo5X!c@pgldd^d@2LxL$fmVa& z?nA$TUIV%jbOY!EpnE`{0DTkm70?@hf_(F!A9OD0QP8EJ`A?(1psPW*f<6Jd2lT9; zqQ0OX06hV^0yK9j+8J~%=v$ymL9fJvYHL9^gKh=g4Y~((ALv2Q6QCzR<*huq2n??Q zoeO#+=u*(fLDzvE2i*obWk31}v;p)G=xv}9Og;jd2f81$6!bOFWuRk!hV-EC0o?{# z3_1u}19}K_1E>Uj-v-SCJqTI~ntK50LFa<516>EY4Rij`ZilpMmCq z-VRy{`Y`A+(E8_K*PuP1J3zO9J_b7F7if3TC7|OG0B-=j1oUyxYS0A-hlaXA?*QEd z`Vi<&&_kg6Kx4mzy@1XDorvJM4m2P1HqbiIZ-aJ&9t7P4dK7dgXzBAv54r>NDCk%I z75$FD`w`F*(0>PA0$Owk_6FJjdN=4xpo5?je}#I3R)b2EwiGlEv=g)xRNe`-9rRhy z@fV@pUqHP;KLuJ1`Uq${=-r2hhVBC01-c7#ALuinHNS>EfPNM$O3>|~b-#ffgDwSq1#~6o_)B2Np!uNFUqpRC+d)@@-Uxa(=yuSDKpzJ^06GME z9CX5O(H@t^Vh@7O1w9736tv_e*fHowK(~Spg6;wRJ?KHuDKDekpcjDV&WyztgU$uL z19U0qPSCZW<6c2}&J)kdx9t1rGdIB{0JEV`t zVz+|M1sw!k3i=Z0TF|qOB0Xp+=pN8^(1W0xK~I3n2h*DQjJmNtHfr_6QRj|7dEBN^ zCr=a{t}!<8&Y>X;-Qbg5-mb+&zVueu@M-umVPfTkDOa9x+NyEuVjn!`ii--T88wo= z0{`vUh`AJ0KazGS{=4F?p&>~gE1NKJeNOr5V=GZ0{1pCL{5Ku^37`SLD&%hkJQ;jU zK_R~(WPNWT^Q+eb*h2mDWgA7vj0!QU`~{uAKe1%6cj=8ndi zW`z9bg1>2m{Fj1%#|Zha1%LAh`ELbZ%3tL6PgmGKd%)j1LjDJ-KWcoO0RL+vq|Y5= z^+%*z@H2LF7T5X>1%@YOTm9}1pZp^b2kqSEy_s0 zAV|Lz{42pn$P@bCX2Uk~#~$$O!7p(69U=c9_$?#wPk=uFK0@$N|21Ly+>~9bF%fN5QNM9SI zKM4L^;IGW!FAw-9z&{9ncLx9ZfS-$r`jWd51G@Z=8n$Wwx!_lWe}T)l>J5J>_zmEf zxcuHQ{aWzb!GG4}=j;pe-wOVDUmO~m=kjj~)9(Sl0{qDS2Y)g62+hL&{YaSp1o$h! zN5~uUTSI>CDX~~L_*mM7{HsI$T<|-=N9Y*x(;l)Ot>n*f z)Axnx_kh0z=_Bh8{sSZEKk+vF+*57(DEpiX{tF}IzZCp8BJ$5eoz{|nom;=e$42Kc z#uJ%skeRs!`!TM}{EeeOHYTS{T0&$Vh0M*6X>(;n&vmw~MCL`vd>=9+aKH_*c06fsnrd{I%d0yZjqMejE6o1wY$Z%tM+D;C~JLk=pD5$h`G7 zGW#HN4fdBu((?vncE63xl+zKre>GZ4eN@-8xs@IeO!mkDZird)U`4kUIdm z^{$+?;TuIN5BLViT)u5+Xw7gLGoy*j1CZGSnRZu3?A-CW`y?;Ol)5sG&pryi?2FHE z`Ocg=Q5IwOMDz2(9|wMv&#EK;QdhqjsdmlR4gOgX>GP0g6Zsi)n$+>iuswG{rV=_n z>gHkF(?rLjNA}mh0-4)}%NVN_nG=vX@iuz$&cGb@wV|O~hwCw;S@cvu=G&0DX}F9T z0V2}|neo`?UpHLFcyy832$?p>jHG8LWWEoXj}6zeN^R*G$eg!*Xy|IJ?}BwT+HiF3 zDAS0Nmz*0c`MaT^Rm1g|06=7NC&K0+bNz4`GmD8#DP-i_p<}pAlVm2or3Eq{xHsBn zZYMu#9@`Fn9nxRq=HblKkAi;<_)#`}82shnN5v^)&cxml_@%CXXUt3k{|@kHx%_Tx zk5a!1@V9|~nag+Lj27^Bf*%zJ-46a^;765D)Q-BbuG;MAocyJ05$~UC_jT+(jrbzb z|2@z#pT+@$?j^{$`Hn4T9(b=Hp9NnZxj!dP!WbMuM%wr;{C5B{;$z3eo&41IIevc` z^j)$edX2CS{AJ)*L03@joC;HDY1?h!-vNGC2H!TW@CU(v4*cgc_&*QQ9|HgG`-X;^ zGx#d$I5hO#4E`>|7du{x{f{l+8$13a z8coNIXUEcJ8zB1{WKElOIkH}x$@$^tozXFQH~2fikE+uq@b`irHGl2|{~7S3+H4>A zFGlE>I@gWCxy-j%=kH>0MASJCvbh-A*Fd%g-vc?RuN|JI4@)6)J7l7K?=tW=f?w+D zaD3}J@Xb3GTz*b@(1+W=e+KE38T@dMVG#Ur-;VYlhrllYKdO9kdFt{J_<7*Z1Ak#g z{&o{vY`hfw&Jp->uDfLf{yOmYj=gspK zcnkQuz>l)w+sQ9?(>wcQ+rdA8^igB@QSc9e9~FNc2LCAdm%IAi`k#w?m%E~U_B8Nw z!H=q61^9EppXKUjd?<5A3;6Zmf5GMJ9P>9}KWv2D_aPV6P7jcuEl$jX?kB*19O*OS z3%f2dr#doLjzVVIccSYu5keKMj6FGw=hdi&l%Wp%0q{pELmy=JMd$!4 zb{GD69sDSNuuFt?N5{+0fIklWsCImV{A}$gswcl2=cFU(F9LrG_yv#+=0#`UxdHr( zz>l)WKJYIGKWe<)1%3ti621ib?Y6G*H@mw|lheQ5c^0V2!lw&FQpF{dGH~rc${aWyM zfuF6Pq?{*EPT_CH_c2qwa&8RuJPJMXcC6`cK4$faaSQA)_%p!2!sR=AS7T6s3h*bo zeA}ERz1-<30w2q#u&t`Y{42np4nBrf$X{&vd4O8LzqNB{=zM=Yey&}QOBpvn=C-b( zp*!(C=<}QnMv3rufd4$&B3m3HZ8ZaX8BZboqA_Tz9Bk}rT(BE$Ht4tuG9{1+bU0@r8^C`6d^AtU=UoV? z&o~(87VuAzcDupJ1MQY`BDBSQ(6ja*qwicC1-~tVpND)Vo`-uV;4j1Xpq$cXv}Y;H zT`0>Y$Xtu>V{)9bh@2Dm$Q`o1kc&E>Tnqjy;Ag9|^zl~kp94S2PwoN#F!)j9{vh}- zfgk0cPJl13u*{Z!9`et9AJ%-}--GWNZSjS$y-Oi;{hpzr`L0aPykNX71OKz&&vW_C z8P+=RcYz-@muv%nFZhey^v)UKAowT1kGp*54#^?#PyeUrHj1G`CxZ_Y4(rIhR_TL0 z@JE9`)8#v7T&3Vo0>8-RJ9iY8fj@nO{MUhh3HVFg^fV&Le;fEM;KOZ&<#Wzx2f=R! zA0b}IZw&kA5ct=EAG!WHAL9Z1sQSshLb(?kwceKdhJOQoRQt<4#4X@QweLFc?-(Kd zHt@HCpUp<4eFm96Y786#e-qNb-)$cW*6>@>%L72)7(ss?_>zBA`<8;g8T_a*x(xhn z;77%B>%jje_))RWHt@d&ev@1N9D7O2)F1qR1HZ!MJHGo6_$NOS?GNSQr`M5Qd|5EY zoiqMC@GnF9DElY{|NY>9&`obnb!_`C1Ai&_7rOkGu>IG8zXJRy``iY8&j{%U!CyN< z`a|Gv7$Lnp`1RQl(&vFcu|4_@Nh$dAz{j>z*naj3i6AjN=`~@>N!Wv#^VLzYU8C>~ z$oThRI3!MLh;`ZDR$|_RqhgQ#Z%c^YQ<_g2h<%bO&NvUs-+%Dr`^Uw;KhEZp*MUgz z^P?{L^yt_hM&aw1a|$0G9eXb4?E6N?wvR^0B4xW6rhe5)*Ts5m8o}hhcTL0ZdFTG? zs94WQc#Y4eN1gOfqhkNTyv7xu^!`{q1pfN@YYY6f1^(Is|KGI0wkK>+D}QR~GXB;I z)NLGPzE1hMMG8oJ##?+V=|R#Hq@|}@ejDlCq)(Ho{v~%(|1;E2df-%xPd?4kd89X! zZYKRE>0Z+3Nsp0EV0jjhYQBRTt={E7v&dS~hnTO*wXu8yvhc!Z6X}zr=TQD~(mTBL zHd?gzPCuH%L~cy=^=fD^i<}vnAB>)x|QW$H_fKIpS0-Tti)$Y#~!x7 zJ4v4)z3A5#-#|L<2!E4~`wf4Sj(gGmt|r|;`UvT3q@BODa<7qgzQo_8oiE$pu~#fz zPa1pG{%$9Ifb<#C%g5S!p5X7?*R1@Nq#q%Dg!J;?S^gf)y2Z-2b$68|kSh?C(C( z*GaqoWbw^!S$h8%+kOX0&tZEmAYDsprQzRbUzeP3(=8_bOSP|SM*r2mr0@TC?JLK; z)k5AIV%u@rC`%hiH<0cn-IindYHvGeZ!HHb@+s24RD0XU@&1=KkNw~BXKFW77*~Dj zS&KYG+W9N{dn@T7(sN$0_#l5*^Y=#57f2^_9@TvEnNJDzFaJN3PdoF|d=@aDO^ko< zC;ji%NA(})INP+}BKMOje;@g|jDw3wD<+IGB4bEzB%O4I#S2JP&o%sgfON`4E4P63 z4$^UFTKr1V)uh`<2T5NfojA$L&m-+2)qD@J{Q6tzU#cI``u(N(5v|v1j)y+3OFu>0 z&GqMQ(s9q(6xWgNA|3Y&i!UX;pY&dCT8Q zI{shn?`qOM(g#RyJ7oEfk{%eZBX4wfFlI z-tW)z_joPnp#5?>f8Wk{_+9)xxYp)$E`Q@_)O^k%-A8^de~%;m!&~+UecNaY=-%;? zIrjHh%E@ym@;TL#ST1&{<#U=Pa(&Ny-bt#2YGW^c+LkYejM6jhm&yEnGU?f*N55*n zD1N-d{?>9IkK5m;P+s%@PhJTCY((GU?-@)##NS%3qx{{Mwu=79-+$1KqyGO_$Oif& zzdXf*7O5m%OuB+}fOI|SX3~2}ca!cV-A{Ui^cd;be~y{YS)|iRi%Ba<7n80a9Uxs# zx|#G|(%qzcN%xZ;Aw5Pq_WR7AbUJAj%J?UoBdr5bb?j_w%dW7^C>DWh^Kk0PRV$w>|#iT1p2T0eG zZYI5#bT{c<(*2}INRN?@{TJp>I-Rtbw32i&=?c;T()Fa9N$(}yO}dwKKj{(DW29sE zGJn$Pq{XC_q>D*ckPeWpC*4eXFX?X5y`=j|kB}ZC9s5J(Pdc5nn6#2~G3g4@0n+uP zn@R5_-A%febU*14(qp7!A7lQc(@Bd-D@hlVt{@#CT~E51^j^~4qGMmqL? zGJn$Pq{XC_q>D*ckPeWpC*4eXFX?X5y`=j|kB}ZC9s4-*C!J1OOj=30m~;i{0O@+t z&7}8|?k3$!x}Wq2=`qr=KVtr*(@Bd-D@hlVt{@#CT~E51^j=b{70+MEUrTX5^UKRG z&zn)vx;%~7RK^Qt7R=1QG+uOZOY8D@L4W*0LQVqjt7sb&`~8py$4&t-3Y@j0Ehmox z?*Ppvk5lSc4&DHJ)GM!~6Fkp5uPG$%pW{>kAB7tq6TfWhskLo1_*pTHQ}EM#*5Y5$ zujfOAV;k^g6y7|magX9($Cqs7f7r<9#-@1ridl`q8*L}%+mei-q8Ery_VE8CKE=Z) zBf03w_3)|0^E`Ye@o64@1@RdkzKHlG9^Onm-@{iCFY@p^h?jWyR^oF#{6XTS9{zpe z6(0U`;tM?d72?$%j?OjhR_Ebo5^wPE3yClB@VUg7dUy@-Wggy2yv4(BBHrfVpCR7v z;a?}-?ct9Q@AL4V5nt`$uM%JD;iE7<#je(Q_}Rp7_wdVzZ}9MW#5a1lJbNbkH+gs$ z@w+_y4&rxv_}>xV>fwXLw|V$aiEsDtmx=H2@RKkwrJN6V_AhkOsuC0^v=bBULD_|?ScdU!AKQV-umyu!o3Nqm8aKTf>b!(Sj?=izS>Z}9MU zjI;G!;^8xiFZJ*R#Fu&aGU6>Behcw758pz(-NPRt-tFPf5%2TxW5ic`__+wk#9yuT z@Dk$dJp3Bsw|n?a#5Z{O-NZL~_;-kJ^6-7c@AB|hiQnzvr=M!eztzJpCBDtWtB7y+ z@a4pJc=%1kAMo%k#CLl5cZl!uaCyu~+W8?5|2gqJ9{w`%K@a~E@kc#;!f96jV;=r) z;!k+^Ma1`cc!Kyd9=@3P0T1sae$c}|LHv-1Zy|oz!@oiNMGxOg{1p%1PyDEd|C;z4 z9)5!OaSwk7HaewWPI&l5#A5}S{{I8S$9VX4#K(E~jl{=$_@{_Z^zgqUKH0+uiBIwH zL&S4E{NIV^dH7iDr%HXNdHB1C&+zcs#4qviTH^U0o+e)8;kOVk@$fGYpX=e@B3|m@ zKO$b?;V%G}_1YUx+j+X4^Yx?&7T5cIx=$Rn-kc8kQFtjv?n>M5RAWfC^<5F<@dQY0 z(A$@vZ}d!w`TM5wexOmYbAo+o)xQGxB>myg)xgg~z4ZQ^?z`VXT+eL_nC3p>dj30& z_+W(o=P0lD*KVQwxHD|Nhk4$6A@Lc+_1x&a#OD#;I?1-1?yvU|fBn2n{42yucn|1% z)bl@qXKM#}-@mkj-rKv5@;MVNuIFLe?-vl)dy%?NFYoKgroWc*dXM8x>hA?UHFh@M z?`zAidNxGp*&Kn(JA9=44Uu8;(Cu#+w)fudVU{)kH)!%=zsFvww(7+e--d- z^<5l+H&Kt?KfQu_J_%gfVam-`{}SR`4URv}N&CkzG@ox!kKQ}HmH1)msr#hWgTY`v z@^=)nl`{umHhg>pJ{7pMgWe0fiTTzN*ZXGI6W;(_>UH2ITaZ%Xn<;<)J1st)_FR7lyO> z{3CI_AFJc{C&cx9I7#^-;3(>_&*>P*QqIA1Y&(=t;zHt+**=?zzn}PT-fQJ&67MFi z_hhubUk9Eo-`$kgd(Ucz@^>q;$sebD*L!Tf^Ocn~ zQhvRct9J4l@hQ*P3h6vD3J0GeulFE7!20GX{xb_`yUE|{5P7|Kt9T7@y+?fu^{gR& ziEK#XqxSFs@NDhyFy;0B@@>>}l=uPK6-r<}lhEK&9=%6AgLnz>Z2D^{ulGTrLkBZ z>}SxkpJk92|JKIyTlH6+)T8%})IM(`uJ>oZME&0*?%U7P#Pxo?&dVo&pNap)ynU6^ za4|sY<@=?n!0*BS{0#1sW4UTRS5p4)&+KpYV;?52&l9K}b^y=T?*kF|m#AmM5vyPA zcCX?uTD;hj*l&sJJ>xCJ+`)uKxKe#2rHI@W9Edu)B5<2~)Ch<}*4K0l*= zub=n?#seC+e37{Cho2y>&o^P%n$ItRi{H@upKw#=^LyfYPhRnpV8GH}eebgN0P{1S zNx&xo-|@8lt#){}!71dB>!{~R&KGg=v}ei3 zt)3F*y9&7IxsLO)*7qLbkFp;xjoHsjz_aO}JXQ4LyoUGK;CapGLg1obpYziGEd!oS z&s7n4O9b9U{kI*m`OUK=_N55+lcFP0y<8=0z6xJc2a)k zS))vjI*y(vUPOC%Kg%=qecAQQ1TOuq&wqWC@+so4d4B1p2t6Ak@B`Fyog6^plV|?L zISDo#B;T>LXRYHU#0!XP`+Si2*6~(PDfJ|Y>vLg>-w0fFQq+*oMc`keo;O%dwTEv4 zm+`3gY&9e1&OKTAE|A+Gm>ONc*X^q(CY=f$hPru=^Pi=RYF!kH$h<(^AxsGb8Z#369N6y-%%aE(9+2qxa0!FMR;G zv4@{pNu9@&5&F9+KWeJAH?@cL#PvBJ^&4M{(DN)JM-BXq34Sc_}>DT ze&5M`QMLbvBjkSoT-t3Dx;YXc+vI6 z8^E*WI|>_OQeS;fz%A4tC+^4XRmAnV`clfLiR*JZi-~`MxW11<`|(@Avz6y@%Iouf zI!^yJLjL!Z-}g>i-z%8!Ntb2UFVCMyeUEV6r1Q(Yz$LC&#<)WL-oupF_c3Vu{F=Bv zU#Icae^QS=|D$@&fPmzCnD#K0<(v&%^3~^PRDL1l^?7l0pZW9~dF-dHv-Mq;WAQsE zpUe4F=d62(>+@0F)bmr|+1mdI<@NmuvnYQS7KT#(n;B2a`y=J^e&V^_KFo)KXVagi zyuPPG+w+UWCvcx%=ihHf==lNV|NecpAUeJ#6|kKdpQ!y50MAyQLy>10A{j1NDjknDf`wZpvy&5&dA0WPm?;Dv!{D;JkvwbEIzogLStIvsQe=Q}h z?`hFEa1HSjT<>VS^s`cFADpxN$STHi&wM@$c`1KB$Hke%zYbi>M#zwtBk(^lUww`g z)iIwbSl~#z>GP->f6gJU??=)0Y$mSH9c%xt2A-`v8zb<0sK<|oA11EP&z7>BKP9fu z(|>^YzZ2KzBz3+XhmMeXJ$lFrYJF!AuV+8zQBN80Y~{a;^2ZsUtDe0P^8Xfrze+v& z{IQmEbg}JUecmt_N5G~0z8}5-c(!uRjlip^N8bZ{vpcm zf5s|S`GdrLzw~B=p3|_9k@}YLy)GYQIop9}tM9Fp{|)0GjmI7&elyqmXm<1Y1#x|z z9MhKh{3$~JX;_d-dGx(a+HRKu&sLt&2>i+jJQ;zXBzO+I)F2B;vI4l2r-ydCi1>Qq z{(jDO;=bMPCw}T%R>4N2Hr*d-Nf~|e4Y2> zz$M<(_byDKd;)m3e6OSYW4GD)v72#15B2-&pt~dV+(&tRPt#4z_c`KMGTzYkIU1p7 zGy*fJuYZ1eK5>0d8Nw&?DJOo-R9k*bALi3ZeC)Xv=QJ7n0q|_?urC6CUiI@m3A$c5 zWv(r!zDG>k{{rCI^cPe95tj1|ma{cN{zl5{`-YN~zX!O9+qc+yY23bx^7fS5~pEcXF^{EA3w}Wbvn%?@Z$QUb5?mF9B}Kv(=VoB8$~Y`SI-ciIo31 z@NDh#`3U^W5%^B(*Y|u}O#S}?Tw@PHhWs%?&&imerM~_-)WyX0eLKxm)(Bk2m%fKX z>(xSeeQy}tm-*ZRJX?Lg5P?5PJ^CKG_fpTJ5%Nz(;J>CGeIJ9i&zr>cy(X&v9Urvq zpzon;W4>1qZ{d9sUB53Rei6?X7E^vL@ND&cA_9Ll0)K(}^?eM_QvZa~?E2paT>4Sp zAE@?RPJA89iRFO#+zed$v6TBo%PbuG3~&nn>A+W*!FT-@43 zoHOS0J_FUnJ`H(khyTI&1V7E^UgG+GjsoJ31J9Q4LCX90uZ~8@Pe6l6zP`P^hq%5E zN87)MxW11{@hgezd#t|8a<&oo&rd%GJX`tiqx?axgWgL${}dtr6y^PO$KeS1Nmy8A z%Qr6qzc2!y16=I6j`u&+FI54T_SE++ew5{36QSo55%}k+N8j_OcDR$czE@HAZJs2q z?+4QT{MU%flmQKp2;SkB47v$f9*;8L#`&nZ??eu2?*W=!9oqI1Eu zl-KuNYWeSs(7)a2!MPjTQ{$-bM#w)&{rS-sg?_Je-E2uh~i2L?=195%duj;vz zxbFx47P#tUQ4HA=fj>h%`kq;h!(WY%KSp_dFPi%E2^b(!Uq4Ph7kIYvToHkPh zX*aVHYl-XoWG^6ocZ8n5i@?7_J^s1>FNo`V-cU-MMKFwAeCsK14{z7O*`;x`l5_mUM5|2pt&?e?7rd@uF*`y@vq_Axw_gRGM;Vu#f(@c5R`&h^Vh%=~YN zUqW2v_Yl|j>0V6vpAgsgJgeQlN_;Hmg(k|sv&NS54(iway$ay6fBxYot)ToJF!?Mo zxc%<_yMlWB{hu3&U%>N}WlVHmgr4t4;0Ge`-$vkn0xs?EKW{RzHhVvwAA!e#iyr@6 zxCXfNqrNXswsbAldH=g*9DDU5sJZj`|AMz$^ z&#Nh)Q)kP6?2rX6BR&e2U~cTmr(!1MUsB_}BF-`~6FL)ptYCjzepex9qC-#dWkWx+oYq34Sc_-`Zd zoO;{-{(ZT30?*dYmr-8d1G$oAyDmb$o$~(urOy%H!uu8KPyU(s#uuz&-4A&=LVr#& zdpRcqmvPbls?}4GM}{p^a+^BDE$`?q!7 z^g@JuEZN-C7oXV`OQo8Yr&E1RD`Nn9`}$X`nAsd_Y3*rUneOdt?Md}@q?+5iI$L`o z+0vC-+1|ChsXf)w*VWUTYU*DdYwqgkZg1^tZJC*$T_Uw2-I-1`_4G8YNws$N^{k1l z=xOR`O||rQbgV%V=T{1neHo%n#i`!b&X!bjS9^a)=gj8r?pW%YWM6ejDwXaMY5c5? z&p{c|souVpbQi>1ug5?x3mFiMh%#KKfE(GdP9Z>$f0Bx)+~qo#0CT~+n$ zWMg@GBAHACNIXALU!R)SSXr6CI#A{22v}a*Skn+e~5Nhk~Xs z6;+Fz>Y5Ul)jK7&WofKgSW|mdO_-KC>lqCQzw>lB(zmULTpmIzlg)}<=S8p`Su!&s7CDzT(0+2Gd3q%thj z74-~rH+f{1(U!fcrm{A*FyZu|EvUh3YNI%d%c>iF^^zpa(O68C1K1XXLZl7FY$>X1 z%NJ&vxhZf>*&-(stDvSfwJ713x>Zn9+mK3TN*e=(bh56j+|^OMqXLQ#uDwBS$tgb2_W-7uJFKfuKK{H0Ls&cH79aENUD9;!O zbL#4A%TqOt)ov%|m(^UIO4c^kyHzO4bStw(ronYwa~f+Z5|zlb!fB48c}Wc4R4s+AIk3T3~ru8o4+~ zP8lcXkXHqBT;dvIL743VGP5Y39V#ELA*`<|@T{(&Fi4KBYD8NOw~??IR>UcNn0c~s zURiy8nd^?ibg;-wR+z4GPCmi{*YXPTgCYqNKQfKa;}0)y*rfFd@nNn36gDg{o-+xn zbf?VtEHWw^YibhJ8EIx~nnZmShJL0(O}D7Ax}gdM4I5_`oP--q!NzLl*EeQhvy)Ye zs;V)?1{3w{#u@}kZhI8NYo!|Jg(5`@YTX#BAY+=FbyY*vqC~1HnVMf+J1;{|SzTRC zW5(PCryv1IMW&C97dF;Z!2u={smv*$1TH$28CMu6Qz~94VsJ`zDd=(Lpjq?l%jy<5 z^(ZfM=el@YVxCkv`o|5w2-jD5lbw~msy-8*V|3J4T;()rLB0Vi(5-O0wKb{o>RLBC zi5D9_3XAAe!c50wTYZTo<*sYAS)(~IRfN@v&oXL~663;$xJ_?-bX_$ffkj}Y5Ukf{ zm_ZQGhuSOzNgvA0(>P*AY1y2uv}jFj`GQ2oR8wS1T2otBpD2f^hgrmJQ-OmSDzM3T z0p*k8`y5>Lm^DVmm?@IU4IyHAbz>5?Vzs%oTh@J6 zUr~8iN5`C`tR7{dn5bJgkgi`?lT4LVG@((}bhjp?)^;&x%Tpdt)Wr*o>XlNyl+h;H z2Wg5$t0{r3Lz_C2eer^h){d^8HL0#1ESr;k1%;`Ui=;ZbTGA`ht+Mc}SqS-AsZ?j{ zs#H^ZdslN4*3nfhiFiD@rt=_IIXDQWOw{ zOI?-jO!ap{9eT{I{j7RfJV)2uw0lsscwu=@Yg1oqYM`|l#X{=@%~Oo3_VxG9PNHY9 zRgg%VY)v1gT30u>2icaCH}&*M2}`6C`?|Wj66v~h-ST8yjG>{*tenGYC5)c3_V$%c zeQjtVsZ4!q^ML6wQ7UaO;^pl>Q6+^*HJeJ=-_Vg-iGsl@ zTSa}cuh>?$swLTken`ZNq$9cpT2oEEz3G*miCUB#eKuR{Yh@31KAO8aF{V2Es+LzT zk7M*nK2@D7y0D+oZGZGgqX#X*rkf+}(cIPF*_ZH)P}*g&si-wV8PE+$4{%JnB#Ev^ zgA7Q&)-6mYi&CD(x`A|Zc1k*rii0k;#husO)N2hVX^JXUuziU^zp@pL@APQcS+kPe zO{+SQYja()AZ5o=kyIV#pk^0J7bjNtHaCg*0LtT)(ELPanY!A_^H)vbg1YkJMNNG@ z>DB0NQweJ|SS9xKw83+=xAp{1&kXWeWy`yI`kYb(^)0GvYQEmszm$PRE{7Eu1;+fX zn}7-Sw6^p&x4wOOGr9sj-2;0yS$K6!r==ferNU8U8mqXXRkq8Jq8Y80>?;y$tE#u= zR||`b7x%!Qzyf<(S6~~iv$?gZA(bdH4y!g6tTT;u# zL10wFW{jcMcI(*4HWHeW#`aBaPL~wWX-YW*50-+LO850vaSY|Rw35||P$xfC*V2*2 zK#E|;Z)+Z*X^LkIv~W#QhKsLPx>sgC*P+XNVVbx^>MCxY1I-K>wnN5vnKQqqsk=>Pm%>F& z-I%T5Q44~eb)Ame#bGK%LuXQ4wS44h7q(ZBT5KlS6k4}6n0#g{n#P`bN?Hvr&`xFI z$V_w)beyT??D=Nm%<6#Z%i&47sAO^Cmhpp;k*@((`_~*crfNr zw{ZFLpn^yhFQ_+DMM~ngb}8xbku2HRX)7ZesisD-jvKczUcAUmuqm`aUm9mB$lT1e zg>W`7OLzv(wsodE#njcH3+%8BZE)7Skxa0v)*O}~XzXn@kxFKmRbkc5UwlnXQ3E1! zOj&(R>2~wI1OpP`4<^b1%t>HM%LJoNN0tB#@x?DnPbFaheoBrI0<6fVry=8@(LqBC(4+E;H0k$g;-^5%|v|09D=DHQwgF#`TuM?%uGOP z55Y|BVT7<9Sp-$WDP*zNL}=lrD)D0JF9~fh{=3_oBrqBl#bJRESYkMUMhw!8g~})> zZZzu^I}zDt_7_FQ_sLw`Bu9v{cx|;)KuZ36%q(M7X8g+pAS1mqT>J%16EEU$?r9Yd z){I3>&AVNS$0njMlsBly;5yEq?qW!GJAH+=-z z1al?UqxB1;*J7rdtudQX`|ZRDC9I0B?HgFSu>m;>G_NY604^NqVp}OV+Tku zrI;>J1*Y~Qe5;0fB>Q=L_2?Jd69aur64Vi6C|TqwbM{X&H6rb`R*bPwhk+p)dP9Sl zv2BCw=6ab5l6@tq3fWieT2oaABSue{9tcWeTUFhmaTCFo(QNUmE$I&AA|)`s20wcx z&Xj^gx`iN^7Pp`wNTl>|CxU2v5%y-f*O-u7!eJ}`Oe4s?4s#DRc&7 zozs_rQOYzk%M?`B9ol@mOa?GhB-Bx#Z9=UroPyn#mrFR1YQyB zYdFo%Zi*LRM!^mX*7WTQJ3e0BbCh()TRQCa2X@a$t{Y^bB z==Jv2rry?4Z2Xtat4hUZ7D|tWeSrn~e`W8?kL8Vmz+jC7vO6qw;+tqCei%I`m_xm3WuIO&<>Ftt$ zwr@>p0JrAosKek&mgL#X8P+izWoC{Vwov1^f!Fl6xG~X$XAQyH-v&ZFkdcWah@CLF zq&P*!F)*GAPex30sY5sEbckl#0?Rg3ng`NO^M~y?TNmQN;Ng%myVk)GL8e8_2{uX+ z{&uevPHojR7jl)pH6wHdP7Q*-G<7PjG7;7A^(01R9PtG92yt_AATI0K=5{#NlyR8i z;uF5rM@~j_F!Nx7Ni}q5tU07uHky(xceN1X7_?vQND}ueHtkL?PGg3)>M9%}7^SA$p@%*0#@_Y>OZLyR=VKNm zErC~)ykxr}b353yhHU$01b5*A-%KkKdM517C2HjQ*V*ab#0~vT?Q&qa+*or_xP2Oo ziTEsU6H+F4Y`(PDwFrT*8nZNy6jQAOP3`^I{cN!|YzCa2Wu(OBXR|%cOkEQ% zuwU&_ndsx>r!FoLo{_h7VO3=pCuW`>7Sh7pWgEhnZ{&1u?9lCtz#yfSjjM?lNx&EG zQ8=zBY?s0eH^WxJVHp}m`ZRQG=FGJ7`qnk(9)O80&^W8peSu?_u3{5*cvfTQlJHCu&C-w6OM0MHZG*PS z<1al!Ho>-Q&!#NyKsVoKHMg}kUmuQjd#r{TJ;aRK5bWrKzH7f^^fcn|{aS?-Y_+Q%IQ+(eP3x2%?D1soo7;v5+_8GLarcsY*j zg>TRN%~u)ZLFL0)WmdU7Ok(^3P1`PaGDPhzfg@_0 z5OW}69UH#m%8y;b21lf*@t?bf#!;@cQ}IX#?j@mXmtllMnoJLu-HOB`>Ow4EQuKE9 z<9d>F?rN$n8_D7T({*wgKzE;vqn8~&G00>eJQND)omOYti)=>!!tV^<*6Lg}>=IVs z7l!TO*tV9s&Pqxs2lK$u6PHvnh8=sJWp=NlEm@i&a9x2Fhxa(}@Q(?*TQDVdI6?DF z6ZZ@oiWw7*l+gGY-B{3)1#WFtQnptlfH&ulH1NQoWJJ;dI=nrG4afBAguT$Ns;brBiqGVI~0HGp#My}*o+h)WsPO&h1t+=Ux+<_Os2D_Appx9JwM z*2EYWXD2uOaM0SmoE}A_HOMi3D$B}Lyx;I^c0rX)l)@64F$OnkCg7$xoF}{|ey{B8 zm#lF$509-iA%04wS5z(O#SK4!5VrNAP)$8m*l13`@kr<K8kdpEKuxn zMaPZtNOoV^R0azY@lDv+6?s`{nx(1<$P8rWGV=}FBf%|8Z0D%WeS zu7{f=OAW@wSq?yTXvy-7QH$I5rcPPR(|F&&jKzJBC_h2BAWby`ADpv^HFfjqytL3C72(K+1dHpUT6sXxm}963&1uWNFYnyy3|n} z`WFoxP1_7xhKs4W*DHe3+pP>eTgf)%*u8htBVjmcuCd{Ei=!9gR_;%VAj~U^n{eH$ zX$Lzp%)R8!zBVjK(D*W8N1LbI=jG6DwUdQAd`tSelJ-&=R*+EgXU8qF=rmQrSd(ig z*P9<`9D9i|Xfzp%j4_5AuenYWh{A%+A*8rqhR@tefm$9D2UX@7k30ZukK*7j&3$rv z?qd#qa8pqleZIL!Z7-H)Ton!nY1S(cW(F`9yv1{NA{q*cYDa-?(TGxai^e!m7*}B0 z!8ps@1mMsKq6O3Ik-M&L&|~%=vsoaQl<+zIT%(4!-WNJ&8nVXGi(Pj7ppM-8sOy&l zp_Zn)jGZ8Kme5ESfNwd(0>ZI}8td*&Uyi zvWa3m5f))`cECM|Afm9tM|@G{R=t!0(Iu{qnJHeca2ks-o=ev4;XZ;^pZ<^cY($Cm z6=6E}ut`$|29U8$t#J;cTw^X_aigzjidMBTG>};yM71IIhHtt8XbO+C4^umv} zo=zKu64r$WO48fd!79=t_{;o+%?)}pz|<1Y#iY#TN%S~%Y+#VaY2V%Ff%)NJc zx~9EruU{8*`ezckU%iDTl4nH69N|XeKfqr=?QMaqedi&>$#nY@Cz^;bvUA&Nv)9c$x+Wz`6@v%zI7d z0#k<1v~?>~j0`2Fxe(ii8wb zooG0L2VI4sVCP<4n3zzdZlS#oj^|&v{R9(1;AFdiGgs!sg^8{h{8KOsKh!CWswB3bsb1Zsob+>bfC_Es9Tmx8qQP^&ImG3=}t+8 z>OutZSzTSQmGS^tI0KlZ>KWBt zdC`^>!R-7>oif}C@{GKgq6L?ox=bhj-+$oBJZvRTU1^Cz*O2MybZ;SEl5xgpA6}7t zc54c9u}!xsq}+kW)9`87WP+GQ4&*WyM5ZxknWrhU#e;!K1P-_`*}oii5ni%#Je5QN z&TTj+xD#_&A>C*SFTa|dQ~RjtO*`4_52xKFgHdCb<#Z#Q!rdUv41O~xP2-sR$C1w< ztN$|%BC~_tFUU3of=wC?Jw$zwnVE=Wp&_2$oqdhw@J0@r7A`qI>($_FQ)pGgBE)Zc z3ldv>atPHRo4TPvaGA#E1Y)Vi&Q-7xEHHcfJMiE^c{!e6HVne#h3v6=OowV7wlV;9Y%~q@2g&B6STqa(5 zyx#=%_Ksmw^9}v!o{T$&>V_nKF)ITFyuEjKfrMvTLo;yYB(06;**4z6z43?kR*Oc# zL4<85>tOdQ>se^@1Jx!rL!V)1P(n@FxRz@;D_eV{*_oLc6eC)Ybz3ZQ|v`>yQb9+k=ZjN*pKIj##HY<2kxGl zio1wob4H%kmJ=A+$rV3?haK&jJ#eXZ$&YPKv`}hg2Oi%tak{+6VuT?VoK={+6N{7Z6_CIWW>V)r1kcS;H_Q z%ee?FKJr8?a8>e1xjlb%eM`n`Lx#n;!^44a#7oob#BV}WJB`VN>>n-LX~^ntFmeiF*3{b;o7u9a6Iob_TUdsJ^SEBTg(l-i3OrmHZ;}N3*4^G0n<+PYeZ8pFq2EQ1wklsi?s4e*Bt7YGTMGonoC>>U;nBO;mqu5I^KIX#T~Ui!(od z|8E7`O`kVt<&|!d&UHQ$`CaMP@!L(m^G8-dsg_q&=x?Pvz4ZEbQkDMm)tb^$%~#Rg zUV8nz>q=?9DsK8J_<=8bm|puYmM^uzhjh?uFhBoCAu1^}{UHCYmC`4?0(k0TPkZS% zeZb})&>0YLKH6>y9mE$m|Gbqpz0%*QAgSes;A{9Q?|SDji4F4a2@SSeNx#0DztT66 z_6#TeKBnKt^eP7t^U>c*$08faN%PmgXLNw+cX|cTbW~^loY7b({RaNsmkoUXd>%VS z{EvJzy{i3tU~c|9K5gs0;}%OxC6V*d^h)K=3ApKBWcnAG{=i92VtmzfO67g@ZhHOu zXM-OXG!|1oVSeBrO{c#vhL~X5ezDCq-2tZ0*NQVisU{>9GnW+k9 zR@=`IU2i4Zsw^shH|B5gpO#. + * + * + * + * The framework defines a function check(a,b) that can be called with + * parameters of different types. The function asserts + * that the two paramters are equal (within a certain, predefined range for + * floating point numbers) and prints the result of the comparison on the + * command line. Additionally a summary of all tests is printed at the end of + * the program. + * Additionally there is TEST macro, which you can place outside main to group + * tests together. Code in the macro is automatically executed at the beginning + * of the program. + * The file also defines a class InstanceCount, that can be used to + * count how many instances of an object are still alive at the end of a + * program. To use it, derive your class from InstanceCount and the + * message is automatically printed at the end of the program. + */ + +#ifndef VERY_SIMPLE_TEST_H + #define VERY_SIMPLE_TEST_H + + #include + #include + #include + #include + + /** Simple macro to execute the code that follows the macro (without call from + * main) + * + * Define a class, that is directly instantiated + * and contains the test code in the constructor. + * + * Usage: + * TEST(MyTest) + * { + * // test code + * } + */ + #define TEST(name) \ + struct _TestClass##name { \ + _TestClass##name(); \ + } _TestClass##name##Instance; \ + _TestClass##name::_TestClass##name() + +// Use a namespace to hide implementation details +namespace Test::Detail { +/** + * Make it possible to print the underlying value of class enums with ostream + * + * The expression typename std::enable_if::value, + * std::ostream>::type decays to ostream if the type T is an enum. Otherwise, + * the function is not generated. + */ +template +std::ostream& operator<<( + typename std::enable_if::value, std::ostream>::type& stream, + const T& e) { + return stream << static_cast::type>(e); +} + +/** + * Convert anything to a string. + */ +template +std::string toString(const T& t) { + std::ostringstream ss; + ss << t; + return "\"" + ss.str() + "\""; +} + +/** + * Convert bools to string "true" or "false" instead of 0 and 1 + */ +template <> +inline std::string toString(const bool& b) { + return b ? "\"true\"" : "\"false\""; +} + +/** + * Comparison function for different types + */ +template +bool isEqual(const T& t1, const T& t2) { + return t1 == t2; +} + +/** + * Double values are equal if they differ no more than 1e-12 + */ +template <> +inline bool isEqual(const double& expectedValue, + const double& actualValue) { + const double epsilon = 1e-12; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * Float values are equal if they differ no more than 1e-6 + */ +template <> +inline bool isEqual(const float& expectedValue, + const float& actualValue) { + const double epsilon = 1e-6; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * This class realizes some basics of the test framework. + * Test summary is printed in the destructor. + * Apart from that, the class implements counting of total and failed tests, + * comparison of floating point numbers within sensible boundaries and prints + * the result of each test on the command line. + */ +class Test { + public: + /** + * Test class is a Singleton + */ + static Test& instance() { + static Test test; + return test; + } + + /** + * the main entry point for tests. Test two values for equality and output the + * result. + */ + template + bool check(const T& expectedValue, const T& actualValue) { + bool testResult = isEqual(expectedValue, actualValue); + if (testResult == true) { + registerPassingTest(); + #pragma omp critical + std::cout << "Test successful! Expected value == actual value (=" + << toString(expectedValue) << ")" << std::endl; + } else { + registerFailingTest(); + #pragma omp critical + std::cout << "Error in test: expected value " << toString(expectedValue) + << ", but actual value was " << toString(actualValue) + << std::endl; + } + + return testResult; + } + + private: + /** + * On destruction, print a summary of all tests. + */ + ~Test() { + std::cout << "\n--------------------------------------" << std::endl; + std::cout << "Test summary:" << std::endl; + std::cout << "Executed tests: " << numTests_ << std::endl; + std::cout << "Failed tests: " << numFailedTests_ << std::endl; + } + + void registerPassingTest() { numTests_++; } + + void registerFailingTest() { + numTests_++; + numFailedTests_++; + } + + /** + * For statistics + */ + std::atomic numTests_ = 0; + + /** + * For statistics + */ + std::atomic numFailedTests_ = 0; +}; + +template +class InstanceCounterHelper { + public: + ~InstanceCounterHelper() { + std::cout << "The remaining number of objects of type " << typeid(T).name() + << " at the end of the program is " << count; + if (count > 0) + std::cout << " (NOT zero!)"; + std::cout << "\nThe total number of objects created was " << total + << std::endl; + } + + void increment() { + count++; + total++; + } + + void decrement() { count--; } + + private: + std::atomic count = 0; + std::atomic total = 0; +}; + +} // namespace Test::Detail + +/** + * Count the instances of a class T. + * Result gets printed automatically at the end of the program. + * To use it, inherit T from InstanceCounter, e.g. + * class MyClass : InstanceCounter + */ +template +class InstanceCounter { + public: + InstanceCounter() { counter().increment(); } + + InstanceCounter(const InstanceCounter&) { counter().increment(); } + + InstanceCounter(const InstanceCounter&&) { counter().increment(); } + + virtual ~InstanceCounter() { counter().decrement(); } + + Test::Detail::InstanceCounterHelper& counter() { + static Test::Detail::InstanceCounterHelper c; + return c; + } +}; + +/** + * Check if the expected value is equal to the actual value. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +template +void check(const T1& actualValue, const T2& expectedValue) { + const T1& expectedValueCasted{ + expectedValue}; // allows conversion in general, but avoids narrowing + // conversion + Test::Detail::Test::instance().check(expectedValueCasted, actualValue); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const int& actualValue, const double& expectedValue) { + Test::Detail::Test::instance().check(expectedValue, + static_cast(actualValue)); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const double& actualValue, const int& expectedValue) { + Test::Detail::Test::instance().check(static_cast(expectedValue), + actualValue); +} + +/** + * Check if the entered value is true. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +inline void check(bool a) { + Test::Detail::Test::instance().check(true, a); +} + +#endif // VERY_SIMPLE_TEST_H + +/** + * V1.0: Creation of franework + * V1.1: make check(bool) inline, automatically convert expected value type to + * actual value type + * V1.2: added possibilty to count constructions and destructions of some type + * V1.3: tweaks on check for int and double types + * V1.4: Adding thread safety in OpenMP programs (not general thread safety, as + * OpenMP and std::thread might not play along) + */ \ No newline at end of file