#![allow(clippy::let_unit_value)]
use super::{FixedUInt, MachineWord};
use crate::machineword::ConstMachineWord;
use const_num_traits::Zero;
use const_num_traits::{DivNonZero, HasNonZero, Nct, Personality};
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct NonZeroFixedUInt<T, const N: usize, P: Personality>(FixedUInt<T, N, P>)
where
T: MachineWord;
impl<T: MachineWord + core::fmt::Debug, const N: usize> core::fmt::Debug
for NonZeroFixedUInt<T, N, const_num_traits::Nct>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "NonZero({:?})", self.0)
}
}
impl<T: MachineWord, const N: usize> core::fmt::Debug
for NonZeroFixedUInt<T, N, const_num_traits::Ct>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "NonZero({:?})", self.0)
}
}
impl<T, const N: usize, P: Personality> NonZeroFixedUInt<T, N, P>
where
T: MachineWord,
{
#[inline]
pub fn get(self) -> FixedUInt<T, N, P> {
self.0
}
}
trait AssertNonzeroCarrier {
const CHECK: ();
}
impl<T: MachineWord, const N: usize, P: Personality> AssertNonzeroCarrier
for NonZeroFixedUInt<T, N, P>
{
const CHECK: () = assert!(
N > 0,
"NonZeroFixedUInt::default() requires N > 0 (an N=0 carrier can only represent zero)"
);
}
impl<T, const N: usize, P: Personality> Default for NonZeroFixedUInt<T, N, P>
where
T: MachineWord,
{
fn default() -> Self {
let _ = <Self as AssertNonzeroCarrier>::CHECK;
NonZeroFixedUInt(FixedUInt::from(1u8))
}
}
impl<T, const N: usize> subtle::ConditionallySelectable
for NonZeroFixedUInt<T, N, const_num_traits::Ct>
where
T: MachineWord + subtle::ConditionallySelectable,
{
fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
Self(<FixedUInt<T, N, const_num_traits::Ct> as subtle::ConditionallySelectable>::conditional_select(
&a.0, &b.0, choice,
))
}
}
c0nst::c0nst! {
c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize, P: Personality> HasNonZero for FixedUInt<T, N, P> {
type NonZero = NonZeroFixedUInt<T, N, P>;
#[inline]
fn into_nonzero(self) -> Option<Self::NonZero> {
if <Self as Zero>::is_zero(&self) {
None
} else {
Some(NonZeroFixedUInt(self))
}
}
#[inline]
fn nonzero_get(nz: Self::NonZero) -> Self {
nz.0
}
}
c0nst impl<T: [c0nst] ConstMachineWord + MachineWord, const N: usize> DivNonZero for FixedUInt<T, N, Nct> {
type Output = FixedUInt<T, N, Nct>;
#[inline]
fn div_nonzero(self, d: Self::NonZero) -> Self::Output {
self / d.0
}
#[inline]
fn rem_nonzero(self, d: Self::NonZero) -> Self::Output {
self % d.0
}
}
}
impl<T, const N: usize, P: Personality> const_num_traits::CtNonZero for FixedUInt<T, N, P>
where
T: MachineWord + subtle::ConstantTimeEq,
{
fn into_nonzero_ct(self) -> subtle::CtOption<Self::NonZero> {
use const_num_traits::ops::ct::CtIsZero;
let zero = self.ct_is_zero();
subtle::CtOption::new(NonZeroFixedUInt(self), !zero)
}
}
#[cfg(test)]
mod tests {
use super::*;
use const_num_traits::{Ct, Nct};
type U32 = FixedUInt<u8, 4, Nct>;
type U32Ct = FixedUInt<u8, 4, Ct>;
#[test]
fn into_nonzero_some_for_nonzero() {
assert!(U32::from(5u32).into_nonzero().is_some());
}
#[test]
fn into_nonzero_none_for_zero() {
assert!(U32::from(0u32).into_nonzero().is_none());
}
#[test]
fn into_nonzero_works_under_ct_too() {
assert!(U32Ct::from(5u32).into_nonzero().is_some());
assert!(U32Ct::from(0u32).into_nonzero().is_none());
}
#[test]
fn nonzero_round_trip() {
let v = U32::from(42u32);
let nz = v.into_nonzero().unwrap();
assert_eq!(<U32 as HasNonZero>::nonzero_get(nz), v);
assert_eq!(nz.get(), v);
}
#[test]
fn div_rem_nonzero_match_div_rem() {
let a = U32::from(100u32);
let m = U32::from(7u32);
let nz = m.into_nonzero().unwrap();
assert_eq!(<U32 as DivNonZero>::div_nonzero(a, nz), a / m);
assert_eq!(<U32 as DivNonZero>::rem_nonzero(a, nz), a % m);
}
#[test]
fn div_rem_nonzero_wider_carrier() {
type U128 = FixedUInt<u32, 4, Nct>;
let a = U128::from(12_345_678u32);
let m = U128::from(101u32);
let nz = m.into_nonzero().unwrap();
assert_eq!(<U128 as DivNonZero>::div_nonzero(a, nz), a / m);
assert_eq!(<U128 as DivNonZero>::rem_nonzero(a, nz), a % m);
}
static_assertions::assert_not_impl_any!(
FixedUInt<u32, 4, const_num_traits::Ct>: DivNonZero
);
#[test]
fn into_nonzero_ct_masks_zero() {
use const_num_traits::CtNonZero;
type U32Nct = FixedUInt<u8, 4, Nct>;
type U32Ct = FixedUInt<u8, 4, Ct>;
let nz = U32Nct::from(5u32).into_nonzero_ct();
assert!(bool::from(nz.is_some()));
assert_eq!(nz.unwrap().get(), U32Nct::from(5u32));
let z = U32Nct::from(0u32).into_nonzero_ct();
assert!(!bool::from(z.is_some()));
let nz_ct: U32Ct = U32Nct::from(42u32).into();
let opt = nz_ct.into_nonzero_ct();
assert!(bool::from(opt.is_some()));
assert_eq!(opt.unwrap().get(), nz_ct);
let z_ct: U32Ct = U32Nct::from(0u32).into();
let opt = z_ct.into_nonzero_ct();
assert!(!bool::from(opt.is_some()));
}
}