diff --git a/bigint.h b/bigint.h index c0ffe87..88db96d 100644 --- a/bigint.h +++ b/bigint.h @@ -44,18 +44,20 @@ namespace BigInt { class bigint { public: - static const auto MAX_SIZE = 1000000000000000000LL; + // LLONG_MAX = 9'223'372'036'854'775'807 + static constexpr auto MAX_SIZE = 1'000'000'000'000'000'000LL; - bigint() = default; + bigint() {is_neg = false; vec.emplace_back(0);} bigint(const std::string &s) { - if (!is_bigint(s)) - throw std::runtime_error("Invalid Big Integer."); + if (!is_bigint(s)) {throw std::runtime_error("Invalid Big Integer.");} if(s[0] == '-') { + *this = bigint(s.substr(1)); + if (*this == 0) + throw std::runtime_error("Invalid Big Integer."); is_neg = true; - vec = string_to_vector(s.substr(1)); } else { @@ -65,7 +67,7 @@ namespace BigInt { bigint(const char c) { - int temp = static_cast(c); + int temp = static_cast(c); if (isdigit(temp)) { *this = bigint(char_to_int(c)); } else { @@ -73,29 +75,35 @@ namespace BigInt { } } - bigint(unsigned int n) : bigint(static_cast(n)) {} + bigint(const unsigned int n) : bigint(static_cast(n)) {} - bigint(int n) : bigint(static_cast(n)) {} + bigint(const int n) : bigint(static_cast(n)) {} - bigint(long n) : bigint(static_cast(n)) {} + bigint(const long n) : bigint(static_cast(n)) {} - bigint(double n) : bigint(static_cast(n)) {} + bigint(const double n) : bigint(static_cast(n)) {} - bigint(long long n) { - if ( n >= MAX_SIZE || n <= -MAX_SIZE) + bigint(const long long n) { + if (n == 0) { - vec.emplace_back(n / MAX_SIZE); - vec.emplace_back(n % MAX_SIZE); - } - else{ - vec.emplace_back(n); + is_neg = false; + vec.push_back(0); + return; } + is_neg = (n<0); - if (n < 0) + unsigned long long val = n; + if (is_neg) { - is_neg = true; - for (auto& x : vec) { x = std::abs(x); } + val = 0ULL - val; + } + vec.reserve(2); + + while (val > 0) { + vec.emplace_back(val % MAX_SIZE); + val /= MAX_SIZE; } + std::reverse(vec.begin(), vec.end()); } bigint(unsigned long long n) { @@ -136,13 +144,14 @@ namespace BigInt { return vec.back(); } - explicit operator std::string() { - return vector_to_string((*this).vec); + explicit operator std::string() const + { + return (this->is_neg ? "-" : "") + vector_to_string(this->vec); } friend std::ostream &operator<<(std::ostream &stream, const bigint &n) { - stream << vector_to_string(n.vec); + stream << std::string(n); return stream; } @@ -152,7 +161,7 @@ namespace BigInt { return *this; } - inline bigint operator+(const bigint &rhs) const + bigint operator+(const bigint &rhs) const { bigint result = *this; result += rhs; @@ -161,12 +170,11 @@ namespace BigInt { bigint operator-=(const bigint &rhs) { - *this = subtract(*this, rhs); return *this; } - inline bigint operator-(const bigint &rhs) const + bigint operator-(const bigint &rhs) const { bigint result = *this; result -= rhs; @@ -179,7 +187,7 @@ namespace BigInt { return *this; } - inline bigint operator*(const bigint &rhs) const + bigint operator*(const bigint &rhs) const { bigint result = *this; result *= rhs; @@ -192,7 +200,7 @@ namespace BigInt { return *this; } - inline bigint operator/(const bigint &rhs) const + bigint operator/(const bigint &rhs) const { bigint result = *this; result /= rhs; @@ -205,7 +213,7 @@ namespace BigInt { return *this; } - inline bigint operator%(const bigint &rhs) const + bigint operator%(const bigint &rhs) const { bigint result = *this; result %= rhs; @@ -259,39 +267,38 @@ namespace BigInt { { return r < l; } friend bool operator<=(const bigint &l, const bigint &r) - { return !(r < l); } + { return r >= l; } friend bool operator>=(const bigint &l, const bigint &r) { return !(l < r); } - explicit operator bool() const - { - return !(this->vec.empty()) || this->vec.front(); + explicit operator bool() const { + return !(vec.size() == 1 && vec.front() == 0); } friend std::hash; - inline static bigint pow(const bigint &base, const bigint &exponent) + static bigint pow(const bigint &base, const bigint &exponent) { if (exponent == 0) return 1; if (exponent == 1) return base; - bigint tmp = pow(base, exponent / 2); - if (exponent % 2 == 0) return tmp * tmp; - else return base * tmp * tmp; + const bigint tmp = pow(base, exponent / 2); + if (exponent % 2 == 0) {return tmp * tmp;} + return base * tmp * tmp; } - inline static bigint maximum(const bigint &lhs, const bigint &rhs) + static bigint maximum(const bigint &lhs, const bigint &rhs) { return lhs > rhs ? lhs : rhs; } - inline static bigint minimum(const bigint &lhs, const bigint &rhs) + static bigint minimum(const bigint &lhs, const bigint &rhs) { return lhs > rhs ? rhs : lhs; } - inline static bigint abs(const bigint &s) + static bigint abs(const bigint &s) { if (is_negative(s)) { @@ -320,26 +327,26 @@ namespace BigInt { static bigint gcd(const bigint &, const bigint &); - inline static bigint lcm(const bigint &lhs, const bigint &rhs) + static bigint lcm(const bigint &lhs, const bigint &rhs) { return (lhs * rhs) / gcd(lhs, rhs); } static bigint factorial(const bigint &); - inline static bool is_even(const bigint &input) + static bool is_even(const bigint &input) { return !(input.vec.back() & 1); } - inline static bool is_negative(const bigint &input) + static bool is_negative(const bigint &input) { return input.is_neg; } static bool is_prime(const bigint &); - inline static bigint sum_of_digits(const bigint& input) + static bigint sum_of_digits(const bigint& input) { bigint sum; for (auto base : input.vec) { @@ -376,8 +383,9 @@ namespace BigInt { static bigint divide(const bigint &, const bigint &); - inline static bigint mod(const bigint &lhs, const bigint &rhs) + static bigint mod(const bigint &lhs, const bigint &rhs) { + if (rhs == 0) {throw std::domain_error("Attempted to modulo by zero.");} if (lhs < rhs) { return lhs; } @@ -397,24 +405,24 @@ namespace BigInt { static int count_digits(const bigint&); - inline static int char_to_int(const char input) + static int char_to_int(const char input) { return input - '0'; } - inline static int int_to_char(const int input) + static int int_to_char(const int input) { return input + '0'; } - inline static bigint negate(const bigint& input) + static bigint negate(const bigint& input) { bigint temp = input; temp.is_neg = true; return temp; } - inline static bool less_than(const bigint& lhs, const bigint& rhs) + static bool less_than(const bigint& lhs, const bigint& rhs) { if (is_negative(lhs) && is_negative(rhs)) { @@ -437,8 +445,8 @@ namespace BigInt { inline bool bigint::is_bigint(const std::string &s) - { // Checks if the input integer is valid Number or not. - if (s.empty()) + { + if (s.empty() || (s.length() > 1 && s[0] == '0')) return false; if (s[0] == '-') { @@ -447,7 +455,7 @@ namespace BigInt { return s.find_first_not_of("0123456789", 0) == std::string::npos; } - std::pair add_with_carry(long long lhs, long long rhs) + inline std::pair add_with_carry(const long long lhs, const long long rhs) { auto sum = lhs + rhs; @@ -458,10 +466,8 @@ namespace BigInt { auto result = sum % bigint::MAX_SIZE; return {carry, result}; } - else - { - return {0, sum}; - } + + return {0, sum}; } inline bigint bigint::add(const bigint &lhs, const bigint &rhs) @@ -498,7 +504,7 @@ namespace BigInt { std::transform(lhs.vec.rbegin(), lhs.vec.rend(), full_rhs.vec.rbegin(), carry_result.rbegin(), add_with_carry); std::vector final(lhs.vec.size() + 1); - for (int i = carry_result.size() - 1; i >= 0; --i) { + for (int i = carry_result.size() - 1; i > 0; --i) { final[i] += carry_result[i].second; final[i - 1] += carry_result[i].first; } @@ -506,7 +512,7 @@ namespace BigInt { return trim(bigint(final)); } - std::pair subtract_with_borrow(long long lhs, long long rhs) + inline std::pair subtract_with_borrow(const long long lhs, const long long rhs) { if (lhs < rhs) { @@ -514,11 +520,9 @@ namespace BigInt { auto result = (lhs + bigint::MAX_SIZE) - rhs; return {1, result}; // 1 represents a borrow } - else - { - auto result = lhs - rhs; - return {0, result}; // 0 means no borrow - } + + auto result = lhs - rhs; + return {0, result}; // 0 means no borrow } inline bigint bigint::subtract(const bigint &lhs, const bigint &rhs) @@ -573,7 +577,7 @@ namespace BigInt { if (is_negative(lhs) && is_negative(rhs)) { - return (abs(lhs) * abs(lhs)); + return (abs(lhs) * abs(rhs)); } if (is_negative(lhs) || is_negative(rhs)) { @@ -734,7 +738,7 @@ namespace BigInt { } return exponent; - // TODO: Convert to using division after checking big O of division vs multiplication + // TODO: Convert to using division after checking bigO of division vs multiplication // std::string logVal = "-1"; // while(s != "0") { // logVal = add(logVal, "1"); @@ -782,8 +786,7 @@ namespace BigInt { inline void bigint::swap(bigint &lhs, bigint &rhs) { - bigint temp; - temp = lhs; + const bigint temp = lhs; lhs = rhs; rhs = temp; } @@ -840,7 +843,7 @@ namespace BigInt { return true; } - bigint bigint::random(size_t length) { + inline bigint bigint::random(size_t length) { const char charset[] = "0123456789"; std::default_random_engine rng(std::random_device{}()); @@ -866,18 +869,16 @@ namespace BigInt { return {str}; } - std::vector bigint::string_to_vector(std::string input) { + inline std::vector bigint::string_to_vector(std::string input) { // Break into chunks of 18 characters std::vector result; - int chunk_size = 18; + constexpr int chunk_size = 18; + const int size = input.size(); - if (input.size() > chunk_size) + if (size > chunk_size) { // Pad the length to get appropriate sized chunks - while ( input.size() % chunk_size != 0) - { - input.insert(0, "0"); - } + input.insert(0, chunk_size - (size % chunk_size), '0'); } for (int i = 0; i < input.size(); i+=chunk_size) { @@ -888,7 +889,7 @@ namespace BigInt { return result; } - std::string bigint::vector_to_string(const std::vector& input) { + inline std::string bigint::vector_to_string(const std::vector& input) { std::stringstream ss; bool first = true; for (auto partial : input) { @@ -902,28 +903,26 @@ namespace BigInt { return ss.str(); } - int bigint::count_digits(const bigint & input) { + inline int bigint::count_digits(const bigint & input) { std::string my_string = vector_to_string(input.vec); return static_cast(my_string.length()) - 1; } } // namespace::BigInt -namespace std { - template<> - struct hash { - std::size_t operator()(const BigInt::bigint& input) const - { - std::size_t seed = input.vec.size(); - for(auto x : input.vec) { - x = ((x >> 16) ^ x) * 0x45d9f3b; - x = ((x >> 16) ^ x) * 0x45d9f3b; - x = (x >> 16) ^ x; - seed ^= x + 0x9e3779b9 + (seed << 6) + (seed >> 2); - } - return seed; - } - }; -} +template<> +struct std::hash { + std::size_t operator()(const BigInt::bigint& input) const + { + std::size_t seed = input.vec.size(); + for(auto x : input.vec) { + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = (x >> 16) ^ x; + seed ^= x + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + return seed; + } +}; #endif /* BIGINT_H_ */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9e632dd..f8f7cb9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,7 +10,7 @@ include(FetchContent) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.14.0 + GIT_TAG v1.17.0 ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) diff --git a/tests/test.cpp b/tests/test.cpp index 12f5a7a..4c52b3c 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -6,262 +6,423 @@ #include #include #include +#include +#include #include "../bigint.h" using namespace BigInt; -class BigInt_test : public ::testing::Test +bool SKIP_PERFORMANCE_TESTS = true; + +constexpr std::string_view kNines = "9999999999999999999"; +constexpr std::string_view kHugeA = "37744193401458640707539380267899264828998907634573602318662036836618621958669475225851195876029606479769348216875016014259295382637670116067802415326896673540817149305648020275612344553440582481192266196913778504499507839960073829863258118424953008896871971652295554512459916848335206655076766027606195514199793888542571641680917367253163346581387963223123048507895574406540841752099433832902520291592993232666589290350588973179516741959648948892906581313716663682087787058913539002195482835009516853"; +constexpr std::string_view kHugeB = "9843174278608822755442867695930794030260633344831664962118526669621620352474231493840674921513269404"; + +constexpr std::string_view AplusB = "37744193401458640707539380267899264828998907634573602318662036836618621958669475225851195876029606479769348216875016014259295382637670116067802415326896673540817149305648020275612344553440582481192266196913778504499507839960073829863258118424953008896871971652295554512459916848335206655076766027606195514199793888542571641680917367253163346581387963223123048507895574406540841752099433832902520291602836406945198113106031840875447535989909582237738246275835190351709407411387770496036157756522786257"; +constexpr std::string_view AminusB = "37744193401458640707539380267899264828998907634573602318662036836618621958669475225851195876029606479769348216875016014259295382637670116067802415326896673540817149305648020275612344553440582481192266196913778504499507839960073829863258118424953008896871971652295554512459916848335206655076766027606195514199793888542571641680917367253163346581387963223123048507895574406540841752099433832902520291583150058387980467595146105483585947929388315548074916351598137012466166706439307508354807913496247449"; +constexpr std::string_view AmulB = "371522673656074543721939903145636335168668810492059508909919665306024609291590328396789563611909881956692074164896572222014925589282684907671824165394055196075714622557089856059685618270608317568409327841248791007327091177305027733796385625358604009972642833675600135432852203678357550499911005775538860759946390549681453248681292307393576029610404491475299486834429621773400380504580240368032838923535804505697336440062521338464979118468824407819968770248251010402682462172033117899977017721981977515651526349027968047250058213343537761879103544485699067309526235122799512472963309861508662467265612"; +constexpr std::string_view AdivB = "3834555025961928452480859540164628181402139687101087313422864247768110206281588207268817088767383084425857331782324292423438870254834351784862584845557954696973368973237622555423975476296891710599837880699585094449938105117601882807098249270619115466337592065938882369152755194714547350194277427676672295574028506048275631567039158386540348169130522428891371330855740227809243175650789881054423775986"; +constexpr std::string_view AmodB = "2156048695288816089284014643351839933776226311026153430599415874278141145999872948608252665245784509"; + +constexpr std::string_view BminusA = "-37744193401458640707539380267899264828998907634573602318662036836618621958669475225851195876029606479769348216875016014259295382637670116067802415326896673540817149305648020275612344553440582481192266196913778504499507839960073829863258118424953008896871971652295554512459916848335206655076766027606195514199793888542571641680917367253163346581387963223123048507895574406540841752099433832902520291583150058387980467595146105483585947929388315548074916351598137012466166706439307508354807913496247449"; + +struct TestCase +{ + std::string lhs; + std::string rhs; + std::string expected; + + TestCase(const std::string_view lhs, const std::string_view rhs, const std::string_view expected) : lhs(lhs), rhs(rhs), expected(expected) {} +}; + +class Test_BigInt : public ::testing::Test{}; +class Test_BigInt_Performance : public ::testing::Test { protected: - BigInt_test() = default; - ~BigInt_test() override = default; - void SetUp() override {} - void TearDown() override {} + void SetUp() override { + if (SKIP_PERFORMANCE_TESTS) + GTEST_SKIP() << "Skipping performance tests for this fixture"; + } }; -TEST(BigInt_test, Creation_Tests) +class Test_BigInt_F : public ::testing::Test +{ +protected: + bigint A {std::string(kHugeA)}; + bigint B {std::string(kHugeB)}; + bigint C {std::string(kNines)}; +}; +class BigInt_AddParamTest : public ::testing::TestWithParam{}; +class BigInt_SubParamTest : public ::testing::TestWithParam{}; +class BigInt_MulParamTest : public ::testing::TestWithParam{}; +class BigInt_DivParamTest : public ::testing::TestWithParam{}; +class BigInt_ModParamTest : public ::testing::TestWithParam{}; + +TEST(Test_BigInt, Invalid_Tests) +{ + EXPECT_THROW(bigint('a'),std::runtime_error); + EXPECT_THROW(bigint(static_cast(0)),std::runtime_error); + EXPECT_THROW(bigint(static_cast(-1)),std::runtime_error); + EXPECT_THROW(bigint("a"),std::runtime_error); + EXPECT_THROW(bigint("?"),std::runtime_error); + EXPECT_THROW(bigint(""),std::runtime_error); + EXPECT_THROW(bigint("123456a7"),std::runtime_error); + EXPECT_THROW(bigint("-123456a7"),std::runtime_error); + EXPECT_THROW(bigint("01234567"),std::runtime_error); + EXPECT_THROW(bigint("007"),std::runtime_error); + ASSERT_THROW(bigint("1234567.9"),std::runtime_error); + ASSERT_THROW(bigint("+1234567"),std::runtime_error); + ASSERT_THROW(bigint(" 1234567"),std::runtime_error); + ASSERT_THROW(bigint("1234567 "),std::runtime_error); + EXPECT_THROW(bigint("-0"),std::runtime_error); + EXPECT_THROW(bigint("-"),std::runtime_error); + EXPECT_THROW(bigint("-00"),std::runtime_error); + EXPECT_THROW(bigint("00"),std::runtime_error); + EXPECT_THROW(bigint("0.0"),std::runtime_error); +} + +TEST(Test_BigInt, Creation_Tests) { int my_int = 100; long my_long = 100; long long my_longlong = 100; + long long my_longlong2 = LLONG_MAX; + long long my_longlong3 = LLONG_MIN; + unsigned long long my_ulonglong = 1000; double my_double = 100.0; char my_char = '7'; std::string my_string = "100"; + std::string my_string3 = "-9223372036854775808"; + std::string my_string4 = "-9223372036854775809"; + + EXPECT_EQ(bigint(my_int), 100); + EXPECT_EQ(bigint(-my_int), -100); + + EXPECT_EQ(bigint(my_long), 100); + EXPECT_EQ(bigint(-my_long), -100); + + EXPECT_EQ(bigint(my_longlong), 100); + EXPECT_EQ(bigint(-my_longlong), -100); + EXPECT_EQ(bigint(my_longlong2), LLONG_MAX); + EXPECT_EQ(bigint(my_longlong3), LLONG_MIN); + + EXPECT_EQ(bigint(my_ulonglong), 1000); + + EXPECT_EQ(bigint(my_double), 100); + EXPECT_EQ(bigint(-my_double), -100); + + EXPECT_EQ(bigint(my_char), 7); - ASSERT_EQ(bigint(my_int), 100); - ASSERT_EQ(bigint(my_long), 100); - ASSERT_EQ(bigint(my_longlong), 100); - ASSERT_EQ(bigint(my_double), 100); - ASSERT_EQ(bigint(my_char), 7); - ASSERT_EQ(bigint(my_string), "100"); + EXPECT_EQ(bigint(my_string), "100"); + EXPECT_EQ(bigint(my_string3), "-9223372036854775808"); + EXPECT_EQ(bigint(my_string4), "-9223372036854775809"); } -TEST(BigInt_test, Addition_Tests) +TEST(Test_BigInt, Stream_Tests) { - bigint small_number = 9955; - bigint huge_number_1 = "123456789"; - bigint huge_number_2 = "9999999999999999999"; - bigint huge_number_3 = "37744193401458640707539380267899264828998907634573602318662036836618621958669475225851195876029606479769348216875016014259295382637670116067802415326896673540817149305648020275612344553440582481192266196913778504499507839960073829863258118424953008896871971652295554512459916848335206655076766027606195514199793888542571641680917367253163346581387963223123048507895574406540841752099433832902520291592993232666589290350588973179516741959648948892906581313716663682087787058913539002195482835009516853"; - bigint huge_number_4 = "9843174278608822755442867695930794030260633344831664962118526669621620352474231493840674921513269404"; - bigint negative_number = "-9876543210123456789314159"; - bigint max_ll = LLONG_MAX; - ASSERT_EQ(bigint(10) + bigint(20), "30"); - ASSERT_EQ(small_number + 5, 9960); - ASSERT_EQ(small_number + small_number, 19910); - ASSERT_EQ(huge_number_1 + 1, "123456790"); - ASSERT_EQ(huge_number_2 + 1, "10000000000000000000"); - ASSERT_EQ(huge_number_3 + huge_number_4, "37744193401458640707539380267899264828998907634573602318662036836618621958669475225851195876029606479769348216875016014259295382637670116067802415326896673540817149305648020275612344553440582481192266196913778504499507839960073829863258118424953008896871971652295554512459916848335206655076766027606195514199793888542571641680917367253163346581387963223123048507895574406540841752099433832902520291602836406945198113106031840875447535989909582237738246275835190351709407411387770496036157756522786257"); - - ASSERT_EQ( max_ll + 1, "9223372036854775808"); - ASSERT_EQ(huge_number_2 + 1, "10000000000000000000"); - - ASSERT_EQ(negative_number + small_number, "-9876543210123456789304204"); - ASSERT_EQ(small_number + negative_number, "-9876543210123456789304204"); - ASSERT_EQ(negative_number + negative_number, "-19753086420246913578628318"); + std::stringstream my_stream; + my_stream << bigint(55); + EXPECT_EQ(my_stream.str(), "55"); + + my_stream = std::stringstream(); + my_stream.clear(); + + my_stream << bigint(-55); + EXPECT_EQ(my_stream.str(), "-55"); + my_stream = std::stringstream(); + my_stream << bigint(std::string{kHugeA}); + EXPECT_EQ(my_stream.str(), std::string{kHugeA}); } -TEST(BigInt_test, Subtraction_Tests) +TEST(Test_BigInt, Bool_Tests) { - bigint small_number = 9955; - bigint huge_number_1 = "123456789"; - bigint huge_number_2 = "10000000000000000000"; - bigint huge_number_3 = "37744193401458640707539380267899264828998907634573602318662036836618621958669475225851195876029606479769348216875016014259295382637670116067802415326896673540817149305648020275612344553440582481192266196913778504499507839960073829863258118424953008896871971652295554512459916848335206655076766027606195514199793888542571641680917367253163346581387963223123048507895574406540841752099433832902520291592993232666589290350588973179516741959648948892906581313716663682087787058913539002195482835009516853"; - bigint huge_number_4 = "9843174278608822755442867695930794030260633344831664962118526669621620352474231493840674921513269404"; - bigint negative_number = "-9876543210123456789314159"; - bigint min_ll = LLONG_MIN; - ASSERT_EQ(bigint(30) - bigint(20), "10"); - ASSERT_EQ(small_number - 5, 9950); - ASSERT_EQ(small_number - small_number, 0); - ASSERT_EQ(min_ll - 1, bigint("-9223372036854775809")); - ASSERT_EQ(huge_number_1 - 1, "123456788"); - ASSERT_EQ(huge_number_2 - 1, "9999999999999999999"); - ASSERT_EQ(huge_number_3 - huge_number_4, "37744193401458640707539380267899264828998907634573602318662036836618621958669475225851195876029606479769348216875016014259295382637670116067802415326896673540817149305648020275612344553440582481192266196913778504499507839960073829863258118424953008896871971652295554512459916848335206655076766027606195514199793888542571641680917367253163346581387963223123048507895574406540841752099433832902520291583150058387980467595146105483585947929388315548074916351598137012466166706439307508354807913496247449"); - - ASSERT_EQ(negative_number - small_number, "-9876543210123456789324114"); - ASSERT_EQ(small_number - negative_number, "9876543210123456789324114"); - ASSERT_EQ(negative_number - negative_number, "0"); + EXPECT_EQ(static_cast(bigint(1)), true); + EXPECT_EQ(static_cast(bigint(0)), false); + EXPECT_EQ(static_cast(bigint()), false); + EXPECT_EQ(static_cast(bigint(-1)), true); + EXPECT_EQ(static_cast(bigint(2)), true); + EXPECT_EQ(static_cast(bigint(std::string{kHugeA})), true); } -TEST(BigInt_test, Multiplication_Tests) +TEST_P(BigInt_AddParamTest, Addition_Tests) { - bigint small_number = 9955; - bigint huge_number_1 = "123456789"; - bigint huge_number_2 = "9999999999999999999"; - bigint huge_number_3 = "37744193401458640707539380267899264828998907634573602318662036836618621958669475225851195876029606479769348216875016014259295382637670116067802415326896673540817149305648020275612344553440582481192266196913778504499507839960073829863258118424953008896871971652295554512459916848335206655076766027606195514199793888542571641680917367253163346581387963223123048507895574406540841752099433832902520291592993232666589290350588973179516741959648948892906581313716663682087787058913539002195482835009516853"; - bigint huge_number_4 = "9843174278608822755442867695930794030260633344831664962118526669621620352474231493840674921513269404"; - bigint huge_number_5 = "600000000000000000000000000500000000000000000004"; - bigint negative_number = "-678345123987345645559001"; - bigint max_ll = LLONG_MAX; - ASSERT_EQ(bigint(30) * bigint(20), "600"); - ASSERT_EQ(small_number * 0, 0); - ASSERT_EQ(small_number * 1, 9955); - ASSERT_EQ(small_number * 5, 49775); - ASSERT_EQ(small_number * small_number, 99102025); - ASSERT_EQ(small_number * max_ll, "91818668626889293158685"); - ASSERT_EQ(huge_number_1 * 2, "246913578"); - ASSERT_EQ(huge_number_2 * huge_number_2, "99999999999999999980000000000000000001"); - ASSERT_EQ(huge_number_3 * huge_number_4, "371522673656074543721939903145636335168668810492059508909919665306024609291590328396789563611909881956692074164896572222014925589282684907671824165394055196075714622557089856059685618270608317568409327841248791007327091177305027733796385625358604009972642833675600135432852203678357550499911005775538860759946390549681453248681292307393576029610404491475299486834429621773400380504580240368032838923535804505697336440062521338464979118468824407819968770248251010402682462172033117899977017721981977515651526349027968047250058213343537761879103544485699067309526235122799512472963309861508662467265612"); - ASSERT_EQ(huge_number_5 * 1000, "600000000000000000000000000500000000000000000004000"); - - ASSERT_EQ(negative_number * small_number, "-6752925709294025901539854955"); - ASSERT_EQ(small_number * negative_number, "-6752925709294025901539854955"); - ASSERT_EQ(negative_number * negative_number, "460152107237407336735321298008566431113772118001"); + const auto&[lhs, rhs, expected] = GetParam(); + const bigint a{lhs}; + const bigint b{rhs}; + const bigint sum = a + b; + // EXPECT_EQ(sum, bigint{expected}) << lhs << " + " << rhs; + EXPECT_EQ(sum, bigint{expected}); } +TEST_P(BigInt_SubParamTest, Subtraction_Tests) +{ + const auto&[lhs, rhs, expected] = GetParam(); + const bigint a{lhs}; + const bigint b{rhs}; + const bigint sub = a - b; + // EXPECT_EQ(sum, bigint{expected}) << lhs << " - " << rhs; + EXPECT_EQ(sub, bigint{expected}); +} -TEST(BigInt_test, Division_Tests) +TEST_P(BigInt_MulParamTest, Multiplication_Tests) { - bigint small_number = 9955; - bigint huge_number_1 = "123456789"; - bigint huge_number_2 = "9999999999999999999"; - bigint huge_number_3 = "37744193401458640707539380267899264828998907634573602318662036836618621958669475225851195876029606479769348216875016014259295382637670116067802415326896673540817149305648020275612344553440582481192266196913778504499507839960073829863258118424953008896871971652295554512459916848335206655076766027606195514199793888542571641680917367253163346581387963223123048507895574406540841752099433832902520291592993232666589290350588973179516741959648948892906581313716663682087787058913539002195482835009516853"; - bigint huge_number_4 = "9843174278608822755442867695930794030260633344831664962118526669621620352474231493840674921513269404"; - ASSERT_EQ(bigint(30) / bigint(20), 1); - ASSERT_EQ(small_number / 5, 1991); - ASSERT_EQ(small_number / 181, 55); - ASSERT_EQ(huge_number_1 / 2, 61728394); - ASSERT_EQ(huge_number_1 / 3, 41152263); - ASSERT_EQ(huge_number_2 / huge_number_1, 81000000737); - ASSERT_EQ(huge_number_3 / huge_number_4, bigint("3834555025961928452480859540164628181402139687101087313422864247768110206281588207268817088767383084425857331782324292423438870254834351784862584845557954696973368973237622555423975476296891710599837880699585094449938105117601882807098249270619115466337592065938882369152755194714547350194277427676672295574028506048275631567039158386540348169130522428891371330855740227809243175650789881054423775986")); + const auto&[lhs, rhs, expected] = GetParam(); + const bigint a{lhs}; + const bigint b{rhs}; + const bigint mul = a * b; + // EXPECT_EQ(sum, bigint{expected}) << lhs << " * " << rhs; + EXPECT_EQ(mul, bigint{expected}); } -TEST(BigInt_test, Comparison_Tests) +TEST_P(BigInt_DivParamTest, Division_Tests) { - bigint small_number = 9955; - bigint huge_number_1 = "123456789"; - bigint huge_number_2 = "9999999999999999999"; - ASSERT_TRUE(small_number > 5); - ASSERT_TRUE(small_number > 0); - ASSERT_TRUE(small_number > -10); - ASSERT_TRUE(small_number > "-123456789"); - ASSERT_FALSE(small_number > huge_number_1); - - ASSERT_TRUE(small_number < huge_number_1); - ASSERT_TRUE(small_number < huge_number_2); - ASSERT_TRUE(huge_number_1 < huge_number_2); - - ASSERT_TRUE(small_number == small_number); - ASSERT_TRUE(small_number >= small_number); - ASSERT_TRUE(small_number <= small_number); - ASSERT_TRUE(huge_number_2 == huge_number_2); - ASSERT_TRUE(huge_number_2 >= huge_number_2); - ASSERT_TRUE(huge_number_2 <= huge_number_2); - - ASSERT_TRUE(huge_number_2 > 0); - ASSERT_TRUE(huge_number_2 > "0"); - ASSERT_TRUE(huge_number_2 > "-1"); - - ASSERT_TRUE(bigint(0) == 0); - ASSERT_TRUE(bigint(0) >= 0); - ASSERT_TRUE(bigint(0) <= 0); - ASSERT_TRUE(bigint(0) == "0"); - ASSERT_TRUE(bigint(0) >= "0"); - ASSERT_TRUE(bigint(0) <= "0"); - - ASSERT_TRUE(bigint(1) == 1); - ASSERT_TRUE(bigint(1) >= 1); - ASSERT_TRUE(bigint(1) <= 1); - ASSERT_TRUE(bigint(1) == "1"); - ASSERT_TRUE(bigint(1) >= "1"); - ASSERT_TRUE(bigint(1) <= "1"); - - ASSERT_TRUE(1 < huge_number_1); - ASSERT_FALSE(1 > huge_number_1); - ASSERT_TRUE(10 < huge_number_1); - ASSERT_TRUE("99" < huge_number_1); - ASSERT_TRUE(123456789 <= huge_number_1); - ASSERT_TRUE(123456789 == huge_number_1); - ASSERT_TRUE(123456789 >= huge_number_1); + const auto&[lhs, rhs, expected] = GetParam(); + const bigint a{lhs}; + const bigint b{rhs}; + const bigint div = a / b; + // EXPECT_EQ(sum, bigint{expected}) << lhs << " / " << rhs; + EXPECT_EQ(div, bigint{expected}); } -TEST(BigInt_test, Modulus_Tests) +TEST_P(BigInt_ModParamTest, Modulus_Tests) +{ + const auto&[lhs, rhs, expected] = GetParam(); + const bigint a{lhs}; + const bigint b{rhs}; + const bigint mod = a % b; + // EXPECT_EQ(sum, bigint{expected}) << lhs << " % " << rhs; + EXPECT_EQ(mod, bigint{expected}); +} + +INSTANTIATE_TEST_SUITE_P(SmallValueAdd, BigInt_AddParamTest, ::testing::Values( +TestCase{"0", "0", "0"}, +TestCase{"0", "5", "5"}, +TestCase{"5", "0", "5"}, +TestCase{"123", "456", "579"}, +TestCase{"999", "1", "1000"}, + +TestCase{"7", "-3", "4"}, +TestCase{"-7", "3", "-4"}, +TestCase{"-7", "-3", "-10"}, +TestCase{"-10", "10", "0"}, +TestCase{"10", "-10", "0"} +)); + +INSTANTIATE_TEST_SUITE_P(SmallValueSub, BigInt_SubParamTest, ::testing::Values( +TestCase{"0", "0", "0"}, +TestCase{"0", "5", "-5"}, +TestCase{"5", "0", "5"}, +TestCase{"999", "1", "998"}, +TestCase{"1000", "1", "999"}, + +TestCase{"7", "-3", "10"}, +TestCase{"-7", "3", "-10"}, +TestCase{"-7", "-3", "-4"}, +TestCase{"-10", "10", "-20"}, +TestCase{"10", "-10", "20"}, +TestCase{"-10", "-10", "0"} +)); + +INSTANTIATE_TEST_SUITE_P(SmallValueMul, BigInt_MulParamTest, ::testing::Values( +TestCase{"0", "0", "0"}, +TestCase{"0", "5", "0"}, +TestCase{"5", "0", "0"}, +TestCase{"999", "1", "999"}, + +TestCase{"7", "-3", "-21"}, +TestCase{"-7", "3", "-21"}, +TestCase{"-7", "-3", "21"} +)); + +INSTANTIATE_TEST_SUITE_P(SmallValueDiv, BigInt_DivParamTest, ::testing::Values( +TestCase{"0", "5", "0"}, +TestCase{"999", "1", "999"}, + +TestCase{"7", "-3", "-2"}, +TestCase{"-7", "3", "-2"}, +TestCase{"-7", "-3", "2"} +)); + +INSTANTIATE_TEST_SUITE_P(LargeValuesAdd, BigInt_AddParamTest, ::testing::Values( +TestCase{kHugeA, kHugeB, AplusB}, +TestCase{kHugeB, kHugeA, AplusB}, +TestCase{kHugeA, "0", kHugeA} +)); + +INSTANTIATE_TEST_SUITE_P(LargeValueSub, BigInt_SubParamTest, ::testing::Values( +TestCase{kHugeA, kHugeB, AminusB}, +TestCase{kHugeB, kHugeA, BminusA}, +TestCase{kHugeA, kHugeA, "0"}, +TestCase{kHugeA, "0", kHugeA}, +TestCase{"0", kHugeA, "-" + std::string{kHugeA}}, +TestCase{kNines, "9900000000000000000", "99999999999999999"} +)); + +INSTANTIATE_TEST_SUITE_P(LargeValueMul, BigInt_MulParamTest, ::testing::Values( +TestCase{kHugeA, kHugeB, AmulB}, +TestCase{kHugeB, kHugeA, AmulB} +)); + +INSTANTIATE_TEST_SUITE_P(LargeValueDiv, BigInt_DivParamTest, ::testing::Values( +TestCase{kHugeA, kHugeB, AdivB}, +TestCase{kHugeA, kHugeA, "1"}, +TestCase{"0", kHugeA, "0"}, +TestCase{kHugeA, "-" + std::string{kHugeB}, "-" + std::string{AdivB}}, +TestCase{kHugeB, kHugeA, "0"} +)); + +INSTANTIATE_TEST_SUITE_P(LargeValueMod, BigInt_ModParamTest, ::testing::Values( +TestCase{kHugeA, kHugeB, AmodB}, +TestCase{kHugeB, kHugeA, kHugeB}, +TestCase{kHugeA, kHugeA, "0"}, +TestCase{kHugeA, "1", "0"} +)); + +TEST_F(Test_BigInt_F, Commutivity_Tests) +{ + EXPECT_EQ(A + B, B + A); + EXPECT_EQ((A + B) + C, A + (B + C)); + EXPECT_EQ(A * B, B * A); + EXPECT_EQ((A * B) * C, A * (B * C)); +} + +TEST_F(Test_BigInt_F, Distributivity_Tests) +{ + EXPECT_EQ(A * (B + C), (A * B) + (A * C)); +} + +TEST(Test_BigInt, Domain_Tests) +{ + EXPECT_THROW(bigint{0} / 0, std::domain_error); + EXPECT_THROW(bigint{77} / 0, std::domain_error); + EXPECT_THROW(bigint{std::string{kHugeA}} / 0, std::domain_error); + EXPECT_THROW(bigint{77} % 0, std::domain_error); +} + +TEST(Test_BigInt, Comparison_Tests) { bigint small_number = 9955; - bigint huge_number_1 = "123456789"; - bigint huge_number_2 = "9999999999999999999"; - ASSERT_EQ(small_number % 2, 1); - ASSERT_EQ(small_number % 3, 1); - ASSERT_EQ(small_number % 4, 3); - ASSERT_EQ(small_number % 5, 0); - - ASSERT_EQ(huge_number_1 % 2, 1); - ASSERT_EQ(huge_number_1 % 3, 0); - ASSERT_EQ(huge_number_1 % 4, 1); - ASSERT_EQ(huge_number_1 % 5, 4); - - ASSERT_EQ(huge_number_1 % small_number, 4834); + bigint huge_number1{std::string{kHugeA}}; + bigint huge_number2{std::string{kHugeB}}; + EXPECT_TRUE(small_number > 5); + EXPECT_TRUE(small_number > 0); + EXPECT_TRUE(small_number > -10); + EXPECT_TRUE(huge_number1 > 0); + EXPECT_TRUE(huge_number1 > "0"); + EXPECT_TRUE(huge_number1 > "-1"); + + EXPECT_TRUE(small_number > huge_number1 * -1); + EXPECT_FALSE(small_number > huge_number1); + EXPECT_FALSE(small_number > small_number); + EXPECT_FALSE(huge_number1 > huge_number1); + + EXPECT_TRUE(small_number < huge_number1); + EXPECT_TRUE(huge_number2 < huge_number1); + + EXPECT_TRUE(small_number == small_number); + EXPECT_TRUE(huge_number1 == huge_number1); + EXPECT_TRUE(small_number >= small_number); + EXPECT_TRUE(small_number <= small_number); + EXPECT_TRUE(huge_number1 >= huge_number1); + EXPECT_TRUE(huge_number1 <= huge_number1); + + EXPECT_TRUE(bigint(0) == 0); + EXPECT_TRUE(bigint(0) >= 0); + EXPECT_TRUE(bigint(0) <= 0); + + EXPECT_TRUE(bigint(1) == 1); + EXPECT_TRUE(bigint(1) >= 1); + EXPECT_TRUE(bigint(1) <= 1); + + EXPECT_TRUE(1 < huge_number1); + EXPECT_FALSE(1 > huge_number1); + EXPECT_TRUE(10 < huge_number1); + EXPECT_TRUE("99" < huge_number1); + EXPECT_TRUE(123456789 <= huge_number1); + EXPECT_FALSE(123456789 == huge_number1); + EXPECT_FALSE(123456789 >= huge_number1); } -TEST(BigInt_test, Speed_Tests) +TEST_F(Test_BigInt_Performance, Speed_Tests) { - using std::chrono::high_resolution_clock; + using std::chrono::steady_clock; using std::chrono::microseconds; using std::chrono::duration_cast; - for (size_t number_size : {5, 10 ,20, 30, 40, 50 ,60 ,70, 80, 90 ,100, 110, 120, 160, 190, 200, 500, 1000}) { - size_t number_count = 10; + constexpr size_t number_count = 500; + const std::vector sizes = {5, 20, 50, 100, 1000}; + + auto formatTime = [](const long long micros) -> std::string { + if (micros >= 1000000) return std::to_string(micros / 1000000.0) + " s"; + if (micros >= 1000) return std::to_string(micros / 1000.0) + " ms"; + return std::to_string(micros) + " us"; + }; + + auto measure_execution = [&](const char* label, const size_t size, auto func) { + auto t1 = steady_clock::now(); + func(); + auto t2 = steady_clock::now(); + + const long long duration = duration_cast(t2 - t1).count(); + const double avg_per_op = static_cast(duration) / number_count; + + std::cout << std::setw(14) << std::left << label << " [" << size << " digits]: " + << formatTime(duration) + << " (Avg: " << avg_per_op << " us/op)" << std::endl; + }; + + volatile int dce_sink = 0; + + std::cout << "--- Starting Performance Tests (Sample size: " << number_count << ") ---" << std::endl; + for (const size_t number_size : sizes) { std::vector huge_numbers; huge_numbers.reserve(number_count); for (int i = 0; i < number_count; ++i) { huge_numbers.emplace_back(bigint::random(number_size)); } + // Ensure values stay defined std::sort(huge_numbers.begin(), huge_numbers.end(), std::greater<>()); bigint answer = 0; - auto formatTime = [](int64_t microseconds) -> std::string { - std::ostringstream result; - - if (microseconds >= 1000000) { - result << (microseconds / 1000000.0) << " seconds"; - } else if (microseconds >= 1000) { - result << (microseconds / 1000.0) << " milliseconds"; - } else { - result << microseconds << " microseconds"; + // Addition + measure_execution("Addition", number_size, [&]() { + for (size_t i = 0; i < huge_numbers.size() - 1; ++i) { + answer = huge_numbers[i] + huge_numbers[i + 1]; + // Tiny check to force evaluation + if (answer == 0) dce_sink++; } + }); + + // Subtraction + measure_execution("Subtraction", number_size, [&]() { + for (size_t i = 0; i < huge_numbers.size() - 1; ++i) { + answer = huge_numbers[i] - huge_numbers[i + 1]; + // Tiny check to force evaluation + if (answer == 0) dce_sink++; + } + }); + + // Multiplication + measure_execution("Multiplication", number_size, [&]() { + for (size_t i = 0; i < huge_numbers.size() - 1; ++i) { + answer = huge_numbers[i] * huge_numbers[i + 1]; + // Tiny check to force evaluation + if (answer == 0) dce_sink++; + } + }); - return result.str(); - }; - - // Addition timing - auto t1 = high_resolution_clock::now(); - for (int i = 0; i < huge_numbers.size() - 1; ++i) { - answer = huge_numbers[i] + huge_numbers[i + 1]; -// std::cout << huge_numbers[i] << " + " << huge_numbers[i + 1] << " = " << answer << std::endl; - } - auto t2 = high_resolution_clock::now(); - - std::cout << number_count - 1 << " Additions: " << "[" << number_size << "] " - << formatTime(duration_cast(t2 - t1).count()) ; - // Subtraction timing - t1 = high_resolution_clock::now(); - for (int i = 0; i < huge_numbers.size() - 1; ++i) { - answer = huge_numbers[i] - huge_numbers[i + 1]; - } - t2 = high_resolution_clock::now(); - std::cout << " Subtractions: " << "[" << number_size << "] " - << formatTime(duration_cast(t2 - t1).count()) ; - - // Multiplication timing - t1 = high_resolution_clock::now(); - for (int i = 0; i < huge_numbers.size() - 1; ++i) { - answer = huge_numbers[i] * huge_numbers[i + 1]; - } - t2 = high_resolution_clock::now(); - std::cout << " Multiplications: " << "[" << number_size << "] " - << formatTime(duration_cast(t2 - t1).count()) ; - - // Division timing - t1 = high_resolution_clock::now(); - for (const auto &huge_number: huge_numbers) { - answer = huge_number / 2; - } - t2 = high_resolution_clock::now(); - std::cout << " Division: " << "[" << number_size << "] " - << formatTime(duration_cast(t2 - t1).count()) << std::endl; + measure_execution("Division", number_size, [&]() { + for (size_t i = 0; i < huge_numbers.size() - 1; ++i) { + answer = huge_numbers[i] / 55; + // Tiny check to force evaluation + if (answer == 0) dce_sink++; + } + }); + std::cout << std::endl; } }