Repository: bitshifter/glam-rs
Pull request #237 (2021-09-19 22:01:46)

impl Not, Shl, Shr, BitAnd, BitOr, and BitXor for UVec and IVec
#236 

bonsairobo: @bitshifter Yea happy to address all of your feedback and add tests.
bonsairobo: I just learned some surprising stuff about shifting by negative numbers. Apparently, Rust won't let you shift by a negative number, since it would overflow. I assumed that it would just reverse the direction of the shift, but instead the negative RHS will get interpreted as unsigned (2s complement), and only the lower bits (within the LHS range) will get used by the shift operation.

So there is perhaps a judgement call to be made here: do we want to allow shifting by signed integers? I would side with whatever Rust does, which is to allow it, but it's only valid (at runtime) if the RHS is nonnegative.
bonsairobo: The last patch makes all of the following possible in 2, 3, or 4 dimensions:

```
IVec << i8
IVec << i16
IVec << i32
IVec << u8
IVec << u16
IVec << u32

IVec << IVec
IVec << UVec

UVec << i8
UVec << i16
UVec << i32
UVec << u8
UVec << u16
UVec << u32

UVec << UVec
UVec << IVec
```
bonsairobo: @bitshifter Any plans to support SIMD for `UVec` and `IVec`? I would be interested in that functionality.
bitshifter: > 
> 
> I just learned some surprising stuff about shifting by negative numbers. Apparently, Rust won't let you shift by a negative number, since it would overflow. I assumed that it would just reverse the direction of the shift, but instead the negative RHS will get interpreted as unsigned (2s complement), and only the lower bits (within the LHS range) will get used by the shift operation.
> 
> So there is perhaps a judgement call to be made here: do we want to allow shifting by signed integers? I would side with whatever Rust does, which is to allow it, but it's only valid (at runtime) if the RHS is nonnegative.

I was kind of surprised that these ops were supported by signed integers!

Behaviour wise I tend to stick with whatever Rust does. As to whether shifting by a signed integer should be supported at all, I'm not sure. I tend to take a minimalist approach to features with glam. Generally if I don't use them and no one has asked for them (supported by a real use case) I don't tend to add things. So if you have no use for shifting by signed integers, or even different sizes (e.g. `i8`/`u8` etc.) then we could not add them for now. 
bonsairobo: I need at least the scalar ops for `u8` and `i32`. It wasn't much effort to add the rest. I'd like to keep them all for completeness.

Patch:
diff --git a/src/core/scalar/vector.rs b/src/core/scalar/vector.rs
index 3f0ee45..f0f2370 100644
--- a/src/core/scalar/vector.rs
+++ b/src/core/scalar/vector.rs
@@ -1096,3 +1096,369 @@ impl SignedVector<f32> for XYZF32A16 {
 
 impl SignedVector3<f32> for XYZF32A16 {}
 impl FloatVector3<f32> for XYZF32A16 {}
+
+// 2D bitwise and shifting
+
+impl<T, Rhs> ScalarShiftOps<Rhs> for XY<T>
+where
+    T: IntegerShiftOps<Rhs>,
+    Rhs: Copy,
+{
+    #[inline(always)]
+    fn scalar_shl(self, rhs: Rhs) -> Self {
+        Self {
+            x: self.x << rhs,
+            y: self.y << rhs,
+        }
+    }
+
+    #[inline(always)]
+    fn scalar_shr(self, rhs: Rhs) -> Self {
+        Self {
+            x: self.x >> rhs,
+            y: self.y >> rhs,
+        }
+    }
+}
+
+impl<T> ScalarBitOps<T> for XY<T>
+where
+    T: Copy + IntegerBitOps,
+{
+    #[inline(always)]
+    fn scalar_bitand(self, rhs: T) -> Self {
+        Self {
+            x: self.x & rhs,
+            y: self.y & rhs,
+        }
+    }
+
+    #[inline(always)]
+    fn scalar_bitor(self, rhs: T) -> Self {
+        Self {
+            x: self.x | rhs,
+            y: self.y | rhs,
+        }
+    }
+
+    #[inline(always)]
+    fn scalar_bitxor(self, rhs: T) -> Self {
+        Self {
+            x: self.x ^ rhs,
+            y: self.y ^ rhs,
+        }
+    }
+}
+
+impl<T, Rhs> VectorShiftOps<XY<Rhs>> for XY<T>
+where
+    T: Copy + IntegerShiftOps<Rhs>,
+{
+    #[inline(always)]
+    fn vector_shl(self, rhs: XY<Rhs>) -> Self {
+        Self {
+            x: self.x << rhs.x,
+            y: self.y << rhs.y,
+        }
+    }
+
+    #[inline(always)]
+    fn vector_shr(self, rhs: XY<Rhs>) -> Self {
+        Self {
+            x: self.x >> rhs.x,
+            y: self.y >> rhs.y,
+        }
+    }
+}
+
+impl<T> VectorBitOps<XY<T>> for XY<T>
+where
+    T: Copy + IntegerBitOps,
+{
+    #[inline(always)]
+    fn not(self) -> Self {
+        Self {
+            x: !self.x,
+            y: !self.y,
+        }
+    }
+
+    #[inline(always)]
+    fn vector_bitand(self, rhs: Self) -> Self {
+        Self {
+            x: self.x & rhs.x,
+            y: self.y & rhs.y,
+        }
+    }
+
+    #[inline(always)]
+    fn vector_bitor(self, rhs: Self) -> Self {
+        Self {
+            x: self.x | rhs.x,
+            y: self.y | rhs.y,
+        }
+    }
+
+    #[inline(always)]
+    fn vector_bitxor(self, rhs: Self) -> Self {
+        Self {
+            x: self.x ^ rhs.x,
+            y: self.y ^ rhs.y,
+        }
+    }
+}
+
+// 3D bitwise and shifting
+
+impl<T, Rhs> ScalarShiftOps<Rhs> for XYZ<T>
+where
+    T: IntegerShiftOps<Rhs>,
+    Rhs: Copy,
+{
+    #[inline(always)]
+    fn scalar_shl(self, rhs: Rhs) -> Self {
+        Self {
+            x: self.x << rhs,
+            y: self.y << rhs,
+            z: self.z << rhs,
+        }
+    }
+
+    #[inline(always)]
+    fn scalar_shr(self, rhs: Rhs) -> Self {
+        Self {
+            x: self.x >> rhs,
+            y: self.y >> rhs,
+            z: self.z >> rhs,
+        }
+    }
+}
+
+impl<T> ScalarBitOps<T> for XYZ<T>
+where
+    T: Copy + IntegerBitOps,
+{
+    #[inline(always)]
+    fn scalar_bitand(self, rhs: T) -> Self {
+        Self {
+            x: self.x & rhs,
+            y: self.y & rhs,
+            z: self.z & rhs,
+        }
+    }
+
+    #[inline(always)]
+    fn scalar_bitor(self, rhs: T) -> Self {
+        Self {
+            x: self.x | rhs,
+            y: self.y | rhs,
+            z: self.z | rhs,
+        }
+    }
+
+    #[inline(always)]
+    fn scalar_bitxor(self, rhs: T) -> Self {
+        Self {
+            x: self.x ^ rhs,
+            y: self.y ^ rhs,
+            z: self.z ^ rhs,
+        }
+    }
+}
+
+impl<T, Rhs> VectorShiftOps<XYZ<Rhs>> for XYZ<T>
+where
+    T: Copy + IntegerShiftOps<Rhs>,
+{
+    #[inline(always)]
+    fn vector_shl(self, rhs: XYZ<Rhs>) -> Self {
+        Self {
+            x: self.x << rhs.x,
+            y: self.y << rhs.y,
+            z: self.z << rhs.z,
+        }
+    }
+
+    #[inline(always)]
+    fn vector_shr(self, rhs: XYZ<Rhs>) -> Self {
+        Self {
+            x: self.x >> rhs.x,
+            y: self.y >> rhs.y,
+            z: self.z >> rhs.z,
+        }
+    }
+}
+
+impl<T> VectorBitOps<XYZ<T>> for XYZ<T>
+where
+    T: Copy + IntegerBitOps,
+{
+    #[inline(always)]
+    fn not(self) -> Self {
+        Self {
+            x: !self.x,
+            y: !self.y,
+            z: !self.z,
+        }
+    }
+
+    #[inline(always)]
+    fn vector_bitand(self, rhs: Self) -> Self {
+        Self {
+            x: self.x & rhs.x,
+            y: self.y & rhs.y,
+            z: self.z & rhs.z,
+        }
+    }
+
+    #[inline(always)]
+    fn vector_bitor(self, rhs: Self) -> Self {
+        Self {
+            x: self.x | rhs.x,
+            y: self.y | rhs.y,
+            z: self.z | rhs.z,
+        }
+    }
+
+    #[inline(always)]
+    fn vector_bitxor(self, rhs: Self) -> Self {
+        Self {
+            x: self.x ^ rhs.x,
+            y: self.y ^ rhs.y,
+            z: self.z ^ rhs.z,
+        }
+    }
+}
+
+// 4D bitwise and shifting
+
+impl<T, Rhs> ScalarShiftOps<Rhs> for XYZW<T>
+where
+    T: IntegerShiftOps<Rhs>,
+    Rhs: Copy,
+{
+    #[inline(always)]
+    fn scalar_shl(self, rhs: Rhs) -> Self {
+        Self {
+            x: self.x << rhs,
+            y: self.y << rhs,
+            z: self.z << rhs,
+            w: self.w << rhs,
+        }
+    }
+
+    #[inline(always)]
+    fn scalar_shr(self, rhs: Rhs) -> Self {
+        Self {
+            x: self.x >> rhs,
+            y: self.y >> rhs,
+            z: self.z >> rhs,
+            w: self.w >> rhs,
+        }
+    }
+}
+
+impl<T> ScalarBitOps<T> for XYZW<T>
+where
+    T: Copy + IntegerBitOps,
+{
+    #[inline(always)]
+    fn scalar_bitand(self, rhs: T) -> Self {
+        Self {
+            x: self.x & rhs,
+            y: self.y & rhs,
+            z: self.z & rhs,
+            w: self.w & rhs,
+        }
+    }
+
+    #[inline(always)]
+    fn scalar_bitor(self, rhs: T) -> Self {
+        Self {
+            x: self.x | rhs,
+            y: self.y | rhs,
+            z: self.z | rhs,
+            w: self.w | rhs,
+        }
+    }
+
+    #[inline(always)]
+    fn scalar_bitxor(self, rhs: T) -> Self {
+        Self {
+            x: self.x ^ rhs,
+            y: self.y ^ rhs,
+            z: self.z ^ rhs,
+            w: self.w ^ rhs,
+        }
+    }
+}
+
+impl<T, Rhs> VectorShiftOps<XYZW<Rhs>> for XYZW<T>
+where
+    T: Copy + IntegerShiftOps<Rhs>,
+{
+    #[inline(always)]
+    fn vector_shl(self, rhs: XYZW<Rhs>) -> Self {
+        Self {
+            x: self.x << rhs.x,
+            y: self.y << rhs.y,
+            z: self.z << rhs.z,
+            w: self.w << rhs.w,
+        }
+    }
+
+    #[inline(always)]
+    fn vector_shr(self, rhs: XYZW<Rhs>) -> Self {
+        Self {
+            x: self.x >> rhs.x,
+            y: self.y >> rhs.y,
+            z: self.z >> rhs.z,
+            w: self.w >> rhs.w,
+        }
+    }
+}
+
+impl<T> VectorBitOps<XYZW<T>> for XYZW<T>
+where
+    T: Copy + IntegerBitOps,
+{
+    #[inline(always)]
+    fn not(self) -> Self {
+        Self {
+            x: !self.x,
+            y: !self.y,
+            z: !self.z,
+            w: !self.w,
+        }
+    }
+
+    #[inline(always)]
+    fn vector_bitand(self, rhs: Self) -> Self {
+        Self {
+            x: self.x & rhs.x,
+            y: self.y & rhs.y,
+            z: self.z & rhs.z,
+            w: self.w & rhs.w,
+        }
+    }
+
+    #[inline(always)]
+    fn vector_bitor(self, rhs: Self) -> Self {
+        Self {
+            x: self.x | rhs.x,
+            y: self.y | rhs.y,
+            z: self.z | rhs.z,
+            w: self.w | rhs.w,
+        }
+    }
+
+    #[inline(always)]
+    fn vector_bitxor(self, rhs: Self) -> Self {
+        Self {
+            x: self.x ^ rhs.x,
+            y: self.y ^ rhs.y,
+            z: self.z ^ rhs.z,
+            w: self.w ^ rhs.w,
+        }
+    }
+}
diff --git a/src/core/traits/scalar.rs b/src/core/traits/scalar.rs
index bd7749a..f3817d5 100644
--- a/src/core/traits/scalar.rs
+++ b/src/core/traits/scalar.rs
@@ -7,7 +7,7 @@ pub use num_traits::{Float, Num, Signed};
 
 use core::{
     marker::Sized,
-    ops::{Add, Div, Mul, Rem, Sub},
+    ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Not, Rem, Shl, Shr, Sub},
 };
 
 // Stub the necessary parts of num traits
@@ -320,6 +320,30 @@ impl NumEx for u32 {
     }
 }
 
+pub trait IntegerShiftOps<Rhs>: Sized + Shl<Rhs, Output = Self> + Shr<Rhs, Output = Self> {}
+
+pub trait IntegerBitOps:
+    Sized + Not<Output = Self> + BitAnd<Output = Self> + BitOr<Output = Self> + BitXor<Output = Self>
+{
+}
+
+impl IntegerShiftOps<i8> for i32 {}
+impl IntegerShiftOps<i16> for i32 {}
+impl IntegerShiftOps<i32> for i32 {}
+impl IntegerShiftOps<u8> for i32 {}
+impl IntegerShiftOps<u16> for i32 {}
+impl IntegerShiftOps<u32> for i32 {}
+
+impl IntegerShiftOps<i8> for u32 {}
+impl IntegerShiftOps<i16> for u32 {}
+impl IntegerShiftOps<i32> for u32 {}
+impl IntegerShiftOps<u8> for u32 {}
+impl IntegerShiftOps<u16> for u32 {}
+impl IntegerShiftOps<u32> for u32 {}
+
+impl IntegerBitOps for i32 {}
+impl IntegerBitOps for u32 {}
+
 #[cfg(test)]
 macro_rules! assert_approx_eq {
     ($a:expr, $b:expr) => {{
diff --git a/src/core/traits/vector.rs b/src/core/traits/vector.rs
index 6ddbb1d..ac67bbc 100644
--- a/src/core/traits/vector.rs
+++ b/src/core/traits/vector.rs
@@ -803,3 +803,26 @@ pub trait FloatVector4<T: FloatEx>: SignedVector4<T> {
         self.sub(other).abs().cmple(Self::splat(max_abs_diff)).all()
     }
 }
+
+pub trait ScalarShiftOps<Rhs> {
+    fn scalar_shl(self, rhs: Rhs) -> Self;
+    fn scalar_shr(self, rhs: Rhs) -> Self;
+}
+
+pub trait VectorShiftOps<Rhs> {
+    fn vector_shl(self, rhs: Rhs) -> Self;
+    fn vector_shr(self, rhs: Rhs) -> Self;
+}
+
+pub trait ScalarBitOps<Rhs> {
+    fn scalar_bitand(self, rhs: Rhs) -> Self;
+    fn scalar_bitor(self, rhs: Rhs) -> Self;
+    fn scalar_bitxor(self, rhs: Rhs) -> Self;
+}
+
+pub trait VectorBitOps<Rhs> {
+    fn not(self) -> Self;
+    fn vector_bitand(self, rhs: Rhs) -> Self;
+    fn vector_bitor(self, rhs: Rhs) -> Self;
+    fn vector_bitxor(self, rhs: Rhs) -> Self;
+}
diff --git a/src/vec.rs b/src/vec.rs
index e174906..dfe1a59 100644
--- a/src/vec.rs
+++ b/src/vec.rs
@@ -777,6 +777,121 @@ macro_rules! impl_vecn_signed_traits {
     };
 }
 
+macro_rules! impl_vecn_shift_op_traits {
+    ($vecn:ident, $rhs:ty, $inner:ident) => {
+        impl Shl<$rhs> for $vecn {
+            type Output = Self;
+
+            #[inline(always)]
+            fn shl(self, rhs: $rhs) -> Self::Output {
+                $vecn($inner::vector_shl(self.0, rhs.0))
+            }
+        }
+
+        impl Shr<$rhs> for $vecn {
+            type Output = Self;
+
+            #[inline(always)]
+            fn shr(self, rhs: $rhs) -> Self::Output {
+                $vecn($inner::vector_shr(self.0, rhs.0))
+            }
+        }
+    };
+}
+
+macro_rules! impl_vecn_scalar_shift_op_traits {
+    ($vecn:ident, $rhs:ty, $inner:ident) => {
+        impl Shl<$rhs> for $vecn {
+            type Output = Self;
+
+            #[inline(always)]
+            fn shl(self, rhs: $rhs) -> Self::Output {
+                $vecn($inner::scalar_shl(self.0, rhs))
+            }
+        }
+
+        impl Shr<$rhs> for $vecn {
+            type Output = Self;
+
+            #[inline(always)]
+            fn shr(self, rhs: $rhs) -> Self::Output {
+                $vecn($inner::scalar_shr(self.0, rhs))
+            }
+        }
+    };
+}
+
+macro_rules! impl_vecn_bit_op_traits {
+    ($vecn:ident, $inner:ident) => {
+        impl Not for $vecn {
+            type Output = Self;
+
+            #[inline(always)]
+            fn not(self) -> Self::Output {
+                $vecn($inner::not(self.0))
+            }
+        }
+
+        impl BitAnd for $vecn {
+            type Output = Self;
+
+            #[inline(always)]
+            fn bitand(self, rhs: Self) -> Self::Output {
+                $vecn($inner::vector_bitand(self.0, rhs.0))
+            }
+        }
+
+        impl BitOr for $vecn {
+            type Output = Self;
+
+            #[inline(always)]
+            fn bitor(self, rhs: Self) -> Self::Output {
+                $vecn($inner::vector_bitor(self.0, rhs.0))
+            }
+        }
+
+        impl BitXor for $vecn {
+            type Output = Self;
+
+            #[inline(always)]
+            fn bitxor(self, rhs: Self) -> Self::Output {
+                $vecn($inner::vector_bitxor(self.0, rhs.0))
+            }
+        }
+    };
+}
+
+macro_rules! impl_vecn_scalar_bit_op_traits {
+    ($vecn:ident, $rhs:ty, $inner:ident) => {
+        impl BitAnd<$rhs> for $vecn {
+            type Output = Self;
+
+            #[inline(always)]
+            fn bitand(self, rhs: $rhs) -> Self::Output {
+                $vecn($inner::scalar_bitand(self.0, rhs))
+            }
+        }
+
+        impl BitOr<$rhs> for $vecn {
+            type Output = Self;
+
+            #[inline(always)]
+            fn bitor(self, rhs: $rhs) -> Self::Output {
+                $vecn($inner::scalar_bitor(self.0, rhs))
+            }
+        }
+
+        impl BitXor<$rhs> for $vecn {
+            type Output = Self;
+
+            #[inline(always)]
+            fn bitxor(self, rhs: $rhs) -> Self::Output {
+                $vecn($inner::scalar_bitxor(self.0, rhs))
+            }
+        }
+    };
+}
+
 macro_rules! impl_as_vec2 {
     () => {
         #[deprecated(since = "0.18.0", note = "please use `as_vec2()` instead")]
diff --git a/src/vec2.rs b/src/vec2.rs
index b30681f..26f5637 100644
--- a/src/vec2.rs
+++ b/src/vec2.rs
@@ -224,6 +224,20 @@ impl IVec2 {
 impl_vec2_signed_traits!(i32, ivec2, IVec2, IVec3, BVec2, XYI32);
 impl_vecn_eq_hash_traits!(i32, 2, IVec2);
 
+impl_vecn_scalar_shift_op_traits!(IVec2, i8, XYI32);
+impl_vecn_scalar_shift_op_traits!(IVec2, i16, XYI32);
+impl_vecn_scalar_shift_op_traits!(IVec2, i32, XYI32);
+impl_vecn_scalar_shift_op_traits!(IVec2, u8, XYI32);
+impl_vecn_scalar_shift_op_traits!(IVec2, u16, XYI32);
+impl_vecn_scalar_shift_op_traits!(IVec2, u32, XYI32);
+
+impl_vecn_shift_op_traits!(IVec2, IVec2, XYI32);
+impl_vecn_shift_op_traits!(IVec2, UVec2, XYI32);
+
+impl_vecn_scalar_bit_op_traits!(IVec2, i32, XYI32);
+
+impl_vecn_bit_op_traits!(IVec2, XYI32);
+
 type XYU32 = XY<u32>;
 
 /// A 2-dimensional vector.
@@ -240,6 +254,20 @@ impl UVec2 {
 impl_vec2_unsigned_traits!(u32, uvec2, UVec2, UVec3, BVec2, XYU32);
 impl_vecn_eq_hash_traits!(u32, 2, UVec2);
 
+impl_vecn_scalar_shift_op_traits!(UVec2, i8, XYU32);
+impl_vecn_scalar_shift_op_traits!(UVec2, i16, XYU32);
+impl_vecn_scalar_shift_op_traits!(UVec2, i32, XYU32);
+impl_vecn_scalar_shift_op_traits!(UVec2, u8, XYU32);
+impl_vecn_scalar_shift_op_traits!(UVec2, u16, XYU32);
+impl_vecn_scalar_shift_op_traits!(UVec2, u32, XYU32);
+
+impl_vecn_shift_op_traits!(UVec2, IVec2, XYU32);
+impl_vecn_shift_op_traits!(UVec2, UVec2, XYU32);
+
+impl_vecn_scalar_bit_op_traits!(UVec2, u32, XYU32);
+
+impl_vecn_bit_op_traits!(UVec2, XYU32);
+
 mod const_test_vec2 {
     const_assert_eq!(
         core::mem::align_of::<f32>(),
diff --git a/src/vec3.rs b/src/vec3.rs
index 6e8719f..577acb7 100644
--- a/src/vec3.rs
+++ b/src/vec3.rs
@@ -377,6 +377,20 @@ impl_vec3_common_traits!(i32, ivec3, IVec2, IVec3, IVec4, XYZI32);
 impl_vecn_signed_traits!(i32, 3, IVec3, XYZI32, SignedVector3);
 impl_vecn_eq_hash_traits!(i32, 3, IVec3);
 
+impl_vecn_scalar_shift_op_traits!(IVec3, i8, XYZI32);
+impl_vecn_scalar_shift_op_traits!(IVec3, i16, XYZI32);
+impl_vecn_scalar_shift_op_traits!(IVec3, i32, XYZI32);
+impl_vecn_scalar_shift_op_traits!(IVec3, u8, XYZI32);
+impl_vecn_scalar_shift_op_traits!(IVec3, u16, XYZI32);
+impl_vecn_scalar_shift_op_traits!(IVec3, u32, XYZI32);
+
+impl_vecn_shift_op_traits!(IVec3, IVec3, XYZI32);
+impl_vecn_shift_op_traits!(IVec3, UVec3, XYZI32);
+
+impl_vecn_scalar_bit_op_traits!(IVec3, i32, XYZI32);
+
+impl_vecn_bit_op_traits!(IVec3, XYZI32);
+
 type XYZU32 = XYZ<u32>;
 
 /// A 3-dimensional vector.
@@ -393,6 +407,20 @@ impl UVec3 {
 impl_vec3_common_traits!(u32, uvec3, UVec2, UVec3, UVec4, XYZU32);
 impl_vecn_eq_hash_traits!(u32, 3, UVec3);
 
+impl_vecn_scalar_shift_op_traits!(UVec3, i8, XYZU32);
+impl_vecn_scalar_shift_op_traits!(UVec3, i16, XYZU32);
+impl_vecn_scalar_shift_op_traits!(UVec3, i32, XYZU32);
+impl_vecn_scalar_shift_op_traits!(UVec3, u8, XYZU32);
+impl_vecn_scalar_shift_op_traits!(UVec3, u16, XYZU32);
+impl_vecn_scalar_shift_op_traits!(UVec3, u32, XYZU32);
+
+impl_vecn_shift_op_traits!(UVec3, IVec3, XYZU32);
+impl_vecn_shift_op_traits!(UVec3, UVec3, XYZU32);
+
+impl_vecn_scalar_bit_op_traits!(UVec3, u32, XYZU32);
+
+impl_vecn_bit_op_traits!(UVec3, XYZU32);
+
 #[test]
 fn test_vec3_private() {
     assert_eq!(
diff --git a/src/vec4.rs b/src/vec4.rs
index b8f4eb2..6603ee7 100644
--- a/src/vec4.rs
+++ b/src/vec4.rs
@@ -337,6 +337,20 @@ impl IVec4 {
 impl_vec4_signed_traits!(i32, ivec4, IVec2, IVec3, IVec4, BVec4, XYZWI32);
 impl_vecn_eq_hash_traits!(i32, 4, IVec4);
 
+impl_vecn_scalar_shift_op_traits!(IVec4, i8, XYZWI32);
+impl_vecn_scalar_shift_op_traits!(IVec4, i16, XYZWI32);
+impl_vecn_scalar_shift_op_traits!(IVec4, i32, XYZWI32);
+impl_vecn_scalar_shift_op_traits!(IVec4, u8, XYZWI32);
+impl_vecn_scalar_shift_op_traits!(IVec4, u16, XYZWI32);
+impl_vecn_scalar_shift_op_traits!(IVec4, u32, XYZWI32);
+
+impl_vecn_shift_op_traits!(IVec4, IVec4, XYZWI32);
+impl_vecn_shift_op_traits!(IVec4, UVec4, XYZWI32);
+
+impl_vecn_scalar_bit_op_traits!(IVec4, i32, XYZWI32);
+
+impl_vecn_bit_op_traits!(IVec4, XYZWI32);
+
 type XYZWU32 = XYZW<u32>;
 
 /// A 4-dimensional vector.
@@ -353,6 +367,20 @@ impl UVec4 {
 impl_vec4_common_traits!(u32, uvec4, UVec2, UVec3, UVec4, BVec4, XYZWU32);
 impl_vecn_eq_hash_traits!(u32, 4, UVec4);
 
+impl_vecn_scalar_shift_op_traits!(UVec4, i8, XYZWU32);
+impl_vecn_scalar_shift_op_traits!(UVec4, i16, XYZWU32);
+impl_vecn_scalar_shift_op_traits!(UVec4, i32, XYZWU32);
+impl_vecn_scalar_shift_op_traits!(UVec4, u8, XYZWU32);
+impl_vecn_scalar_shift_op_traits!(UVec4, u16, XYZWU32);
+impl_vecn_scalar_shift_op_traits!(UVec4, u32, XYZWU32);
+
+impl_vecn_shift_op_traits!(UVec4, IVec4, XYZWU32);
+impl_vecn_shift_op_traits!(UVec4, UVec4, XYZWU32);
+
+impl_vecn_scalar_bit_op_traits!(UVec4, u32, XYZWU32);
+
+impl_vecn_bit_op_traits!(UVec4, XYZWU32);
+
 #[test]
 fn test_vec4_private() {
     assert_eq!(
diff --git a/src/vec_mask.rs b/src/vec_mask.rs
index 190e828..bd823a0 100644
--- a/src/vec_mask.rs
+++ b/src/vec_mask.rs
@@ -75,14 +75,14 @@ macro_rules! impl_vecnmask_traits {
             type Output = Self;
             #[inline]
             fn bitand(self, other: Self) -> Self {
-                Self(self.0.bitand(other.0))
+                Self(MaskVector::bitand(self.0, other.0))
             }
         }
 
         impl BitAndAssign for $vecnmask {
             #[inline]
             fn bitand_assign(&mut self, other: Self) {
-                self.0 = self.0.bitand(other.0);
+                self.0 = MaskVector::bitand(self.0, other.0);
             }
         }
 
@@ -90,14 +90,14 @@ macro_rules! impl_vecnmask_traits {
             type Output = Self;
             #[inline]
             fn bitor(self, other: Self) -> Self {
-                Self(self.0.bitor(other.0))
+                Self(MaskVector::bitor(self.0, other.0))
             }
         }
 
         impl BitOrAssign for $vecnmask {
             #[inline]
             fn bitor_assign(&mut self, other: Self) {
-                self.0 = self.0.bitor(other.0);
+                self.0 = MaskVector::bitor(self.0, other.0);
             }
         }
 
@@ -105,7 +105,7 @@ macro_rules! impl_vecnmask_traits {
             type Output = Self;
             #[inline]
             fn not(self) -> Self {
-                Self(self.0.not())
+                Self(MaskVector::not(self.0))
             }
         }
 


Test patch:
diff --git a/tests/vec2.rs b/tests/vec2.rs
index 2d73b52..7a6e1c3 100644
--- a/tests/vec2.rs
+++ b/tests/vec2.rs
@@ -662,6 +662,131 @@ macro_rules! impl_vec2_float_tests {
     };
 }
 
+macro_rules! impl_vec2_scalar_shift_op_test {
+    ($vec2:ident, $t_min:literal, $t_max:literal, $rhs_min:literal, $rhs_max:literal) => {
+        glam_test!(test_vec2_scalar_shift_ops, {
+            for x in $t_min..$t_max {
+                for y in $t_min..$t_max {
+                    for rhs in $rhs_min..$rhs_max {
+                        assert_eq!($vec2::new(x, y) << rhs, $vec2::new(x << rhs, y << rhs));
+                        assert_eq!($vec2::new(x, y) >> rhs, $vec2::new(x >> rhs, y >> rhs));
+                    }
+                }
+            }
+        });
+    };
+}
+
+macro_rules! impl_vec2_scalar_shift_op_tests {
+    ($vec2:ident, $t_min:literal, $t_max:literal) => {
+        mod shift_by_i8 {
+            use glam::$vec2;
+            impl_vec2_scalar_shift_op_test!($vec2, $t_min, $t_max, 0i8, 2);
+        }
+        mod shift_by_i16 {
+            use glam::$vec2;
+            impl_vec2_scalar_shift_op_test!($vec2, $t_min, $t_max, 0i16, 2);
+        }
+        mod shift_by_i32 {
+            use glam::$vec2;
+            impl_vec2_scalar_shift_op_test!($vec2, $t_min, $t_max, 0i32, 2);
+        }
+        mod shift_by_u8 {
+            use glam::$vec2;
+            impl_vec2_scalar_shift_op_test!($vec2, $t_min, $t_max, 0u8, 2);
+        }
+        mod shift_by_u16 {
+            use glam::$vec2;
+            impl_vec2_scalar_shift_op_test!($vec2, $t_min, $t_max, 0u16, 2);
+        }
+        mod shift_by_u32 {
+            use glam::$vec2;
+            impl_vec2_scalar_shift_op_test!($vec2, $t_min, $t_max, 0u32, 2);
+        }
+    };
+}
+
+macro_rules! impl_vec2_shift_op_test {
+    ($vec2:ident, $rhs:ident, $t_min:literal, $t_max:literal) => {
+        glam_test!(test_vec2_shift_ops, {
+            for x1 in $t_min..$t_max {
+                for y1 in $t_min..$t_max {
+                    for x2 in $t_min..$t_max {
+                        for y2 in $t_min..$t_max {
+                            assert_eq!(
+                                $vec2::new(x1, y1) << $rhs::new(x2, y2),
+                                $vec2::new(x1 << x2, y1 << y2)
+                            );
+                            assert_eq!(
+                                $vec2::new(x1, y1) >> $rhs::new(x2, y2),
+                                $vec2::new(x1 >> x2, y1 >> y2)
+                            );
+                        }
+                    }
+                }
+            }
+        });
+    };
+}
+
+macro_rules! impl_vec2_shift_op_tests {
+    ($vec2:ident) => {
+        mod shift_ivec2_by_ivec2 {
+            use super::*;
+            impl_vec2_shift_op_test!($vec2, IVec2, 0, 2);
+        }
+        mod shift_ivec2_by_uvec2 {
+            use super::*;
+            impl_vec2_shift_op_test!($vec2, UVec2, 0, 2);
+        }
+    };
+}
+
+macro_rules! impl_vec2_scalar_bit_op_tests {
+    ($vec2:ident, $t_min:literal, $t_max:literal) => {
+        glam_test!(test_vec2_scalar_bit_ops, {
+            for x in $t_min..$t_max {
+                for y in $t_min..$t_max {
+                    for rhs in $t_min..$t_max {
+                        assert_eq!($vec2::new(x, y) & rhs, $vec2::new(x & rhs, y & rhs));
+                        assert_eq!($vec2::new(x, y) | rhs, $vec2::new(x | rhs, y | rhs));
+                        assert_eq!($vec2::new(x, y) ^ rhs, $vec2::new(x ^ rhs, y ^ rhs));
+                    }
+                }
+            }
+        });
+    };
+}
+
+macro_rules! impl_vec2_bit_op_tests {
+    ($vec2:ident, $t_min:literal, $t_max:literal) => {
+        glam_test!(test_vec2_bit_ops, {
+            for x1 in $t_min..$t_max {
+                for y1 in $t_min..$t_max {
+                    assert_eq!(!$vec2::new(x1, y1), $vec2::new(!x1, !y1));
+
+                    for x2 in $t_min..$t_max {
+                        for y2 in $t_min..$t_max {
+                            assert_eq!(
+                                $vec2::new(x1, y1) & $vec2::new(x2, y2),
+                                $vec2::new(x1 & x2, y1 & y2)
+                            );
+                            assert_eq!(
+                                $vec2::new(x1, y1) | $vec2::new(x2, y2),
+                                $vec2::new(x1 | x2, y1 | y2)
+                            );
+                            assert_eq!(
+                                $vec2::new(x1, y1) ^ $vec2::new(x2, y2),
+                                $vec2::new(x1 ^ x2, y1 ^ y2)
+                            );
+                        }
+                    }
+                }
+            }
+        });
+    };
+}
+
 mod vec2 {
     use glam::{const_vec2, vec2, BVec2, Mat2, Vec2, Vec3};
 
@@ -710,7 +835,7 @@ mod dvec2 {
 }
 
 mod ivec2 {
-    use glam::{const_ivec2, ivec2, BVec2, IVec2, IVec3};
+    use glam::{const_ivec2, ivec2, BVec2, IVec2, IVec3, UVec2};
 
     glam_test!(test_align, {
         use core::mem;
@@ -722,10 +847,16 @@ mod ivec2 {
 
     impl_vec2_signed_tests!(i32, const_ivec2, ivec2, IVec2, IVec3, BVec2);
     impl_vec2_eq_hash_tests!(i32, ivec2);
+
+    impl_vec2_scalar_shift_op_tests!(IVec2, -2, 2);
+    impl_vec2_shift_op_tests!(IVec2);
+
+    impl_vec2_scalar_bit_op_tests!(IVec2, -2, 2);
+    impl_vec2_bit_op_tests!(IVec2, -2, 2);
 }
 
 mod uvec2 {
-    use glam::{const_uvec2, uvec2, BVec2, UVec2, UVec3};
+    use glam::{const_uvec2, uvec2, BVec2, IVec2, UVec2, UVec3};
 
     glam_test!(test_align, {
         use core::mem;
@@ -737,4 +868,10 @@ mod uvec2 {
 
     impl_vec2_tests!(u32, const_uvec2, uvec2, UVec2, UVec3, BVec2);
     impl_vec2_eq_hash_tests!(u32, uvec2);
+
+    impl_vec2_scalar_shift_op_tests!(UVec2, 0, 2);
+    impl_vec2_shift_op_tests!(UVec2);
+
+    impl_vec2_scalar_bit_op_tests!(UVec2, 0, 2);
+    impl_vec2_bit_op_tests!(UVec2, 0, 2);
 }
diff --git a/tests/vec3.rs b/tests/vec3.rs
index 55d630a..d29a8c2 100644
--- a/tests/vec3.rs
+++ b/tests/vec3.rs
@@ -736,6 +736,158 @@ macro_rules! impl_vec3_float_tests {
     };
 }
 
+macro_rules! impl_vec3_scalar_shift_op_test {
+    ($vec3:ident, $t_min:literal, $t_max:literal, $rhs_min:literal, $rhs_max:literal) => {
+        glam_test!(test_vec3_scalar_shift_ops, {
+            for x in $t_min..$t_max {
+                for y in $t_min..$t_max {
+                    for z in $t_min..$t_max {
+                        for rhs in $rhs_min..$rhs_max {
+                            assert_eq!(
+                                $vec3::new(x, y, z) << rhs,
+                                $vec3::new(x << rhs, y << rhs, z << rhs)
+                            );
+                            assert_eq!(
+                                $vec3::new(x, y, z) >> rhs,
+                                $vec3::new(x >> rhs, y >> rhs, z >> rhs)
+                            );
+                        }
+                    }
+                }
+            }
+        });
+    };
+}
+
+macro_rules! impl_vec3_scalar_shift_op_tests {
+    ($vec3:ident, $t_min:literal, $t_max:literal) => {
+        mod shift_by_i8 {
+            use glam::$vec3;
+            impl_vec3_scalar_shift_op_test!($vec3, $t_min, $t_max, 0i8, 2);
+        }
+        mod shift_by_i16 {
+            use glam::$vec3;
+            impl_vec3_scalar_shift_op_test!($vec3, $t_min, $t_max, 0i16, 2);
+        }
+        mod shift_by_i32 {
+            use glam::$vec3;
+            impl_vec3_scalar_shift_op_test!($vec3, $t_min, $t_max, 0i32, 2);
+        }
+        mod shift_by_u8 {
+            use glam::$vec3;
+            impl_vec3_scalar_shift_op_test!($vec3, $t_min, $t_max, 0u8, 2);
+        }
+        mod shift_by_u16 {
+            use glam::$vec3;
+            impl_vec3_scalar_shift_op_test!($vec3, $t_min, $t_max, 0u16, 2);
+        }
+        mod shift_by_u32 {
+            use glam::$vec3;
+            impl_vec3_scalar_shift_op_test!($vec3, $t_min, $t_max, 0u32, 2);
+        }
+    };
+}
+
+macro_rules! impl_vec3_shift_op_test {
+    ($vec3:ident, $rhs:ident, $t_min:literal, $t_max:literal) => {
+        glam_test!(test_vec3_shift_ops, {
+            for x1 in $t_min..$t_max {
+                for y1 in $t_min..$t_max {
+                    for z1 in $t_min..$t_max {
+                        for x2 in $t_min..$t_max {
+                            for y2 in $t_min..$t_max {
+                                for z2 in $t_min..$t_max {
+                                    assert_eq!(
+                                        $vec3::new(x1, y1, z1) << $rhs::new(x2, y2, z2),
+                                        $vec3::new(x1 << x2, y1 << y2, z1 << z2)
+                                    );
+                                    assert_eq!(
+                                        $vec3::new(x1, y1, z1) >> $rhs::new(x2, y2, z2),
+                                        $vec3::new(x1 >> x2, y1 >> y2, z1 >> z2)
+                                    );
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        });
+    };
+}
+
+macro_rules! impl_vec3_shift_op_tests {
+    ($vec3:ident) => {
+        mod shift_ivec3_by_ivec3 {
+            use super::*;
+            impl_vec3_shift_op_test!($vec3, IVec3, 0, 2);
+        }
+        mod shift_ivec3_by_uvec3 {
+            use super::*;
+            impl_vec3_shift_op_test!($vec3, UVec3, 0, 2);
+        }
+    };
+}
+
+macro_rules! impl_vec3_scalar_bit_op_tests {
+    ($vec3:ident, $t_min:literal, $t_max:literal) => {
+        glam_test!(test_vec3_scalar_bit_ops, {
+            for x in $t_min..$t_max {
+                for y in $t_min..$t_max {
+                    for z in $t_min..$t_max {
+                        for rhs in $t_min..$t_max {
+                            assert_eq!(
+                                $vec3::new(x, y, z) & rhs,
+                                $vec3::new(x & rhs, y & rhs, z & rhs)
+                            );
+                            assert_eq!(
+                                $vec3::new(x, y, z) | rhs,
+                                $vec3::new(x | rhs, y | rhs, z | rhs)
+                            );
+                            assert_eq!(
+                                $vec3::new(x, y, z) ^ rhs,
+                                $vec3::new(x ^ rhs, y ^ rhs, z ^ rhs)
+                            );
+                        }
+                    }
+                }
+            }
+        });
+    };
+}
+
+macro_rules! impl_vec3_bit_op_tests {
+    ($vec3:ident, $t_min:literal, $t_max:literal) => {
+        glam_test!(test_vec3_bit_ops, {
+            for x1 in $t_min..$t_max {
+                for y1 in $t_min..$t_max {
+                    for z1 in $t_min..$t_max {
+                        assert_eq!(!$vec3::new(x1, y1, z1), $vec3::new(!x1, !y1, !z1));
+
+                        for x2 in $t_min..$t_max {
+                            for y2 in $t_min..$t_max {
+                                for z2 in $t_min..$t_max {
+                                    assert_eq!(
+                                        $vec3::new(x1, y1, z1) & $vec3::new(x2, y2, z2),
+                                        $vec3::new(x1 & x2, y1 & y2, z1 & z2)
+                                    );
+                                    assert_eq!(
+                                        $vec3::new(x1, y1, z1) | $vec3::new(x2, y2, z2),
+                                        $vec3::new(x1 | x2, y1 | y2, z1 | z2)
+                                    );
+                                    assert_eq!(
+                                        $vec3::new(x1, y1, z1) ^ $vec3::new(x2, y2, z2),
+                                        $vec3::new(x1 ^ x2, y1 ^ y2, z1 ^ z2)
+                                    );
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        });
+    };
+}
+
 mod vec3 {
     use glam::{const_vec3, vec3, BVec3, Vec3};
 
@@ -887,7 +1039,7 @@ mod dvec3 {
 }
 
 mod ivec3 {
-    use glam::{const_ivec3, ivec3, BVec3, IVec3};
+    use glam::{const_ivec3, ivec3, BVec3, IVec3, UVec3};
 
     glam_test!(test_align, {
         use std::mem;
@@ -899,10 +1051,16 @@ mod ivec3 {
 
     impl_vec3_signed_tests!(i32, const_ivec3, ivec3, IVec3, BVec3);
     impl_vec3_eq_hash_tests!(i32, ivec3);
+
+    impl_vec3_scalar_shift_op_tests!(IVec3, -2, 2);
+    impl_vec3_shift_op_tests!(IVec3);
+
+    impl_vec3_scalar_bit_op_tests!(IVec3, -2, 2);
+    impl_vec3_bit_op_tests!(IVec3, -2, 2);
 }
 
 mod uvec3 {
-    use glam::{const_uvec3, uvec3, BVec3, UVec3};
+    use glam::{const_uvec3, uvec3, BVec3, IVec3, UVec3};
 
     glam_test!(test_align, {
         use std::mem;
@@ -914,4 +1072,10 @@ mod uvec3 {
 
     impl_vec3_tests!(u32, const_uvec3, uvec3, UVec3, BVec3);
     impl_vec3_eq_hash_tests!(u32, uvec3);
+
+    impl_vec3_scalar_shift_op_tests!(UVec3, 0, 2);
+    impl_vec3_shift_op_tests!(UVec3);
+
+    impl_vec3_scalar_bit_op_tests!(UVec3, 0, 2);
+    impl_vec3_bit_op_tests!(UVec3, 0, 2);
 }
diff --git a/tests/vec4.rs b/tests/vec4.rs
index 62e0b26..31ec4db 100644
--- a/tests/vec4.rs
+++ b/tests/vec4.rs
@@ -781,7 +781,174 @@ macro_rules! impl_vec4_float_tests {
         });
     };
 }
+macro_rules! impl_vec4_scalar_shift_op_test {
+    ($vec4:ident, $t_min:literal, $t_max:literal, $rhs_min:literal, $rhs_max:literal) => {
+        glam_test!(test_vec4_scalar_shift_ops, {
+            for x in $t_min..$t_max {
+                for y in $t_min..$t_max {
+                    for z in $t_min..$t_max {
+                        for w in $t_min..$t_max {
+                            for rhs in $rhs_min..$rhs_max {
+                                assert_eq!(
+                                    $vec4::new(x, y, z, w) << rhs,
+                                    $vec4::new(x << rhs, y << rhs, z << rhs, w << rhs)
+                                );
+                                assert_eq!(
+                                    $vec4::new(x, y, z, w) >> rhs,
+                                    $vec4::new(x >> rhs, y >> rhs, z >> rhs, w >> rhs)
+                                );
+                            }
+                        }
+                    }
+                }
+            }
+        });
+    };
+}
+
+macro_rules! impl_vec4_scalar_shift_op_tests {
+    ($vec4:ident, $t_min:literal, $t_max:literal) => {
+        mod shift_by_i8 {
+            use glam::$vec4;
+            impl_vec4_scalar_shift_op_test!($vec4, $t_min, $t_max, 0i8, 2);
+        }
+        mod shift_by_i16 {
+            use glam::$vec4;
+            impl_vec4_scalar_shift_op_test!($vec4, $t_min, $t_max, 0i16, 2);
+        }
+        mod shift_by_i32 {
+            use glam::$vec4;
+            impl_vec4_scalar_shift_op_test!($vec4, $t_min, $t_max, 0i32, 2);
+        }
+        mod shift_by_u8 {
+            use glam::$vec4;
+            impl_vec4_scalar_shift_op_test!($vec4, $t_min, $t_max, 0u8, 2);
+        }
+        mod shift_by_u16 {
+            use glam::$vec4;
+            impl_vec4_scalar_shift_op_test!($vec4, $t_min, $t_max, 0u16, 2);
+        }
+        mod shift_by_u32 {
+            use glam::$vec4;
+            impl_vec4_scalar_shift_op_test!($vec4, $t_min, $t_max, 0u32, 2);
+        }
+    };
+}
+
+macro_rules! impl_vec4_shift_op_test {
+    ($vec4:ident, $rhs:ident, $t_min:literal, $t_max:literal) => {
+        glam_test!(test_vec4_shift_ops, {
+            for x1 in $t_min..$t_max {
+                for y1 in $t_min..$t_max {
+                    for z1 in $t_min..$t_max {
+                        for w1 in $t_min..$t_max {
+                            for x2 in $t_min..$t_max {
+                                for y2 in $t_min..$t_max {
+                                    for z2 in $t_min..$t_max {
+                                        for w2 in $t_min..$t_max {
+                                            assert_eq!(
+                                                $vec4::new(x1, y1, z1, w1)
+                                                    << $rhs::new(x2, y2, z2, w2),
+                                                $vec4::new(x1 << x2, y1 << y2, z1 << z2, w1 << w2)
+                                            );
+                                            assert_eq!(
+                                                $vec4::new(x1, y1, z1, w1)
+                                                    >> $rhs::new(x2, y2, z2, w2),
+                                                $vec4::new(x1 >> x2, y1 >> y2, z1 >> z2, w1 >> w2)
+                                            );
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        });
+    };
+}
 
+macro_rules! impl_vec4_shift_op_tests {
+    ($vec4:ident) => {
+        mod shift_ivec4_by_ivec4 {
+            use super::*;
+            impl_vec4_shift_op_test!($vec4, IVec4, 0, 2);
+        }
+        mod shift_ivec4_by_uvec4 {
+            use super::*;
+            impl_vec4_shift_op_test!($vec4, UVec4, 0, 2);
+        }
+    };
+}
+
+macro_rules! impl_vec4_scalar_bit_op_tests {
+    ($vec4:ident, $t_min:literal, $t_max:literal) => {
+        glam_test!(test_vec4_scalar_bit_ops, {
+            for x in $t_min..$t_max {
+                for y in $t_min..$t_max {
+                    for z in $t_min..$t_max {
+                        for w in $t_min..$t_max {
+                            for rhs in $t_min..$t_max {
+                                assert_eq!(
+                                    $vec4::new(x, y, z, w) & rhs,
+                                    $vec4::new(x & rhs, y & rhs, z & rhs, w & rhs)
+                                );
+                                assert_eq!(
+                                    $vec4::new(x, y, z, w) | rhs,
+                                    $vec4::new(x | rhs, y | rhs, z | rhs, w | rhs)
+                                );
+                                assert_eq!(
+                                    $vec4::new(x, y, z, w) ^ rhs,
+                                    $vec4::new(x ^ rhs, y ^ rhs, z ^ rhs, w ^ rhs)
+                                );
+                            }
+                        }
+                    }
+                }
+            }
+        });
+    };
+}
+
+macro_rules! impl_vec4_bit_op_tests {
+    ($vec4:ident, $t_min:literal, $t_max:literal) => {
+        glam_test!(test_vec4_bit_ops, {
+            for x1 in $t_min..$t_max {
+                for y1 in $t_min..$t_max {
+                    for z1 in $t_min..$t_max {
+                        for w1 in $t_min..$t_max {
+                            assert_eq!(!$vec4::new(x1, y1, z1, w1), $vec4::new(!x1, !y1, !z1, !w1));
+
+                            for x2 in $t_min..$t_max {
+                                for y2 in $t_min..$t_max {
+                                    for z2 in $t_min..$t_max {
+                                        for w2 in $t_min..$t_max {
+                                            assert_eq!(
+                                                $vec4::new(x1, y1, z1, w1)
+                                                    & $vec4::new(x2, y2, z2, w2),
+                                                $vec4::new(x1 & x2, y1 & y2, z1 & z2, w1 & w2)
+                                            );
+                                            assert_eq!(
+                                                $vec4::new(x1, y1, z1, w1)
+                                                    | $vec4::new(x2, y2, z2, w2),
+                                                $vec4::new(x1 | x2, y1 | y2, z1 | z2, w1 | w2)
+                                            );
+                                            assert_eq!(
+                                                $vec4::new(x1, y1, z1, w1)
+                                                    ^ $vec4::new(x2, y2, z2, w2),
+                                                $vec4::new(x1 ^ x2, y1 ^ y2, z1 ^ z2, w1 ^ w2)
+                                            );
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        });
+    };
+}
 mod vec4 {
     use glam::{const_vec4, vec4, Vec2, Vec3, Vec4};
 
@@ -929,7 +1096,7 @@ mod dvec4 {
 }
 
 mod ivec4 {
-    use glam::{const_ivec4, ivec4, BVec4, IVec2, IVec3, IVec4};
+    use glam::{const_ivec4, ivec4, BVec4, IVec2, IVec3, IVec4, UVec4};
 
     glam_test!(test_align, {
         use std::mem;
@@ -941,10 +1108,16 @@ mod ivec4 {
 
     impl_vec4_signed_tests!(i32, const_ivec4, ivec4, IVec4, IVec3, IVec2, BVec4);
     impl_vec4_eq_hash_tests!(i32, ivec4);
+
+    impl_vec4_scalar_shift_op_tests!(IVec4, -2, 2);
+    impl_vec4_shift_op_tests!(IVec4);
+
+    impl_vec4_scalar_bit_op_tests!(IVec4, -2, 2);
+    impl_vec4_bit_op_tests!(IVec4, -2, 2);
 }
 
 mod uvec4 {
-    use glam::{const_uvec4, uvec4, BVec4, UVec2, UVec3, UVec4};
+    use glam::{const_uvec4, uvec4, BVec4, IVec4, UVec2, UVec3, UVec4};
 
     glam_test!(test_align, {
         use std::mem;
@@ -956,4 +1129,10 @@ mod uvec4 {
 
     impl_vec4_tests!(u32, const_uvec4, uvec4, UVec4, UVec3, UVec2, BVec4);
     impl_vec4_eq_hash_tests!(u32, uvec4);
+
+    impl_vec4_scalar_shift_op_tests!(UVec4, 0, 2);
+    impl_vec4_shift_op_tests!(UVec4);
+
+    impl_vec4_scalar_bit_op_tests!(UVec4, 0, 2);
+    impl_vec4_bit_op_tests!(UVec4, 0, 2);
 }