diff --git a/bigint.h b/bigint.h index 88db96d..86d1b88 100644 --- a/bigint.h +++ b/bigint.h @@ -157,7 +157,11 @@ namespace BigInt { bigint operator+=(const bigint &rhs) { - *this = add(*this, rhs); + if (*this == 0 && rhs == 0) return *this; + if (*this == 0) {*this = rhs; return *this;} + if (rhs != 0) { + *this = add(*this, rhs); + } return *this; } @@ -246,6 +250,21 @@ namespace BigInt { return tmp; } + bigint operator-() const & + { + bigint temp = *this; + if (temp == 0) return temp; + temp.is_neg = !this->is_neg; + return temp; + } + + bigint operator-() && + { + if (*this == 0) return *this; + this->is_neg = !this->is_neg; + return *this; + } + friend bool operator==(const bigint &l, const bigint &r) { if (l.is_neg != r.is_neg) @@ -300,14 +319,17 @@ namespace BigInt { static bigint abs(const bigint &s) { - if (is_negative(s)) - { - bigint temp = s; - temp.is_neg = false; + if (!is_negative(s)) return s; - return temp; - } + bigint temp = s; + temp.is_neg = false; + + return temp; + } + static bigint abs(bigint&& s) + { + s.is_neg = false; return s; } @@ -355,20 +377,32 @@ namespace BigInt { return sum; } + /** + * @brief Generates a random positive bigint of a specified length. + * + * This method ensures the resulting bigint is valid by using a random device + * and engine for non-deterministic seeding. + * + * @param length The number of digits the generated bigint should have. + * @return A bigint object representing the randomly generated number. + */ static bigint random(size_t length); private: std::vector vec{}; - bool is_neg{}; + bool is_neg{false}; // Function Definitions for Internal Uses - static bigint trim(const bigint& input) { - auto temp = input; - while (temp.vec.front() == 0){ - temp.vec.erase(temp.vec.begin()); + static bigint trim(bigint input) { + while (input.vec.size() > 1 && input.vec.front() == 0) { + input.vec.erase(input.vec.begin()); } - return temp; + if (input.vec.empty()) { + input.vec.push_back(0); + input.is_neg = false; + } + return input; } static std::vector string_to_vector(std::string input); @@ -418,10 +452,16 @@ namespace BigInt { static bigint negate(const bigint& input) { bigint temp = input; - temp.is_neg = true; + temp.is_neg = !temp.is_neg; return temp; } + static bigint negate(bigint&& input) + { + input.is_neg = !input.is_neg; + return input; + } + static bool less_than(const bigint& lhs, const bigint& rhs) { if (is_negative(lhs) && is_negative(rhs)) @@ -455,61 +495,50 @@ namespace BigInt { return s.find_first_not_of("0123456789", 0) == std::string::npos; } - inline std::pair add_with_carry(const long long lhs, const long long rhs) + inline bigint bigint::add(const bigint &lhs, const bigint &rhs) { - auto sum = lhs + rhs; + bool negate_answer = false; + // Ensure both are positive + if (is_negative(lhs) && is_negative(rhs)) negate_answer = true; + else if (is_negative(lhs)) return subtract(rhs, abs(lhs)); + else if (is_negative(rhs)) return subtract(lhs, abs(rhs)); - if (sum >= bigint::MAX_SIZE) - { - // Carry needs to happen - auto carry = sum / bigint::MAX_SIZE; - auto result = sum % bigint::MAX_SIZE; - return {carry, result}; - } + // Ensure LHS is larger than RHS + if (lhs.vec.size() < rhs.vec.size()) return add(rhs, lhs); - return {0, sum}; - } + // Prepare result vector with enough space (max size + 1 for potential carry) + std::vector result; + result.reserve(lhs.vec.size() + 1); - inline bigint bigint::add(const bigint &lhs, const bigint &rhs) - { - // Ensure LHS is larger than RHS, and both are positive - if (lhs == 0) return rhs; - if (rhs == 0) return lhs; - if (is_negative(lhs) && is_negative(rhs)) - { - return negate(add(abs(lhs) ,abs(rhs))); - } - if (is_negative(lhs)) - { - return rhs - abs(lhs); - } - if (is_negative(rhs)) - { - return lhs - abs(rhs); - } - if (lhs < rhs) - { - return add(rhs, lhs); - } + long long carry = 0; + auto it_l = lhs.vec.rbegin(); + auto it_r = rhs.vec.rbegin(); - bigint full_rhs = rhs; - std::vector> carry_result(lhs.vec.size() + 1); + while (it_l != lhs.vec.rend()) { + long long sum = *it_l + carry; + if (it_r != rhs.vec.rend()) { + sum += *it_r; + ++it_r; + } - // Fill the smaller to match the larger size - while (lhs.vec.size() > full_rhs.vec.size()) - { - full_rhs.vec.insert(full_rhs.vec.begin(), 0); - } + if (sum >= MAX_SIZE) { + sum -= MAX_SIZE; + carry = 1; + } else { + carry = 0; + } - std::transform(lhs.vec.rbegin(), lhs.vec.rend(), full_rhs.vec.rbegin(), carry_result.rbegin(), add_with_carry); + result.push_back(sum); + ++it_l; + } - std::vector final(lhs.vec.size() + 1); - for (int i = carry_result.size() - 1; i > 0; --i) { - final[i] += carry_result[i].second; - final[i - 1] += carry_result[i].first; + if (carry > 0) { + result.push_back(carry); } - return trim(bigint(final)); + std::reverse(result.begin(), result.end()); + bigint result_bigint {std::move(result)}; + return negate_answer ? negate(result_bigint) : result_bigint; } inline std::pair subtract_with_borrow(const long long lhs, const long long rhs) @@ -843,8 +872,8 @@ namespace BigInt { return true; } - inline bigint bigint::random(size_t length) { - const char charset[] = "0123456789"; + inline bigint bigint::random(const size_t length) { + constexpr char charset[] = "0123456789"; std::default_random_engine rng(std::random_device{}()); // Distribution for the first digit (1-9) diff --git a/tests/test.cpp b/tests/test.cpp index 4c52b3c..034b62b 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -12,8 +12,12 @@ #include "../bigint.h" using namespace BigInt; +using std::chrono::steady_clock; +using std::chrono::microseconds; +using std::chrono::duration_cast; -bool SKIP_PERFORMANCE_TESTS = true; +// bool SKIP_PERFORMANCE_TESTS = true; +bool SKIP_PERFORMANCE_TESTS = false; constexpr std::string_view kNines = "9999999999999999999"; constexpr std::string_view kHugeA = "37744193401458640707539380267899264828998907634573602318662036836618621958669475225851195876029606479769348216875016014259295382637670116067802415326896673540817149305648020275612344553440582481192266196913778504499507839960073829863258118424953008896871971652295554512459916848335206655076766027606195514199793888542571641680917367253163346581387963223123048507895574406540841752099433832902520291592993232666589290350588973179516741959648948892906581313716663682087787058913539002195482835009516853"; @@ -37,9 +41,32 @@ struct TestCase }; class Test_BigInt : public ::testing::Test{}; + +static 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"; +}; + +static auto measure_execution = [](const int count, const char* label, const size_t size, auto func) { + const auto t1 = steady_clock::now(); + func(); + const auto t2 = steady_clock::now(); + + const long long duration = duration_cast(t2 - t1).count(); + const double avg_per_op = static_cast(duration) / count; + + std::cout << std::setw(14) << std::left << label << " [" << size << " digits]: " + << formatTime(duration) + << " (Avg: " << avg_per_op << " us/op)" << std::endl; +}; + class Test_BigInt_Performance : public ::testing::Test { protected: + static constexpr size_t number_count = 500; + const std::vector sizes = {5, 20, 50, 100, 1000, 10'000, 100'000}; + volatile int dce_sink = 0; void SetUp() override { if (SKIP_PERFORMANCE_TESTS) GTEST_SKIP() << "Skipping performance tests for this fixture"; @@ -145,6 +172,14 @@ TEST(Test_BigInt, Bool_Tests) EXPECT_EQ(static_cast(bigint(std::string{kHugeA})), true); } +TEST(Test_BigInt, Negate_Tests) +{ + EXPECT_EQ(-bigint(0), 0); + EXPECT_EQ(-bigint(1), -1); + EXPECT_EQ(-bigint(-1), 1); + EXPECT_EQ(-bigint(std::string{kHugeA}), bigint("-" + std::string{kHugeA})); +} + TEST_P(BigInt_AddParamTest, Addition_Tests) { const auto&[lhs, rhs, expected] = GetParam(); @@ -344,36 +379,8 @@ TEST(Test_BigInt, Comparison_Tests) EXPECT_FALSE(123456789 >= huge_number1); } -TEST_F(Test_BigInt_Performance, Speed_Tests) +TEST_F(Test_BigInt_Performance, Addition_Speed_Tests) { - using std::chrono::steady_clock; - using std::chrono::microseconds; - using std::chrono::duration_cast; - - 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) { @@ -387,8 +394,7 @@ TEST_F(Test_BigInt_Performance, Speed_Tests) bigint answer = 0; - // Addition - measure_execution("Addition", number_size, [&]() { + measure_execution(number_count, "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 @@ -396,8 +402,25 @@ TEST_F(Test_BigInt_Performance, Speed_Tests) } }); - // Subtraction - measure_execution("Subtraction", number_size, [&]() { + std::cout << std::endl; + } +} +TEST_F(Test_BigInt_Performance, Subtraction_Speed_Tests) +{ + 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; + + measure_execution(number_count,"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 @@ -405,8 +428,26 @@ TEST_F(Test_BigInt_Performance, Speed_Tests) } }); - // Multiplication - measure_execution("Multiplication", number_size, [&]() { + std::cout << std::endl; + } +} + +TEST_F(Test_BigInt_Performance, Multiplication_Speed_Tests) +{ + 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; + + measure_execution(number_count,"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 @@ -414,15 +455,33 @@ TEST_F(Test_BigInt_Performance, Speed_Tests) } }); + std::cout << std::endl; + } +} + +TEST_F(Test_BigInt_Performance, Division_Speed_Tests) +{ + 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; - measure_execution("Division", number_size, [&]() { + measure_execution(number_count, "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; } } -