1use hermes_simd_core::{arch::SimdArch, kernel::SimdKernel, scalar::Scalar, view::SimdError};
19use hermes_simd_macros::runtime_dispatch;
20
21const MAX_STACK_LANES: usize = 128;
22
23#[inline]
24fn mul_pair<T, const CONJ_B: bool>(ar: T, ai: T, br: T, bi: T) -> (T, T)
25where
26 T: Scalar,
27{
28 if CONJ_B {
29 (ar * br + ai * bi, ai * br - ar * bi)
31 } else {
32 (ar * br - ai * bi, ar * bi + ai * br)
34 }
35}
36
37#[inline(always)]
43unsafe fn complex_mul_vector<T, A, const CONJ_B: bool>(av: A::Vector, bv: A::Vector) -> A::Vector
44where
45 T: Scalar,
46 A: SimdArch + SimdKernel<T>,
47{
48 let b_sw = A::swap_adjacent(bv);
49 if CONJ_B {
50 A::fmsubadd(A::dup_odd(av), b_sw, A::mul(A::dup_even(av), bv))
52 } else {
53 A::fmaddsub(A::dup_even(av), bv, A::mul(A::dup_odd(av), b_sw))
55 }
56}
57
58#[inline]
80pub fn interleaved_complex_mul_assign<T, A, const CONJ_B: bool>(
81 a: &mut [T],
82 b: &[T],
83) -> Result<(), SimdError>
84where
85 T: Scalar,
86 A: SimdArch + SimdKernel<T>,
87{
88 if a.len() != b.len() || (a.len() & 1) != 0 {
89 return Err(SimdError::LengthMismatch);
90 }
91
92 if A::REGISTER_WIDTH_BITS == 0 && a.len() >= 32_768 {
93 let mut lane = 0usize;
94 while lane + 8 <= a.len() {
95 let (re0, im0) = mul_pair::<T, CONJ_B>(a[lane], a[lane + 1], b[lane], b[lane + 1]);
96 a[lane] = re0;
97 a[lane + 1] = im0;
98
99 let (re1, im1) =
100 mul_pair::<T, CONJ_B>(a[lane + 2], a[lane + 3], b[lane + 2], b[lane + 3]);
101 a[lane + 2] = re1;
102 a[lane + 3] = im1;
103
104 let (re2, im2) =
105 mul_pair::<T, CONJ_B>(a[lane + 4], a[lane + 5], b[lane + 4], b[lane + 5]);
106 a[lane + 4] = re2;
107 a[lane + 5] = im2;
108
109 let (re3, im3) =
110 mul_pair::<T, CONJ_B>(a[lane + 6], a[lane + 7], b[lane + 6], b[lane + 7]);
111 a[lane + 6] = re3;
112 a[lane + 7] = im3;
113
114 lane += 8;
115 }
116 while lane < a.len() {
117 let (re, im) = mul_pair::<T, CONJ_B>(a[lane], a[lane + 1], b[lane], b[lane + 1]);
118 a[lane] = re;
119 a[lane + 1] = im;
120 lane += 2;
121 }
122 return Ok(());
123 }
124
125 let lanes = A::LANE_COUNT;
126 let mut offset = 0usize;
127
128 if lanes >= 2 && lanes & 1 == 0 {
131 while offset + 2 * lanes <= a.len() {
132 unsafe {
137 let av0 = A::load_unaligned(a.as_ptr().add(offset));
138 let bv0 = A::load_unaligned(b.as_ptr().add(offset));
139 let res0 = complex_mul_vector::<T, A, CONJ_B>(av0, bv0);
140 A::store_unaligned(a.as_mut_ptr().add(offset), res0);
141
142 let next = offset + lanes;
143 let av1 = A::load_unaligned(a.as_ptr().add(next));
144 let bv1 = A::load_unaligned(b.as_ptr().add(next));
145 let res1 = complex_mul_vector::<T, A, CONJ_B>(av1, bv1);
146 A::store_unaligned(a.as_mut_ptr().add(next), res1);
147 }
148 offset += 2 * lanes;
149 }
150
151 while offset + lanes <= a.len() {
152 unsafe {
156 let av = A::load_unaligned(a.as_ptr().add(offset));
157 let bv = A::load_unaligned(b.as_ptr().add(offset));
158 let res = complex_mul_vector::<T, A, CONJ_B>(av, bv);
159 A::store_unaligned(a.as_mut_ptr().add(offset), res);
160 }
161 offset += lanes;
162 }
163 }
164
165 let mut lane = offset;
166 while lane < a.len() {
167 let (re, im) = mul_pair::<T, CONJ_B>(a[lane], a[lane + 1], b[lane], b[lane + 1]);
168 a[lane] = re;
169 a[lane + 1] = im;
170 lane += 2;
171 }
172
173 Ok(())
174}
175
176#[inline]
196pub fn interleaved_complex_dot<T, A, const CONJ_B: bool>(
197 a: &[T],
198 b: &[T],
199) -> Result<(T, T), SimdError>
200where
201 T: Scalar,
202 A: SimdArch + SimdKernel<T>,
203{
204 if a.len() != b.len() || (a.len() & 1) != 0 {
205 return Err(SimdError::LengthMismatch);
206 }
207
208 let lanes = A::LANE_COUNT;
209 let mut offset = 0usize;
210 let mut re = T::ZERO;
211 let mut im = T::ZERO;
212
213 if lanes >= 2 && lanes & 1 == 0 && offset + lanes <= a.len() {
214 assert!(
215 lanes <= MAX_STACK_LANES,
216 "SIMD lane count exceeds stack buffer"
217 );
218 let (mut acc0, mut acc1) = unsafe { (A::zero(), A::zero()) };
222 while offset + 2 * lanes <= a.len() {
223 unsafe {
226 let av0 = A::load_unaligned(a.as_ptr().add(offset));
227 let bv0 = A::load_unaligned(b.as_ptr().add(offset));
228 acc0 = A::add(acc0, complex_mul_vector::<T, A, CONJ_B>(av0, bv0));
229 let av1 = A::load_unaligned(a.as_ptr().add(offset + lanes));
230 let bv1 = A::load_unaligned(b.as_ptr().add(offset + lanes));
231 acc1 = A::add(acc1, complex_mul_vector::<T, A, CONJ_B>(av1, bv1));
232 }
233 offset += 2 * lanes;
234 }
235 while offset + lanes <= a.len() {
236 unsafe {
239 let av = A::load_unaligned(a.as_ptr().add(offset));
240 let bv = A::load_unaligned(b.as_ptr().add(offset));
241 acc0 = A::add(acc0, complex_mul_vector::<T, A, CONJ_B>(av, bv));
242 }
243 offset += lanes;
244 }
245 let acc = unsafe { A::add(acc0, acc1) };
247
248 let mut buf = [T::ZERO; MAX_STACK_LANES];
250 unsafe { A::store_unaligned(buf.as_mut_ptr(), acc) };
252 let mut lane = 0usize;
253 while lane < lanes {
254 re = re + buf[lane];
255 im = im + buf[lane + 1];
256 lane += 2;
257 }
258 }
259
260 let mut lane = offset;
261 while lane < a.len() {
262 let (prod_re, prod_im) = mul_pair::<T, CONJ_B>(a[lane], a[lane + 1], b[lane], b[lane + 1]);
263 re = re + prod_re;
264 im = im + prod_im;
265 lane += 2;
266 }
267
268 Ok((re, im))
269}
270
271#[runtime_dispatch(avx512f, avx2, neon, scalar)]
272pub(super) fn dispatch_interleaved_complex_mul_assign_impl<T, const CONJ_B: bool, A>(
273 a: &mut [T],
274 b: &[T],
275) -> Result<(), SimdError>
276where
277 T: Scalar,
278 A: SimdArch + SimdKernel<T>,
279{
280 interleaved_complex_mul_assign::<T, A, CONJ_B>(a, b)
281}
282
283#[runtime_dispatch(avx512f, avx2, neon, scalar)]
284pub(super) fn dispatch_interleaved_complex_dot_impl<T, const CONJ_B: bool, A>(
285 a: &[T],
286 b: &[T],
287) -> Result<(T, T), SimdError>
288where
289 T: Scalar,
290 A: SimdArch + SimdKernel<T>,
291{
292 interleaved_complex_dot::<T, A, CONJ_B>(a, b)
293}