use crate::Vec4;
use std::arch::x86_64::*;
#[repr(C)]
#[derive(Copy, Clone)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Zeroable, bytemuck::Pod))]
pub struct Dvec4 {
pub(crate) inner: __m256d,
}
impl std::fmt::Debug for Dvec4 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.as_array().fmt(f)
}
}
impl Vec4<f64> for Dvec4 {
#[inline]
fn new(x: f64, y: f64, z: f64, w: f64) -> Dvec4 {
unsafe {
Dvec4 {
inner: _mm256_set_pd(w, z, y, x),
}
}
}
#[inline]
fn as_array(&self) -> &[f64; 4] {
unsafe { &*(self as *const Dvec4 as *const [f64; 4]) }
}
#[inline]
fn as_mut_array(&mut self) -> &mut [f64; 4] {
unsafe { &mut *(self as *mut Dvec4 as *mut [f64; 4]) }
}
#[inline]
fn add_componentwise(&self, rhs: Dvec4) -> Dvec4 {
unsafe {
Dvec4 {
inner: _mm256_add_pd(self.inner, rhs.inner),
}
}
}
#[inline]
fn sub_componentwise(&self, rhs: Dvec4) -> Dvec4 {
unsafe {
Dvec4 {
inner: _mm256_sub_pd(self.inner, rhs.inner),
}
}
}
#[inline]
fn mul_componentwise(&self, rhs: Dvec4) -> Dvec4 {
unsafe {
Dvec4 {
inner: _mm256_mul_pd(self.inner, rhs.inner),
}
}
}
#[inline]
fn div_componentwise(&self, rhs: Dvec4) -> Dvec4 {
unsafe {
Dvec4 {
inner: _mm256_div_pd(self.inner, rhs.inner),
}
}
}
#[inline]
fn min_componentwise(&self, rhs: Dvec4) -> Dvec4 {
unsafe {
Dvec4 {
inner: _mm256_min_pd(self.inner, rhs.inner),
}
}
}
#[inline]
fn max_componentwise(&self, rhs: Dvec4) -> Dvec4 {
unsafe {
Dvec4 {
inner: _mm256_max_pd(self.inner, rhs.inner),
}
}
}
#[inline]
fn floor(&self) -> Dvec4 {
unsafe {
Dvec4 {
inner: _mm256_floor_pd(self.inner),
}
}
}
#[inline]
fn min_reduce(&self) -> f64 {
unsafe {
let reduce128 = _mm_min_pd(
_mm256_castpd256_pd128(self.inner),
_mm256_extractf128_pd::<1>(self.inner),
);
let reduce64 = _mm_min_sd(reduce128, _mm_permute_pd::<1>(reduce128));
_mm_cvtsd_f64(reduce64)
}
}
#[inline]
fn max_reduce(&self) -> f64 {
unsafe {
let reduce128 = _mm_max_pd(
_mm256_castpd256_pd128(self.inner),
_mm256_extractf128_pd::<1>(self.inner),
);
let reduce64 = _mm_max_sd(reduce128, _mm_permute_pd::<1>(reduce128));
_mm_cvtsd_f64(reduce64)
}
}
#[inline]
fn eq_reduce(&self, rhs: Dvec4) -> bool {
unsafe {
let mask = _mm256_cmp_pd::<_CMP_EQ_OQ>(self.inner, rhs.inner);
let reduce = _mm256_movemask_epi8(std::mem::transmute(mask));
reduce as u32 == 0xffffffff
}
}
#[inline]
fn dot(&self, rhs: Dvec4) -> f64 {
unsafe {
let prod = _mm256_mul_pd(self.inner, rhs.inner);
let reduce128 = _mm_add_pd(
_mm256_castpd256_pd128(prod),
_mm256_extractf128_pd::<1>(prod),
);
let reduce64 = _mm_add_sd(reduce128, _mm_permute_pd::<1>(reduce128));
_mm_cvtsd_f64(reduce64)
}
}
#[inline]
fn cross(&self, rhs: Dvec4) -> Dvec4 {
unsafe {
let left = _mm256_mul_pd(
self.inner,
_mm256_permute4x64_pd::<0b_11_00_10_01>(rhs.inner),
);
let right = _mm256_mul_pd(
rhs.inner,
_mm256_permute4x64_pd::<0b_11_00_10_01>(self.inner),
);
let result = _mm256_permute4x64_pd::<0b_11_00_10_01>(_mm256_sub_pd(left, right));
Dvec4 { inner: result }
}
}
}
implement_scalarops!(Dvec4, f64);
implement_vecops!(Dvec4, f64);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn eq_works() {
let a = Dvec4::new(1.0, 2.0, 3.0, 4.0);
let b = Dvec4::new(1.0, 2.0, 3.0, 4.0);
let c = Dvec4::new(5.0, 6.0, 7.0, 8.0);
assert_eq!(a == b, true);
assert_eq!(b == a, true);
assert_eq!(a == a, true);
assert_eq!(a == c, false);
let d = Dvec4::new(0.0, -0.0, 0.0, -0.0);
let e = Dvec4::new(0.0, 0.0, -0.0, -0.0);
assert_eq!(d == e, true);
let f = Dvec4::new(f64::NAN, f64::NAN, f64::NAN, f64::NAN);
assert_eq!(f == f, false);
}
}