use core::arch::x86_64::{
__m256i, __m512i, _mm512_castsi256_si512, _mm512_castsi512_si256, _mm512_extracti64x4_epi64,
_mm512_inserti64x4,
};
use core::array;
use crate::clmul::wide::{Lanes64, Wide};
#[inline(always)]
pub(crate) fn join(low: __m256i, high: __m256i) -> __m512i {
unsafe { _mm512_inserti64x4::<1>(_mm512_castsi256_si512(low), high) }
}
#[inline(always)]
pub(crate) fn join_all<const D: usize>(low: [__m256i; D], high: [__m256i; D]) -> [__m512i; D] {
array::from_fn(|i| join(low[i], high[i]))
}
#[inline(always)]
fn fold_halves(x: __m512i) -> __m256i {
unsafe { _mm512_castsi512_si256(x).xor(_mm512_extracti64x4_epi64::<1>(x)) }
}
#[inline(always)]
pub(crate) fn sum_of_products<U, V, const D: usize>(
u: &[U],
v: &[V],
pair: impl Fn(&[U; 2], &[V; 2]) -> [Wide<__m512i>; D],
single: impl Fn(&U, &V) -> [Wide<__m256i>; D],
) -> [Wide<__m256i>; D] {
let (u_pairs, u_last) = u.as_chunks::<2>();
let (v_pairs, v_last) = v.as_chunks::<2>();
let wide = u_pairs
.iter()
.zip(v_pairs)
.fold([Wide::zero(); D], |sum, (a, b)| {
let product = pair(a, b);
array::from_fn(|i| sum[i].xor(product[i]))
});
let mut sum = wide.map(|w| Wide {
even: fold_halves(w.even),
odd: fold_halves(w.odd),
});
if let (Some(a), Some(b)) = (u_last.first(), v_last.first()) {
let product = single(a, b);
sum = array::from_fn(|i| sum[i].xor(product[i]));
}
sum
}