#![allow(clippy::let_unit_value)]
use super::HeaplessBigInt;
use crate::MachineWord;
use const_num_traits::{CarryingMul, Ct, DivNonZero, HasNonZero, Nct, One, Personality, Zero};
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct NonZeroHeaplessBigInt<T, const CAP: usize, P: Personality>(HeaplessBigInt<T, CAP, P>)
where
T: MachineWord;
impl<T: MachineWord + core::fmt::Debug, const CAP: usize> core::fmt::Debug
for NonZeroHeaplessBigInt<T, CAP, Nct>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "NonZero({:?})", self.0)
}
}
impl<T: MachineWord, const CAP: usize> core::fmt::Debug for NonZeroHeaplessBigInt<T, CAP, Ct> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "NonZero({:?})", self.0)
}
}
impl<T, const CAP: usize, P: Personality> NonZeroHeaplessBigInt<T, CAP, P>
where
T: MachineWord,
{
#[inline]
pub fn get(self) -> HeaplessBigInt<T, CAP, P> {
self.0
}
}
trait AssertNonzeroCarrier {
const CHECK: ();
}
impl<T: MachineWord, const CAP: usize, P: Personality> AssertNonzeroCarrier
for NonZeroHeaplessBigInt<T, CAP, P>
{
const CHECK: () = assert!(
CAP > 0,
"NonZeroHeaplessBigInt::default() requires CAP > 0 (a CAP=0 carrier can only hold zero)"
);
}
impl<T, const CAP: usize, P: Personality> Default for NonZeroHeaplessBigInt<T, CAP, P>
where
T: MachineWord,
{
fn default() -> Self {
let _ = <Self as AssertNonzeroCarrier>::CHECK;
NonZeroHeaplessBigInt(<HeaplessBigInt<T, CAP, P> as One>::one().widened(CAP as u16))
}
}
impl<T, const CAP: usize> subtle::ConditionallySelectable for NonZeroHeaplessBigInt<T, CAP, Ct>
where
T: MachineWord + subtle::ConditionallySelectable,
{
fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self {
Self(
<HeaplessBigInt<T, CAP, Ct> as subtle::ConditionallySelectable>::conditional_select(
&a.0, &b.0, choice,
),
)
}
}
impl<T, const CAP: usize, P: Personality> HasNonZero for HeaplessBigInt<T, CAP, P>
where
T: MachineWord,
{
type NonZero = NonZeroHeaplessBigInt<T, CAP, P>;
#[inline]
fn into_nonzero(self) -> Option<Self::NonZero> {
if <Self as Zero>::is_zero(&self) {
None
} else {
Some(NonZeroHeaplessBigInt(self))
}
}
#[inline]
fn nonzero_get(nz: Self::NonZero) -> Self {
nz.0
}
}
impl<T, const CAP: usize> DivNonZero for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = Self;
#[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 CAP: usize, P: Personality> const_num_traits::CtNonZero for HeaplessBigInt<T, CAP, 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(NonZeroHeaplessBigInt(self), !zero)
}
}
#[cfg(test)]
mod tests {
use super::*;
use const_num_traits::CtNonZero;
type H = HeaplessBigInt<u8, 4, Nct>;
type HCt = HeaplessBigInt<u8, 4, Ct>;
#[test]
fn into_nonzero_some_none() {
assert!(H::from(5u32).into_nonzero().is_some());
assert!(H::from(0u32).into_nonzero().is_none());
}
#[test]
fn default_is_one_at_full_capacity() {
let d = <NonZeroHeaplessBigInt<u8, 4, Nct> as Default>::default();
assert_eq!(d.get(), H::from(1u8));
assert_eq!(d.get().len(), 4);
}
#[test]
fn nonzero_round_trip() {
let v = H::from(42u32);
let nz = v.into_nonzero().unwrap();
assert_eq!(<H as HasNonZero>::nonzero_get(nz), v);
assert_eq!(nz.get(), v);
}
#[test]
fn div_rem_nonzero_match_operators() {
let a = H::from(100u32);
let m = H::from(7u32);
let nz = m.into_nonzero().unwrap();
assert_eq!(<H as DivNonZero>::div_nonzero(a, nz), a / m);
assert_eq!(<H as DivNonZero>::rem_nonzero(a, nz), a % m);
}
static_assertions::assert_not_impl_any!(HeaplessBigInt<u8, 4, Ct>: DivNonZero);
#[test]
fn into_nonzero_ct_masks_zero() {
let nz = H::from(5u32).into_nonzero_ct();
assert!(bool::from(nz.is_some()));
assert_eq!(nz.unwrap().get(), H::from(5u32));
assert!(!bool::from(H::from(0u32).into_nonzero_ct().is_some()));
let nz_ct = HCt::from(42u32);
assert!(bool::from(nz_ct.into_nonzero_ct().is_some()));
let z_ct = HCt::from(0u32);
assert!(!bool::from(z_ct.into_nonzero_ct().is_some()));
}
}