use super::I256;
use crate::{error::tfie, uint::U256};
use core::num::TryFromIntError;
macro_rules! impl_from {
($($t:ty),* $(,)?) => {$(
impl From<$t> for I256 {
#[inline]
fn from(value: $t) -> Self {
value.as_i256()
}
}
)*};
}
impl_from! {
bool,
i8, i16, i32, i64, i128,
u8, u16, u32, u64, u128,
}
impl TryFrom<U256> for I256 {
type Error = TryFromIntError;
fn try_from(value: U256) -> Result<Self, Self::Error> {
if value > I256::MAX.as_u256() {
return Err(tfie());
}
Ok(value.as_i256())
}
}
pub trait AsI256 {
#[allow(clippy::wrong_self_convention)]
fn as_i256(self) -> I256;
}
impl AsI256 for I256 {
#[inline]
fn as_i256(self) -> I256 {
self
}
}
impl AsI256 for U256 {
#[inline]
fn as_i256(self) -> I256 {
U256::as_i256(self)
}
}
macro_rules! impl_as_i256 {
($($t:ty),* $(,)?) => {$(
impl AsI256 for $t {
#[inline]
fn as_i256(self) -> I256 {
#[allow(unused_comparisons)]
let hi = if self >= 0 { 0 } else { !0 };
I256::from_words(hi, self as _)
}
}
)*};
}
impl_as_i256! {
i8, i16, i32, i64, i128,
u8, u16, u32, u64, u128,
isize, usize,
}
impl AsI256 for bool {
#[inline]
fn as_i256(self) -> I256 {
I256::new(self as _)
}
}
macro_rules! impl_as_i256_float {
($($t:ty [$b:ty]),* $(,)?) => {$(
impl AsI256 for $t {
#[inline]
fn as_i256(self) -> I256 {
const M: $b = (<$t>::MANTISSA_DIGITS - 1) as _;
const MAN_MASK: $b = !(!0 << M);
const MAN_ONE: $b = 1 << M;
const EXP_MASK: $b = !0 >> <$t>::MANTISSA_DIGITS;
const EXP_OFFSET: $b = EXP_MASK / 2;
const ABS_MASK: $b = !0 >> 1;
const SIG_MASK: $b = !ABS_MASK;
let abs = <$t>::from_bits(self.to_bits() & ABS_MASK);
let sign = -(((self.to_bits() & SIG_MASK) >> (<$b>::BITS - 2)) as i128)
.wrapping_sub(1); if abs >= 1.0 {
let bits = abs.to_bits();
let exponent = ((bits >> M) & EXP_MASK) - EXP_OFFSET;
let mantissa = (bits & MAN_MASK) | MAN_ONE;
if exponent <= M {
(I256::from(mantissa >> (M - exponent))) * sign
} else if exponent < 255 {
(I256::from(mantissa) << (exponent - M)) * sign
} else if sign > 0 {
I256::MAX
} else {
I256::MIN
}
} else {
I256::ZERO
}
}
}
)*};
}
impl_as_i256_float! {
f32[u32], f64[u64],
}
macro_rules! impl_try_into {
($($t:ty),* $(,)?) => {$(
impl TryFrom<I256> for $t {
type Error = TryFromIntError;
#[inline]
fn try_from(x: I256) -> Result<Self, Self::Error> {
if x <= <$t>::MAX.as_i256() {
Ok(*x.low() as _)
} else {
Err(tfie())
}
}
}
)*};
}
impl_try_into! {
i8, i16, i32, i64, i128,
u8, u16, u32, u64, u128,
isize, usize,
}
macro_rules! impl_into_float {
($($t:ty => $f:ident),* $(,)?) => {$(
impl From<I256> for $t {
#[inline]
fn from(x: I256) -> $t {
x.$f()
}
}
)*};
}
impl_into_float! {
f32 => as_f32, f64 => as_f64,
}