use super::{FixedUInt, MachineWord};
use crate::machineword::ConstMachineWord;
use const_num_traits::ops::ct::CtParity;
use const_num_traits::{ConstOne, ConstZero, Parity, Personality};
use subtle::{Choice, ConstantTimeEq};
c0nst::c0nst! {
c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> Parity for FixedUInt<T, N, P> {
fn is_odd(self) -> bool {
if N == 0 {
false
} else {
(self.array[0] & <T as ConstOne>::ONE)
!= <T as ConstZero>::ZERO
}
}
fn is_even(self) -> bool {
!<Self as Parity>::is_odd(self)
}
}
}
impl<T, const N: usize, P: Personality> CtParity for FixedUInt<T, N, P>
where
T: MachineWord + ConstantTimeEq,
{
fn ct_is_odd(&self) -> Choice {
if N == 0 {
return Choice::from(0);
}
let lsb = self.array[0] & <T as ConstOne>::ONE;
!lsb.ct_eq(&<T as ConstZero>::ZERO)
}
fn ct_is_even(&self) -> Choice {
!<Self as CtParity>::ct_is_odd(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use const_num_traits::{Ct, Nct};
type U16Nct = FixedUInt<u8, 2, Nct>;
type U16Ct = FixedUInt<u8, 2, Ct>;
#[test]
fn parity_nct() {
assert!(Parity::is_odd(U16Nct::from(1u8)));
assert!(Parity::is_odd(U16Nct::from(3u8)));
assert!(Parity::is_odd(U16Nct::from(0xFFFFu16)));
assert!(!Parity::is_odd(U16Nct::from(0u8)));
assert!(!Parity::is_odd(U16Nct::from(2u8)));
assert!(!Parity::is_odd(U16Nct::from(0xFFFEu16)));
assert!(Parity::is_even(U16Nct::from(0u8)));
assert!(!Parity::is_even(U16Nct::from(1u8)));
}
#[test]
fn parity_ct() {
let one_ct: U16Ct = FixedUInt::<u8, 2, Nct>::from(1u8).into();
let two_ct: U16Ct = FixedUInt::<u8, 2, Nct>::from(2u8).into();
assert!(Parity::is_odd(one_ct));
assert!(Parity::is_even(two_ct));
}
#[test]
#[allow(clippy::needless_borrows_for_generic_args)]
fn parity_ref() {
let v = U16Nct::from(7u8);
assert!(Parity::is_odd(&v));
assert!(!Parity::is_even(&v));
}
c0nst::c0nst! {
pub c0nst fn const_is_odd<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(v: FixedUInt<T, N, P>) -> bool {
Parity::is_odd(v)
}
pub c0nst fn const_is_even<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality>(v: FixedUInt<T, N, P>) -> bool {
Parity::is_even(v)
}
}
#[test]
fn nightly_const_eval_parity() {
assert!(const_is_odd(U16Nct::from(7u8)));
assert!(const_is_even(U16Nct::from(8u8)));
#[cfg(feature = "nightly")]
{
const ODD: U16Nct = FixedUInt::from_array([7, 0]);
const EVEN: U16Nct = FixedUInt::from_array([8, 0]);
const IS_ODD: bool = const_is_odd(ODD);
const IS_EVEN: bool = const_is_even(EVEN);
const IS_ODD_OF_EVEN: bool = const_is_odd(EVEN);
assert!(IS_ODD);
assert!(IS_EVEN);
assert!(!IS_ODD_OF_EVEN);
}
}
#[test]
fn ct_parity_nct() {
assert!(bool::from(CtParity::ct_is_odd(&U16Nct::from(1u8))));
assert!(bool::from(CtParity::ct_is_odd(&U16Nct::from(3u8))));
assert!(bool::from(CtParity::ct_is_odd(&U16Nct::from(0xFFFFu16))));
assert!(!bool::from(CtParity::ct_is_odd(&U16Nct::from(0u8))));
assert!(!bool::from(CtParity::ct_is_odd(&U16Nct::from(2u8))));
assert!(bool::from(CtParity::ct_is_even(&U16Nct::from(0u8))));
assert!(!bool::from(CtParity::ct_is_even(&U16Nct::from(1u8))));
}
#[test]
fn ct_parity_ct() {
let one_ct: U16Ct = FixedUInt::<u8, 2, Nct>::from(1u8).into();
let two_ct: U16Ct = FixedUInt::<u8, 2, Nct>::from(2u8).into();
assert!(bool::from(CtParity::ct_is_odd(&one_ct)));
assert!(bool::from(CtParity::ct_is_even(&two_ct)));
}
#[test]
fn ct_parity_agrees_with_parity() {
for v in [
0u16, 1, 2, 3, 7, 8, 0xFE, 0xFF, 0x100, 0x101, 0xFFFE, 0xFFFF,
] {
let nct = U16Nct::from(v);
let ct: U16Ct = U16Nct::from(v).into();
assert_eq!(
bool::from(CtParity::ct_is_odd(&nct)),
Parity::is_odd(nct),
"Nct ct_is_odd disagrees with is_odd at v={v}"
);
assert_eq!(
bool::from(CtParity::ct_is_odd(&ct)),
Parity::is_odd(ct),
"Ct ct_is_odd disagrees with is_odd at v={v}"
);
}
}
#[test]
fn odd_new_ct_round_trips_for_ct_carrier() {
use const_num_traits::Odd;
let odd_ct: U16Ct = FixedUInt::<u8, 2, Nct>::from(7u8).into();
let even_ct: U16Ct = FixedUInt::<u8, 2, Nct>::from(8u8).into();
let p_odd = Odd::<U16Ct>::new_ct(odd_ct);
let p_even = Odd::<U16Ct>::new_ct(even_ct);
assert!(
bool::from(p_odd.is_some()),
"Odd::new_ct(7) should mask Some"
);
assert!(
!bool::from(p_even.is_some()),
"Odd::new_ct(8) should mask None"
);
let recovered = p_odd.unwrap();
assert_eq!(recovered.get(), odd_ct);
}
}