#![deny(missing_docs)]
#![deny(warnings)]
#![allow(const_err)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "x128", feature(i128_type, i128))]
#[cfg(feature = "std")]
extern crate core;
#[cfg(test)]
#[macro_use]
extern crate quickcheck;
use core::fmt;
#[cfg(feature="std")]
use std::error;
#[cfg(test)]
mod test;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Error {
    
    Infinite,
    
    NaN,
    
    
    Overflow,
    
    
    Underflow,
}
impl Error {
    
    
    fn description_helper(&self) -> &str {
        match *self {
            Error::Infinite => "Cannot store infinite value in finite type",
            Error::NaN => "Cannot store NaN in type which does not support it",
            Error::Overflow => "Overflow during numeric conversion",
            Error::Underflow => "Underflow during numeric conversion",
        }
    }
}
impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description_helper())
    }
}
#[cfg(feature="std")]
impl error::Error for Error {
    fn description(&self) -> &str {
        self.description_helper()
    }
}
pub trait From<Src> {
    
    type Output;
    
    fn cast(Src) -> Self::Output;
}
macro_rules! fns {
    ($($ty:ident),+) => {
        $(
            
            #[inline]
            pub fn $ty<T>(x: T) -> <$ty as From<T>>::Output
                where $ty: From<T>
            {
                <$ty as From<T>>::cast(x)
            }
         )+
    }
}
fns!(f32, f64, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
#[cfg(feature = "x128")]
fns!(i128, u128);
macro_rules! promotion {
    ($($src:ty => $($dst: ty),+);+;) => {
        $(
            $(
                impl From<$src> for $dst {
                    type Output = $dst;
                    #[inline]
                    fn cast(src: $src) -> $dst {
                        src as $dst
                    }
                }
            )+
        )+
    }
}
macro_rules! half_promotion {
    ($($src:ty => $($dst:ty),+);+;) => {
        $(
            $(
                impl From<$src> for $dst {
                    type Output = Result<$dst, Error>;
                    #[inline]
                    fn cast(src: $src) -> Self::Output {
                        if src < 0 {
                            Err(Error::Underflow)
                        } else {
                            Ok(src as $dst)
                        }
                    }
                }
            )+
        )+
    }
}
macro_rules! from_unsigned {
    ($($src:ident => $($dst:ident),+);+;) => {
        $(
            $(
                impl From<$src> for $dst {
                    type Output = Result<$dst, Error>;
                    #[inline]
                    fn cast(src: $src) -> Self::Output {
                        use core::$dst;
                        if src > $dst::MAX as $src {
                            Err(Error::Overflow)
                        } else {
                            Ok(src as $dst)
                        }
                    }
                }
            )+
        )+
    }
}
macro_rules! from_signed {
    ($($src:ident => $($dst:ident),+);+;) => {
        $(
            $(
                impl From<$src> for $dst {
                    type Output = Result<$dst, Error>;
                    #[inline]
                    fn cast(src: $src) -> Self::Output {
                        use core::$dst;
                        Err(if src < $dst::MIN as $src {
                            Error::Underflow
                        } else if src > $dst::MAX as $src {
                            Error::Overflow
                        } else {
                            return Ok(src as $dst);
                        })
                    }
                }
            )+
        )+
    }
}
macro_rules! from_float {
    ($($src:ident => $($dst:ident),+);+;) => {
        $(
            $(
                impl From<$src> for $dst {
                    type Output = Result<$dst, Error>;
                    #[inline]
                    fn cast(src: $src) -> Self::Output {
                        use core::{$dst, $src};
                        Err(if src != src {
                            Error::NaN
                        } else if src == $src::INFINITY ||
                            src == $src::NEG_INFINITY {
                            Error::Infinite
                        } else if src < $dst::MIN as $src {
                            Error::Underflow
                        } else if src > $dst::MAX as $src {
                            Error::Overflow
                        } else {
                            return Ok(src as $dst);
                        })
                    }
                }
            )+
        )+
    }
}
macro_rules! from_float_dst {
    ($($src:ident => $($dst:ident),+);+;) => {
        $(
            $(
                impl From<$src> for $dst {
                    type Output = Result<$dst, Error>;
                    #[inline]
                    #[allow(unused_comparisons)]
                    fn cast(src: $src) -> Self::Output {
                        use core::{$dst, $src};
                        Err(if src != src {
                            Error::NaN
                        } else if src == $src::INFINITY ||
                            src == $src::NEG_INFINITY {
                            Error::Infinite
                        } else if ($dst::MIN == 0) && src < 0.0 {
                            Error::Underflow
                        } else {
                            return Ok(src as $dst);
                        })
                    }
                }
            )+
        )+
    }
}
#[cfg(target_pointer_width = "32")]
mod _32 {
    use {Error, From};
    
    promotion! {
        i8    => f32, f64, i8, i16, i32, isize, i64;
        i16   => f32, f64,     i16, i32, isize, i64;
        i32   => f32, f64,          i32, isize, i64;
        isize => f32, f64,          i32, isize, i64;
        i64   => f32, f64,                      i64;
    }
    half_promotion! {
        i8    =>                                     u8, u16, u32, usize, u64;
        i16   =>                                         u16, u32, usize, u64;
        i32   =>                                              u32, usize, u64;
        isize =>                                              u32, usize, u64;
        i64   =>                                                          u64;
    }
    from_signed! {
        i16   =>           i8,                       u8;
        i32   =>           i8, i16,                  u8, u16;
        isize =>           i8, i16,                  u8, u16;
        i64   =>           i8, i16, i32, isize,      u8, u16, u32, usize;
    }
    
    promotion! {
        u8    => f32, f64,     i16, i32, isize, i64, u8, u16, u32, usize, u64;
        u16   => f32, f64,          i32, isize, i64,     u16, u32, usize, u64;
        u32   => f32, f64,                      i64,          u32, usize, u64;
        usize => f32, f64,                      i64,          u32, usize, u64;
        u64   => f32, f64,                                                u64;
    }
    from_unsigned! {
        u8    =>           i8;
        u16   =>           i8, i16,                  u8;
        u32   =>           i8, i16, i32, isize,      u8, u16;
        usize =>           i8, i16, i32, isize,      u8, u16;
        u64   =>           i8, i16, i32, isize, i64, u8, u16, u32, usize;
    }
    
    promotion! {
        f32   => f32, f64;
        f64   =>      f64;
    }
    from_float! {
        f32   =>           i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
        f64   =>           i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
    }
}
#[cfg(target_pointer_width = "64")]
mod _64 {
    use {Error, From};
    
    promotion! {
        i8    => f32, f64, i8, i16, i32, i64, isize;
        i16   => f32, f64,     i16, i32, i64, isize;
        i32   => f32, f64,          i32, i64, isize;
        i64   => f32, f64,               i64, isize;
        isize => f32, f64,               i64, isize;
    }
    half_promotion! {
        i8    =>                                     u8, u16, u32, u64, usize;
        i16   =>                                         u16, u32, u64, usize;
        i32   =>                                              u32, u64, usize;
        i64   =>                                                   u64, usize;
        isize =>                                                   u64, usize;
    }
    from_signed! {
        i16   =>           i8,                       u8;
        i32   =>           i8, i16,                  u8, u16;
        i64   =>           i8, i16, i32,             u8, u16, u32;
        isize =>           i8, i16, i32,             u8, u16, u32;
    }
    
    promotion! {
        u8    => f32, f64,     i16, i32, i64, isize, u8, u16, u32, u64, usize;
        u16   => f32, f64,          i32, i64, isize,     u16, u32, u64, usize;
        u32   => f32, f64,               i64, isize,          u32, u64, usize;
        u64   => f32, f64,                                         u64, usize;
        usize => f32, f64,                                         u64, usize;
    }
    from_unsigned! {
        u8    =>           i8;
        u16   =>           i8, i16,                  u8;
        u32   =>           i8, i16, i32,             u8, u16;
        u64   =>           i8, i16, i32, i64, isize, u8, u16, u32;
        usize =>           i8, i16, i32, i64, isize, u8, u16, u32;
    }
    
    promotion! {
        f32  => f32, f64;
        f64  =>      f64;
    }
    from_float! {
        f32  =>           i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
        f64  =>           i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
    }
}
#[cfg(feature = "x128")]
mod _x128 {
    use {Error, From};
    
    promotion! {
        i8    =>                              i128;
        i16   =>                              i128;
        i32   =>                              i128;
        i64   =>                              i128;
        isize =>                              i128;
        i128  => f32, f64,                    i128;
    }
    half_promotion! {
        i8    =>                                                              u128;
        i16   =>                                                              u128;
        i32   =>                                                              u128;
        i64   =>                                                              u128;
        isize =>                                                              u128;
        i128  =>                                                              u128;
    }
    from_signed! {
        i128  =>           i8, i16, i32, i64,       isize, u8, u16, u32, u64,       usize;
    }
    
    promotion! {
        u8    =>                              i128,                           u128;
        u16   =>                              i128,                           u128;
        u32   =>                              i128,                           u128;
        u64   =>                              i128,                           u128;
        usize =>                              i128,                           u128;
        u128  =>      f64,                                                    u128;
    }
    from_unsigned! {
        u128 => f32,       i8, i16, i32, i64, i128, isize, u8, u16, u32, u64,       usize;
    }
    
    from_float! {
        f32  => i128;
        f64  => i128, u128;
    }
    from_float_dst! {
        f32  => u128;
    }
}
impl From<f64> for f32 {
    type Output = Result<f32, Error>;
    #[inline]
    fn cast(src: f64) -> Self::Output {
        use core::{f32, f64};
        if src != src || src == f64::INFINITY || src == f64::NEG_INFINITY {
            Ok(src as f32)
        } else if src < f32::MIN as f64 {
            Err(Error::Underflow)
        } else if src > f32::MAX as f64 {
            Err(Error::Overflow)
        } else {
            Ok(src as f32)
        }
    }
}