abacus: bignum: add division & modulo
Like the C language, the `%` operator is rather the remainder, such that `(a/b)*b + (a%b) = a`. I still call it modulo though...
This commit is contained in:
parent
7cd0664e60
commit
4e3d53ecd4
|
@ -92,6 +92,37 @@ digits_type do_multiplication(digits_type const& lhs, digits_type const& rhs) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<digits_type, digits_type> do_div_mod(digits_type const& lhs,
|
||||||
|
digits_type const& rhs) {
|
||||||
|
if (rhs.size() == 0) {
|
||||||
|
throw std::invalid_argument("attempt to divide by zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
digits_type quotient;
|
||||||
|
digits_type remainder = lhs;
|
||||||
|
|
||||||
|
while (!do_less_than(remainder, rhs)) {
|
||||||
|
digits_type multiple = rhs;
|
||||||
|
digits_type rank;
|
||||||
|
rank.push_back(1);
|
||||||
|
|
||||||
|
digits_type prev_multiple = multiple;
|
||||||
|
digits_type prev_rank = rank;
|
||||||
|
|
||||||
|
while (!do_less_than(remainder, multiple)) {
|
||||||
|
prev_multiple = multiple;
|
||||||
|
prev_rank = rank;
|
||||||
|
multiple = do_addition(multiple, multiple);
|
||||||
|
rank = do_addition(rank, rank);
|
||||||
|
}
|
||||||
|
|
||||||
|
quotient = do_addition(quotient, prev_rank);
|
||||||
|
remainder = do_substraction(remainder, prev_multiple);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(quotient, remainder);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
BigNum::BigNum(std::int64_t number) {
|
BigNum::BigNum(std::int64_t number) {
|
||||||
|
@ -226,6 +257,14 @@ void BigNum::multiply(BigNum const& rhs) {
|
||||||
canonicalize();
|
canonicalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BigNum::divide(BigNum const& rhs) {
|
||||||
|
std::tie(*this, std::ignore) = div_mod(*this, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BigNum::modulo(BigNum const& rhs) {
|
||||||
|
std::tie(std::ignore, *this) = div_mod(*this, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
bool BigNum::equal(BigNum const& rhs) const {
|
bool BigNum::equal(BigNum const& rhs) const {
|
||||||
assert(is_canonicalized());
|
assert(is_canonicalized());
|
||||||
assert(rhs.is_canonicalized());
|
assert(rhs.is_canonicalized());
|
||||||
|
@ -295,4 +334,28 @@ bool BigNum::is_negative() const {
|
||||||
return sign_ <= 0;
|
return sign_ <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<BigNum, BigNum> div_mod(BigNum const& lhs, BigNum const& rhs) {
|
||||||
|
assert(lhs.is_canonicalized());
|
||||||
|
assert(rhs.is_canonicalized());
|
||||||
|
|
||||||
|
if (lhs.is_zero()) {
|
||||||
|
return std::make_pair(BigNum(), BigNum());
|
||||||
|
}
|
||||||
|
|
||||||
|
BigNum quotient;
|
||||||
|
BigNum remainder;
|
||||||
|
|
||||||
|
std::tie(quotient.digits_, remainder.digits_)
|
||||||
|
= do_div_mod(lhs.digits_, rhs.digits_);
|
||||||
|
|
||||||
|
// Respect the identity `(a/b)*b + (a%b) = a`
|
||||||
|
quotient.sign_ = lhs.sign_ * rhs.sign_;
|
||||||
|
remainder.sign_ = lhs.sign_;
|
||||||
|
|
||||||
|
quotient.canonicalize();
|
||||||
|
remainder.canonicalize();
|
||||||
|
|
||||||
|
return std::make_pair(quotient, remainder);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace abacus::bignum
|
} // namespace abacus::bignum
|
||||||
|
|
|
@ -62,6 +62,31 @@ public:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
friend BigNum& operator/=(BigNum& lhs, BigNum const& rhs) {
|
||||||
|
lhs.divide(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.modulo(rhs);
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend BigNum operator%(BigNum const& lhs, BigNum const& rhs) {
|
||||||
|
auto ret = lhs;
|
||||||
|
ret %= rhs;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::pair<BigNum, BigNum> div_mod(BigNum const& lhs,
|
||||||
|
BigNum const& rhs);
|
||||||
|
|
||||||
friend bool operator==(BigNum const& lhs, BigNum const& rhs) {
|
friend bool operator==(BigNum const& lhs, BigNum const& rhs) {
|
||||||
return lhs.equal(rhs);
|
return lhs.equal(rhs);
|
||||||
}
|
}
|
||||||
|
@ -98,6 +123,8 @@ private:
|
||||||
void add(BigNum const& rhs);
|
void add(BigNum const& rhs);
|
||||||
void substract(BigNum const& rhs);
|
void substract(BigNum const& rhs);
|
||||||
void multiply(BigNum const& rhs);
|
void multiply(BigNum const& rhs);
|
||||||
|
void divide(BigNum const& rhs);
|
||||||
|
void modulo(BigNum const& rhs);
|
||||||
|
|
||||||
bool equal(BigNum const& rhs) const;
|
bool equal(BigNum const& rhs) const;
|
||||||
bool less_than(BigNum const& rhs) const;
|
bool less_than(BigNum const& rhs) const;
|
||||||
|
|
|
@ -206,3 +206,64 @@ TEST(BigNum, multiplication_mixed_signs) {
|
||||||
EXPECT_EQ(minus_two * two, minus_four);
|
EXPECT_EQ(minus_two * two, minus_four);
|
||||||
EXPECT_EQ(minus_two * minus_two, four);
|
EXPECT_EQ(minus_two * minus_two, four);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(BigNum, division) {
|
||||||
|
auto const zero = BigNum(0);
|
||||||
|
auto const one = BigNum(1);
|
||||||
|
auto const two = BigNum(2);
|
||||||
|
|
||||||
|
EXPECT_EQ(one / one, one);
|
||||||
|
EXPECT_EQ(one / two, zero);
|
||||||
|
EXPECT_EQ(two / one, two);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BigNum, division_negative) {
|
||||||
|
auto const zero = BigNum(0);
|
||||||
|
auto const one = BigNum(1);
|
||||||
|
auto const minus_one = BigNum(-1);
|
||||||
|
auto const two = BigNum(2);
|
||||||
|
auto const minus_two = BigNum(-2);
|
||||||
|
|
||||||
|
EXPECT_EQ(one / minus_one, minus_one);
|
||||||
|
EXPECT_EQ(minus_one / one, minus_one);
|
||||||
|
EXPECT_EQ(minus_one / minus_one, one);
|
||||||
|
EXPECT_EQ(two / minus_two, minus_one);
|
||||||
|
EXPECT_EQ(minus_two / two, minus_one);
|
||||||
|
EXPECT_EQ(minus_two / minus_two, one);
|
||||||
|
|
||||||
|
EXPECT_EQ(one / minus_two, zero);
|
||||||
|
EXPECT_EQ(minus_one / two, zero);
|
||||||
|
EXPECT_EQ(one / minus_two, zero);
|
||||||
|
EXPECT_EQ(minus_one / minus_two, zero);
|
||||||
|
EXPECT_EQ(two / minus_one, minus_two);
|
||||||
|
EXPECT_EQ(minus_two / minus_one, two);
|
||||||
|
EXPECT_EQ(minus_two / one, minus_two);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BigNum, division_truncate) {
|
||||||
|
auto const one = BigNum(1);
|
||||||
|
auto const minus_one = BigNum(-1);
|
||||||
|
auto const three = BigNum(3);
|
||||||
|
auto const minus_three = BigNum(-3);
|
||||||
|
auto const five = BigNum(5);
|
||||||
|
auto const minus_five = BigNum(-5);
|
||||||
|
|
||||||
|
EXPECT_EQ(five / three, one);
|
||||||
|
EXPECT_EQ(five / minus_three, minus_one);
|
||||||
|
EXPECT_EQ(minus_five / three, minus_one);
|
||||||
|
EXPECT_EQ(minus_five / minus_three, one);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(BigNum, div_mod_identity) {
|
||||||
|
auto const three = BigNum(3);
|
||||||
|
auto const minus_three = BigNum(-3);
|
||||||
|
auto const five = BigNum(5);
|
||||||
|
auto const minus_five = BigNum(-5);
|
||||||
|
|
||||||
|
EXPECT_EQ((five / three) * three + (five % three), five);
|
||||||
|
EXPECT_EQ((five / minus_three) * minus_three + (five % minus_three), five);
|
||||||
|
EXPECT_EQ((minus_five / three) * three + (minus_five % three), minus_five);
|
||||||
|
EXPECT_EQ((minus_five / minus_three) * minus_three
|
||||||
|
+ (minus_five % minus_three),
|
||||||
|
minus_five);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue