use super::*;
use crate::simd::{self, SimdIsa};
fn vector_isas() -> Vec<SimdIsa> {
simd::available()
.into_iter()
.filter(|&isa| isa != SimdIsa::Scalar)
.collect()
}
struct Random(u64);
impl Random {
fn next(&mut self) -> u64 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
self.0
}
fn signed(&mut self) -> f32 {
(self.next() >> 40) as f32 / (1_u64 << 23) as f32 - 1.0
}
fn mixed(&mut self, len: usize) -> Vec<f32> {
const EDGES: [f32; 12] = [
0.0,
-0.0,
1.0,
-1.0,
1.0e-40,
-1.0e-40,
f32::MIN_POSITIVE,
3.0e30,
-3.0e30,
1.000_000_1,
-1.000_000_1,
0.5,
];
(0..len)
.map(|_| {
let pick = self.next();
if pick % 4 == 0 {
EDGES[(pick >> 8) as usize % EDGES.len()]
} else {
self.signed() * 2.0
}
})
.collect()
}
}
fn assert_bits_eq(label: &str, isa: SimdIsa, actual: &[f32], expected: &[f32]) {
assert_eq!(actual.len(), expected.len());
for (i, (a, e)) in actual.iter().zip(expected).enumerate() {
assert_eq!(
a.to_bits(),
e.to_bits(),
"{label}: {} differs from scalar at {i}: {a:e} vs {e:e}",
isa.name()
);
}
}
const LENGTHS: [usize; 12] = [0, 1, 3, 4, 5, 7, 8, 9, 15, 17, 33, 1024];
#[test]
fn imdct_vector_arms_match_scalar_bit_for_bit() {
let mut random = Random(0x1234_5678_9abc_def1);
for n in [8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096] {
let inputs = [
(0..n).map(|_| random.signed()).collect::<Vec<_>>(),
random.mixed(n),
vec![0.0; n],
(0..n)
.map(|i| if i == n / 3 { 1.0 } else { -0.0 })
.collect(),
(0..n)
.map(|i| if i % 2 == 0 { 1.0e20 } else { -1.0e-20 })
.collect(),
];
for spec in &inputs {
let mut expected = vec![0.0; 2 * n];
Imdct::new(n).imdct_with(SimdIsa::Scalar, spec, &mut expected);
for isa in vector_isas() {
let mut actual = vec![f32::NAN; 2 * n];
Imdct::new(n).imdct_with(isa, spec, &mut actual);
assert_bits_eq(&format!("imdct n={n}"), isa, &actual, &expected);
}
}
}
}
#[test]
fn scalar_imdct_matches_the_definition() {
let mut random = Random(0x0bad_cafe_f00d_d00d);
for n in [8, 32, 256, 1024] {
let spec: Vec<f32> = (0..n).map(|_| random.signed()).collect();
let mut actual = vec![0.0; 2 * n];
Imdct::new(n).imdct_with(SimdIsa::Scalar, &spec, &mut actual);
let pi_2n = std::f64::consts::PI / (4 * n) as f64;
for (i, &value) in actual.iter().enumerate() {
let expected: f64 = spec
.iter()
.enumerate()
.map(|(j, &x)| {
f64::from(x) * (pi_2n * ((2 * i + 1 + n) * (2 * j + 1)) as f64).cos()
})
.sum();
let tolerance = 2.0e-6 * n as f64;
assert!(
(f64::from(value) - expected).abs() <= tolerance,
"n={n} sample {i}: {value} vs {expected}"
);
}
}
}
#[test]
fn scalar_imdct_matches_symphonias() {
let mut random = Random(0x5eed_5eed_5eed_5eed);
for n in [32, 128, 1024, 4096] {
let spec: Vec<f32> = (0..n).map(|_| random.signed()).collect();
let mut ours = vec![0.0; 2 * n];
let mut theirs = vec![0.0; 2 * n];
Imdct::new(n).imdct_with(SimdIsa::Scalar, &spec, &mut ours);
symphonia_core::dsp::mdct::Imdct::new(n).imdct(&spec, &mut theirs);
let peak = theirs.iter().fold(0.0_f32, |m, v| m.max(v.abs()));
for (i, (a, b)) in ours.iter().zip(&theirs).enumerate() {
assert!(
(a - b).abs() <= 1.0e-5 * peak,
"n={n} sample {i}: {a} vs Symphonia's {b}"
);
}
}
}
#[test]
fn overlap_add_vector_arms_match_scalar_bit_for_bit() {
let mut random = Random(0x0f0f_1e1e_2d2d_3c3c);
for len in LENGTHS {
let left = random.mixed(len);
let right = random.mixed(len);
let win: Vec<f32> = (0..len).map(|_| random.signed().abs()).collect();
let win_rev: Vec<f32> = win.iter().rev().copied().collect();
let mut expected = vec![0.0; len];
overlap_add_with(
SimdIsa::Scalar,
&mut expected,
&left,
&right,
&win,
&win_rev,
);
for isa in vector_isas() {
let mut actual = vec![f32::NAN; len];
overlap_add_with(isa, &mut actual, &left, &right, &win, &win_rev);
assert_bits_eq(&format!("overlap_add len={len}"), isa, &actual, &expected);
}
}
}
#[test]
fn clamp_vector_arms_match_scalar_bit_for_bit() {
let mut random = Random(0x7777_1111_3333_5555);
let specials = [
f32::NAN,
-f32::NAN,
f32::INFINITY,
f32::NEG_INFINITY,
0.0,
-0.0,
1.0,
-1.0,
1.000_000_1,
-1.000_000_1,
0.999_999_94,
-0.999_999_94,
1.0e-40,
f32::MAX,
f32::MIN,
];
for len in LENGTHS {
let mut input = random.mixed(len);
for (slot, &special) in input.iter_mut().zip(specials.iter().cycle()).step_by(3) {
*slot = special;
}
let mut expected = input.clone();
clamp_unit_with(SimdIsa::Scalar, &mut expected);
for isa in vector_isas() {
let mut actual = input.clone();
clamp_unit_with(isa, &mut actual);
assert_bits_eq(&format!("clamp len={len}"), isa, &actual, &expected);
}
}
}
#[test]
fn clamp_is_f32_clamp() {
let input = [2.0, -2.0, 0.25, -0.0, f32::INFINITY, f32::NEG_INFINITY];
let mut output = input;
clamp_unit_with(SimdIsa::Scalar, &mut output);
for (out, inp) in output.iter().zip(input) {
assert_eq!(out.to_bits(), inp.clamp(-1.0, 1.0).to_bits());
}
let mut nan = [f32::NAN];
clamp_unit_with(SimdIsa::Scalar, &mut nan);
assert!(nan[0].is_nan());
}
#[test]
fn coupling_vector_arms_match_scalar_bit_for_bit() {
let mut random = Random(0x2468_ace0_1357_9bdf);
for len in LENGTHS {
let magnitude = random.mixed(len);
let angle = random.mixed(len);
let mut expected = (magnitude.clone(), angle.clone());
inverse_coupling_with(SimdIsa::Scalar, &mut expected.0, &mut expected.1);
for isa in vector_isas() {
let mut actual = (magnitude.clone(), angle.clone());
inverse_coupling_with(isa, &mut actual.0, &mut actual.1);
let label = format!("coupling len={len}");
assert_bits_eq(&label, isa, &actual.0, &expected.0);
assert_bits_eq(&label, isa, &actual.1, &expected.1);
}
}
}
#[test]
fn coupling_follows_the_specification() {
let cases = [
((3.0, 1.0), (3.0, 2.0)),
((3.0, -1.0), (2.0, 3.0)),
((-3.0, 1.0), (-3.0, -2.0)),
((-3.0, -1.0), (-2.0, -3.0)),
((0.0, 1.0), (0.0, 1.0)),
((0.0, -1.0), (1.0, 0.0)),
((1.0, 0.0), (1.0, 1.0)),
];
for isa in simd::available() {
let mut magnitude: Vec<f32> = cases.iter().map(|((m, _), _)| *m).collect();
let mut angle: Vec<f32> = cases.iter().map(|((_, a), _)| *a).collect();
magnitude.resize(8, 0.0);
angle.resize(8, 0.0);
inverse_coupling_with(isa, &mut magnitude, &mut angle);
for (i, (_, (m, a))) in cases.iter().enumerate() {
assert_eq!(
(magnitude[i], angle[i]),
(*m, *a),
"{} case {i}",
isa.name()
);
}
}
}
#[test]
fn floor_product_vector_arms_match_scalar_bit_for_bit() {
let mut random = Random(0x1111_2222_3333_4444);
for len in LENGTHS {
let floor = random.mixed(len);
let residue = random.mixed(len);
let mut expected = floor.clone();
apply_floor_with(SimdIsa::Scalar, &mut expected, &residue);
for isa in vector_isas() {
let mut actual = floor.clone();
apply_floor_with(isa, &mut actual, &residue);
assert_bits_eq(&format!("floor len={len}"), isa, &actual, &expected);
}
}
}
#[test]
fn dispatchers_follow_the_override() {
let _guard = simd::test_lock();
let mut random = Random(0x9999_8888_7777_6666);
let spec: Vec<f32> = (0..256).map(|_| random.signed()).collect();
let mut expected = vec![0.0; 512];
Imdct::new(256).imdct_with(SimdIsa::Scalar, &spec, &mut expected);
for isa in simd::available() {
simd::set_override(Some(isa));
assert_eq!(active_isa(), isa);
let mut actual = vec![0.0; 512];
Imdct::new(256).imdct(&spec, &mut actual);
assert_bits_eq("imdct via override", isa, &actual, &expected);
}
simd::set_override(None);
assert_eq!(active_isa(), simd::detected());
}
#[test]
fn bench_stages_agree_across_instruction_sets() {
let _guard = simd::test_lock();
let mut inputs = bench::VorbisStageInputs::new();
simd::set_override(Some(SimdIsa::Scalar));
let expected = [
inputs.run_imdct(),
inputs.run_overlap_add(),
inputs.run_coupling(),
inputs.run_floor_product(),
];
for isa in vector_isas() {
simd::set_override(Some(isa));
let actual = [
inputs.run_imdct(),
inputs.run_overlap_add(),
inputs.run_coupling(),
inputs.run_floor_product(),
];
assert_eq!(actual, expected, "{}", isa.name());
}
simd::set_override(None);
}