Skip to main content

crypto_bigint/uint/ref_type/
cmp.rs

1//! [`UintRef`] comparison operations.
2//!
3//! Constant time unless explicitly noted otherwise.
4
5use core::{cmp::Ordering, mem::transmute};
6
7use super::UintRef;
8use crate::{Choice, CtEq, Limb, word};
9
10impl UintRef {
11    /// Returns the truthy value if `self` is odd or the falsy value otherwise.
12    #[inline]
13    #[must_use]
14    pub const fn is_odd(&self) -> Choice {
15        debug_assert!(self.nlimbs() >= 1, "should have limbs");
16        word::choice_from_lsb(self.limbs[0].0 & 1)
17    }
18
19    /// Returns [`Choice::TRUE`] if `self` != `0` or [`Choice::FALSE`] otherwise.
20    #[inline]
21    #[must_use]
22    pub const fn is_nonzero(&self) -> Choice {
23        let mut b = 0;
24        let mut i = 0;
25        while i < self.nlimbs() {
26            b |= self.limbs[i].0;
27            i += 1;
28        }
29        Limb(b).is_nonzero()
30    }
31
32    /// Are all of limbs equal to `0`?
33    #[inline]
34    #[must_use]
35    pub const fn is_zero(&self) -> Choice {
36        self.is_nonzero().not()
37    }
38
39    /// Determine in variable time whether the `self` is zero.
40    #[inline]
41    pub(crate) const fn is_zero_vartime(&self) -> bool {
42        let mut i = 0;
43        while i < self.nlimbs() {
44            if self.limbs[i].0 != 0 {
45                return false;
46            }
47            i += 1;
48        }
49        true
50    }
51
52    /// Returns the Ordering between `lhs` and `rhs`.
53    #[allow(clippy::cast_possible_wrap)]
54    #[inline(always)]
55    pub(crate) const fn cmp(lhs: &Self, rhs: &Self) -> Ordering {
56        let overlap = if lhs.nlimbs() < rhs.nlimbs() {
57            lhs.nlimbs()
58        } else {
59            rhs.nlimbs()
60        };
61
62        let mut borrow = Limb::ZERO;
63        let mut diff = Limb::ZERO;
64        let mut i = 0;
65
66        while i < overlap {
67            let (w, b) = rhs.limbs[i].borrowing_sub(lhs.limbs[i], borrow);
68            diff = diff.bitor(w);
69            borrow = b;
70            i += 1;
71        }
72        let lesser = rhs.trailing(overlap).is_nonzero();
73        let greater = lhs.trailing(overlap).is_nonzero();
74
75        // FIXME cast_signed() is stable in Rust 1.87
76        let sgn = borrow
77            .lsb_to_choice()
78            .and(lesser.not())
79            .or(greater)
80            .select_u8(255, 1) as i8;
81        let ord = (diff.is_nonzero().or(lesser).or(greater).to_u8() as i8) * sgn;
82
83        #[allow(unsafe_code)]
84        // SAFETY: Ordering is repr(i8)
85        unsafe {
86            transmute(ord)
87        }
88    }
89
90    /// Returns the Ordering between `self` and `rhs` in variable time.
91    #[inline(always)]
92    #[must_use]
93    pub const fn cmp_vartime(&self, rhs: &Self) -> Ordering {
94        if self.nlimbs() < rhs.nlimbs() {
95            return rhs.cmp_vartime(self).reverse();
96        }
97
98        let mut i = self.nlimbs();
99        while i > rhs.nlimbs() {
100            i -= 1;
101            if self.limbs[i].0 != 0 {
102                return Ordering::Greater;
103            }
104        }
105        while i > 0 {
106            i -= 1;
107            let (val, borrow) = self.limbs[i].borrowing_sub(rhs.limbs[i], Limb::ZERO);
108            if val.0 != 0 {
109                return if borrow.0 != 0 {
110                    Ordering::Less
111                } else {
112                    Ordering::Greater
113                };
114            }
115        }
116
117        Ordering::Equal
118    }
119
120    /// Returns the truthy value if `lhs < rhs` and the falsy value otherwise.
121    #[inline(always)]
122    pub(crate) const fn lt(lhs: &Self, rhs: &Self) -> Choice {
123        let overlap = if lhs.nlimbs() < rhs.nlimbs() {
124            lhs.nlimbs()
125        } else {
126            rhs.nlimbs()
127        };
128
129        // NOTE: this is effectively a `borrowing_sub` that only computes the borrow
130        let mut i = 0;
131        let mut borrow = Limb::ZERO;
132
133        while i < overlap {
134            borrow = lhs.limbs[i].borrowing_sub(rhs.limbs[i], borrow).1;
135            i += 1;
136        }
137        let lesser = rhs.trailing(overlap).is_nonzero();
138        let greater = lhs.trailing(overlap).is_nonzero();
139        borrow.lsb_to_choice().and(greater.not()).or(lesser)
140    }
141}
142
143impl Eq for UintRef {}
144
145impl Ord for UintRef {
146    fn cmp(&self, other: &Self) -> Ordering {
147        Self::cmp(self, other)
148    }
149}
150
151impl PartialOrd for UintRef {
152    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
153        Some(self.cmp(other))
154    }
155}
156
157impl PartialEq for UintRef {
158    fn eq(&self, other: &Self) -> bool {
159        self.ct_eq(other).into()
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use core::cmp::Ordering;
166
167    use super::UintRef;
168    use crate::Limb;
169
170    #[test]
171    fn cmp() {
172        fn check(a: &UintRef, b: &UintRef, ord: Ordering) {
173            assert_eq!(UintRef::cmp(a, b), ord);
174            assert_eq!(UintRef::cmp_vartime(a, b), ord);
175            assert_eq!(a.cmp(b), ord);
176            if ord == Ordering::Equal {
177                assert_eq!(a, b);
178            } else {
179                assert_ne!(a, b);
180            }
181        }
182
183        let z_small = UintRef::new(&[Limb::ZERO, Limb::ZERO]);
184        let z_big = UintRef::new(&[Limb::ZERO, Limb::ZERO, Limb::ZERO]);
185        let a = UintRef::new(&[Limb::ZERO, Limb::ZERO, Limb::ONE]);
186        let b = UintRef::new(&[Limb::ONE, Limb::ZERO]);
187
188        check(z_small, z_big, Ordering::Equal);
189        check(z_big, z_small, Ordering::Equal);
190        check(z_small, a, Ordering::Less);
191        check(z_big, a, Ordering::Less);
192        check(a, z_small, Ordering::Greater);
193        check(a, z_big, Ordering::Greater);
194        check(a, b, Ordering::Greater);
195        check(b, a, Ordering::Less);
196    }
197
198    #[test]
199    fn lt() {
200        fn check(a: &UintRef, b: &UintRef) {
201            assert!(UintRef::lt(a, b).to_bool());
202            assert!(!UintRef::lt(b, a).to_bool());
203            assert!(a < b);
204            assert!(b > a);
205        }
206
207        let lesser = UintRef::new(&[Limb::ZERO, Limb::ZERO, Limb::ZERO]);
208        let greater = UintRef::new(&[Limb::ZERO, Limb::ONE, Limb::ZERO]);
209        check(lesser, greater);
210
211        let smaller = UintRef::new(&[Limb::ZERO, Limb::ZERO]);
212        let bigger = UintRef::new(&[Limb::ZERO, Limb::ZERO, Limb::ONE]);
213        check(smaller, bigger);
214    }
215}