From 932bd6247724ef9a8dbd7edfe50d54ffa052d04c Mon Sep 17 00:00:00 2001 From: Leonetienne Date: Sun, 27 Feb 2022 16:55:18 +0100 Subject: [PATCH] Implemented and tested Base_X_2_Y --- Src/GeneralUtility.cpp | 51 +++++++++++++++++- Src/GeneralUtility.h | 8 +++ Test/BaseX_2_Y.cpp | 114 +++++++++++++++++++++++++++++++++++++++++ Test/CMakeLists.txt | 2 +- 4 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 Test/BaseX_2_Y.cpp diff --git a/Src/GeneralUtility.cpp b/Src/GeneralUtility.cpp index 7cfda9e..d813b1c 100644 --- a/Src/GeneralUtility.cpp +++ b/Src/GeneralUtility.cpp @@ -18,7 +18,7 @@ namespace { std::uint64_t GeneralUtility::BaseX_2_10(const std::string& num, const std::string& set) { // If base is 0, throw logic error if (set.length() == 0) - throw std::logic_error("Can't convert form base0! Please supply a nonempty set!"); + throw std::logic_error("Can't convert from base0! Please supply a nonempty set!"); unsigned long long int buf = 0; for (std::size_t i = 0; i < num.length(); i++) { @@ -77,3 +77,52 @@ GeneralUtility::StringDivision(const std::string& dividend, const unsigned int d // else return the answer return std::make_pair(ss.str(), curRest); } + +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 = StringDivision(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(); +} diff --git a/Src/GeneralUtility.h b/Src/GeneralUtility.h index 93e06c7..6a19f13 100644 --- a/Src/GeneralUtility.h +++ b/Src/GeneralUtility.h @@ -29,6 +29,14 @@ public: //! \return A 64-bit integer representing the number static std::uint64_t BaseX_2_10(const std::string& num, const std::string& set); + //! Will convert a number from an arbitrary base to another arbitrary base. + //! \param num A string representation of a number + //! \param set_in The set/base of the input + //! \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 minLen = 1); + private: // No instantiation! >:( GeneralUtility(); diff --git a/Test/BaseX_2_Y.cpp b/Test/BaseX_2_Y.cpp new file mode 100644 index 0000000..a432d5b --- /dev/null +++ b/Test/BaseX_2_Y.cpp @@ -0,0 +1,114 @@ +#include +#include "Catch2.h" + +// Tests base 10 to 10 +TEST_CASE(__FILE__"/Base10_to_10", "[BaseX_2_Y]") +{ + // Setup + const std::string set_in = "0123456789"; + const std::string set_out = "0123456789"; + const std::string in = "1990381"; + const std::string expected_out = "1990381"; + + // Exercise + const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out); + + // Verify + REQUIRE(out == expected_out); +} + +// Tests base 10 to 16 +TEST_CASE(__FILE__"/Base10_to_16", "[BaseX_2_Y]") +{ + // Setup + const std::string set_in = "0123456789"; + const std::string set_out = "0123456789abcdef"; + const std::string in = "1990381"; + const std::string expected_out = "1e5eed"; + + // Exercise + const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out); + + // Verify + REQUIRE(out == expected_out); +} + +// Tests base 16 to 10 +TEST_CASE(__FILE__"/Base16_to_10", "[BaseX_2_Y]") +{ + // Setup + const std::string set_in = "0123456789abcdef"; + const std::string set_out = "0123456789"; + const std::string in = "1e5eed"; + const std::string expected_out = "1990381"; + + // Exercise + const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out); + + // Verify + REQUIRE(out == expected_out); +} + +// Tests base 2 to 16 +TEST_CASE(__FILE__"/Base2_to_16", "[BaseX_2_Y]") +{ + // Setup + const std::string set_in = "01"; + const std::string set_out = "0123456789abcdef"; + const std::string in = "110000110001110011010111101011111"; + const std::string expected_out = "18639af5f"; + + // Exercise + const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out); + + // Verify + REQUIRE(out == expected_out); +} + +// Tests base 16 to 2 +TEST_CASE(__FILE__"/Base16_to_2", "[BaseX_2_Y]") +{ + // Setup + const std::string set_in = "0123456789abcdef"; + const std::string set_out = "01"; + const std::string in = "18639af5f"; + const std::string expected_out = "110000110001110011010111101011111"; + + // Exercise + const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out); + + // Verify + REQUIRE(out == expected_out); +} + +// Tests that padding works +TEST_CASE(__FILE__"/TestPadding_ResultSmallEnough", "[BaseX_2_Y]") +{ + // Setup + const std::string set_in = "0123456789"; + const std::string set_out = "01"; + const std::string in = "3"; + const std::string expected_out = "00000011"; // We are expecting the output to be padded to 8 digits. + + // Exercise + const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out, 8); + + // Verify + REQUIRE(out == expected_out); +} + +// Tests that nothing gets padded if the result is longer than the pad-length +TEST_CASE(__FILE__"/TestPadding_ResultLargerThanPad", "[BaseX_2_Y]") +{ + // Setup + const std::string set_in = "0123456789"; + const std::string set_out = "01"; + const std::string in = "500"; + const std::string expected_out = "111110100"; + + // Exercise + const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out, 8); + + // Verify + REQUIRE(out == expected_out); +} diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index be3c85c..4645bbc 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -13,7 +13,7 @@ add_executable(Test Ord.cpp StringDivision.cpp BaseX_2_10.cpp + BaseX_2_Y.cpp ) target_link_libraries(Test GeneralUtility) -