Skip to main content

crypto_bigint/int/
sign.rs

1use crate::{Choice, ConstZero, CtOption, Int, Uint, Word, word};
2
3impl<const LIMBS: usize> Int<LIMBS> {
4    /// Returns the word of most significant [`Limb`].
5    ///
6    /// For the degenerate 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    #[inline(always)]
10    const fn most_significant_word(&self) -> Word {
11        if Self::LIMBS == 0 {
12            Word::ZERO
13        } else {
14            self.0.to_words()[LIMBS - 1]
15        }
16    }
17
18    /// Construct new [`Int`] from an absolute value and sign.
19    ///
20    /// Returns `None` when the result exceeds the bounds of an [`Int<LIMBS>`].
21    #[inline]
22    #[must_use]
23    pub const fn new_from_abs_sign(abs: Uint<LIMBS>, is_negative: Choice) -> CtOption<Self> {
24        let abs_int = abs.as_int();
25        let abs_msb = abs_int.is_negative();
26        let signed = abs_int.wrapping_neg_if(is_negative);
27
28        // abs is an acceptable input if the high bit is unset, covering 0..=Int::MAX,
29        // or if it is equal to Int::MIN (bit sequence '1000...0000') and the sign is negative.
30        // Int::MIN and zero are the only values for which wrapping negation does not change
31        // the MSB, so we check if the sign is negative (wrapping negation is performed) and
32        // the sign of the wrapping negation is also negative.
33        let fits = abs_msb.not().or(is_negative.and(signed.is_negative()));
34        CtOption::new(signed, fits)
35    }
36
37    /// Construct a new [`Int`] from an [`CtOption`] of an absolute value and sign.
38    #[inline]
39    pub(crate) const fn new_from_abs_opt_sign(
40        maybe_abs: CtOption<Uint<LIMBS>>,
41        is_negative: Choice,
42    ) -> CtOption<Self> {
43        Self::new_from_abs_sign(maybe_abs.to_inner_unchecked(), is_negative)
44            .filter_by(maybe_abs.is_some())
45    }
46
47    /// Whether this [`Int`] is negative, as a `Choice`.
48    #[inline(always)]
49    #[must_use]
50    pub const fn is_negative(&self) -> Choice {
51        word::choice_from_msb(self.most_significant_word())
52    }
53
54    /// Whether this [`Int`] is positive, as a `Choice`.
55    #[must_use]
56    pub const fn is_positive(&self) -> Choice {
57        self.is_negative().not().and(self.is_nonzero())
58    }
59
60    /// The sign and magnitude of this [`Int`].
61    #[must_use]
62    pub const fn abs_sign(&self) -> (Uint<LIMBS>, Choice) {
63        let sign = self.is_negative();
64        // Note: this negate_if is safe to use, since we are negating based on self.is_negative()
65        let abs = self.wrapping_neg_if(sign);
66        (abs.0, sign)
67    }
68
69    /// The magnitude of this [`Int`].
70    #[must_use]
71    pub const fn abs(&self) -> Uint<LIMBS> {
72        self.abs_sign().0
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use crate::{I128, U128};
80
81    #[test]
82    fn new_from_abs_sign() {
83        assert!(
84            I128::new_from_abs_sign(U128::ZERO, Choice::FALSE)
85                .is_some()
86                .to_bool()
87        );
88        assert!(
89            I128::new_from_abs_sign(U128::ZERO, Choice::TRUE)
90                .is_some()
91                .to_bool()
92        );
93        assert!(
94            I128::new_from_abs_sign(I128::MIN.abs(), Choice::FALSE)
95                .is_none()
96                .to_bool()
97        );
98        assert!(
99            I128::new_from_abs_sign(I128::MIN.abs(), Choice::TRUE)
100                .is_some()
101                .to_bool()
102        );
103        assert!(
104            I128::new_from_abs_sign(I128::MAX.abs(), Choice::FALSE)
105                .is_some()
106                .to_bool()
107        );
108        assert!(
109            I128::new_from_abs_sign(I128::MAX.abs(), Choice::TRUE)
110                .is_some()
111                .to_bool()
112        );
113        assert!(
114            I128::new_from_abs_sign(U128::MAX, Choice::TRUE)
115                .is_none()
116                .to_bool()
117        );
118    }
119
120    #[test]
121    fn is_negative() {
122        assert!(I128::MIN.is_negative().to_bool());
123        assert!(I128::MINUS_ONE.is_negative().to_bool());
124        assert!(!I128::ZERO.is_negative().to_bool());
125        assert!(!I128::ONE.is_negative().to_bool());
126        assert!(!I128::MAX.is_negative().to_bool());
127
128        let random_negative = I128::from_be_hex("91113333555577779999BBBBDDDDFFFF");
129        assert!(random_negative.is_negative().to_bool());
130
131        let random_positive = I128::from_be_hex("71113333555577779999BBBBDDDDFFFF");
132        assert!(!random_positive.is_negative().to_bool());
133    }
134
135    #[test]
136    fn is_positive() {
137        assert!(!I128::MIN.is_positive().to_bool());
138        assert!(!I128::MINUS_ONE.is_positive().to_bool());
139        assert!(!I128::ZERO.is_positive().to_bool());
140        assert!(I128::ONE.is_positive().to_bool());
141        assert!(I128::MAX.is_positive().to_bool());
142
143        let random_negative = I128::from_be_hex("deadbeefcafebabedeadbeefcafebabe");
144        assert!(!random_negative.is_positive().to_bool());
145
146        let random_positive = I128::from_be_hex("0badc0dedeadc0decafebabedeadcafe");
147        assert!(random_positive.is_positive().to_bool());
148    }
149}