@@ -4,7 +4,7 @@ use core::ops::{Mul, MulAssign};
44use num_traits:: WrappingMul ;
55use subtle:: CtOption ;
66
7- use crate :: { Checked , CheckedMul , ConcatMixed , ConstChoice , ConstCtOption , Int , Uint } ;
7+ use crate :: { Checked , CheckedMul , ConcatMixed , ConstChoice , ConstCtOption , Int , Uint , Zero } ;
88
99impl < const LIMBS : usize > Int < LIMBS > {
1010 /// Compute "wide" multiplication as a 3-tuple `(lo, hi, negate)`.
@@ -64,41 +64,12 @@ impl<const LIMBS: usize> Int<LIMBS> {
6464 Int :: from_bits ( product_abs. wrapping_neg_if ( product_sign) )
6565 }
6666
67- /// Multiply `self` by `rhs`, returning a `ConstCtOption` which is `is_some` only if
68- /// overflow did not occur.
69- pub const fn checked_mul < const RHS_LIMBS : usize > (
70- & self ,
71- rhs : & Int < RHS_LIMBS > ,
72- ) -> ConstCtOption < Self > {
73- let ( abs_lhs, lhs_sgn) = self . abs_sign ( ) ;
74- let ( abs_rhs, rhs_sgn) = rhs. abs_sign ( ) ;
75- let maybe_res = abs_lhs. checked_mul ( & abs_rhs) ;
76- let ( lo, is_some) = maybe_res. components_ref ( ) ;
77- Self :: new_from_abs_sign ( * lo, lhs_sgn. xor ( rhs_sgn) ) . and_choice ( is_some)
78- }
79-
80- /// Multiply `self` by `rhs`, saturating at the numeric bounds instead of overflowing.
81- pub const fn saturating_mul < const RHS_LIMBS : usize > ( & self , rhs : & Int < RHS_LIMBS > ) -> Self {
82- let ( abs_lhs, lhs_sgn) = self . abs_sign ( ) ;
83- let ( abs_rhs, rhs_sgn) = rhs. abs_sign ( ) ;
84- let maybe_res = abs_lhs. checked_mul ( & abs_rhs) ;
85- let ( lo, is_some) = maybe_res. components_ref ( ) ;
86- let is_neg = lhs_sgn. xor ( rhs_sgn) ;
87- let bound = Self :: select ( & Self :: MAX , & Self :: MIN , is_neg) ;
88- Self :: new_from_abs_sign ( * lo, is_neg)
89- . and_choice ( is_some)
90- . unwrap_or ( bound)
91- }
92-
9367 /// Multiply `self` by `rhs`, wrapping the result in case of overflow.
94- /// This is equivalent to `(self * rhs) % (Uint::<LIMBS>::MAX + 1)`.
9568 pub const fn wrapping_mul < const RHS_LIMBS : usize > ( & self , rhs : & Int < RHS_LIMBS > ) -> Self {
96- if RHS_LIMBS >= LIMBS {
97- Self ( self . 0 . wrapping_mul ( & rhs. 0 ) )
98- } else {
99- let ( abs_rhs, rhs_sgn) = rhs. abs_sign ( ) ;
100- Self ( self . 0 . wrapping_mul ( & abs_rhs) . wrapping_neg_if ( rhs_sgn) )
101- }
69+ let ( abs_lhs, lhs_sgn) = self . abs_sign ( ) ;
70+ let ( abs_rhs, rhs_sgn) = rhs. abs_sign ( ) ;
71+ let ( lo, _) = abs_lhs. widening_mul ( & abs_rhs) ;
72+ * lo. wrapping_neg_if ( lhs_sgn. xor ( rhs_sgn) ) . as_int ( )
10273 }
10374}
10475
@@ -131,7 +102,9 @@ impl<const LIMBS: usize> Int<LIMBS> {
131102impl < const LIMBS : usize , const RHS_LIMBS : usize > CheckedMul < Int < RHS_LIMBS > > for Int < LIMBS > {
132103 #[ inline]
133104 fn checked_mul ( & self , rhs : & Int < RHS_LIMBS > ) -> CtOption < Self > {
134- self . checked_mul ( rhs) . into ( )
105+ let ( lo, hi, is_negative) = self . widening_mul ( rhs) ;
106+ let val = Self :: new_from_abs_sign ( lo, is_negative) ;
107+ CtOption :: from ( val) . and_then ( |int| CtOption :: new ( int, hi. is_zero ( ) ) )
135108 }
136109}
137110
@@ -200,7 +173,7 @@ impl<const LIMBS: usize> MulAssign<&Checked<Int<LIMBS>>> for Checked<Int<LIMBS>>
200173
201174#[ cfg( test) ]
202175mod tests {
203- use crate :: { ConstChoice , I64 , I128 , I256 , Int , U64 , U128 , U256 } ;
176+ use crate :: { CheckedMul , ConstChoice , I64 , I128 , I256 , Int , U128 , U256 } ;
204177
205178 #[ test]
206179 #[ allow( clippy:: init_numbered_fields) ]
@@ -298,26 +271,20 @@ mod tests {
298271 #[ test]
299272 fn test_wrapping_mul ( ) {
300273 // wrapping
301- let a = 0xFFFFFFFB7B63198EF870DF1F90D9BD9Eu128 as i128 ;
302- let b = 0xF20C29FA87B356AA3B4C05C4F9C24B4Au128 as i128 ;
303- let z = 0xAA700D354D6CF4EE881F8FF8093A19ACu128 as i128 ;
304- assert_eq ! ( a. wrapping_mul( b) , z) ;
274+ let a = I128 :: from_be_hex ( "FFFFFFFB7B63198EF870DF1F90D9BD9E" ) ;
275+ let b = I128 :: from_be_hex ( "F20C29FA87B356AA3B4C05C4F9C24B4A" ) ;
305276 assert_eq ! (
306- I128 :: from_i128 ( a ) . wrapping_mul( & I128 :: from_i128 ( b ) ) ,
307- I128 :: from_i128 ( z )
277+ a . wrapping_mul( & b ) ,
278+ I128 :: from_be_hex ( "AA700D354D6CF4EE881F8FF8093A19AC" )
308279 ) ;
309280
310281 // no wrapping
311- let c = -12345i64 ;
282+ let c = I64 :: from_i64 ( -12345i64 ) ;
312283 assert_eq ! (
313- I128 :: from_i128 ( a ) . wrapping_mul( & I128 :: from_i64 ( c ) ) ,
314- I128 :: from_i128 ( a . wrapping_mul ( c as i128 ) )
284+ a . wrapping_mul( & c ) ,
285+ I128 :: from_be_hex ( "0000D9DEF2248095850866CFEBF727D2" )
315286 ) ;
316287
317- // overflow into MSB
318- let a = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFu128 as i128 ;
319- assert ! ( !a. is_negative( ) && a. wrapping_mul( a) . is_negative( ) ) ;
320-
321288 // core case
322289 assert_eq ! ( i8 :: MAX . wrapping_mul( 2 ) , -2 ) ;
323290 assert_eq ! ( i64 :: MAX . wrapping_mul( 2 ) , -2 ) ;
@@ -345,88 +312,6 @@ mod tests {
345312 ) ;
346313 }
347314
348- #[ test]
349- fn test_wrapping_mul_mixed ( ) {
350- let a = U64 :: from_u64 ( 0x0011223344556677 ) ;
351- let b = U128 :: from_u128 ( 0x8899aabbccddeeff_8899aabbccddeeff ) ;
352- let expected = a. as_int ( ) . concatenating_mul ( b. as_int ( ) ) ;
353- assert_eq ! ( a. as_int( ) . wrapping_mul( b. as_int( ) ) , expected. resize( ) ) ;
354- assert_eq ! ( b. as_int( ) . wrapping_mul( a. as_int( ) ) , expected. resize( ) ) ;
355- assert_eq ! (
356- a. as_int( ) . wrapping_neg( ) . wrapping_mul( b. as_int( ) ) ,
357- expected. wrapping_neg( ) . resize( )
358- ) ;
359- assert_eq ! (
360- a. as_int( ) . wrapping_mul( & b. as_int( ) . wrapping_neg( ) ) ,
361- expected. wrapping_neg( ) . resize( )
362- ) ;
363- assert_eq ! (
364- b. as_int( ) . wrapping_neg( ) . wrapping_mul( a. as_int( ) ) ,
365- expected. wrapping_neg( ) . resize( )
366- ) ;
367- assert_eq ! (
368- b. as_int( ) . wrapping_mul( & a. as_int( ) . wrapping_neg( ) ) ,
369- expected. wrapping_neg( ) . resize( )
370- ) ;
371- assert_eq ! (
372- a. as_int( )
373- . wrapping_neg( )
374- . wrapping_mul( & b. as_int( ) . wrapping_neg( ) ) ,
375- expected. resize( )
376- ) ;
377- assert_eq ! (
378- b. as_int( )
379- . wrapping_neg( )
380- . wrapping_mul( & a. as_int( ) . wrapping_neg( ) ) ,
381- expected. resize( )
382- ) ;
383- }
384-
385- #[ test]
386- fn test_saturating_mul ( ) {
387- // wrapping
388- let a = 0xFFFFFFFB7B63198EF870DF1F90D9BD9Eu128 as i128 ;
389- let b = 0xF20C29FA87B356AA3B4C05C4F9C24B4Au128 as i128 ;
390- assert_eq ! ( a. saturating_mul( b) , i128 :: MAX ) ;
391- assert_eq ! (
392- I128 :: from_i128( a) . saturating_mul( & I128 :: from_i128( b) ) ,
393- I128 :: MAX
394- ) ;
395-
396- // no wrapping
397- let c = -12345i64 ;
398- assert_eq ! (
399- I128 :: from_i128( a) . saturating_mul( & I128 :: from_i64( c) ) ,
400- I128 :: from_i128( a. saturating_mul( c as i128 ) )
401- ) ;
402-
403- // core case
404- assert_eq ! ( i8 :: MAX . saturating_mul( 2 ) , i8 :: MAX ) ;
405- assert_eq ! ( i8 :: MAX . saturating_mul( -2 ) , i8 :: MIN ) ;
406- assert_eq ! ( i64 :: MAX . saturating_mul( 2 ) , i64 :: MAX ) ;
407- assert_eq ! ( i64 :: MAX . saturating_mul( -2 ) , i64 :: MIN ) ;
408- assert_eq ! ( I128 :: MAX . saturating_mul( & I128 :: from_i64( 2i64 ) ) , I128 :: MAX ) ;
409- assert_eq ! ( I128 :: MAX . saturating_mul( & I128 :: from_i64( -2i64 ) ) , I128 :: MIN ) ;
410-
411- let x = -197044252290277702i64 ;
412- let y = -2631691865753118366 ;
413- assert_eq ! ( x. saturating_mul( y) , i64 :: MAX ) ;
414- assert_eq ! ( I64 :: from_i64( x) . saturating_mul( & I64 :: from_i64( y) ) , I64 :: MAX ) ;
415-
416- let x = -86027672844719838068326470675019902915i128 ;
417- let y = 21188806580823612823777395451044967239i128 ;
418- assert_eq ! ( x. saturating_mul( y) , i128 :: MIN ) ;
419- assert_eq ! ( x. saturating_mul( -y) , i128 :: MAX ) ;
420- assert_eq ! (
421- I128 :: from_i128( x) . saturating_mul( & I128 :: from_i128( y) ) ,
422- I128 :: MIN
423- ) ;
424- assert_eq ! (
425- I128 :: from_i128( x) . saturating_mul( & I128 :: from_i128( -y) ) ,
426- I128 :: MAX
427- ) ;
428- }
429-
430315 #[ test]
431316 fn test_concatenating_mul ( ) {
432317 assert_eq ! (
0 commit comments