From c5ffec9cca20fdca66bad80dcc56a51c6c43b852 Mon Sep 17 00:00:00 2001 From: Christos Choutouridis Date: Mon, 30 Dec 2024 19:12:55 +0200 Subject: [PATCH] HW2: First locally working version of the 2 variations --- homework_2/Makefile | 16 +++ homework_2/include/config.h | 2 +- homework_2/include/distsort.hpp | 180 ++++++++++++++++---------- homework_2/include/utils.hpp | 75 ++++++++++- homework_2/src/distsort.cpp | 24 +--- homework_2/src/main.cpp | 13 +- homework_2/test/tests_Bitonic.cpp | 9 +- homework_2/test/tests_Bubbletonic.cpp | 33 ++++- homework_2/test/tests_Common.cpp | 156 ++++++++++++++++++++++ 9 files changed, 403 insertions(+), 105 deletions(-) create mode 100644 homework_2/test/tests_Common.cpp diff --git a/homework_2/Makefile b/homework_2/Makefile index 00c4264..37bca74 100644 --- a/homework_2/Makefile +++ b/homework_2/Makefile @@ -185,6 +185,8 @@ distbubbletonic: CFLAGS := $(REL_CFLAGS) -DCODE_VERSION=BUBBLETONIC distbubbletonic: CXXFLAGS := $(REL_CXXFLAGS) -DCODE_VERSION=BUBBLETONIC distbubbletonic: TARGET := distbubbletonic distbubbletonic: $(BUILD_DIR)/$(TARGET) + @mkdir -p out + cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) distbitonic: CC := mpicc distbitonic: CXX := mpic++ @@ -192,6 +194,8 @@ distbitonic: CFLAGS := $(REL_CFLAGS) -DCODE_VERSION=BITONIC distbitonic: CXXFLAGS := $(REL_CXXFLAGS) -DCODE_VERSION=BITONIC distbitonic: TARGET := distbitonic distbitonic: $(BUILD_DIR)/$(TARGET) + @mkdir -p out + cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) deb_distbubbletonic: CC := mpicc deb_distbubbletonic: CXX := mpic++ @@ -199,6 +203,8 @@ deb_distbubbletonic: CFLAGS := $(DEB_CFLAGS) -DCODE_VERSION=BUBBLETONIC -DDEBUG deb_distbubbletonic: CXXFLAGS := $(DEB_CXXFLAGS) -DCODE_VERSION=BUBBLETONIC -DDEBUG deb_distbubbletonic: TARGET := deb_distbubbletonic deb_distbubbletonic: $(BUILD_DIR)/$(TARGET) + @mkdir -p out + cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) deb_distbitonic: CC := mpicc deb_distbitonic: CXX := mpic++ @@ -206,6 +212,8 @@ deb_distbitonic: CFLAGS := $(DEB_CFLAGS) -DCODE_VERSION=BITONIC -DDEBUG deb_distbitonic: CXXFLAGS := $(DEB_CXXFLAGS) -DCODE_VERSION=BITONIC -DDEBUG deb_distbitonic: TARGET := deb_distbitonic deb_distbitonic: $(BUILD_DIR)/$(TARGET) + @mkdir -p out + cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) tests: CC := mpicc tests: CXX := mpic++ @@ -213,6 +221,14 @@ tests: CFLAGS := $(DEB_CFLAGS) -DCODE_VERSION=BITONIC -DDEBUG -DTESTING tests: CXXFLAGS := $(DEB_CXXFLAGS) -DCODE_VERSION=BITONIC -DDEBUG -DTESTING tests: TARGET := tests tests: $(BUILD_DIR)/$(TARGET) + @mkdir -p out + cp $(BUILD_DIR)/$(TARGET) out/$(TARGET) + +measurements: + make clean + make distbubbletonic + make clean + make distbitonic all: debug distbubbletonic distbitonic diff --git a/homework_2/include/config.h b/homework_2/include/config.h index 68bb2e6..182cb9c 100644 --- a/homework_2/include/config.h +++ b/homework_2/include/config.h @@ -25,7 +25,7 @@ #endif // Value type selection -using distValue_t = uint8_t; +using distValue_t = uint32_t; /*! * Session option for each invocation of the executable diff --git a/homework_2/include/distsort.hpp b/homework_2/include/distsort.hpp index abcc804..23bcf2d 100644 --- a/homework_2/include/distsort.hpp +++ b/homework_2/include/distsort.hpp @@ -45,8 +45,8 @@ template inline bool ascending(mpi_id_t, [[maybe_unused]] size_t * Returns the ascending or descending configuration of the node's sequence based on * the current node (MPI process) and the depth of the sorting network * - * @param node The current node (MPI process) - * @return True if we need ascending configuration, false otherwise + * @param node [mpi_id_t] The current node (MPI process) + * @return [bool] True if we need ascending configuration, false otherwise */ template <> inline bool ascending(mpi_id_t node, [[maybe_unused]] size_t depth) noexcept { @@ -57,10 +57,9 @@ bool ascending(mpi_id_t node, [[maybe_unused]] size_t dep * Returns the ascending or descending configuration of the node's sequence based on * the current node (MPI process) and the depth of the sorting network * - * @param node The current node (MPI process) - * @param depth The total depth of the sorting network (same for each step for a given network) - * - * @return True if we need ascending configuration, false otherwise + * @param node [mpi_id_t] The current node (MPI process) + * @param depth [size_t] The total depth of the sorting network (same for each step for a given network) + * @return [bool] True if we need ascending configuration, false otherwise */ template <> inline bool ascending(mpi_id_t node, size_t depth) noexcept { @@ -77,9 +76,9 @@ template inline mpi_id_t partner(mpi_id_t, size_t) noexcept = de * Returns the node's partner for data exchange during the sorting network iterations * of Bubbletonic * - * @param node The current node - * @param step The step of the sorting network - * @return The node id of the partner for data exchange + * @param node [mpi_id_t] The current node + * @param step [size_t] The step of the sorting network + * @return [mpi_id_t] The node id of the partner for data exchange */ template <> inline mpi_id_t partner(mpi_id_t node, size_t step) noexcept { @@ -91,9 +90,9 @@ mpi_id_t partner(mpi_id_t node, size_t step) noexcept { * Returns the node's partner for data exchange during the sorting network iterations * of Bitonic * - * @param node The current node - * @param step The step of the sorting network - * @return The node id of the partner for data exchange + * @param node [mpi_id_t] The current node + * @param step [size_t] The step of the sorting network + * @return [mpi_id_t] The node id of the partner for data exchange */ template <> inline mpi_id_t partner(mpi_id_t node, size_t step) noexcept { @@ -105,32 +104,34 @@ mpi_id_t partner(mpi_id_t node, size_t step) noexcept { * The primary function template of keepSmall(). It is DISABLED since , it is explicitly specialized * for each of the \c SortMode */ -template inline bool keepSmall(mpi_id_t, mpi_id_t, [[maybe_unused]] size_t) noexcept = delete; +template inline bool keepSmall(mpi_id_t, mpi_id_t, [[maybe_unused]] size_t) = delete; /*! * Predicate to check if a node keeps the small numbers during the bubbletonic sort network exchange. * - * @param node The node for which we check - * @param partner The partner of the data exchange - * @return True if the node should keep the small values, false otherwise + * @param node [mpi_id_t] The node for which we check + * @param partner [mpi_id_t] The partner of the data exchange + * @return [bool] True if the node should keep the small values, false otherwise */ template <> inline -bool keepSmall(mpi_id_t node, mpi_id_t partner, [[maybe_unused]] size_t depth) noexcept { - assert(node != partner); +bool keepSmall(mpi_id_t node, mpi_id_t partner, [[maybe_unused]] size_t depth) { + if (node == partner) + throw std::runtime_error("(keepSmall) Node and Partner can not be the same\n"); return (node < partner); } /*! * Predicate to check if a node keeps the small numbers during the bitonic sort network exchange. * - * @param node The node for which we check - * @param partner The partner of the data exchange - * @param depth The total depth of the sorting network (same for each step for a given network) - * @return True if the node should keep the small values, false otherwise + * @param node [mpi_id_t] The node for which we check + * @param partner [mpi_id_t] The partner of the data exchange + * @param depth [size_t] The total depth of the sorting network (same for each step for a given network) + * @return [bool] True if the node should keep the small values, false otherwise */ template <> inline -bool keepSmall(mpi_id_t node, mpi_id_t partner, size_t depth) noexcept { - assert(node != partner); +bool keepSmall(mpi_id_t node, mpi_id_t partner, size_t depth) { + if (node == partner) + throw std::runtime_error("(keepSmall) Node and Partner can not be the same\n"); return ascending(node, depth) == (node < partner); } @@ -138,24 +139,26 @@ bool keepSmall(mpi_id_t node, mpi_id_t partner, size_t depth) * Predicate to check if the node is active in the current iteration of the bubbletonic * sort exchange. * - * @param node The node to check - * @param nodes The total number of nodes - * @return True if the node is active, false otherwise + * @param node [mpi_id_t] The node to check + * @param nodes [size_t] The total number of nodes + * @return [bool] True if the node is active, false otherwise */ -bool isActive(mpi_id_t node, size_t nodes) noexcept; +bool isActive(mpi_id_t node, size_t nodes); /* * ============================== Data utilities ============================== */ /*! + * Sort a range using the build-in O(Nlog(N)) algorithm + * + * @tparam RangeT A range type with random access iterator * - * @tparam RangeT - * @param data - * @param ascending + * @param data [RangeT] The data to be sorted + * @param ascending [bool] Flag to indicate the sorting order */ template -void fullSort(RangeT& data, bool ascending) { +void fullSort(RangeT& data, bool ascending) noexcept { // Use introsort from stdlib++ here, unless ... if (ascending) std::sort(data.begin(), data.end(), std::less<>()); @@ -164,66 +167,87 @@ void fullSort(RangeT& data, bool ascending) { } /*! + * Core functionality of sort for shadowed buffer types using + * the "elbow sort" algorithm. + * + * @note: + * This algorithm can not work "in place". + * We use the active buffer as source and the shadow as target. + * At the end we switch which buffer is active and which is the shadow. + * @note + * This is the core functionality. Use the elbowSort() function instead + * + * @tparam ShadowedT A Shadowed buffer type with random access iterator. + * @tparam CompT A Comparison type for binary operation comparisons * - * @tparam ShadowedT - * @tparam CompT - * @param data - * @param comp + * @param data [ShadowedT] The data to sort + * @param ascending [bool] Flag to indicate the sorting order + * @param comp [CompT] The binary operator object */ template -void elbowSortCore(ShadowedT& data, CompT comp) { - size_t N = data.size(); - auto active = data.getActive(); - auto shadow = data.getShadow(); +void elbowSortCore(ShadowedT& data, bool ascending, CompT comp) noexcept { + auto& active = data.getActive(); // Get the source vector (the data to sort) + auto& shadow = data.getShadow(); // Get the target vector (the sorted data) + + size_t N = data.size(); // The total size is the same or both vectors size_t left = std::distance( active.begin(), - std::min_element(active.begin(), active.end()) - ); + (ascending) ? + std::min_element(active.begin(), active.end()) : + std::max_element(active.begin(), active.end()) + ); // start 'left' from elbow of the bitonic size_t right = (left == N-1) ? 0 : left + 1; + // Walk in opposite directions from elbow and insert-sort to target vector for (size_t i = 0 ; i -void elbowSort(ShadowedT& data, bool ascending) { +void elbowSort(ShadowedT& data, bool ascending) noexcept { if (ascending) - elbowSortCore(data, std::less<>()); + elbowSortCore(data, ascending, std::less<>()); else - elbowSortCore(data, std::greater<>()); + elbowSortCore(data, ascending, std::greater<>()); } /*! + * Takes two sorted sequences where one is in increasing and the other is in decreasing order + * and selects either the larger or the smaller items in one-to-one comparison between them. + * The result is a bitonic sequence. + * + * @tparam RangeT A range type with random access iterator * - * @tparam RangeT - * @param local - * @param remote - * @param keepsmall + * @param local [RangeT] Reference to the local sequence + * @param remote [const RangeT] Reference to the remote sequence (copied locally by MPI) + * @param keepSmall [bool] Flag to indicate if we keep the small items in local sequence */ template -void minmax(RangeT& local, RangeT& remote, bool keepsmall) { +void minmax(RangeT& local, const RangeT& remote, bool keepSmall) noexcept { using value_t = typename RangeT::value_type; std::transform( local.begin(), local.end(), remote.begin(), local.begin(), - [keepsmall](const value_t& a, const value_t& b){ - return (keepsmall) ? std::min(a, b) : std::max(a, b); + [&keepSmall](const value_t& a, const value_t& b){ + return (keepSmall) ? std::min(a, b) : std::max(a, b); }); } @@ -232,27 +256,36 @@ void minmax(RangeT& local, RangeT& remote, bool keepsmall) { */ /*! + * A distributed version of the Bubbletonic sort algorithm. * - * @tparam ShadowedT - * @param data - * @param Processes + * @note + * Each MPI process should run an instance of this function. + * + * @tparam ShadowedT A Shadowed buffer type with random access iterator. + * + * @param data [ShadowedT] The local to MPI process data to sort + * @param Processes [mpi_id_t] The total number of MPI processes */ template void distBubbletonic(ShadowedT& data, mpi_id_t Processes) { // Initially sort to create a half part of a bitonic sequence fullSort(data, ascending(mpi.rank(), 0)); - // Sort network - for (size_t step = 0; step < Processes-1; ++step) { + // Sort network (O(N) iterations) + for (size_t step = 0; step < static_cast(Processes-1); ++step) { + // Find out exchange configuration auto part = partner(mpi.rank(), step); auto ks = keepSmall(mpi.rank(), part, Processes); - if (isActive(mpi.rank(), Processes)) { + if ( isActive(mpi.rank(), Processes) && + isActive(part, Processes) ) { + // Exchange with partner, keep nim-or-max and sort - O(N) mpi.exchange(part, data.getActive(), data.getShadow(), step); minmax(data.getActive(), data.getShadow(), ks); elbowSort(data, ascending(mpi.rank(), Processes)); } } + // Invert if the node was descending. if (!ascending(mpi.rank(), 0)) elbowSort(data, true); @@ -260,27 +293,34 @@ void distBubbletonic(ShadowedT& data, mpi_id_t Processes) { /*! + * A distributed version of the Bitonic sort algorithm. + * + * @note + * Each MPI process should run an instance of this function. + * + * @tparam ShadowedT A Shadowed buffer type with random access iterator. * - * @tparam ShadowedT - * @param data - * @param Processes + * @param data [ShadowedT] The local to MPI process data to sort + * @param Processes [mpi_id_t] The total number of MPI processes */ template void distBitonic(ShadowedT& data, mpi_id_t Processes) { - auto p = static_cast(std::log2(Processes)); - // Initially sort to create a half part of a bitonic sequence fullSort(data, ascending(mpi.rank(), 0)); - // Run through sort network using elbow-sort + // Run through sort network using elbow-sort ( O(LogN * LogN) iterations ) + auto p = static_cast(std::log2(Processes)); for (size_t depth = 1; depth <= p; ++depth) { for (size_t step = depth; step > 0;) { --step; + // Find out exchange configuration auto part = partner(mpi.rank(), step); auto ks = keepSmall(mpi.rank(), part, depth); + // Exchange with partner, keep nim-or-max mpi.exchange(part, data.getActive(), data.getShadow(), (depth << 8) | step); minmax(data.getActive(), data.getShadow(), ks); } + // sort - O(N) elbowSort (data, ascending(mpi.rank(), depth)); } } diff --git a/homework_2/include/utils.hpp b/homework_2/include/utils.hpp index 437d404..10a6f2e 100644 --- a/homework_2/include/utils.hpp +++ b/homework_2/include/utils.hpp @@ -23,14 +23,19 @@ template struct MPI_TypeMapper; // Specializations for supported types template <> struct MPI_TypeMapper { static MPI_Datatype getType() { return MPI_CHAR; } }; template <> struct MPI_TypeMapper { static MPI_Datatype getType() { return MPI_UNSIGNED_CHAR; } }; + template <> struct MPI_TypeMapper { static MPI_Datatype getType() { return MPI_SHORT; } }; template <> struct MPI_TypeMapper { static MPI_Datatype getType() { return MPI_INT; } }; template <> struct MPI_TypeMapper { static MPI_Datatype getType() { return MPI_LONG; } }; template <> struct MPI_TypeMapper { static MPI_Datatype getType() { return MPI_LONG_LONG; } }; template <> struct MPI_TypeMapper{ static MPI_Datatype getType() { return MPI_UNSIGNED_SHORT; } }; +template <> struct MPI_TypeMapper { static MPI_Datatype getType() { return MPI_UNSIGNED; } }; template <> struct MPI_TypeMapper { static MPI_Datatype getType() { return MPI_UNSIGNED_LONG; } }; template <> struct MPI_TypeMapper { static MPI_Datatype getType() { return MPI_UNSIGNED_LONG_LONG; } }; +template <> struct MPI_TypeMapper { static MPI_Datatype getType() { return MPI_FLOAT; } }; +template <> struct MPI_TypeMapper { static MPI_Datatype getType() { return MPI_DOUBLE; } }; + template struct MPI_t { using ID_t = TID; // Export TID type (currently int defined by the standard) @@ -38,6 +43,7 @@ struct MPI_t { void init(int *argc, char ***argv) { // Initialize the MPI environment MPI_Init(argc, argv); + initialized_ = true; // Get the number of processes int size_value, rank_value; @@ -53,11 +59,6 @@ struct MPI_t { name_ = std::string (processor_name, name_len); } - void finalize() { - // Finalize the MPI environment. - MPI_Finalize(); - } - template void exchange(ID_t partner, const std::vector& send_data, std::vector& recv_data, int tag) { using namespace std::string_literals; @@ -83,10 +84,21 @@ struct MPI_t { [[nodiscard]] ID_t size() const noexcept { return size_; } [[nodiscard]] const std::string& name() const noexcept { return name_; } + void finalize() { + // Finalize the MPI environment + initialized_ = false; + MPI_Finalize(); + } + ~MPI_t() { + // Finalize the MPI environment even on unexpected errors + if (initialized_) + MPI_Finalize(); + } private: ID_t rank_{}; ID_t size_{}; std::string name_{}; + bool initialized_{}; }; extern MPI_t<> mpi; @@ -100,6 +112,42 @@ struct ShadowedVec_t { using const_iterator = typename std::vector::const_iterator; using size_type = typename std::vector::size_type; + // Default constructor + ShadowedVec_t() = default; + + // Constructor from an std::vector + explicit ShadowedVec_t(const std::vector& vec) + : North(vec), South(), active(north) { + South.resize(North.size()); + } + + explicit ShadowedVec_t(std::vector&& vec) + : North(std::move(vec)), South(), active(north) { + South.resize(North.size()); + } + + // Copy assignment operator + ShadowedVec_t& operator=(const ShadowedVec_t& other) { + if (this != &other) { // Avoid self-assignment + North = other.North; + South = other.South; + active = other.active; + } + return *this; + } + + // Move assignment operator + ShadowedVec_t& operator=(ShadowedVec_t&& other) noexcept { + if (this != &other) { // Avoid self-assignment + North = std::move(other.North); + South = std::move(other.South); + active = other.active; + + // There is no need to zero out other since it is valid but in a non-defined state + } + return *this; + } + // Dispatch to active vector Value_t& operator[](size_type index) { return getActive()[index]; } const Value_t& operator[](size_type index) const { return getActive()[index]; } @@ -155,10 +203,25 @@ struct ShadowedVec_t { const std::vector& getShadow() const { return (active == north) ? South : North; } + + // Comparisons + bool operator== (const ShadowedVec_t& other) { + return getActive() == other.getActive(); + } + bool operator!= (const ShadowedVec_t& other) { + return getActive() != other.getActive(); + } + bool operator== (const std::vector& other) { + return getActive() == other; + } + bool operator!= (const std::vector& other) { + return getActive() != other; + } + private: - enum { north, south } active{north}; std::vector North{}; std::vector South{}; + enum { north, south } active{north}; }; using distBuffer_t = ShadowedVec_t; diff --git a/homework_2/src/distsort.cpp b/homework_2/src/distsort.cpp index b3589b0..de735b5 100644 --- a/homework_2/src/distsort.cpp +++ b/homework_2/src/distsort.cpp @@ -6,26 +6,16 @@ * Christos Choutouridis AEM:8997 * */ - -#if !defined DEBUG -#define NDEBUG -#endif -#include - #include "utils.hpp" #include "distsort.hpp" -/*! - * Predicate to check if the node is active in the current iteration of the bubbletonic - * sort exchange. - * - * @param node The node to check - * @param nodes The total number of nodes - * @return True if the node is active, false otherwise - */ -bool isActive(mpi_id_t node, size_t nodes) noexcept { - assert(nodes > 0); - return (node >= 0) && (node < (nodes-1)); +bool isActive(mpi_id_t node, size_t nodes) { + if (!((nodes > 0) && + (nodes <= std::numeric_limits::max()) )) + throw std::runtime_error("(isActive) Non-acceptable value of MPI Nodes\n"); + // ^ Assert that mpi_id_t can hold nodes, and thus we can cast without data loss! + + return (node >= 0) && (node < static_cast(nodes)); } diff --git a/homework_2/src/main.cpp b/homework_2/src/main.cpp index 2ac7089..e4fecfe 100644 --- a/homework_2/src/main.cpp +++ b/homework_2/src/main.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "utils.hpp" #include "config.h" @@ -121,9 +122,15 @@ int main(int argc, char* argv[]) try { #endif logger << "Initialize local array of " << session.arraySize << " elements" << logger.endl; - std::srand(unsigned(std::time(nullptr))); + std::random_device rd; // Mersenne seeded from hw if possible. range: [type_min, type_max] + std::mt19937 gen(rd()); + std::uniform_int_distribution dis( + std::numeric_limits::min(), + std::numeric_limits::max() + ); + // Fill vector Data.resize(session.arraySize); - std::generate(Data.begin(), Data.end(), std::rand); + std::generate(Data.begin(), Data.end(), [&]() { return dis(gen); }); if (mpi.rank() == 0) logger << "Starting distributed sorting ... "; @@ -139,7 +146,7 @@ int main(int argc, char* argv[]) try { std::string timeMsg = "rank " + std::to_string(mpi.rank()); timer.print_dt(timeMsg.c_str()); - std::cout << "[Data]: Rank " << mpi.rank() << ": [" << (int)Data.front() << " .. " << (int)Data.back() << "]" << std::endl; + std::cout << "[Data]: Rank " << mpi.rank() << ": [" << +Data.front() << " .. " << +Data.back() << "]" << std::endl; mpi.finalize(); return 0; } diff --git a/homework_2/test/tests_Bitonic.cpp b/homework_2/test/tests_Bitonic.cpp index 75641c5..4e4eb12 100644 --- a/homework_2/test/tests_Bitonic.cpp +++ b/homework_2/test/tests_Bitonic.cpp @@ -230,12 +230,13 @@ TEST(TdistBitonic_UT, partner_test3) { /* ================================== keepSmall ================================== */ /* * bool keepSmall(mpi_id_t node, mpi_id_t partner, size_t depth); - * Assertion check + * Throw check (Not assert - ASSERT_DEATH) */ TEST(TdistBitonic_UT, keepsmall_test1) { - ASSERT_DEATH(keepSmall(0, 0, 0), ""); - ASSERT_DEATH(keepSmall(1, 1, 42), ""); - ASSERT_DEATH(keepSmall(7, 7, 42), ""); + // node and partner must differ or else ... + EXPECT_THROW(keepSmall(0, 0, 0), std::runtime_error); + EXPECT_THROW(keepSmall(1, 1, 42), std::runtime_error); + EXPECT_THROW(keepSmall(7, 7, 42), std::runtime_error); } /* diff --git a/homework_2/test/tests_Bubbletonic.cpp b/homework_2/test/tests_Bubbletonic.cpp index 931c7f3..cb1535f 100644 --- a/homework_2/test/tests_Bubbletonic.cpp +++ b/homework_2/test/tests_Bubbletonic.cpp @@ -95,12 +95,13 @@ TEST(TdistBubbletonic_UT, partner_Bubbletonic_test3) { /* ================================== keepSmall ================================== */ /* * bool keepSmall(mpi_id_t node, mpi_id_t partner, size_t depth); - * Assertion check + * Throw check (Not assert - ASSERT_DEATH) */ TEST(TdistBubbletonic_UT, keepsmall_test1) { - ASSERT_DEATH(keepSmall(0, 0, 0), ""); - ASSERT_DEATH(keepSmall(1, 1, 42), ""); - ASSERT_DEATH(keepSmall(7, 7, 42), ""); + // node and partner must differ or else ... + EXPECT_THROW(keepSmall(0, 0, 0), std::runtime_error); + EXPECT_THROW(keepSmall(1, 1, 42), std::runtime_error); + EXPECT_THROW(keepSmall(7, 7, 42), std::runtime_error); } /* @@ -119,6 +120,30 @@ TEST(TdistBubbletonic_UT, keepsmall_test2) { EXPECT_EQ(keepSmall(4, 9, 42), true); } +/* ================================== isActive ================================== */ +/* + * bool isActive(mpi_id_t node, size_t nodes); + * Throw check + */ +TEST(TdistBubbletonic_UT, isActive_test1) { + EXPECT_THROW(isActive(0, 0), std::runtime_error); + EXPECT_THROW(isActive(0, static_cast(std::numeric_limits::max()) + 1), std::runtime_error); +} + +/* + * bool isActive(mpi_id_t node, size_t nodes); + * Boundary 3 BVA + */ +TEST(TdistBubbletonic_UT, isActive_test2) { + EXPECT_EQ(isActive(-1, 8), false); + EXPECT_EQ(isActive(0, 8), true); + EXPECT_EQ(isActive(1, 8), true); + EXPECT_EQ(isActive(7, 8), true); + EXPECT_EQ(isActive(8, 8), false); + EXPECT_EQ(isActive(9, 8), false); +} + + #if 0 TEST(TdistBubbletonic_UT, distBubbletonic_test1) { diff --git a/homework_2/test/tests_Common.cpp b/homework_2/test/tests_Common.cpp new file mode 100644 index 0000000..31df90c --- /dev/null +++ b/homework_2/test/tests_Common.cpp @@ -0,0 +1,156 @@ +/** + * \file + * \brief PDS HW2 tests + * + * \author + * Christos Choutouridis AEM:8997 + * + */ + +#include + +#include // rand/srand +#include // rand/srand +#include "distsort.hpp" + + +/* ================================== fullSort ================================== */ + +/* + * + */ +TEST(TdistCommonUT, fullSort_test1) { + std::vector ts_data = {3, 2, 1, 4, 5, 7, 8, 6}; + std::vector ts_expected = {1, 2, 3, 4, 5, 6, 7, 8}; + bool ts_ascending = true; + + fullSort(ts_data, ts_ascending); + EXPECT_EQ((ts_data == ts_expected), true); +} + +TEST(TdistCommonUT, fullSort_test2) { + std::vector ts_data = {3, 2, 1, 4, 5, 7, 8, 6}; + std::vector ts_expected = {8, 7, 6, 5, 4, 3, 2, 1}; + bool ts_ascending = false; + + fullSort(ts_data, ts_ascending); + EXPECT_EQ((ts_data == ts_expected), true); +} + +/* ================================== elbowSort ================================== */ + +TEST(TdistCommonUT, elbowSort_test1) { + ShadowedVec_t ts_data1(std::vector{3, 2, 1, 4, 5, 5, 7, 8}); + ShadowedVec_t ts_data2(std::vector{4, 5, 7, 8, 5, 3, 2, 1}); + ShadowedVec_t ts_data3(std::vector{1, 2, 3, 4, 5, 5, 7, 8}); + ShadowedVec_t ts_data4(std::vector{8, 7, 5, 5, 4, 3, 2, 1}); + std::vector ts_expected = {1, 2, 3, 4, 5, 5, 7, 8}; + bool ts_ascending = true; + + elbowSort(ts_data1, ts_ascending); + elbowSort(ts_data2, ts_ascending); + elbowSort(ts_data3, ts_ascending); + elbowSort(ts_data4, ts_ascending); + EXPECT_EQ((ts_data1 == ts_expected), true); + EXPECT_EQ((ts_data2 == ts_expected), true); + EXPECT_EQ((ts_data3 == ts_expected), true); + EXPECT_EQ((ts_data4 == ts_expected), true); +} + +TEST(TdistCommonUT, elbowSort_test2) { + ShadowedVec_t ts_data1(std::vector{3, 2, 1, 4, 5, 5, 7, 8}); + ShadowedVec_t ts_data2(std::vector{4, 5, 7, 8, 5, 3, 2, 1}); + ShadowedVec_t ts_data3(std::vector{1, 2, 3, 4, 5, 5, 7, 8}); + ShadowedVec_t ts_data4(std::vector{8, 7, 5, 5, 4, 3, 2, 1}); + std::vector ts_expected = {8, 7, 5, 5, 4, 3, 2, 1}; + bool ts_ascending = false; + + elbowSort(ts_data1, ts_ascending); + elbowSort(ts_data2, ts_ascending); + elbowSort(ts_data3, ts_ascending); + elbowSort(ts_data4, ts_ascending); + EXPECT_EQ((ts_data1 == ts_expected), true); + EXPECT_EQ((ts_data2 == ts_expected), true); + EXPECT_EQ((ts_data3 == ts_expected), true); + EXPECT_EQ((ts_data4 == ts_expected), true); +} + +TEST(TdistCommonUT, elbowSort_test3) { + ShadowedVec_t ts_data(std::vector{8, 7, 5, 5, 4, 3, 2, 1}); + std::vector ts_expected_asc = {1, 2, 3, 4, 5, 5, 7, 8}; + std::vector ts_expected_des = {8, 7, 5, 5, 4, 3, 2, 1}; + + // Check alternation for active-shadow vector inside Buffer and elbow algorithm + elbowSort(ts_data, true); + EXPECT_EQ((ts_data == ts_expected_asc), true); + elbowSort(ts_data, false); + EXPECT_EQ((ts_data == ts_expected_des), true); + elbowSort(ts_data, true); + EXPECT_EQ((ts_data == ts_expected_asc), true); + elbowSort(ts_data, false); + EXPECT_EQ((ts_data == ts_expected_des), true); +} + +#if 0 +TEST(TdistBubbletonic_UT, distBubbletonic_test1) { + AllData_t ts_Data { + ShadowedVec_t (8), ShadowedVec_t (8) + }; + + std::srand(unsigned(std::time(nullptr))); + for (auto& v : ts_Data) { + std::generate(v.begin(), v.end(), std::rand); + } + + distBubbletonic(2, ts_Data); + + auto max = std::numeric_limits::min(); + for (auto& v : ts_Data) { + EXPECT_EQ((max <= v[0]), true); + EXPECT_EQ(std::is_sorted(v.begin(), v.end()), true); + max = v.back(); + } +} + + +TEST(TdistBubbletonic_UT, distBubbletonic_test2) { + AllData_t ts_Data { + ShadowedVec_t (8), ShadowedVec_t (8), ShadowedVec_t (8), ShadowedVec_t (8) + }; + + std::srand(unsigned(std::time(nullptr))); + for (auto& v : ts_Data) { + std::generate(v.begin(), v.end(), std::rand); + } + + distBubbletonic(4, ts_Data); + + auto max = std::numeric_limits::min(); + for (auto& v : ts_Data) { + EXPECT_EQ((max <= v[0]), true); + EXPECT_EQ(std::is_sorted(v.begin(), v.end()), true); + max = v.back(); + } +} + +TEST(TdistBubbletonic_UT, distBubbletonic_test3) { + AllData_t ts_Data { + ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32), + ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32), ShadowedVec_t (32) + }; + + std::srand(unsigned(std::time(nullptr))); + for (auto& v : ts_Data) { + std::generate(v.begin(), v.end(), std::rand); + } + + distBubbletonic(8, ts_Data); + + auto max = std::numeric_limits::min(); + for (auto& v : ts_Data) { + EXPECT_EQ((max <= v[0]), true); + EXPECT_EQ(std::is_sorted(v.begin(), v.end()), true); + max = v.back(); + } +} +#endif \ No newline at end of file