rmath-rs 0.1.0

collection of GLSL/HLSL-like vector math to make software-rendering more convenient
Documentation
use core::arch::x86_64::{
    __m128, _mm_add_ps, _mm_and_ps, _mm_andnot_ps, _mm_cmplt_ps, _mm_cvtss_f32, _mm_div_ps,
    _mm_floor_ps, _mm_hadd_ps, _mm_mul_ps, _mm_set_ps, _mm_set_ps1, _mm_setzero_ps, _mm_shuffle_ps,
    _mm_sub_ps, _mm_xor_ps,
};
use core::ops::{Add, Div, Mul, Neg, Sub};

#[derive(Debug)]
pub struct Vector4 {
    data: __m128,
}

impl Vector4 {
    /// Creates Vector4 instance from SIMD __m128
    #[inline]
    fn from128(v: __m128) -> Vector4 {
        Vector4 { data: v }
    }

    /// Creates Vector4 instance from one float value.
    /// `x`, `y`, `z` and `w` will be set to the same value.
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v = Vector4::from1(0.1);
    /// assert_approx_eq!(v.x(), 0.1);
    /// assert_approx_eq!(v.y(), 0.1);
    /// assert_approx_eq!(v.z(), 0.1);
    /// assert_approx_eq!(v.w(), 0.1);
    /// ```
    #[inline]
    pub fn from1(v: f32) -> Self {
        unsafe { Vector4::from128(_mm_set_ps1(v)) }
    }

    /// Creates Vector4 instance from four float values.
    /// `x`, `y`, `z` and `w` will be set to the value
    /// of appropriate parameter.
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v = Vector4::from4(0.2, 1.1, -2.9, 99.9);
    /// assert_approx_eq!(v.x(), 0.2);
    /// assert_approx_eq!(v.y(), 1.1);
    /// assert_approx_eq!(v.z(), -2.9);
    /// assert_approx_eq!(v.w(), 99.9);
    /// ```  
    #[inline]
    pub fn from4(x: f32, y: f32, z: f32, w: f32) -> Self {
        unsafe { Vector4::from128(_mm_set_ps(w, z, y, x)) }
    }

    /// Retrieves `x` component of `Vector4`.
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v = Vector4::from4(0.2, 1.1, -2.9, 99.9);
    /// assert_approx_eq!(v.x(), 0.2);
    /// ```  
    #[inline]
    pub fn x(&self) -> f32 {
        unsafe { _mm_cvtss_f32(self.data) }
    }

    /// Retrieves `y` component of `Vector4`.
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v = Vector4::from4(0.2, 1.1, -2.9, 99.9);
    /// assert_approx_eq!(v.y(), 1.1);
    /// ```  
    #[inline]
    pub fn y(&self) -> f32 {
        unsafe { _mm_cvtss_f32(_mm_shuffle_ps(self.data, self.data, 0b01010101)) }
    }

    /// Retrieves `z` component of `Vector4`.
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v = Vector4::from4(0.2, 1.1, -2.9, 99.9);
    /// assert_approx_eq!(v.z(), -2.9);
    /// ```  
    #[inline]
    pub fn z(&self) -> f32 {
        unsafe { _mm_cvtss_f32(_mm_shuffle_ps(self.data, self.data, 0b10101010)) }
    }

    /// Retrieves `w` component of `Vector4`.
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v = Vector4::from4(0.2, 1.1, -2.9, 99.9);
    /// assert_approx_eq!(v.w(), 99.9);
    /// ```  
    #[inline]
    pub fn w(&self) -> f32 {
        unsafe { _mm_cvtss_f32(_mm_shuffle_ps(self.data, self.data, 0b11111111)) }
    }

    /// Finds the nearest integer less than or equal to the parameter
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v = Vector4::from4(0.2, 1.1, -2.9, 99.9);
    /// let v2 = v.floor();
    /// assert_approx_eq!(v2.x(), 0.0);
    /// assert_approx_eq!(v2.y(), 1.0);
    /// assert_approx_eq!(v2.z(), -3.0);
    /// assert_approx_eq!(v2.w(), 99.0);
    /// ```
    /// See: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/floor.xhtml
    #[inline]
    pub fn floor(&self) -> Vector4 {
        unsafe { Vector4::from128(_mm_floor_ps(self.data)) }
    }

    /// Finds absolute value of parameter
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v = Vector4::from4(0.2, 1.1, -2.9, 99.9);
    /// let v2 = v.abs();
    /// assert_approx_eq!(v2.x(), 0.2);
    /// assert_approx_eq!(v2.y(), 1.1);
    /// assert_approx_eq!(v2.z(), 2.9);
    /// assert_approx_eq!(v2.w(), 99.9);
    /// ```
    #[inline]
    pub fn abs(&self) -> Vector4 {
        unsafe { Vector4::from128(_mm_andnot_ps(_mm_set_ps1(-0.0), self.data)) }
    }

    /// Computes the fractional part of the argument
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v = Vector4::from4(0.2, 1.1, -2.9, 99.9);
    /// let v2 = v.fract();
    /// assert_approx_eq!(v2.x(), 0.2);
    /// assert_approx_eq!(v2.y(), 0.1);
    /// assert_approx_eq!(v2.z(), 0.1);
    /// assert_approx_eq!(v2.w(), 0.9, 1.0e-5); // this one becomes 0.9000015 with regular 1.0e-6 precision :/
    /// ```
    ///
    /// https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/fract.xhtml
    #[inline]
    pub fn fract(&self) -> Vector4 {
        let f = self.floor();
        self - &f
    }

    /// Computes value of one parameter modulo another.
    /// Consistent with GLSL implementation
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v = Vector4::from4(0.2, 1.1, -2.9, 99.9);
    /// let v2 = Vector4::from1(-1.8);
    /// let v3 = v.modulo(&v2);
    /// assert_approx_eq!(v3.x(), -1.6);
    /// assert_approx_eq!(v3.y(), -0.7);
    /// assert_approx_eq!(v3.z(), -1.1);
    /// assert_approx_eq!(v3.w(), -0.9, 1.0e-5);
    /// ```
    ///
    /// https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/mod.xhtml
    #[inline]
    pub fn modulo(&self, other: &Vector4) -> Vector4 {
        unsafe {
            Vector4::from128(_mm_sub_ps(
                self.data,
                _mm_mul_ps(other.data, _mm_floor_ps(_mm_div_ps(self.data, other.data))),
            ))
        }
    }

    /// Computes value of one parameter modulo another.
    /// Consistent with [Euclidean](https://en.wikipedia.org/wiki/Modulo_operation) division algorithm.
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v = Vector4::from4(0.2, 1.1, -2.9, 99.9);
    /// let v2 = Vector4::from1(-1.8);
    /// let v3 = v.modulo_euclidean(&v2);
    /// assert_approx_eq!(v3.x(), 0.2);
    /// assert_approx_eq!(v3.y(), 1.1);
    /// assert_approx_eq!(v3.z(), 0.7);
    /// assert_approx_eq!(v3.w(), 0.9, 1.0e-5);
    /// ```
    #[inline]
    pub fn modulo_euclidean(&self, other: &Vector4) -> Vector4 {
        unsafe {
            let ret = self.modulo(&other);
            let mask = _mm_cmplt_ps(ret.data, _mm_setzero_ps());
            Vector4::from128(_mm_add_ps(
                ret.data,
                _mm_and_ps(mask, _mm_andnot_ps(_mm_set_ps1(-0.0), other.data)),
            ))
        }
    }

    /// Computes value of distance squared between two vectors
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v1 = Vector4::from4(0.2, 1.1, -2.9, 99.9);
    /// let v2 = Vector4::from4(0.9, 1.8, 2.9, -14.4);
    /// let d = v1.distance_sq(&v2);
    /// assert_approx_eq!(d, 13099.11);
    /// ```
    #[inline]
    pub fn distance_sq(&self, other: &Vector4) -> f32 {
        unsafe {
            let a_minus_b = _mm_sub_ps(self.data, other.data);
            let a_minus_b_sq = _mm_mul_ps(a_minus_b, a_minus_b);
            let h_add = _mm_hadd_ps(a_minus_b_sq, a_minus_b_sq);
            _mm_cvtss_f32(_mm_hadd_ps(h_add, h_add))
        }
    }

    /// Computes value of distance between two vectors
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v1 = Vector4::from4(0.2, 1.1, -2.9, 99.9);
    /// let v2 = Vector4::from4(0.9, 1.8, 2.9, -14.4);
    /// let d = v1.distance(&v2);
    /// assert_approx_eq!(d, 114.45134, 1.0e-5);
    /// ```
    #[inline]
    pub fn distance(&self, other: &Vector4) -> f32 {
        self.distance_sq(other).sqrt()
    }
}

impl Add<&Vector4> for &Vector4 {
    type Output = Vector4;

    /// Implements Add trait for Vector4.
    /// Operator +(Vector4, Vector4).
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v1 = Vector4::from1(0.1);
    /// let v2 = Vector4::from1(1.8);
    /// let v3 = &v1 + &v2;
    /// assert_approx_eq!(v3.x(), 1.9);
    /// assert_approx_eq!(v3.y(), 1.9);
    /// assert_approx_eq!(v3.z(), 1.9);
    /// assert_approx_eq!(v3.w(), 1.9);
    /// ```
    #[inline]
    fn add(self, other: &Vector4) -> Vector4 {
        unsafe { Vector4::from128(_mm_add_ps(self.data, other.data)) }
    }
}

impl Add<f32> for &Vector4 {
    type Output = Vector4;

    /// Implements Add trait for Vector4.
    /// Operator +(Vector4, f32).
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v1 = Vector4::from1(0.1);
    /// let v2 = &v1 + 1.8;
    /// assert_approx_eq!(v2.x(), 1.9);
    /// # assert_approx_eq!(v2.y(), 1.9);
    /// # assert_approx_eq!(v2.z(), 1.9);
    /// # assert_approx_eq!(v2.w(), 1.9);
    /// ```  
    #[inline]
    fn add(self, other: f32) -> Vector4 {
        unsafe { Vector4::from128(_mm_add_ps(self.data, _mm_set_ps1(other))) }
    }
}

impl Sub for &Vector4 {
    type Output = Vector4;

    /// Implements Sub trait for Vector4.
    /// Operator -(Vector4, Vector4).
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v1 = Vector4::from1(0.1);
    /// let v2 = Vector4::from1(1.8);
    /// let v3 = &v1 - &v2;
    /// assert_approx_eq!(v3.x(), -1.7);
    /// # assert_approx_eq!(v3.y(), -1.7);
    /// # assert_approx_eq!(v3.z(), -1.7);
    /// # assert_approx_eq!(v3.w(), -1.7);
    /// ```  
    #[inline]
    fn sub(self, other: &Vector4) -> Vector4 {
        unsafe { Vector4::from128(_mm_sub_ps(self.data, other.data)) }
    }
}

impl Sub<f32> for &Vector4 {
    type Output = Vector4;

    /// Implements Sub trait for Vector4.
    /// Operator -(Vector4, f32).
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v1 = Vector4::from1(0.1);
    /// let v2 = &v1 - 1.8;
    /// assert_approx_eq!(v2.x(), -1.7);
    /// # assert_approx_eq!(v2.y(), -1.7);
    /// # assert_approx_eq!(v2.z(), -1.7);
    /// # assert_approx_eq!(v2.w(), -1.7);
    /// ```  
    #[inline]
    fn sub(self, other: f32) -> Vector4 {
        unsafe { Vector4::from128(_mm_sub_ps(self.data, _mm_set_ps1(other))) }
    }
}

impl Mul for &Vector4 {
    type Output = Vector4;

    /// Implements Mul trait for Vector4.
    /// Operator *(Vector4, Vector4).
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v1 = Vector4::from1(0.1);
    /// let v2 = Vector4::from1(1.8);
    /// let v3 = &v1 * &v2;
    /// assert_approx_eq!(v3.x(), 0.18);
    /// # assert_approx_eq!(v3.y(), 0.18);
    /// # assert_approx_eq!(v3.z(), 0.18);
    /// # assert_approx_eq!(v3.w(), 0.18);
    /// ```  
    #[inline]
    fn mul(self, other: &Vector4) -> Vector4 {
        unsafe { Vector4::from128(_mm_mul_ps(self.data, other.data)) }
    }
}

impl Mul<f32> for &Vector4 {
    type Output = Vector4;

    /// Implements Mul trait for Vector4.
    /// Operator *(Vector4, f32).
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v1 = Vector4::from1(0.1);
    /// let v2 = &v1 * 1.8;
    /// assert_approx_eq!(v2.x(), 0.18);
    /// # assert_approx_eq!(v2.y(), 0.18);
    /// # assert_approx_eq!(v2.z(), 0.18);
    /// # assert_approx_eq!(v2.w(), 0.18);
    /// ```  
    #[inline]
    fn mul(self, other: f32) -> Vector4 {
        unsafe { Vector4::from128(_mm_mul_ps(self.data, _mm_set_ps1(other))) }
    }
}

impl Div for &Vector4 {
    type Output = Vector4;

    /// Implements Div trait for Vector4.
    /// Operator /(Vector4, Vector4).
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v1 = Vector4::from1(0.1);
    /// let v2 = Vector4::from1(1.8);
    /// let v3 = &v1 / &v2;
    /// assert_approx_eq!(v3.x(), 0.055555556);
    /// # assert_approx_eq!(v3.y(), 0.055555556);
    /// # assert_approx_eq!(v3.z(), 0.055555556);
    /// # assert_approx_eq!(v3.w(), 0.055555556);
    /// ```  
    #[inline]
    fn div(self, other: &Vector4) -> Vector4 {
        unsafe { Vector4::from128(_mm_div_ps(self.data, other.data)) }
    }
}

impl Div<f32> for &Vector4 {
    type Output = Vector4;

    /// Implements Div trait for Vector4.
    /// Operator /(Vector4, f32).
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v1 = Vector4::from1(0.1);
    /// let v2 = &v1 / 1.8;
    /// assert_approx_eq!(v2.x(), 0.055555556);
    /// # assert_approx_eq!(v2.y(), 0.055555556);
    /// # assert_approx_eq!(v2.z(), 0.055555556);
    /// # assert_approx_eq!(v2.w(), 0.055555556);
    /// ```  
    #[inline]
    fn div(self, other: f32) -> Vector4 {
        unsafe { Vector4::from128(_mm_div_ps(self.data, _mm_set_ps1(other))) }
    }
}

impl Neg for &Vector4 {
    type Output = Vector4;

    /// Implements Neg trait for Vector4.
    /// Operator -(Vector4).
    ///
    /// ```
    /// # use assert_approx_eq::assert_approx_eq;
    /// use rmath_rs::Vector4;
    /// let v = Vector4::from4(0.2, 1.1, -2.9, 99.9);
    /// let v2 = -&v;
    /// assert_approx_eq!(v2.x(), -0.2);
    /// assert_approx_eq!(v2.y(), -1.1);
    /// assert_approx_eq!(v2.z(), 2.9);
    /// assert_approx_eq!(v2.w(), -99.9);
    /// ```  
    #[inline]
    fn neg(self) -> Vector4 {
        unsafe { Vector4::from128(_mm_xor_ps(self.data, _mm_set_ps1(-0.0))) }
    }
}