#[inline]
pub fn dot_q15_i32(a: &[i16], b: &[i16]) -> i32 {
debug_assert_eq!(a.len(), b.len(), "dot_q15_i32: length mismatch");
#[cfg(all(feature = "fft-extern", not(feature = "fft-rustfft")))]
{
unsafe extern "Rust" {
fn mfsk_core_dot_q15_i32(a: *const i16, b: *const i16, n: usize) -> i32;
}
return unsafe { mfsk_core_dot_q15_i32(a.as_ptr(), b.as_ptr(), a.len()) };
}
#[cfg(not(all(feature = "fft-extern", not(feature = "fft-rustfft"))))]
{
let mut acc: i32 = 0;
for (&x, &y) in a.iter().zip(b.iter()) {
acc += ((x as i32) * (y as i32)) >> 15;
}
acc
}
}
#[cfg(all(test, feature = "fft-rustfft"))]
mod tests {
use super::*;
#[test]
fn dot_q15_zero() {
let a = [0i16; 16];
let b = [12345i16; 16];
assert_eq!(dot_q15_i32(&a, &b), 0);
}
#[test]
fn dot_q15_unit_basis() {
let a = [100i16; 100];
let b = [32767i16; 100];
let result = dot_q15_i32(&a, &b);
assert!((9900..=9999).contains(&result), "got {result}");
}
#[test]
fn dot_q15_anti_phase() {
let a: alloc::vec::Vec<i16> = (0..1000).map(|k| (k * 30).clamp(0, 30000) as i16).collect();
let neg_a: alloc::vec::Vec<i16> = a.iter().map(|&x| -x).collect();
let pos = dot_q15_i32(&a, &a);
let neg = dot_q15_i32(&a, &neg_a);
let n = a.len() as i32;
let drift = (pos + neg).abs();
assert!(drift <= n, "pos={pos} neg={neg} drift={drift} (max {n})");
assert!(pos > 0);
}
}