-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implemented div_limb and div_rem_limb_with_reciprocal
- Loading branch information
Showing
5 changed files
with
152 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
//! Implementation of constant-time division via reciprocal precomputation, as described in | ||
//! "Improved Division by Invariant Integers" by Niels Möller and Torbjorn Granlund | ||
//! (DOI: 10.1109/TC.2010.143, <https://gmplib.org/~tege/division-paper.pdf>). | ||
use crate::{BoxedUint, ConstChoice, Limb, Reciprocal, WideWord, Word}; | ||
|
||
/// Multiplies `x` and `y`, returning the most significant | ||
/// and the least significant words as `(hi, lo)`. | ||
#[inline(always)] | ||
const fn mulhilo(x: Word, y: Word) -> (Word, Word) { | ||
let res = (x as WideWord) * (y as WideWord); | ||
((res >> Word::BITS) as Word, res as Word) | ||
} | ||
|
||
/// Adds wide numbers represented by pairs of (most significant word, least significant word) | ||
/// and returns the result in the same format `(hi, lo)`. | ||
#[inline(always)] | ||
const fn addhilo(x_hi: Word, x_lo: Word, y_hi: Word, y_lo: Word) -> (Word, Word) { | ||
let res = (((x_hi as WideWord) << Word::BITS) | (x_lo as WideWord)) | ||
+ (((y_hi as WideWord) << Word::BITS) | (y_lo as WideWord)); | ||
((res >> Word::BITS) as Word, res as Word) | ||
} | ||
|
||
/// Calculate the quotient and the remainder of the division of a wide word | ||
/// (supplied as high and low words) by `d`, with a precalculated reciprocal `v`. | ||
#[inline(always)] | ||
const fn div2by1(u1: Word, u0: Word, reciprocal: &Reciprocal) -> (Word, Word) { | ||
let d = reciprocal.divisor_normalized; | ||
|
||
debug_assert!(d >= (1 << (Word::BITS - 1))); | ||
debug_assert!(u1 < d); | ||
|
||
let (q1, q0) = mulhilo(reciprocal.reciprocal, u1); | ||
let (q1, q0) = addhilo(q1, q0, u1, u0); | ||
let q1 = q1.wrapping_add(1); | ||
let r = u0.wrapping_sub(q1.wrapping_mul(d)); | ||
|
||
let r_gt_q0 = ConstChoice::from_word_lt(q0, r); | ||
let q1 = r_gt_q0.select_word(q1, q1.wrapping_sub(1)); | ||
let r = r_gt_q0.select_word(r, r.wrapping_add(d)); | ||
|
||
// If this was a normal `if`, we wouldn't need wrapping ops, because there would be no overflow. | ||
// But since we calculate both results either way, we have to wrap. | ||
// Added an assert to still check the lack of overflow in debug mode. | ||
debug_assert!(r < d || q1 < Word::MAX); | ||
let r_ge_d = ConstChoice::from_word_le(d, r); | ||
let q1 = r_ge_d.select_word(q1, q1.wrapping_add(1)); | ||
let r = r_ge_d.select_word(r, r.wrapping_sub(d)); | ||
|
||
(q1, r) | ||
} | ||
|
||
/// Divides `u` by the divisor encoded in the `reciprocal`, and returns | ||
/// the quotient and the remainder. | ||
#[inline(always)] | ||
#[allow(dead_code, unused_variables)] | ||
pub(crate) fn div_rem_limb_with_reciprocal( | ||
u: &BoxedUint, | ||
reciprocal: &Reciprocal, | ||
) -> (BoxedUint, Limb) { | ||
let nlimbs = u.as_limbs().len(); | ||
let (u_shifted, u_hi) = u.shl_limb(reciprocal.shift); | ||
let mut r = u_hi.0; | ||
let mut q = vec![Limb::ZERO; nlimbs]; | ||
|
||
let mut j = nlimbs; | ||
while j > 0 { | ||
j -= 1; | ||
let (qj, rj) = div2by1(r, u_shifted.as_limbs()[j].0, reciprocal); | ||
q[j] = Limb(qj); | ||
r = rj; | ||
} | ||
(BoxedUint { limbs: q.into() }, Limb(r >> reciprocal.shift)) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::{div2by1, Reciprocal}; | ||
use crate::{Limb, NonZero, Word}; | ||
#[test] | ||
fn div2by1_overflow() { | ||
// A regression test for a situation when in div2by1() an operation (`q1 + 1`) | ||
// that is protected from overflowing by a condition in the original paper (`r >= d`) | ||
// still overflows because we're calculating the results for both branches. | ||
let r = Reciprocal::new(NonZero::new(Limb(Word::MAX - 1)).unwrap()); | ||
assert_eq!( | ||
div2by1(Word::MAX - 2, Word::MAX - 63, &r), | ||
(Word::MAX, Word::MAX - 65) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters