From 6e62b312ce8fd92e6b7fbbeff5372a4d5eed7f2d Mon Sep 17 00:00:00 2001 From: Leonetienne Date: Sun, 27 Feb 2022 19:27:18 +0100 Subject: [PATCH] Abstracted BaseX_2_Y to work with arbitrary containers and digit-types --- Src/GeneralUtility.cpp | 51 +------------------------------ Src/GeneralUtility.h | 57 ++++++++++++++++++++++++++++++++++- Test/BaseX_2_Y.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 51 deletions(-) diff --git a/Src/GeneralUtility.cpp b/Src/GeneralUtility.cpp index 414e829..bd2a906 100644 --- a/Src/GeneralUtility.cpp +++ b/Src/GeneralUtility.cpp @@ -1,55 +1,6 @@ #include "GeneralUtility.h" #include -std::string GeneralUtility::BaseX_2_Y(const std::string &num, const std::string &set_in, const std::string &set_out, const std::uint32_t minOutLen) { - if ((set_in.length() == 0) || (set_out.length() == 0)) - throw std::logic_error("Can't convert from or to base0! Please supply a nonempty set!"); - - std::stringstream ss; - - // Generate a 0-value string for inbase - ss << set_in[0]; - const std::string zeroInbase = ss.str(); - ss.str(""); - - - if (num != zeroInbase) { - - std::string buf = num; - while (buf != zeroInbase) { - const auto divRes = DigitstringDivision(buf, set_out.length(), set_in); - const std::uint64_t mod = divRes.second; - buf = divRes.first; - ss << set_out[mod]; - } - - // Now reverse buf - buf = ss.str(); - ss.str(""); - for (std::size_t i = 0; i < buf.length(); i++) - ss << buf[buf.length() - i - 1]; - } - else - { - // If num is 0, just pass a null-value. The algorithm would hang otherwise. - ss << set_out[0]; - } - - - // Add as much null-values to the left as requested. - if (ss.str().length() < minOutLen) - { - const std::size_t cachedLen = ss.str().length(); - const std::string cachedStr = ss.str(); - ss.str(""); - for (std::size_t i = 0; i < minOutLen - cachedLen; i++) - ss << set_out[0]; - ss << cachedStr; - } - - return ss.str(); -} - std::string GeneralUtility::Base10_2_X(const std::uint64_t &num, const std::string &set, const std::uint32_t minOutLen) { // Convert num to a string std::stringstream ss; @@ -57,7 +8,7 @@ std::string GeneralUtility::Base10_2_X(const std::uint64_t &num, const std::stri const std::string numStr = ss.str(); // Use BaseX_2_Y to convert to outbase - const std::string convertedNum = BaseX_2_Y(numStr, "0123456789", set, minOutLen); + const std::string convertedNum = BaseX_2_Y(numStr, "0123456789", set, minOutLen); // return it return convertedNum; diff --git a/Src/GeneralUtility.h b/Src/GeneralUtility.h index fd72868..3e22b4e 100644 --- a/Src/GeneralUtility.h +++ b/Src/GeneralUtility.h @@ -46,7 +46,8 @@ public: //! \param set_out The desired set/base to output //! \param minLen The minimum output length. Setting this will result in zero-padded output (Like, 00000001 instead of 1) //! \return `num` in base `set_out` - static std::string BaseX_2_Y(const std::string& num, const std::string& set_in, const std::string& set_out, const std::uint32_t minOutLen = 1); + template + static T_ContainerOut BaseX_2_Y(const T_ContainerIn& num, const T_ContainerIn& set_in, const T_ContainerOut& set_out, const std::uint32_t minOutLen = 1); private: // No instantiation! >:( @@ -163,4 +164,58 @@ GeneralUtility::DigitstringDivision(const T_Container& dividend, const unsigned return std::make_pair(result, curRest); } +template +T_ContainerOut GeneralUtility::BaseX_2_Y(const T_ContainerIn& num, const T_ContainerIn& set_in, const T_ContainerOut& set_out, const std::uint32_t minOutLen) { + if ((set_in.size() == 0) || (set_out.size() == 0)) + throw std::logic_error("Can't convert from or to base0! Please supply a nonempty set!"); + + T_ContainerOut result; + + // Generate a 0-value string for inbase + const T_ContainerIn zeroInbase({set_in[0]}); + + if (num != zeroInbase) { + // Populate result object + { + T_ContainerIn buf = num; + while (buf != zeroInbase) { + const auto divRes = DigitstringDivision(buf, set_out.size(), set_in); + const std::uint64_t mod = divRes.second; + buf = divRes.first; + result.insert(result.cend(), set_out[mod]); + } + } + + // Reverse result object item order + { + // Now reverse result + T_ContainerOut buf = result; + result.clear(); + for (std::size_t i = 0; i < buf.size(); i++) + result.insert(result.cend(), buf[buf.size() - i - 1]); + } + } + else + { + // If num is 0, just pass a null-value. The algorithm would hang otherwise. + result.insert(result.cend(), set_out[0]); + } + + + // Add as much null-values to the left as requested. + if (result.size() < minOutLen) + { + const std::size_t cachedLen = result.size(); + const T_ContainerOut cachedStr = result; + result.clear(); + for (std::size_t i = 0; i < minOutLen - cachedLen; i++) + result.insert(result.cend(), set_out[0]); + + for (const auto& it : cachedStr) + result.insert(result.cend(), it); + } + + return result; +} + #endif //GENERALUTILITY_GENERALUTILITY_H diff --git a/Test/BaseX_2_Y.cpp b/Test/BaseX_2_Y.cpp index 78689f3..de5e61e 100644 --- a/Test/BaseX_2_Y.cpp +++ b/Test/BaseX_2_Y.cpp @@ -173,3 +173,71 @@ TEST_CASE(__FILE__"/NotLimitedByIntegerLimits", "[BaseX_2_Y]") // Verify that hex_initial equals hex_converted REQUIRE(hex_initial == hex_convertedBack); } + +// Test to convert weird bases (longer to shorter) +TEST_CASE(__FILE__"/BaseLongCustom_to_ShortCustom", "[BaseX_2_Y]") +{ + // Let's convert from fruits to beers + + // Setup + const std::vector set_in = { "apple", "apricot", "avocado", "banana", "bell pepper", "bilberry" }; + const std::vector set_out = { "heinecken", "corona", "flensburger", "rothaus" }; + const std::vector in = { "apricot", "apple", "apple" }; + const std::vector expected_out = { "flensburger", "corona", "heinecken" }; + + // Exercise + const std::vector out = GeneralUtility::BaseX_2_Y(in, set_in, set_out); + + // Verify + REQUIRE(out == expected_out); +} + +// Test to convert weird bases (shorter to longer) +TEST_CASE(__FILE__"/BaseShortCustom_to_LongCustom", "[BaseX_2_Y]") +{ + // Let's convert from fruits to beers + + // Setup + const std::vector set_in = { "heinecken", "corona", "flensburger", "rothaus" }; + const std::vector set_out = { "apple", "apricot", "avocado", "banana", "bell pepper", "bilberry" }; + const std::vector in = { "flensburger", "corona", "heinecken" }; + const std::vector expected_out = { "apricot", "apple", "apple" }; + + // Exercise + const std::vector out = GeneralUtility::BaseX_2_Y(in, set_in, set_out); + + // Verify + REQUIRE(out == expected_out); +} + +// Tests that converting from a normal base to a custom base works +TEST_CASE(__FILE__"/Base10_to_fruits", "[BaseX_2_Y]") +{ + // Setup + const std::string set_in = "0123456789"; + const std::vector set_out = { "apple", "apricot", "avocado", "banana", "bell pepper", "bilberry" }; + const std::string in = "599"; + const std::vector expected_out = { "avocado", "bell pepper", "banana", "bilberry" }; + + // Exercise + const std::vector out = GeneralUtility::BaseX_2_Y(in, set_in, set_out); + + // Verify + REQUIRE(out == expected_out); +} + +// Tests that converting from a custom base to a normal base works +TEST_CASE(__FILE__"/Base1_fruits_to_10", "[BaseX_2_Y]") +{ + // Setup + const std::vector set_in = { "apple", "apricot", "avocado", "banana", "bell pepper", "bilberry" }; + const std::string set_out = "0123456789"; + const std::vector in = { "avocado", "bell pepper", "banana", "bilberry" }; + const std::string expected_out = "599"; + + // Exercise + const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out); + + // Verify + REQUIRE(out == expected_out); +}