#[cfg(all(feature = "simd", target_arch = "x86_64"))]
mod x86_64;
#[cfg(all(feature = "simd", target_arch = "aarch64"))]
mod aarch64;
pub mod generic;
#[inline]
pub fn dot(a: &[f32], b: &[f32]) -> f32 {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
{
x86_64::dot(a, b)
}
#[cfg(all(feature = "simd", target_arch = "aarch64"))]
{
aarch64::dot(a, b)
}
#[cfg(not(all(feature = "simd", any(target_arch = "x86_64", target_arch = "aarch64"))))]
{
generic::ops::dot::<generic::ScalarFloat>(a, b)
}
}
#[inline]
pub fn mul_elementwise(dst: &mut [f32], a: &[f32], b: &[f32]) {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
{
x86_64::mul_elementwise(dst, a, b)
}
#[cfg(all(feature = "simd", target_arch = "aarch64"))]
{
aarch64::mul_elementwise(dst, a, b)
}
#[cfg(not(all(feature = "simd", any(target_arch = "x86_64", target_arch = "aarch64"))))]
{
generic::ops::mul_elementwise::<generic::ScalarFloat>(dst, a, b)
}
}
#[inline]
pub fn mix_scalar(dst: &mut [f32], dry: &[f32], wet: &[f32], mix: f32) {
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
{
x86_64::mix_scalar(dst, dry, wet, mix)
}
#[cfg(all(feature = "simd", target_arch = "aarch64"))]
{
aarch64::mix_scalar(dst, dry, wet, mix)
}
#[cfg(not(all(feature = "simd", any(target_arch = "x86_64", target_arch = "aarch64"))))]
{
generic::ops::mix_scalar::<generic::ScalarFloat>(dst, dry, wet, mix)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn scalar_dot(a: &[f32], b: &[f32]) -> f32 {
generic::ops::dot::<generic::ScalarFloat>(a, b)
}
fn scalar_mul_elementwise(dst: &mut [f32], a: &[f32], b: &[f32]) {
generic::ops::mul_elementwise::<generic::ScalarFloat>(dst, a, b)
}
fn scalar_mix_scalar(dst: &mut [f32], dry: &[f32], wet: &[f32], mix: f32) {
generic::ops::mix_scalar::<generic::ScalarFloat>(dst, dry, wet, mix)
}
#[test]
fn dot_matches_scalar() {
let a: Vec<f32> = (0..37).map(|i| i as f32 * 0.5).collect();
let b: Vec<f32> = (0..37).map(|i| (i as f32 * 0.3).sin()).collect();
let expected = scalar_dot(&a, &b);
assert!((dot(&a, &b) - expected).abs() < 1e-4);
}
#[test]
fn mul_elementwise_matches_scalar() {
let a: Vec<f32> = (0..23).map(|i| i as f32).collect();
let b: Vec<f32> = (0..23).map(|i| 1.0 / (i as f32 + 1.0)).collect();
let mut dst = vec![0.0f32; 23];
let mut expected = vec![0.0f32; 23];
mul_elementwise(&mut dst, &a, &b);
scalar_mul_elementwise(&mut expected, &a, &b);
for i in 0..23 {
assert!((dst[i] - expected[i]).abs() < 1e-6);
}
}
#[test]
fn mix_scalar_matches_manual() {
let dry: Vec<f32> = (0..19).map(|i| i as f32).collect();
let wet: Vec<f32> = (0..19).map(|i| -(i as f32)).collect();
let mut dst = vec![0.0f32; 19];
let mut expected = vec![0.0f32; 19];
mix_scalar(&mut dst, &dry, &wet, 0.25);
scalar_mix_scalar(&mut expected, &dry, &wet, 0.25);
for i in 0..19 {
let expected = expected[i];
assert!((dst[i] - expected).abs() < 1e-6);
}
}
}