📄 test_numeric.rs
/home/palash/git/iron_learn/tests/test_numeric.rs
Language: rs • Lines: 185
#[cfg(test)]
mod exhaustive_tests {
    use iron_learn::Complex;
    use iron_learn::Numeric;
    use iron_learn::SignedNumeric;

    /// 1. Generic Axiom Testing
    #[allow(clippy::eq_op)]
    fn verify_numeric_identities<T: Numeric>(val: T) {
        // Identity: x + 0 = x
        assert_eq!(val + T::zero(), val, "Addition identity failed for {}", val);
        // Identity: x * 1 = x
        assert_eq!(
            val * T::one(),
            val,
            "Multiplication identity failed for {}",
            val
        );
        // Identity: x - x = 0
        assert_eq!(
            val - val,
            T::zero(),
            "Subtraction self-nullification failed for {}",
            val
        );
        // Identity: x / 1 = x
        assert_eq!(val / T::one(), val, "Division identity failed for {}", val);
    }

    #[test]
    fn test_all_identities() {
        verify_numeric_identities(100i8);
        verify_numeric_identities(5000u32);
        verify_numeric_identities(std::f64::consts::PI);
        verify_numeric_identities(Complex::new(5.0, -2.0));
    }

    /// 2. Integer Boundary Tests
    #[test]
    fn test_integer_boundaries() {
        // Test i8 (Range: -128 to 127)
        assert_eq!(i8::from_f64(127.0), 127);
        assert_eq!(i8::from_f64(-128.0), -128);

        // Test u8 (Range: 0 to 255)
        assert_eq!(u8::from_u32(255), 255);
        // Note: from_u32 uses .unwrap(), so u8::from_u32(300) would panic.
    }

    /// 3. Casting Precision & Truncation
    #[test]
    fn test_casting_behavior() {
        // Floating point to Integer (Truncation check)
        assert_eq!(i32::from_f64(10.99), 10);
        assert_eq!(i32::from_f64(-10.99), -10);

        // Integer to Float
        let big_val: i128 = 1_000_000_000_000;
        assert_eq!(big_val.f64(), 1_000_000_000_000.0);
    }

    /// 4. FloatingPoint Specifics (NaN, Infinity, Precision)
    #[test]
    fn test_fp_special_cases() {
        let nan = f64::NAN;
        let inf = f64::INFINITY;

        assert!(nan.f64().is_nan());
        assert!(inf.f64().is_infinite());

        // Transcendental checks
        let angle = f64::from_f64(std::f64::consts::PI);
        assert!((angle.sin() - 0.0).abs() < 1e-10);
        assert!((angle.cos() + 1.0).abs() < 1e-10);
    }

    /// 5. Complex Number Arithmetic
    #[test]
    fn test_complex_deep_math() {
        let a = Complex::new(2.0, 3.0);
        let b = Complex::new(1.0, -1.0);

        // (2+3i) + (1-i) = 3+2i
        assert_eq!(a + b, Complex::new(3.0, 2.0));
        // (2+3i) * (1-i) = 2 - 2i + 3i - 3i^2 = 2 + i + 3 = 5 + i
        assert_eq!(a * b, Complex::new(5.0, 1.0));
    }

    /// 6. SignedNumeric Negation
    #[test]
    fn test_negation_all_types() {
        fn test_neg<T: SignedNumeric>(val: T, expected: T) {
            assert_eq!(-val, expected);
        }

        test_neg(10i32, -10i32);
        test_neg(5.5f64, -5.5f64);
        test_neg(Complex::new(1.0, 2.0), Complex::new(-1.0, -2.0));
    }
}

#[cfg(test)]
mod tests {
    use iron_learn::Complex;
    use iron_learn::Numeric;

    /// Macro to generate tests for standard Numeric types
    macro_rules! test_numeric_impl {
        ($t:ty, $name:ident, $is_float:expr) => {
            #[test]
            fn $name() {
                let val: $t = <$t as Numeric>::one();
                let zero: $t = <$t as Numeric>::zero();

                // Test zero and one
                assert_eq!(val, 1 as $t);
                assert_eq!(zero, 0 as $t);

                // Test f32/f64 conversions
                assert_eq!(val.f32(), 1.0_f32);
                assert_eq!(val.f64(), 1.0_f64);

                // Test from_u32 and from_f64
                // We use 10 to avoid overflow panics in try_into blocks
                assert_eq!(<$t as Numeric>::from_u32(10), 10 as $t);
                assert_eq!(<$t as Numeric>::from_f64(10.0), 10 as $t);
            }
        };
    }

    // Call the macro for every integer and float type implemented in the file
    test_numeric_impl!(i8, test_i8, false);
    test_numeric_impl!(i16, test_i16, false);
    test_numeric_impl!(i32, test_i32, false);
    test_numeric_impl!(i64, test_i64, false);
    test_numeric_impl!(i128, test_i128, false);
    test_numeric_impl!(isize, test_isize, false);
    test_numeric_impl!(u8, test_u8, false);
    test_numeric_impl!(u16, test_u16, false);
    test_numeric_impl!(u32, test_u32, false);
    test_numeric_impl!(u64, test_u64, false);
    test_numeric_impl!(u128, test_u128, false);
    test_numeric_impl!(usize, test_usize, false);
    test_numeric_impl!(f32, test_f32, true);
    test_numeric_impl!(f64, test_f64, true);

    #[test]
    fn test_complex_numeric() {
        let _c = Complex::new(5.0, 0.0);
        assert_eq!(Complex::zero(), Complex::new(0.0, 0.0));
        assert_eq!(Complex::one(), Complex::new(1.0, 0.0));
        assert_eq!(Complex::from_u32(10), Complex::new(10.0, 0.0));
        assert_eq!(Complex::from_f64(10.5), Complex::new(10.5, 0.0));
    }

    #[test]
    #[should_panic(expected = "InvalidOperation")]
    fn test_complex_f32_panic() {
        Complex::one().f32(); // Hits the panic line in Complex impl
    }

    #[test]
    #[should_panic(expected = "InvalidOperation")]
    fn test_complex_f64_panic() {
        Complex::one().f64(); // Hits the panic line in Complex impl
    }

    #[test]
    fn test_floating_point_ops() {
        let val: f64 = 4.0;
        assert_eq!(val.sqrt(), 2.0);
        assert_eq!(val.abs(), 4.0);
        assert_eq!(val.max(10.0), 10.0);
        assert_eq!(val.min(1.0), 1.0);
        // Add more ops as needed to hit all trait methods
        let _ = val.sin();
        let _ = val.cos();
        let _ = val.tan();
        let _ = val.tanh();
        let _ = val.exp();
        let _ = val.ln();
        let _ = val.log10();
        let _ = val.round();
    }
}