Compare commits

...

3 commits

View file

@ -30,6 +30,35 @@ void trim_leading_zeros(digits_type& num) {
num.erase(it.base(), num.end()); num.erase(it.base(), num.end());
} }
// More optimised than full-on div_mod
digits_type do_halve(digits_type num) {
assert(num.size() != 0);
int carry = 0;
for (auto i = num.rbegin(); i != num.rend(); ++i) {
auto const was_odd = (*i % 2) == 1;
*i /= 2;
*i += carry;
if (was_odd) {
carry = BASE / 2;
} else {
carry = 0;
}
}
trim_leading_zeros(num);
return num;
}
bool is_odd(digits_type const& num) {
if (num.size() == 0) {
return false;
}
return (num.front() % 2) == 1;
}
digits_type do_addition(digits_type const& lhs, digits_type const& rhs) { digits_type do_addition(digits_type const& lhs, digits_type const& rhs) {
int carry = 0; int carry = 0;
digits_type res; digits_type res;
@ -110,59 +139,33 @@ std::pair<digits_type, digits_type> do_div_mod(digits_type const& lhs,
throw std::invalid_argument("attempt to divide by zero"); throw std::invalid_argument("attempt to divide by zero");
} }
digits_type quotient;
digits_type remainder = lhs;
while (!do_less_than(remainder, rhs)) {
// TODO: use `do_halve` to back down after calculate highest multiple
digits_type multiple = rhs; digits_type multiple = rhs;
digits_type rank; digits_type rank;
rank.push_back(1); rank.push_back(1);
digits_type prev_multiple = multiple; while (!do_less_than(lhs, multiple)) {
digits_type prev_rank = rank;
while (!do_less_than(remainder, multiple)) {
prev_multiple = multiple;
prev_rank = rank;
multiple = do_addition(multiple, multiple); multiple = do_addition(multiple, multiple);
rank = do_addition(rank, rank); rank = do_addition(rank, rank);
} }
quotient = do_addition(quotient, prev_rank); digits_type quotient;
remainder = do_substraction(remainder, prev_multiple); digits_type remainder = lhs;
while (!do_less_than(remainder, rhs)) {
while (do_less_than(remainder, multiple)) {
multiple = do_halve(multiple);
rank = do_halve(rank);
}
assert(!do_less_than(multiple, rhs));
quotient = do_addition(quotient, rank);
remainder = do_substraction(remainder, multiple);
} }
return std::make_pair(quotient, remainder); return std::make_pair(quotient, remainder);
} }
// More optimised than full-on div_mod
digits_type do_halve(digits_type num) {
assert(num.size() != 0);
int carry = 0;
for (auto i = num.rbegin(); i != num.rend(); ++i) {
auto const was_odd = (*i % 2) == 1;
*i /= 2;
*i += carry;
if (was_odd) {
carry = BASE / 2;
}
}
trim_leading_zeros(num);
return num;
}
bool is_odd(digits_type const& num) {
if (num.size() == 0) {
return false;
}
return (num.front() % 2) == 1;
}
digits_type do_pow(digits_type lhs, digits_type rhs) { digits_type do_pow(digits_type lhs, digits_type rhs) {
assert(rhs.size() != 0); assert(rhs.size() != 0);