const_num_traits/ops/
ct.rs1use subtle::{Choice, ConstantTimeEq, CtOption};
25
26pub trait CtIsZero {
28 fn ct_is_zero(&self) -> Choice;
37}
38
39pub trait CtParity {
43 fn ct_is_odd(&self) -> Choice;
45
46 fn ct_is_even(&self) -> Choice;
48}
49
50pub trait CtIsPowerOfTwo {
55 fn ct_is_power_of_two(&self) -> Choice;
57}
58
59pub trait CtCheckedAdd: Sized {
64 fn ct_checked_add(&self, v: &Self) -> CtOption<Self>;
74}
75
76pub trait CtCheckedSub: Sized {
81 fn ct_checked_sub(&self, v: &Self) -> CtOption<Self>;
84}
85
86pub trait CtCheckedMul: Sized {
91 fn ct_checked_mul(&self, v: &Self) -> CtOption<Self>;
94}
95
96pub trait CtCheckedNeg: Sized {
101 fn ct_checked_neg(&self) -> CtOption<Self>;
104}
105
106pub trait CtCheckedSignedDiff: Sized {
111 type Signed;
113
114 fn ct_checked_signed_diff(&self, rhs: &Self) -> CtOption<Self::Signed>;
117}
118
119macro_rules! ct_common_impl {
120 ($($t:ty)*) => {$(
121 impl CtIsZero for $t {
122 #[inline]
123 fn ct_is_zero(&self) -> Choice {
124 self.ct_eq(&0)
125 }
126 }
127
128 impl CtParity for $t {
129 #[inline]
130 fn ct_is_odd(&self) -> Choice {
131 Choice::from((*self & 1) as u8)
132 }
133
134 #[inline]
135 fn ct_is_even(&self) -> Choice {
136 Choice::from(((*self & 1) ^ 1) as u8)
137 }
138 }
139
140 impl CtCheckedAdd for $t {
141 #[inline]
142 fn ct_checked_add(&self, v: &Self) -> CtOption<Self> {
143 let (val, overflow) = <$t>::overflowing_add(*self, *v);
144 CtOption::new(val, Choice::from(!overflow as u8))
145 }
146 }
147
148 impl CtCheckedSub for $t {
149 #[inline]
150 fn ct_checked_sub(&self, v: &Self) -> CtOption<Self> {
151 let (val, overflow) = <$t>::overflowing_sub(*self, *v);
152 CtOption::new(val, Choice::from(!overflow as u8))
153 }
154 }
155
156 impl CtCheckedMul for $t {
157 #[inline]
158 fn ct_checked_mul(&self, v: &Self) -> CtOption<Self> {
159 let (val, overflow) = <$t>::overflowing_mul(*self, *v);
160 CtOption::new(val, Choice::from(!overflow as u8))
161 }
162 }
163
164 impl CtCheckedNeg for $t {
165 #[inline]
166 fn ct_checked_neg(&self) -> CtOption<Self> {
167 let (val, overflow) = <$t>::overflowing_neg(*self);
168 CtOption::new(val, Choice::from(!overflow as u8))
169 }
170 }
171 )*};
172}
173
174ct_common_impl!(usize u8 u16 u32 u64 u128);
175ct_common_impl!(isize i8 i16 i32 i64 i128);
176
177macro_rules! ct_unsigned_impl {
178 ($($t:ty => $s:ty;)*) => {$(
179 impl CtIsPowerOfTwo for $t {
180 #[inline]
182 fn ct_is_power_of_two(&self) -> Choice {
183 <$t>::count_ones(*self).ct_eq(&1)
184 }
185 }
186
187 impl CtCheckedSignedDiff for $t {
188 type Signed = $s;
189
190 #[inline]
193 fn ct_checked_signed_diff(&self, rhs: &Self) -> CtOption<$s> {
194 let res = <$t>::wrapping_sub(*self, *rhs) as $s;
195 let overflow = (*self >= *rhs) == (res < 0);
196 CtOption::new(res, Choice::from(!overflow as u8))
197 }
198 }
199 )*};
200}
201
202ct_unsigned_impl! {
203 u8 => i8;
204 u16 => i16;
205 u32 => i32;
206 u64 => i64;
207 usize => isize;
208 u128 => i128;
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn ct_predicates() {
217 assert_eq!(0u64.ct_is_zero().unwrap_u8(), 1);
218 assert_eq!(1u64.ct_is_zero().unwrap_u8(), 0);
219 assert_eq!(0i32.ct_is_zero().unwrap_u8(), 1);
220 assert_eq!((-1i32).ct_is_zero().unwrap_u8(), 0);
221 assert_eq!(3u8.ct_is_odd().unwrap_u8(), 1);
222 assert_eq!(3u8.ct_is_even().unwrap_u8(), 0);
223 assert_eq!((-3i8).ct_is_odd().unwrap_u8(), 1);
224 assert_eq!(16u32.ct_is_power_of_two().unwrap_u8(), 1);
225 assert_eq!(0u32.ct_is_power_of_two().unwrap_u8(), 0);
226 assert_eq!(10u32.ct_is_power_of_two().unwrap_u8(), 0);
227 }
228
229 #[test]
230 fn ct_checked_ops() {
231 assert_eq!(250u8.ct_checked_add(&5).unwrap(), 255);
232 assert!(bool::from(255u8.ct_checked_add(&1).is_none()));
233 assert_eq!(5u8.ct_checked_sub(&5).unwrap(), 0);
234 assert!(bool::from(0u8.ct_checked_sub(&1).is_none()));
235 assert_eq!(16u8.ct_checked_mul(&15).unwrap(), 240);
236 assert!(bool::from(16u8.ct_checked_mul(&16).is_none()));
237 assert_eq!((-5i8).ct_checked_neg().unwrap(), 5);
238 assert!(bool::from(i8::MIN.ct_checked_neg().is_none()));
239 assert!(bool::from(1u8.ct_checked_neg().is_none()));
240 assert_eq!(0u8.ct_checked_neg().unwrap(), 0);
241 for a in 0u8..=255 {
243 for b in [0u8, 1, 7, 127, 128, 255] {
244 assert_eq!(a.checked_add(b), a.ct_checked_add(&b).into());
245 assert_eq!(a.checked_sub(b), a.ct_checked_sub(&b).into());
246 assert_eq!(a.checked_mul(b), a.ct_checked_mul(&b).into());
247 }
248 }
249 }
250
251 #[test]
252 fn ct_signed_diff() {
253 assert_eq!(10u8.ct_checked_signed_diff(&14).unwrap(), -4);
254 assert!(bool::from(u8::MAX.ct_checked_signed_diff(&0).is_none()));
255 use crate::ops::mixed::CheckedSignedDiff;
257 for a in 0u8..=255 {
258 for b in [0u8, 1, 100, 127, 128, 255] {
259 let plain = CheckedSignedDiff::checked_signed_diff(a, b);
260 let ct: Option<i8> = a.ct_checked_signed_diff(&b).into();
261 assert_eq!(plain, ct, "{a} {b}");
262 }
263 }
264 }
265}