use crate::hardware::{HardwareCapabilities, InstructionSet};
use wide::f32x8;
pub fn fwht(data: &mut [f32]) {
let n = data.len();
if !n.is_power_of_two() {
return; }
let caps = HardwareCapabilities::global();
match caps.instructions {
InstructionSet::Fallback => fwht_scalar(data),
_ => fwht_simd(data),
}
}
pub fn fwht_scalar(data: &mut [f32]) {
let n = data.len();
let mut h = 1;
while h < n {
for i in (0..n).step_by(h * 2) {
for j in i..i + h {
let x = data[j];
let y = data[j + h];
data[j] = x + y;
data[j + h] = x - y;
}
}
h *= 2;
}
let scale = 1.0 / (n as f32).sqrt();
for x in data.iter_mut() {
*x *= scale;
}
}
pub fn fwht_simd(data: &mut [f32]) {
let n = data.len();
let mut h = 1;
while h < 8 && h < n {
for i in (0..n).step_by(h * 2) {
for j in i..i + h {
let x = data[j];
let y = data[j + h];
data[j] = x + y;
data[j + h] = x - y;
}
}
h *= 2;
}
while h < n {
for i in (0..n).step_by(h * 2) {
for j in (i..i + h).step_by(8) {
if j + 8 <= i + h {
let x_slice = &data[j..j + 8];
let y_slice = &data[j + h..j + h + 8];
let x = f32x8::new([
x_slice[0], x_slice[1], x_slice[2], x_slice[3], x_slice[4], x_slice[5],
x_slice[6], x_slice[7],
]);
let y = f32x8::new([
y_slice[0], y_slice[1], y_slice[2], y_slice[3], y_slice[4], y_slice[5],
y_slice[6], y_slice[7],
]);
let new_x = x + y;
let new_y = x - y;
let arr_x: [f32; 8] = new_x.into();
let arr_y: [f32; 8] = new_y.into();
data[j..j + 8].copy_from_slice(&arr_x);
data[j + h..j + h + 8].copy_from_slice(&arr_y);
} else {
for k in j..i + h {
let x = data[k];
let y = data[k + h];
data[k] = x + y;
data[k + h] = x - y;
}
}
}
}
h *= 2;
}
let scale = 1.0 / (n as f32).sqrt();
let scale_v = f32x8::splat(scale);
let mut chunks = data.chunks_exact_mut(8);
for chunk in &mut chunks {
let x = f32x8::new([
chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5], chunk[6], chunk[7],
]);
let v = x * scale_v;
let arr: [f32; 8] = v.into();
chunk.copy_from_slice(&arr);
}
for x in chunks.into_remainder() {
*x *= scale;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fwht_scalar() {
let mut data = vec![1.0, 0.0, 1.0, 0.0];
fwht_scalar(&mut data);
let expected = [1.0, 1.0, 0.0, 0.0];
for (a, b) in data.iter().zip(expected.iter()) {
assert!((a - b).abs() < 1e-5);
}
}
#[test]
fn test_fwht_simd_vs_scalar() {
let mut d1 = vec![0.5f32; 1024];
for (i, item) in d1.iter_mut().enumerate() {
*item = (i as f32).sin();
}
let mut d2 = d1.clone();
fwht_scalar(&mut d1);
fwht_simd(&mut d2);
for (a, b) in d1.iter().zip(d2.iter()) {
assert!((a - b).abs() < 1e-4);
}
}
}