use core::arch::aarch64::*;
use num_complex::Complex;
use std::ops::{Deref, DerefMut};
use crate::array_utils::DoubleBuf;
macro_rules! read_complex_to_array {
($input:ident, { $($idx:literal),* }) => {
[
$(
$input.load_complex($idx),
)*
]
}
}
macro_rules! read_partial1_complex_to_array {
($input:ident, { $($idx:literal),* }) => {
[
$(
$input.load1_complex($idx),
)*
]
}
}
macro_rules! write_complex_to_array {
($input:ident, $output:ident, { $($idx:literal),* }) => {
$(
$output.store_complex($input[$idx], $idx);
)*
}
}
macro_rules! write_partial_lo_complex_to_array {
($input:ident, $output:ident, { $($idx:literal),* }) => {
$(
$output.store_partial_lo_complex($input[$idx], $idx);
)*
}
}
macro_rules! write_complex_to_array_strided {
($input:ident, $output:ident, $stride:literal, { $($idx:literal),* }) => {
$(
$output.store_complex($input[$idx], $idx*$stride);
)*
}
}
pub trait NeonNum {
type VectorType;
const COMPLEX_PER_VECTOR: usize;
}
impl NeonNum for f32 {
type VectorType = float32x4_t;
const COMPLEX_PER_VECTOR: usize = 2;
}
impl NeonNum for f64 {
type VectorType = float64x2_t;
const COMPLEX_PER_VECTOR: usize = 1;
}
pub trait NeonArray<T: NeonNum>: Deref {
unsafe fn load_complex(&self, index: usize) -> T::VectorType;
unsafe fn load_partial1_complex(&self, index: usize) -> T::VectorType;
unsafe fn load1_complex(&self, index: usize) -> T::VectorType;
}
impl NeonArray<f32> for &[Complex<f32>] {
#[inline(always)]
unsafe fn load_complex(&self, index: usize) -> <f32 as NeonNum>::VectorType {
debug_assert!(self.len() >= index + <f32 as NeonNum>::COMPLEX_PER_VECTOR);
vld1q_f32(self.as_ptr().add(index) as *const f32)
}
#[inline(always)]
unsafe fn load_partial1_complex(&self, index: usize) -> <f32 as NeonNum>::VectorType {
debug_assert!(self.len() >= index + 1);
let temp = vmovq_n_f32(0.0);
vreinterpretq_f32_u64(vld1q_lane_u64::<0>(
self.as_ptr().add(index) as *const u64,
vreinterpretq_u64_f32(temp),
))
}
#[inline(always)]
unsafe fn load1_complex(&self, index: usize) -> <f32 as NeonNum>::VectorType {
debug_assert!(self.len() >= index + 1);
vreinterpretq_f32_u64(vld1q_dup_u64(self.as_ptr().add(index) as *const u64))
}
}
impl NeonArray<f32> for &mut [Complex<f32>] {
#[inline(always)]
unsafe fn load_complex(&self, index: usize) -> <f32 as NeonNum>::VectorType {
debug_assert!(self.len() >= index + <f32 as NeonNum>::COMPLEX_PER_VECTOR);
vld1q_f32(self.as_ptr().add(index) as *const f32)
}
#[inline(always)]
unsafe fn load_partial1_complex(&self, index: usize) -> <f32 as NeonNum>::VectorType {
debug_assert!(self.len() >= index + 1);
let temp = vmovq_n_f32(0.0);
vreinterpretq_f32_u64(vld1q_lane_u64::<0>(
self.as_ptr().add(index) as *const u64,
vreinterpretq_u64_f32(temp),
))
}
#[inline(always)]
unsafe fn load1_complex(&self, index: usize) -> <f32 as NeonNum>::VectorType {
debug_assert!(self.len() >= index + 1);
vreinterpretq_f32_u64(vld1q_dup_u64(self.as_ptr().add(index) as *const u64))
}
}
impl NeonArray<f64> for &[Complex<f64>] {
#[inline(always)]
unsafe fn load_complex(&self, index: usize) -> <f64 as NeonNum>::VectorType {
debug_assert!(self.len() >= index + <f64 as NeonNum>::COMPLEX_PER_VECTOR);
vld1q_f64(self.as_ptr().add(index) as *const f64)
}
#[inline(always)]
unsafe fn load_partial1_complex(&self, _index: usize) -> <f64 as NeonNum>::VectorType {
unimplemented!("Impossible to do a partial load of complex f64's");
}
#[inline(always)]
unsafe fn load1_complex(&self, _index: usize) -> <f64 as NeonNum>::VectorType {
unimplemented!("Impossible to do a partial load of complex f64's");
}
}
impl NeonArray<f64> for &mut [Complex<f64>] {
#[inline(always)]
unsafe fn load_complex(&self, index: usize) -> <f64 as NeonNum>::VectorType {
debug_assert!(self.len() >= index + <f64 as NeonNum>::COMPLEX_PER_VECTOR);
vld1q_f64(self.as_ptr().add(index) as *const f64)
}
#[inline(always)]
unsafe fn load_partial1_complex(&self, _index: usize) -> <f64 as NeonNum>::VectorType {
unimplemented!("Impossible to do a partial load of complex f64's");
}
#[inline(always)]
unsafe fn load1_complex(&self, _index: usize) -> <f64 as NeonNum>::VectorType {
unimplemented!("Impossible to do a partial load of complex f64's");
}
}
impl<'a, T: NeonNum> NeonArray<T> for DoubleBuf<'a, T>
where
&'a [Complex<T>]: NeonArray<T>,
{
#[inline(always)]
unsafe fn load_complex(&self, index: usize) -> T::VectorType {
self.input.load_complex(index)
}
#[inline(always)]
unsafe fn load_partial1_complex(&self, index: usize) -> T::VectorType {
self.input.load_partial1_complex(index)
}
#[inline(always)]
unsafe fn load1_complex(&self, index: usize) -> T::VectorType {
self.input.load1_complex(index)
}
}
pub trait NeonArrayMut<T: NeonNum>: NeonArray<T> + DerefMut {
unsafe fn store_complex(&mut self, vector: T::VectorType, index: usize);
unsafe fn store_partial_lo_complex(&mut self, vector: T::VectorType, index: usize);
unsafe fn store_partial_hi_complex(&mut self, vector: T::VectorType, index: usize);
}
impl NeonArrayMut<f32> for &mut [Complex<f32>] {
#[inline(always)]
unsafe fn store_complex(&mut self, vector: <f32 as NeonNum>::VectorType, index: usize) {
debug_assert!(self.len() >= index + <f32 as NeonNum>::COMPLEX_PER_VECTOR);
vst1q_f32(self.as_mut_ptr().add(index) as *mut f32, vector);
}
#[inline(always)]
unsafe fn store_partial_hi_complex(
&mut self,
vector: <f32 as NeonNum>::VectorType,
index: usize,
) {
debug_assert!(self.len() >= index + 1);
let high = vget_high_f32(vector);
vst1_f32(self.as_mut_ptr().add(index) as *mut f32, high);
}
#[inline(always)]
unsafe fn store_partial_lo_complex(
&mut self,
vector: <f32 as NeonNum>::VectorType,
index: usize,
) {
debug_assert!(self.len() >= index + 1);
let low = vget_low_f32(vector);
vst1_f32(self.as_mut_ptr().add(index) as *mut f32, low);
}
}
impl NeonArrayMut<f64> for &mut [Complex<f64>] {
#[inline(always)]
unsafe fn store_complex(&mut self, vector: <f64 as NeonNum>::VectorType, index: usize) {
debug_assert!(self.len() >= index + <f64 as NeonNum>::COMPLEX_PER_VECTOR);
vst1q_f64(self.as_mut_ptr().add(index) as *mut f64, vector);
}
#[inline(always)]
unsafe fn store_partial_hi_complex(
&mut self,
_vector: <f64 as NeonNum>::VectorType,
_index: usize,
) {
unimplemented!("Impossible to do a partial store of complex f64's");
}
#[inline(always)]
unsafe fn store_partial_lo_complex(
&mut self,
_vector: <f64 as NeonNum>::VectorType,
_index: usize,
) {
unimplemented!("Impossible to do a partial store of complex f64's");
}
}
impl<'a, T: NeonNum> NeonArrayMut<T> for DoubleBuf<'a, T>
where
Self: NeonArray<T>,
&'a mut [Complex<T>]: NeonArrayMut<T>,
{
#[inline(always)]
unsafe fn store_complex(&mut self, vector: T::VectorType, index: usize) {
self.output.store_complex(vector, index);
}
#[inline(always)]
unsafe fn store_partial_hi_complex(&mut self, vector: T::VectorType, index: usize) {
self.output.store_partial_hi_complex(vector, index);
}
#[inline(always)]
unsafe fn store_partial_lo_complex(&mut self, vector: T::VectorType, index: usize) {
self.output.store_partial_lo_complex(vector, index);
}
}
#[cfg(test)]
mod unit_tests {
use super::*;
use num_complex::Complex;
#[test]
fn test_load_f64() {
unsafe {
let val1: Complex<f64> = Complex::new(1.0, 2.0);
let val2: Complex<f64> = Complex::new(3.0, 4.0);
let val3: Complex<f64> = Complex::new(5.0, 6.0);
let val4: Complex<f64> = Complex::new(7.0, 8.0);
let values = vec![val1, val2, val3, val4];
let slice = values.as_slice();
let load1 = slice.load_complex(0);
let load2 = slice.load_complex(1);
let load3 = slice.load_complex(2);
let load4 = slice.load_complex(3);
assert_eq!(
val1,
std::mem::transmute::<float64x2_t, Complex<f64>>(load1)
);
assert_eq!(
val2,
std::mem::transmute::<float64x2_t, Complex<f64>>(load2)
);
assert_eq!(
val3,
std::mem::transmute::<float64x2_t, Complex<f64>>(load3)
);
assert_eq!(
val4,
std::mem::transmute::<float64x2_t, Complex<f64>>(load4)
);
}
}
#[test]
fn test_store_f64() {
unsafe {
let val1: Complex<f64> = Complex::new(1.0, 2.0);
let val2: Complex<f64> = Complex::new(3.0, 4.0);
let val3: Complex<f64> = Complex::new(5.0, 6.0);
let val4: Complex<f64> = Complex::new(7.0, 8.0);
let nbr1 = vld1q_f64(&val1 as *const _ as *const f64);
let nbr2 = vld1q_f64(&val2 as *const _ as *const f64);
let nbr3 = vld1q_f64(&val3 as *const _ as *const f64);
let nbr4 = vld1q_f64(&val4 as *const _ as *const f64);
let mut values: Vec<Complex<f64>> = vec![Complex::new(0.0, 0.0); 4];
let mut slice = values.as_mut_slice();
slice.store_complex(nbr1, 0);
slice.store_complex(nbr2, 1);
slice.store_complex(nbr3, 2);
slice.store_complex(nbr4, 3);
assert_eq!(val1, values[0]);
assert_eq!(val2, values[1]);
assert_eq!(val3, values[2]);
assert_eq!(val4, values[3]);
}
}
}