use std::f32::consts::FRAC_1_SQRT_2;
pub fn mid_side_into(left: &[f32], right: &[f32], mid: &mut Vec<f32>, side: &mut Vec<f32>) {
let n = left.len().min(right.len());
mid.resize(n, 0.0);
side.resize(n, 0.0);
mid.truncate(n);
side.truncate(n);
for i in 0..n {
mid[i] = (left[i] + right[i]) * FRAC_1_SQRT_2;
side[i] = (left[i] - right[i]) * FRAC_1_SQRT_2;
}
}
#[must_use]
pub fn mid_side(left: &[f32], right: &[f32]) -> Vec<Vec<f32>> {
let (mut mid, mut side) = (Vec::new(), Vec::new());
mid_side_into(left, right, &mut mid, &mut side);
vec![mid, side]
}
#[must_use]
pub fn prefer_mid_side(left: &[f32], right: &[f32]) -> bool {
let mut mid_e = 0f64;
let mut side_e = 0f64;
let n = left.len().min(right.len());
for i in 0..n {
let m = f64::from(left[i] + right[i]);
let s = f64::from(left[i] - right[i]);
mid_e += m * m;
side_e += s * s;
}
side_e * 2.0 < mid_e
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mid_side_inverts_to_left_right() {
let l = [0.5f32, -0.3, 0.1, 0.8];
let r = [0.4f32, -0.2, -0.1, 0.7];
let ms = mid_side(&l, &r);
for i in 0..4 {
let dl = (ms[0][i] + ms[1][i]) * FRAC_1_SQRT_2;
let dr = (ms[0][i] - ms[1][i]) * FRAC_1_SQRT_2;
assert!((dl - l[i]).abs() < 1e-6 && (dr - r[i]).abs() < 1e-6);
}
}
#[test]
fn decision_tracks_correlation() {
let mono = vec![0.5f32; 1152];
assert!(prefer_mid_side(&mono, &mono));
let anti: Vec<f32> = mono.iter().map(|x| -x).collect();
assert!(!prefer_mid_side(&mono, &anti));
}
}