Skip to main content

crypto_bigint/uint/
cmp.rs

1//! [`Uint`] comparison operations.
2//!
3//! Constant-time unless explicitly noted otherwise.
4
5use core::cmp::Ordering;
6
7use super::Uint;
8use crate::{Choice, CtEq, Limb, UintRef};
9
10impl<const LIMBS: usize> Uint<LIMBS> {
11    /// Returns [`Choice::TRUE`] if `self` != `0` or [`Choice::FALSE`] otherwise.
12    #[inline]
13    #[must_use]
14    pub const fn is_nonzero(&self) -> Choice {
15        self.as_uint_ref().is_nonzero()
16    }
17
18    /// Determine in variable time whether the `self` is zero.
19    #[inline]
20    #[must_use]
21    pub const fn is_zero_vartime(&self) -> bool {
22        self.as_uint_ref().is_zero_vartime()
23    }
24
25    /// Returns the truthy value if `self` is odd or the falsy value otherwise.
26    #[inline]
27    #[must_use]
28    pub const fn is_odd(&self) -> Choice {
29        self.as_uint_ref().is_odd()
30    }
31
32    /// Returns the truthy value if `self == rhs` or the falsy value otherwise.
33    #[inline]
34    pub(crate) const fn eq(lhs: &Self, rhs: &Self) -> Choice {
35        let mut acc = 0;
36        let mut i = 0;
37
38        while i < LIMBS {
39            acc |= lhs.limbs[i].0 ^ rhs.limbs[i].0;
40            i += 1;
41        }
42
43        // acc == 0 if and only if self == rhs
44        Limb(acc).is_nonzero().not()
45    }
46
47    /// Returns the truthy value if `self < rhs` and the falsy value otherwise.
48    #[inline]
49    pub(crate) const fn lt(lhs: &Self, rhs: &Self) -> Choice {
50        UintRef::lt(lhs.as_uint_ref(), rhs.as_uint_ref())
51    }
52
53    /// Returns the truthy value if `self <= rhs` and the falsy value otherwise.
54    #[inline]
55    pub(crate) const fn lte(lhs: &Self, rhs: &Self) -> Choice {
56        Self::gt(lhs, rhs).not()
57    }
58
59    /// Returns the truthy value if `self > rhs` and the falsy value otherwise.
60    #[inline]
61    pub(crate) const fn gt(lhs: &Self, rhs: &Self) -> Choice {
62        UintRef::lt(rhs.as_uint_ref(), lhs.as_uint_ref())
63    }
64
65    /// Returns the Ordering between `self` and `rhs`.
66    #[inline]
67    pub(crate) const fn cmp(lhs: &Self, rhs: &Self) -> Ordering {
68        UintRef::cmp(lhs.as_uint_ref(), rhs.as_uint_ref())
69    }
70
71    /// Returns the Ordering between `self` and `rhs` in variable time.
72    #[must_use]
73    pub const fn cmp_vartime(&self, rhs: &Self) -> Ordering {
74        UintRef::cmp_vartime(self.as_uint_ref(), rhs.as_uint_ref())
75    }
76}
77
78impl<const LIMBS: usize> Eq for Uint<LIMBS> {}
79
80impl<const LIMBS: usize> Ord for Uint<LIMBS> {
81    fn cmp(&self, other: &Self) -> Ordering {
82        Self::cmp(self, other)
83    }
84}
85
86impl<const LIMBS: usize> PartialOrd for Uint<LIMBS> {
87    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
88        Some(self.cmp(other))
89    }
90}
91
92impl<const LIMBS: usize> PartialEq for Uint<LIMBS> {
93    fn eq(&self, other: &Self) -> bool {
94        self.ct_eq(other).into()
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use crate::{Choice, Integer, U128, Uint};
101    use core::cmp::Ordering;
102
103    #[test]
104    fn is_zero() {
105        assert!(U128::ZERO.is_zero().to_bool());
106        assert!(!U128::ONE.is_zero().to_bool());
107        assert!(!U128::MAX.is_zero().to_bool());
108    }
109
110    #[test]
111    fn is_odd() {
112        // inherent methods
113        assert!(!U128::ZERO.is_odd().to_bool());
114        assert!(U128::ONE.is_odd().to_bool());
115        assert!(U128::MAX.is_odd().to_bool());
116
117        // `Integer` methods
118        assert!(!<U128 as Integer>::is_odd(&U128::ZERO).to_bool());
119        assert!(<U128 as Integer>::is_odd(&U128::ONE).to_bool());
120        assert!(<U128 as Integer>::is_odd(&U128::MAX).to_bool());
121    }
122
123    #[test]
124    fn lte() {
125        let a = U128::ZERO;
126        let b = U128::ONE;
127        let c = U128::MAX;
128
129        assert!(Uint::lte(&a, &b).to_bool());
130        assert!(Uint::lte(&a, &c).to_bool());
131        assert!(Uint::lte(&b, &c).to_bool());
132
133        assert!(Uint::lte(&a, &a).to_bool());
134        assert!(Uint::lte(&b, &b).to_bool());
135        assert!(Uint::lte(&c, &c).to_bool());
136
137        assert!(!Uint::lte(&b, &a).to_bool());
138        assert!(!Uint::lte(&c, &a).to_bool());
139        assert!(!Uint::lte(&c, &b).to_bool());
140    }
141
142    #[test]
143    fn cmp() {
144        let a = U128::ZERO;
145        let b = U128::ONE;
146        let c = U128::MAX;
147
148        assert_eq!(a.cmp(&b), Ordering::Less);
149        assert_eq!(a.cmp(&c), Ordering::Less);
150        assert_eq!(b.cmp(&c), Ordering::Less);
151
152        assert_eq!(a.cmp(&a), Ordering::Equal);
153        assert_eq!(b.cmp(&b), Ordering::Equal);
154        assert_eq!(c.cmp(&c), Ordering::Equal);
155
156        assert_eq!(b.cmp(&a), Ordering::Greater);
157        assert_eq!(c.cmp(&a), Ordering::Greater);
158        assert_eq!(c.cmp(&b), Ordering::Greater);
159    }
160
161    #[test]
162    fn cmp_vartime() {
163        let a = U128::ZERO;
164        let b = U128::ONE;
165        let c = U128::MAX;
166
167        assert_eq!(a.cmp_vartime(&b), Ordering::Less);
168        assert_eq!(a.cmp_vartime(&c), Ordering::Less);
169        assert_eq!(b.cmp_vartime(&c), Ordering::Less);
170
171        assert_eq!(a.cmp_vartime(&a), Ordering::Equal);
172        assert_eq!(b.cmp_vartime(&b), Ordering::Equal);
173        assert_eq!(c.cmp_vartime(&c), Ordering::Equal);
174
175        assert_eq!(b.cmp_vartime(&a), Ordering::Greater);
176        assert_eq!(c.cmp_vartime(&a), Ordering::Greater);
177        assert_eq!(c.cmp_vartime(&b), Ordering::Greater);
178    }
179
180    #[test]
181    fn conditional_swap() {
182        let mut a = U128::ZERO;
183        let mut b = U128::MAX;
184
185        Uint::conditional_swap(&mut a, &mut b, Choice::FALSE);
186        assert_eq!(a, Uint::ZERO);
187        assert_eq!(b, Uint::MAX);
188
189        Uint::conditional_swap(&mut a, &mut b, Choice::TRUE);
190        assert_eq!(a, Uint::MAX);
191        assert_eq!(b, Uint::ZERO);
192    }
193}