diff --git a/src/bignum/bignum.cc b/src/bignum/bignum.cc index 5d6ccb2..cf0d6b6 100644 --- a/src/bignum/bignum.cc +++ b/src/bignum/bignum.cc @@ -18,6 +18,57 @@ bool do_less_than(digits_type const& lhs, digits_type const& rhs) { rhs.rend()); } +digits_type do_addition(digits_type const& lhs, digits_type const& rhs) { + int carry = 0; + digits_type res; + + auto it1 = lhs.begin(); + auto it2 = rhs.begin(); + + auto const end1 = lhs.end(); + auto const end2 = rhs.end(); + + while (it1 != end1 && it2 != end2) { + int addition = *it1 + *it2 + carry; + carry = addition / 10; + res.push_back(addition % 10); + ++it1; + ++it2; + } + + auto it = it1; + auto end = end1; + if (it1 == end1) { + it = it2; + end = end2; + } + + while (it != end) { + int addition = *it + carry; + carry = addition / 10; + res.push_back(addition % 10); + ++it; + } + + return res; +} + +digits_type do_substraction(digits_type const& lhs, digits_type const& rhs) { + assert(!do_less_than(lhs, rhs)); + + digits_type complement; + auto const take_complement = [](auto num) { return 9 - num; }; + std::transform(lhs.begin(), lhs.end(), std::back_inserter(complement), + take_complement); + + complement = do_addition(complement, rhs); + + std::transform(complement.begin(), complement.end(), complement.begin(), + take_complement); + + return complement; +} + } // namespace BigNum::BigNum(std::int64_t number) { @@ -60,6 +111,48 @@ void BigNum::flip_sign() { sign_ *= -1; } +void BigNum::add(BigNum const& rhs) { + assert(is_canonicalized()); + assert(rhs.is_canonicalized()); + + if (rhs.sign_ == 0) { + return; + } + + if (sign_ == 0) { + *this = rhs; + return; + } + + if (sign_ == rhs.sign_) { + digits_ = do_addition(digits_, rhs.digits_); + } else { + bool flipped = do_less_than(digits_, rhs.digits_); + if (flipped) { + digits_ = do_substraction(rhs.digits_, digits_); + } else { + digits_ = do_substraction(digits_, rhs.digits_); + } + if (flipped) { + flip_sign(); + } + canonicalize(); + } + + assert(is_canonicalized()); +} + +void BigNum::substract(BigNum const& rhs) { + assert(is_canonicalized()); + assert(rhs.is_canonicalized()); + + flip_sign(); + add(rhs); + flip_sign(); + + assert(is_canonicalized()); +} + bool BigNum::equal(BigNum const& rhs) const { assert(is_canonicalized()); assert(rhs.is_canonicalized()); diff --git a/src/bignum/bignum.hh b/src/bignum/bignum.hh index 8774eb0..149e962 100644 --- a/src/bignum/bignum.hh +++ b/src/bignum/bignum.hh @@ -25,6 +25,28 @@ public: return ret; } + friend BigNum& operator+=(BigNum& lhs, BigNum const& rhs) { + lhs.add(rhs); + return lhs; + } + + friend BigNum operator+(BigNum const& lhs, BigNum const& rhs) { + auto ret = lhs; + ret += rhs; + return ret; + } + + friend BigNum& operator-=(BigNum& lhs, BigNum const& rhs) { + lhs.substract(rhs); + return lhs; + } + + friend BigNum operator-(BigNum const& lhs, BigNum const& rhs) { + auto ret = lhs; + ret -= rhs; + return ret; + } + friend bool operator==(BigNum const& lhs, BigNum const& rhs) { return lhs.equal(rhs); } @@ -53,6 +75,8 @@ private: std::ostream& dump(std::ostream& out) const; void flip_sign(); + void add(BigNum const& rhs); + void substract(BigNum const& rhs); bool equal(BigNum const& rhs) const; bool less_than(BigNum const& rhs) const; diff --git a/tests/unit/bignum.cc b/tests/unit/bignum.cc index 65ff7d5..bf788b6 100644 --- a/tests/unit/bignum.cc +++ b/tests/unit/bignum.cc @@ -56,3 +56,89 @@ TEST(BigNum, unary) { auto const minus_one = BigNum(-1); EXPECT_EQ(minus_one, -one); } + +TEST(BigNum, addition_positive) { + auto const zero = BigNum(0); + auto const one = BigNum(1); + auto const two = BigNum(2); + + EXPECT_EQ(zero + zero, zero); + EXPECT_EQ(zero + one, one); + EXPECT_EQ(one + zero, one); + EXPECT_EQ(one + one, two); +} + +TEST(BigNum, addition_negative) { + auto const zero = BigNum(0); + auto const minus_one = BigNum(-1); + auto const minus_two = BigNum(-2); + + EXPECT_EQ(zero + zero, zero); + EXPECT_EQ(zero + minus_one, minus_one); + EXPECT_EQ(minus_one + zero, minus_one); + EXPECT_EQ(minus_one + minus_one, minus_two); +} + +TEST(BigNum, addition_mixed_signs) { + auto const zero = BigNum(0); + auto const one = BigNum(1); + auto const minus_one = BigNum(-1); + + EXPECT_EQ(one + minus_one, zero); + EXPECT_EQ(minus_one + one, zero); +} + +TEST(BigNum, addition_flips_sign) { + auto const one = BigNum(1); + auto const two = BigNum(2); + auto const minus_one = BigNum(-1); + auto const minus_two = BigNum(-2); + + EXPECT_EQ(one + minus_two, minus_one); + EXPECT_EQ(minus_two + one, minus_one); + + EXPECT_EQ(minus_one + two, one); + EXPECT_EQ(two + minus_one, one); +} + +TEST(BigNum, substraction_positive) { + auto const zero = BigNum(0); + auto const one = BigNum(1); + auto const two = BigNum(2); + + EXPECT_EQ(zero - zero, zero); + EXPECT_EQ(two - zero, two); + EXPECT_EQ(two - one, one); + EXPECT_EQ(one - one, zero); +} + +TEST(BigNum, substraction_negative) { + auto const zero = BigNum(0); + auto const minus_one = BigNum(-1); + auto const one = BigNum(1); + + EXPECT_EQ(zero - zero, zero); + EXPECT_EQ(zero - minus_one, one); + EXPECT_EQ(zero - one, minus_one); +} + +TEST(BigNum, substraction_mixed_signs) { + auto const zero = BigNum(0); + auto const one = BigNum(1); + auto const two = BigNum(2); + auto const minus_one = BigNum(-1); + auto const minus_two = BigNum(-2); + + EXPECT_EQ(one - minus_one, two); + EXPECT_EQ(minus_one - one, minus_two); +} + +TEST(BigNum, substraction_flips_sign) { + auto const one = BigNum(1); + auto const two = BigNum(2); + auto const minus_one = BigNum(-1); + auto const minus_two = BigNum(-2); + + EXPECT_EQ(one - two, minus_one); + EXPECT_EQ(minus_one - minus_two, one); +}