@@ -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 , Zero } ;
7+ use crate :: { Checked , CheckedMul , ConcatMixed , ConstChoice , ConstCtOption , Int , Uint } ;
88
99impl < const LIMBS : usize > Int < LIMBS > {
1010 /// Compute "wide" multiplication as a 3-tuple `(lo, hi, negate)`.
@@ -64,12 +64,26 @@ 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+
6780 /// Multiply `self` by `rhs`, wrapping the result in case of overflow.
81+ /// This is equivalent to `(self * rhs) % (Uint::<LIMBS>::MAX + 1)`.
6882 pub const fn wrapping_mul < const RHS_LIMBS : usize > ( & self , rhs : & Int < RHS_LIMBS > ) -> Self {
6983 let ( abs_lhs, lhs_sgn) = self . abs_sign ( ) ;
7084 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 ( )
85+ let lo = Self ( abs_lhs. wrapping_mul ( & abs_rhs) ) ;
86+ lo. wrapping_neg_if ( lhs_sgn. xor ( rhs_sgn) )
7387 }
7488}
7589
@@ -102,9 +116,7 @@ impl<const LIMBS: usize> Int<LIMBS> {
102116impl < const LIMBS : usize , const RHS_LIMBS : usize > CheckedMul < Int < RHS_LIMBS > > for Int < LIMBS > {
103117 #[ inline]
104118 fn checked_mul ( & self , rhs : & Int < RHS_LIMBS > ) -> CtOption < Self > {
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 ( ) ) )
119+ self . checked_mul ( rhs) . into ( )
108120 }
109121}
110122
@@ -173,7 +185,7 @@ impl<const LIMBS: usize> MulAssign<&Checked<Int<LIMBS>>> for Checked<Int<LIMBS>>
173185
174186#[ cfg( test) ]
175187mod tests {
176- use crate :: { CheckedMul , ConstChoice , I64 , I128 , I256 , Int , U128 , U256 } ;
188+ use crate :: { ConstChoice , I64 , I128 , I256 , Int , U128 , U256 } ;
177189
178190 #[ test]
179191 #[ allow( clippy:: init_numbered_fields) ]
@@ -271,20 +283,26 @@ mod tests {
271283 #[ test]
272284 fn test_wrapping_mul ( ) {
273285 // wrapping
274- let a = I128 :: from_be_hex ( "FFFFFFFB7B63198EF870DF1F90D9BD9E" ) ;
275- let b = I128 :: from_be_hex ( "F20C29FA87B356AA3B4C05C4F9C24B4A" ) ;
286+ let a = 0xFFFFFFFB7B63198EF870DF1F90D9BD9Eu128 as i128 ;
287+ let b = 0xF20C29FA87B356AA3B4C05C4F9C24B4Au128 as i128 ;
288+ let z = 0xAA700D354D6CF4EE881F8FF8093A19ACu128 as i128 ;
289+ assert_eq ! ( a. wrapping_mul( b) , z) ;
276290 assert_eq ! (
277- a . wrapping_mul( & b ) ,
278- I128 :: from_be_hex ( "AA700D354D6CF4EE881F8FF8093A19AC" )
291+ I128 :: from_i128 ( a ) . wrapping_mul( & I128 :: from_i128 ( b ) ) ,
292+ I128 :: from_i128 ( z )
279293 ) ;
280294
281295 // no wrapping
282- let c = I64 :: from_i64 ( -12345i64 ) ;
296+ let c = -12345i64 ;
283297 assert_eq ! (
284- a . wrapping_mul( & c ) ,
285- I128 :: from_be_hex ( "0000D9DEF2248095850866CFEBF727D2" )
298+ I128 :: from_i128 ( a ) . wrapping_mul( & I128 :: from_i64 ( c ) ) ,
299+ I128 :: from_i128 ( a . wrapping_mul ( c as i128 ) )
286300 ) ;
287301
302+ // overflow into MSB
303+ let a = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFu128 as i128 ;
304+ assert ! ( !a. is_negative( ) && a. wrapping_mul( a) . is_negative( ) ) ;
305+
288306 // core case
289307 assert_eq ! ( i8 :: MAX . wrapping_mul( 2 ) , -2 ) ;
290308 assert_eq ! ( i64 :: MAX . wrapping_mul( 2 ) , -2 ) ;
0 commit comments