11//! [`BoxedUint`] division operations.
22
33use crate :: {
4- uint:: { boxed, div_limb:: div3by2} ,
4+ uint:: {
5+ boxed,
6+ div_limb:: { div2by1, div3by2} ,
7+ } ,
58 BoxedUint , CheckedDiv , ConstChoice , ConstantTimeSelect , DivRemLimb , Limb , NonZero , Reciprocal ,
6- RemLimb , Word , Wrapping ,
9+ RemLimb , Wrapping ,
710} ;
811use core:: ops:: { Div , DivAssign , Rem , RemAssign } ;
912use subtle:: CtOption ;
@@ -166,7 +169,7 @@ impl BoxedUint {
166169
167170 while xi > 0 {
168171 // Divide high dividend words by the high divisor word to estimate the quotient word
169- let ( mut quo, _ ) = div3by2 ( x_hi. 0 , x_lo. 0 , x[ xi - 1 ] . 0 , & reciprocal, y[ size - 2 ] . 0 ) ;
172+ let mut quo = div3by2 ( x_hi. 0 , x_lo. 0 , x[ xi - 1 ] . 0 , & reciprocal, y[ size - 2 ] . 0 ) ;
170173
171174 // This loop is a no-op once xi is smaller than the number of words in the divisor
172175 let done = ConstChoice :: from_u32_lt ( xi as u32 , dwords - 1 ) ;
@@ -206,14 +209,20 @@ impl BoxedUint {
206209 }
207210
208211 let limb_div = ConstChoice :: from_u32_eq ( 1 , dwords) ;
212+
209213 // Calculate quotient and remainder for the case where the divisor is a single word
210- let ( quo2, rem2) = div3by2 ( x_hi. 0 , x_lo. 0 , 0 , & reciprocal, 0 ) ;
214+ // Note that `div2by1()` will panic if `x_hi >= reciprocal.divisor_normalized`,
215+ // but this can only be the case if `limb_div` is falsy,
216+ // in which case we discard the result anyway,
217+ // so we conditionally set `x_hi` to zero for this branch.
218+ let x_hi_adjusted = Limb :: select ( Limb :: ZERO , x_hi, limb_div) ;
219+ let ( quo2, rem2) = div2by1 ( x_hi_adjusted. 0 , x_lo. 0 , & reciprocal) ;
211220
212221 // Adjust the quotient for single limb division
213222 x[ 0 ] = Limb :: select ( x[ 0 ] , Limb ( quo2) , limb_div) ;
214223
215224 // Copy out the remainder
216- y[ 0 ] = Limb :: select ( x[ 0 ] , Limb ( rem2 as Word ) , limb_div) ;
225+ y[ 0 ] = Limb :: select ( x[ 0 ] , Limb ( rem2) , limb_div) ;
217226 i = 1 ;
218227 while i < size {
219228 y[ i] = Limb :: select ( Limb :: ZERO , x[ i] , ConstChoice :: from_u32_lt ( i as u32 , dwords) ) ;
@@ -382,6 +391,42 @@ impl RemLimb for BoxedUint {
382391 }
383392}
384393
394+ /// Computes `limbs << shift` inplace, where `0 <= shift < Limb::BITS`, returning the carry.
395+ fn shl_limb_vartime ( limbs : & mut [ Limb ] , shift : u32 ) -> Limb {
396+ if shift == 0 {
397+ return Limb :: ZERO ;
398+ }
399+
400+ let lshift = shift;
401+ let rshift = Limb :: BITS - shift;
402+ let limbs_num = limbs. len ( ) ;
403+
404+ let carry = limbs[ limbs_num - 1 ] >> rshift;
405+ for i in ( 1 ..limbs_num) . rev ( ) {
406+ limbs[ i] = ( limbs[ i] << lshift) | ( limbs[ i - 1 ] >> rshift) ;
407+ }
408+ limbs[ 0 ] <<= lshift;
409+
410+ carry
411+ }
412+
413+ /// Computes `limbs >> shift` inplace, where `0 <= shift < Limb::BITS`.
414+ fn shr_limb_vartime ( limbs : & mut [ Limb ] , shift : u32 ) {
415+ if shift == 0 {
416+ return ;
417+ }
418+
419+ let lshift = Limb :: BITS - shift;
420+ let rshift = shift;
421+
422+ let limbs_num = limbs. len ( ) ;
423+
424+ for i in 0 ..limbs_num - 1 {
425+ limbs[ i] = ( limbs[ i] >> rshift) | ( limbs[ i + 1 ] << lshift) ;
426+ }
427+ limbs[ limbs_num - 1 ] >>= rshift;
428+ }
429+
385430/// Computes `x` / `y`, returning the quotient in `x` and the remainder in `y`.
386431///
387432/// This function operates in variable-time. It will panic if the divisor is zero
@@ -408,51 +453,44 @@ pub(crate) fn div_rem_vartime_in_place(x: &mut [Limb], y: &mut [Limb]) {
408453 }
409454
410455 let lshift = y[ yc - 1 ] . leading_zeros ( ) ;
411- let rshift = if lshift == 0 { 0 } else { Limb :: BITS - lshift } ;
412- let mut x_hi = Limb :: ZERO ;
413- let mut carry;
414-
415- if lshift != 0 {
416- // Shift divisor such that it has no leading zeros
417- // This means that div2by1 requires no extra shifts, and ensures that the high word >= b/2
418- carry = Limb :: ZERO ;
419- for i in 0 ..yc {
420- ( y[ i] , carry) = ( Limb ( ( y[ i] . 0 << lshift) | carry. 0 ) , Limb ( y[ i] . 0 >> rshift) ) ;
421- }
422456
423- // Shift the dividend to match
424- carry = Limb :: ZERO ;
425- for i in 0 ..xc {
426- ( x[ i] , carry) = ( Limb ( ( x[ i] . 0 << lshift) | carry. 0 ) , Limb ( x[ i] . 0 >> rshift) ) ;
427- }
428- x_hi = carry;
429- }
457+ // Shift divisor such that it has no leading zeros
458+ // This means that div2by1 requires no extra shifts, and ensures that the high word >= b/2
459+ shl_limb_vartime ( y, lshift) ;
460+
461+ // Shift the dividend to match
462+ let mut x_hi = shl_limb_vartime ( x, lshift) ;
430463
431464 let reciprocal = Reciprocal :: new ( y[ yc - 1 ] . to_nz ( ) . expect ( "zero divisor" ) ) ;
432465
433466 for xi in ( yc - 1 ..xc) . rev ( ) {
434467 // Divide high dividend words by the high divisor word to estimate the quotient word
435- let ( mut quo, _ ) = div3by2 ( x_hi. 0 , x[ xi] . 0 , x[ xi - 1 ] . 0 , & reciprocal, y[ yc - 2 ] . 0 ) ;
468+ let mut quo = div3by2 ( x_hi. 0 , x[ xi] . 0 , x[ xi - 1 ] . 0 , & reciprocal, y[ yc - 2 ] . 0 ) ;
436469
437470 // Subtract q*divisor from the dividend
438- carry = Limb :: ZERO ;
439- let mut borrow = Limb :: ZERO ;
440- let mut tmp;
441- for i in 0 ..yc {
442- ( tmp, carry) = Limb :: ZERO . mac ( y[ i] , Limb ( quo) , carry) ;
443- ( x[ xi + i + 1 - yc] , borrow) = x[ xi + i + 1 - yc] . sbb ( tmp, borrow) ;
444- }
445- ( _, borrow) = x_hi. sbb ( carry, borrow) ;
471+ let borrow = {
472+ let mut carry = Limb :: ZERO ;
473+ let mut borrow = Limb :: ZERO ;
474+ let mut tmp;
475+ for i in 0 ..yc {
476+ ( tmp, carry) = Limb :: ZERO . mac ( y[ i] , Limb ( quo) , carry) ;
477+ ( x[ xi + i + 1 - yc] , borrow) = x[ xi + i + 1 - yc] . sbb ( tmp, borrow) ;
478+ }
479+ ( _, borrow) = x_hi. sbb ( carry, borrow) ;
480+ borrow
481+ } ;
446482
447483 // If the subtraction borrowed, then decrement q and add back the divisor
448484 // The probability of this being needed is very low, about 2/(Limb::MAX+1)
449- let ct_borrow = ConstChoice :: from_word_mask ( borrow. 0 ) ;
450- carry = Limb :: ZERO ;
451- for i in 0 ..yc {
452- ( x[ xi + i + 1 - yc] , carry) =
453- x[ xi + i + 1 - yc] . adc ( Limb :: select ( Limb :: ZERO , y[ i] , ct_borrow) , carry) ;
454- }
455- quo = ct_borrow. select_word ( quo, quo. saturating_sub ( 1 ) ) ;
485+ quo = {
486+ let ct_borrow = ConstChoice :: from_word_mask ( borrow. 0 ) ;
487+ let mut carry = Limb :: ZERO ;
488+ for i in 0 ..yc {
489+ ( x[ xi + i + 1 - yc] , carry) =
490+ x[ xi + i + 1 - yc] . adc ( Limb :: select ( Limb :: ZERO , y[ i] , ct_borrow) , carry) ;
491+ }
492+ ct_borrow. select_word ( quo, quo. wrapping_sub ( 1 ) )
493+ } ;
456494
457495 // Store the quotient within dividend and set x_hi to the current highest word
458496 x_hi = x[ xi] ;
@@ -464,12 +502,7 @@ pub(crate) fn div_rem_vartime_in_place(x: &mut [Limb], y: &mut [Limb]) {
464502 y[ yc - 1 ] = x_hi;
465503
466504 // Unshift the remainder from the earlier adjustment
467- if lshift != 0 {
468- carry = Limb :: ZERO ;
469- for i in ( 0 ..yc) . rev ( ) {
470- ( y[ i] , carry) = ( Limb ( ( y[ i] . 0 >> lshift) | carry. 0 ) , Limb ( y[ i] . 0 << rshift) ) ;
471- }
472- }
505+ shr_limb_vartime ( y, lshift) ;
473506
474507 // Shift the quotient to the low limbs within dividend
475508 // let x_size = xc - yc + 1;
0 commit comments