Snap for 9710098 from fc7a0ebe8921dd07368f0c64d813ef41dfa7371d to mainline-tzdata5-release

Change-Id: Ibfc5044cecd54e50cebe4065296b9c6baab8115c
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0d06e33..335c2d2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -31,7 +31,8 @@
     strategy:
       matrix:
         os: [ubuntu-latest, macos-latest, windows-latest]
-        toolchain: [1.58.1, stable, beta, nightly]
+        #toolchain: [1.58.1, stable, beta, nightly] # weirdness with 1.58.1 and git2
+        toolchain: [stable, beta, nightly]
     runs-on: ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v2
diff --git a/Android.bp b/Android.bp
index 7ac23d2..32fbe8d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -43,15 +43,11 @@
     name: "libglam",
     crate_name: "glam",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.22.0",
+    cargo_pkg_version: "0.23.0",
     srcs: ["src/lib.rs"],
     edition: "2021",
     features: [
         "default",
         "std",
     ],
-    apex_available: [
-        "//apex_available:platform",
-        "//apex_available:anyapex",
-    ],
 }
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97e2544..858f3b1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,17 @@
 The format is based on [Keep a Changelog], and this project adheres to
 [Semantic Versioning].
 
+## [0.23.0] - 2023-02-22
+
+### Breaking changes
+
+* When the `scalar-math` feature is enabled the vector mask type for `Vec3A` was
+  changed from `BVec3` to `BVec3A`.
+
+### Added
+
+* Added `copysign` method to signed vector types.
+
 ## [0.22.0] - 2022-10-24
 
 ### Breaking changes
@@ -32,7 +43,7 @@
 * Added `Sum<Self>` and `Product<Self>` implementations for all vector, matrix
   and quaternion types.
 
-* Added 4x4 matrix methods `look_at_lh` and `look_at_rh`. These were previously
+* Added 4x4 matrix methods `look_to_lh` and `look_to_rh`. These were previously
   private.
 
 * Added `dot_into_vec` methods to vector which returns the result of the dot
@@ -889,7 +900,8 @@
 
 [Keep a Changelog]: https://keepachangelog.com/
 [Semantic Versioning]: https://semver.org/spec/v2.0.0.html
-[Unreleased]: https://github.com/bitshifter/glam-rs/compare/0.22.0...HEAD
+[Unreleased]: https://github.com/bitshifter/glam-rs/compare/0.23.0...HEAD
+[0.23.0]: https://github.com/bitshifter/glam-rs/compare/0.22.0...0.23.0
 [0.22.0]: https://github.com/bitshifter/glam-rs/compare/0.21.3...0.22.0
 [0.21.3]: https://github.com/bitshifter/glam-rs/compare/0.21.2...0.21.3
 [0.21.2]: https://github.com/bitshifter/glam-rs/compare/0.21.1...0.21.2
diff --git a/Cargo.toml b/Cargo.toml
index 624a2fa..4c95345 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
 edition = "2021"
 rust-version = "1.58.1"
 name = "glam"
-version = "0.22.0"
+version = "0.23.0"
 authors = ["Cameron Hart <cameron.hart@gmail.com>"]
 description = "A simple and fast 3D math library for games and graphics"
 readme = "README.md"
@@ -136,7 +136,7 @@
 std = []
 
 [target."cfg(not(target_arch = \"wasm32\"))".dev-dependencies.criterion]
-version = "0.3"
+version = "0.4"
 features = ["html_reports"]
 
 [target."cfg(target_arch = \"wasm32\")".dev-dependencies.wasm-bindgen-test]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index f145c6e..15c69e1 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "glam"
-version = "0.22.0" # remember to update html_root_url
+version = "0.23.0" # remember to update html_root_url
 edition = "2021"
 authors = ["Cameron Hart <cameron.hart@gmail.com>"]
 description = "A simple and fast 3D math library for games and graphics"
@@ -60,7 +60,7 @@
 serde_json = "1.0"
 
 [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
-criterion = { version = "0.3", features = ["html_reports"] }
+criterion = { version = "0.4", features = ["html_reports"] }
 
 [target.'cfg(target_arch = "wasm32")'.dev-dependencies]
 wasm-bindgen-test = "0.3.0"
diff --git a/METADATA b/METADATA
index 085b098..2e939a8 100644
--- a/METADATA
+++ b/METADATA
@@ -11,13 +11,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/glam/glam-0.22.0.crate"
+    value: "https://static.crates.io/crates/glam/glam-0.23.0.crate"
   }
-  version: "0.22.0"
+  version: "0.23.0"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2022
-    month: 12
-    day: 12
+    year: 2023
+    month: 3
+    day: 6
   }
 }
diff --git a/README.md b/README.md
index 3e13cf5..037bfb7 100644
--- a/README.md
+++ b/README.md
@@ -70,7 +70,7 @@
 
 ```toml
 [dependencies]
-glam = { version = "0.22", default-features = false, features = ["libm"] }
+glam = { version = "0.23", default-features = false, features = ["libm"] }
 ```
 
 To support both `std` and `no_std` builds in project, you can use the following
@@ -84,7 +84,7 @@
 libm = ["glam/libm"]
 
 [dependencies]
-glam = { version = "0.22", default-features = false }
+glam = { version = "0.23", default-features = false }
 ```
 
 ### Optional features
diff --git a/benches/support.rs b/benches/support.rs
index 3fe5dd7..27da939 100644
--- a/benches/support.rs
+++ b/benches/support.rs
@@ -7,6 +7,12 @@
     inc: u64,
 }
 
+impl Default for PCG32 {
+    fn default() -> Self {
+        PCG32::seed(0x853c49e6748fea9b, 0xda3e39cb94b95bdb)
+    }
+}
+
 impl PCG32 {
     pub fn seed(initstate: u64, initseq: u64) -> Self {
         let mut rng = PCG32 {
@@ -19,10 +25,6 @@
         rng
     }
 
-    pub fn default() -> Self {
-        PCG32::seed(0x853c49e6748fea9b, 0xda3e39cb94b95bdb)
-    }
-
     pub fn next_u32(&mut self) -> u32 {
         let oldstate = self.state;
         self.state = oldstate
diff --git a/clippy.toml b/clippy.toml
index ddbdbc1..5ff4f79 100644
--- a/clippy.toml
+++ b/clippy.toml
@@ -1 +1 @@
-msrv = "1.58"
+msrv = "1.58.1"
diff --git a/src/bool.rs b/src/bool.rs
index 889a0ed..c3cbb9f 100644
--- a/src/bool.rs
+++ b/src/bool.rs
@@ -17,12 +17,14 @@
 ))]
 mod wasm32;
 
-#[cfg(not(any(
-    feature = "scalar-math",
-    feature = "core-simd",
-    target_feature = "sse2",
-    target_feature = "simd128"
-),))]
+#[cfg(any(
+    not(any(
+        feature = "core-simd",
+        target_feature = "sse2",
+        target_feature = "simd128"
+    )),
+    feature = "scalar-math"
+))]
 mod scalar;
 
 pub use bvec2::BVec2;
@@ -56,12 +58,14 @@
 #[cfg(all(feature = "core-simd", not(feature = "scalar-math")))]
 pub use coresimd::bvec4a::BVec4A;
 
-#[cfg(not(any(
-    feature = "scalar-math",
-    feature = "core-simd",
-    target_feature = "sse2",
-    target_feature = "simd128"
-),))]
+#[cfg(any(
+    not(any(
+        feature = "core-simd",
+        target_feature = "sse2",
+        target_feature = "simd128"
+    )),
+    feature = "scalar-math"
+))]
 pub use scalar::bvec3a::BVec3A;
 
 #[cfg(not(any(
diff --git a/src/bool/scalar/bvec3a.rs b/src/bool/scalar/bvec3a.rs
index dbae395..f35908c 100644
--- a/src/bool/scalar/bvec3a.rs
+++ b/src/bool/scalar/bvec3a.rs
@@ -5,7 +5,7 @@
 use core::ops::*;
 
 /// A 3-dimensional `u32` vector mask.
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 #[repr(C, align(16))]
 pub struct BVec3A {
     pub x: u32,
diff --git a/src/bool/scalar/bvec4a.rs b/src/bool/scalar/bvec4a.rs
index c12d127..28b0066 100644
--- a/src/bool/scalar/bvec4a.rs
+++ b/src/bool/scalar/bvec4a.rs
@@ -5,7 +5,7 @@
 use core::ops::*;
 
 /// A 4-dimensional `u32` vector mask.
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 #[repr(C, align(16))]
 pub struct BVec4A {
     pub x: u32,
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 @@
         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 @@
         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 88a3c13..311e1e0 100644
--- a/src/f32/scalar/vec3a.rs
+++ b/src/f32/scalar/vec3a.rs
@@ -1,6 +1,6 @@
 // Generated from vec.rs.tera template. Edit the template, not the generated file.
 
-use crate::{BVec3, Vec2, Vec3, Vec4};
+use crate::{BVec3A, Vec2, Vec3, Vec4};
 
 #[cfg(not(target_arch = "spirv"))]
 use core::fmt;
@@ -85,11 +85,11 @@
     /// A true element in the mask uses the corresponding element from `if_true`, and false
     /// uses the element from `if_false`.
     #[inline]
-    pub fn select(mask: BVec3, if_true: Self, if_false: Self) -> Self {
+    pub fn select(mask: BVec3A, if_true: Self, if_false: Self) -> Self {
         Self {
-            x: if mask.x { if_true.x } else { if_false.x },
-            y: if mask.y { if_true.y } else { if_false.y },
-            z: if mask.z { if_true.z } else { if_false.z },
+            x: if mask.x != 0 { if_true.x } else { if_false.x },
+            y: if mask.y != 0 { if_true.y } else { if_false.y },
+            z: if mask.z != 0 { if_true.z } else { if_false.z },
         }
     }
 
@@ -234,8 +234,8 @@
     /// In other words, this computes `[self.x == rhs.x, self.y == rhs.y, ..]` for all
     /// elements.
     #[inline]
-    pub fn cmpeq(self, rhs: Self) -> BVec3 {
-        BVec3::new(self.x.eq(&rhs.x), self.y.eq(&rhs.y), self.z.eq(&rhs.z))
+    pub fn cmpeq(self, rhs: Self) -> BVec3A {
+        BVec3A::new(self.x.eq(&rhs.x), self.y.eq(&rhs.y), self.z.eq(&rhs.z))
     }
 
     /// Returns a vector mask containing the result of a `!=` comparison for each element of
@@ -244,8 +244,8 @@
     /// In other words this computes `[self.x != rhs.x, self.y != rhs.y, ..]` for all
     /// elements.
     #[inline]
-    pub fn cmpne(self, rhs: Self) -> BVec3 {
-        BVec3::new(self.x.ne(&rhs.x), self.y.ne(&rhs.y), self.z.ne(&rhs.z))
+    pub fn cmpne(self, rhs: Self) -> BVec3A {
+        BVec3A::new(self.x.ne(&rhs.x), self.y.ne(&rhs.y), self.z.ne(&rhs.z))
     }
 
     /// Returns a vector mask containing the result of a `>=` comparison for each element of
@@ -254,8 +254,8 @@
     /// In other words this computes `[self.x >= rhs.x, self.y >= rhs.y, ..]` for all
     /// elements.
     #[inline]
-    pub fn cmpge(self, rhs: Self) -> BVec3 {
-        BVec3::new(self.x.ge(&rhs.x), self.y.ge(&rhs.y), self.z.ge(&rhs.z))
+    pub fn cmpge(self, rhs: Self) -> BVec3A {
+        BVec3A::new(self.x.ge(&rhs.x), self.y.ge(&rhs.y), self.z.ge(&rhs.z))
     }
 
     /// Returns a vector mask containing the result of a `>` comparison for each element of
@@ -264,8 +264,8 @@
     /// In other words this computes `[self.x > rhs.x, self.y > rhs.y, ..]` for all
     /// elements.
     #[inline]
-    pub fn cmpgt(self, rhs: Self) -> BVec3 {
-        BVec3::new(self.x.gt(&rhs.x), self.y.gt(&rhs.y), self.z.gt(&rhs.z))
+    pub fn cmpgt(self, rhs: Self) -> BVec3A {
+        BVec3A::new(self.x.gt(&rhs.x), self.y.gt(&rhs.y), self.z.gt(&rhs.z))
     }
 
     /// Returns a vector mask containing the result of a `<=` comparison for each element of
@@ -274,8 +274,8 @@
     /// In other words this computes `[self.x <= rhs.x, self.y <= rhs.y, ..]` for all
     /// elements.
     #[inline]
-    pub fn cmple(self, rhs: Self) -> BVec3 {
-        BVec3::new(self.x.le(&rhs.x), self.y.le(&rhs.y), self.z.le(&rhs.z))
+    pub fn cmple(self, rhs: Self) -> BVec3A {
+        BVec3A::new(self.x.le(&rhs.x), self.y.le(&rhs.y), self.z.le(&rhs.z))
     }
 
     /// Returns a vector mask containing the result of a `<` comparison for each element of
@@ -284,8 +284,8 @@
     /// In other words this computes `[self.x < rhs.x, self.y < rhs.y, ..]` for all
     /// elements.
     #[inline]
-    pub fn cmplt(self, rhs: Self) -> BVec3 {
-        BVec3::new(self.x.lt(&rhs.x), self.y.lt(&rhs.y), self.z.lt(&rhs.z))
+    pub fn cmplt(self, rhs: Self) -> BVec3A {
+        BVec3A::new(self.x.lt(&rhs.x), self.y.lt(&rhs.y), self.z.lt(&rhs.z))
     }
 
     /// Returns a vector containing the absolute value of each element of `self`.
@@ -312,6 +312,16 @@
         }
     }
 
+    /// 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
@@ -340,8 +350,8 @@
     ///
     /// In other words, this computes `[x.is_nan(), y.is_nan(), z.is_nan(), w.is_nan()]`.
     #[inline]
-    pub fn is_nan_mask(self) -> BVec3 {
-        BVec3::new(self.x.is_nan(), self.y.is_nan(), self.z.is_nan())
+    pub fn is_nan_mask(self) -> BVec3A {
+        BVec3A::new(self.x.is_nan(), self.y.is_nan(), self.z.is_nan())
     }
 
     /// Computes the length of `self`.
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 @@
         }
     }
 
+    /// 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 @@
         }
     }
 
+    /// 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 @@
         }
     }
 
+    /// 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 @@
         }
     }
 
+    /// 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 @@
         }
     }
 
+    /// 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 @@
         }
     }
 
+    /// 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 @@
         }
     }
 
+    /// 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 @@
         }
     }
 
+    /// 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 @@
         }
     }
 
+    /// 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 @@
         }
     }
 
+    /// 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/float_ex.rs b/src/float_ex.rs
index d972015..70f382a 100644
--- a/src/float_ex.rs
+++ b/src/float_ex.rs
@@ -46,6 +46,6 @@
 impl FloatEx for f64 {
     #[inline(always)]
     fn acos_approx(self) -> Self {
-        f64::acos(self.max(-1.0).min(1.0))
+        f64::acos(self.clamp(-1.0, 1.0))
     }
 }
diff --git a/src/i32/ivec2.rs b/src/i32/ivec2.rs
index 87b5122..0a78305 100644
--- a/src/i32/ivec2.rs
+++ b/src/i32/ivec2.rs
@@ -247,9 +247,9 @@
 
     /// Returns a vector with elements representing the sign of `self`.
     ///
-    /// - `1.0` if the number is positive, `+0.0` or `INFINITY`
-    /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
-    /// - `NAN` if the number is `NAN`
+    ///  - `0` if the number is zero
+    ///  - `1` if the number is positive
+    ///  - `-1` if the number is negative
     #[inline]
     pub fn signum(self) -> Self {
         Self {
@@ -258,6 +258,12 @@
         }
     }
 
+    /// 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..2f71ac0 100644
--- a/src/i32/ivec3.rs
+++ b/src/i32/ivec3.rs
@@ -288,9 +288,9 @@
 
     /// Returns a vector with elements representing the sign of `self`.
     ///
-    /// - `1.0` if the number is positive, `+0.0` or `INFINITY`
-    /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
-    /// - `NAN` if the number is `NAN`
+    ///  - `0` if the number is zero
+    ///  - `1` if the number is positive
+    ///  - `-1` if the number is negative
     #[inline]
     pub fn signum(self) -> Self {
         Self {
@@ -300,6 +300,12 @@
         }
     }
 
+    /// 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..7c07621 100644
--- a/src/i32/ivec4.rs
+++ b/src/i32/ivec4.rs
@@ -312,9 +312,9 @@
 
     /// Returns a vector with elements representing the sign of `self`.
     ///
-    /// - `1.0` if the number is positive, `+0.0` or `INFINITY`
-    /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY`
-    /// - `NAN` if the number is `NAN`
+    ///  - `0` if the number is zero
+    ///  - `1` if the number is positive
+    ///  - `-1` if the number is negative
     #[inline]
     pub fn signum(self) -> Self {
         Self {
@@ -325,6 +325,12 @@
         }
     }
 
+    /// 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
diff --git a/src/lib.rs b/src/lib.rs
index b26016c..3027ec2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -245,7 +245,7 @@
 The minimum supported Rust version is `1.58.1`.
 
 */
-#![doc(html_root_url = "https://docs.rs/glam/0.22.0")]
+#![doc(html_root_url = "https://docs.rs/glam/0.23.0")]
 #![cfg_attr(not(feature = "std"), no_std)]
 #![cfg_attr(target_arch = "spirv", feature(repr_simd))]
 #![deny(
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 @@
             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 @@
             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 fae8858..5df8d09 100644
--- a/tests/vec3.rs
+++ b/tests/vec3.rs
@@ -665,6 +665,43 @@
             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);
@@ -1082,12 +1119,6 @@
 }
 
 mod vec3a {
-    #[cfg(any(
-        not(any(target_feature = "sse2", target_feature = "simd128")),
-        feature = "scalar-math"
-    ))]
-    use glam::BVec3;
-    #[cfg(not(feature = "scalar-math"))]
     use glam::BVec3A;
     use glam::{vec3a, Vec3A, Vec4};
 
@@ -1095,16 +1126,8 @@
         use std::mem;
         assert_eq!(16, mem::size_of::<Vec3A>());
         assert_eq!(16, mem::align_of::<Vec3A>());
-        #[cfg(not(feature = "scalar-math"))]
-        {
-            assert_eq!(16, mem::size_of::<BVec3A>());
-            assert_eq!(16, mem::align_of::<BVec3A>());
-        }
-        #[cfg(feature = "scalar-math")]
-        {
-            assert_eq!(3, mem::size_of::<BVec3>());
-            assert_eq!(1, mem::align_of::<BVec3>());
-        }
+        assert_eq!(16, mem::size_of::<BVec3A>());
+        assert_eq!(16, mem::align_of::<BVec3A>());
     });
 
     glam_test!(test_mask_align16, {
@@ -1162,17 +1185,7 @@
         assert_eq!(v2.min_element(), 2.0);
     });
 
-    #[cfg(all(
-        any(target_feature = "sse2", target_feature = "simd128"),
-        not(feature = "scalar-math")
-    ))]
     impl_vec3_float_tests!(f32, vec3a, Vec3A, BVec3A);
-
-    #[cfg(any(
-        not(any(target_feature = "sse2", target_feature = "simd128")),
-        feature = "scalar-math"
-    ))]
-    impl_vec3_float_tests!(f32, vec3a, Vec3A, BVec3);
 }
 
 mod dvec3 {
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 @@
             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);