use hermes_simd_core::{arch::SimdArch, kernel::SimdKernel, scalar::Scalar, view::SimdError};
use hermes_simd_macros::runtime_dispatch;
const MAX_STACK_LANES: usize = 128;
#[inline]
fn mul_pair<T, const CONJ_B: bool>(ar: T, ai: T, br: T, bi: T) -> (T, T)
where
T: Scalar,
{
if CONJ_B {
(ar * br + ai * bi, ai * br - ar * bi)
} else {
(ar * br - ai * bi, ar * bi + ai * br)
}
}
#[inline(always)]
unsafe fn complex_mul_vector<T, A, const CONJ_B: bool>(av: A::Vector, bv: A::Vector) -> A::Vector
where
T: Scalar,
A: SimdArch + SimdKernel<T>,
{
let b_sw = A::swap_adjacent(bv);
if CONJ_B {
A::fmsubadd(A::dup_odd(av), b_sw, A::mul(A::dup_even(av), bv))
} else {
A::fmaddsub(A::dup_even(av), bv, A::mul(A::dup_odd(av), b_sw))
}
}
#[inline]
pub fn interleaved_complex_mul_assign<T, A, const CONJ_B: bool>(
a: &mut [T],
b: &[T],
) -> Result<(), SimdError>
where
T: Scalar,
A: SimdArch + SimdKernel<T>,
{
if a.len() != b.len() || (a.len() & 1) != 0 {
return Err(SimdError::LengthMismatch);
}
if A::REGISTER_WIDTH_BITS == 0 && a.len() >= 32_768 {
let mut lane = 0usize;
while lane + 8 <= a.len() {
let (re0, im0) = mul_pair::<T, CONJ_B>(a[lane], a[lane + 1], b[lane], b[lane + 1]);
a[lane] = re0;
a[lane + 1] = im0;
let (re1, im1) =
mul_pair::<T, CONJ_B>(a[lane + 2], a[lane + 3], b[lane + 2], b[lane + 3]);
a[lane + 2] = re1;
a[lane + 3] = im1;
let (re2, im2) =
mul_pair::<T, CONJ_B>(a[lane + 4], a[lane + 5], b[lane + 4], b[lane + 5]);
a[lane + 4] = re2;
a[lane + 5] = im2;
let (re3, im3) =
mul_pair::<T, CONJ_B>(a[lane + 6], a[lane + 7], b[lane + 6], b[lane + 7]);
a[lane + 6] = re3;
a[lane + 7] = im3;
lane += 8;
}
while lane < a.len() {
let (re, im) = mul_pair::<T, CONJ_B>(a[lane], a[lane + 1], b[lane], b[lane + 1]);
a[lane] = re;
a[lane + 1] = im;
lane += 2;
}
return Ok(());
}
let lanes = A::LANE_COUNT;
let mut offset = 0usize;
if lanes >= 2 && lanes & 1 == 0 {
while offset + 2 * lanes <= a.len() {
unsafe {
let av0 = A::load_unaligned(a.as_ptr().add(offset));
let bv0 = A::load_unaligned(b.as_ptr().add(offset));
let res0 = complex_mul_vector::<T, A, CONJ_B>(av0, bv0);
A::store_unaligned(a.as_mut_ptr().add(offset), res0);
let next = offset + lanes;
let av1 = A::load_unaligned(a.as_ptr().add(next));
let bv1 = A::load_unaligned(b.as_ptr().add(next));
let res1 = complex_mul_vector::<T, A, CONJ_B>(av1, bv1);
A::store_unaligned(a.as_mut_ptr().add(next), res1);
}
offset += 2 * lanes;
}
while offset + lanes <= a.len() {
unsafe {
let av = A::load_unaligned(a.as_ptr().add(offset));
let bv = A::load_unaligned(b.as_ptr().add(offset));
let res = complex_mul_vector::<T, A, CONJ_B>(av, bv);
A::store_unaligned(a.as_mut_ptr().add(offset), res);
}
offset += lanes;
}
}
let mut lane = offset;
while lane < a.len() {
let (re, im) = mul_pair::<T, CONJ_B>(a[lane], a[lane + 1], b[lane], b[lane + 1]);
a[lane] = re;
a[lane + 1] = im;
lane += 2;
}
Ok(())
}
#[inline]
pub fn interleaved_complex_dot<T, A, const CONJ_B: bool>(
a: &[T],
b: &[T],
) -> Result<(T, T), SimdError>
where
T: Scalar,
A: SimdArch + SimdKernel<T>,
{
if a.len() != b.len() || (a.len() & 1) != 0 {
return Err(SimdError::LengthMismatch);
}
let lanes = A::LANE_COUNT;
let mut offset = 0usize;
let mut re = T::ZERO;
let mut im = T::ZERO;
if lanes >= 2 && lanes & 1 == 0 && offset + lanes <= a.len() {
assert!(
lanes <= MAX_STACK_LANES,
"SIMD lane count exceeds stack buffer"
);
let (mut acc0, mut acc1) = unsafe { (A::zero(), A::zero()) };
while offset + 2 * lanes <= a.len() {
unsafe {
let av0 = A::load_unaligned(a.as_ptr().add(offset));
let bv0 = A::load_unaligned(b.as_ptr().add(offset));
acc0 = A::add(acc0, complex_mul_vector::<T, A, CONJ_B>(av0, bv0));
let av1 = A::load_unaligned(a.as_ptr().add(offset + lanes));
let bv1 = A::load_unaligned(b.as_ptr().add(offset + lanes));
acc1 = A::add(acc1, complex_mul_vector::<T, A, CONJ_B>(av1, bv1));
}
offset += 2 * lanes;
}
while offset + lanes <= a.len() {
unsafe {
let av = A::load_unaligned(a.as_ptr().add(offset));
let bv = A::load_unaligned(b.as_ptr().add(offset));
acc0 = A::add(acc0, complex_mul_vector::<T, A, CONJ_B>(av, bv));
}
offset += lanes;
}
let acc = unsafe { A::add(acc0, acc1) };
let mut buf = [T::ZERO; MAX_STACK_LANES];
unsafe { A::store_unaligned(buf.as_mut_ptr(), acc) };
let mut lane = 0usize;
while lane < lanes {
re = re + buf[lane];
im = im + buf[lane + 1];
lane += 2;
}
}
let mut lane = offset;
while lane < a.len() {
let (prod_re, prod_im) = mul_pair::<T, CONJ_B>(a[lane], a[lane + 1], b[lane], b[lane + 1]);
re = re + prod_re;
im = im + prod_im;
lane += 2;
}
Ok((re, im))
}
#[runtime_dispatch(avx512f, avx2, neon, scalar)]
pub(super) fn dispatch_interleaved_complex_mul_assign_impl<T, const CONJ_B: bool, A>(
a: &mut [T],
b: &[T],
) -> Result<(), SimdError>
where
T: Scalar,
A: SimdArch + SimdKernel<T>,
{
interleaved_complex_mul_assign::<T, A, CONJ_B>(a, b)
}
#[runtime_dispatch(avx512f, avx2, neon, scalar)]
pub(super) fn dispatch_interleaved_complex_dot_impl<T, const CONJ_B: bool, A>(
a: &[T],
b: &[T],
) -> Result<(T, T), SimdError>
where
T: Scalar,
A: SimdArch + SimdKernel<T>,
{
interleaved_complex_dot::<T, A, CONJ_B>(a, b)
}