diff --git a/include/constructors/constructors.hpp b/include/constructors/constructors.hpp index 6e6ebbe..dff2114 100644 --- a/include/constructors/constructors.hpp +++ b/include/constructors/constructors.hpp @@ -9,6 +9,7 @@ #include "BigInt.hpp" #include "functions/utility.hpp" +#include "operators/arithmetic_assignment.hpp" /* @@ -47,31 +48,81 @@ BigInt::BigInt(const long long& num) { } +/* + Anonymous namespace + ----------------- + Contains helper functions for constructing an BigInt from Strings +*/ + +namespace { + +/* + parse_hex_string + -------------------- + Parses the provided string store the result in magnitude. + str is assumed to have no sign. str must be in hex. First two chars are ignored +*/ + + void parse_hex_string(std::string &&str, std::string &magnitude) { + BigInt res; + for (size_t idx = 2; idx < str.size(); idx++) { + res *= 16; + char c = str[idx]; + // digit + if (c >= '0' && c <= '9') + res += c - '0'; + // lowercase hex + else if (c >= 'a' && c <= 'f') + res += c - 'a' + 10; + // uppercase hex + else if (c >= 'A' && c <= 'F') + res += c - 'A' + 10; + else + throw std::invalid_argument("Expected a hex integer, got \'" + str + "\'"); + } + magnitude = res.to_string(); + } + + +/* + parse_string + -------------------- + Parses the provided string store the result in magnitude. + str is assumed to have no sign. str can be given in hex by prepending "0x" +*/ + + void parse_string(std::string &&str, std::string &magnitude) { + if (str.size() > 1 && str[0] == '0' && str[1] == 'x') { + parse_hex_string(std::move(str), magnitude); + return; + } + + // check if every char is decimal + for (auto c : str) + if (c < '0' || c > '9') + throw std::invalid_argument("Expected a decimal integer, got \'" + str + "\'"); + + magnitude = std::move(str); + } + +} // namespace + /* String to BigInt ---------------- */ BigInt::BigInt(const std::string& num) { - if (num[0] == '+' or num[0] == '-') { // check for sign - std::string magnitude = num.substr(1); - if (is_valid_number(magnitude)) { - value = magnitude; + std::string magnitude = num; + sign = '+'; + if (magnitude[0] == '+' or magnitude[0] == '-') { sign = num[0]; - } - else { - throw std::invalid_argument("Expected an integer, got \'" + num + "\'"); - } - } - else { // if no sign is specified - if (is_valid_number(num)) { - value = num; - sign = '+'; // positive by default - } - else { - throw std::invalid_argument("Expected an integer, got \'" + num + "\'"); - } + // delete first char + magnitude.erase(0,1); } + + parse_string(std::move(magnitude), value); + strip_leading_zeroes(value); } diff --git a/include/functions/utility.hpp b/include/functions/utility.hpp index a26ed45..83ec121 100644 --- a/include/functions/utility.hpp +++ b/include/functions/utility.hpp @@ -10,21 +10,6 @@ #include -/* - is_valid_number - --------------- - Checks whether the given string is a valid integer. -*/ - -bool is_valid_number(const std::string& num) { - for (char digit : num) - if (digit < '0' or digit > '9') - return false; - - return true; -} - - /* strip_leading_zeroes -------------------- diff --git a/test/constructors/constructors.cpp b/test/constructors/constructors.cpp index 7bfd773..1d9bb64 100644 --- a/test/constructors/constructors.cpp +++ b/test/constructors/constructors.cpp @@ -22,6 +22,25 @@ TEST_CASE("Construct zero-valued BigInts", "[constructors]") { REQUIRE(num4 == 0); } +TEST_CASE("Construct BigInts from hex strings", "[constructors]") { + BigInt num1_hex("0x0"); // should be 0 by default + BigInt num1_dec("0"); + REQUIRE(num1_hex == 0); + REQUIRE(num1_hex == num1_dec); + + BigInt num2("0xfa54"); // 0xf54 passed as an integer + REQUIRE(num2 == 0xfa54); + + BigInt num3("0x0f7ab4d822924430b1b97859af0eaa41"); + REQUIRE(num3 == "20575548111226062968771475379166554689"); + + BigInt num4("-0x0f7ab4d822924430b1b97859af0eaa41"); + REQUIRE(num4 == "-20575548111226062968771475379166554689"); + + BigInt num5("-0x0F7AB4D822924430B1B97859AF0EAA41"); + REQUIRE(num5 == "-20575548111226062968771475379166554689"); +} + TEST_CASE("Randomly construct BigInts from integers and strings", "[constructors][random][integer][string]") { std::random_device generator; @@ -39,13 +58,21 @@ TEST_CASE("Randomly construct BigInts from integers and strings", BigInt trouble("123BigInt"); // without sign } catch (std::logic_error &e) { - CHECK(e.what() == std::string("Expected an integer, got \'123BigInt\'")); + CHECK(e.what() == std::string("Expected a decimal integer, got \'123BigInt\'")); } try { BigInt trouble("-4a5b6c"); // with sign // looks like hex, but is still not allowed } catch (std::logic_error &e) { - CHECK(e.what() == std::string("Expected an integer, got \'-4a5b6c\'")); + CHECK(e.what() == std::string("Expected a decimal integer, got \'4a5b6c\'")); + } + + try { + BigInt trouble("0x4a5b6cg7923"); // with sign + // looks like hex, but contains illegal g + } + catch (std::logic_error &e) { + CHECK(e.what() == std::string("Expected a hex integer, got \'0x4a5b6cg7923\'")); } }