crypto_bigint/int/
sign.rs

1use crate::{ConstChoice, ConstCtOption, Int, Uint, Word};
2use num_traits::ConstZero;
3
4impl<const LIMBS: usize> Int<LIMBS> {
5    /// Returns the word of most significant [`Limb`].
6    /// For the generative case where the number of limbs is zero,
7    /// zeroed word is returned (which is semantically correct).
8    /// This method leaks the limb length of the value, which is also OK.
9    const fn most_significant_word(&self) -> Word {
10        if Self::LIMBS == 0 {
11            Word::ZERO
12        } else {
13            self.0.to_words()[LIMBS - 1]
14        }
15    }
16
17    /// Construct new [`Int`] from an absolute value and sign.
18    /// Returns `None` when the absolute value does not fit in an [`Int<LIMBS>`].
19    pub const fn new_from_abs_sign(
20        abs: Uint<LIMBS>,
21        is_negative: ConstChoice,
22    ) -> ConstCtOption<Self> {
23        let magnitude = Self(abs).wrapping_neg_if(is_negative);
24        let fits = Uint::lte(&abs, &Int::MAX.0).or(is_negative.and(Uint::eq(&abs, &Int::MIN.0)));
25        ConstCtOption::new(magnitude, fits)
26    }
27
28    /// Whether this [`Int`] is negative, as a `ConstChoice`.
29    pub const fn is_negative(&self) -> ConstChoice {
30        ConstChoice::from_word_msb(self.most_significant_word())
31    }
32
33    /// Whether this [`Int`] is positive, as a `ConstChoice`.
34    pub const fn is_positive(&self) -> ConstChoice {
35        self.is_negative().not().and(self.is_nonzero())
36    }
37
38    /// The sign and magnitude of this [`Int`].
39    pub const fn abs_sign(&self) -> (Uint<LIMBS>, ConstChoice) {
40        let sign = self.is_negative();
41        // Note: this negate_if is safe to use, since we are negating based on self.is_negative()
42        let abs = self.wrapping_neg_if(sign);
43        (abs.0, sign)
44    }
45
46    /// The magnitude of this [`Int`].
47    pub const fn abs(&self) -> Uint<LIMBS> {
48        self.abs_sign().0
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55    use crate::I128;
56
57    #[test]
58    fn is_negative() {
59        assert_eq!(I128::MIN.is_negative(), ConstChoice::TRUE);
60        assert_eq!(I128::MINUS_ONE.is_negative(), ConstChoice::TRUE);
61        assert_eq!(I128::ZERO.is_negative(), ConstChoice::FALSE);
62        assert_eq!(I128::ONE.is_negative(), ConstChoice::FALSE);
63        assert_eq!(I128::MAX.is_negative(), ConstChoice::FALSE);
64
65        let random_negative = I128::from_be_hex("91113333555577779999BBBBDDDDFFFF");
66        assert_eq!(random_negative.is_negative(), ConstChoice::TRUE);
67
68        let random_positive = I128::from_be_hex("71113333555577779999BBBBDDDDFFFF");
69        assert_eq!(random_positive.is_negative(), ConstChoice::FALSE);
70    }
71
72    #[test]
73    fn is_positive() {
74        assert_eq!(I128::MIN.is_positive(), ConstChoice::FALSE);
75        assert_eq!(I128::MINUS_ONE.is_positive(), ConstChoice::FALSE);
76        assert_eq!(I128::ZERO.is_positive(), ConstChoice::FALSE);
77        assert_eq!(I128::ONE.is_positive(), ConstChoice::TRUE);
78        assert_eq!(I128::MAX.is_positive(), ConstChoice::TRUE);
79
80        let random_negative = I128::from_be_hex("deadbeefcafebabedeadbeefcafebabe");
81        assert_eq!(random_negative.is_positive(), ConstChoice::FALSE);
82
83        let random_positive = I128::from_be_hex("0badc0dedeadc0decafebabedeadcafe");
84        assert_eq!(random_positive.is_positive(), ConstChoice::TRUE);
85    }
86}