Specialized soruce files for utility domains

This commit is contained in:
Leonetienne 2022-02-27 19:46:34 +01:00
parent 51503ba0a8
commit d3e8236285
9 changed files with 168 additions and 155 deletions

View File

@ -1,22 +1,15 @@
#ifndef GENERALUTILITY_GENERALUTILITY_H
#define GENERALUTILITY_GENERALUTILITY_H
#ifndef GENERALUTILITY_BASECONVERSION_H
#define GENERALUTILITY_BASECONVERSION_H
#include <algorithm>
#include <utility>
#include <sstream>
#include <stdexcept>
class GeneralUtility {
public:
//! Will return the index of `item` in `set`.
//! \tparam T_Type The type of `item`
//! \tparam T_Container The type of container
//! \param item The item to find the index for
//! \param set The container to be looking in
//! \return The index of `item` in `set`. -1 if not found.
template <typename T_Type, class T_Container>
static int Ord(const T_Type& item, const T_Container& set);
#include "ContainerUtility.h"
class BaseConversion {
public:
//! Will divide a number of arbitrary base in `dividend` by an integer divisor.
//! This is a specific helper function for the base conversion functions.
//! \param dividend The number to be divided in listlike container-form (such as a string)
@ -52,7 +45,7 @@ public:
private:
// No instantiation! >:(
GeneralUtility();
BaseConversion();
};
namespace {
@ -68,7 +61,7 @@ namespace {
}
template <class T_Container>
std::uint64_t GeneralUtility::BaseX_2_10(const T_Container& num, const T_Container& set) {
std::uint64_t BaseConversion::BaseX_2_10(const T_Container& num, const T_Container& set) {
// If base is 0, throw logic error
if (set.size() == 0)
throw std::logic_error("Can't convert from base0! Please supply a nonempty set!");
@ -86,24 +79,10 @@ std::uint64_t GeneralUtility::BaseX_2_10(const T_Container& num, const T_Contain
return buf;
}
template<typename T_Type, class T_Container>
int GeneralUtility::Ord(const T_Type& item, const T_Container& set) {
const auto result =
std::find_if(set.begin(), set.end(), [item](const T_Type& c) -> bool {
return c == item;
});
// No item found
if (result == set.end())
return -1;
else
return result - set.begin();
}
// Based on: https://www.geeksforgeeks.org/divide-large-number-represented-string/
template <class T_Container>
std::pair<T_Container, int>
GeneralUtility::DigitstringDivision(const T_Container& dividend, const unsigned int divisor, const T_Container& set) {
BaseConversion::DigitstringDivision(const T_Container& dividend, const unsigned int divisor, const T_Container& set) {
// Quick rejects:
// No set? Throw logic error
@ -120,7 +99,7 @@ GeneralUtility::DigitstringDivision(const T_Container& dividend, const unsigned
// Verify that all digits are represented in the set/base
for (const auto& c : dividend)
if (Ord(c, set) == -1)
if (ContainerUtility::Ord(c, set) == -1)
throw std::logic_error("The supplied dividend contains a digit that is not represented in the set/base!");
@ -129,11 +108,11 @@ GeneralUtility::DigitstringDivision(const T_Container& dividend, const unsigned
// Find prefix of number that is larger than divisor.
int idx = 0;
int temp = Ord(dividend[idx], set);
int temp = ContainerUtility::Ord(dividend[idx], set);
while (temp < divisor) {
idx++;
if (idx < dividend.size())
temp = temp * set.size() + Ord(dividend[idx], set);
temp = temp * set.size() + ContainerUtility::Ord(dividend[idx], set);
else
break;
}
@ -150,7 +129,7 @@ GeneralUtility::DigitstringDivision(const T_Container& dividend, const unsigned
// Take next digit of number
idx++;
if (idx < dividend.size())
temp = (temp % divisor) * set.size() + Ord(dividend[idx], set);
temp = (temp % divisor) * set.size() + ContainerUtility::Ord(dividend[idx], set);
}
// If divisor is greater than number
@ -166,7 +145,7 @@ GeneralUtility::DigitstringDivision(const T_Container& dividend, const unsigned
}
template <class T_ContainerIn, class T_ContainerOut>
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) {
T_ContainerOut BaseConversion::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!");
@ -220,7 +199,7 @@ T_ContainerOut GeneralUtility::BaseX_2_Y(const T_ContainerIn& num, const T_Conta
}
template <class T_Container>
T_Container GeneralUtility::Base10_2_X(const std::uint64_t &num, const T_Container& set, const std::uint32_t minOutLen) {
T_Container BaseConversion::Base10_2_X(const std::uint64_t &num, const T_Container& set, const std::uint32_t minOutLen) {
// Convert num to a string
std::stringstream ss;
ss << num;
@ -233,4 +212,4 @@ T_Container GeneralUtility::Base10_2_X(const std::uint64_t &num, const T_Contain
return convertedNum;
}
#endif //GENERALUTILITY_GENERALUTILITY_H
#endif //GENERALUTILITY_BASECONVERSION_H

View File

@ -4,5 +4,5 @@ project(GeneralUtility)
set(CMAKE_CXX_STANDARD 17)
add_library(GeneralUtility
GeneralUtility.cpp
BaseConversion.h
)

36
Src/ContainerUtility.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef GENERALUTILITY_CONTAINERUTILITY_H
#define GENERALUTILITY_GENERALUTILITYCONTAINERUTILITY_H
#include <algorithm>
class ContainerUtility {
public:
//! Will return the index of `item` in `set`.
//! \tparam T_Type The type of `item`
//! \tparam T_Container The type of container
//! \param item The item to find the index for
//! \param set The container to be looking in
//! \return The index of `item` in `set`. -1 if not found.
template <typename T_Type, class T_Container>
static int Ord(const T_Type& item, const T_Container& set);
private:
// No instantiation! >:(
ContainerUtility();
};
template<typename T_Type, class T_Container>
int ContainerUtility::Ord(const T_Type& item, const T_Container& set) {
const auto result =
std::find_if(set.begin(), set.end(), [item](const T_Type& c) -> bool {
return c == item;
});
// No item found
if (result == set.end())
return -1;
else
return result - set.begin();
}
#endif //GENERALUTILITY_CONTAINERUTILITY_H

View File

@ -1 +0,0 @@
#include "GeneralUtility.h"

View File

@ -1,8 +1,8 @@
#include <GeneralUtility.h>
#include <BaseConversion.h>
#include "Catch2.h"
// Tests base 10 to 10
TEST_CASE(__FILE__"/Base10_to_10", "[Base10_2_X]")
TEST_CASE(__FILE__"/Base10_to_10", "[BaseConversion][Base10_2_X]")
{
// Setup
const std::string set = "0123456789";
@ -10,14 +10,14 @@ TEST_CASE(__FILE__"/Base10_to_10", "[Base10_2_X]")
const std::string expected_out = "1990381";
// Exercise
const std::string out = GeneralUtility::Base10_2_X(in, set);
const std::string out = BaseConversion::Base10_2_X(in, set);
// Verify
REQUIRE(out == expected_out);
}
// Tests base 10 to 16
TEST_CASE(__FILE__"/Base10_to_16", "[Base10_2_X]")
TEST_CASE(__FILE__"/Base10_to_16", "[BaseConversion][Base10_2_X]")
{
// Setup
const std::string set = "0123456789abcdef";
@ -25,14 +25,14 @@ TEST_CASE(__FILE__"/Base10_to_16", "[Base10_2_X]")
const std::string expected_out = "1990381";
// Exercise
const std::string out = GeneralUtility::Base10_2_X(in, set);
const std::string out = BaseConversion::Base10_2_X(in, set);
// Verify
REQUIRE(out == expected_out);
}
// Tests base 10 to 2
TEST_CASE(__FILE__"/Base10_to_2", "[Base10_2_X]")
TEST_CASE(__FILE__"/Base10_to_2", "[BaseConversion][Base10_2_X]")
{
// Setup
const std::string set = "01";
@ -40,14 +40,14 @@ TEST_CASE(__FILE__"/Base10_to_2", "[Base10_2_X]")
const std::string expected_out = "10111011";
// Exercise
const std::string out = GeneralUtility::Base10_2_X(in, set);
const std::string out = BaseConversion::Base10_2_X(in, set);
// Verify
REQUIRE(out == expected_out);
}
// Tests base 10 to fucking big
TEST_CASE(__FILE__"/Base10_to_FuckingBig", "[Base10_2_X]")
TEST_CASE(__FILE__"/Base10_to_FuckingBig", "[BaseConversion][Base10_2_X]")
{
// Setup
const std::string set = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
@ -55,14 +55,14 @@ TEST_CASE(__FILE__"/Base10_to_FuckingBig", "[Base10_2_X]")
const std::string expected_out = "rn5qZuTD";
// Exercise
const std::string out = GeneralUtility::Base10_2_X(in, set);
const std::string out = BaseConversion::Base10_2_X(in, set);
// Verify
REQUIRE(out == expected_out);
}
// Tests that padding works
TEST_CASE(__FILE__"/TestPadding", "[Base10_2_X]")
TEST_CASE(__FILE__"/TestPadding", "[BaseConversion][Base10_2_X]")
{
// Setup
const std::string set = "01";
@ -70,14 +70,14 @@ TEST_CASE(__FILE__"/TestPadding", "[Base10_2_X]")
const std::string expected_out = "00000101";
// Exercise
const std::string out = GeneralUtility::Base10_2_X(in, set, 8);
const std::string out = BaseConversion::Base10_2_X(in, set, 8);
// Verify
REQUIRE(out == expected_out);
}
// Tests that conversion with more complex 'digits' works. Weird-ass usecase
TEST_CASE(__FILE__"/BaseWeird_to_10", "[Base10_2_X]")
TEST_CASE(__FILE__"/BaseWeird_to_10", "[BaseConversion][Base10_2_X]")
{
// Setup
@ -90,7 +90,7 @@ TEST_CASE(__FILE__"/BaseWeird_to_10", "[Base10_2_X]")
const std::vector<std::string> expected_out = { "Apple", "Cherry", "Strawberry" };
// Exercise
const std::vector<std::string> out = GeneralUtility::Base10_2_X(in, set);
const std::vector<std::string> out = BaseConversion::Base10_2_X(in, set);
// Verify
REQUIRE(out == expected_out);

View File

@ -1,8 +1,8 @@
#include <GeneralUtility.h>
#include <BaseConversion.h>
#include "Catch2.h"
// Tests base 10 to 10
TEST_CASE(__FILE__"/Base10_to_10", "[BaseX_2_10]")
TEST_CASE(__FILE__"/Base10_to_10", "[BaseConversion][BaseX_2_10]")
{
// Setup
const std::string set = "0123456789";
@ -10,14 +10,14 @@ TEST_CASE(__FILE__"/Base10_to_10", "[BaseX_2_10]")
const std::uint64_t expected_out = 1990381;
// Exercise
const std::uint64_t out = GeneralUtility::BaseX_2_10(in, set);
const std::uint64_t out = BaseConversion::BaseX_2_10(in, set);
// Verify
REQUIRE(out == expected_out);
}
// Tests base 16 to 10
TEST_CASE(__FILE__"/Base16_to_10", "[BaseX_2_10]")
TEST_CASE(__FILE__"/Base16_to_10", "[BaseConversion][BaseX_2_10]")
{
// Setup
const std::string set = "0123456789abcdef";
@ -25,14 +25,14 @@ TEST_CASE(__FILE__"/Base16_to_10", "[BaseX_2_10]")
const std::uint64_t expected_out = 0x1a83f9cefa;
// Exercise
const std::uint64_t out = GeneralUtility::BaseX_2_10(in, set);
const std::uint64_t out = BaseConversion::BaseX_2_10(in, set);
// Verify
REQUIRE(out == expected_out);
}
// Tests base 2 to 10
TEST_CASE(__FILE__"/Base2_to_10", "[BaseX_2_10]")
TEST_CASE(__FILE__"/Base2_to_10", "[BaseConversion][BaseX_2_10]")
{
// Setup
const std::string set = "01";
@ -40,14 +40,14 @@ TEST_CASE(__FILE__"/Base2_to_10", "[BaseX_2_10]")
const std::uint64_t expected_out = 0b10101010110000111111;
// Exercise
const std::uint64_t out = GeneralUtility::BaseX_2_10(in, set);
const std::uint64_t out = BaseConversion::BaseX_2_10(in, set);
// Verify
REQUIRE(out == expected_out);
}
// Tests base fucking big to 10
TEST_CASE(__FILE__"/BaseFuckingBig_to_10", "[BaseX_2_10]")
TEST_CASE(__FILE__"/BaseFuckingBig_to_10", "[BaseConversion][BaseX_2_10]")
{
// Setup
const std::string set = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
@ -55,22 +55,22 @@ TEST_CASE(__FILE__"/BaseFuckingBig_to_10", "[BaseX_2_10]")
const std::uint64_t expected_out = 189434143264827;
// Exercise
const std::uint64_t out = GeneralUtility::BaseX_2_10(in, set);
const std::uint64_t out = BaseConversion::BaseX_2_10(in, set);
// Verify
REQUIRE(out == expected_out);
}
// Tests that a logic error is thrown when the supplied set is empty
TEST_CASE(__FILE__"/NoSetSupplied", "[BaseX_2_10]")
TEST_CASE(__FILE__"/NoSetSupplied", "[BaseConversion][BaseX_2_10]")
{
REQUIRE_THROWS_AS(
GeneralUtility::BaseX_2_10<std::string>("699", "")
BaseConversion::BaseX_2_10<std::string>("699", "")
, std::logic_error);
}
// Tests that conversion with more complex 'digits' works. Weird-ass usecase
TEST_CASE(__FILE__"/BaseWeird_to_10", "[BaseX_2_10]")
TEST_CASE(__FILE__"/BaseWeird_to_10", "[BaseConversion][BaseX_2_10]")
{
// Setup
@ -83,7 +83,7 @@ TEST_CASE(__FILE__"/BaseWeird_to_10", "[BaseX_2_10]")
const std::uint64_t expected_out = 69;
// Exercise
const std::uint64_t out = GeneralUtility::BaseX_2_10(in, set);
const std::uint64_t out = BaseConversion::BaseX_2_10(in, set);
// Verify
REQUIRE(out == expected_out);

View File

@ -1,8 +1,8 @@
#include <GeneralUtility.h>
#include <BaseConversion.h>
#include "Catch2.h"
// Tests base 10 to 10
TEST_CASE(__FILE__"/Base10_to_10", "[BaseX_2_Y]")
TEST_CASE(__FILE__"/Base10_to_10", "[BaseConversion][BaseX_2_Y]")
{
// Setup
const std::string set_in = "0123456789";
@ -11,14 +11,14 @@ TEST_CASE(__FILE__"/Base10_to_10", "[BaseX_2_Y]")
const std::string expected_out = "1990381";
// Exercise
const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out);
const std::string out = BaseConversion::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]")
TEST_CASE(__FILE__"/Base10_to_16", "[BaseConversion][BaseX_2_Y]")
{
// Setup
const std::string set_in = "0123456789";
@ -27,14 +27,14 @@ TEST_CASE(__FILE__"/Base10_to_16", "[BaseX_2_Y]")
const std::string expected_out = "1e5eed";
// Exercise
const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out);
const std::string out = BaseConversion::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]")
TEST_CASE(__FILE__"/Base16_to_10", "[BaseConversion][BaseX_2_Y]")
{
// Setup
const std::string set_in = "0123456789abcdef";
@ -43,14 +43,14 @@ TEST_CASE(__FILE__"/Base16_to_10", "[BaseX_2_Y]")
const std::string expected_out = "1990381";
// Exercise
const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out);
const std::string out = BaseConversion::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]")
TEST_CASE(__FILE__"/Base2_to_16", "[BaseConversion][BaseX_2_Y]")
{
// Setup
const std::string set_in = "01";
@ -59,14 +59,14 @@ TEST_CASE(__FILE__"/Base2_to_16", "[BaseX_2_Y]")
const std::string expected_out = "18639af5f";
// Exercise
const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out);
const std::string out = BaseConversion::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]")
TEST_CASE(__FILE__"/Base16_to_2", "[BaseConversion][BaseX_2_Y]")
{
// Setup
const std::string set_in = "0123456789abcdef";
@ -75,14 +75,14 @@ TEST_CASE(__FILE__"/Base16_to_2", "[BaseX_2_Y]")
const std::string expected_out = "110000110001110011010111101011111";
// Exercise
const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out);
const std::string out = BaseConversion::BaseX_2_Y(in, set_in, set_out);
// Verify
REQUIRE(out == expected_out);
}
// Tests base 16 to fucking big
TEST_CASE(__FILE__"/Base16_to_FuckingBig", "[Base10_2_X]")
TEST_CASE(__FILE__"/Base16_to_FuckingBig", "[BaseConversion][Base10_2_X]")
{
// Setup
const std::string set_in = "0123456789abcdef";
@ -91,14 +91,14 @@ TEST_CASE(__FILE__"/Base16_to_FuckingBig", "[Base10_2_X]")
const std::string expected_out = "aymsa";
// Exercise
const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out);
const std::string out = BaseConversion::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]")
TEST_CASE(__FILE__"/TestPadding_ResultSmallEnough", "[BaseConversion][BaseX_2_Y]")
{
// Setup
const std::string set_in = "0123456789";
@ -107,14 +107,14 @@ TEST_CASE(__FILE__"/TestPadding_ResultSmallEnough", "[BaseX_2_Y]")
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);
const std::string out = BaseConversion::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]")
TEST_CASE(__FILE__"/TestPadding_ResultLargerThanPad", "[BaseConversion][BaseX_2_Y]")
{
// Setup
const std::string set_in = "0123456789";
@ -123,14 +123,14 @@ TEST_CASE(__FILE__"/TestPadding_ResultLargerThanPad", "[BaseX_2_Y]")
const std::string expected_out = "111110100";
// Exercise
const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out, 8);
const std::string out = BaseConversion::BaseX_2_Y(in, set_in, set_out, 8);
// Verify
REQUIRE(out == expected_out);
}
// Tests that BaseX_2_Y is not limited by integer boundaries
TEST_CASE(__FILE__"/NotLimitedByIntegerLimits", "[BaseX_2_Y]")
TEST_CASE(__FILE__"/NotLimitedByIntegerLimits", "[BaseConversion][BaseX_2_Y]")
{
// So what we're doing is the following:
// 1) Store a 4096-bit integer in a string (hex)
@ -165,17 +165,17 @@ TEST_CASE(__FILE__"/NotLimitedByIntegerLimits", "[BaseX_2_Y]")
;
// Convert to base62
const std::string base62 = GeneralUtility::BaseX_2_Y(hex_initial, set_hex, set_base62);
const std::string base62 = BaseConversion::BaseX_2_Y(hex_initial, set_hex, set_base62);
// Convert back to hex
const std::string hex_convertedBack = GeneralUtility::BaseX_2_Y(base62, set_base62, set_hex);
const std::string hex_convertedBack = BaseConversion::BaseX_2_Y(base62, set_base62, set_hex);
// 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]")
TEST_CASE(__FILE__"/BaseLongCustom_to_ShortCustom", "[BaseConversion][BaseX_2_Y]")
{
// Let's convert from fruits to beers
@ -186,14 +186,14 @@ TEST_CASE(__FILE__"/BaseLongCustom_to_ShortCustom", "[BaseX_2_Y]")
const std::vector<std::string> expected_out = { "flensburger", "corona", "heinecken" };
// Exercise
const std::vector<std::string> out = GeneralUtility::BaseX_2_Y(in, set_in, set_out);
const std::vector<std::string> out = BaseConversion::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]")
TEST_CASE(__FILE__"/BaseShortCustom_to_LongCustom", "[BaseConversion][BaseX_2_Y]")
{
// Let's convert from fruits to beers
@ -204,14 +204,14 @@ TEST_CASE(__FILE__"/BaseShortCustom_to_LongCustom", "[BaseX_2_Y]")
const std::vector<std::string> expected_out = { "apricot", "apple", "apple" };
// Exercise
const std::vector<std::string> out = GeneralUtility::BaseX_2_Y(in, set_in, set_out);
const std::vector<std::string> out = BaseConversion::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]")
TEST_CASE(__FILE__"/Base10_to_fruits", "[BaseConversion][BaseX_2_Y]")
{
// Setup
const std::string set_in = "0123456789";
@ -220,14 +220,14 @@ TEST_CASE(__FILE__"/Base10_to_fruits", "[BaseX_2_Y]")
const std::vector<std::string> expected_out = { "avocado", "bell pepper", "banana", "bilberry" };
// Exercise
const std::vector<std::string> out = GeneralUtility::BaseX_2_Y(in, set_in, set_out);
const std::vector<std::string> out = BaseConversion::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]")
TEST_CASE(__FILE__"/Base1_fruits_to_10", "[BaseConversion][BaseX_2_Y]")
{
// Setup
const std::vector<std::string> set_in = { "apple", "apricot", "avocado", "banana", "bell pepper", "bilberry" };
@ -236,14 +236,14 @@ TEST_CASE(__FILE__"/Base1_fruits_to_10", "[BaseX_2_Y]")
const std::string expected_out = "599";
// Exercise
const std::string out = GeneralUtility::BaseX_2_Y(in, set_in, set_out);
const std::string out = BaseConversion::BaseX_2_Y(in, set_in, set_out);
// Verify
REQUIRE(out == expected_out);
}
// Tests that padding with custom digit types works
TEST_CASE(__FILE__"/TestPadding_CustomDigitTypes", "[BaseX_2_Y]")
TEST_CASE(__FILE__"/TestPadding_CustomDigitTypes", "[BaseConversion][BaseX_2_Y]")
{
// Setup
const std::string set_in = "0123456789";
@ -252,7 +252,7 @@ TEST_CASE(__FILE__"/TestPadding_CustomDigitTypes", "[BaseX_2_Y]")
const std::vector<std::string> expected_out = { "apple", "apple", "apple", "apple", "banana", "tomato", "banana", "tomato" };
// Exercise
const std::vector<std::string> out = GeneralUtility::BaseX_2_Y(in, set_in, set_out, 8);
const std::vector<std::string> out = BaseConversion::BaseX_2_Y(in, set_in, set_out, 8);
// Verify
REQUIRE(out == expected_out);

View File

@ -1,37 +1,36 @@
#include <GeneralUtility.h>
#include <BaseConversion.h>
#include "Catch2.h"
#include <string>
#include <vector>
#include <time.h>
#include <sstream>
#include <stdexcept>
// Tests that basic division (base10) is working, with oracle values
TEST_CASE(__FILE__"/Base10", "[DigitstringDivision]")
TEST_CASE(__FILE__"/Base10", "[BaseConversion][DigitstringDivision]")
{
const std::string set = "0123456789";
SECTION("No rest") {
const auto res = GeneralUtility::DigitstringDivision<std::string>("200", 10, set);
const auto res = BaseConversion::DigitstringDivision<std::string>("200", 10, set);
REQUIRE(res.first == "20");
REQUIRE(res.second == 0);
}
SECTION("With rest") {
const auto res = GeneralUtility::DigitstringDivision<std::string>("205", 10, set);
const auto res = BaseConversion::DigitstringDivision<std::string>("205", 10, set);
REQUIRE(res.first == "20");
REQUIRE(res.second == 5);
}
SECTION("With rest, and divisor > dividend") {
const auto res = GeneralUtility::DigitstringDivision<std::string>("205", 299, set);
const auto res = BaseConversion::DigitstringDivision<std::string>("205", 299, set);
REQUIRE(res.first == "0");
REQUIRE(res.second == 205);
}
}
// Tests that basic division (base10) is working, by doing a lot of random, precalculated divisions
TEST_CASE(__FILE__"/Base10_Randoms", "[DigitstringDivision]")
TEST_CASE(__FILE__"/Base10_Randoms", "[BaseConversion][DigitstringDivision]")
{
srand(time(0));
const std::string set = "0123456789";
@ -52,7 +51,7 @@ TEST_CASE(__FILE__"/Base10_Randoms", "[DigitstringDivision]")
const std::string dividend = ss.str();
// Compute results
const auto res = GeneralUtility::DigitstringDivision(dividend, i_divisor, set);
const auto res = BaseConversion::DigitstringDivision(dividend, i_divisor, set);
const std::uint32_t actual_result = std::stoi(res.first);
const std::uint32_t actual_rest = res.second;
@ -63,124 +62,124 @@ TEST_CASE(__FILE__"/Base10_Randoms", "[DigitstringDivision]")
}
// Tests that base16 division is working, with oracle values
TEST_CASE(__FILE__"/Base16", "[DigitstringDivision]")
TEST_CASE(__FILE__"/Base16", "[BaseConversion][DigitstringDivision]")
{
const std::string set = "0123456789abcdef";
SECTION("No rest") {
const auto res = GeneralUtility::DigitstringDivision<std::string>("1f4", 10, set);
const auto res = BaseConversion::DigitstringDivision<std::string>("1f4", 10, set);
REQUIRE(res.first == "32");
REQUIRE(res.second == 0);
}
SECTION("With rest") {
const auto res = GeneralUtility::DigitstringDivision<std::string>("1fd", 10, set);
const auto res = BaseConversion::DigitstringDivision<std::string>("1fd", 10, set);
REQUIRE(res.first == "32");
REQUIRE(res.second == 9);
}
SECTION("With rest, and divisor > dividend") {
const auto res = GeneralUtility::DigitstringDivision<std::string>("1f4", 999, set);
const auto res = BaseConversion::DigitstringDivision<std::string>("1f4", 999, set);
REQUIRE(res.first == "0");
REQUIRE(res.second == 500);
}
}
// Tests that base2 division is working, with oracle values
TEST_CASE(__FILE__"/Base2", "[DigitstringDivision]")
TEST_CASE(__FILE__"/Base2", "[BaseConversion][DigitstringDivision]")
{
const std::string set = "01";
SECTION("No rest") {
const auto res = GeneralUtility::DigitstringDivision<std::string>("111110100", 10, set);
const auto res = BaseConversion::DigitstringDivision<std::string>("111110100", 10, set);
REQUIRE(res.first == "110010");
REQUIRE(res.second == 0);
}
SECTION("With rest") {
const auto res = GeneralUtility::DigitstringDivision<std::string>("111111011", 10, set);
const auto res = BaseConversion::DigitstringDivision<std::string>("111111011", 10, set);
REQUIRE(res.first == "110010");
REQUIRE(res.second == 7);
}
SECTION("With rest, and divisor > dividend") {
const auto res = GeneralUtility::DigitstringDivision<std::string>("111110100", 599, set);
const auto res = BaseConversion::DigitstringDivision<std::string>("111110100", 599, set);
REQUIRE(res.first == "0");
REQUIRE(res.second == 500);
}
}
// Tests that fucking big bases are working, with oracle values
TEST_CASE(__FILE__"/BaseFuckingBig", "[DigitstringDivision]")
TEST_CASE(__FILE__"/BaseFuckingBig", "[BaseConversion][DigitstringDivision]")
{
const std::string set = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
SECTION("No rest") {
const auto res = GeneralUtility::DigitstringDivision<std::string>("9RieQV", 29915, set);
const auto res = BaseConversion::DigitstringDivision<std::string>("9RieQV", 29915, set);
REQUIRE(res.first == "1DGL");
REQUIRE(res.second == 0);
}
SECTION("With rest") {
const auto res = GeneralUtility::DigitstringDivision<std::string>("9RieQZ", 29915, set);
const auto res = BaseConversion::DigitstringDivision<std::string>("9RieQZ", 29915, set);
REQUIRE(res.first == "1DGL");
REQUIRE(res.second == 4);
}
SECTION("With rest, and divisor > dividend") {
const auto res = GeneralUtility::DigitstringDivision<std::string>("8x", 600, set);
const auto res = BaseConversion::DigitstringDivision<std::string>("8x", 600, set);
REQUIRE(res.first == "0");
REQUIRE(res.second == 555);
}
}
// Tests that having a dividend of size 0 returns 0
TEST_CASE(__FILE__"/DividendLengthZero", "[DigitstringDivision]")
TEST_CASE(__FILE__"/DividendLengthZero", "[BaseConversion][DigitstringDivision]")
{
const auto& res = GeneralUtility::DigitstringDivision<std::string>("", 19, "0123456789");
const auto& res = BaseConversion::DigitstringDivision<std::string>("", 19, "0123456789");
REQUIRE(res.first == "0");
REQUIRE(res.second == 0);
}
// Tests that a division by zero exception is thrown when appropriate
TEST_CASE(__FILE__"/DivisionByZero", "[DigitstringDivision]")
TEST_CASE(__FILE__"/DivisionByZero", "[BaseConversion][DigitstringDivision]")
{
REQUIRE_THROWS_AS(
GeneralUtility::DigitstringDivision<std::string>("699", 0, "0123456789")
BaseConversion::DigitstringDivision<std::string>("699", 0, "0123456789")
, std::overflow_error);
}
// Tests that a logic error is thrown when the supplied set is empty
TEST_CASE(__FILE__"/NoSetSupplied", "[DigitstringDivision]")
TEST_CASE(__FILE__"/NoSetSupplied", "[BaseConversion][DigitstringDivision]")
{
REQUIRE_THROWS_AS(
GeneralUtility::DigitstringDivision<std::string>("699", 5, "")
BaseConversion::DigitstringDivision<std::string>("699", 5, "")
, std::logic_error);
}
// Tests that a logic error is thrown if the dividend contains a character not present in the set
TEST_CASE(__FILE__"/InvalidDigitsInDividend", "[DigitstringDivision]")
TEST_CASE(__FILE__"/InvalidDigitsInDividend", "[BaseConversion][DigitstringDivision]")
{
REQUIRE_THROWS_AS(
GeneralUtility::DigitstringDivision<std::string>("699z", 5, "0123465789")
BaseConversion::DigitstringDivision<std::string>("699z", 5, "0123465789")
, std::logic_error);
}
// Tests that weird, abstract bases are working, with oracle values
TEST_CASE(__FILE__"/BaseWeird", "[DigitstringDivision]")
TEST_CASE(__FILE__"/BaseWeird", "[BaseConversion][DigitstringDivision]")
{
const std::vector<std::string> set = { "apple", "apricot", "avocado", "banana", "bell pepper", "bilberry" };
SECTION("No rest") {
const auto res = GeneralUtility::DigitstringDivision<std::vector<std::string>>({"apricot", "bilberry", "bell pepper"}, 10, set);
const auto res = BaseConversion::DigitstringDivision<std::vector<std::string>>({"apricot", "bilberry", "bell pepper"}, 10, set);
REQUIRE(res.first == std::vector<std::string>({"apricot", "apricot"}));
REQUIRE(res.second == 0);
}
SECTION("With rest") {
const auto res = GeneralUtility::DigitstringDivision<std::vector<std::string>>({"avocado", "apple", "banana"}, 10, set);
const auto res = BaseConversion::DigitstringDivision<std::vector<std::string>>({"avocado", "apple", "banana"}, 10, set);
REQUIRE(res.first == std::vector<std::string>({"apricot", "apricot"}));
REQUIRE(res.second == 5);
}
SECTION("With rest, and divisor > dividend") {
const auto res = GeneralUtility::DigitstringDivision<std::vector<std::string>>({"apricot", "bilberry", "bell pepper"}, 100, set);
const auto res = BaseConversion::DigitstringDivision<std::vector<std::string>>({"apricot", "bilberry", "bell pepper"}, 100, set);
REQUIRE(res.first == std::vector<std::string>({"apple"}));
REQUIRE(res.second == 70);
}

View File

@ -1,61 +1,61 @@
#include <GeneralUtility.h>
#include <ContainerUtility.h>
#include "Catch2.h"
#include <string>
#include <vector>
// Tests that the Ord method works with characters in a string
TEST_CASE(__FILE__"/WorksWithCharsInString", "[Ord]")
TEST_CASE(__FILE__"/WorksWithCharsInString", "[ContainerUtility][Ord]")
{
const std::string set = "0123456789abcdef";
REQUIRE(GeneralUtility::Ord('0', set) == 0);
REQUIRE(GeneralUtility::Ord('1', set) == 1);
REQUIRE(GeneralUtility::Ord('2', set) == 2);
REQUIRE(GeneralUtility::Ord('3', set) == 3);
REQUIRE(GeneralUtility::Ord('4', set) == 4);
REQUIRE(GeneralUtility::Ord('5', set) == 5);
REQUIRE(GeneralUtility::Ord('6', set) == 6);
REQUIRE(GeneralUtility::Ord('7', set) == 7);
REQUIRE(GeneralUtility::Ord('8', set) == 8);
REQUIRE(GeneralUtility::Ord('9', set) == 9);
REQUIRE(GeneralUtility::Ord('a', set) == 10);
REQUIRE(GeneralUtility::Ord('b', set) == 11);
REQUIRE(GeneralUtility::Ord('c', set) == 12);
REQUIRE(GeneralUtility::Ord('d', set) == 13);
REQUIRE(GeneralUtility::Ord('e', set) == 14);
REQUIRE(GeneralUtility::Ord('f', set) == 15);
REQUIRE(ContainerUtility::Ord('0', set) == 0);
REQUIRE(ContainerUtility::Ord('1', set) == 1);
REQUIRE(ContainerUtility::Ord('2', set) == 2);
REQUIRE(ContainerUtility::Ord('3', set) == 3);
REQUIRE(ContainerUtility::Ord('4', set) == 4);
REQUIRE(ContainerUtility::Ord('5', set) == 5);
REQUIRE(ContainerUtility::Ord('6', set) == 6);
REQUIRE(ContainerUtility::Ord('7', set) == 7);
REQUIRE(ContainerUtility::Ord('8', set) == 8);
REQUIRE(ContainerUtility::Ord('9', set) == 9);
REQUIRE(ContainerUtility::Ord('a', set) == 10);
REQUIRE(ContainerUtility::Ord('b', set) == 11);
REQUIRE(ContainerUtility::Ord('c', set) == 12);
REQUIRE(ContainerUtility::Ord('d', set) == 13);
REQUIRE(ContainerUtility::Ord('e', set) == 14);
REQUIRE(ContainerUtility::Ord('f', set) == 15);
}
// Tests that, if an object is not found, -1 is returned
TEST_CASE(__FILE__"/ReturnsNeg1IfNotFound", "[Ord]")
TEST_CASE(__FILE__"/ReturnsNeg1IfNotFound", "[ContainerUtility][Ord]")
{
const std::string set = "0123456789abcdef";
REQUIRE(GeneralUtility::Ord('z', set) == -1);
REQUIRE(ContainerUtility::Ord('z', set) == -1);
}
// Tests that Ord works with vectors <string>
TEST_CASE(__FILE__"/WorksWithVector_String", "[Ord]")
TEST_CASE(__FILE__"/WorksWithVector_String", "[ContainerUtility][Ord]")
{
const std::vector<std::string> vec = { "Apple", "Banana", "Tomato", "Olives" };
REQUIRE(GeneralUtility::Ord(std::string("Apple"), vec) == 0);
REQUIRE(GeneralUtility::Ord(std::string("Banana"), vec) == 1);
REQUIRE(GeneralUtility::Ord(std::string("Tomato"), vec) == 2);
REQUIRE(GeneralUtility::Ord(std::string("Olives"), vec) == 3);
REQUIRE(ContainerUtility::Ord(std::string("Apple"), vec) == 0);
REQUIRE(ContainerUtility::Ord(std::string("Banana"), vec) == 1);
REQUIRE(ContainerUtility::Ord(std::string("Tomato"), vec) == 2);
REQUIRE(ContainerUtility::Ord(std::string("Olives"), vec) == 3);
INFO("Now testing that unknown is -1");
REQUIRE(GeneralUtility::Ord(std::string("Pepper"), vec) == -1);
REQUIRE(ContainerUtility::Ord(std::string("Pepper"), vec) == -1);
}
// Tests that Ord works with vectors <int>
TEST_CASE(__FILE__"/WorksWithVector_Int", "[Ord]")
TEST_CASE(__FILE__"/WorksWithVector_Int", "[ContainerUtility][Ord]")
{
const std::vector<int> vec = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (std::size_t i = 0 ; i < 10; i++)
REQUIRE(GeneralUtility::Ord((int)i, vec) == i);
REQUIRE(ContainerUtility::Ord((int)i, vec) == i);
INFO("Now testing that unknown is -1");
REQUIRE(GeneralUtility::Ord((int)99, vec) == -1);
REQUIRE(ContainerUtility::Ord((int)99, vec) == -1);
}