#[cfg(target_arch = "aarch64")]
use core::arch::aarch64::*;
#[cfg(target_arch = "aarch64")]
use crate::ntt::ZETAS;
use crate::params::N;
#[cfg(target_arch = "aarch64")]
use crate::params::QINV;
#[cfg(any(target_arch = "aarch64", test))]
use crate::params::Q;
#[cfg(target_arch = "aarch64")]
#[inline]
unsafe fn montgomery_mul_neon(zeta: int32x4_t, y: int32x4_t) -> int32x4_t {
let q_v = vdupq_n_s32(Q);
let qinv_v = vdupq_n_s32(QINV as i32);
let zeta_lo = vget_low_s32(zeta);
let zeta_hi = vget_high_s32(zeta);
let y_lo = vget_low_s32(y);
let y_hi = vget_high_s32(y);
let a_lo = vmull_s32(zeta_lo, y_lo); let a_hi = vmull_s32(zeta_hi, y_hi);
let a_lo32_lo = vmovn_s64(a_lo); let a_lo32_hi = vmovn_s64(a_hi);
let qinv_lo = vget_low_s32(qinv_v);
let qinv_hi = vget_high_s32(qinv_v);
let t_lo = vmul_s32(a_lo32_lo, qinv_lo);
let t_hi = vmul_s32(a_lo32_hi, qinv_hi);
let q_lo = vget_low_s32(q_v);
let q_hi = vget_high_s32(q_v);
let tq_lo = vmull_s32(t_lo, q_lo);
let tq_hi = vmull_s32(t_hi, q_hi);
let r_lo = vshrn_n_s64::<32>(vsubq_s64(a_lo, tq_lo));
let r_hi = vshrn_n_s64::<32>(vsubq_s64(a_hi, tq_hi));
vcombine_s32(r_lo, r_hi)
}
#[cfg(target_arch = "aarch64")]
#[inline]
unsafe fn butterfly_neon(a: &mut [i32; N], j: usize, len: usize, zeta: i32) {
let zeta_v = vdupq_n_s32(zeta);
let x = vld1q_s32(a.as_ptr().add(j));
let y = vld1q_s32(a.as_ptr().add(j + len));
let t = montgomery_mul_neon(zeta_v, y);
vst1q_s32(a.as_mut_ptr().add(j), vaddq_s32(x, t));
vst1q_s32(a.as_mut_ptr().add(j + len), vsubq_s32(x, t));
}
#[cfg(target_arch = "aarch64")]
#[inline]
unsafe fn inv_butterfly_neon(a: &mut [i32; N], j: usize, len: usize, zeta_neg: i32) {
let zeta_v = vdupq_n_s32(zeta_neg);
let x = vld1q_s32(a.as_ptr().add(j));
let y = vld1q_s32(a.as_ptr().add(j + len));
let sum = vaddq_s32(x, y);
let diff = vsubq_s32(x, y);
let reduced = montgomery_mul_neon(zeta_v, diff);
vst1q_s32(a.as_mut_ptr().add(j), sum);
vst1q_s32(a.as_mut_ptr().add(j + len), reduced);
}
#[cfg(target_arch = "aarch64")]
pub unsafe fn ntt_neon(a: &mut [i32; N]) {
let mut k: usize = 0;
let mut len = 128;
while len > 0 {
let mut start = 0;
while start < N {
k += 1;
let zeta = ZETAS[k];
if len >= 4 {
let mut j = start;
while j + 4 <= start + len {
butterfly_neon(a, j, len, zeta);
j += 4;
}
} else {
for j in start..start + len {
let t = crate::reduce::montgomery_reduce(zeta as i64 * a[j + len] as i64);
a[j + len] = a[j] - t;
a[j] += t;
}
}
start += 2 * len;
}
len >>= 1;
}
}
#[cfg(target_arch = "aarch64")]
pub unsafe fn invntt_neon(a: &mut [i32; N]) {
let f: i32 = 41978;
let mut k: usize = 256;
let mut len = 1;
while len < N {
let mut start = 0;
while start < N {
k -= 1;
let zeta = -ZETAS[k];
if len >= 4 {
let mut j = start;
while j + 4 <= start + len {
inv_butterfly_neon(a, j, len, zeta);
j += 4;
}
} else {
for j in start..start + len {
let t = a[j];
a[j] = t + a[j + len];
a[j + len] = t - a[j + len];
a[j + len] = crate::reduce::montgomery_reduce(zeta as i64 * a[j + len] as i64);
}
}
start += 2 * len;
}
len <<= 1;
}
let f_v = vdupq_n_s32(f);
let mut j = 0;
while j + 4 <= N {
let v = vld1q_s32(a.as_ptr().add(j));
let scaled = montgomery_mul_neon(f_v, v);
vst1q_s32(a.as_mut_ptr().add(j), scaled);
j += 4;
}
}
pub fn ntt_simd(a: &mut [i32; N]) {
#[cfg(target_arch = "aarch64")]
{
unsafe {
ntt_neon(a);
}
return;
}
#[allow(unreachable_code)]
{
crate::ntt::ntt(a);
}
}
pub fn invntt_simd(a: &mut [i32; N]) {
#[cfg(target_arch = "aarch64")]
{
unsafe {
invntt_neon(a);
}
return;
}
#[allow(unreachable_code)]
{
crate::ntt::invntt_tomont(a);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ntt_neon_matches_scalar() {
let mut a_scalar = [0i32; N];
let mut a_simd = [0i32; N];
for i in 0..N {
let v = (i as i32 * 37 + 11) % Q;
a_scalar[i] = v;
a_simd[i] = v;
}
crate::ntt::ntt(&mut a_scalar);
ntt_simd(&mut a_simd);
assert_eq!(a_scalar, a_simd, "NEON NTT diverged from scalar");
}
#[test]
fn test_invntt_neon_matches_scalar() {
let mut a_scalar = [0i32; N];
let mut a_simd = [0i32; N];
for i in 0..N {
let v = (i as i32 * 37 + 11) % Q;
a_scalar[i] = v;
a_simd[i] = v;
}
crate::ntt::ntt(&mut a_scalar);
crate::ntt::ntt(&mut a_simd);
crate::ntt::invntt_tomont(&mut a_scalar);
invntt_simd(&mut a_simd);
assert_eq!(a_scalar, a_simd, "NEON INVNTT diverged from scalar");
}
}