use crate::is_signed::IsSigned;
use crate::uints::div_ceil_usize;
use crate::{Arbi, Digit};
use core::ops::Shl;
macro_rules! impl_to_integral {
($($t:ty => ($to_unchecked:ident, $to_checked:ident, $fits:ident)),*) => {
$(
impl Arbi {
/// Convert this [`Arbi`] integer to a primitive integer type value.
///
/// This is "wrapping".
///
/// # Note
/// - `From<Arbi>` and `From<&Arbi>` are implemented for each primitive
/// integral type and has the same semantics. See, for example,
/// [`impl From<&Arbi> for i32`](#impl-From<%26Arbi>-for-i32).
pub fn $to_unchecked(&self) -> $t {
type TargetT = $t;
if self.size() == 0 {
return 0;
}
const T_BITS: usize = TargetT::BITS as usize;
const T_IS_SIGNED: bool = TargetT::IS_SIGNED;
const T_BITS_IS_GT_DIGIT_BITS: bool = T_BITS > Digit::BITS as usize;
let mut ret: TargetT = 0;
if T_BITS_IS_GT_DIGIT_BITS {
const MAX_DIGITS_FOR_T: usize =
div_ceil_usize(T_BITS, Digit::BITS as usize);
let n_digits: usize = self.size().min(MAX_DIGITS_FOR_T);
for i in (0..n_digits).rev() {
ret = if T_BITS_IS_GT_DIGIT_BITS {
(ret.shl(Digit::BITS)) | self.vec[i] as TargetT } else {
(ret.shl(Digit::BITS) as Digit | self.vec[i]) as TargetT };
}
} else {
ret = self.vec[0] as TargetT;
}
if T_IS_SIGNED {
if self.is_negative() {
ret = (0 as TargetT).wrapping_sub(ret);
}
} else if self.is_negative() {
ret = (!ret).wrapping_add(1);
}
ret
}
pub fn $to_checked(&self) -> Option<$t> {
if self.$fits() {
Some(self.$to_unchecked())
} else {
None
}
}
pub fn $fits(&self) -> bool {
type TargetT = $t;
(&TargetT::MIN..=&TargetT::MAX).contains(&self)
}
}
#[cfg(test)]
mod $to_unchecked {
#[test]
fn $to_unchecked() {
use super::*;
use crate::util::test::{
get_seedable_rng, get_uniform_die, Distribution,
};
use crate::{QDigit, SQDigit};
let mut arbi = Arbi::new();
assert_eq!(0, arbi.$to_unchecked());
assert_eq!(Some(0), arbi.$to_checked());
arbi = Arbi::from(<$t>::MIN);
assert_eq!(<$t>::MIN, arbi.$to_unchecked());
assert_eq!(Some(<$t>::MIN), arbi.$to_checked());
arbi = Arbi::from(<$t>::MAX);
assert_eq!(<$t>::MAX, arbi.$to_unchecked());
assert_eq!(Some(<$t>::MAX), arbi.$to_checked());
if <$t>::BITS < 128 {
arbi = Arbi::from((<$t>::MIN as i128).wrapping_sub(1));
assert_eq!(
((<$t>::MIN as i128).wrapping_sub(1)) as $t,
arbi.$to_unchecked()
);
assert_eq!(None, arbi.$to_checked());
arbi = Arbi::from((<$t>::MAX as u128).wrapping_add(1));
assert_eq!(
((<$t>::MAX as u128).wrapping_add(1)) as $t,
arbi.$to_unchecked()
);
assert_eq!(None, arbi.$to_checked());
}
let (mut rng, _) = get_seedable_rng();
let die = get_uniform_die(<$t>::MIN, <$t>::MAX);
for _ in 0..i16::MAX {
let rv: $t = die.sample(&mut rng);
let arbi = Arbi::from(rv);
assert_eq!(rv, arbi.$to_unchecked());
assert_eq!(Some(rv), arbi.$to_checked());
}
if <$t>::BITS == 128 && !<$t>::IS_SIGNED {
let die = get_uniform_die(QDigit::MIN, QDigit::MAX);
for _ in 0..i16::MAX {
let rv: QDigit = die.sample(&mut rng);
let arbi = Arbi::from(rv);
assert_eq!(rv as $t, arbi.$to_unchecked());
if (rv >= (<$t>::MIN as QDigit))
&& (rv <= (<$t>::MAX as QDigit))
{
assert!(arbi.$fits());
assert_eq!(Some(rv as $t), arbi.$to_checked());
} else {
assert!(!arbi.$fits());
assert_eq!(None, arbi.$to_checked());
}
}
} else {
let die = get_uniform_die(SQDigit::MIN, SQDigit::MAX);
for _ in 0..i16::MAX {
let rv: SQDigit = die.sample(&mut rng);
let arbi = Arbi::from(rv);
assert_eq!(rv as $t, arbi.$to_unchecked());
if (rv >= (<$t>::MIN as SQDigit))
&& (rv <= (<$t>::MAX as SQDigit))
{
assert!(arbi.$fits());
assert_eq!(Some(rv as $t), arbi.$to_checked());
} else {
assert!(!arbi.$fits());
assert_eq!(None, arbi.$to_checked());
}
}
}
}
}
impl From<Arbi> for $t {
fn from(arbi: Arbi) -> Self {
arbi.$to_unchecked()
}
}
impl From<&Arbi> for $t {
fn from(arbi: &Arbi) -> Self {
arbi.$to_unchecked()
}
}
)*
};
}
impl_to_integral!(
i8 => (wrapping_to_i8, checked_to_i8, fits_i8),
i16 => (wrapping_to_i16, checked_to_i16, fits_i16),
i32 => (wrapping_to_i32, checked_to_i32, fits_i32),
i64 => (wrapping_to_i64, checked_to_i64, fits_i64),
i128 => (wrapping_to_i128, checked_to_i128, fits_i128),
isize => (wrapping_to_isize, checked_to_isize, fits_isize),
u8 => (wrapping_to_u8, checked_to_u8, fits_u8),
u16 => (wrapping_to_u16, checked_to_u16, fits_u16),
u32 => (wrapping_to_u32, checked_to_u32, fits_u32),
u64 => (wrapping_to_u64, checked_to_u64, fits_u64),
u128 => (wrapping_to_u128, checked_to_u128, fits_u128),
usize => (wrapping_to_usize, checked_to_usize, fits_usize)
);