Skip to main content

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}