Skip to content

Commit

Permalink
use improved base conversions provided by strmath.hpp
Browse files Browse the repository at this point in the history
  • Loading branch information
radj307 committed Mar 3, 2022
1 parent 47746fd commit 1a4f99b
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 213 deletions.
28 changes: 16 additions & 12 deletions conv2/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ struct PrintHelp {
<< " -b --bitwise Perform bitwise calculations on binary, decimal, and/or hexadecimal numbers." << '\n'
<< '\n'
<< "MODIFIERS:\n"
<< " -B --binary Print the output value(s) in binary instead of decimal."
<< " -B --binary Print the output value(s) in binary instead of decimal." << '\n'
<< " -O --octal Print the output value(s) in octal instead of decimal." << '\n'
<< " -x --hex Print the output value(s) in hexadecimal instead of decimal." << '\n'
<< '\n'
<< "USAGE:\n"
Expand Down Expand Up @@ -302,7 +303,8 @@ int main(const int argc, char** argv)

// handle blocking arguments
color.setActive(!checkarg('n', "no-color"));
bool quiet{ checkarg('q', "quiet") };
bool extraQuiet{ checkarg('Q', "extra-quiet") };
bool quiet{ extraQuiet || checkarg('q', "quiet") };
bool numGrouping{ checkarg('g', "group") };

// [-h|--help]
Expand All @@ -312,7 +314,7 @@ int main(const int argc, char** argv)
}
// [-v|--version]
else if (checkarg('v', "version")) {
std::cout << (quiet ? "" : "conv2 ") << CONV2_VERSION << std::endl;
std::cout << (quiet ? "" : "conv2 v") << CONV2_VERSION << std::endl;
return 0;
}

Expand Down Expand Up @@ -358,14 +360,14 @@ int main(const int argc, char** argv)
for (const auto& it : parameters) {
if (!quiet)
buffer << color(OUTCOLOR::INPUT) << it << color() << ' ' << color(OUTCOLOR::OPERATOR) << '=' << color() << ' ';
switch (base::detect_base(it, base::ValueBase::DECIMAL | base::ValueBase::HEXADECIMAL)) {
case base::ValueBase::DECIMAL:
buffer << color(OUTCOLOR::OUTPUT) << base::decimalToHex(it, std::uppercase, (numGrouping ? str::NumberGrouping : str::Placeholder), "0x") << color() << '\n';
switch (base::detectBase(it, Base::DECIMAL | Base::HEXADECIMAL)) {
case Base::DECIMAL:
buffer << color(OUTCOLOR::OUTPUT) << "0x" << str::fromBase10(it, 16) << color() << '\n';
break;
case base::ValueBase::HEXADECIMAL:
buffer << color(OUTCOLOR::OUTPUT) << base::hexToDecimal(it) << color() << '\n';
case Base::HEXADECIMAL:
buffer << color(OUTCOLOR::OUTPUT) << str::toBase10(it, 16) << color() << '\n';
break;
case base::ValueBase::INVALID: [[fallthrough]];
case Base::ZERO: [[fallthrough]];
default:
throw make_exception("Invalid number: \"", it, "\"!");
}
Expand Down Expand Up @@ -545,12 +547,14 @@ int main(const int argc, char** argv)
buffer << color(OUTCOLOR::INPUT) << left << color() << ' ' << color(OUTCOLOR::OPERATOR) << operation << color() << ' ' << color(OUTCOLOR::INPUT) << right << color() << ' ' << color(OUTCOLOR::OPERATOR) << '=' << color() << ' ';
}
// print output
base::value result{ bitwise::calculateOperation(it) };
long long result{ bitwise::calculateOperation(it) };
std::string outstr;
if (checkarg('B', "binary"))
outstr = base::decimalToBinary(result);
outstr = str::fromBase10(result, 2);
else if (checkarg('O', "octal"))
outstr = str::fromBase10(result, 8);
else if (checkarg('x', "hex"))
outstr = base::decimalToHex(result);
outstr = str::fromBase10(result, 16);
else
outstr = std::to_string(result);
buffer << color(OUTCOLOR::OUTPUT) << outstr << color() << '\n';
Expand Down
270 changes: 83 additions & 187 deletions convlib/include/base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,198 +4,94 @@
#include <algorithm>
#include <str.hpp>
#include <indentor.hpp>
#include <strmath.hpp>

namespace base {
using value = long long;
using otherval = std::string;


inline value binaryToDecimal(const std::string& binary) noexcept(false)
{
value v{ 0ll };
for (const auto& ch : binary) {
v <<= 1ll;
if (ch == '1')
v |= 0b1;
else if (ch != '0')
throw make_exception("Invalid binary number: \'", ch, '\'');
}
return v;
}
inline otherval decimalToBinary(value n) noexcept
{
otherval v{};
for (; n > 0ll; n /= 2ll)
v += n % 2ll;
return v;
}

/**
* @brief Converts a given character to its equivalent hexadecimal value.
* @param ch - Input character. 1-9, A=10, B=11, C=12, D=13, E=14, F=15. Any character outside of this range will throw an exception.
* @returns value
*/
inline value hexToDecimal(const char ch) noexcept(false)
{
if (isdigit(ch))
return static_cast<value>(ch - '0');
else if (isalpha(ch))
return (static_cast<value>(str::toupper(ch) - 'A') + 10);
throw make_exception("getHexValue()\tFailed to convert \'", ch, "\' to hexadecimal!");
}

/**
* @brief Convert from hexadecimal to decimal.
* @param hex Input hexadecimal value.
* @returns value
*/
inline value hexToDecimal(otherval const& hex) noexcept(false)
{
const auto from_base{ 16 };
if (!hex.empty()) {
value power{ 1 }, result{ 0 };
for (auto c{ hex.rbegin() }; c != hex.rend(); ++c) {
if (*c == 'x')
break; // avoid checking "0x" prefix
const auto v{ hexToDecimal(*c) };
if (v >= from_base)
throw make_exception("Hexadecimal value \'", *c, "\' converted to invalid hex number \"", v, "\"!\n", indent(10), "Please report this error to the developer!");
result += v * power;
power *= from_base;
# pragma region BaseEnum

namespace enumerator {
// @brief The underlying type of the Base enum.
using BaseT = unsigned char;
// @brief Enum that represents recognized numeric bases.
enum class Base : BaseT {
ZERO = 0,
BINARY = 2,
OCTAL = 8,
DECIMAL = 10,
HEXADECIMAL = 16,
};
[[nodiscard]] Base operator|(const Base& l, const Base& r) { return static_cast<Base>(static_cast<BaseT>(l) | static_cast<BaseT>(r)); }
[[nodiscard]] Base operator^(const Base& l, const Base& r) { return static_cast<Base>(static_cast<BaseT>(l) ^ static_cast<BaseT>(r)); }
[[nodiscard]] Base operator&(const Base& l, const Base& r) { return static_cast<Base>(static_cast<BaseT>(l) & static_cast<BaseT>(r)); }
[[nodiscard]] bool operator==(Base& base, const BaseT& o) { return static_cast<BaseT>(base) == o; }
[[nodiscard]] bool operator==(BaseT& baset, const Base& o) { return baset == static_cast<BaseT>(o); }
[[nodiscard]] bool operator!=(Base& base, auto&& o) { return !operator==(base, std::forward<decltype(o)>(o)); }

inline std::string BaseToString(const Base& b)
{
switch (b) {
case Base::BINARY:
return "Binary";
case Base::OCTAL:
return "Octal";
case Base::DECIMAL:
return "Decimal";
case Base::HEXADECIMAL:
return "Hexadecimal";
case Base::ZERO: [[fallthrough]];
default:
break;
}
return result;
return "(null)";
}
inline Base StringToBase(std::string str)
{
str = str::tolower(str);

if (str == "binary")
return Base::BINARY;
else if (str == "octal")
return Base::OCTAL;
else if (str == "decimal")
return Base::DECIMAL;
else if (str == "hexadecimal")
return Base::HEXADECIMAL;

return Base::ZERO;
}
else throw make_exception("Received an empty hexadecimal number, conversion failed!");
}

/**
* @brief Convert from decimal to hexadecimal.
* @param dec Input decimal value.
* @param ...fmt Additional stream formatting objects to include before printing the value.
* @returns otherval
*/
template<var::Streamable<std::stringstream>... Ts>
inline otherval decimalToHex(value const& dec, Ts&&... fmt) noexcept
{
return str::stringify(
std::hex,
std::forward<Ts>(fmt)...,
dec
);
}

/**
* @brief Convert from decimal to hexadecimal.
* @param dec Input decimal value as a string.
* @param ...fmt Additional stream formatting objects to include before printing the value.
* @returns otherval
*/
template<var::Streamable<std::stringstream>... Ts>
inline otherval decimalToHex(std::string const& dec, Ts&&... fmt) noexcept
{
return str::stringify(
std::hex,
std::forward<Ts>(fmt)...,
str::stoll(dec)
);
}

/**
* @enum ValueBase
* @brief Used to represent the base of a number. Only used internally within the base namespace.
*/
enum class ValueBase : unsigned char {
INVALID,
BINARY,
OCTAL,
DECIMAL,
HEXADECIMAL,
};

static bool validBinary(const std::string& str)
{
const auto& lc{ str::tolower(str) };
return std::all_of(str.begin() + (str::startsWith(lc, "0b") ? 2ull : 0ull), str.end(), [](auto&& c) {
return c == '0' || c == '1';
});
}
static bool validOctal(std::string str)
{
str = str::tolower(str);
return std::all_of(str.begin() + !!str::startsWith(str, '\\'), str.end(), [](auto&& c) {
return c >= '0' && c <= '7';
});
}
static bool validDecimal(std::string str)
{
return std::all_of(str.begin() + !!str::startsWith(str, '-'), str.end(), [](auto&& c) {
return isdigit(c) || c == '.';
});
}
static bool validHexadecimal(const std::string& str)
{
const auto& lc{ str::tolower(str) };
return std::all_of(lc.begin() + (str::startsWith(lc, "0x") ? 2ull : (str.size() > 0ull && str.at(0ull) == '#' ? 1ull : 0ull)), lc.end(), [](auto&& c) {
return isdigit(c) || (c >= 'a' && c <= 'f');
});
}

base::ValueBase operator|(const base::ValueBase& l, const base::ValueBase r)
{
return static_cast<base::ValueBase>(static_cast<unsigned char>(l) | static_cast<unsigned char>(r));
}
base::ValueBase operator&(const base::ValueBase& l, const base::ValueBase r)
{
return static_cast<base::ValueBase>(static_cast<unsigned char>(l) & static_cast<unsigned char>(r));
}
base::ValueBase operator^(const base::ValueBase& l, const base::ValueBase r)
{
return static_cast<base::ValueBase>(static_cast<unsigned char>(l) ^ static_cast<unsigned char>(r));
}

base::ValueBase& operator|=(base::ValueBase& l, const base::ValueBase r)
{
return l = static_cast<base::ValueBase>(static_cast<unsigned char>(l) | static_cast<unsigned char>(r));
}
base::ValueBase& operator&=(base::ValueBase& l, const base::ValueBase r)
{
return l = static_cast<base::ValueBase>(static_cast<unsigned char>(l) & static_cast<unsigned char>(r));
}
base::ValueBase& operator^=(base::ValueBase& l, const base::ValueBase r)
{
return l = static_cast<base::ValueBase>(static_cast<unsigned char>(l) ^ static_cast<unsigned char>(r));
}

template<var::numeric T> [[nodiscard]] bool operator==(const base::ValueBase& l, const T& r)
{
return static_cast<T>(l) == r;
}
template<var::numeric T> [[nodiscard]] bool operator!=(const base::ValueBase& l, const T& r)
{
return static_cast<T>(l) != r;
}


inline ValueBase detect_base(const std::string& arg, const ValueBase& allowedTypes)
{
if ((allowedTypes & ValueBase::BINARY) != 0 && validBinary(arg))
return ValueBase::BINARY;
else if ((allowedTypes & ValueBase::OCTAL) != 0 && validOctal(arg))
return ValueBase::OCTAL;
else if ((allowedTypes & ValueBase::DECIMAL) != 0 && validDecimal(arg))
return ValueBase::DECIMAL;
else if ((allowedTypes & ValueBase::HEXADECIMAL) != 0 && validHexadecimal(arg))
return ValueBase::HEXADECIMAL;
else return ValueBase::INVALID;
using enumerator::Base;

# pragma endregion BaseEnum

inline Base detectBase(const std::string& s, const Base& allowBases = Base::BINARY | Base::OCTAL | Base::DECIMAL | Base::HEXADECIMAL)
{
const auto& allow{ [&allowBases](const Base& base) {
return (base & allowBases) != Base::ZERO;
} };
// check string prefix:
if (str::startsWith(s, "0b"))
return Base::BINARY;
else if (str::startsWith(s, '\\'))
return Base::OCTAL;
else if (str::startsWith(s, "0x"))
return Base::HEXADECIMAL;
// fallback to checking string contents:
// don't check binary or octal because both must be explicitly specified and both contain all decimal digits.
else if (str::isdecimal(s))
return Base::DECIMAL;
else if (str::ishex(s))
return Base::HEXADECIMAL;
// invalid number:
return Base::ZERO;
}

}

using base::operator&;
using base::operator&=;
using base::operator|;
using base::operator|=;
using base::operator^;
using base::operator^=;
using base::operator==;
using base::operator!=;
using base::enumerator::Base;
using base::enumerator::operator|;
using base::enumerator::operator^;
using base::enumerator::operator&;
using base::enumerator::operator==;
using base::enumerator::operator!=;
using base::enumerator::BaseToString;
using base::enumerator::StringToBase;
Loading

0 comments on commit 1a4f99b

Please sign in to comment.