diff options
Diffstat (limited to 'src/lib/math/bigint/divide.cpp')
-rw-r--r-- | src/lib/math/bigint/divide.cpp | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/src/lib/math/bigint/divide.cpp b/src/lib/math/bigint/divide.cpp new file mode 100644 index 000000000..8b21bce13 --- /dev/null +++ b/src/lib/math/bigint/divide.cpp @@ -0,0 +1,140 @@ +/* +* Division Algorithm +* (C) 1999-2007,2012 Jack Lloyd +* +* Distributed under the terms of the Botan license +*/ + +#include <botan/divide.h> +#include <botan/internal/mp_core.h> +#include <botan/internal/mp_madd.h> + +namespace Botan { + +namespace { + +/* +* Handle signed operands, if necessary +*/ +void sign_fixup(const BigInt& x, const BigInt& y, BigInt& q, BigInt& r) + { + if(x.sign() == BigInt::Negative) + { + q.flip_sign(); + if(r.is_nonzero()) { --q; r = y.abs() - r; } + } + if(y.sign() == BigInt::Negative) + q.flip_sign(); + } + +bool division_check(word q, word y2, word y1, + word x3, word x2, word x1) + { + // Compute (y3,y2,y1) = (y2,y1) * q + + word y3 = 0; + y1 = word_madd2(q, y1, &y3); + y2 = word_madd2(q, y2, &y3); + + // Return (y3,y2,y1) >? (x3,x2,x1) + + if(y3 > x3) return true; + if(y3 < x3) return false; + + if(y2 > x2) return true; + if(y2 < x2) return false; + + if(y1 > x1) return true; + if(y1 < x1) return false; + + return false; + } + +} + +/* +* Solve x = q * y + r +*/ +void divide(const BigInt& x, const BigInt& y_arg, BigInt& q, BigInt& r) + { + if(y_arg.is_zero()) + throw BigInt::DivideByZero(); + + BigInt y = y_arg; + const size_t y_words = y.sig_words(); + + r = x; + q = 0; + + r.set_sign(BigInt::Positive); + y.set_sign(BigInt::Positive); + + s32bit compare = r.cmp(y); + + if(compare == 0) + { + q = 1; + r = 0; + } + else if(compare > 0) + { + size_t shifts = 0; + word y_top = y.word_at(y.sig_words()-1); + while(y_top < MP_WORD_TOP_BIT) { y_top <<= 1; ++shifts; } + y <<= shifts; + r <<= shifts; + + const size_t n = r.sig_words() - 1, t = y_words - 1; + + if(n < t) + throw Internal_Error("BigInt division word sizes"); + + q.grow_to(n - t + 1); + + word* q_words = q.mutable_data(); + + if(n <= t) + { + while(r > y) { r -= y; ++q; } + r >>= shifts; + sign_fixup(x, y_arg, q, r); + return; + } + + BigInt temp = y << (MP_WORD_BITS * (n-t)); + + while(r >= temp) { r -= temp; q_words[n-t] += 1; } + + for(size_t j = n; j != t; --j) + { + const word x_j0 = r.word_at(j); + const word x_j1 = r.word_at(j-1); + const word y_t = y.word_at(t); + + if(x_j0 == y_t) + q_words[j-t-1] = MP_WORD_MAX; + else + q_words[j-t-1] = bigint_divop(x_j0, x_j1, y_t); + + while(division_check(q_words[j-t-1], + y_t, y.word_at(t-1), + x_j0, x_j1, r.word_at(j-2))) + { + q_words[j-t-1] -= 1; + } + + r -= (q_words[j-t-1] * y) << (MP_WORD_BITS * (j-t-1)); + + if(r.is_negative()) + { + r += y << (MP_WORD_BITS * (j-t-1)); + q_words[j-t-1] -= 1; + } + } + r >>= shifts; + } + + sign_fixup(x, y_arg, q, r); + } + +} |