Skip to content

Commit db33158

Browse files
committed
Implemented div_limb and div_rem_limb_with_reciprocal
1 parent 0dd4ef7 commit db33158

File tree

5 files changed

+152
-9
lines changed

5 files changed

+152
-9
lines changed

src/uint/boxed.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod bits;
88
mod cmp;
99
mod ct;
1010
mod div;
11+
mod div_limb;
1112
pub(crate) mod encoding;
1213
mod inv_mod;
1314
mod mul;
@@ -51,6 +52,20 @@ pub struct BoxedUint {
5152
}
5253

5354
impl BoxedUint {
55+
/// Convenience function to instantiate BoxedUint from Limb(s)
56+
pub fn from_limbs(limbs: Vec<Limb>) -> Self {
57+
return Self {
58+
limbs: limbs.into(),
59+
};
60+
}
61+
62+
/// Instantiate BoxedUint by cloning from a slice of Limb(s)
63+
pub fn copy_from_limb_slice(limbs: &[Limb]) -> Self {
64+
return Self {
65+
limbs: limbs.to_vec().into(),
66+
};
67+
}
68+
5469
fn limbs_for_precision(at_least_bits_precision: u32) -> usize {
5570
((at_least_bits_precision + Limb::BITS - 1) / Limb::BITS) as usize
5671
}

src/uint/boxed/div.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
//! [`BoxedUint`] division operations.
22
3-
use crate::{BoxedUint, CheckedDiv, Limb, NonZero, Wrapping, Reciprocal};
3+
use crate::{BoxedUint, CheckedDiv, Limb, NonZero, Reciprocal, Wrapping};
44
use core::ops::{Div, DivAssign, Rem, RemAssign};
55
use subtle::{Choice, ConstantTimeEq, ConstantTimeLess, CtOption};
66

7+
use super::div_limb::div_rem_limb_with_reciprocal;
8+
79
impl BoxedUint {
810
/// Computes self / rhs, returns the quotient, remainder.
911
pub fn div_rem(&self, rhs: &NonZero<Self>) -> (Self, Self) {
@@ -24,11 +26,17 @@ impl BoxedUint {
2426
self.div_rem_vartime_unchecked(rhs.as_ref())
2527
}
2628

27-
/// Computes `self` / `rhs` using a pre-made reciprocal
28-
/// returns the quotient (q) and remainder (r)
29+
/// Computes `self` / `rhs` using a pre-made reciprocal,
30+
/// returns the quotient (q) and remainder (r).
31+
#[inline(always)]
2932
pub fn div_rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> (Self, Limb) {
30-
todo!("Need to implement shl_limb");
31-
// No need to implement div2by1
33+
div_rem_limb_with_reciprocal(self, reciprocal)
34+
}
35+
36+
/// Computes `self` / `rhs`, returns the quotient (q) and remainder (r).
37+
#[inline(always)]
38+
pub fn div_rem_limb(&self, rhs: NonZero<Limb>) -> (Self, Limb) {
39+
div_rem_limb_with_reciprocal(self, &Reciprocal::new(rhs))
3240
}
3341

3442
/// Computes self % rhs, returns the remainder.

src/uint/boxed/div_limb.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//! Implementation of constant-time division via reciprocal precomputation, as described in
2+
//! "Improved Division by Invariant Integers" by Niels Möller and Torbjorn Granlund
3+
//! (DOI: 10.1109/TC.2010.143, <https://gmplib.org/~tege/division-paper.pdf>).
4+
use crate::{BoxedUint, ConstChoice, Limb, Reciprocal, WideWord, Word};
5+
6+
/// Multiplies `x` and `y`, returning the most significant
7+
/// and the least significant words as `(hi, lo)`.
8+
#[inline(always)]
9+
const fn mulhilo(x: Word, y: Word) -> (Word, Word) {
10+
let res = (x as WideWord) * (y as WideWord);
11+
((res >> Word::BITS) as Word, res as Word)
12+
}
13+
14+
/// Adds wide numbers represented by pairs of (most significant word, least significant word)
15+
/// and returns the result in the same format `(hi, lo)`.
16+
#[inline(always)]
17+
const fn addhilo(x_hi: Word, x_lo: Word, y_hi: Word, y_lo: Word) -> (Word, Word) {
18+
let res = (((x_hi as WideWord) << Word::BITS) | (x_lo as WideWord))
19+
+ (((y_hi as WideWord) << Word::BITS) | (y_lo as WideWord));
20+
((res >> Word::BITS) as Word, res as Word)
21+
}
22+
23+
/// Calculate the quotient and the remainder of the division of a wide word
24+
/// (supplied as high and low words) by `d`, with a precalculated reciprocal `v`.
25+
#[inline(always)]
26+
const fn div2by1(u1: Word, u0: Word, reciprocal: &Reciprocal) -> (Word, Word) {
27+
let d = reciprocal.divisor_normalized;
28+
29+
debug_assert!(d >= (1 << (Word::BITS - 1)));
30+
debug_assert!(u1 < d);
31+
32+
let (q1, q0) = mulhilo(reciprocal.reciprocal, u1);
33+
let (q1, q0) = addhilo(q1, q0, u1, u0);
34+
let q1 = q1.wrapping_add(1);
35+
let r = u0.wrapping_sub(q1.wrapping_mul(d));
36+
37+
let r_gt_q0 = ConstChoice::from_word_lt(q0, r);
38+
let q1 = r_gt_q0.select_word(q1, q1.wrapping_sub(1));
39+
let r = r_gt_q0.select_word(r, r.wrapping_add(d));
40+
41+
// If this was a normal `if`, we wouldn't need wrapping ops, because there would be no overflow.
42+
// But since we calculate both results either way, we have to wrap.
43+
// Added an assert to still check the lack of overflow in debug mode.
44+
debug_assert!(r < d || q1 < Word::MAX);
45+
let r_ge_d = ConstChoice::from_word_le(d, r);
46+
let q1 = r_ge_d.select_word(q1, q1.wrapping_add(1));
47+
let r = r_ge_d.select_word(r, r.wrapping_sub(d));
48+
49+
(q1, r)
50+
}
51+
52+
/// Divides `u` by the divisor encoded in the `reciprocal`, and returns
53+
/// the quotient and the remainder.
54+
#[inline(always)]
55+
#[allow(dead_code, unused_variables)]
56+
pub(crate) fn div_rem_limb_with_reciprocal(
57+
u: &BoxedUint,
58+
reciprocal: &Reciprocal,
59+
) -> (BoxedUint, Limb) {
60+
let nlimbs = u.as_limbs().len();
61+
let (u_shifted, u_hi) = u.shl_limb(reciprocal.shift);
62+
let mut r = u_hi.0;
63+
let mut q = vec![Limb::ZERO; nlimbs];
64+
65+
let mut j = nlimbs;
66+
while j > 0 {
67+
j -= 1;
68+
let (qj, rj) = div2by1(r, u_shifted.as_limbs()[j].0, reciprocal);
69+
q[j] = Limb(qj);
70+
r = rj;
71+
}
72+
(BoxedUint { limbs: q.into() }, Limb(r >> reciprocal.shift))
73+
}
74+
75+
#[cfg(test)]
76+
mod tests {
77+
use super::{div2by1, Reciprocal};
78+
use crate::{Limb, NonZero, Word};
79+
#[test]
80+
fn div2by1_overflow() {
81+
// A regression test for a situation when in div2by1() an operation (`q1 + 1`)
82+
// that is protected from overflowing by a condition in the original paper (`r >= d`)
83+
// still overflows because we're calculating the results for both branches.
84+
let r = Reciprocal::new(NonZero::new(Limb(Word::MAX - 1)).unwrap());
85+
assert_eq!(
86+
div2by1(Word::MAX - 2, Word::MAX - 63, &r),
87+
(Word::MAX, Word::MAX - 65)
88+
);
89+
}
90+
}

src/uint/boxed/shl.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! [`BoxedUint`] bitwise left shift operations.
22
3-
use crate::{BoxedUint, ConstChoice, Limb, Zero};
3+
use crate::{BoxedUint, ConstChoice, Limb, Word, Zero};
44
use core::ops::{Shl, ShlAssign};
55
use subtle::{Choice, ConstantTimeLess};
66

@@ -71,6 +71,36 @@ impl BoxedUint {
7171
Some(())
7272
}
7373

74+
/// Computes `self << shift` where `0 <= shift < Limb::BITS`,
75+
/// returning the result and the carry.
76+
#[inline(always)]
77+
pub(crate) fn shl_limb(&self, shift: u32) -> (Self, Limb) {
78+
let nlimbs = self.limbs.len();
79+
let mut limbs = vec![Limb::ZERO; nlimbs];
80+
81+
let nz = ConstChoice::from_u32_nonzero(shift);
82+
let lshift = shift;
83+
let rshift = nz.if_true_u32(Limb::BITS - shift);
84+
let carry = nz.if_true_word(self.limbs[nlimbs - 1].0.wrapping_shr(Word::BITS - shift));
85+
86+
limbs[0] = Limb(self.limbs[0].0 << lshift);
87+
let mut i = 1;
88+
while i < nlimbs {
89+
let mut limb = self.limbs[i].0 << lshift;
90+
let hi = self.limbs[i - 1].0 >> rshift;
91+
limb |= nz.if_true_word(hi);
92+
limbs[i] = Limb(limb);
93+
i += 1
94+
}
95+
96+
(
97+
BoxedUint {
98+
limbs: limbs.into(),
99+
},
100+
Limb(carry),
101+
)
102+
}
103+
74104
/// Computes `self << shift`.
75105
/// Returns `None` if `shift >= self.bits_precision()`.
76106
///

src/uint/div_limb.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,9 @@ const fn div2by1(u1: Word, u0: Word, reciprocal: &Reciprocal) -> (Word, Word) {
160160
/// A pre-calculated reciprocal for division by a single limb.
161161
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
162162
pub struct Reciprocal {
163-
divisor_normalized: Word,
164-
shift: u32,
165-
reciprocal: Word,
163+
pub(crate) divisor_normalized: Word,
164+
pub(crate) shift: u32,
165+
pub(crate) reciprocal: Word,
166166
}
167167

168168
impl Reciprocal {

0 commit comments

Comments
 (0)