Repository: bitshifter/glam-rs
Pull request #221 (2021-08-03 11:37:58)

Issue 218
Add `Rem` and `RemAssign` trait implementations for vector types.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f228f65..d840e55 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog], and this project adheres to
 [Semantic Versioning].
 
+## [Unreleased]
+
+### Added
+
+* Added `Rem` and `RemAssign` implementations for all vector types.
+
 ## [0.17.3] - 2021-07-18
 
 ### Fixed
diff --git a/src/core/scalar/vector.rs b/src/core/scalar/vector.rs
index fc29026..3f0ee45 100644
--- a/src/core/scalar/vector.rs
+++ b/src/core/scalar/vector.rs
@@ -233,6 +233,22 @@ impl<T: NumEx> Vector<T> for XY<T> {
         }
     }
 
+    #[inline]
+    fn rem(self, other: Self) -> Self {
+        Self {
+            x: self.x % other.x,
+            y: self.y % other.y,
+        }
+    }
+
+    #[inline]
+    fn rem_scalar(self, other: T) -> Self {
+        Self {
+            x: self.x % other,
+            y: self.y % other,
+        }
+    }
+
     #[inline]
     fn min(self, other: Self) -> Self {
         Self {
@@ -400,6 +416,24 @@ impl<T: NumEx> Vector<T> for XYZ<T> {
         }
     }
 
+    #[inline]
+    fn rem(self, other: Self) -> Self {
+        Self {
+            x: self.x % other.x,
+            y: self.y % other.y,
+            z: self.z % other.z,
+        }
+    }
+
+    #[inline]
+    fn rem_scalar(self, other: T) -> Self {
+        Self {
+            x: self.x % other,
+            y: self.y % other,
+            z: self.z % other,
+        }
+    }
+
     #[inline]
     fn min(self, other: Self) -> Self {
         Self {
@@ -590,6 +624,26 @@ impl<T: NumEx> Vector<T> for XYZW<T> {
         }
     }
 
+    #[inline]
+    fn rem(self, other: Self) -> Self {
+        Self {
+            x: self.x % other.x,
+            y: self.y % other.y,
+            z: self.z % other.z,
+            w: self.w % other.w,
+        }
+    }
+
+    #[inline]
+    fn rem_scalar(self, other: T) -> Self {
+        Self {
+            x: self.x % other,
+            y: self.y % other,
+            z: self.z % other,
+            w: self.w % other,
+        }
+    }
+
     #[inline]
     fn min(self, other: Self) -> Self {
         Self {
@@ -940,6 +994,11 @@ impl Vector<f32> for XYZF32A16 {
         XYZ::mul_add(self.into(), b.into(), c.into()).into()
     }
 
+    #[inline]
+    fn rem(self, other: Self) -> Self {
+        XYZ::rem(self.into(), other.into()).into()
+    }
+
     #[inline]
     fn sub(self, other: Self) -> Self {
         XYZ::sub(self.into(), other.into()).into()
@@ -965,6 +1024,11 @@ impl Vector<f32> for XYZF32A16 {
         XYZ::div_scalar(self.into(), other).into()
     }
 
+    #[inline]
+    fn rem_scalar(self, other: f32) -> Self {
+        XYZ::rem_scalar(self.into(), other).into()
+    }
+
     #[inline]
     fn min(self, other: Self) -> Self {
         XYZ::min(self.into(), other.into()).into()
diff --git a/src/core/sse2/vector.rs b/src/core/sse2/vector.rs
index f31a0f6..acf4ea2 100644
--- a/src/core/sse2/vector.rs
+++ b/src/core/sse2/vector.rs
@@ -262,6 +262,19 @@ impl Vector<f32> for __m128 {
         unsafe { _mm_div_ps(self, _mm_set_ps1(other)) }
     }
 
+    #[inline(always)]
+    fn rem(self, other: Self) -> Self {
+        unsafe {
+            let n = m128_floor(_mm_div_ps(self, other));
+            _mm_sub_ps(self, _mm_mul_ps(n, other))
+        }
+    }
+
+    #[inline(always)]
+    fn rem_scalar(self, other: f32) -> Self {
+        unsafe { self.rem(_mm_set1_ps(other)) }
+    }
+
     #[inline(always)]
     fn min(self, other: Self) -> Self {
         unsafe { _mm_min_ps(self, other) }
diff --git a/src/core/traits/scalar.rs b/src/core/traits/scalar.rs
index 1b2c18e..ef47f95 100644
--- a/src/core/traits/scalar.rs
+++ b/src/core/traits/scalar.rs
@@ -4,7 +4,7 @@ pub use num_traits::{Float, Num, Signed};
 
 use core::{
     marker::Sized,
-    ops::{Add, Div, Mul, Sub},
+    ops::{Add, Div, Mul, Rem, Sub},
 };
 
 // Stub the necessary parts of num traits
@@ -161,6 +161,7 @@ pub trait NumEx:
     + Div<Output = Self>
     + Mul<Output = Self>
     + Sub<Output = Self>
+    + Rem<Output = Self>
 {
     fn min(self, other: Self) -> Self;
     fn max(self, other: Self) -> Self;
diff --git a/src/core/traits/vector.rs b/src/core/traits/vector.rs
index 468413d..036233e 100644
--- a/src/core/traits/vector.rs
+++ b/src/core/traits/vector.rs
@@ -93,6 +93,7 @@ pub trait Vector<T>: Sized + Copy + Clone {
     fn div(self, other: Self) -> Self;
     fn mul(self, other: Self) -> Self;
     fn mul_add(self, a: Self, b: Self) -> Self;
+    fn rem(self, rhs: Self) -> Self;
     fn sub(self, other: Self) -> Self;
 
     fn scale(self, other: T) -> Self {
@@ -103,6 +104,7 @@ pub trait Vector<T>: Sized + Copy + Clone {
     fn sub_scalar(self, other: T) -> Self;
     fn mul_scalar(self, other: T) -> Self;
     fn div_scalar(self, other: T) -> Self;
+    fn rem_scalar(self, rhs: T) -> Self;
 
     fn min(self, other: Self) -> Self;
     fn max(self, other: Self) -> Self;
diff --git a/src/vec.rs b/src/vec.rs
index 5abb7a3..e3e576b 100644
--- a/src/vec.rs
+++ b/src/vec.rs
@@ -659,6 +659,44 @@ macro_rules! impl_vecn_common_traits {
             }
         }
 
+        impl Rem<$vecn> for $vecn {
+            type Output = Self;
+            #[inline(always)]
+            fn rem(self, other: $vecn) -> Self {
+                Self(self.0.rem(other.0))
+            }
+        }
+
+        impl RemAssign<$vecn> for $vecn {
+            #[inline(always)]
+            fn rem_assign(&mut self, other: $vecn) {
+                self.0 = self.0.rem(other.0)
+            }
+        }
+
+        impl Rem<$t> for $vecn {
+            type Output = Self;
+            #[inline(always)]
+            fn rem(self, other: $t) -> Self {
+                Self(self.0.rem_scalar(other))
+            }
+        }
+
+        impl RemAssign<$t> for $vecn {
+            #[inline(always)]
+            fn rem_assign(&mut self, other: $t) {
+                self.0 = self.0.rem_scalar(other)
+            }
+        }
+
+        impl Rem<$vecn> for $t {
+            type Output = $vecn;
+            #[inline(always)]
+            fn rem(self, other: $vecn) -> $vecn {
+                $vecn($inner::splat(self).rem(other.0))
+            }
+        }
+
         #[cfg(not(target_arch = "spriv"))]
         impl AsRef<[$t; $size]> for $vecn {
             #[inline(always)]


Test patch:
diff --git a/tests/vec2.rs b/tests/vec2.rs
index 69b3521..487a41d 100644
--- a/tests/vec2.rs
+++ b/tests/vec2.rs
@@ -92,6 +92,12 @@ macro_rules! impl_vec2_tests {
             assert_eq!($new(1 as $t, 1 as $t), (a / a));
             assert_eq!($new(1 as $t, 2 as $t), (a / 2 as $t));
             assert_eq!($new(2 as $t, 1 as $t), (4 as $t / a));
+            assert_eq!($new(0 as $t, 0 as $t), a % a);
+            assert_eq!($new(0 as $t, 1 as $t), a % (a - 1 as $t));
+            assert_eq!($new(0 as $t, 0 as $t), a % 1 as $t);
+            assert_eq!($new(2 as $t, 1 as $t), a % 3 as $t);
+            assert_eq!($new(1 as $t, 1 as $t), 17 as $t % a);
+            assert_eq!($new(2 as $t, 4 as $t), a % 8 as $t);
         }
 
         #[test]
@@ -110,6 +116,10 @@ macro_rules! impl_vec2_tests {
             assert_eq!($new(2 as $t, 4 as $t), b);
             b /= 2 as $t;
             assert_eq!($new(1 as $t, 2 as $t), b);
+            b %= (b + 1 as $t);
+            assert_eq!($new(1 as $t, 2 as $t), b);
+            b %= b;
+            assert_eq!($new(0 as $t, 0 as $t), b);
         }
 
         #[test]
diff --git a/tests/vec3.rs b/tests/vec3.rs
index ffd3f6b..a968e1e 100644
--- a/tests/vec3.rs
+++ b/tests/vec3.rs
@@ -99,14 +99,20 @@ macro_rules! impl_vec3_tests {
         #[test]
         fn test_ops() {
             let a = $new(2 as $t, 4 as $t, 8 as $t);
-            assert_eq!((4 as $t, 8 as $t, 16 as $t), (a + a).into());
-            assert_eq!((0 as $t, 0 as $t, 0 as $t), (a - a).into());
-            assert_eq!((4 as $t, 16 as $t, 64 as $t), (a * a).into());
-            assert_eq!((4 as $t, 8 as $t, 16 as $t), (a * 2 as $t).into());
-            assert_eq!((4 as $t, 8 as $t, 16 as $t), (2 as $t * a).into());
-            assert_eq!((1 as $t, 1 as $t, 1 as $t), (a / a).into());
-            assert_eq!((1 as $t, 2 as $t, 4 as $t), (a / 2 as $t).into());
-            assert_eq!((4 as $t, 2 as $t, 1 as $t), (8 as $t / a).into());
+            assert_eq!($new(4 as $t, 8 as $t, 16 as $t), a + a);
+            assert_eq!($new(0 as $t, 0 as $t, 0 as $t), a - a);
+            assert_eq!($new(4 as $t, 16 as $t, 64 as $t), a * a);
+            assert_eq!($new(4 as $t, 8 as $t, 16 as $t), a * 2 as $t);
+            assert_eq!($new(4 as $t, 8 as $t, 16 as $t), 2 as $t * a);
+            assert_eq!($new(1 as $t, 1 as $t, 1 as $t), a / a);
+            assert_eq!($new(1 as $t, 2 as $t, 4 as $t), a / 2 as $t);
+            assert_eq!($new(4 as $t, 2 as $t, 1 as $t), 8 as $t / a);
+            assert_eq!($new(0 as $t, 0 as $t, 0 as $t), a % a);
+            assert_eq!($new(0 as $t, 1 as $t, 1 as $t), a % (a - 1 as $t));
+            assert_eq!($new(0 as $t, 0 as $t, 0 as $t), a % 1 as $t);
+            assert_eq!($new(2 as $t, 1 as $t, 2 as $t), a % 3 as $t);
+            assert_eq!($new(1 as $t, 1 as $t, 1 as $t), 17 as $t % a);
+            assert_eq!($new(2 as $t, 4 as $t, 0 as $t), a % 8 as $t);
         }
 
         #[test]
@@ -114,17 +120,21 @@ macro_rules! impl_vec3_tests {
             let a = $new(1 as $t, 2 as $t, 3 as $t);
             let mut b = a;
             b += a;
-            assert_eq!((2 as $t, 4 as $t, 6 as $t), b.into());
+            assert_eq!($new(2 as $t, 4 as $t, 6 as $t), b);
             b -= a;
-            assert_eq!((1 as $t, 2 as $t, 3 as $t), b.into());
+            assert_eq!($new(1 as $t, 2 as $t, 3 as $t), b);
             b *= a;
-            assert_eq!((1 as $t, 4 as $t, 9 as $t), b.into());
+            assert_eq!($new(1 as $t, 4 as $t, 9 as $t), b);
             b /= a;
-            assert_eq!((1 as $t, 2 as $t, 3 as $t), b.into());
+            assert_eq!($new(1 as $t, 2 as $t, 3 as $t), b);
             b *= 2 as $t;
-            assert_eq!((2 as $t, 4 as $t, 6 as $t), b.into());
+            assert_eq!($new(2 as $t, 4 as $t, 6 as $t), b);
             b /= 2 as $t;
-            assert_eq!((1 as $t, 2 as $t, 3 as $t), b.into());
+            assert_eq!($new(1 as $t, 2 as $t, 3 as $t), b);
+            b %= (b + 1 as $t);
+            assert_eq!($new(1 as $t, 2 as $t, 3 as $t), b);
+            b %= b;
+            assert_eq!($new(0 as $t, 0 as $t, 0 as $t), b);
         }
 
         #[test]
diff --git a/tests/vec4.rs b/tests/vec4.rs
index 8f69a53..8088e2b 100644
--- a/tests/vec4.rs
+++ b/tests/vec4.rs
@@ -134,14 +134,20 @@ macro_rules! impl_vec4_tests {
         #[test]
         fn test_ops() {
             let a = $new(2 as $t, 4 as $t, 8 as $t, 16 as $t);
-            assert_eq!((4 as $t, 8 as $t, 16 as $t, 32 as $t), (a + a).into());
-            assert_eq!((0 as $t, 0 as $t, 0 as $t, 0 as $t), (a - a).into());
-            assert_eq!((4 as $t, 16 as $t, 64 as $t, 256 as $t), (a * a).into());
-            assert_eq!((4 as $t, 8 as $t, 16 as $t, 32 as $t), (a * 2 as $t).into());
-            assert_eq!((4 as $t, 8 as $t, 16 as $t, 32 as $t), (2 as $t * a).into());
-            assert_eq!((1 as $t, 1 as $t, 1 as $t, 1 as $t), (a / a).into());
-            assert_eq!((1 as $t, 2 as $t, 4 as $t, 8 as $t), (a / 2 as $t).into());
-            assert_eq!((8 as $t, 4 as $t, 2 as $t, 1 as $t), (16 as $t / a).into());
+            assert_eq!($new(4 as $t, 8 as $t, 16 as $t, 32 as $t), a + a);
+            assert_eq!($new(0 as $t, 0 as $t, 0 as $t, 0 as $t), a - a);
+            assert_eq!($new(4 as $t, 16 as $t, 64 as $t, 256 as $t), a * a);
+            assert_eq!($new(4 as $t, 8 as $t, 16 as $t, 32 as $t), a * 2 as $t);
+            assert_eq!($new(4 as $t, 8 as $t, 16 as $t, 32 as $t), 2 as $t * a);
+            assert_eq!($new(1 as $t, 1 as $t, 1 as $t, 1 as $t), a / a);
+            assert_eq!($new(1 as $t, 2 as $t, 4 as $t, 8 as $t), a / 2 as $t);
+            assert_eq!($new(8 as $t, 4 as $t, 2 as $t, 1 as $t), 16 as $t / a);
+            assert_eq!($new(0 as $t, 0 as $t, 0 as $t, 0 as $t), a % a);
+            assert_eq!($new(0 as $t, 1 as $t, 1 as $t, 1 as $t), a % (a - 1 as $t));
+            assert_eq!($new(0 as $t, 0 as $t, 0 as $t, 0 as $t), a % 1 as $t);
+            assert_eq!($new(2 as $t, 1 as $t, 2 as $t, 1 as $t), a % 3 as $t);
+            assert_eq!($new(1 as $t, 1 as $t, 1 as $t, 1 as $t), 17 as $t % a);
+            assert_eq!($new(2 as $t, 4 as $t, 0 as $t, 0 as $t), a % 8 as $t);
         }
 
         #[test]
@@ -149,17 +155,21 @@ macro_rules! impl_vec4_tests {
             let a = $new(1 as $t, 2 as $t, 3 as $t, 4 as $t);
             let mut b = a;
             b += a;
-            assert_eq!((2 as $t, 4 as $t, 6 as $t, 8 as $t), b.into());
+            assert_eq!($new(2 as $t, 4 as $t, 6 as $t, 8 as $t), b);
             b -= a;
-            assert_eq!((1 as $t, 2 as $t, 3 as $t, 4 as $t), b.into());
+            assert_eq!($new(1 as $t, 2 as $t, 3 as $t, 4 as $t), b);
             b *= a;
-            assert_eq!((1 as $t, 4 as $t, 9 as $t, 16 as $t), b.into());
+            assert_eq!($new(1 as $t, 4 as $t, 9 as $t, 16 as $t), b);
             b /= a;
-            assert_eq!((1 as $t, 2 as $t, 3 as $t, 4 as $t), b.into());
+            assert_eq!($new(1 as $t, 2 as $t, 3 as $t, 4 as $t), b);
             b *= 2 as $t;
-            assert_eq!((2 as $t, 4 as $t, 6 as $t, 8 as $t), b.into());
+            assert_eq!($new(2 as $t, 4 as $t, 6 as $t, 8 as $t), b);
             b /= 2 as $t;
-            assert_eq!((1 as $t, 2 as $t, 3 as $t, 4 as $t), b.into());
+            assert_eq!($new(1 as $t, 2 as $t, 3 as $t, 4 as $t), b);
+            b %= (b + 1 as $t);
+            assert_eq!($new(1 as $t, 2 as $t, 3 as $t, 4 as $t), b);
+            b %= b;
+            assert_eq!($new(0 as $t, 0 as $t, 0 as $t, 0 as $t), b);
         }
 
         #[test]