use std::convert::TryFrom;
use dashu::{
base::{Approximation, Sign},
float::{
FBig,
round::{
Round, Rounding,
mode::{Down, HalfEven, Up},
},
},
integer::{IBig, UBig},
rational::RBig,
};
use num::{NumCast, One, Zero};
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>;
}
pub trait SaturatingCast<TI>: Sized {
fn saturating_cast(v: TI) -> Self;
}
pub trait CastInternalRational {
fn from_rational(v: RBig) -> Self;
fn into_rational(self) -> Fallible<RBig>;
}
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});
}
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;
}
pub(crate) trait NextFloat {
fn next_up_(self) -> Self;
fn next_down_(self) -> Self;
}
impl NextFloat for f64 {
fn next_up_(self) -> Self {
const TINY_BITS: u64 = 0x1; const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
let bits = self.to_bits();
if self.is_nan() || bits == Self::INFINITY.to_bits() {
return self;
}
let abs = bits & CLEAR_SIGN_MASK;
let next_bits = if abs == 0 {
TINY_BITS
} else if bits == abs {
bits + 1
} else {
bits - 1
};
Self::from_bits(next_bits)
}
fn next_down_(self) -> Self {
const NEG_TINY_BITS: u64 = 0x8000_0000_0000_0001; const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;
let bits = self.to_bits();
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
return self;
}
let abs = bits & CLEAR_SIGN_MASK;
let next_bits = if abs == 0 {
NEG_TINY_BITS
} else if bits == abs {
bits - 1
} else {
bits + 1
};
Self::from_bits(next_bits)
}
}
impl NextFloat for f32 {
fn next_up_(self) -> Self {
const TINY_BITS: u32 = 0x1; const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
let bits = self.to_bits();
if self.is_nan() || bits == Self::INFINITY.to_bits() {
return self;
}
let abs = bits & CLEAR_SIGN_MASK;
let next_bits = if abs == 0 {
TINY_BITS
} else if bits == abs {
bits + 1
} else {
bits - 1
};
Self::from_bits(next_bits)
}
fn next_down_(self) -> Self {
const NEG_TINY_BITS: u32 = 0x8000_0001; const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff;
let bits = self.to_bits();
if self.is_nan() || bits == Self::NEG_INFINITY.to_bits() {
return self;
}
let abs = bits & CLEAR_SIGN_MASK;
let next_bits = if abs == 0 {
NEG_TINY_BITS
} else if bits == abs {
bits - 1
} else {
bits + 1
};
Self::from_bits(next_bits)
}
}
pub trait ToFloatRounded {
fn to_f32_rounded(self) -> f32;
fn to_f64_rounded(self) -> f64;
}
impl ToFloatRounded for FBig<Up> {
fn to_f32_rounded(self) -> f32 {
match self.to_f32() {
Approximation::Exact(v) | Approximation::Inexact(v, Rounding::AddOne) => v,
Approximation::Inexact(v, Rounding::SubOne)
| Approximation::Inexact(v, Rounding::NoOp) => v.next_up_(),
}
}
fn to_f64_rounded(self) -> f64 {
match self.to_f64() {
Approximation::Exact(v) | Approximation::Inexact(v, Rounding::AddOne) => v,
Approximation::Inexact(v, Rounding::SubOne)
| Approximation::Inexact(v, Rounding::NoOp) => v.next_up_(),
}
}
}
impl ToFloatRounded for FBig<Down> {
fn to_f32_rounded(self) -> f32 {
match self.to_f32() {
Approximation::Exact(v) | Approximation::Inexact(v, Rounding::SubOne) => v,
Approximation::Inexact(v, Rounding::AddOne)
| Approximation::Inexact(v, Rounding::NoOp) => v.next_down_(),
}
}
fn to_f64_rounded(self) -> f64 {
match self.to_f64() {
Approximation::Exact(v) | Approximation::Inexact(v, Rounding::SubOne) => v,
Approximation::Inexact(v, Rounding::AddOne)
| Approximation::Inexact(v, Rounding::NoOp) => v.next_down_(),
}
}
}
trait FromFBig<R: Round> {
fn from_fbig(value: FBig<R>) -> Self;
}
impl<R: Round> FromFBig<R> for f32
where
FBig<R>: ToFloatRounded,
{
fn from_fbig(value: FBig<R>) -> Self {
value.to_f32_rounded()
}
}
impl<R: Round> FromFBig<R> for f64
where
FBig<R>: ToFloatRounded,
{
fn from_fbig(value: FBig<R>) -> Self {
value.to_f64_rounded()
}
}
macro_rules! impl_inf_cast_int_float {
($int:ty, $float:ty) => {
impl InfCast<$int> for $float {
fn inf_cast(v_int: $int) -> Fallible<Self> {
Ok(<$float>::from_fbig(FBig::<Up>::from(IBig::from(v_int))))
}
fn neg_inf_cast(v_int: $int) -> Fallible<Self> {
Ok(<$float>::from_fbig(FBig::<Down>::from(IBig::from(v_int))))
}
}
};
}
cartesian!([u8, u16, i8, i16], [f32, f64], impl_inf_cast_from);
cartesian!(
[u64, u128, i64, i128, usize, isize],
[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, usize, i8, i16, i32, i64, i128],
impl_inf_cast_float_int
);
#[cfg(test)]
mod test;
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())
}
}
impl<R: Round> RoundCast<f32> for FBig<R> {
fn round_cast(v: f32) -> Fallible<Self> {
FBig::try_from(v).map_err(|_| err!(FailedCast, "found NaN"))
}
}
impl<R: Round> RoundCast<f64> for FBig<R> {
fn round_cast(v: f64) -> Fallible<Self> {
FBig::try_from(v).map_err(|_| err!(FailedCast, "found NaN"))
}
}
impl<R: Round> RoundCast<FBig<R>> for f32 {
fn round_cast(v: FBig<R>) -> Fallible<Self> {
Ok(v.with_rounding::<HalfEven>().to_f32().value())
}
}
impl<R: Round> RoundCast<FBig<R>> for f64 {
fn round_cast(v: FBig<R>) -> Fallible<Self> {
Ok(v.with_rounding::<HalfEven>().to_f64().value())
}
}
impl RoundCast<RBig> for f32 {
fn round_cast(v: RBig) -> Fallible<Self> {
Ok(v.to_f32().value())
}
}
impl RoundCast<RBig> for f64 {
fn round_cast(v: RBig) -> Fallible<Self> {
Ok(v.to_f64().value())
}
}
impl<R: Round> InfCast<f32> for FBig<R> {
fn inf_cast(v: f32) -> Fallible<Self> {
FBig::try_from(v).map_err(|_| err!(FailedCast, "found NaN"))
}
fn neg_inf_cast(v: f32) -> Fallible<Self> {
FBig::try_from(v).map_err(|_| err!(FailedCast, "found NaN"))
}
}
impl<R: Round> InfCast<f64> for FBig<R> {
fn inf_cast(v: f64) -> Fallible<Self> {
FBig::try_from(v).map_err(|_| err!(FailedCast, "found NaN"))
}
fn neg_inf_cast(v: f64) -> Fallible<Self> {
FBig::try_from(v).map_err(|_| err!(FailedCast, "found NaN"))
}
}
impl<R: Round> InfCast<FBig<R>> for f32 {
fn inf_cast(v: FBig<R>) -> Fallible<Self> {
Ok(v.with_rounding::<Up>().to_f32_rounded())
}
fn neg_inf_cast(v: FBig<R>) -> Fallible<Self> {
Ok(v.with_rounding::<Down>().to_f32_rounded())
}
}
impl<R: Round> InfCast<FBig<R>> for f64 {
fn inf_cast(v: FBig<R>) -> Fallible<Self> {
Ok(v.with_rounding::<Up>().to_f64_rounded())
}
fn neg_inf_cast(v: FBig<R>) -> Fallible<Self> {
Ok(v.with_rounding::<Down>().to_f64_rounded())
}
}
macro_rules! impl_InfCast_dashu {
($($TI:ty),+; $TO:ty, $to:ident) => {
$(impl InfCast<$TI> for $TO {
fn inf_cast(v: $TI) -> Fallible<Self> {
use Approximation::*;
Ok(match v.$to() {
Exact(v) | Inexact(v, Sign::Positive) => v,
Inexact(v, _) => v.next_up_(),
})
}
fn neg_inf_cast(v: $TI) -> Fallible<Self> {
use Approximation::*;
Ok(match v.$to() {
Exact(v) | Inexact(v, Sign::Negative) => v,
Inexact(v, _) => v.next_down_(),
})
}
})+
}
}
impl_InfCast_dashu!(RBig, UBig, IBig; f64, to_f64);
impl_InfCast_dashu!(RBig, UBig, IBig; f32, to_f32);
macro_rules! impl_saturating_cast_ubig_int {
($($T:ty)+) => {$(
impl SaturatingCast<UBig> for $T {
fn saturating_cast(v: UBig) -> Self {
<$T>::try_from(v).unwrap_or(<$T>::MAX)
}
}
)+}
}
impl_saturating_cast_ubig_int! {u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize}
macro_rules! impl_saturating_cast_ibig_uint {
($($T:ty)+) => {$(
impl SaturatingCast<IBig> for $T {
fn saturating_cast(v: IBig) -> Self {
let positive = v > IBig::ZERO;
<$T>::try_from(v).unwrap_or_else(|_| if positive { <$T>::MAX } else { <$T>::MIN })
}
}
)+}
}
impl_saturating_cast_ibig_uint! {i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize}
macro_rules! impl_cast_internal_rational_float {
($ty:ty, $method:ident) => {
impl CastInternalRational for $ty {
fn from_rational(v: RBig) -> Self {
v.$method().value()
}
fn into_rational(self) -> Fallible<RBig> {
RBig::try_from(self).map_err(|_| {
err!(
FailedFunction,
"{} must be representable as a fraction",
self
)
})
}
}
};
}
impl_cast_internal_rational_float!(f32, to_f32);
impl_cast_internal_rational_float!(f64, to_f64);
macro_rules! impl_cast_internal_rational_int {
($($ty:ty)+) => {
$(impl CastInternalRational for $ty {
fn from_rational(v: RBig) -> Self {
<$ty>::try_from(v.round())
.unwrap_or_else(|_| if v > RBig::ZERO { <$ty>::MAX } else { <$ty>::MIN })
}
fn into_rational(self) -> Fallible<RBig> {
Ok(RBig::from(self))
}
})+
};
}
impl_cast_internal_rational_int!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize);