use std::convert::TryFrom;
use num::{NumCast, One, Zero};
#[cfg(feature = "use-mpfr")]
use rug::Float;
use crate::error::Fallible;
pub trait ExactIntCast<TI>: Sized + ExactIntBounds {
fn exact_int_cast(v: TI) -> Fallible<Self>;
}
pub trait ExactIntBounds {
const MAX_CONSECUTIVE: Self;
const MIN_CONSECUTIVE: Self;
}
pub trait InfCast<TI>: Sized {
fn inf_cast(v: TI) -> Fallible<Self>;
fn neg_inf_cast(v: TI) -> Fallible<Self>;
}
pub trait RoundCast<TI>: Sized {
fn round_cast(v: TI) -> Fallible<Self>;
}
macro_rules! cartesian {
(@[$(($a1:tt, $a2:tt))*] [] $b:tt $init_b:tt $submacro:tt) =>
($($submacro!{$a1, $a2})*);
(@$out:tt [$a:tt, $($at:tt)*] [] $init_b:tt $submacro:tt) =>
(cartesian!{@$out [$($at)*] $init_b $init_b $submacro});
(@[$($out:tt)*] [$a:tt, $($at:tt)*] [$b:tt, $($bt:tt)*] $init_b:tt $submacro:tt) =>
(cartesian!{@[$($out)* ($a, $b)] [$a, $($at)*] [$($bt)*] $init_b $submacro});
(@diag[$($start_a:tt),*], [$mid_a:tt, $($end_a:tt),*], [$($start_b:tt),*], [$mid_b:tt, $($end_b:tt),*], $lower:tt, $diag:tt, $upper:tt) => {
$($lower!($mid_a, $start_b);)*
$diag!($mid_a, $mid_b);
$($upper!($mid_a, $end_b);)*
cartesian!{@diag[$($start_a,)* $mid_a], [$($end_a),*], [$($start_b,)* $mid_b], [$($end_b),*], $lower, $diag, $upper}
};
(@diag[$($start_a:tt),*], [$last_a:tt], [$($start_b:tt),*], [$last_b:tt], $lower:tt, $diag:tt, $upper:tt) => {
$($lower!($last_a, $start_b);)*
$diag!($last_a, $last_b);
};
([$($a:tt)*], [$($b:tt)*], $submacro:tt) =>
(cartesian!{@[] [$($a)*,] [$($b)*,] [$($b)*,] $submacro});
([$($a:tt)*], $submacro:tt) =>
(cartesian!{@[] [$($a)*,] [$($a)*,] [$($a)*,] $submacro});
([$($a:tt)*], [$($b:tt)*], $lower:tt, $diag:tt, $upper:tt) =>
(cartesian!{@diag[], [$($a)*], [], [$($b)*], $lower, $diag, $upper});
([$($a:tt)*], $lower:tt, $diag:tt, $upper:tt) =>
(cartesian!{@diag[], [$($a)*], [], [$($a)*], $lower, $diag, $upper});
}
pub(crate) use cartesian;
macro_rules! impl_exact_int_cast_from {
($ti:ty, $to:ty) => {
impl ExactIntCast<$ti> for $to {
#[inline]
fn exact_int_cast(v: $ti) -> Fallible<Self> {
Ok(From::from(v))
}
}
};
}
macro_rules! impl_exact_int_cast_try_from {
($ti:ty, $to:ty) => {
impl ExactIntCast<$ti> for $to {
fn exact_int_cast(v: $ti) -> Fallible<Self> {
TryFrom::try_from(v).map_err(|e| err!(FailedCast, "{:?}", e))
}
}
};
}
cartesian! {[u8, u16, u32, u64, u128], impl_exact_int_cast_try_from, impl_exact_int_cast_from, impl_exact_int_cast_from}
cartesian!(
[u8, u16, u32, u64, u128],
[i8, i16, i32, i64, i128],
impl_exact_int_cast_try_from,
impl_exact_int_cast_try_from,
impl_exact_int_cast_from
);
cartesian!(
[i8, i16, i32, i64, i128],
[u8, u16, u32, u64, u128],
impl_exact_int_cast_try_from
);
cartesian! {[i8, i16, i32, i64, i128], impl_exact_int_cast_try_from, impl_exact_int_cast_from, impl_exact_int_cast_from}
macro_rules! impl_exact_int_cast_int_float {
($int:ty, $float:ty) => (impl ExactIntCast<$int> for $float {
fn exact_int_cast(v_int: $int) -> Fallible<Self> {
let v_float = v_int as $float;
if !(<$float>::MIN_CONSECUTIVE..<$float>::MAX_CONSECUTIVE).contains(&v_float) {
fallible!(FailedCast, "exact_int_cast: integer is outside of consecutive integer bounds and may be subject to rounding")
} else {
Ok(v_float)
}
}
})
}
cartesian!([u8, u16, i8, i16], [f32, f64], impl_exact_int_cast_from);
cartesian!(
[u64, u128, i64, i128, usize, isize],
[f32, f64],
impl_exact_int_cast_int_float
);
impl_exact_int_cast_int_float!(u32, f32);
impl_exact_int_cast_from!(u32, f64);
impl_exact_int_cast_int_float!(i32, f32);
impl_exact_int_cast_from!(i32, f64);
cartesian!(
[usize, isize],
[u8, u16, u32, u64, u128, i8, i16, i32, i64, i128],
impl_exact_int_cast_try_from
);
cartesian!(
[u8, u16, u32, u64, u128, i8, i16, i32, i64, i128],
[usize, isize],
impl_exact_int_cast_try_from
);
impl_exact_int_cast_from!(usize, usize);
impl_exact_int_cast_from!(isize, isize);
impl_exact_int_cast_try_from!(usize, isize);
impl_exact_int_cast_try_from!(isize, usize);
macro_rules! impl_inf_cast_exact {
($ti:ty, $to:ty) => {
impl InfCast<$ti> for $to {
fn inf_cast(v: $ti) -> Fallible<Self> {
ExactIntCast::exact_int_cast(v)
}
fn neg_inf_cast(v: $ti) -> Fallible<Self> {
ExactIntCast::exact_int_cast(v)
}
}
};
}
cartesian!(
[u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize],
impl_inf_cast_exact
);
macro_rules! impl_inf_cast_from {
($ti:ty, $to:ty) => {
impl InfCast<$ti> for $to {
#[inline]
fn inf_cast(v: $ti) -> Fallible<Self> {
Ok(From::from(v))
}
fn neg_inf_cast(v: $ti) -> Fallible<Self> {
Ok(From::from(v))
}
}
};
}
macro_rules! impl_exact_int_bounds {
($($ty:ty),*) => ($(impl ExactIntBounds for $ty {
const MAX_CONSECUTIVE: Self = Self::MAX;
const MIN_CONSECUTIVE: Self = Self::MIN;
})*)
}
impl_exact_int_bounds!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
impl ExactIntBounds for f64 {
const MAX_CONSECUTIVE: Self = 9_007_199_254_740_992.0;
const MIN_CONSECUTIVE: Self = -9_007_199_254_740_992.0;
}
impl ExactIntBounds for f32 {
const MAX_CONSECUTIVE: Self = 16_777_216.0;
const MIN_CONSECUTIVE: Self = -16_777_216.0;
}
macro_rules! impl_inf_cast_int_float {
($int:ty, $float:ty) => {
#[cfg(feature = "use-mpfr")]
impl InfCast<$int> for $float {
fn inf_cast(v_int: $int) -> Fallible<Self> {
use rug::{float::Round, Float};
let float = Float::with_val_round(Self::MANTISSA_DIGITS, v_int, Round::Up).0;
Self::inf_cast(float)
}
fn neg_inf_cast(v_int: $int) -> Fallible<Self> {
use rug::{float::Round, Float};
let float = Float::with_val_round(Self::MANTISSA_DIGITS, v_int, Round::Down).0;
Self::neg_inf_cast(float)
}
}
#[cfg(not(feature = "use-mpfr"))]
impl InfCast<$int> for $float {
fn inf_cast(v_int: $int) -> Fallible<Self> {
Self::exact_int_cast(v_int)
}
fn neg_inf_cast(v_int: $int) -> Fallible<Self> {
Self::exact_int_cast(v_int)
}
}
};
}
cartesian!([u8, u16, i8, i16], [f32, f64], impl_inf_cast_from);
cartesian!(
[u64, u128, i64, i128, usize],
[f32, f64],
impl_inf_cast_int_float
);
impl_inf_cast_int_float!(u32, f32);
impl_inf_cast_from!(u32, f64);
impl_inf_cast_int_float!(i32, f32);
impl_inf_cast_from!(i32, f64);
impl_inf_cast_from!(f32, f32);
impl_inf_cast_from!(f32, f64);
impl InfCast<f64> for f32 {
fn inf_cast(vf64: f64) -> Fallible<Self> {
if vf64.is_nan() {
return Ok(f32::NAN);
}
let vf32 = vf64 as f32;
if vf64 > vf32 as f64 {
return Ok(f32::from_bits(if vf32.is_sign_negative() {
vf32.to_bits() - 1
} else {
vf32.to_bits() + 1
}));
}
Ok(vf32)
}
fn neg_inf_cast(vf64: f64) -> Fallible<Self> {
if vf64.is_nan() {
return Ok(f32::NAN);
}
let vf32 = vf64 as f32;
if vf64 < vf32 as f64 {
return Ok(f32::from_bits(if vf32.is_sign_negative() {
vf32.to_bits() + 1
} else {
vf32.to_bits() - 1
}));
}
Ok(vf32)
}
}
impl_inf_cast_from!(f64, f64);
macro_rules! impl_inf_cast_float_int {
($ti:ty, $to:ty) => (impl InfCast<$ti> for $to {
fn inf_cast(mut v: $ti) -> Fallible<Self> {
v = v.ceil();
if Self::MIN as $ti > v || Self::MAX as $ti < v {
fallible!(FailedCast, "Failed to cast float to int. Float value is outside of range.")
} else {
Ok(v as Self)
}
}
fn neg_inf_cast(mut v: $ti) -> Fallible<Self> {
v = v.floor();
if Self::MIN as $ti > v || Self::MAX as $ti < v {
fallible!(FailedCast, "Failed to cast float to int. Float value is outside of range.")
} else {
Ok(v as Self)
}
}
})
}
cartesian!(
[f32, f64],
[u8, u16, u32, u64, u128, i8, i16, i32, i64, i128],
impl_inf_cast_float_int
);
#[cfg(test)]
mod test_inf_cast {
use crate::traits::InfCast;
#[allow(dead_code)]
enum Diff {
Equal,
Prev,
Next,
Less,
Greater,
}
fn check_rounded_cast(input: f64, diff: Diff) {
let casted = f32::inf_cast(input).unwrap() as f64;
if input.is_nan() {
assert!(casted.is_nan());
return;
}
let error = match diff {
Diff::Equal => (casted != input).then(|| "casted value must be equal to input"),
Diff::Greater => {
(casted <= input).then(|| "casted value must be greater than input value")
}
Diff::Less => (casted >= input).then(|| "casted value must be less than input value"),
Diff::Next => (f64::from_bits(input.to_bits() + 1) != casted)
.then(|| "casted must be one step greater than input"),
Diff::Prev => (f64::from_bits(input.to_bits() - 1) != casted)
.then(|| "casted must be one step less than input"),
};
if let Some(message) = error {
println!("bits {:064b}", input.to_bits());
println!("input {}", input);
println!("output {}", casted);
panic!("{}", message)
}
}
#[test]
#[ignore]
fn test_f64_f32() {
check_rounded_cast(0., Diff::Equal);
check_rounded_cast(f64::MIN_POSITIVE, Diff::Greater);
check_rounded_cast(1.9999999999999998, Diff::Next);
for u32_bits in 1..u32::MAX / 2 {
let f64_value = f32::from_bits(u32_bits) as f64;
let u64_bits = f64_value.to_bits();
if u32_bits % 100_000_000 == 0 {
println!("checkpoint every 300 million tests: {}", f64_value);
}
check_rounded_cast(f64_value, Diff::Equal);
check_rounded_cast(f64::from_bits(u64_bits - 1), Diff::Next);
check_rounded_cast(f64::from_bits(u64_bits + 1), Diff::Greater);
}
}
}
macro_rules! impl_round_cast_num {
($TI:ty, $TO:ty) => {
impl RoundCast<$TI> for $TO {
fn round_cast(v: $TI) -> Fallible<Self> {
<$TO as NumCast>::from(v).ok_or_else(|| err!(FailedCast))
}
}
};
}
macro_rules! impl_round_cast_self_string_bool {
($T:ty, $_T:ty) => {
impl RoundCast<$T> for $T {
fn round_cast(v: $T) -> Fallible<Self> {
Ok(v)
}
}
impl RoundCast<bool> for $T {
fn round_cast(v: bool) -> Fallible<Self> {
Ok(if v { Self::one() } else { Self::zero() })
}
}
impl RoundCast<$T> for bool {
fn round_cast(v: $T) -> Fallible<Self> {
Ok(!v.is_zero())
}
}
impl RoundCast<String> for $T {
fn round_cast(v: String) -> Fallible<Self> {
v.parse::<$T>().map_err(|_e| err!(FailedCast))
}
}
impl RoundCast<$T> for String {
fn round_cast(v: $T) -> Fallible<Self> {
Ok(v.to_string())
}
}
};
}
cartesian! {[u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64], impl_round_cast_num, impl_round_cast_self_string_bool, impl_round_cast_num}
impl RoundCast<bool> for bool {
fn round_cast(v: bool) -> Fallible<Self> {
Ok(v)
}
}
impl RoundCast<String> for String {
fn round_cast(v: String) -> Fallible<Self> {
Ok(v)
}
}
impl RoundCast<String> for bool {
fn round_cast(v: String) -> Fallible<Self> {
Ok(!v.is_empty())
}
}
impl RoundCast<bool> for String {
fn round_cast(v: bool) -> Fallible<Self> {
Ok(v.to_string())
}
}
#[cfg(feature = "use-mpfr")]
impl InfCast<f32> for Float {
fn inf_cast(v: f32) -> Fallible<Self> {
Ok(Float::with_val_round(f32::MANTISSA_DIGITS, v, rug::float::Round::Up).0)
}
fn neg_inf_cast(v: f32) -> Fallible<Self> {
Ok(Float::with_val_round(f32::MANTISSA_DIGITS, v, rug::float::Round::Down).0)
}
}
#[cfg(feature = "use-mpfr")]
impl InfCast<f64> for Float {
fn inf_cast(v: f64) -> Fallible<Self> {
Ok(Float::with_val_round(f64::MANTISSA_DIGITS, v, rug::float::Round::Up).0)
}
fn neg_inf_cast(v: f64) -> Fallible<Self> {
Ok(Float::with_val_round(f64::MANTISSA_DIGITS, v, rug::float::Round::Down).0)
}
}
#[cfg(feature = "use-mpfr")]
impl InfCast<Float> for f32 {
fn inf_cast(v: Float) -> Fallible<Self> {
Ok(v.to_f32_round(rug::float::Round::Up))
}
fn neg_inf_cast(v: Float) -> Fallible<Self> {
Ok(v.to_f32_round(rug::float::Round::Down))
}
}
#[cfg(feature = "use-mpfr")]
impl InfCast<Float> for f64 {
fn inf_cast(v: Float) -> Fallible<Self> {
Ok(v.to_f64_round(rug::float::Round::Up))
}
fn neg_inf_cast(v: Float) -> Fallible<Self> {
Ok(v.to_f64_round(rug::float::Round::Down))
}
}