crypto_bigint/int/
cmp.rs

1//! [`Int`] comparisons.
2//!
3//! By default, these are all constant-time.
4
5use core::cmp::Ordering;
6
7use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};
8
9use crate::{ConstChoice, Int, Uint};
10
11impl<const LIMBS: usize> Int<LIMBS> {
12    /// Return `b` if `c` is truthy, otherwise return `a`.
13    #[inline]
14    pub(crate) const fn select(a: &Self, b: &Self, c: ConstChoice) -> Self {
15        Self(Uint::select(&a.0, &b.0, c))
16    }
17
18    /// Returns the truthy value if `self`!=0 or the falsy value otherwise.
19    #[inline]
20    pub(crate) const fn is_nonzero(&self) -> ConstChoice {
21        Uint::is_nonzero(&self.0)
22    }
23
24    /// Returns the truthy value if `self == rhs` or the falsy value otherwise.
25    #[inline]
26    pub(crate) const fn eq(lhs: &Self, rhs: &Self) -> ConstChoice {
27        Uint::eq(&lhs.0, &rhs.0)
28    }
29
30    /// Returns the truthy value if `self < rhs` and the falsy value otherwise.
31    #[inline]
32    pub(crate) const fn lt(lhs: &Self, rhs: &Self) -> ConstChoice {
33        Uint::lt(&lhs.invert_msb().0, &rhs.invert_msb().0)
34    }
35
36    /// Returns the truthy value if `self > rhs` and the falsy value otherwise.
37    #[inline]
38    pub(crate) const fn gt(lhs: &Self, rhs: &Self) -> ConstChoice {
39        Uint::gt(&lhs.invert_msb().0, &rhs.invert_msb().0)
40    }
41
42    /// Returns the ordering between `self` and `rhs` as an i8.
43    /// Values correspond to the Ordering enum:
44    ///   -1 is Less
45    ///   0 is Equal
46    ///   1 is Greater
47    #[inline]
48    pub(crate) const fn cmp(lhs: &Self, rhs: &Self) -> i8 {
49        Uint::cmp(&lhs.invert_msb().0, &rhs.invert_msb().0)
50    }
51
52    /// Returns the Ordering between `self` and `rhs` in variable time.
53    pub const fn cmp_vartime(&self, rhs: &Self) -> Ordering {
54        self.invert_msb().0.cmp_vartime(&rhs.invert_msb().0)
55    }
56}
57
58impl<const LIMBS: usize> ConstantTimeEq for Int<LIMBS> {
59    #[inline]
60    fn ct_eq(&self, other: &Self) -> Choice {
61        Int::eq(self, other).into()
62    }
63}
64
65impl<const LIMBS: usize> ConstantTimeGreater for Int<LIMBS> {
66    #[inline]
67    fn ct_gt(&self, other: &Self) -> Choice {
68        Int::gt(self, other).into()
69    }
70}
71
72impl<const LIMBS: usize> ConstantTimeLess for Int<LIMBS> {
73    #[inline]
74    fn ct_lt(&self, other: &Self) -> Choice {
75        Int::lt(self, other).into()
76    }
77}
78
79impl<const LIMBS: usize> Eq for Int<LIMBS> {}
80
81impl<const LIMBS: usize> Ord for Int<LIMBS> {
82    fn cmp(&self, other: &Self) -> Ordering {
83        let c = Self::cmp(self, other);
84        match c {
85            -1 => Ordering::Less,
86            0 => Ordering::Equal,
87            _ => Ordering::Greater,
88        }
89    }
90}
91
92impl<const LIMBS: usize> PartialOrd for Int<LIMBS> {
93    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
94        Some(self.cmp(other))
95    }
96}
97
98impl<const LIMBS: usize> PartialEq for Int<LIMBS> {
99    fn eq(&self, other: &Self) -> bool {
100        self.ct_eq(other).into()
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use subtle::{ConstantTimeGreater, ConstantTimeLess};
107
108    use crate::I128;
109
110    #[test]
111    fn test_is_nonzero() {
112        assert_eq!(I128::MAX.is_nonzero().to_u8(), 1u8);
113        assert_eq!(I128::ONE.is_nonzero().to_u8(), 1u8);
114        assert_eq!(I128::ZERO.is_nonzero().to_u8(), 0u8);
115        assert_eq!(I128::MINUS_ONE.is_nonzero().to_u8(), 1u8);
116        assert_eq!(I128::MAX.is_nonzero().to_u8(), 1u8);
117    }
118
119    #[test]
120    fn test_eq() {
121        assert_eq!(I128::ZERO, I128::ONE.wrapping_add(&I128::MINUS_ONE));
122        assert_eq!(I128::ONE, I128::ZERO.wrapping_add(&I128::ONE));
123        assert_ne!(I128::ONE, I128::ZERO);
124    }
125
126    #[test]
127    fn test_gt() {
128        // x > y
129        assert!(I128::MAX > I128::ONE);
130        assert!(I128::ONE > I128::ZERO);
131        assert!(I128::ZERO > I128::MINUS_ONE);
132        assert!(I128::MINUS_ONE > I128::MIN);
133        assert!(I128::MAX > I128::MIN);
134
135        // a !> a
136        assert_ne!(true, I128::MAX > I128::MAX);
137        assert_ne!(true, I128::ONE > I128::ONE);
138        assert_ne!(true, I128::ZERO > I128::ZERO);
139        assert_ne!(true, I128::MINUS_ONE > I128::MINUS_ONE);
140        assert_ne!(true, I128::MIN > I128::MIN);
141    }
142
143    #[test]
144    fn test_lt() {
145        // x < y
146        assert!(I128::ONE < I128::MAX);
147        assert!(I128::ZERO < I128::ONE);
148        assert!(I128::MINUS_ONE < I128::ZERO);
149        assert!(I128::MIN < I128::MINUS_ONE);
150        assert!(I128::MIN < I128::MAX);
151
152        // a !< a
153        assert_ne!(true, I128::MAX < I128::MAX);
154        assert_ne!(true, I128::ONE < I128::ONE);
155        assert_ne!(true, I128::ZERO < I128::ZERO);
156        assert_ne!(true, I128::MINUS_ONE < I128::MINUS_ONE);
157        assert_ne!(true, I128::MIN < I128::MIN);
158    }
159
160    #[test]
161    fn ct_gt() {
162        let a = I128::MIN;
163        let b = I128::ZERO;
164        let c = I128::MAX;
165
166        assert!(bool::from(b.ct_gt(&a)));
167        assert!(bool::from(c.ct_gt(&a)));
168        assert!(bool::from(c.ct_gt(&b)));
169
170        assert!(!bool::from(a.ct_gt(&a)));
171        assert!(!bool::from(b.ct_gt(&b)));
172        assert!(!bool::from(c.ct_gt(&c)));
173
174        assert!(!bool::from(a.ct_gt(&b)));
175        assert!(!bool::from(a.ct_gt(&c)));
176        assert!(!bool::from(b.ct_gt(&c)));
177    }
178
179    #[test]
180    fn ct_lt() {
181        let a = I128::ZERO;
182        let b = I128::ONE;
183        let c = I128::MAX;
184
185        assert!(bool::from(a.ct_lt(&b)));
186        assert!(bool::from(a.ct_lt(&c)));
187        assert!(bool::from(b.ct_lt(&c)));
188
189        assert!(!bool::from(a.ct_lt(&a)));
190        assert!(!bool::from(b.ct_lt(&b)));
191        assert!(!bool::from(c.ct_lt(&c)));
192
193        assert!(!bool::from(b.ct_lt(&a)));
194        assert!(!bool::from(c.ct_lt(&a)));
195        assert!(!bool::from(c.ct_lt(&b)));
196    }
197}