|
1 | 1 | //! [`Uint`] division operations. |
2 | 2 |
|
3 | 3 | use super::div_limb::{ |
4 | | - div2by1, div_rem_limb_with_reciprocal, rem_limb_with_reciprocal, rem_limb_with_reciprocal_wide, |
| 4 | + div3by2, div_rem_limb_with_reciprocal, rem_limb_with_reciprocal, rem_limb_with_reciprocal_wide, |
5 | 5 | Reciprocal, |
6 | 6 | }; |
7 | | -use crate::{ |
8 | | - CheckedDiv, ConstChoice, DivRemLimb, Limb, NonZero, RemLimb, Uint, WideWord, Word, Wrapping, |
9 | | -}; |
| 7 | +use crate::{CheckedDiv, ConstChoice, DivRemLimb, Limb, NonZero, RemLimb, Uint, Word, Wrapping}; |
10 | 8 | use core::ops::{Div, DivAssign, Rem, RemAssign}; |
11 | 9 | use subtle::CtOption; |
12 | 10 |
|
@@ -135,21 +133,7 @@ impl<const LIMBS: usize> Uint<LIMBS> { |
135 | 133 |
|
136 | 134 | loop { |
137 | 135 | // Divide high dividend words by the high divisor word to estimate the quotient word |
138 | | - let (mut quo, mut rem) = div2by1(x_hi.0, x[xi].0, &reciprocal); |
139 | | - |
140 | | - i = 0; |
141 | | - while i < 2 { |
142 | | - let qy = (quo as WideWord) * (y[yc - 2].0 as WideWord); |
143 | | - let rx = ((rem as WideWord) << Word::BITS) | (x[xi - 1].0 as WideWord); |
144 | | - // Constant-time check for q*y[-2] < r*x[-1], based on ConstChoice::from_word_lt |
145 | | - let diff = ConstChoice::from_word_lsb( |
146 | | - ((((!rx) & qy) | (((!rx) | qy) & (rx.wrapping_sub(qy)))) |
147 | | - >> (WideWord::BITS - 1)) as Word, |
148 | | - ); |
149 | | - quo = diff.select_word(quo, quo.saturating_sub(1)); |
150 | | - rem = diff.select_word(rem, rem.saturating_add(y[yc - 1].0)); |
151 | | - i += 1; |
152 | | - } |
| 136 | + let (mut quo, _) = div3by2(x_hi.0, x[xi].0, x[xi - 1].0, &reciprocal, y[yc - 2].0); |
153 | 137 |
|
154 | 138 | // Subtract q*divisor from the dividend |
155 | 139 | carry = Limb::ZERO; |
@@ -283,21 +267,7 @@ impl<const LIMBS: usize> Uint<LIMBS> { |
283 | 267 |
|
284 | 268 | loop { |
285 | 269 | // Divide high dividend words by the high divisor word to estimate the quotient word |
286 | | - let (mut quo, mut rem) = div2by1(x_hi.0, x[xi].0, &reciprocal); |
287 | | - |
288 | | - i = 0; |
289 | | - while i < 2 { |
290 | | - let qy = (quo as WideWord) * (y[yc - 2].0 as WideWord); |
291 | | - let rx = ((rem as WideWord) << Word::BITS) | (x[xi - 1].0 as WideWord); |
292 | | - // Constant-time check for q*y[-2] < r*x[-1], based on ConstChoice::from_word_lt |
293 | | - let diff = ConstChoice::from_word_lsb( |
294 | | - ((((!rx) & qy) | (((!rx) | qy) & (rx.wrapping_sub(qy)))) |
295 | | - >> (WideWord::BITS - 1)) as Word, |
296 | | - ); |
297 | | - quo = diff.select_word(quo, quo.saturating_sub(1)); |
298 | | - rem = diff.select_word(rem, rem.saturating_add(y[yc - 1].0)); |
299 | | - i += 1; |
300 | | - } |
| 270 | + let (quo, _) = div3by2(x_hi.0, x[xi].0, x[xi - 1].0, &reciprocal, y[yc - 2].0); |
301 | 271 |
|
302 | 272 | // Subtract q*divisor from the dividend |
303 | 273 | carry = Limb::ZERO; |
@@ -357,6 +327,12 @@ impl<const LIMBS: usize> Uint<LIMBS> { |
357 | 327 | (x[i], carry) = (Limb((x[i].0 >> lshift) | carry.0), Limb(x[i].0 << rshift)); |
358 | 328 | } |
359 | 329 | } |
| 330 | + // Clear upper limbs |
| 331 | + i = LIMBS - 1; |
| 332 | + while i >= yc { |
| 333 | + x[i] = Limb::ZERO; |
| 334 | + i -= 1; |
| 335 | + } |
360 | 336 |
|
361 | 337 | Uint::new(x) |
362 | 338 | } |
@@ -864,7 +840,7 @@ impl<const LIMBS: usize> RemLimb for Uint<LIMBS> { |
864 | 840 |
|
865 | 841 | #[cfg(test)] |
866 | 842 | mod tests { |
867 | | - use crate::{Limb, NonZero, Uint, Word, U256}; |
| 843 | + use crate::{Limb, NonZero, Uint, Word, U128, U256, U64}; |
868 | 844 |
|
869 | 845 | #[cfg(feature = "rand")] |
870 | 846 | use { |
@@ -940,6 +916,26 @@ mod tests { |
940 | 916 | assert_eq!(r, U256::ZERO); |
941 | 917 | } |
942 | 918 |
|
| 919 | + #[test] |
| 920 | + fn div_edge() { |
| 921 | + let lo = U128::from_be_hex("00000000000000000000000000000001"); |
| 922 | + let hi = U128::from_be_hex("00000000000000000000000000000001"); |
| 923 | + let y = U128::from_be_hex("00000000000000010000000000000001"); |
| 924 | + let x = U256::from((lo, hi)); |
| 925 | + let expect = (U64::MAX.resize::<{ U256::LIMBS }>(), U256::from(2u64)); |
| 926 | + |
| 927 | + let (q1, r1) = Uint::div_rem(&x, &NonZero::new(y.resize()).unwrap()); |
| 928 | + assert_eq!((q1, r1), expect); |
| 929 | + let (q2, r2) = Uint::div_rem_vartime(&x, &NonZero::new(y).unwrap()); |
| 930 | + assert_eq!((q2, r2.resize()), expect); |
| 931 | + let r3 = Uint::rem(&x, &NonZero::new(y.resize()).unwrap()); |
| 932 | + assert_eq!(r3, expect.1); |
| 933 | + let r4 = Uint::rem_vartime(&x, &NonZero::new(y.resize()).unwrap()); |
| 934 | + assert_eq!(r4, expect.1); |
| 935 | + let r5 = Uint::rem_wide_vartime((lo, hi), &NonZero::new(y).unwrap()); |
| 936 | + assert_eq!(r5.resize(), expect.1); |
| 937 | + } |
| 938 | + |
943 | 939 | #[test] |
944 | 940 | fn reduce_one() { |
945 | 941 | let r = U256::from(10u8).rem_vartime(&NonZero::new(U256::ONE).unwrap()); |
|
0 commit comments