use core::arch::hexagon::v128::*;
type V = HvxVector;
pub const LANES: usize = 32;
#[derive(Clone, Copy)]
struct Wide {
hi: V,
lo: V,
}
#[inline(always)]
unsafe fn add(a: Wide, b: Wide) -> Wide {
unsafe {
let lo = Q6_Vw_vadd_VwVw(a.lo, b.lo);
let carry = Q6_Vuw_vlsr_VuwR(Q6_V_vor_VV(Q6_V_vand_VV(a.lo, b.lo), Q6_V_vand_VV(Q6_V_vor_VV(a.lo, b.lo), Q6_V_vnot_V(lo))), 31);
Wide { hi: Q6_Vw_vadd_VwVw(Q6_Vw_vadd_VwVw(a.hi, b.hi), carry), lo }
}
}
#[inline(always)]
unsafe fn sub(a: Wide, b: Wide) -> Wide {
unsafe {
let lo = Q6_Vw_vsub_VwVw(a.lo, b.lo);
let na = Q6_V_vnot_V(a.lo);
let borrow = Q6_Vuw_vlsr_VuwR(Q6_V_vor_VV(Q6_V_vand_VV(na, b.lo), Q6_V_vand_VV(Q6_V_vor_VV(na, b.lo), lo)), 31);
Wide { hi: Q6_Vw_vsub_VwVw(Q6_Vw_vsub_VwVw(a.hi, b.hi), borrow), lo }
}
}
#[inline(always)]
unsafe fn product(a: V, b: V) -> Wide {
unsafe {
let p = Q6_W_vmpyoacc_WVwVh(Q6_W_vmpye_VwVuh(a, b), a, b);
Wide { hi: Q6_V_hi_W(p), lo: Q6_V_lo_W(p) }
}
}
#[inline(always)]
unsafe fn fx<const RND: i32, const SH: i32>(e: V, a: V) -> Wide {
unsafe {
let r = add(product(a, e), Wide { hi: Q6_V_vzero(), lo: Q6_V_vsplat_R(RND) });
Wide { hi: Q6_Vw_vasr_VwR(r.hi, SH), lo: Q6_V_vor_VV(Q6_Vuw_vlsr_VuwR(r.lo, SH), Q6_Vw_vasl_VwR(r.hi, 32 - SH)) }
}
}
#[inline(always)]
unsafe fn load(s: &[i32], k: usize) -> V {
debug_assert!(k + LANES <= s.len());
unsafe { core::ptr::read_unaligned(s.as_ptr().add(k).cast::<V>()) }
}
#[inline(always)]
unsafe fn store(d: &mut [i32], k: usize, v: V) {
debug_assert!(k + LANES <= d.len());
unsafe { core::ptr::write_unaligned(d.as_mut_ptr().add(k).cast::<V>(), v) }
}
pub unsafe fn rows<const RND: i32, const SH: i32, const LAST_ADD: bool>(m: &[i32; 8], cols: [&[i32]; 4], hi: [&mut [i32]; 4], lo: [&mut [i32]; 4]) {
let n = cols[0].len();
debug_assert!(n.is_multiple_of(LANES) && cols.iter().all(|c| c.len() == n));
unsafe {
let mut e = [Q6_V_vzero(); 8];
for (v, &x) in e.iter_mut().zip(m) {
*v = Q6_V_vsplat_R(x);
}
let [hi0r, hi0i, hi1r, hi1i] = hi;
let [lo0r, lo0i, lo1r, lo1i] = lo;
let f = fx::<RND, SH>;
let mut k = 0;
while k < n {
let (a0r, a0i, a1r, a1i) = (load(cols[0], k), load(cols[1], k), load(cols[2], k), load(cols[3], k));
let t = add(sub(f(e[0], a0r), f(e[1], a0i)), f(e[2], a1r));
let o0r = if LAST_ADD { add(t, f(e[3], a1i)) } else { sub(t, f(e[3], a1i)) };
let o0i = add(add(add(f(e[0], a0i), f(e[1], a0r)), f(e[2], a1i)), f(e[3], a1r));
let o1r = sub(add(sub(f(e[4], a0r), f(e[5], a0i)), f(e[6], a1r)), f(e[7], a1i));
let o1i = add(add(add(f(e[4], a0i), f(e[5], a0r)), f(e[6], a1i)), f(e[7], a1r));
for (h, l, o) in [(&mut *hi0r, &mut *lo0r, o0r), (&mut *hi0i, &mut *lo0i, o0i), (&mut *hi1r, &mut *lo1r, o1r), (&mut *hi1i, &mut *lo1i, o1i)] {
store(h, k, o.hi);
store(l, k, o.lo);
}
k += LANES;
}
}
}
pub unsafe fn probe<const RND: i32, const SH: i32>(a: &[i32], b: &[i32], prod: [&mut [i32]; 2], fixed: [&mut [i32]; 2]) {
let n = a.len();
debug_assert!(n.is_multiple_of(LANES) && b.len() == n);
let [ph, pl] = prod;
let [fh, fl] = fixed;
unsafe {
let mut k = 0;
while k < n {
let (va, vb) = (load(a, k), load(b, k));
let p = product(va, vb);
let f = fx::<RND, SH>(vb, va);
store(ph, k, p.hi);
store(pl, k, p.lo);
store(fh, k, f.hi);
store(fl, k, f.lo);
k += LANES;
}
}
}