Repository: bitshifter/glam-rs
Pull request #371 (2022-12-28 08:47:58)

Add copysign operation and unit tests for it
Just mirror's Rust's existing `f32/f64::copysign` functionality but vectorized component-wise.

It turns out that this operation is exactly whats needed when computing the support point of a box given its extents and a direction. Example usage from a physics engine I'm working on that prompted me to make this PR:

```rs
fn support_point(&self, dir: Vec3A) -> Vec3A {
    self.extents.copysign(dir)
}
```


Patch:
diff --git a/codegen/templates/vec.rs.tera b/codegen/templates/vec.rs.tera
index 511ce3d..b1a79a7 100644
--- a/codegen/templates/vec.rs.tera
+++ b/codegen/templates/vec.rs.tera
@@ -829,6 +829,32 @@ impl {{ self_t }} {
         {% endif %}
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        {% if is_scalar and is_float %}
+            Self {
+                {% for c in components %}
+                    {{ c }}: self.{{ c }}.copysign(rhs.{{ c }}),
+                {%- endfor %}
+            }
+        {% elif is_scalar %}
+            Self::select(rhs.cmpge(Self::ZERO), self, -self)
+        {% elif is_coresimd %}
+            Self(self.0.copysign(rhs.0))
+        {% elif is_sse2 %}
+            unsafe {
+                let mask = Self::splat(-0.0);
+                Self(_mm_or_ps(_mm_and_ps(rhs.0, mask.0), _mm_andnot_ps(mask.0, self.0)))
+            }
+        {% elif is_wasm32 %}
+            unsafe {
+                let mask = Self::splat(-0.0);
+                Self(v128_or(v128_and(rhs.0, mask.0), v128_andnot(self.0, mask.0)))
+            }
+        {% endif %}
+    }
+
     /// Returns a bitmask with the lowest {{ dim }} bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/f32/coresimd/vec3a.rs b/src/f32/coresimd/vec3a.rs
index adaceb6..86e16d9 100644
--- a/src/f32/coresimd/vec3a.rs
+++ b/src/f32/coresimd/vec3a.rs
@@ -292,6 +292,12 @@ impl Vec3A {
         Self(self.0.signum())
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        Self(self.0.copysign(rhs.0))
+    }
+
     /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/f32/coresimd/vec4.rs b/src/f32/coresimd/vec4.rs
index 61b4903..bc26d66 100644
--- a/src/f32/coresimd/vec4.rs
+++ b/src/f32/coresimd/vec4.rs
@@ -268,6 +268,12 @@ impl Vec4 {
         Self(self.0.signum())
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        Self(self.0.copysign(rhs.0))
+    }
+
     /// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/f32/scalar/vec3a.rs b/src/f32/scalar/vec3a.rs
index 808e480..311e1e0 100644
--- a/src/f32/scalar/vec3a.rs
+++ b/src/f32/scalar/vec3a.rs
@@ -312,6 +312,16 @@ impl Vec3A {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        Self {
+            x: self.x.copysign(rhs.x),
+            y: self.y.copysign(rhs.y),
+            z: self.z.copysign(rhs.z),
+        }
+    }
+
     /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/f32/scalar/vec4.rs b/src/f32/scalar/vec4.rs
index cf791cf..17c84a4 100644
--- a/src/f32/scalar/vec4.rs
+++ b/src/f32/scalar/vec4.rs
@@ -339,6 +339,17 @@ impl Vec4 {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        Self {
+            x: self.x.copysign(rhs.x),
+            y: self.y.copysign(rhs.y),
+            z: self.z.copysign(rhs.z),
+            w: self.w.copysign(rhs.w),
+        }
+    }
+
     /// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/f32/sse2/vec3a.rs b/src/f32/sse2/vec3a.rs
index 7fa8a6a..c13d9bf 100644
--- a/src/f32/sse2/vec3a.rs
+++ b/src/f32/sse2/vec3a.rs
@@ -319,6 +319,18 @@ impl Vec3A {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        unsafe {
+            let mask = Self::splat(-0.0);
+            Self(_mm_or_ps(
+                _mm_and_ps(rhs.0, mask.0),
+                _mm_andnot_ps(mask.0, self.0),
+            ))
+        }
+    }
+
     /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/f32/sse2/vec4.rs b/src/f32/sse2/vec4.rs
index 0e0882c..f1a1311 100644
--- a/src/f32/sse2/vec4.rs
+++ b/src/f32/sse2/vec4.rs
@@ -294,6 +294,18 @@ impl Vec4 {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        unsafe {
+            let mask = Self::splat(-0.0);
+            Self(_mm_or_ps(
+                _mm_and_ps(rhs.0, mask.0),
+                _mm_andnot_ps(mask.0, self.0),
+            ))
+        }
+    }
+
     /// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/f32/vec2.rs b/src/f32/vec2.rs
index 16bd17d..fc92447 100644
--- a/src/f32/vec2.rs
+++ b/src/f32/vec2.rs
@@ -264,6 +264,15 @@ impl Vec2 {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        Self {
+            x: self.x.copysign(rhs.x),
+            y: self.y.copysign(rhs.y),
+        }
+    }
+
     /// Returns a bitmask with the lowest 2 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/f32/vec3.rs b/src/f32/vec3.rs
index b049ed8..2838a63 100644
--- a/src/f32/vec3.rs
+++ b/src/f32/vec3.rs
@@ -306,6 +306,16 @@ impl Vec3 {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        Self {
+            x: self.x.copysign(rhs.x),
+            y: self.y.copysign(rhs.y),
+            z: self.z.copysign(rhs.z),
+        }
+    }
+
     /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/f32/wasm32/vec3a.rs b/src/f32/wasm32/vec3a.rs
index 85b3fe4..33df7cf 100644
--- a/src/f32/wasm32/vec3a.rs
+++ b/src/f32/wasm32/vec3a.rs
@@ -300,6 +300,18 @@ impl Vec3A {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        unsafe {
+            let mask = Self::splat(-0.0);
+            Self(v128_or(
+                v128_and(rhs.0, mask.0),
+                v128_andnot(self.0, mask.0),
+            ))
+        }
+    }
+
     /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/f32/wasm32/vec4.rs b/src/f32/wasm32/vec4.rs
index ab53d7a..10f3927 100644
--- a/src/f32/wasm32/vec4.rs
+++ b/src/f32/wasm32/vec4.rs
@@ -282,6 +282,18 @@ impl Vec4 {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        unsafe {
+            let mask = Self::splat(-0.0);
+            Self(v128_or(
+                v128_and(rhs.0, mask.0),
+                v128_andnot(self.0, mask.0),
+            ))
+        }
+    }
+
     /// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/f64/dvec2.rs b/src/f64/dvec2.rs
index c20404b..c52356e 100644
--- a/src/f64/dvec2.rs
+++ b/src/f64/dvec2.rs
@@ -264,6 +264,15 @@ impl DVec2 {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        Self {
+            x: self.x.copysign(rhs.x),
+            y: self.y.copysign(rhs.y),
+        }
+    }
+
     /// Returns a bitmask with the lowest 2 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/f64/dvec3.rs b/src/f64/dvec3.rs
index 8cfaef8..ffdc6f2 100644
--- a/src/f64/dvec3.rs
+++ b/src/f64/dvec3.rs
@@ -306,6 +306,16 @@ impl DVec3 {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        Self {
+            x: self.x.copysign(rhs.x),
+            y: self.y.copysign(rhs.y),
+            z: self.z.copysign(rhs.z),
+        }
+    }
+
     /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/f64/dvec4.rs b/src/f64/dvec4.rs
index 62ccf4f..de08d42 100644
--- a/src/f64/dvec4.rs
+++ b/src/f64/dvec4.rs
@@ -331,6 +331,17 @@ impl DVec4 {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        Self {
+            x: self.x.copysign(rhs.x),
+            y: self.y.copysign(rhs.y),
+            z: self.z.copysign(rhs.z),
+            w: self.w.copysign(rhs.w),
+        }
+    }
+
     /// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/i32/ivec2.rs b/src/i32/ivec2.rs
index 87b5122..3748b1c 100644
--- a/src/i32/ivec2.rs
+++ b/src/i32/ivec2.rs
@@ -258,6 +258,12 @@ impl IVec2 {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        Self::select(rhs.cmpge(Self::ZERO), self, -self)
+    }
+
     /// Returns a bitmask with the lowest 2 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/i32/ivec3.rs b/src/i32/ivec3.rs
index 6ccd5f9..ee993b7 100644
--- a/src/i32/ivec3.rs
+++ b/src/i32/ivec3.rs
@@ -300,6 +300,12 @@ impl IVec3 {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        Self::select(rhs.cmpge(Self::ZERO), self, -self)
+    }
+
     /// Returns a bitmask with the lowest 3 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes
diff --git a/src/i32/ivec4.rs b/src/i32/ivec4.rs
index 5d875b2..3912ee7 100644
--- a/src/i32/ivec4.rs
+++ b/src/i32/ivec4.rs
@@ -325,6 +325,12 @@ impl IVec4 {
         }
     }
 
+    /// Returns a vector with signs of `rhs` and the magnitudes of `self`.
+    #[inline]
+    pub fn copysign(self, rhs: Self) -> Self {
+        Self::select(rhs.cmpge(Self::ZERO), self, -self)
+    }
+
     /// Returns a bitmask with the lowest 4 bits set to the sign bits from the elements of `self`.
     ///
     /// A negative element results in a `1` bit and a positive element in a `0` bit.  Element `x` goes


Test patch:
diff --git a/tests/vec2.rs b/tests/vec2.rs
index 8ebdd62..65bc6a2 100644
--- a/tests/vec2.rs
+++ b/tests/vec2.rs
@@ -593,7 +593,7 @@ macro_rules! impl_vec2_float_tests {
             should_glam_assert!({ $vec2::ONE.reject_from_normalized($vec2::ONE) });
         });
 
-        glam_test!(test_sign, {
+        glam_test!(test_signum, {
             assert_eq!($vec2::ZERO.signum(), $vec2::ONE);
             assert_eq!((-$vec2::ZERO).signum(), -$vec2::ONE);
             assert_eq!($vec2::ONE.signum(), $vec2::ONE);
@@ -603,6 +603,43 @@ macro_rules! impl_vec2_float_tests {
             assert!($vec2::splat(NAN).signum().is_nan_mask().all());
         });
 
+        glam_test!(test_copysign, {
+            assert_eq!($vec2::ZERO.copysign(-$vec2::ZERO), -$vec2::ZERO);
+            assert_eq!((-$vec2::ZERO).copysign(-$vec2::ZERO), -$vec2::ZERO);
+            assert_eq!($vec2::ZERO.copysign($vec2::ZERO), $vec2::ZERO);
+            assert_eq!((-$vec2::ZERO).copysign($vec2::ZERO), $vec2::ZERO);
+            assert_eq!($vec2::ONE.copysign(-$vec2::ZERO), -$vec2::ONE);
+            assert_eq!((-$vec2::ONE).copysign(-$vec2::ZERO), -$vec2::ONE);
+            assert_eq!($vec2::ONE.copysign($vec2::ZERO), $vec2::ONE);
+            assert_eq!((-$vec2::ONE).copysign($vec2::ZERO), $vec2::ONE);
+            assert_eq!($vec2::ZERO.copysign(-$vec2::ONE), -$vec2::ZERO);
+            assert_eq!((-$vec2::ZERO).copysign(-$vec2::ONE), -$vec2::ZERO);
+            assert_eq!($vec2::ZERO.copysign($vec2::ONE), $vec2::ZERO);
+            assert_eq!((-$vec2::ZERO).copysign($vec2::ONE), $vec2::ZERO);
+            assert_eq!($vec2::ONE.copysign(-$vec2::ONE), -$vec2::ONE);
+            assert_eq!((-$vec2::ONE).copysign(-$vec2::ONE), -$vec2::ONE);
+            assert_eq!($vec2::ONE.copysign($vec2::ONE), $vec2::ONE);
+            assert_eq!((-$vec2::ONE).copysign($vec2::ONE), $vec2::ONE);
+            assert_eq!(
+                $vec2::splat(INFINITY).copysign($vec2::ONE),
+                $vec2::splat(INFINITY)
+            );
+            assert_eq!(
+                $vec2::splat(INFINITY).copysign(-$vec2::ONE),
+                $vec2::splat(NEG_INFINITY)
+            );
+            assert_eq!(
+                $vec2::splat(NEG_INFINITY).copysign($vec2::ONE),
+                $vec2::splat(INFINITY)
+            );
+            assert_eq!(
+                $vec2::splat(NEG_INFINITY).copysign(-$vec2::ONE),
+                $vec2::splat(NEG_INFINITY)
+            );
+            assert!($vec2::splat(NAN).copysign($vec2::ONE).is_nan_mask().all());
+            assert!($vec2::splat(NAN).copysign(-$vec2::ONE).is_nan_mask().all());
+        });
+
         glam_test!(test_is_negative_bitmask, {
             assert_eq!($vec2::ZERO.is_negative_bitmask(), 0b00);
             assert_eq!((-$vec2::ZERO).is_negative_bitmask(), 0b11);
diff --git a/tests/vec3.rs b/tests/vec3.rs
index e144e3d..5df8d09 100644
--- a/tests/vec3.rs
+++ b/tests/vec3.rs
@@ -665,6 +665,43 @@ macro_rules! impl_vec3_float_tests {
             assert!($vec3::splat(NAN).signum().is_nan_mask().all());
         });
 
+        glam_test!(test_copysign, {
+            assert_eq!($vec3::ZERO.copysign(-$vec3::ZERO), -$vec3::ZERO);
+            assert_eq!((-$vec3::ZERO).copysign(-$vec3::ZERO), -$vec3::ZERO);
+            assert_eq!($vec3::ZERO.copysign($vec3::ZERO), $vec3::ZERO);
+            assert_eq!((-$vec3::ZERO).copysign($vec3::ZERO), $vec3::ZERO);
+            assert_eq!($vec3::ONE.copysign(-$vec3::ZERO), -$vec3::ONE);
+            assert_eq!((-$vec3::ONE).copysign(-$vec3::ZERO), -$vec3::ONE);
+            assert_eq!($vec3::ONE.copysign($vec3::ZERO), $vec3::ONE);
+            assert_eq!((-$vec3::ONE).copysign($vec3::ZERO), $vec3::ONE);
+            assert_eq!($vec3::ZERO.copysign(-$vec3::ONE), -$vec3::ZERO);
+            assert_eq!((-$vec3::ZERO).copysign(-$vec3::ONE), -$vec3::ZERO);
+            assert_eq!($vec3::ZERO.copysign($vec3::ONE), $vec3::ZERO);
+            assert_eq!((-$vec3::ZERO).copysign($vec3::ONE), $vec3::ZERO);
+            assert_eq!($vec3::ONE.copysign(-$vec3::ONE), -$vec3::ONE);
+            assert_eq!((-$vec3::ONE).copysign(-$vec3::ONE), -$vec3::ONE);
+            assert_eq!($vec3::ONE.copysign($vec3::ONE), $vec3::ONE);
+            assert_eq!((-$vec3::ONE).copysign($vec3::ONE), $vec3::ONE);
+            assert_eq!(
+                $vec3::splat(INFINITY).copysign($vec3::ONE),
+                $vec3::splat(INFINITY)
+            );
+            assert_eq!(
+                $vec3::splat(INFINITY).copysign(-$vec3::ONE),
+                $vec3::splat(NEG_INFINITY)
+            );
+            assert_eq!(
+                $vec3::splat(NEG_INFINITY).copysign($vec3::ONE),
+                $vec3::splat(INFINITY)
+            );
+            assert_eq!(
+                $vec3::splat(NEG_INFINITY).copysign(-$vec3::ONE),
+                $vec3::splat(NEG_INFINITY)
+            );
+            assert!($vec3::splat(NAN).copysign($vec3::ONE).is_nan_mask().all());
+            assert!($vec3::splat(NAN).copysign(-$vec3::ONE).is_nan_mask().all());
+        });
+
         glam_test!(test_is_negative_bitmask, {
             assert_eq!($vec3::ZERO.is_negative_bitmask(), 0b000);
             assert_eq!((-$vec3::ZERO).is_negative_bitmask(), 0b111);
diff --git a/tests/vec4.rs b/tests/vec4.rs
index 47f4fa8..ce86019 100644
--- a/tests/vec4.rs
+++ b/tests/vec4.rs
@@ -747,6 +747,43 @@ macro_rules! impl_vec4_float_tests {
             assert!($vec4::splat(NAN).signum().is_nan_mask().all());
         });
 
+        glam_test!(test_copysign, {
+            assert_eq!($vec4::ZERO.copysign(-$vec4::ZERO), -$vec4::ZERO);
+            assert_eq!((-$vec4::ZERO).copysign(-$vec4::ZERO), -$vec4::ZERO);
+            assert_eq!($vec4::ZERO.copysign($vec4::ZERO), $vec4::ZERO);
+            assert_eq!((-$vec4::ZERO).copysign($vec4::ZERO), $vec4::ZERO);
+            assert_eq!($vec4::ONE.copysign(-$vec4::ZERO), -$vec4::ONE);
+            assert_eq!((-$vec4::ONE).copysign(-$vec4::ZERO), -$vec4::ONE);
+            assert_eq!($vec4::ONE.copysign($vec4::ZERO), $vec4::ONE);
+            assert_eq!((-$vec4::ONE).copysign($vec4::ZERO), $vec4::ONE);
+            assert_eq!($vec4::ZERO.copysign(-$vec4::ONE), -$vec4::ZERO);
+            assert_eq!((-$vec4::ZERO).copysign(-$vec4::ONE), -$vec4::ZERO);
+            assert_eq!($vec4::ZERO.copysign($vec4::ONE), $vec4::ZERO);
+            assert_eq!((-$vec4::ZERO).copysign($vec4::ONE), $vec4::ZERO);
+            assert_eq!($vec4::ONE.copysign(-$vec4::ONE), -$vec4::ONE);
+            assert_eq!((-$vec4::ONE).copysign(-$vec4::ONE), -$vec4::ONE);
+            assert_eq!($vec4::ONE.copysign($vec4::ONE), $vec4::ONE);
+            assert_eq!((-$vec4::ONE).copysign($vec4::ONE), $vec4::ONE);
+            assert_eq!(
+                $vec4::splat(INFINITY).copysign($vec4::ONE),
+                $vec4::splat(INFINITY)
+            );
+            assert_eq!(
+                $vec4::splat(INFINITY).copysign(-$vec4::ONE),
+                $vec4::splat(NEG_INFINITY)
+            );
+            assert_eq!(
+                $vec4::splat(NEG_INFINITY).copysign($vec4::ONE),
+                $vec4::splat(INFINITY)
+            );
+            assert_eq!(
+                $vec4::splat(NEG_INFINITY).copysign(-$vec4::ONE),
+                $vec4::splat(NEG_INFINITY)
+            );
+            assert!($vec4::splat(NAN).copysign($vec4::ONE).is_nan_mask().all());
+            assert!($vec4::splat(NAN).copysign(-$vec4::ONE).is_nan_mask().all());
+        });
+
         glam_test!(test_is_negative_bitmask, {
             assert_eq!($vec4::ZERO.is_negative_bitmask(), 0b0000);
             assert_eq!((-$vec4::ZERO).is_negative_bitmask(), 0b1111);