Skip to main content

crypto_bigint/int/
neg.rs

1//! [`Int`] negation-related operations.
2
3use crate::{Choice, CtOption, Int, Uint, WrappingNeg};
4
5impl<const LIMBS: usize> Int<LIMBS> {
6    /// Map this [`Int`] to its two's-complement negation:
7    /// map `self` to `(self ^ 1111...1111) + 0000...0001`.
8    ///
9    /// Returns the negation, as well as whether the operation overflowed.
10    /// The operation overflows when attempting to negate [`Int::MIN`]; the positive counterpart
11    /// of this value cannot be represented.
12    #[must_use]
13    pub const fn overflowing_neg(&self) -> (Self, Choice) {
14        Self(self.0.bitxor(&Uint::MAX)).overflowing_add(&Int::ONE)
15    }
16
17    /// Wrapping negate this [`Int`].
18    ///
19    /// Warning: this operation maps [`Int::MIN`] to itself, since the positive counterpart of this
20    /// value cannot be represented.
21    #[must_use]
22    pub const fn wrapping_neg(&self) -> Self {
23        self.overflowing_neg().0
24    }
25
26    /// Wrapping negate this [`Int`] if `negate` is truthy; otherwise do nothing.
27    ///
28    /// Warning: this operation maps [`Int::MIN`] to itself, since the positive counterpart of this
29    /// value cannot be represented.
30    #[must_use]
31    pub const fn wrapping_neg_if(&self, negate: Choice) -> Int<LIMBS> {
32        Self(self.0.wrapping_neg_if(negate))
33    }
34
35    /// Negate this [`Int`].
36    ///
37    /// Yields `None` when `self == Self::MIN`, since the positive counterpart of this value cannot
38    /// be represented.
39    #[must_use]
40    pub const fn checked_neg(&self) -> CtOption<Self> {
41        let (value, overflow) = self.overflowing_neg();
42        CtOption::new(value, overflow.not())
43    }
44}
45
46impl<const LIMBS: usize> WrappingNeg for Int<LIMBS> {
47    #[inline]
48    fn wrapping_neg(&self) -> Self {
49        self.wrapping_neg()
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use crate::{Choice, I128};
56
57    #[test]
58    fn overflowing_neg() {
59        let min_plus_one = I128 {
60            0: I128::MIN.0.wrapping_add(&I128::ONE.0),
61        };
62
63        let (res, overflow) = I128::MIN.overflowing_neg();
64        assert_eq!(res, I128::MIN);
65        assert!(overflow.to_bool());
66
67        let (res, overflow) = I128::MINUS_ONE.overflowing_neg();
68        assert_eq!(res, I128::ONE);
69        assert!(!overflow.to_bool());
70
71        let (res, overflow) = I128::ZERO.overflowing_neg();
72        assert_eq!(res, I128::ZERO);
73        assert!(!overflow.to_bool());
74
75        let (res, overflow) = I128::ONE.overflowing_neg();
76        assert_eq!(res, I128::MINUS_ONE);
77        assert!(!overflow.to_bool());
78
79        let (res, overflow) = I128::MAX.overflowing_neg();
80        assert_eq!(res, min_plus_one);
81        assert!(!overflow.to_bool());
82    }
83
84    #[test]
85    fn wrapping_neg_if() {
86        let min_plus_one = I128 {
87            0: I128::MIN.0.wrapping_add(&I128::ONE.0),
88        };
89
90        let do_negate = Choice::TRUE;
91        assert_eq!(I128::MIN.wrapping_neg_if(do_negate), I128::MIN);
92        assert_eq!(I128::MINUS_ONE.wrapping_neg_if(do_negate), I128::ONE);
93        assert_eq!(I128::ZERO.wrapping_neg_if(do_negate), I128::ZERO);
94        assert_eq!(I128::ONE.wrapping_neg_if(do_negate), I128::MINUS_ONE);
95        assert_eq!(I128::MAX.wrapping_neg_if(do_negate), min_plus_one);
96
97        let do_not_negate = Choice::FALSE;
98        assert_eq!(I128::MIN.wrapping_neg_if(do_not_negate), I128::MIN);
99        assert_eq!(
100            I128::MINUS_ONE.wrapping_neg_if(do_not_negate),
101            I128::MINUS_ONE
102        );
103        assert_eq!(I128::ZERO.wrapping_neg_if(do_not_negate), I128::ZERO);
104        assert_eq!(I128::ONE.wrapping_neg_if(do_not_negate), I128::ONE);
105        assert_eq!(I128::MAX.wrapping_neg_if(do_not_negate), I128::MAX);
106    }
107
108    #[test]
109    fn checked_neg() {
110        assert!(I128::MIN.checked_neg().is_none().to_bool());
111        assert_eq!(I128::MINUS_ONE.checked_neg().unwrap(), I128::ONE);
112        assert_eq!(I128::ZERO.checked_neg().unwrap(), I128::ZERO);
113        assert_eq!(I128::ONE.checked_neg().unwrap(), I128::MINUS_ONE);
114        assert_eq!(
115            I128::MAX.checked_neg().unwrap(),
116            I128::from_be_hex("80000000000000000000000000000001")
117        );
118
119        let negative = I128::from_be_hex("91113333555577779999BBBBDDDDFFFF");
120        let positive = I128::from_be_hex("6EEECCCCAAAA88886666444422220001");
121        assert_eq!(negative.checked_neg().unwrap(), positive);
122        assert_eq!(positive.checked_neg().unwrap(), negative);
123        assert_eq!(
124            positive.checked_neg().unwrap().checked_neg().unwrap(),
125            positive
126        );
127    }
128}