From 4b31f30e40b1ee668c5880f73ad39d57fa53f9e1 Mon Sep 17 00:00:00 2001 From: Samuel Herts Date: Tue, 21 May 2024 11:55:54 -0400 Subject: [PATCH] Properly implement division and add tests --- bigint.h | 217 ++++++++++++++++++------------------------------- tests/test.cpp | 5 +- 2 files changed, 81 insertions(+), 141 deletions(-) diff --git a/bigint.h b/bigint.h index 45b6b81..e40878a 100644 --- a/bigint.h +++ b/bigint.h @@ -72,6 +72,12 @@ namespace bigint_ns { // return sum % 3; // } +// auto div = lhs / rhs; +// auto mult = div * rhs; +// auto sub = lhs - mult; +// +// return sub; + return lhs - ((lhs / rhs) * rhs); } @@ -93,6 +99,25 @@ namespace bigint_ns { { return input.str.insert(0, "-"); } + inline static bool less_than(const bigint& lhs, const bigint& rhs) + { + if (is_negative(lhs) && is_negative(rhs)) + { + return less_than(abs(rhs), abs(lhs)); + } + + if (is_negative(lhs) || is_negative(rhs)) + { + return is_negative(lhs); + } + + if(lhs.str.length() == rhs.str.length()) + { + return lhs.str < rhs.str; + } + + return lhs.str.length() < rhs.str.length(); + } public: bool is_big = false; @@ -234,37 +259,22 @@ namespace bigint_ns { bigint operator*=(const bigint &rhs) { - if (!this->is_big) { - if (!rhs.is_big) { - if ((this->base_repr == -1 && rhs.base_repr == std::numeric_limits::min()) || - (rhs.base_repr == -1 && this->base_repr == std::numeric_limits::min()) || - (rhs.base_repr != 0 && - this->base_repr > std::numeric_limits::max() / rhs.base_repr) || - (rhs.base_repr != 0 && - this->base_repr < std::numeric_limits::min() / rhs.base_repr)) - { - *this = multiply(std::to_string(this->base_repr), std::to_string(rhs.base_repr)); - } - else - { - this->base_repr *= rhs.base_repr; - } - } - else - { - *this = multiply(std::to_string(this->base_repr), rhs.str); - } + if (this->is_big || rhs.is_big) + { + *this = multiply(this->is_big ? this->str : std::to_string(this->base_repr), rhs.is_big ? rhs.str : std::to_string(rhs.base_repr)); + } + else if ((this->base_repr == -1 && rhs.base_repr == std::numeric_limits::min()) || + (rhs.base_repr == -1 && this->base_repr == std::numeric_limits::min()) || + (rhs.base_repr != 0 && + this->base_repr > std::numeric_limits::max() / rhs.base_repr) || + (rhs.base_repr != 0 && + this->base_repr < std::numeric_limits::min() / rhs.base_repr)) + { + *this = multiply(std::to_string(this->base_repr), std::to_string(rhs.base_repr)); } else { - if (!rhs.is_big) - { - *this = multiply(this->str, std::to_string(rhs.base_repr)); - } - else - { - *this = multiply(this->str, rhs.str); - } + this->base_repr *= rhs.base_repr; } return *this; } @@ -278,21 +288,15 @@ namespace bigint_ns { bigint &operator/=(const bigint &rhs) { - if (!this->is_big) { - if (!rhs.is_big) { - this->base_repr /= rhs.base_repr; - } else { - *this = divide(std::to_string(this->base_repr), rhs.str); - } - } else { - if (!rhs.is_big) - { - *this = divide( this->str, std::to_string(rhs.base_repr)); - } - else{ - *this = divide(this->str, rhs.str); - } + if (this->is_big || rhs.is_big) + { + *this = divide(this->is_big ? this->str : std::to_string(this->base_repr), rhs.is_big ? rhs.str : std::to_string(rhs.base_repr)); } + else + { + this->base_repr /= rhs.base_repr; + } + return *this; } @@ -305,19 +309,13 @@ namespace bigint_ns { bigint operator%=(const bigint &rhs) { - if (!this->is_big) { - if (!rhs.is_big) { - this->base_repr %= rhs.base_repr; - } else { - *this = mod(std::to_string(this->base_repr), rhs.str); - } - } else { - if (!rhs.is_big) { - *this = mod(this->str, std::to_string(rhs.base_repr)); - } - else { - *this = mod(this->str, rhs.str); - } + if (this->is_big || rhs.is_big) + { + *this = mod(this->is_big ? this->str : std::to_string(this->base_repr), rhs.is_big ? rhs.str : std::to_string(rhs.base_repr)); + } + else + { + this->base_repr %= rhs.base_repr; } return *this; @@ -374,38 +372,15 @@ namespace bigint_ns { // strict weak ordering. friend bool operator<(const bigint &lhs, const bigint &rhs) { - if (!lhs.is_big) { - if (!rhs.is_big) { - return lhs.base_repr < rhs.base_repr; - } - // TODO: Implement half - auto temp = std::to_string(lhs.base_repr); - if(temp.length() == rhs.str.length()) - { - return temp < rhs.str; - } - - return temp.length() < rhs.str.length(); - } - - if (is_negative(lhs) && is_negative(rhs)) { - return abs(lhs) > abs(rhs); + if (lhs.is_big || rhs.is_big) + { + return less_than(lhs.is_big ? lhs.str : std::to_string(lhs.base_repr), rhs.is_big ? rhs.str : std::to_string(rhs.base_repr)); } - if (is_negative(lhs)) { - return true; - } + return lhs.base_repr < rhs.base_repr; - if (is_negative(rhs)) { - return false; - } - if(lhs.str.length() == rhs.str.length()) - { - return lhs.str < rhs.str; - } - return lhs.str.length() < rhs.str.length(); } friend bool operator>(const bigint &l, const bigint &r) @@ -727,67 +702,29 @@ namespace bigint_ns { return 0; } - // As result can be very large store it in string - std::string ans; - - // Find prefix of number that is larger - // than divisor. - int idx = 0; - bigint temp = char_to_int(numerator.str[idx]); - while (idx < (numerator.str.size() - 1) && temp < denominator) { - temp = temp * 10 + (char_to_int(numerator.str[++idx])); - } - // Repeatedly divide divisor with temp. After - // every division, update temp to include one - // more digit. - while ((numerator.str.size() - 1) > idx) + bigint count = "0"; + bigint temp = numerator; + while(temp >= denominator) { - // Store result in answer i.e. temp / divisor - ans += int_to_char(int(temp / int(denominator))); - - // Take next digit of number - temp = char_to_int(int((temp % denominator) * 10 + numerator.str[++idx])); - } - - ans += int_to_char(int(temp / int(denominator))); - - // If divisor is greater than number - if (ans.length() == 0) - return "0"; - - // else return ans - return ans; - - - // TODO: Implement shortDivide -// if(str2.length() <= 19) { -// std::stringstream strstrm(str2); -// unsigned long long int int_str2 = 0; -// strstrm >> int_str2; -// ans = shortDivide(str1, int_str2); -// } - } - - inline std::string bigint::shortDivide(std::string s1, unsigned long long int divisor) - { // return arithmetic division of str1/str2 - std::string ans; - int idx = 0; - long long int temp = s1[idx] - '0'; - - while (temp < divisor) { - temp = temp * 10 + (s1[++idx] - '0'); - if (idx >= s1.length()) - break; - } - while (s1.length() > idx) { - ans += (temp / divisor) + '0'; - temp = (temp % divisor) * 10 + s1[++idx] - '0'; + int lenDiff = temp.str.length() - denominator.str.length(); + if(lenDiff > 0 && temp.str[0] > denominator.str[0]) + { + count += pow(10, lenDiff); + temp -= denominator * pow(10, lenDiff); + } + else if(lenDiff > 0) + { + count += pow(10, lenDiff-1); + temp -= denominator * pow(10, lenDiff-1); + } + else + { + count++; + temp -= denominator; + } } - if (ans.length() == 0) - return "0"; - - return ans; + return count; } diff --git a/tests/test.cpp b/tests/test.cpp index 428e9bb..9b3f4cf 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -69,9 +69,11 @@ TEST(BigInt_test, Multiplication_Tests) bigint small_number = 9955; bigint huge_number_1 = "123456789"; bigint huge_number_2 = "9999999999999999999"; + bigint max_ll = LLONG_MAX; ASSERT_EQ(bigint("30") * bigint("20"), "600"); 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"); } @@ -82,10 +84,11 @@ TEST(BigInt_test, Division_Tests) bigint small_number = 9955; bigint huge_number_1 = "123456789"; bigint huge_number_2 = "9999999999999999999"; - ASSERT_EQ(bigint("30") / bigint("20"), "1"); + 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); }