use crate::int::PrimBits;
use crate::ops::parity::Parity;
use crate::ops::pow2::IsPowerOfTwo;
use core::marker::PhantomData;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TypestateError;
impl core::fmt::Display for TypestateError {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("value does not satisfy the typestate's predicate")
}
}
impl core::error::Error for TypestateError {}
pub struct PowerOfTwo<T> {
exp: u32,
_t: PhantomData<T>,
}
impl<T> Clone for PowerOfTwo<T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for PowerOfTwo<T> {}
impl<T> PowerOfTwo<T> {
#[inline]
pub const unsafe fn from_exp_unchecked(exp: u32) -> Self {
PowerOfTwo {
exp,
_t: PhantomData,
}
}
#[inline]
pub const fn exp(self) -> u32 {
self.exp
}
}
c0nst::c0nst! {
impl<T> PowerOfTwo<T> {
#[inline]
pub c0nst fn new_checked(value: T) -> Option<Self>
where
T: [c0nst] IsPowerOfTwo + [c0nst] PrimBits,
{
if value.is_power_of_two() {
let width = T::ZERO.count_zeros();
let exp = width - 1 - value.leading_zeros();
Some(unsafe { Self::from_exp_unchecked(exp) })
} else {
None
}
}
}
}
c0nst::c0nst! {
pub c0nst trait PowerOfTwoOps: Sized {
type Output;
fn div_pow2(self, p: PowerOfTwo<Self>) -> Self::Output;
fn rem_pow2(self, p: PowerOfTwo<Self>) -> Self::Output;
fn is_multiple_of_pow2(self, p: PowerOfTwo<Self>) -> bool;
fn next_multiple_of_pow2(self, p: PowerOfTwo<Self>) -> Self::Output;
fn checked_next_multiple_of_pow2(self, p: PowerOfTwo<Self>) -> Option<Self::Output>;
}
}
macro_rules! pow2_typestate_impl {
($($t:ty),+) => {$(
impl PowerOfTwo<$t> {
#[inline]
pub const fn new(value: $t) -> Option<Self> {
if value.is_power_of_two() {
Some(PowerOfTwo { exp: value.ilog2(), _t: PhantomData })
} else {
None
}
}
#[inline]
pub const fn get(self) -> $t {
(1 as $t) << self.exp
}
}
c0nst::c0nst! {
c0nst impl PowerOfTwoOps for $t {
type Output = $t;
#[inline]
fn div_pow2(self, p: PowerOfTwo<$t>) -> $t {
self >> p.exp
}
#[inline]
fn rem_pow2(self, p: PowerOfTwo<$t>) -> $t {
let mask = ((1 as $t) << p.exp) - 1;
self & mask
}
#[inline]
fn is_multiple_of_pow2(self, p: PowerOfTwo<$t>) -> bool {
let mask = ((1 as $t) << p.exp) - 1;
(self & mask) == 0
}
#[inline]
fn next_multiple_of_pow2(self, p: PowerOfTwo<$t>) -> $t {
let mask = ((1 as $t) << p.exp) - 1;
(self + mask) & !mask
}
#[inline]
fn checked_next_multiple_of_pow2(self, p: PowerOfTwo<$t>) -> Option<$t> {
let mask = ((1 as $t) << p.exp) - 1;
match self.checked_add(mask) {
Some(s) => Some(s & !mask),
None => None,
}
}
}
}
impl TryFrom<$t> for PowerOfTwo<$t> {
type Error = TypestateError;
#[inline]
fn try_from(value: $t) -> Result<Self, TypestateError> {
Self::new(value).ok_or(TypestateError)
}
}
)+};
}
pow2_typestate_impl!(u8, u16, u32, u64, u128, usize);
pub struct BitIndex<T> {
index: u32,
_t: PhantomData<T>,
}
impl<T> Clone for BitIndex<T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for BitIndex<T> {}
impl<T> BitIndex<T> {
#[inline]
pub const unsafe fn from_u32_unchecked(index: u32) -> Self {
BitIndex {
index,
_t: PhantomData,
}
}
#[inline]
pub const fn get(self) -> u32 {
self.index
}
}
c0nst::c0nst! {
impl<T> BitIndex<T> {
#[inline]
pub c0nst fn new_checked(index: u32) -> Option<Self>
where
T: [c0nst] PrimBits,
{
if index < T::ZERO.count_zeros() {
Some(BitIndex { index, _t: PhantomData })
} else {
None
}
}
}
}
c0nst::c0nst! {
pub c0nst trait BitIndexOps: Sized {
type Output;
fn shl_index(self, index: BitIndex<Self>) -> Self::Output;
fn shr_index(self, index: BitIndex<Self>) -> Self::Output;
}
}
macro_rules! bit_index_impl {
($($t:ty),+) => {$(
impl BitIndex<$t> {
#[inline]
pub const fn new(index: u32) -> Option<Self> {
if index < <$t>::BITS {
Some(BitIndex { index, _t: PhantomData })
} else {
None
}
}
}
c0nst::c0nst! {
c0nst impl BitIndexOps for $t {
type Output = $t;
#[inline]
fn shl_index(self, index: BitIndex<$t>) -> $t {
self << index.index
}
#[inline]
fn shr_index(self, index: BitIndex<$t>) -> $t {
self >> index.index
}
}
}
impl TryFrom<u32> for BitIndex<$t> {
type Error = TypestateError;
#[inline]
fn try_from(index: u32) -> Result<Self, TypestateError> {
Self::new(index).ok_or(TypestateError)
}
}
)+};
}
bit_index_impl!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
);
c0nst::c0nst! {
pub c0nst trait HasNonZero: Sized {
type NonZero: Copy;
fn into_nonzero(self) -> Option<Self::NonZero>;
fn nonzero_get(nz: Self::NonZero) -> Self;
}
}
c0nst::c0nst! {
pub c0nst trait DivNonZero: [c0nst] HasNonZero {
type Output;
fn div_nonzero(self, d: Self::NonZero) -> Self::Output;
fn rem_nonzero(self, d: Self::NonZero) -> Self::Output;
}
}
macro_rules! nonzero_bridge_impl {
($($t:ty),+) => {$(
c0nst::c0nst! {
c0nst impl HasNonZero for $t {
type NonZero = core::num::NonZero<$t>;
#[inline]
fn into_nonzero(self) -> Option<core::num::NonZero<$t>> {
core::num::NonZero::new(self)
}
#[inline]
fn nonzero_get(nz: core::num::NonZero<$t>) -> $t {
nz.get()
}
}
}
c0nst::c0nst! {
c0nst impl DivNonZero for $t {
type Output = $t;
#[inline]
fn div_nonzero(self, d: core::num::NonZero<$t>) -> $t {
self / d.get()
}
#[inline]
fn rem_nonzero(self, d: core::num::NonZero<$t>) -> $t {
self % d.get()
}
}
}
)+};
}
nonzero_bridge_impl!(u8, u16, u32, u64, u128, usize);
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NonNegative<T>(T);
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Positive<T>(T);
macro_rules! sign_typestate_impl {
($($t:ty => $u:ty),+) => {$(
impl NonNegative<$t> {
#[inline]
pub const fn new(value: $t) -> Option<Self> {
if value >= 0 { Some(NonNegative(value)) } else { None }
}
#[inline]
pub const unsafe fn new_unchecked(value: $t) -> Self { NonNegative(value) }
#[inline]
pub const fn get(self) -> $t { self.0 }
#[inline]
pub fn from_ref(value: &$t) -> Option<&Self> {
if *value >= 0 {
Some(unsafe { &*(value as *const $t as *const Self) })
} else { None }
}
#[inline]
pub const fn to_unsigned(self) -> $u { self.0 as $u }
#[inline]
pub const fn abs(self) -> $t { self.0 }
#[inline]
pub const fn isqrt(self) -> $t { self.0.isqrt() }
#[inline]
pub const fn into_nonmin(self) -> NonMin<$t> { NonMin(self.0) }
}
impl Positive<$t> {
#[inline]
pub const fn new(value: $t) -> Option<Self> {
if value > 0 { Some(Positive(value)) } else { None }
}
#[inline]
pub const unsafe fn new_unchecked(value: $t) -> Self { Positive(value) }
#[inline]
pub const fn get(self) -> $t { self.0 }
#[inline]
pub fn from_ref(value: &$t) -> Option<&Self> {
if *value > 0 {
Some(unsafe { &*(value as *const $t as *const Self) })
} else { None }
}
#[inline]
pub const fn to_unsigned(self) -> $u { self.0 as $u }
#[inline]
pub const fn abs(self) -> $t { self.0 }
#[inline]
pub const fn isqrt(self) -> $t { self.0.isqrt() }
#[inline]
pub const fn into_nonnegative(self) -> NonNegative<$t> {
NonNegative(self.0)
}
#[inline]
pub const fn into_nonzero(self) -> core::num::NonZero<$t> {
unsafe { core::num::NonZero::new_unchecked(self.0) }
}
#[inline]
pub const fn into_nonmin(self) -> NonMin<$t> { NonMin(self.0) }
}
impl TryFrom<$t> for NonNegative<$t> {
type Error = TypestateError;
#[inline]
fn try_from(value: $t) -> Result<Self, TypestateError> {
Self::new(value).ok_or(TypestateError)
}
}
impl TryFrom<$t> for Positive<$t> {
type Error = TypestateError;
#[inline]
fn try_from(value: $t) -> Result<Self, TypestateError> {
Self::new(value).ok_or(TypestateError)
}
}
)+};
}
sign_typestate_impl!(
i8 => u8, i16 => u16, i32 => u32, i64 => u64, i128 => u128, isize => usize
);
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NonMin<T>(T);
macro_rules! nonmin_impl {
($($t:ty),+) => {$(
impl NonMin<$t> {
#[inline]
pub const fn new(value: $t) -> Option<Self> {
if value != <$t>::MIN { Some(NonMin(value)) } else { None }
}
#[inline]
pub const unsafe fn new_unchecked(value: $t) -> Self { NonMin(value) }
#[inline]
pub const fn get(self) -> $t { self.0 }
#[inline]
pub fn from_ref(value: &$t) -> Option<&Self> {
if *value != <$t>::MIN {
Some(unsafe { &*(value as *const $t as *const Self) })
} else { None }
}
#[inline]
pub const fn neg(self) -> $t { -self.0 }
#[inline]
pub const fn abs(self) -> $t { self.0.abs() }
#[inline]
pub const fn div_nonzero(self, d: core::num::NonZero<$t>) -> $t {
self.0 / d.get()
}
#[inline]
pub const fn rem_nonzero(self, d: core::num::NonZero<$t>) -> $t {
self.0 % d.get()
}
}
impl TryFrom<$t> for NonMin<$t> {
type Error = TypestateError;
#[inline]
fn try_from(value: $t) -> Result<Self, TypestateError> {
Self::new(value).ok_or(TypestateError)
}
}
)+};
}
nonmin_impl!(i8, i16, i32, i64, i128, isize);
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Odd<T>(T);
impl<T> Odd<T> {
#[inline]
pub const unsafe fn new_unchecked(value: T) -> Self {
Odd(value)
}
#[inline]
pub fn get(self) -> T {
self.0
}
}
impl<T> AsRef<T> for Odd<T> {
#[inline]
fn as_ref(&self) -> &T {
&self.0
}
}
c0nst::c0nst! {
impl<T: Parity + Copy> Odd<T> {
#[inline]
pub c0nst fn new(value: T) -> Option<Self>
where
T: [c0nst] Parity,
{
if value.is_odd() { Some(Odd(value)) } else { None }
}
}
}
impl<T> Odd<T>
where
for<'a> &'a T: Parity,
{
#[inline]
pub fn from_ref(value: &T) -> Option<&Self> {
if value.is_odd() {
Some(unsafe { &*(value as *const T as *const Odd<T>) })
} else {
None
}
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Even<T>(T);
impl<T> Even<T> {
#[inline]
pub const unsafe fn new_unchecked(value: T) -> Self {
Even(value)
}
#[inline]
pub fn get(self) -> T {
self.0
}
}
impl<T> AsRef<T> for Even<T> {
#[inline]
fn as_ref(&self) -> &T {
&self.0
}
}
macro_rules! parity_try_from {
($($t:ty),+) => {$(
impl TryFrom<$t> for Odd<$t> {
type Error = TypestateError;
#[inline]
fn try_from(value: $t) -> Result<Self, TypestateError> {
Self::new(value).ok_or(TypestateError)
}
}
impl TryFrom<$t> for Even<$t> {
type Error = TypestateError;
#[inline]
fn try_from(value: $t) -> Result<Self, TypestateError> {
Self::new(value).ok_or(TypestateError)
}
}
)+};
}
parity_try_from!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
);
c0nst::c0nst! {
impl<T: Parity + Copy> Even<T> {
#[inline]
pub c0nst fn new(value: T) -> Option<Self>
where
T: [c0nst] Parity,
{
if value.is_even() { Some(Even(value)) } else { None }
}
}
}
impl<T> Even<T>
where
for<'a> &'a T: Parity,
{
#[inline]
pub fn from_ref(value: &T) -> Option<&Self> {
if value.is_even() {
Some(unsafe { &*(value as *const T as *const Even<T>) })
} else {
None
}
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
pub struct Finite<T>(T);
macro_rules! finite_impl {
($($t:ty => $exp_mask:expr),+) => {$(
impl Finite<$t> {
#[inline]
pub const fn new(value: $t) -> Option<Self> {
if (value.to_bits() & $exp_mask) != $exp_mask {
Some(Finite(value))
} else {
None
}
}
#[inline]
pub const unsafe fn new_unchecked(value: $t) -> Self { Finite(value) }
#[inline]
pub const fn get(self) -> $t { self.0 }
#[inline]
pub fn from_ref(value: &$t) -> Option<&Self> {
if (value.to_bits() & $exp_mask) != $exp_mask {
Some(unsafe { &*(value as *const $t as *const Self) })
} else {
None
}
}
}
impl TryFrom<$t> for Finite<$t> {
type Error = TypestateError;
#[inline]
fn try_from(value: $t) -> Result<Self, TypestateError> {
Self::new(value).ok_or(TypestateError)
}
}
impl PartialEq for Finite<$t> {
#[inline]
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
impl Eq for Finite<$t> {}
impl PartialOrd for Finite<$t> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Finite<$t> {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.0.partial_cmp(&other.0).unwrap()
}
}
)+};
}
finite_impl!(
f32 => 0x7F80_0000u32,
f64 => 0x7FF0_0000_0000_0000u64
);
#[cfg(feature = "ct")]
pub use ct_constructors::CtNonZero;
#[cfg(feature = "ct")]
mod ct_constructors {
use super::{Even, HasNonZero, NonMin, NonNegative, Odd, Positive, PowerOfTwo};
use crate::ops::ct::{CtIsPowerOfTwo, CtIsZero, CtParity};
use subtle::{Choice, ConstantTimeEq, CtOption};
macro_rules! ct_pow2 {
($($t:ty),+) => {$(
impl PowerOfTwo<$t> {
#[inline]
pub fn new_ct(value: $t) -> CtOption<PowerOfTwo<$t>> {
let choice = value.ct_is_power_of_two();
let exp = value.trailing_zeros() % <$t>::BITS;
let proof = unsafe { PowerOfTwo::from_exp_unchecked(exp) };
CtOption::new(proof, choice)
}
}
)+};
}
ct_pow2!(u8, u16, u32, u64, u128, usize);
macro_rules! ct_sign {
($($t:ty),+) => {$(
impl NonNegative<$t> {
#[inline]
pub fn new_ct(value: $t) -> CtOption<NonNegative<$t>> {
let neg = Choice::from(((value >> (<$t>::BITS - 1)) & 1) as u8);
CtOption::new(NonNegative(value), !neg)
}
}
impl Positive<$t> {
#[inline]
pub fn new_ct(value: $t) -> CtOption<Positive<$t>> {
let neg = Choice::from(((value >> (<$t>::BITS - 1)) & 1) as u8);
let zero = value.ct_is_zero();
CtOption::new(Positive(value), !neg & !zero)
}
}
)+};
}
ct_sign!(i8, i16, i32, i64, i128, isize);
macro_rules! ct_nonmin {
($($t:ty),+) => {$(
impl NonMin<$t> {
#[inline]
pub fn new_ct(value: $t) -> CtOption<NonMin<$t>> {
let is_min = value.ct_eq(&<$t>::MIN);
CtOption::new(NonMin(value), !is_min)
}
}
)+};
}
ct_nonmin!(i8, i16, i32, i64, i128, isize);
impl<T: CtParity + Copy> Odd<T> {
#[inline]
pub fn new_ct(value: T) -> CtOption<Odd<T>> {
let choice = value.ct_is_odd();
CtOption::new(Odd(value), choice)
}
}
impl<T: CtParity + Copy> Even<T> {
#[inline]
pub fn new_ct(value: T) -> CtOption<Even<T>> {
let choice = value.ct_is_even();
CtOption::new(Even(value), choice)
}
}
pub trait CtNonZero: HasNonZero {
fn into_nonzero_ct(self) -> CtOption<Self::NonZero>;
}
macro_rules! ct_nonzero {
($($t:ty),+) => {$(
impl CtNonZero for $t {
#[inline]
fn into_nonzero_ct(self) -> CtOption<core::num::NonZero<$t>> {
let zero = self.ct_is_zero();
let safe = self | (zero.unwrap_u8() as $t);
let nz = unsafe { core::num::NonZero::new_unchecked(safe) };
CtOption::new(nz, !zero)
}
}
)+};
}
ct_nonzero!(u8, u16, u32, u64, u128, usize);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn construction_and_accessors() {
assert!(PowerOfTwo::<u32>::new(0).is_none());
assert!(PowerOfTwo::<u32>::new(3).is_none());
assert!(PowerOfTwo::<u32>::new(6).is_none());
let p = PowerOfTwo::<u32>::new(64).unwrap();
assert_eq!(p.exp(), 6);
assert_eq!(p.get(), 64);
assert_eq!(PowerOfTwo::<u8>::new(1).unwrap().exp(), 0);
assert_eq!(PowerOfTwo::<u8>::new(128).unwrap().get(), 128);
assert_eq!(PowerOfTwo::<u32>::new_checked(64).unwrap().exp(), 6);
assert!(PowerOfTwo::<u32>::new_checked(63).is_none());
assert_eq!(PowerOfTwo::<u8>::new_checked(128).unwrap().get(), 128);
assert_eq!(BitIndex::<u32>::new_checked(5).unwrap().get(), 5);
assert!(BitIndex::<u32>::new_checked(32).is_none());
}
#[test]
fn consuming_ops() {
let p = PowerOfTwo::<u32>::new(16).unwrap();
assert_eq!(100u32.div_pow2(p), 100 / 16);
assert_eq!(100u32.rem_pow2(p), 100 % 16);
assert!(!100u32.is_multiple_of_pow2(p));
assert!(96u32.is_multiple_of_pow2(p));
let one = PowerOfTwo::<u64>::new(1).unwrap();
assert_eq!(12345u64.div_pow2(one), 12345);
assert_eq!(12345u64.rem_pow2(one), 0);
assert!(12345u64.is_multiple_of_pow2(one));
}
#[test]
fn matches_naive_exhaustive_u8() {
for k in 0..8u32 {
let d = 1u8 << k;
let p = PowerOfTwo::<u8>::new(d).unwrap();
for x in 0..=u8::MAX {
assert_eq!(x.div_pow2(p), x >> k);
assert_eq!(x.rem_pow2(p), x & (d - 1));
assert_eq!(x.is_multiple_of_pow2(p), x % d == 0);
}
}
}
#[test]
fn const_constructor_on_stable() {
const P: Option<PowerOfTwo<u32>> = PowerOfTwo::<u32>::new(256);
const GOT: u32 = match P {
Some(p) => p.get(),
None => 0,
};
assert_eq!(GOT, 256);
}
#[test]
fn next_multiple_of_pow2_works() {
let p = PowerOfTwo::<u32>::new(8).unwrap();
assert_eq!(5u32.next_multiple_of_pow2(p), 8);
assert_eq!(8u32.next_multiple_of_pow2(p), 8);
assert_eq!(9u32.next_multiple_of_pow2(p), 16);
assert_eq!(0u32.next_multiple_of_pow2(p), 0);
assert_eq!(100u32.checked_next_multiple_of_pow2(p), Some(104));
assert_eq!(u32::MAX.checked_next_multiple_of_pow2(p), None);
}
#[test]
fn nonzero_bridge_and_div() {
let d = 17u32.into_nonzero().unwrap();
assert_eq!(u32::nonzero_get(d), 17);
assert_eq!(100u32.div_nonzero(d), 100 / 17);
assert_eq!(100u32.rem_nonzero(d), 100 % 17);
assert!(0u32.into_nonzero().is_none());
}
#[test]
fn sign_typestates() {
assert!(NonNegative::<i32>::new(-1).is_none());
assert_eq!(NonNegative::<i32>::new(5).unwrap().to_unsigned(), 5u32);
assert_eq!(NonNegative::<i32>::new(0).unwrap().abs(), 0);
assert_eq!(NonNegative::<i32>::new(81).unwrap().isqrt(), 9);
assert!(Positive::<i32>::new(0).is_none());
assert!(Positive::<i32>::new(-1).is_none());
let p = Positive::<i32>::new(7).unwrap();
assert_eq!(p.to_unsigned(), 7u32);
assert_eq!(p.into_nonnegative().get(), 7);
assert_eq!(p.into_nonzero().get(), 7);
let v = 5i32;
assert!(NonNegative::<i32>::from_ref(&v).is_some());
let n = -5i32;
assert!(Positive::<i32>::from_ref(&n).is_none());
}
#[test]
fn odd_typestate() {
assert!(Odd::<u32>::new(4).is_none());
assert_eq!(Odd::<u32>::new(7).unwrap().get(), 7);
assert!(Odd::<i32>::new(-3).is_some()); let v = 9u32;
assert!(Odd::from_ref(&v).is_some());
let e = 8u32;
assert!(Odd::from_ref(&e).is_none());
assert!(Even::<u32>::new(4).is_some());
assert!(Even::<u32>::new(7).is_none());
assert_eq!(Even::<u32>::new(8).unwrap().get(), 8);
assert!(Even::from_ref(&8u32).is_some());
let o = Odd::<u32>::new(7).unwrap();
assert_eq!(*o.as_ref(), 7);
let ev = Even::<u32>::new(8).unwrap();
assert_eq!(*ev.as_ref(), 8);
}
#[test]
fn try_from_checked_constructors() {
assert_eq!(Odd::<u32>::try_from(7).map(|o| o.get()), Ok(7));
assert_eq!(Odd::<u32>::try_from(8), Err(TypestateError));
assert_eq!(Even::<u32>::try_from(8).map(|e| e.get()), Ok(8));
let o: Odd<i32> = (-3i32).try_into().unwrap();
assert_eq!(o.get(), -3);
assert_eq!(NonNegative::<i32>::try_from(0).map(|n| n.get()), Ok(0));
assert_eq!(NonNegative::<i32>::try_from(-1), Err(TypestateError));
assert_eq!(Positive::<i32>::try_from(0), Err(TypestateError));
assert_eq!(Positive::<i32>::try_from(5).map(|p| p.get()), Ok(5));
assert_eq!(NonMin::<i8>::try_from(i8::MIN), Err(TypestateError));
assert_eq!(NonMin::<i8>::try_from(5).map(|n| n.get()), Ok(5));
assert_eq!(PowerOfTwo::<u32>::try_from(64).map(|p| p.exp()), Ok(6));
assert!(PowerOfTwo::<u32>::try_from(63).is_err());
assert_eq!(BitIndex::<u64>::try_from(40u32).map(|i| i.get()), Ok(40));
assert!(BitIndex::<u8>::try_from(8u32).is_err());
let x: Finite<f64> = 1.5f64.try_into().unwrap();
assert_eq!(x.get(), 1.5);
assert_eq!(Finite::<f64>::try_from(f64::NAN), Err(TypestateError));
}
#[test]
fn bit_index_ops() {
assert!(BitIndex::<u8>::new(8).is_none()); assert!(BitIndex::<u8>::new(7).is_some());
assert!(BitIndex::<u32>::new(32).is_none());
let i = BitIndex::<u8>::new(3).unwrap();
assert_eq!(i.get(), 3);
assert_eq!(1u8.shl_index(i), 8);
assert_eq!(0x80u8.shr_index(i), 0x10);
let j = BitIndex::<i32>::new(2).unwrap();
assert_eq!((-16i32).shr_index(j), -4);
assert_eq!(3i32.shl_index(j), 12);
for n in 0..8u32 {
let bi = BitIndex::<u8>::new(n).unwrap();
for x in 0..=u8::MAX {
assert_eq!(x.shl_index(bi), x << n);
assert_eq!(x.shr_index(bi), x >> n);
}
}
}
#[test]
fn nonmin_ops() {
assert!(NonMin::<i32>::new(i32::MIN).is_none());
assert!(NonMin::<i8>::new(i8::MIN).is_none());
let a = NonMin::<i32>::new(-7).unwrap();
assert_eq!(a.get(), -7);
assert_eq!(a.abs(), 7);
assert_eq!(a.neg(), 7);
let d = core::num::NonZero::new(2i32).unwrap();
assert_eq!(a.div_nonzero(d), -7 / 2);
assert_eq!(a.rem_nonzero(d), -7 % 2);
let near = NonMin::<i32>::new(i32::MIN + 1).unwrap();
let neg1 = core::num::NonZero::new(-1i32).unwrap();
assert_eq!(near.div_nonzero(neg1), i32::MAX);
assert!(NonMin::<i32>::from_ref(&i32::MIN).is_none());
assert!(NonMin::<i32>::from_ref(&5).is_some());
let p = Positive::<i32>::new(9).unwrap();
assert_eq!(p.into_nonmin().abs(), 9);
let nn = NonNegative::<i32>::new(0).unwrap();
assert_eq!(nn.into_nonmin().neg(), 0);
}
#[test]
fn finite_total_order() {
assert!(Finite::<f64>::new(f64::NAN).is_none());
assert!(Finite::<f64>::new(f64::INFINITY).is_none());
assert!(Finite::<f64>::new(f64::NEG_INFINITY).is_none());
assert!(Finite::<f32>::new(f32::NAN).is_none());
assert!(Finite::<f64>::new(1.5).is_some());
assert!(Finite::<f64>::new(0.0).is_some());
let a = Finite::<f64>::new(1.0).unwrap();
let b = Finite::<f64>::new(2.0).unwrap();
assert!(a < b);
assert_eq!(a.max(b).get(), 2.0);
assert_eq!(a.min(b).get(), 1.0);
assert_eq!(a.cmp(&b), core::cmp::Ordering::Less);
let z = Finite::<f64>::new(0.0).unwrap();
let nz = Finite::<f64>::new(-0.0).unwrap();
assert_eq!(z, nz);
assert_eq!(z.cmp(&nz), core::cmp::Ordering::Equal);
let mut v = [b, a, z];
v.sort();
assert_eq!([v[0].get(), v[1].get(), v[2].get()], [0.0, 1.0, 2.0]);
}
#[test]
fn const_typestate_constructors_on_stable() {
const I: Option<BitIndex<u32>> = BitIndex::<u32>::new(5);
const NM: Option<NonMin<i32>> = NonMin::<i32>::new(-3);
const F: Option<Finite<f64>> = Finite::<f64>::new(1.0);
assert!(I.is_some() && NM.is_some() && F.is_some());
}
#[test]
#[cfg(feature = "ct")]
fn ct_constructors_mask_correctly() {
assert!(bool::from(PowerOfTwo::<u32>::new_ct(16).is_some()));
assert!(bool::from(PowerOfTwo::<u32>::new_ct(15).is_none()));
assert!(bool::from(PowerOfTwo::<u32>::new_ct(0).is_none()));
assert_eq!(PowerOfTwo::<u32>::new_ct(16).unwrap().get(), 16);
assert!(bool::from(NonNegative::<i32>::new_ct(0).is_some()));
assert!(bool::from(NonNegative::<i32>::new_ct(-1).is_none()));
assert!(bool::from(Positive::<i32>::new_ct(0).is_none()));
assert!(bool::from(Positive::<i32>::new_ct(5).is_some()));
assert!(bool::from(Odd::<u32>::new_ct(7).is_some()));
assert!(bool::from(Odd::<u32>::new_ct(8).is_none()));
assert!(bool::from(Even::<u32>::new_ct(8).is_some()));
assert!(bool::from(Even::<u32>::new_ct(7).is_none()));
assert!(bool::from(17u32.into_nonzero_ct().is_some()));
assert!(bool::from(0u32.into_nonzero_ct().is_none()));
assert_eq!(17u32.into_nonzero_ct().unwrap().get(), 17);
assert!(bool::from(NonMin::<i32>::new_ct(5).is_some()));
assert!(bool::from(NonMin::<i32>::new_ct(i32::MIN).is_none()));
assert_eq!(NonMin::<i32>::new_ct(-9).unwrap().get(), -9);
}
}