use crate::constants::MU;
use crate::math_utils::{portable_ln, portable_pow, round_half_away_from_zero};
pub fn mu_compress(value: f64) -> f64 {
let v = value.clamp(-1.0, 1.0);
v.signum() * portable_ln(1.0 + MU * v.abs()) / portable_ln(1.0 + MU)
}
pub fn mu_expand(compressed: f64) -> f64 {
compressed.signum() * (portable_pow(1.0 + MU, compressed.abs()) - 1.0) / MU
}
pub fn mu_law_quantize(value: f64, bits: u32) -> u32 {
let compressed = mu_compress(value);
let max_val = (1u32 << bits) - 1;
let index = round_half_away_from_zero((compressed + 1.0) / 2.0 * max_val as f64);
(index as i64).clamp(0, max_val as i64) as u32
}
pub fn mu_law_dequantize(index: u32, bits: u32) -> f64 {
let max_val = (1u32 << bits) - 1;
let compressed = index as f64 / max_val as f64 * 2.0 - 1.0;
mu_expand(compressed)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_extremes() {
for &v in &[-1.0, -0.5, 0.0, 0.5, 1.0] {
let c = mu_compress(v);
let rt = mu_expand(c);
assert!(
(rt - v).abs() < 1e-12,
"µ-law roundtrip failed at v={v}: got {rt}"
);
}
}
#[test]
fn compressed_range() {
assert!((mu_compress(1.0) - 1.0).abs() < 1e-12);
assert!((mu_compress(-1.0) + 1.0).abs() < 1e-12);
assert!((mu_compress(0.0)).abs() < 1e-12);
}
#[test]
fn quantize_dequantize_4bit() {
let mid = mu_law_quantize(0.0, 4);
let max_val = (1u32 << 4) - 1; assert_eq!(mid, 8, "midpoint for 4-bit should be 8");
assert_eq!(mu_law_quantize(-1.0, 4), 0);
assert_eq!(mu_law_quantize(1.0, 4), max_val);
}
#[test]
fn quantize_dequantize_5bit() {
let mid = mu_law_quantize(0.0, 5);
let max_val = (1u32 << 5) - 1; assert_eq!(mid, 16, "midpoint for 5-bit should be 16");
assert_eq!(mu_law_quantize(-1.0, 5), 0);
assert_eq!(mu_law_quantize(1.0, 5), max_val);
}
#[test]
fn quantize_roundtrip_preserves_sign() {
for bits in [4, 5, 6] {
for &v in &[-0.9, -0.5, -0.1, 0.1, 0.5, 0.9] {
let q = mu_law_quantize(v, bits);
let dq = mu_law_dequantize(q, bits);
if v > 0.0 {
assert!(dq >= 0.0, "sign should be preserved for v={v}");
} else {
assert!(dq <= 0.0, "sign should be preserved for v={v}");
}
}
}
}
}