//! Panic-free casting between numeric types.



/// Backend implementation trait for panic-free casting between numeric types.
/// 
/// It is generally recommended to use the [`As`] trait for casting instead of this trait.
pub trait CastFrom<T> {
    #[must_use = crate::doc::must_use_op!()]
    fn cast_from(value: T) -> Self;
}

#[cfg(test)]
pub(crate) trait CastTo<U> {
    fn cast_to(self) -> U;
}

#[cfg(test)]
impl<T, U> CastTo<U> for T
where
    U: CastFrom<T>,
{
    fn cast_to(self) -> U {
        U::cast_from(self)
    }
}

/// Trait which allows panic-free casting between numeric types.
///
/// The behavior matches the behavior of the `as` conversion operator between primitive types. This trait can be used to convert between [`Integer`](crate::Integer)s, as well as between [`Integer`](crate::Integer) and primitive integers. Conversions between primitive integers themselves are also implemented for completeness.
pub trait As {
    /// Casts `self` to type `T`. The [semantics of numeric casting](https://doc.rust-lang.org/reference/expressions/operator-expr.html#semantics) with the `as` operator are followed, so `<T as As>::as_::<U>` can be used in the same way as `T as U` for numeric conversions.
    ///
    /// # Examples
    ///
    /// ```
    /// use bnum::types::{U256, I512, I256, U1024};
    /// use bnum::cast::As;
    ///
    /// // Cast `u64` to `U256`:
    /// let a = 399872465243u64;
    /// let b: U256 = a.as_();
    /// assert_eq!(a.as_::<u16>(), b.as_());
    ///
    /// // Cast `i128` to `I512`:
    /// let c = -2098409234529234584094i128;
    /// let d = c.as_::<I512>();
    /// //assert_eq!(c.as_::<I256>(), d.as_());
    ///
    /// // Cast `I512` to `U1024` (result will be sign-extended with leading ones):
    /// let e: U1024 = d.as_();
    /// assert_eq!(d, e.as_());
    ///
    /// // Cast `U256` to `f64` and back:
    /// let f: f64 = b.as_();
    /// assert_eq!(b, f.as_());
    /// ```
    fn as_<T>(self) -> T
    where
        T: CastFrom<Self>,
        Self: Sized;
}

impl<U> As for U {
    #[inline]
    fn as_<T>(self) -> T
    where
        T: CastFrom<Self>,
        Self: Sized,
    {
        T::cast_from(self)
    }
}

macro_rules! primitive_cast_impl {
    ($from: ty as [$($ty: ty), *]) => {
        $(
            impl CastFrom<$from> for $ty {
                #[inline]
                fn cast_from(value: $from) -> Self {
                    value as Self
                }
            }
        )*
    };
}

macro_rules! multiple_impls {
    ($($from: ty), *) => {
        $(
            primitive_cast_impl!($from as [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64]);

            #[cfg(all(test, nightly))]
            primitive_cast_impl!($from as [f16, f128]);
        )*
    };
}

primitive_cast_impl!(bool as [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, bool]);
primitive_cast_impl!(char as [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, char]);
primitive_cast_impl!(u8 as [u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, char]);
multiple_impls!(
    u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64
);

pub(crate) mod float;
