#[cfg(not(feature = "std"))]
use crate::compat::Math;
pub use crate::dnn::vec_portable::fma;
#[inline(always)]
pub fn fma64(a: f64, b: f64, c: f64) -> f64 {
crate::dnn::freq::fma(a, b, c)
}
#[inline(always)]
pub fn exp(x: f64) -> f64 {
x.exp()
}
#[inline(always)]
pub fn ln(x: f64) -> f64 {
x.ln()
}
#[inline(always)]
pub fn sin(x: f64) -> f64 {
x.sin()
}
#[inline(always)]
pub fn cos(x: f64) -> f64 {
x.cos()
}
#[inline(always)]
pub fn sqrt64(x: f64) -> f64 {
x.sqrt()
}
#[inline(always)]
pub fn sqrt32(x: f32) -> f32 {
x.sqrt()
}
#[cfg(feature = "osce-bwe")]
#[inline(always)]
pub fn floor64(x: f64) -> f64 {
x.floor()
}
#[inline(always)]
pub fn float2int(x: f32) -> i32 {
x.round_ties_even() as i32
}
#[allow(clippy::excessive_precision)]
#[inline]
pub fn celt_log2(x: f32) -> f32 {
const X_NORM: [f32; 8] = [
1.000000000000000000000000000,
8.88888895511627197265625e-01,
8.00000000000000000000000e-01,
7.27272748947143554687500e-01,
6.66666686534881591796875e-01,
6.15384638309478759765625e-01,
5.71428596973419189453125e-01,
5.33333361148834228515625e-01,
];
const Y_NORM: [f32; 8] = [
0.0000000000000000000000000000,
1.699250042438507080078125e-01,
3.219280838966369628906250e-01,
4.594316184520721435546875e-01,
5.849624872207641601562500e-01,
7.004396915435791015625000e-01,
8.073549270629882812500000e-01,
9.068905711174011230468750e-01,
];
const A0: f32 = 8.74628424644470214843750000e-02;
const A1: f32 = 1.357829570770263671875000000000;
const A2: f32 = -6.3897705078125000000000000e-01;
const A3: f32 = 4.01971250772476196289062500e-01;
const A4: f32 = -2.8415444493293762207031250e-01;
let bits = x.to_bits() as i32;
let integer = (bits >> 23) - 127;
let bits = bits.wrapping_sub(((integer as u32) << 23) as i32);
let range_idx = ((bits >> 20) & 0x7) as usize;
let m = fma(f32::from_bits(bits as u32), X_NORM[range_idx], -1.0625);
let p = fma(m, A4, A3);
let p = fma(m, p, A2);
let p = fma(m, p, A1);
let p = fma(m, p, A0);
integer as f32 + p + Y_NORM[range_idx]
}
#[inline]
pub fn celt_log(x: f32) -> f32 {
celt_log2(x) * core::f32::consts::LN_2
}
#[allow(clippy::excessive_precision)]
#[cfg(feature = "osce-bwe")]
#[inline]
pub fn celt_cos_norm2(x: f32) -> f32 {
const A0: f32 = 9.999999403953552246093750000000e-01;
const A2: f32 = -1.233698248863220214843750000000000;
const A4: f32 = 2.536507546901702880859375000000e-01;
const A6: f32 = -2.08106283098459243774414062500e-02;
const A8: f32 = 8.581906440667808055877685546875e-04;
let fl = floor64(0.25 * (x + 1.0) as f64);
let x = fma64(fl, -4.0, x as f64) as f32;
let above = x > 1.0;
let output_sign: f32 = if above { -1.0 } else { 1.0 };
let x = x - if above { 2.0 } else { 0.0 };
let x_norm_sq = x * x;
let p = fma(x_norm_sq, A8, A6);
let p = fma(x_norm_sq, p, A4);
let p = fma(x_norm_sq, p, A2);
let p = fma(x_norm_sq, p, A0);
p * output_sign
}
#[cfg(feature = "osce-bwe")]
#[inline]
pub fn celt_sin(x: f32) -> f32 {
celt_cos_norm2(fma64(x as f64, core::f64::consts::FRAC_PI_2, -1.0) as f32)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn float2int_rounds_ties_to_even() {
assert_eq!(float2int(0.5), 0);
assert_eq!(float2int(1.5), 2);
assert_eq!(float2int(-2.5), -2);
assert_eq!(float2int(-32767.0), -32767);
assert_eq!(float2int(f32::NAN), 0);
}
#[test]
fn celt_log2_is_close_to_log2() {
for &x in &[1e-6f32, 0.001, 0.5, 1.0, 1.5, 2.0, 3.7, 1000.0, 65535.0] {
let d = celt_log2(x) - x.log2();
assert!(d.abs() < 1e-4, "{x}: {d}");
}
}
#[cfg(feature = "osce-bwe")]
#[test]
fn celt_cos_norm2_is_close_to_cosine() {
for i in -400..400 {
let x = i as f32 / 100.0;
let want = (core::f64::consts::FRAC_PI_2 * x as f64).cos() as f32;
assert!((celt_cos_norm2(x) - want).abs() < 1e-5, "{x}");
}
}
#[cfg(feature = "osce-bwe")]
#[test]
fn celt_sin_is_the_libopus_formula() {
for i in 1..200 {
let y = i as f32 / 20.0 - 5.0;
let want = (core::f64::consts::FRAC_PI_2 * (core::f64::consts::FRAC_PI_2 * y as f64 - 1.0)).cos();
assert!((celt_sin(y) as f64 - want).abs() < 1e-5, "{y}");
}
}
}