use crate::avx::AvxDct2Butterfly7;
use crate::avx::storef::AvxStoreF;
use crate::avx::type2::mixed_radix3f::{
dct2_radix_n_cos_twiddles_avx_f, dct2_radix_n_rotation_twiddles_avx_f,
};
use crate::avx::util::fma;
use crate::bidirectional::{BidirectionalStore, InPlaceStore};
use crate::type2::MixedRadix7Sample;
use crate::util::DctSample;
use crate::{PxdctError, PxdctExecutor};
use num_traits::{One, Zero};
pub(crate) struct AvxDct2Butterfly49f {
rotation_layer: [AvxStoreF; 6],
cos_twiddles: [AvxStoreF; 6],
bf7: AvxDct2Butterfly7<f32>,
}
impl Default for AvxDct2Butterfly49f {
fn default() -> Self {
AvxDct2Butterfly49f::new()
}
}
impl AvxDct2Butterfly49f {
pub(crate) fn new() -> AvxDct2Butterfly49f {
let rotation_layer = unsafe { dct2_radix_n_rotation_twiddles_avx_f(7, 7, 49) };
let cos_twiddles = unsafe { dct2_radix_n_cos_twiddles_avx_f(7, 7, 49) };
AvxDct2Butterfly49f {
rotation_layer: rotation_layer.try_into().unwrap(),
bf7: AvxDct2Butterfly7::default(),
cos_twiddles: cos_twiddles.try_into().unwrap(),
}
}
}
impl AvxDct2Butterfly49f {
#[inline(always)]
pub(crate) fn exec<S: BidirectionalStore<f32>>(
&self,
data: &mut S,
a_buffer: &mut [f32; 7],
c_buffer: &mut [f32; 21],
s_buffer: &mut [f32; 21],
) {
for n in 0..7 {
a_buffer[n] = data[n * 7 + 3];
}
self.bf7.exec(&mut InPlaceStore::new(a_buffer));
let q_modules = 7;
for m in 0..3 {
let mut sign = f32::one();
for n in 0..7 {
let u0 = data[7 * n + m];
let u1 = data[7 * n + 7 - m - 1];
c_buffer[m * 7 + n] = u0 + u1;
s_buffer[m * 7 + n] = (u0 - u1).mulsign(sign);
sign = -sign;
}
self.bf7
.exec(&mut InPlaceStore::new(&mut c_buffer[m * 7..(m + 1) * 7]));
self.bf7
.exec(&mut InPlaceStore::new(&mut s_buffer[m * 7..(m + 1) * 7]));
}
{
let qc = c_buffer[0];
let mut c0 = qc; let mut c1 = qc * f32::R7_COS_EVEN2_M0; let mut c2 = qc * f32::R7_COS_EVEN2_M2; let mut c3 = qc * f32::R7_COS_EVEN2_M1;
let s0_twiddled = s_buffer[0];
let mut s0 = s0_twiddled * f32::R7_SIN_ODD0_M0;
let mut s1 = s0_twiddled * f32::R7_SIN_ODD1_M0;
let mut s2 = s0_twiddled * f32::R7_SIN_ODD2_M0;
{
let ci = c_buffer[q_modules];
let si = s_buffer[q_modules];
let ci2 = c_buffer[q_modules * 2];
let si2 = s_buffer[q_modules * 2];
c0 = ci + c0 + ci2;
c1 = fma(ci, f32::R7_COS_EVEN2_M1, c1);
c1 = fma(ci2, f32::R7_COS_EVEN2_M2, c1);
c2 = fma(ci, f32::R7_COS_EVEN2_M0, c2);
c2 = fma(ci2, f32::R7_COS_EVEN2_M1, c2);
c3 = fma(ci, f32::R7_COS_EVEN2_M2, c3);
c3 = fma(ci2, f32::R7_COS_EVEN2_M0, c3);
s0 = fma(si, f32::R7_SIN_ODD0_M1, s0);
s0 = fma(si2, f32::R7_SIN_ODD0_M2, s0);
s1 = fma(si, f32::R7_SIN_ODD1_M1, s1);
s1 = fma(si2, f32::R7_SIN_ODD1_M2, s1);
s2 = fma(si, f32::R7_SIN_ODD2_M1, s2);
s2 = fma(si2, f32::R7_SIN_ODD2_M2, s2);
}
let a0 = a_buffer[0];
let dc = c0 + a0;
data[0] = dc;
let dc2 = c2 + a0;
data[q_modules * 4] = dc2;
data[q_modules * 3] = -s1;
data[q_modules] = s0;
let qid2 = -(c1 + a0); data[q_modules * 2] = qid2;
let dc3 = c3 + a0;
data[q_modules * 6] = -dc3;
data[q_modules * 5] = s2;
unsafe {
let k = 1;
let uk = 0;
let rotation_twiddle_re = self.rotation_layer[uk];
let rotation_twiddle_im = self.rotation_layer[uk + 1];
let c_forward = AvxStoreF::load6(&c_buffer[k..]);
let s_forward = AvxStoreF::load6(&s_buffer[q_modules - k - 5..]).reverse6();
let rotated_dc = fma(s_forward, rotation_twiddle_re, c_forward);
let twiddle_re = self.cos_twiddles[uk];
let twiddle_im = self.cos_twiddles[uk + 1];
let twiddled_dc = rotated_dc * twiddle_re;
let mut dc0 = twiddled_dc;
let mut dc2 = twiddled_dc * f32::R7_COS_EVEN2_M0;
let mut dc4 = twiddled_dc * f32::R7_COS_EVEN2_M2;
let mut dc6 = twiddled_dc * f32::R7_COS_EVEN2_M1;
let rotated_ds = fma(c_forward, rotation_twiddle_im, s_forward);
let twiddled_ds = rotated_ds * twiddle_im;
let mut ds1 = twiddled_ds * f32::R7_SIN_ODD0_M0;
let mut ds3 = twiddled_ds * f32::R7_SIN_ODD1_M0;
let mut ds5 = twiddled_ds * f32::R7_SIN_ODD2_M0;
{
let c_forward = AvxStoreF::load6(&c_buffer[q_modules + k..]);
let s_forward = AvxStoreF::load6(&s_buffer[q_modules * 2 - k - 5..]).reverse6();
let rotation_twiddle_re = self.rotation_layer[uk + 2];
let rotation_twiddle_im = self.rotation_layer[uk + 3];
let twiddle_re = self.cos_twiddles[uk + 2];
let twiddle_im = self.cos_twiddles[uk + 3];
let rotated_dc1 = fma(s_forward, rotation_twiddle_re, c_forward);
let rotated_ds2 = fma(c_forward, rotation_twiddle_im, s_forward);
let twiddled_dc = twiddle_re * rotated_dc1;
let twiddled_ds = twiddle_im * rotated_ds2;
dc0 = twiddled_dc + dc0;
dc2 = AvxStoreF::f32_mul_add(f32::R7_COS_EVEN2_M1, twiddled_dc, dc2);
dc4 = AvxStoreF::f32_mul_add(f32::R7_COS_EVEN2_M0, twiddled_dc, dc4);
dc6 = AvxStoreF::f32_mul_add(f32::R7_COS_EVEN2_M2, twiddled_dc, dc6);
ds1 = AvxStoreF::f32_mul_add(f32::R7_SIN_ODD0_M1, twiddled_ds, ds1);
ds3 = AvxStoreF::f32_mul_add(f32::R7_SIN_ODD1_M1, twiddled_ds, ds3);
ds5 = AvxStoreF::f32_mul_add(f32::R7_SIN_ODD2_M1, twiddled_ds, ds5);
}
{
let c_forward = AvxStoreF::load6(&c_buffer[q_modules * 2 + k..]);
let s_forward = AvxStoreF::load6(&s_buffer[q_modules * 3 - k - 5..]).reverse6();
let rotation_twiddle_re = self.rotation_layer[uk + 4];
let rotation_twiddle_im = self.rotation_layer[uk + 5];
let twiddle_re = self.cos_twiddles[uk + 4];
let twiddle_im = self.cos_twiddles[uk + 5];
let rotated_dc1 = fma(s_forward, rotation_twiddle_re, c_forward);
let rotated_ds2 = fma(c_forward, rotation_twiddle_im, s_forward);
let twiddled_dc = twiddle_re * rotated_dc1;
let twiddled_ds = twiddle_im * rotated_ds2;
dc0 = twiddled_dc + dc0;
dc2 = AvxStoreF::f32_mul_add(f32::R7_COS_EVEN2_M2, twiddled_dc, dc2);
dc4 = AvxStoreF::f32_mul_add(f32::R7_COS_EVEN2_M1, twiddled_dc, dc4);
dc6 = AvxStoreF::f32_mul_add(f32::R7_COS_EVEN2_M0, twiddled_dc, dc6);
ds1 = AvxStoreF::f32_mul_add(f32::R7_SIN_ODD0_M2, twiddled_ds, ds1);
ds3 = AvxStoreF::f32_mul_add(f32::R7_SIN_ODD1_M2, twiddled_ds, ds3);
ds5 = AvxStoreF::f32_mul_add(f32::R7_SIN_ODD2_M2, twiddled_ds, ds5);
}
let a0 = AvxStoreF::load6(&a_buffer[k..]);
let dc = dc0 + a0;
dc.write6(data.slice_from_mut(k..));
let dss1 = AvxStoreF::f32_mul_add(2., ds1, -dc);
{
let q = dss1.reverse6();
q.write6(data.slice_from_mut(q_modules * 2 - k - 5..));
}
dc2 = -(dc2 + a0); dc2 = AvxStoreF::f32_mul_add(2., dc2, -dss1);
{
dc2.write6(data.slice_from_mut(q_modules * 2 + k..));
}
let dss3 = AvxStoreF::f32_mul_add(2., -ds3, -dc2);
{
let q = dss3.reverse6();
q.write6(data.slice_from_mut(q_modules * 4 - k - 5..));
}
dc4 += a0;
let mdc4 = AvxStoreF::f32_mul_add(2., dc4, -dss3);
{
mdc4.write6(data.slice_from_mut(q_modules * 4 + k..));
}
let dss5 = AvxStoreF::f32_mul_add(2., ds5, -mdc4);
{
let q = dss5.reverse6();
q.write6(data.slice_from_mut(q_modules * 6 - k - 5..));
}
dc6 += a0;
dc6 = AvxStoreF::f32_mul_add(2., -dc6, -dss5);
{
dc6.write6(data.slice_from_mut(q_modules * 6 + k..));
}
}
}
}
}
impl AvxDct2Butterfly49f {
#[target_feature(enable = "avx2", enable = "fma")]
fn execute_impl(&self, data: &mut [f32]) -> Result<(), PxdctError> {
if !data.len().is_multiple_of(49) {
return Err(PxdctError::InvalidSizeMultiplier(data.len(), self.length()));
}
let mut a_buffer = [f32::zero(); 7];
let mut c_buffer = [f32::zero(); 21];
let mut s_buffer = [f32::zero(); 21];
for chunk in data.as_chunks_mut::<49>().0.iter_mut() {
self.exec(
&mut InPlaceStore::new(chunk),
&mut a_buffer,
&mut c_buffer,
&mut s_buffer,
);
}
Ok(())
}
#[target_feature(enable = "avx2", enable = "fma")]
fn execute_into_impl(&self, input: &[f32], output: &mut [f32]) -> Result<(), PxdctError> {
use crate::util::validate_oof_sizes;
validate_oof_sizes!(input, output, 49);
let mut a_buffer = [f32::zero(); 7];
let mut c_buffer = [f32::zero(); 21];
let mut s_buffer = [f32::zero(); 21];
use crate::bidirectional::BiStore;
for (src, dst) in input
.as_chunks::<49>()
.0
.iter()
.zip(output.as_chunks_mut::<49>().0.iter_mut())
{
self.exec(
&mut BiStore::new(src, dst),
&mut a_buffer,
&mut c_buffer,
&mut s_buffer,
);
}
Ok(())
}
}
impl PxdctExecutor<f32> for AvxDct2Butterfly49f {
fn execute(&self, data: &mut [f32]) -> Result<(), PxdctError> {
unsafe { self.execute_impl(data) }
}
fn execute_with_scratch(&self, data: &mut [f32], _: &mut [f32]) -> Result<(), PxdctError> {
unsafe { self.execute_impl(data) }
}
fn execute_into(&self, input: &[f32], output: &mut [f32]) -> Result<(), PxdctError> {
unsafe { self.execute_into_impl(input, output) }
}
fn execute_into_with_scratch(
&self,
input: &[f32],
output: &mut [f32],
_: &mut [f32],
) -> Result<(), PxdctError> {
unsafe { self.execute_into_impl(input, output) }
}
fn length(&self) -> usize {
49
}
fn scratch_size(&self) -> usize {
0
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::PxdctExecutor;
use crate::avx::dct2_bf_power2::gen_test_avx_butterfly;
use crate::tests::naive_dct2_f32;
gen_test_avx_butterfly!(test_bf49_f32, AvxDct2Butterfly49f, 49, 1e-3, naive_dct2_f32);
}