use crate::{ext::xmpz, misc, ops::NegAssign, Integer};
use az::{Az, Cast, CheckedCast, OverflowingCast, Round, SaturatingCast, WrappingCast};
use core::cmp::Ordering;
macro_rules! cast_int {
($Prim:ty, $fits:path, $get_abs:path) => {
impl Cast<Integer> for $Prim {
#[inline]
fn cast(self) -> Integer {
Integer::from(self)
}
}
impl Cast<$Prim> for Integer {
#[inline]
fn cast(self) -> $Prim {
(&self).cast()
}
}
impl Cast<$Prim> for &'_ Integer {
#[inline]
fn cast(self) -> $Prim {
let (wrapped, overflow) = self.overflowing_cast();
debug_assert!(!overflow, "overflow");
wrapped
}
}
impl CheckedCast<$Prim> for Integer {
#[inline]
fn checked_cast(self) -> Option<$Prim> {
(&self).checked_cast()
}
}
impl CheckedCast<$Prim> for &'_ Integer {
#[inline]
fn checked_cast(self) -> Option<$Prim> {
if $fits(self) {
Some(self.wrapping_cast())
} else {
None
}
}
}
impl SaturatingCast<$Prim> for Integer {
#[inline]
fn saturating_cast(self) -> $Prim {
(&self).saturating_cast()
}
}
impl SaturatingCast<$Prim> for &'_ Integer {
#[inline]
fn saturating_cast(self) -> $Prim {
if $fits(self) {
self.wrapping_cast()
} else if self.cmp0() == Ordering::Less {
<$Prim>::min_value()
} else {
<$Prim>::max_value()
}
}
}
impl WrappingCast<$Prim> for Integer {
#[inline]
fn wrapping_cast(self) -> $Prim {
(&self).wrapping_cast()
}
}
impl WrappingCast<$Prim> for &'_ Integer {
#[inline]
fn wrapping_cast(self) -> $Prim {
let val = $get_abs(self);
if self.cmp0() == Ordering::Less {
val.wrapping_neg()
} else {
val
}
.wrapping_cast()
}
}
impl OverflowingCast<$Prim> for Integer {
#[inline]
fn overflowing_cast(self) -> ($Prim, bool) {
(&self).overflowing_cast()
}
}
impl OverflowingCast<$Prim> for &'_ Integer {
#[inline]
fn overflowing_cast(self) -> ($Prim, bool) {
(self.wrapping_cast(), !$fits(self))
}
}
};
}
impl Cast<Integer> for bool {
#[inline]
fn cast(self) -> Integer {
if self {
Integer::from(1u32)
} else {
Integer::new()
}
}
}
cast_int! { i8, xmpz::fits_i8, xmpz::get_abs_u8 }
cast_int! { i16, xmpz::fits_i16, xmpz::get_abs_u16 }
cast_int! { i32, xmpz::fits_i32, xmpz::get_abs_u32 }
cast_int! { i64, xmpz::fits_i64, xmpz::get_abs_u64 }
cast_int! { i128, xmpz::fits_i128, xmpz::get_abs_u128 }
#[cfg(target_pointer_width = "32")]
cast_int! { isize, xmpz::fits_i32, xmpz::get_abs_u32 }
#[cfg(target_pointer_width = "64")]
cast_int! { isize, xmpz::fits_i64, xmpz::get_abs_u64 }
cast_int! { u8, xmpz::fits_u8, xmpz::get_abs_u8 }
cast_int! { u16, xmpz::fits_u16, xmpz::get_abs_u16 }
cast_int! { u32, xmpz::fits_u32, xmpz::get_abs_u32 }
cast_int! { u64, xmpz::fits_u64, xmpz::get_abs_u64 }
cast_int! { u128, xmpz::fits_u128, xmpz::get_abs_u128 }
#[cfg(target_pointer_width = "32")]
cast_int! { usize, xmpz::fits_u32, xmpz::get_abs_u32 }
#[cfg(target_pointer_width = "64")]
cast_int! { usize, xmpz::fits_u64, xmpz::get_abs_u64 }
impl Cast<Integer> for f32 {
#[inline]
fn cast(self) -> Integer {
self.checked_cast().expect("not finite")
}
}
impl CheckedCast<Integer> for f32 {
#[inline]
fn checked_cast(self) -> Option<Integer> {
let bits = self.to_bits();
let biased_exp = (bits >> 23) & 0xFF;
if biased_exp < 0x7F {
Some(Integer::new())
} else if biased_exp == 0xFF {
None
} else {
let mantissa_with_one = 0x0080_0000 | (bits & 0x007F_FFFF);
let mut val = if biased_exp <= 150 {
Integer::from(mantissa_with_one >> (150 - biased_exp))
} else {
Integer::from(mantissa_with_one) << (biased_exp - 150)
};
if bits & 0x8000_0000 != 0 {
val.neg_assign();
}
Some(val)
}
}
}
impl Cast<f32> for Integer {
#[inline]
fn cast(self) -> f32 {
(&self).cast()
}
}
impl Cast<f32> for &'_ Integer {
#[inline]
fn cast(self) -> f32 {
misc::trunc_f64_to_f32(self.cast())
}
}
impl Cast<Integer> for f64 {
#[inline]
fn cast(self) -> Integer {
self.checked_cast().expect("not finite")
}
}
impl CheckedCast<Integer> for f64 {
#[inline]
fn checked_cast(self) -> Option<Integer> {
let bits = self.to_bits();
let biased_exp = (bits >> 52) & 0x7FF;
if biased_exp < 0x3FF {
Some(Integer::new())
} else if biased_exp == 0x7FF {
None
} else {
let mantissa_with_one = 0x0010_0000_0000_0000 | (bits & 0x000F_FFFF_FFFF_FFFF);
let mut val = if biased_exp <= 1075 {
Integer::from(mantissa_with_one >> (1075 - biased_exp).az::<u32>())
} else {
Integer::from(mantissa_with_one) << (biased_exp - 1075).az::<u32>()
};
if bits & 0x8000_0000_0000_0000 != 0 {
val.neg_assign();
}
Some(val)
}
}
}
impl Cast<f64> for Integer {
#[inline]
fn cast(self) -> f64 {
(&self).cast()
}
}
impl Cast<f64> for &'_ Integer {
#[inline]
fn cast(self) -> f64 {
xmpz::get_f64(self)
}
}
impl Cast<Integer> for Round<f32> {
#[inline]
fn cast(self) -> Integer {
self.checked_cast().expect("not finite")
}
}
impl CheckedCast<Integer> for Round<f32> {
#[inline]
fn checked_cast(self) -> Option<Integer> {
let bits = self.0.to_bits();
let biased_exp = (bits >> 23) & 0xFF;
if biased_exp < 0x7F {
Some(Integer::new())
} else if biased_exp == 0xFF {
None
} else {
let mantissa_with_one = 0x0080_0000 | (bits & 0x007F_FFFF);
let mut val = if biased_exp <= 150 {
let mut round = 1u32 << (150 - biased_exp) >> 1;
if mantissa_with_one & round == 0 || mantissa_with_one & (3 * round - 1) == 0 {
round = 0;
}
Integer::from((mantissa_with_one + round) >> (150 - biased_exp))
} else {
Integer::from(mantissa_with_one) << (biased_exp - 150)
};
if bits & 0x8000_0000 != 0 {
val.neg_assign();
}
Some(val)
}
}
}
impl Cast<Integer> for Round<f64> {
#[inline]
fn cast(self) -> Integer {
self.checked_cast().expect("not finite")
}
}
impl CheckedCast<Integer> for Round<f64> {
#[inline]
fn checked_cast(self) -> Option<Integer> {
let bits = self.0.to_bits();
let biased_exp = (bits >> 52) & 0x7FF;
if biased_exp < 0x3FF {
Some(Integer::new())
} else if biased_exp == 0x7FF {
None
} else {
let mantissa_with_one = 0x0010_0000_0000_0000 | (bits & 0x000F_FFFF_FFFF_FFFF);
let mut val = if biased_exp <= 1075 {
let mut round = 1u64 << (1075 - biased_exp) >> 1;
if mantissa_with_one & round == 0 || mantissa_with_one & (3 * round - 1) == 0 {
round = 0;
}
Integer::from((mantissa_with_one + round) >> (1075 - biased_exp).az::<u32>())
} else {
Integer::from(mantissa_with_one) << (biased_exp - 1075).az::<u32>()
};
if bits & 0x8000_0000_0000_0000 != 0 {
val.neg_assign();
}
Some(val)
}
}
}
#[cfg(test)]
#[allow(clippy::cognitive_complexity, clippy::float_cmp)]
mod tests {
use crate::Integer;
use az::{
Az, Cast, CheckedAs, CheckedCast, OverflowingAs, OverflowingCast, Round, SaturatingAs,
SaturatingCast, WrappingAs, WrappingCast,
};
use core::{borrow::Borrow, f32, f64, fmt::Debug};
#[test]
fn check_bool() {
let zero = Integer::new();
let one = Integer::from(1);
assert_eq!(false.az::<Integer>(), zero);
assert_eq!(true.az::<Integer>(), one);
}
fn check_there_and_back<T>(min: T, max: T)
where
T: Copy + Debug + Eq + Cast<Integer>,
for<'a> &'a Integer:
Cast<T> + CheckedCast<T> + SaturatingCast<T> + WrappingCast<T> + OverflowingCast<T>,
{
let min_int: Integer = min.az::<Integer>();
let max_int: Integer = max.az::<Integer>();
assert_eq!(min_int.borrow().az::<T>(), min);
assert_eq!(max_int.borrow().az::<T>(), max);
assert_eq!(min_int.borrow().checked_as::<T>(), Some(min));
assert_eq!(max_int.borrow().checked_as::<T>(), Some(max));
assert_eq!(min_int.borrow().saturating_as::<T>(), min);
assert_eq!(max_int.borrow().saturating_as::<T>(), max);
assert_eq!(min_int.borrow().wrapping_as::<T>(), min);
assert_eq!(max_int.borrow().wrapping_as::<T>(), max);
assert_eq!(min_int.borrow().overflowing_as::<T>(), (min, false));
assert_eq!(max_int.borrow().overflowing_as::<T>(), (max, false));
let too_small: Integer = min_int - 1;
let too_large: Integer = max_int + 1;
assert_eq!(too_small.borrow().checked_as::<T>(), None);
assert_eq!(too_large.borrow().checked_as::<T>(), None);
assert_eq!(too_small.borrow().saturating_as::<T>(), min);
assert_eq!(too_large.borrow().saturating_as::<T>(), max);
assert_eq!(too_small.borrow().wrapping_as::<T>(), max);
assert_eq!(too_large.borrow().wrapping_as::<T>(), min);
assert_eq!(too_small.borrow().overflowing_as::<T>(), (max, true));
assert_eq!(too_large.borrow().overflowing_as::<T>(), (min, true));
}
#[test]
fn check_integers() {
check_there_and_back(i8::min_value(), i8::max_value());
check_there_and_back(i16::min_value(), i16::max_value());
check_there_and_back(i32::min_value(), i32::max_value());
check_there_and_back(i64::min_value(), i64::max_value());
check_there_and_back(i128::min_value(), i128::max_value());
check_there_and_back(isize::min_value(), isize::max_value());
check_there_and_back(u8::min_value(), u8::max_value());
check_there_and_back(u16::min_value(), u16::max_value());
check_there_and_back(u32::min_value(), u32::max_value());
check_there_and_back(u64::min_value(), u64::max_value());
check_there_and_back(u128::min_value(), u128::max_value());
check_there_and_back(usize::min_value(), usize::max_value());
}
#[test]
fn check_floats() {
let f32_max: Integer = Integer::from((1u32 << 24) - 1) << (127 - 23);
let f64_max: Integer = Integer::from((1u64 << 53) - 1) << (1023 - 52);
assert_eq!(f32::NAN.checked_as::<Integer>(), None);
assert_eq!(f32::NEG_INFINITY.checked_as::<Integer>(), None);
assert_eq!((-f32::MAX).az::<Integer>(), *f32_max.as_neg());
assert_eq!((-2f32).az::<Integer>(), -2);
assert_eq!((-1.99f32).az::<Integer>(), -1);
assert_eq!((-1f32).az::<Integer>(), -1);
assert_eq!((-0.99f32).az::<Integer>(), 0);
assert_eq!(0.99f32.az::<Integer>(), 0);
assert_eq!(1f32.az::<Integer>(), 1);
assert_eq!(1.99f32.az::<Integer>(), 1);
assert_eq!(2f32.az::<Integer>(), 2);
assert_eq!(f32::MAX.az::<Integer>(), f32_max);
assert_eq!(f32::INFINITY.checked_as::<Integer>(), None);
assert_eq!(f64::NAN.checked_as::<Integer>(), None);
assert_eq!(f64::NEG_INFINITY.checked_as::<Integer>(), None);
assert_eq!((-f64::MAX).az::<Integer>(), *f64_max.as_neg());
assert_eq!((-2f64).az::<Integer>(), -2);
assert_eq!((-1.99f64).az::<Integer>(), -1);
assert_eq!((-1f64).az::<Integer>(), -1);
assert_eq!((-0.99f64).az::<Integer>(), 0);
assert_eq!(0.99f64.az::<Integer>(), 0);
assert_eq!(1f64.az::<Integer>(), 1);
assert_eq!(1.99f64.az::<Integer>(), 1);
assert_eq!(2f64.az::<Integer>(), 2);
assert_eq!(f64::MAX.az::<Integer>(), f64_max);
assert_eq!(f64::INFINITY.checked_as::<Integer>(), None);
let zero: Integer = Integer::new();
let one: Integer = Integer::from(1);
let two: Integer = Integer::from(2);
let f32_overflow: Integer = Integer::from(1) << 128;
let f64_overflow: Integer = Integer::from(1) << 1024;
let still_f32_max: Integer = f32_overflow.clone() - 1;
let still_f64_max: Integer = f64_overflow.clone() - 1;
assert_eq!(
(*f32_overflow.as_neg()).borrow().az::<f32>(),
f32::NEG_INFINITY
);
assert_eq!((*still_f32_max.as_neg()).borrow().az::<f32>(), -f32::MAX);
assert_eq!((*f32_max.as_neg()).borrow().az::<f32>(), -f32::MAX);
assert_eq!((*two.as_neg()).borrow().az::<f32>(), -2f32);
assert_eq!((*one.as_neg()).borrow().az::<f32>(), -1f32);
assert_eq!(zero.borrow().az::<f32>(), 0f32);
assert_eq!(one.borrow().az::<f32>(), 1f32);
assert_eq!(two.borrow().az::<f32>(), 2f32);
assert_eq!(f32_max.borrow().az::<f32>(), f32::MAX);
assert_eq!(still_f32_max.borrow().az::<f32>(), f32::MAX);
assert_eq!(f32_overflow.borrow().az::<f32>(), f32::INFINITY);
assert_eq!(
(*f64_overflow.as_neg()).borrow().az::<f64>(),
f64::NEG_INFINITY
);
assert_eq!((*still_f64_max.as_neg()).borrow().az::<f64>(), -f64::MAX);
assert_eq!((*f64_max.as_neg()).borrow().az::<f64>(), -f64::MAX);
assert_eq!((*two.as_neg()).borrow().az::<f64>(), -2f64);
assert_eq!((*one.as_neg()).borrow().az::<f64>(), -1f64);
assert_eq!(zero.borrow().az::<f64>(), 0f64);
assert_eq!(one.borrow().az::<f64>(), 1f64);
assert_eq!(two.borrow().az::<f64>(), 2f64);
assert_eq!(f64_max.borrow().az::<f64>(), f64::MAX);
assert_eq!(f64_overflow.borrow().az::<f64>(), f64::INFINITY);
}
#[test]
fn check_round_floats() {
let f32_max: Integer = Integer::from((1u32 << 24) - 1) << (127 - 23);
let f64_max: Integer = Integer::from((1u64 << 53) - 1) << (1023 - 52);
assert_eq!(Round(f32::NAN).checked_as::<Integer>(), None);
assert_eq!(Round(f32::NEG_INFINITY).checked_as::<Integer>(), None);
assert_eq!(Round(-f32::MAX).az::<Integer>(), *f32_max.as_neg());
assert_eq!(Round(-4f32).az::<Integer>(), -4);
assert_eq!(Round(-3.5f32).az::<Integer>(), -4);
assert_eq!(Round(-3.49f32).az::<Integer>(), -3);
assert_eq!(Round(-2.51f32).az::<Integer>(), -3);
assert_eq!(Round(-2.5f32).az::<Integer>(), -2);
assert_eq!(Round(-2f32).az::<Integer>(), -2);
assert_eq!(Round(-1.5f32).az::<Integer>(), -2);
assert_eq!(Round(-1.49f32).az::<Integer>(), -1);
assert_eq!(Round(-1f32).az::<Integer>(), -1);
assert_eq!(Round(-0.5f32).az::<Integer>(), 0);
assert_eq!(Round(0.5f32).az::<Integer>(), 0);
assert_eq!(Round(1f32).az::<Integer>(), 1);
assert_eq!(Round(1.49f32).az::<Integer>(), 1);
assert_eq!(Round(1.5f32).az::<Integer>(), 2);
assert_eq!(Round(2f32).az::<Integer>(), 2);
assert_eq!(Round(2.5f32).az::<Integer>(), 2);
assert_eq!(Round(2.51f32).az::<Integer>(), 3);
assert_eq!(Round(3.49f32).az::<Integer>(), 3);
assert_eq!(Round(3.5f32).az::<Integer>(), 4);
assert_eq!(Round(4f32).az::<Integer>(), 4);
assert_eq!(Round(f32::MAX).az::<Integer>(), f32_max);
assert_eq!(Round(f32::INFINITY).checked_as::<Integer>(), None);
assert_eq!(Round(f64::NAN).checked_as::<Integer>(), None);
assert_eq!(Round(f64::NEG_INFINITY).checked_as::<Integer>(), None);
assert_eq!(Round(-f64::MAX).az::<Integer>(), *f64_max.as_neg());
assert_eq!(Round(-4f64).az::<Integer>(), -4);
assert_eq!(Round(-3.5f64).az::<Integer>(), -4);
assert_eq!(Round(-3.49f64).az::<Integer>(), -3);
assert_eq!(Round(-2.51f64).az::<Integer>(), -3);
assert_eq!(Round(-2.5f64).az::<Integer>(), -2);
assert_eq!(Round(-2f64).az::<Integer>(), -2);
assert_eq!(Round(-1.5f64).az::<Integer>(), -2);
assert_eq!(Round(-1.49f64).az::<Integer>(), -1);
assert_eq!(Round(-1f64).az::<Integer>(), -1);
assert_eq!(Round(-0.5f64).az::<Integer>(), 0);
assert_eq!(Round(0.5f64).az::<Integer>(), 0);
assert_eq!(Round(1f64).az::<Integer>(), 1);
assert_eq!(Round(1.49f64).az::<Integer>(), 1);
assert_eq!(Round(1.5f64).az::<Integer>(), 2);
assert_eq!(Round(2f64).az::<Integer>(), 2);
assert_eq!(Round(2.5f64).az::<Integer>(), 2);
assert_eq!(Round(2.51f64).az::<Integer>(), 3);
assert_eq!(Round(3.49f64).az::<Integer>(), 3);
assert_eq!(Round(3.5f64).az::<Integer>(), 4);
assert_eq!(Round(4f64).az::<Integer>(), 4);
assert_eq!(Round(f64::MAX).az::<Integer>(), f64_max);
assert_eq!(Round(f64::INFINITY).checked_as::<Integer>(), None);
}
}