Coverage Report

Created: 2026-05-16 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/crypto-bigint-0.5.5/src/limb/cmp.rs
Line
Count
Source
1
//! Limb comparisons
2
3
use super::HI_BIT;
4
use crate::{CtChoice, Limb};
5
use core::cmp::Ordering;
6
use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};
7
8
impl Limb {
9
    /// Is this limb an odd number?
10
    #[inline]
11
3.41k
    pub fn is_odd(&self) -> Choice {
12
3.41k
        Choice::from(self.0 as u8 & 1)
13
3.41k
    }
<crypto_bigint::limb::Limb>::is_odd
Line
Count
Source
11
3.41k
    pub fn is_odd(&self) -> Choice {
12
3.41k
        Choice::from(self.0 as u8 & 1)
13
3.41k
    }
Unexecuted instantiation: <crypto_bigint::limb::Limb>::is_odd
14
15
    /// Perform a comparison of the inner value in variable-time.
16
    ///
17
    /// Note that the [`PartialOrd`] and [`Ord`] impls wrap constant-time
18
    /// comparisons using the `subtle` crate.
19
0
    pub fn cmp_vartime(&self, other: &Self) -> Ordering {
20
0
        self.0.cmp(&other.0)
21
0
    }
22
23
    /// Performs an equality check in variable-time.
24
0
    pub const fn eq_vartime(&self, other: &Self) -> bool {
25
0
        self.0 == other.0
26
0
    }
27
28
    /// Return `b` if `c` is truthy, otherwise return `a`.
29
    #[inline]
30
0
    pub(crate) const fn ct_select(a: Self, b: Self, c: CtChoice) -> Self {
31
0
        Self(c.select(a.0, b.0))
32
0
    }
33
34
    /// Returns the truthy value if `self != 0` and the falsy value otherwise.
35
    #[inline]
36
66.2k
    pub(crate) const fn ct_is_nonzero(&self) -> CtChoice {
37
66.2k
        let inner = self.0;
38
66.2k
        CtChoice::from_lsb((inner | inner.wrapping_neg()) >> HI_BIT)
39
66.2k
    }
<crypto_bigint::limb::Limb>::ct_is_nonzero
Line
Count
Source
36
66.2k
    pub(crate) const fn ct_is_nonzero(&self) -> CtChoice {
37
66.2k
        let inner = self.0;
38
66.2k
        CtChoice::from_lsb((inner | inner.wrapping_neg()) >> HI_BIT)
39
66.2k
    }
Unexecuted instantiation: <crypto_bigint::limb::Limb>::ct_is_nonzero
40
41
    /// Returns the truthy value if `lhs == rhs` and the falsy value otherwise.
42
    #[inline]
43
0
    pub(crate) const fn ct_eq(lhs: Self, rhs: Self) -> CtChoice {
44
0
        let x = lhs.0;
45
0
        let y = rhs.0;
46
47
        // x ^ y == 0 if and only if x == y
48
0
        Self(x ^ y).ct_is_nonzero().not()
49
0
    }
50
51
    /// Returns the truthy value if `lhs < rhs` and the falsy value otherwise.
52
    #[inline]
53
0
    pub(crate) const fn ct_lt(lhs: Self, rhs: Self) -> CtChoice {
54
0
        let x = lhs.0;
55
0
        let y = rhs.0;
56
0
        let bit = (((!x) & y) | (((!x) | y) & (x.wrapping_sub(y)))) >> (Limb::BITS - 1);
57
0
        CtChoice::from_lsb(bit)
58
0
    }
59
60
    /// Returns the truthy value if `lhs <= rhs` and the falsy value otherwise.
61
    #[inline]
62
0
    pub(crate) const fn ct_le(lhs: Self, rhs: Self) -> CtChoice {
63
0
        let x = lhs.0;
64
0
        let y = rhs.0;
65
0
        let bit = (((!x) | y) & ((x ^ y) | !(y.wrapping_sub(x)))) >> (Limb::BITS - 1);
66
0
        CtChoice::from_lsb(bit)
67
0
    }
68
}
69
70
impl ConstantTimeEq for Limb {
71
    #[inline]
72
0
    fn ct_eq(&self, other: &Self) -> Choice {
73
0
        self.0.ct_eq(&other.0)
74
0
    }
75
}
76
77
impl ConstantTimeGreater for Limb {
78
    #[inline]
79
0
    fn ct_gt(&self, other: &Self) -> Choice {
80
0
        self.0.ct_gt(&other.0)
81
0
    }
82
}
83
84
impl ConstantTimeLess for Limb {
85
    #[inline]
86
0
    fn ct_lt(&self, other: &Self) -> Choice {
87
0
        self.0.ct_lt(&other.0)
88
0
    }
89
}
90
91
impl Eq for Limb {}
92
93
impl Ord for Limb {
94
0
    fn cmp(&self, other: &Self) -> Ordering {
95
0
        let mut n = 0i8;
96
0
        n -= self.ct_lt(other).unwrap_u8() as i8;
97
0
        n += self.ct_gt(other).unwrap_u8() as i8;
98
99
0
        match n {
100
0
            -1 => Ordering::Less,
101
0
            1 => Ordering::Greater,
102
            _ => {
103
0
                debug_assert_eq!(n, 0);
104
0
                debug_assert!(bool::from(self.ct_eq(other)));
105
0
                Ordering::Equal
106
            }
107
        }
108
0
    }
109
}
110
111
impl PartialOrd for Limb {
112
    #[inline]
113
0
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
114
0
        Some(self.cmp(other))
115
0
    }
116
}
117
118
impl PartialEq for Limb {
119
    #[inline]
120
0
    fn eq(&self, other: &Self) -> bool {
121
0
        self.ct_eq(other).into()
122
0
    }
123
}
124
125
#[cfg(test)]
126
mod tests {
127
    use crate::{Limb, Zero};
128
    use core::cmp::Ordering;
129
    use subtle::{ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};
130
131
    #[test]
132
    fn is_zero() {
133
        assert!(bool::from(Limb::ZERO.is_zero()));
134
        assert!(!bool::from(Limb::ONE.is_zero()));
135
        assert!(!bool::from(Limb::MAX.is_zero()));
136
    }
137
138
    #[test]
139
    fn is_odd() {
140
        assert!(!bool::from(Limb::ZERO.is_odd()));
141
        assert!(bool::from(Limb::ONE.is_odd()));
142
        assert!(bool::from(Limb::MAX.is_odd()));
143
    }
144
145
    #[test]
146
    fn ct_eq() {
147
        let a = Limb::ZERO;
148
        let b = Limb::MAX;
149
150
        assert!(bool::from(a.ct_eq(&a)));
151
        assert!(!bool::from(a.ct_eq(&b)));
152
        assert!(!bool::from(b.ct_eq(&a)));
153
        assert!(bool::from(b.ct_eq(&b)));
154
    }
155
156
    #[test]
157
    fn ct_gt() {
158
        let a = Limb::ZERO;
159
        let b = Limb::ONE;
160
        let c = Limb::MAX;
161
162
        assert!(bool::from(b.ct_gt(&a)));
163
        assert!(bool::from(c.ct_gt(&a)));
164
        assert!(bool::from(c.ct_gt(&b)));
165
166
        assert!(!bool::from(a.ct_gt(&a)));
167
        assert!(!bool::from(b.ct_gt(&b)));
168
        assert!(!bool::from(c.ct_gt(&c)));
169
170
        assert!(!bool::from(a.ct_gt(&b)));
171
        assert!(!bool::from(a.ct_gt(&c)));
172
        assert!(!bool::from(b.ct_gt(&c)));
173
    }
174
175
    #[test]
176
    fn ct_lt() {
177
        let a = Limb::ZERO;
178
        let b = Limb::ONE;
179
        let c = Limb::MAX;
180
181
        assert!(bool::from(a.ct_lt(&b)));
182
        assert!(bool::from(a.ct_lt(&c)));
183
        assert!(bool::from(b.ct_lt(&c)));
184
185
        assert!(!bool::from(a.ct_lt(&a)));
186
        assert!(!bool::from(b.ct_lt(&b)));
187
        assert!(!bool::from(c.ct_lt(&c)));
188
189
        assert!(!bool::from(b.ct_lt(&a)));
190
        assert!(!bool::from(c.ct_lt(&a)));
191
        assert!(!bool::from(c.ct_lt(&b)));
192
    }
193
194
    #[test]
195
    fn cmp() {
196
        assert_eq!(Limb::ZERO.cmp(&Limb::ONE), Ordering::Less);
197
        assert_eq!(Limb::ONE.cmp(&Limb::ONE), Ordering::Equal);
198
        assert_eq!(Limb::MAX.cmp(&Limb::ONE), Ordering::Greater);
199
    }
200
}