polynomen 1.0.0

Polynomial library
Documentation
//! Numerical traits

/// Absolute value trait
pub trait Abs {
    /// Absolute value
    fn abs(&self) -> Self;
}

/// Additive identity trait
pub trait Zero {
    /// Additive identity
    fn zero() -> Self;
    /// Check if `self` is the additive identity
    fn is_zero(&self) -> bool;
}

/// Multiplicative identity trait
pub trait One {
    /// Multiplicative identity
    fn one() -> Self;
    /// Check if `self` is the multiplicative identity
    fn is_one(&self) -> bool;
}

/// Type conversion
pub trait NumCast: Sized {
    /// Convert from type `T`
    fn from(n: usize) -> Option<Self>;
}

/// Constants trait
pub trait Const {
    /// Twice the greek pi
    fn tau() -> Self;
}

/// Reciprocal of a number trait
pub trait Inv {
    /// Reciprocal of a number
    fn inv(&self) -> Self;
}

/// Logarithm of a number trait
pub trait Ln {
    /// Natural logarithm of a number
    fn ln(&self) -> Self;
}

/// Cosine of a number trait
pub trait Cos {
    /// Cosine of a number
    fn cos(&self) -> Self;
}

/// Sine of a number trait
pub trait Sin {
    /// Sine of a number
    fn sin(&self) -> Self;
}

/// Exponent of a number trait
pub trait Pow {
    /// Exponent of a number
    fn powf(&self, exp: Self) -> Self;
    /// Exponent of a number with integer exponent
    fn powi(&self, exp: i32) -> Self;
}

/// Sign of a number trait
pub trait Sign {
    /// Return 1 for positive, 0 for zero, -1 for negative
    /// If the number has +0.0 and -0.0, it does not return 0
    fn signum(&self) -> Self;
    /// Return true if the number si negative
    fn is_sign_negative(&self) -> bool;
}

/// Square root of a number trait
pub trait Sqrt {
    /// Square root of a number
    fn sqrt(&self) -> Self;
}

/// Maximum of two numbers trait
pub trait Max {
    /// Maximum of two numbers
    fn max(self, other: Self) -> Self;
}

/// Epsilon trait
pub trait Epsilon {
    /// Epsilon: 1 + ε = 1
    fn epsilon() -> Self;
}

macro_rules! impl_trait_float {
    ($t:ty, $id:ident, $z:expr, $o:expr) => {
        impl Abs for $t {
            fn abs(&self) -> Self {
                <$t>::abs(*self)
            }
        }
        impl Cos for $t {
            fn cos(&self) -> Self {
                <$t>::cos(*self)
            }
        }
        impl Inv for $t {
            fn inv(&self) -> Self {
                <$t>::recip(*self)
            }
        }
        impl Ln for $t {
            fn ln(&self) -> Self {
                <$t>::ln(*self)
            }
        }
        impl Max for $t {
            fn max(self, other: Self) -> Self {
                <$t>::max(self, other)
            }
        }
        impl Pow for $t {
            fn powf(&self, exp: Self) -> Self {
                <$t>::powf(*self, exp)
            }
            fn powi(&self, exp: i32) -> Self {
                <$t>::powi(*self, exp)
            }
        }
        impl Sign for $t {
            fn signum(&self) -> Self {
                <$t>::signum(*self)
            }
            fn is_sign_negative(&self) -> bool {
                <$t>::is_sign_negative(*self)
            }
        }
        impl Sin for $t {
            fn sin(&self) -> Self {
                <$t>::sin(*self)
            }
        }
        impl Sqrt for $t {
            fn sqrt(&self) -> Self {
                <$t>::sqrt(*self)
            }
        }
        impl Zero for $t {
            fn zero() -> Self {
                $z
            }
            fn is_zero(&self) -> bool {
                *self == $z
            }
        }
        impl One for $t {
            fn one() -> Self {
                $o
            }
            #[allow(clippy::float_cmp)]
            fn is_one(&self) -> bool {
                *self == $o
            }
        }
        impl Epsilon for $t {
            fn epsilon() -> Self {
                std::$id::EPSILON
            }
        }
        impl Const for $t {
            fn tau() -> Self {
                std::$id::consts::TAU
            }
        }
        impl NumCast for $t {
            fn from(n: usize) -> Option<Self> {
                if n == 0 {
                    return Some(0.);
                }
                // Safe cast $source is either 4 or 8 bytes.
                let size: u32 = std::mem::size_of::<usize>() as u32 * 8;
                let lz = n.leading_zeros();
                let tz = n.trailing_zeros();
                // Leading bit is not used for sign in unsigned type.
                // Since the first one of the mantissa is implicit the actual
                // number of representable bits is #mantissa + 1.
                // Integer types cannot be converted in subnormal floats.
                if size - (lz + tz) > <$t>::MANTISSA_DIGITS + 1 {
                    None
                } else {
                    Some(n as $t)
                }
            }
        }
    };
}
impl_trait_float!(f32, f32, 0.0, 1.0);
impl_trait_float!(f64, f64, 0.0, 1.0);

macro_rules! impl_trait_signed {
    ($t:ty, $z:expr, $o:expr) => {
        impl Abs for $t {
            fn abs(&self) -> Self {
                <$t>::abs(*self)
            }
        }
        impl Max for $t {
            fn max(self, other: Self) -> Self {
                Ord::max(self, other)
            }
        }
        impl Zero for $t {
            fn zero() -> Self {
                $z
            }
            fn is_zero(&self) -> bool {
                *self == $z
            }
        }
        impl One for $t {
            fn one() -> Self {
                $o
            }
            fn is_one(&self) -> bool {
                *self == $o
            }
        }
        impl NumCast for $t {
            fn from(n: usize) -> Option<Self> {
                <$t>::try_from(n).ok()
            }
        }
    };
}
impl_trait_signed!(i8, 0, 1);
impl_trait_signed!(i16, 0, 1);
impl_trait_signed!(i32, 0, 1);
impl_trait_signed!(i64, 0, 1);
impl_trait_signed!(i128, 0, 1);
impl_trait_signed!(isize, 0, 1);

macro_rules! impl_trait_unsigned {
    ($t:ty, $z:expr, $o:expr) => {
        impl Abs for $t {
            fn abs(&self) -> Self {
                *self
            }
        }
        impl Max for $t {
            fn max(self, other: Self) -> Self {
                Ord::max(self, other)
            }
        }
        impl Zero for $t {
            fn zero() -> Self {
                $z
            }
            fn is_zero(&self) -> bool {
                *self == $z
            }
        }
        impl One for $t {
            fn one() -> Self {
                $o
            }
            fn is_one(&self) -> bool {
                *self == $o
            }
        }
        impl NumCast for $t {
            fn from(n: usize) -> Option<Self> {
                <$t>::try_from(n).ok()
            }
        }
    };
}
impl_trait_unsigned!(u8, 0, 1);
impl_trait_unsigned!(u16, 0, 1);
impl_trait_unsigned!(u32, 0, 1);
impl_trait_unsigned!(u64, 0, 1);
impl_trait_unsigned!(u128, 0, 1);
impl_trait_unsigned!(usize, 0, 1);

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn float_one() {
        assert!(f32::one().is_one());
    }

    #[test]
    fn numeric_numcast() {
        let a: f32 = NumCast::from(12).unwrap();
        assert_eq!(12., a);
        let a: f64 = NumCast::from(123_432).unwrap();
        assert_eq!(123_432., a);
        let a: Option<f32> = NumCast::from(8_000_000_001);
        assert_eq!(None, a);
    }

    #[test]
    fn numeric_numcast_f32_limit() {
        // f32 mantissa is 24 bit long, so anything longer than 25 bits of
        // significant bits is not representable as f32.
        let a: f32 = NumCast::from(0b1000_0000_0000_0000_0000_0000_1).unwrap();
        assert_eq!(1. + 24.0_f32.exp2(), a);
        let a: Option<f32> = NumCast::from(0b1000_0000_0000_0000_0000_0000_01);
        assert_eq!(None, a);
    }

    #[test]
    fn numeric_numcast_f64_limit() {
        // f64 mantissa is 53 bit long, so anything longer than 54 bits of
        // significant bits is not representable as f64.
        let a: f64 =
            NumCast::from(0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_01)
                .unwrap();
        assert_eq!(1. + 53.0_f64.exp2(), a);
        let a: Option<f64> =
            NumCast::from(0b1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_001);
        assert_eq!(None, a);
    }

    #[test]
    fn signed_max() {
        assert_eq!(12, Max::max(-4i32, 12));
    }

    #[test]
    fn signed_one() {
        assert!(i128::one().is_one());
    }

    #[test]
    fn signed_numcast() {
        let a: i8 = NumCast::from(17).unwrap();
        assert_eq!(17, a);
        let b: i16 = NumCast::from(17).unwrap();
        assert_eq!(17, b);
        let c: i32 = NumCast::from(17).unwrap();
        assert_eq!(17, c);
        let d: i64 = NumCast::from(17).unwrap();
        assert_eq!(17, d);
        let e: i128 = NumCast::from(17).unwrap();
        assert_eq!(17, e);
        let f: isize = NumCast::from(17).unwrap();
        assert_eq!(17, f);
    }

    #[test]
    fn unsigned_abs() {
        assert_eq!(4, 4u32.abs());
    }

    #[test]
    fn unsigned_max() {
        assert_eq!(12, Max::max(4u32, 12u32));
    }

    #[test]
    fn unsigned_zero() {
        assert!(usize::zero().is_zero());
    }

    #[test]
    fn unsigned_one() {
        assert!(u128::one().is_one());
    }

    #[test]
    fn unsigned_numcast() {
        let a: u8 = NumCast::from(17).unwrap();
        assert_eq!(17, a);
        let b: u16 = NumCast::from(17).unwrap();
        assert_eq!(17, b);
        let c: u32 = NumCast::from(17).unwrap();
        assert_eq!(17, c);
        let d: u64 = NumCast::from(17).unwrap();
        assert_eq!(17, d);
        let e: u128 = NumCast::from(17).unwrap();
        assert_eq!(17, e);
        let f: usize = NumCast::from(17).unwrap();
        assert_eq!(17, f);
    }
}