const_num_traits/ops/parity.rs
1//! Parity (odd/even) checks.
2//!
3//! std has no inherent `is_odd`/`is_even` (they live in `num-integer`'s
4//! `Integer` bundle); this is the atom version. Unlike a blanket impl over
5//! `BitAnd + One + PartialEq + Clone` (the usual generic fallback shape),
6//! a per-type trait needs no `PartialEq`/`Clone` bounds — important both
7//! for constant-time types that deliberately don't expose `PartialEq` and
8//! for multi-limb bigints, which can answer from the lowest limb alone
9//! instead of comparing whole values.
10//!
11//! Both methods live in one trait: they're each other's negation, one
12//! capability. Note for constant-time callers: the returned `bool` *is* the
13//! low bit of the operand — treat it as secret-derived (Tier B).
14//!
15//! **CT tier B (caller-leaky)**: the check is one AND, but the returned
16//! `bool` is the operand's low bit. A masked counterpart, `CtParity`, is
17//! available with the `ct` feature.
18
19c0nst::c0nst! {
20/// Checks whether a value is odd or even.
21pub c0nst trait Parity {
22 /// Returns `true` if `self` is odd.
23 ///
24 /// For signed types this is a property of the value's low bit, so it is
25 /// correct for negative values too (`-3` is odd).
26 ///
27 /// ```
28 /// use const_num_traits::Parity;
29 ///
30 /// assert!(Parity::is_odd(3u8));
31 /// assert!(!Parity::is_odd(4u8));
32 /// assert!(Parity::is_odd(-3i8));
33 /// ```
34 fn is_odd(self) -> bool;
35
36 /// Returns `true` if `self` is even.
37 ///
38 /// ```
39 /// use const_num_traits::Parity;
40 ///
41 /// assert!(Parity::is_even(4u8));
42 /// assert!(!Parity::is_even(3u8));
43 /// assert!(Parity::is_even(0u8));
44 /// ```
45 fn is_even(self) -> bool;
46}
47}
48
49macro_rules! parity_impl {
50 ($($t:ty)*) => {$(
51 c0nst::c0nst! {
52 c0nst impl Parity for $t {
53 #[inline]
54 fn is_odd(self) -> bool {
55 self & 1 == 1
56 }
57
58 #[inline]
59 fn is_even(self) -> bool {
60 self & 1 == 0
61 }
62 }
63 }
64 )*};
65}
66
67parity_impl!(usize u8 u16 u32 u64 u128);
68parity_impl!(isize i8 i16 i32 i64 i128);
69
70c0nst::c0nst! {
71/// Blanket impl: `&T` is [`Parity`] whenever `T: Parity + Copy`.
72///
73/// This lets zero-cost typestate constructors (e.g. `Odd::from_ref`) borrow-
74/// check the predicate without consuming the value — `from_ref` needs
75/// `&T: Parity`, which this supplies for every `Copy` `T`. Non-`Copy` types
76/// (heap bigints) implement `Parity for &Self` themselves.
77c0nst impl<T: [c0nst] Parity + Copy> Parity for &T {
78 #[inline]
79 fn is_odd(self) -> bool {
80 (*self).is_odd()
81 }
82
83 #[inline]
84 fn is_even(self) -> bool {
85 (*self).is_even()
86 }
87}
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn parity() {
96 assert!(Parity::is_odd(1u8));
97 assert!(Parity::is_even(0u8));
98 assert!(Parity::is_even(u128::MAX - 1));
99 assert!(Parity::is_odd(u128::MAX));
100 assert!(Parity::is_odd(-1i8));
101 assert!(Parity::is_odd(i8::MIN.wrapping_sub(1))); // i8::MAX, odd
102 assert!(Parity::is_even(i8::MIN));
103 assert!(Parity::is_odd(-3i64));
104 assert!(Parity::is_even(-4i64));
105 }
106}