use super::{bool32x4, common_types::ConstUnionHack128bit, macros::*, num_traits::*};
#[cfg(all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
))]
use crate::wide::{CmpEq, CmpGe, CmpGt, CmpLe, CmpLt, CmpNe};
use crate::FloatEx;
use auto_ops_det::impl_op_ex;
#[cfg(not(target_arch = "spirv"))]
use core::fmt;
use core::ops;
use core::ops::{Add, Div, Mul, Rem, Sub};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
))]
type InnerF32x4 = crate::wide::f32x4;
#[cfg(any(
feature = "libm_force",
all(feature = "libm_fallback", not(feature = "std")),
feature = "scalar-math"
))]
type InnerF32x4 = [f32; 4];
#[allow(non_camel_case_types)]
#[cfg_attr(
all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
),
repr(transparent)
)]
#[cfg_attr(
all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
),
derive(Debug)
)]
#[cfg_attr(
any(
feature = "libm_force",
feature = "scalar-math",
all(feature = "libm_fallback", not(feature = "std"))
),
repr(align(16))
)]
#[derive(Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct f32x4(InnerF32x4);
macro_rules! define_const {
( $( $const_name:ident ),* ) => {
$(
pub const $const_name: Self = Self::const_splat(core::f32::$const_name);
)*
};
}
impl f32x4 {
pub const PI: Self = f32x4::const_splat(core::f32::consts::PI);
pub const TWO: Self = Self::const_splat(2.0_f32);
define_const!(MIN, MAX, INFINITY, NEG_INFINITY, EPSILON);
#[inline]
fn map(self, f: impl Fn(f32) -> f32) -> Self {
let arr: [f32; 4] = self.into();
Self::from([f(arr[0]), f(arr[1]), f(arr[2]), f(arr[3])])
}
#[inline]
fn zip_map(self, rhs: Self, f: impl Fn(f32, f32) -> f32) -> Self {
let arr: &[f32; 4] = self.as_ref();
let rhs: &[f32; 4] = rhs.as_ref();
Self::from([
f(arr[0], rhs[0]),
f(arr[1], rhs[1]),
f(arr[2], rhs[2]),
f(arr[3], rhs[3]),
])
}
#[cfg(all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
))]
#[inline]
pub fn to_radians(self) -> Self {
Self(self.0.to_radians())
}
#[cfg(any(
feature = "libm_force",
feature = "scalar-math",
all(feature = "libm_fallback", not(feature = "std"))
))]
#[inline]
pub fn to_radians(self) -> Self {
Self::map(self, f32::to_radians)
}
#[inline]
pub const fn const_splat(val: f32) -> Self {
unsafe { ConstUnionHack128bit { f32a4: [val; 4] }.f32x4 }
}
#[cfg(not(feature = "scalar-math"))]
#[inline]
const fn cast_wide_f32x4(self) -> crate::wide::f32x4 {
unsafe { ConstUnionHack128bit { f32x4: self }.widef32x4 }
}
#[cfg(not(feature = "scalar-math"))]
#[inline]
const fn from_wide_f32x4(val: crate::wide::f32x4) -> Self {
unsafe { ConstUnionHack128bit { widef32x4: val }.f32x4 }
}
}
impl NumConstEx for f32x4 {
const ZERO: Self = Self::const_splat(0f32);
const ONE: Self = Self::const_splat(1f32);
const TWO: Self = Self::const_splat(2_f32);
}
impl NanConstEx for f32x4 {
const NAN: Self = unsafe {
ConstUnionHack128bit {
f32a4: [f32::NAN; 4],
}
.f32x4
};
}
impl FloatConstEx for f32x4 {
const NEG_ONE: Self = unsafe {
ConstUnionHack128bit {
f32a4: [-1.0_f32; 4],
}
.f32x4
};
const TWO: Self = unsafe {
ConstUnionHack128bit {
f32a4: [2.0_f32; 4],
}
.f32x4
};
const HALF: Self = Self::const_splat(0.5f32);
}
#[cfg(all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
))]
impl From<[f32; 4]> for f32x4 {
#[inline]
fn from(vals: [f32; 4]) -> Self {
Self(vals.into())
}
}
#[cfg(any(
feature = "libm_force",
feature = "scalar-math",
all(feature = "libm_fallback", not(feature = "std"))
))]
impl From<[f32; 4]> for f32x4 {
#[inline]
fn from(vals: [f32; 4]) -> Self {
Self(vals)
}
}
#[cfg(any(
feature = "libm_force",
feature = "scalar-math",
all(feature = "libm_fallback", not(feature = "std"))
))]
impl From<f32x4> for [f32; 4] {
#[inline]
fn from(val: f32x4) -> [f32; 4] {
val.0
}
}
#[cfg(all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
))]
impl From<f32x4> for [f32; 4] {
#[inline]
fn from(val: f32x4) -> [f32; 4] {
val.0.into()
}
}
impl AsRef<[f32; 4]> for f32x4 {
#[inline]
fn as_ref(&self) -> &[f32; 4] {
#[cfg(all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
))]
return as_array_ref!(self.0);
#[cfg(any(
feature = "libm_force",
feature = "scalar-math",
all(feature = "libm_fallback", not(feature = "std"))
))]
&self.0
}
}
impl AsMut<[f32; 4]> for f32x4 {
#[inline]
fn as_mut(&mut self) -> &mut [f32; 4] {
#[cfg(all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
))]
return as_array_mut!(self.0);
#[cfg(any(
feature = "libm_force",
feature = "scalar-math",
all(feature = "libm_fallback", not(feature = "std"))
))]
&mut self.0
}
}
impl From<f32> for f32x4 {
#[inline]
fn from(val: f32) -> Self {
Self::splat(val)
}
}
impl Num for f32x4 {
type Element = f32;
type Bool = bool32x4;
#[inline]
fn lanes() -> usize {
4
}
#[inline]
fn splat(val: Self::Element) -> Self {
f32x4::const_splat(val)
}
#[inline]
fn extract(&self, i: usize) -> Self::Element {
self.as_ref()[i]
}
#[inline]
unsafe fn extract_unchecked(&self, i: usize) -> Self::Element {
*self.as_ref().get_unchecked(i)
}
#[inline]
fn replace(&mut self, i: usize, val: Self::Element) {
self.as_mut()[i] = val;
}
#[inline]
unsafe fn replace_unchecked(&mut self, i: usize, val: Self::Element) {
*self.as_mut().get_unchecked_mut(i) = val;
}
#[inline]
fn select(self, cond: Self::Bool, other: Self) -> Self {
#[cfg(not(feature = "scalar-math"))]
{
Self::from_wide_f32x4(
cond.cast_f32x4()
.cast_wide_f32x4()
.blend(self.cast_wide_f32x4(), other.cast_wide_f32x4()),
)
}
#[cfg(feature = "scalar-math")]
{
let mut arr = [0_f32; 4];
for (i, xi) in arr.iter_mut().enumerate() {
*xi = if cond.0[i] == u32::MAX {
self.0[i]
} else {
other.0[i]
};
}
Self(arr)
}
}
}
#[cfg(all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
))]
mod impl_f32_ops {
use super::*;
impl_op_ex!(-|a: &f32x4| -> f32x4 { f32x4(-a.0) });
impl_op_ex!(+ |a: &f32x4, b: &f32x4| -> f32x4 { f32x4(a.0 + b.0) });
impl_op_ex!(-|a: &f32x4, b: &f32x4| -> f32x4 { f32x4(a.0 - b.0) });
impl_op_ex!(*|a: &f32x4, b: &f32x4| -> f32x4 { f32x4(a.0 * b.0) });
impl_op_ex!(/ |a: &f32x4, b: &f32x4| -> f32x4 { f32x4(a.0 / b.0) });
impl_op_ex!(+= |a: &mut f32x4, b: &f32x4| { a.0 += b.0 });
impl_op_ex!(-= |a: &mut f32x4, b: &f32x4| { a.0 -= b.0 });
impl_op_ex!(*= |a: &mut f32x4, b: &f32x4| { a.0 *= b.0 });
impl_op_ex!(/= |a: &mut f32x4, b: &f32x4| { a.0 /= b.0 });
}
#[cfg(any(
feature = "libm_force",
feature = "scalar-math",
all(feature = "libm_fallback", not(feature = "std"))
))]
mod impl_f32_ops {
use super::*;
impl_op_ex!(-|a: &f32x4| -> f32x4 { f32x4::map(*a, |x| -x) });
impl_op_ex!(+ |a: &f32x4, b: &f32x4| -> f32x4 { f32x4::zip_map(*a, *b, |x, y| x + y) });
impl_op_ex!(-|a: &f32x4, b: &f32x4| -> f32x4 { f32x4::zip_map(*a, *b, |x, y| x - y) });
impl_op_ex!(*|a: &f32x4, b: &f32x4| -> f32x4 { f32x4::zip_map(*a, *b, |x, y| x * y) });
impl_op_ex!(/ |a: &f32x4, b: &f32x4| -> f32x4 { f32x4::zip_map(*a, *b, |x, y| x / y) });
impl_op_ex!(+= |a: &mut f32x4, b: &f32x4| { *a = *a + *b });
impl_op_ex!(-= |a: &mut f32x4, b: &f32x4| { *a = *a - *b });
impl_op_ex!(*= |a: &mut f32x4, b: &f32x4| { *a = *a * *b });
impl_op_ex!(/= |a: &mut f32x4, b: &f32x4| { *a = *a / *b });
}
impl_op_ex!(%= |a: &mut f32x4, b: &f32x4| { *a = *a % *b });
impl_op_ex!(% |a: &f32x4, b: &f32x4| -> f32x4 { f32x4::zip_map(*a, *b, |x, y| x % y) });
#[cfg(all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
))]
impl PartialOrdEx for f32x4 {
#[inline]
fn gt(self, other: Self) -> Self::Bool {
Self(self.0.cmp_gt(other.0)).cast_bool32x4()
}
#[inline]
fn lt(self, other: Self) -> Self::Bool {
Self(self.0.cmp_lt(other.0)).cast_bool32x4()
}
#[inline]
fn ge(self, other: Self) -> Self::Bool {
Self(self.0.cmp_ge(other.0)).cast_bool32x4()
}
#[inline]
fn le(self, other: Self) -> Self::Bool {
Self(self.0.cmp_le(other.0)).cast_bool32x4()
}
#[inline]
fn eq(self, other: Self) -> Self::Bool {
Self(self.0.cmp_eq(other.0)).cast_bool32x4()
}
#[inline]
fn ne(self, other: Self) -> Self::Bool {
Self(self.0.cmp_ne(other.0)).cast_bool32x4()
}
#[inline]
fn max(self, other: Self) -> Self {
Self(self.0.max(other.0))
}
#[inline]
fn min(self, other: Self) -> Self {
Self(self.0.min(other.0))
}
#[inline]
fn clamp(self, min: Self, max: Self) -> Self {
self.min(max).max(min)
}
}
#[cfg(any(
feature = "libm_force",
feature = "scalar-math",
all(feature = "libm_fallback", not(feature = "std"))
))]
impl PartialOrdEx for f32x4 {
#[inline]
fn gt(self, other: Self) -> Self::Bool {
let mut res = [true; 4];
for (i, xi) in res.iter_mut().enumerate() {
*xi = self.0[i] > other.0[i];
}
res.into()
}
#[inline]
fn lt(self, other: Self) -> Self::Bool {
let mut res = [true; 4];
for (i, xi) in res.iter_mut().enumerate() {
*xi = self.0[i] < other.0[i];
}
res.into()
}
#[inline]
fn ge(self, other: Self) -> Self::Bool {
let mut res = [true; 4];
for (i, xi) in res.iter_mut().enumerate() {
*xi = self.0[i] >= other.0[i];
}
res.into()
}
#[inline]
fn le(self, other: Self) -> Self::Bool {
let mut res = [true; 4];
for (i, xi) in res.iter_mut().enumerate() {
*xi = self.0[i] <= other.0[i];
}
res.into()
}
#[inline]
fn eq(self, other: Self) -> Self::Bool {
let mut res = [true; 4];
for (i, xi) in res.iter_mut().enumerate() {
*xi = self.0[i] == other.0[i];
}
res.into()
}
#[inline]
fn ne(self, other: Self) -> Self::Bool {
let mut res = [true; 4];
for (i, xi) in res.iter_mut().enumerate() {
*xi = self.0[i] != other.0[i];
}
res.into()
}
#[inline]
fn max(self, other: Self) -> Self {
self.zip_map(other, |x, y| if x >= y { x } else { y })
}
#[inline]
fn min(self, other: Self) -> Self {
self.zip_map(other, |x, y| if x < y { x } else { y })
}
#[inline]
fn clamp(self, min: Self, max: Self) -> Self {
self.min(max).max(min)
}
}
impl Signed for f32x4 {
#[cfg(all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
))]
#[inline]
fn absf(self) -> Self {
Self(self.0.abs())
}
#[cfg(any(
feature = "libm_force",
feature = "scalar-math",
all(feature = "libm_fallback", not(feature = "std"))
))]
#[inline]
fn absf(self) -> Self {
self.map(Signed::absf)
}
#[inline]
fn signumf(self) -> Self {
self.map(Signed::signumf)
}
}
#[cfg(all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
))]
impl Float for f32x4 {
#[inline]
fn asinf(self) -> Self {
Self(self.0.asin())
}
#[inline]
fn acosf(self) -> Self {
Self(self.0.acos())
}
#[inline]
fn atanf(self) -> Self {
Self(self.0.atan())
}
#[inline]
fn atan2f(self, x: Self) -> Self {
Self(self.0.atan2(x.0))
}
#[inline]
fn ceilf(self) -> Self {
self.map_lanes(|e| e.ceil())
}
#[inline]
fn copysignf(self, sign: Self) -> Self {
self.zip_map_lanes(sign, |e, se| e.copysign(se))
}
#[inline]
fn expf(self) -> Self {
Self(self.0.exp())
}
#[inline]
fn floorf(self) -> Self {
self.map_lanes(|e| e.floor())
}
#[inline]
fn is_finite(self) -> bool32x4 {
let arr: [f32; 4] = self.into();
bool32x4::from([
arr[0].is_finite(),
arr[1].is_finite(),
arr[2].is_finite(),
arr[3].is_finite(),
])
}
#[inline]
fn is_nan(self) -> bool32x4 {
let arr: [f32; 4] = self.into();
bool32x4::from([
arr[0].is_nan(),
arr[1].is_nan(),
arr[2].is_nan(),
arr[3].is_nan(),
])
}
#[inline]
fn mul_addf(self, b: Self, c: Self) -> Self {
Self(self.0.mul_add(b.0, c.0))
}
#[inline]
fn powif(self, n: i32) -> Self {
Self(self.0.pow_f32x4(crate::wide::f32x4::from(n as f32)))
}
#[inline]
fn powff(self, n: Self) -> Self {
Self(self.0.pow_f32x4(n.0))
}
#[inline]
fn recip(self) -> Self {
self.map_lanes(|e| e.recip())
}
#[inline]
fn roundf(self) -> Self {
Self(self.0.round())
}
#[inline]
fn sqrtf(self) -> Self {
Self(self.0.sqrt())
}
#[inline]
fn sinf(self) -> Self {
Self(self.0.sin())
}
#[inline]
fn cosf(self) -> Self {
Self(self.0.cos())
}
#[inline]
fn sin_cosf(self) -> (Self, Self) {
let t = self.0.sin_cos();
(Self(t.0), Self(t.1))
}
#[inline]
fn tanf(self) -> Self {
Self(self.0.tan())
}
#[inline]
fn minf(self, other: Self) -> Self {
Self(self.0.min(other.0))
}
#[inline]
fn maxf(self, other: Self) -> Self {
Self(self.0.max(other.0))
}
}
#[cfg(any(
feature = "libm_force",
feature = "scalar-math",
all(feature = "libm_fallback", not(feature = "std"))
))]
impl Float for f32x4 {
#[inline]
fn asinf(self) -> Self {
self.map(Float::asinf)
}
#[inline]
fn acosf(self) -> Self {
self.map(Float::acosf)
}
#[inline]
fn atanf(self) -> Self {
self.map(Float::atanf)
}
#[inline]
fn atan2f(self, x: Self) -> Self {
self.zip_map(x, Float::atan2f)
}
#[inline]
fn ceilf(self) -> Self {
self.map(Float::ceilf)
}
#[inline]
fn copysignf(self, sign: Self) -> Self {
self.zip_map_lanes(sign, Float::copysignf)
}
#[inline]
fn expf(self) -> Self {
self.map(Float::expf)
}
#[inline]
fn floorf(self) -> Self {
self.map_lanes(Float::floorf)
}
#[inline]
fn is_finite(self) -> bool32x4 {
let arr: &[f32; 4] = self.as_ref();
bool32x4::from([
arr[0].is_finite(),
arr[1].is_finite(),
arr[2].is_finite(),
arr[3].is_finite(),
])
}
#[inline]
fn is_nan(self) -> bool32x4 {
let arr: &[f32; 4] = self.as_ref();
bool32x4::from([
arr[0].is_nan(),
arr[1].is_nan(),
arr[2].is_nan(),
arr[3].is_nan(),
])
}
#[inline]
fn mul_addf(self, b: Self, c: Self) -> Self {
let mut res = [0f32; 4];
for (i, xi) in res.iter_mut().enumerate() {
*xi = Float::mul_addf(self.0[i], b.0[i], c.0[i])
}
res.into()
}
#[inline]
fn powif(self, n: i32) -> Self {
self.map(|x| Float::powif(x, n))
}
#[inline]
fn powff(self, n: Self) -> Self {
self.zip_map(n, Float::powff)
}
#[inline]
fn recip(self) -> Self {
self.map(Float::recip)
}
#[inline]
fn roundf(self) -> Self {
self.map(Float::roundf)
}
#[inline]
fn sqrtf(self) -> Self {
self.map(Float::sqrtf)
}
#[inline]
fn sinf(self) -> Self {
self.map(Float::sinf)
}
#[inline]
fn cosf(self) -> Self {
self.map(Float::cosf)
}
#[inline]
fn sin_cosf(self) -> (Self, Self) {
let mut sin_res = [0f32; 4];
let mut cos_res = [0f32; 4];
for i in 0..4 {
(sin_res[i], cos_res[i]) = Float::sin_cosf(self.0[i])
}
(sin_res.into(), cos_res.into())
}
#[inline]
fn tanf(self) -> Self {
self.map(Float::tanf)
}
#[inline]
fn minf(self, other: Self) -> Self {
self.zip_map(other, Float::minf)
}
#[inline]
fn maxf(self, other: Self) -> Self {
self.zip_map(other, Float::maxf)
}
}
impl_wide_partial_eq!(f32x4);
impl NumEx for f32x4 {}
impl SignedEx for f32x4 {}
impl FloatEx for f32x4 {
#[inline]
fn acos_approx(self) -> Self {
let nonnegative = self.ge(Self::ZERO);
let x = self.absf();
let omx = (Self::ONE - x).max(Self::ZERO);
let root = omx.sqrtf();
#[allow(clippy::approx_constant)]
let mut result = ((((((Self::splat(-0.001_262_491_1) * x + Self::splat(0.006_670_09))
* x
- Self::splat(0.017_088_126))
* x
+ Self::splat(0.030_891_88))
* x
- Self::splat(0.050_174_303))
* x
+ Self::splat(0.088_978_99))
* x
- Self::splat(0.214_598_8))
* x
+ Self::splat(1.570_796_3);
result *= root;
result.select(nonnegative, Self::PI - result)
}
}
#[cfg(all(
not(target_arch = "spirv"),
all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
)
))]
impl fmt::Display for f32x4 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[cfg(all(
not(target_arch = "spirv"),
any(
feature = "libm_force",
feature = "scalar-math",
all(feature = "libm_fallback", not(feature = "std"))
)
))]
impl fmt::Display for f32x4 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"({}, {}, {}, {})",
self.0[0], self.0[1], self.0[2], self.0[3]
)
}
}
#[cfg(any(
feature = "libm_force",
feature = "scalar-math",
all(feature = "libm_fallback", not(feature = "std"))
))]
impl fmt::Debug for f32x4 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"f32x4(({:?}, {:?}, {:?}, {:?}))",
self.0[0], self.0[1], self.0[2], self.0[3]
)
}
}
impl_wide_scalar_ops!(f32x4, f32);
impl_default!(f32x4);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_f32x4_signum() {
let a1 = f32x4::ONE;
let a2 = f32x4::ZERO;
let b1 = -a1;
let b2 = -a2;
assert_eq!(a1.signumf(), a1);
assert_eq!(b1.signumf(), -a1);
assert_eq!(a2.signumf(), a1);
#[cfg(all(
feature = "std",
not(feature = "libm_force"),
not(feature = "scalar-math")
))]
assert_eq!(b2.signumf(), a1);
#[cfg(any(
feature = "libm_force",
feature = "scalar-math",
all(feature = "libm_fallback", not(feature = "std"))
))]
assert_eq!(b2.signumf(), -a1);
}
#[test]
fn test_f32_as_ref() {
let f1 = f32x4::ONE;
let a1 = f1.as_ref();
assert_eq!(a1[0], 1_f32);
assert_eq!(a1[1], 1_f32);
assert_eq!(a1[2], 1_f32);
assert_eq!(a1[3], 1_f32);
assert_eq!(*a1, [1f32; 4]);
}
}