gemath 0.1.0

Type-safe game math with type-level units/spaces, typed angles, and explicit fallible ops (plus optional geometry/collision).
Documentation
#![cfg(all(feature = "half_float", any(feature = "std", feature = "alloc")))]

use gemath::*;

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

    const EPSILON: f32 = 1e-3; // f16 has lower precision

    #[test]
    fn test_half_from_f32_to_f32() {
        let f32_val: f32 = 0.123;
        let h = Half::from_f32(f32_val);
        let f32_back = h.to_f32();
        assert!(
            (f32_val - f32_back).abs() < EPSILON,
            "Original: {}, Back: {}",
            f32_val,
            f32_back
        );

        let f32_val_neg: f32 = -3.14159;
        let h_neg = Half::from_f32(f32_val_neg);
        let f32_back_neg = h_neg.to_f32();
        assert!(
            (f32_val_neg - f32_back_neg).abs() < EPSILON,
            "Original: {}, Back: {}",
            f32_val_neg,
            f32_back_neg
        );
    }

    #[test]
    fn test_half_from_trait_f32() {
        let f32_val: f32 = 10.5;
        let h: Half = f32_val.into();
        let f32_back: f32 = h.into();
        assert!((f32_val - f32_back).abs() < EPSILON);
    }

    #[test]
    fn test_half_from_f16_to_f16() {
        let f16_val = f16::from_f32(0.5);
        let h = Half::new(f16_val);
        assert_eq!(h.0, f16_val);

        let f16_back: f16 = h.into();
        assert_eq!(f16_back, f16_val);
    }

    #[test]
    fn test_half_default() {
        let h_def: Half = Default::default();
        assert_eq!(h_def.to_f32(), 0.0);
        assert_eq!(h_def.0, f16::ZERO);
    }

    #[test]
    fn test_half_arithmetic() {
        let a = Half::from_f32(1.5);
        let b = Half::from_f32(2.0);
        let c = Half::from_f32(-0.5);
        // Add
        assert!(((a + b).to_f32() - 3.5).abs() < EPSILON);
        // Sub
        assert!(((b - a).to_f32() - 0.5).abs() < EPSILON);
        // Mul
        assert!(((a * b).to_f32() - 3.0).abs() < EPSILON);
        // Div
        assert!(((b / a).to_f32() - (2.0 / 1.5)).abs() < EPSILON);
        // AddAssign
        let mut x = a;
        x += b;
        assert!((x.to_f32() - 3.5).abs() < EPSILON);
        // SubAssign
        let mut y = b;
        y -= a;
        assert!((y.to_f32() - 0.5).abs() < EPSILON);
        // MulAssign
        let mut z = a;
        z *= b;
        assert!((z.to_f32() - 3.0).abs() < EPSILON);
        // DivAssign
        let mut w = b;
        w /= a;
        assert!((w.to_f32() - (2.0 / 1.5)).abs() < EPSILON);
        // Negative
        assert!(((a + c).to_f32() - 1.0).abs() < EPSILON);
    }

    #[test]
    fn test_half_array_slice_conversions() {
        use gemath::{
            f16s_from_half_array, f16s_from_half_slice, f32s_from_half_array, f32s_from_half_slice,
            halfs_from_f16_array, halfs_from_f16_slice, halfs_from_f32_array, halfs_from_f32_slice,
        };
        use half::f16;
        let f32s = [1.0f32, -2.0, 3.5, 0.0];
        let halfs = halfs_from_f32_slice(&f32s);
        for (i, h) in halfs.iter().enumerate() {
            assert!((h.to_f32() - f32s[i]).abs() < EPSILON);
        }
        let f32s_back = f32s_from_half_slice(&halfs);
        for (a, b) in f32s.iter().zip(f32s_back.iter()) {
            assert!((*a - *b).abs() < EPSILON);
        }
        // f16 conversions
        let f16s: Vec<f16> = f32s.iter().map(|&v| f16::from_f32(v)).collect();
        let halfs2 = halfs_from_f16_slice(&f16s);
        for (i, h) in halfs2.iter().enumerate() {
            assert_eq!(h.0, f16s[i]);
        }
        let f16s_back = f16s_from_half_slice(&halfs2);
        for (a, b) in f16s.iter().zip(f16s_back.iter()) {
            assert_eq!(*a, *b);
        }
        // Array conversions
        let arr_halfs = halfs_from_f32_array(f32s);
        for (i, h) in arr_halfs.iter().enumerate() {
            assert!((h.to_f32() - f32s[i]).abs() < EPSILON);
        }
        let arr_f32s = f32s_from_half_array(arr_halfs);
        for (a, b) in f32s.iter().zip(arr_f32s.iter()) {
            assert!((*a - *b).abs() < EPSILON);
        }
        let arr_f16s: [f16; 4] = [
            f16::from_f32(1.0),
            f16::from_f32(-2.0),
            f16::from_f32(3.5),
            f16::from_f32(0.0),
        ];
        let arr_halfs2 = halfs_from_f16_array(arr_f16s);
        for (i, h) in arr_halfs2.iter().enumerate() {
            assert_eq!(h.0, arr_f16s[i]);
        }
        let arr_f16s_back = f16s_from_half_array(arr_halfs2);
        for (a, b) in arr_f16s.iter().zip(arr_f16s_back.iter()) {
            assert_eq!(*a, *b);
        }
    }
}