Skip to main content

crypto_bigint/uint/
mul_signed.rs

1use crate::{Choice, Concat, CtOption, Int, Uint};
2
3impl<const LIMBS: usize> Uint<LIMBS> {
4    /// Compute "wide" multiplication between an [`Uint`] and [`Int`] as 3-tuple `(lo, hi, negate)`.
5    /// The `(lo, hi)` components contain the _magnitude of the product_, with sizes
6    /// corresponding to the sizes of the operands; `negate` indicates whether the result should be
7    /// negated when converted from [`Uint`] to [`Int`].
8    ///
9    /// Note: even if `negate` is truthy, the magnitude might be zero!
10    #[must_use]
11    pub const fn widening_mul_signed<const RHS_LIMBS: usize>(
12        &self,
13        rhs: &Int<RHS_LIMBS>,
14    ) -> (Uint<LIMBS>, Uint<RHS_LIMBS>, Choice) {
15        let (rhs_abs, rhs_sgn) = rhs.abs_sign();
16        let (lo, hi) = self.widening_mul(&rhs_abs);
17        (lo, hi, rhs_sgn)
18    }
19
20    /// Multiply `self` by [`Int`] `rhs`, returning a concatenated "wide" result.
21    #[must_use]
22    pub const fn concatenating_mul_signed<const RHS_LIMBS: usize, const WIDE_LIMBS: usize>(
23        &self,
24        rhs: &Int<RHS_LIMBS>,
25    ) -> Int<WIDE_LIMBS>
26    where
27        Uint<LIMBS>: Concat<RHS_LIMBS, Output = Uint<WIDE_LIMBS>>,
28    {
29        let (rhs_abs, rhs_sign) = rhs.abs_sign();
30        let product_abs = self.concatenating_mul(&rhs_abs);
31
32        // always fits
33        *product_abs.wrapping_neg_if(rhs_sign).as_int()
34    }
35
36    /// Checked multiplication of `self` with [`Int`] `rhs`.
37    #[must_use]
38    pub fn checked_mul_signed<const RHS_LIMBS: usize>(
39        &self,
40        rhs: &Int<RHS_LIMBS>,
41    ) -> CtOption<Int<LIMBS>> {
42        let (abs_rhs, rhs_sgn) = rhs.abs_sign();
43        let maybe_res = self.checked_mul(&abs_rhs);
44        Int::new_from_abs_opt_sign(maybe_res, rhs_sgn)
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use crate::{I64, I128, I256, U64, U128};
51
52    #[test]
53    fn widening_mul_signed() {
54        let (lo, hi, rhs_sgn) = U128::MAX.widening_mul_signed(&I64::from_i64(-55));
55        assert_eq!(lo, U128::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC9"));
56        assert_eq!(hi, U64::from_u64(54));
57        assert!(rhs_sgn.to_bool());
58    }
59
60    #[test]
61    fn concatenating_mul_signed() {
62        assert_eq!(
63            U128::MAX.concatenating_mul_signed(&I128::from_i64(-55)),
64            I256::from_be_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC900000000000000000000000000000037")
65        );
66        assert_eq!(
67            U128::MAX.concatenating_mul_signed(&I128::MAX),
68            I256::from_be_hex("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE80000000000000000000000000000001")
69        );
70        assert_eq!(
71            U128::MAX.concatenating_mul_signed(&I128::MIN),
72            I256::from_be_hex("8000000000000000000000000000000080000000000000000000000000000000")
73        );
74    }
75
76    #[test]
77    fn checked_mul_signed() {
78        assert_eq!(
79            U64::from_be_hex("00000000FFFFFFFF")
80                .checked_mul_signed(&I64::from_be_hex("FFFFFFFF80000000"))
81                .unwrap(),
82            I64::from_be_hex("8000000080000000")
83        );
84        assert!(bool::from(
85            U64::MAX.checked_mul_signed(&I128::ONE).is_none()
86        ));
87    }
88}