use core::marker::PhantomData;
use super::{AlphaStatus, BetaStatus, Epilogue, KernelFamily};
use crate::scalar::{ComplexFloat, Scalar};
use crate::simd::KernelSimd;
pub struct ComplexGemm<T, const CONJ_A: bool, const CONJ_B: bool>(PhantomData<T>);
impl<T, const CONJ_A: bool, const CONJ_B: bool> Clone for ComplexGemm<T, CONJ_A, CONJ_B> {
fn clone(&self) -> Self {
*self
}
}
impl<T, const CONJ_A: bool, const CONJ_B: bool> Copy for ComplexGemm<T, CONJ_A, CONJ_B> {}
#[allow(clippy::too_many_arguments)]
#[inline]
unsafe fn pack_planar<T: ComplexFloat>(
dst: *mut T,
src: *const T,
lead: isize,
depth: isize,
n_lead: usize,
depth_len: usize,
width: usize,
conj: bool,
) {
unsafe {
let tile = crate::tuning::pack_transpose_tile();
let zero = <T::Real as Scalar>::ZERO;
let pack_im = |im: T::Real| if conj { -im } else { im };
let mut panel = dst as *mut T::Real;
let mut base = 0usize;
while base < n_lead {
let live = core::cmp::min(width, n_lead - base);
if lead == 1 {
for p in 0..depth_len {
let re_off = p * 2 * width;
let s = src.offset(base as isize + p as isize * depth);
for i in 0..width {
if i < live {
let z = *s.add(i);
*panel.add(re_off + i) = z.re();
*panel.add(re_off + width + i) = pack_im(z.im());
} else {
*panel.add(re_off + i) = zero;
*panel.add(re_off + width + i) = zero;
}
}
}
} else {
let mut p0 = 0;
while p0 < depth_len {
let pe = core::cmp::min(p0 + tile, depth_len);
for i in 0..width {
if i < live {
let row = src.offset((base + i) as isize * lead);
for p in p0..pe {
let z = *row.offset(p as isize * depth);
*panel.add(p * 2 * width + i) = z.re();
*panel.add(p * 2 * width + width + i) = pack_im(z.im());
}
} else {
for p in p0..pe {
*panel.add(p * 2 * width + i) = zero;
*panel.add(p * 2 * width + width + i) = zero;
}
}
}
p0 = pe;
}
}
panel = panel.add(depth_len * 2 * width);
base += width;
}
}
}
impl<T, const CONJ_A: bool, const CONJ_B: bool> KernelFamily for ComplexGemm<T, CONJ_A, CONJ_B>
where
T: ComplexFloat,
{
type Lhs = T;
type Rhs = T;
type Acc = T;
type Out = T;
const FORCE_PACK_LHS: bool = true;
const FORCE_PACK_RHS: bool = true;
#[inline]
unsafe fn pack_lhs(
dst: *mut T,
src: *const T,
rs: isize,
cs: isize,
mc: usize,
kc: usize,
mr: usize,
) {
unsafe {
pack_planar(
dst, src, rs, cs, mc, kc, mr, CONJ_A,
);
}
}
#[inline]
unsafe fn pack_rhs(
dst: *mut T,
src: *const T,
rs: isize,
cs: isize,
kc: usize,
nc: usize,
nr: usize,
) {
unsafe {
pack_planar(
dst, src, cs, rs, nc, kc, nr, CONJ_B,
);
}
}
#[allow(clippy::too_many_arguments)]
#[inline(always)]
unsafe fn microkernel<S, const MR_REG: usize, const NR: usize>(
simd: S,
kc: usize,
alpha: T,
beta: T,
alpha_status: AlphaStatus,
beta_status: BetaStatus,
a: *const T,
a_cs: isize,
b: *const T,
b_rs: isize,
_b_cs: isize,
c: *mut T,
rsc: isize,
csc: isize,
mr_eff: usize,
nr_eff: usize,
scratch: *mut T,
) where
S: KernelSimd<T, T, T, T>,
{
unsafe {
simd.cplx_microkernel::<MR_REG, NR>(
kc,
alpha,
beta,
alpha_status == AlphaStatus::One,
beta_status == BetaStatus::Zero,
beta_status == BetaStatus::One,
a,
a_cs,
b,
b_rs,
c,
rsc,
csc,
mr_eff,
nr_eff,
scratch,
)
}
}
#[allow(clippy::too_many_arguments)]
#[inline(always)]
unsafe fn microkernel_epi<S, E, const MR_REG: usize, const NR: usize>(
simd: S,
kc: usize,
alpha: T,
beta: T,
alpha_status: AlphaStatus,
beta_status: BetaStatus,
a: *const T,
a_cs: isize,
b: *const T,
b_rs: isize,
b_cs: isize,
c: *mut T,
rsc: isize,
csc: isize,
mr_eff: usize,
nr_eff: usize,
row0: usize,
col0: usize,
last_k: bool,
epi: &E,
scratch: *mut T,
) where
S: KernelSimd<T, T, T, T>,
E: Epilogue<Self>,
{
unsafe {
Self::microkernel::<S, MR_REG, NR>(
simd,
kc,
alpha,
beta,
alpha_status,
beta_status,
a,
a_cs,
b,
b_rs,
b_cs,
c,
rsc,
csc,
mr_eff,
nr_eff,
scratch,
);
if !E::IS_IDENTITY && last_k {
for j in 0..nr_eff {
for i in 0..mr_eff {
let cp = c.offset(i as isize * rsc + j as isize * csc);
*cp = epi.apply(*cp, row0 + i, col0 + j);
}
}
}
}
}
}