#![allow(dead_code)]
use crate::celt::math::celt_ilog2;
pub(crate) fn celt_maxabs16(samples: &[i16]) -> i32 {
let mut max_abs = 0i32;
for &sample in samples {
let abs = i32::from(sample).abs();
if abs > max_abs {
max_abs = abs;
}
}
max_abs
}
pub(crate) fn celt_maxabs32(samples: &[i32]) -> i32 {
let mut max_abs = 0i32;
for &sample in samples {
let abs = sample.abs();
if abs > max_abs {
max_abs = abs;
}
}
max_abs
}
fn vshr32(a: i32, shift: i32) -> i32 {
match shift.cmp(&0) {
core::cmp::Ordering::Greater => a >> shift,
core::cmp::Ordering::Less => a.wrapping_shl((-shift) as u32),
core::cmp::Ordering::Equal => a,
}
}
fn pshr32(a: i32, shift: u32) -> i32 {
if shift == 0 {
a
} else {
let bias = 1i64 << (shift - 1);
((i64::from(a) + bias) >> shift) as i32
}
}
fn round16(value: i32, bits: u32) -> i16 {
pshr32(value, bits) as i16
}
fn mult16_16(a: i16, b: i16) -> i32 {
i32::from(a) * i32::from(b)
}
fn mult16_16_q15(a: i16, b: i16) -> i16 {
(mult16_16(a, b) >> 15) as i16
}
fn mult16_16_p15(a: i16, b: i16) -> i16 {
((mult16_16(a, b) + 16_384) >> 15) as i16
}
fn mult16_32_q15(a: i16, b: i32) -> i32 {
((i64::from(a) * i64::from(b)) >> 15) as i32
}
fn mult32_32_q31(a: i32, b: i32) -> i32 {
((i64::from(a) * i64::from(b)) >> 31) as i32
}
fn shl32(value: i32, shift: u32) -> i32 {
value.wrapping_shl(shift)
}
fn shl16(value: i16, shift: u32) -> i16 {
i32::from(value).wrapping_shl(shift) as i16
}
fn add16(a: i16, b: i16) -> i16 {
a.wrapping_add(b)
}
fn sub16(a: i16, b: i16) -> i16 {
a.wrapping_sub(b)
}
fn add32(a: i32, b: i32) -> i32 {
a.wrapping_add(b)
}
fn sub32(a: i32, b: i32) -> i32 {
a.wrapping_sub(b)
}
fn min16(a: i16, b: i16) -> i16 {
if a < b { a } else { b }
}
pub(crate) fn celt_rsqrt_norm(x: i32) -> i16 {
let n = (x - 32_768) as i16; let r = add16(
23_557,
mult16_16_q15(n, add16(-13_490, mult16_16_q15(n, 6_713))),
);
let r2 = mult16_16_q15(r, r);
let y = shl16(sub16(add16(mult16_16_q15(r2, n), r2), 16_384), 1);
add16(
r,
mult16_16_q15(r, mult16_16_q15(y, sub16(mult16_16_q15(y, 12_288), 16_384))),
)
}
pub(crate) fn celt_sqrt(mut x: i32) -> i32 {
if x == 0 {
return 0;
}
if x >= 1_073_741_824 {
return 32_767;
}
let k = (celt_ilog2(x) >> 1) - 7;
x = vshr32(x, 2 * k);
let n = (x - 32_768) as i16;
let coeffs = [23_171, 11_574, -2_901, 1_592, -1_002, 336];
let mut acc = coeffs[5];
acc = add16(coeffs[4], mult16_16_q15(n, acc));
acc = add16(coeffs[3], mult16_16_q15(n, acc));
acc = add16(coeffs[2], mult16_16_q15(n, acc));
acc = add16(coeffs[1], mult16_16_q15(n, acc));
let result = add32(i32::from(coeffs[0]), i32::from(mult16_16_q15(n, acc)));
vshr32(result, 7 - k)
}
fn celt_cos_pi_2(x: i16) -> i16 {
let x2 = mult16_16_p15(x, x);
let inner = add32(8_277, i32::from(mult16_16_p15(-626, x2)));
let mid = mult16_16_p15(x2, inner as i16);
let outer = add32(-7_651, i32::from(mid));
let poly = mult16_16_p15(x2, outer as i16);
let acc = add32(i32::from(sub16(32_767, x2)), i32::from(poly));
let clipped = if acc > 32_766 { 32_766 } else { acc };
add16(1, clipped as i16)
}
pub(crate) fn celt_cos_norm(mut x: i32) -> i16 {
x &= 0x0001_FFFF;
if x > (1 << 16) {
x = (1 << 17) - x;
}
if x & 0x0000_7FFF != 0 {
if x < (1 << 15) {
celt_cos_pi_2(x as i16)
} else {
-celt_cos_pi_2((65_536 - x) as i16)
}
} else if x & 0x0000_FFFF != 0 {
0
} else if x & 0x0001_FFFF != 0 {
-32_767
} else {
32_767
}
}
pub(crate) fn celt_rcp(x: i32) -> i32 {
debug_assert!(x > 0);
let i = celt_ilog2(x);
let n = (vshr32(x, i - 15) - 32_768) as i16;
let mut r = add16(30_840, mult16_16_q15(-15_420, n));
let term = add16(mult16_16_q15(r, n), add16(r, -32_768i16));
r = sub16(r, mult16_16_q15(r, term));
let term = add16(mult16_16_q15(r, n), add16(r, -32_768i16));
r = sub16(r, add16(1, mult16_16_q15(r, term)));
vshr32(i32::from(r), i - 16)
}
pub(crate) fn frac_div32_q29(a: i32, b: i32) -> i32 {
debug_assert!(b != 0);
let shift = celt_ilog2(b) - 29;
let a = vshr32(a, shift);
let b = vshr32(b, shift);
let rcp = round16(celt_rcp(i32::from(round16(b, 16))), 3);
let mut result = mult16_32_q15(rcp, a);
let rem = pshr32(a, 2) - mult32_32_q31(result, b);
result = add32(result, shl32(mult16_32_q15(rcp, rem), 2));
result
}
pub(crate) fn frac_div32(a: i32, b: i32) -> i32 {
let result = frac_div32_q29(a, b);
if result >= 536_870_912 {
2_147_483_647
} else if result <= -536_870_912 {
-2_147_483_647
} else {
shl32(result, 2)
}
}
#[cfg(test)]
mod tests {
use super::{
celt_cos_norm, celt_maxabs16, celt_maxabs32, celt_rcp, celt_rsqrt_norm, celt_sqrt,
frac_div32, frac_div32_q29, vshr32,
};
#[test]
fn vshr32_matches_expected_behaviour() {
assert_eq!(vshr32(1 << 16, 1), 1 << 15);
assert_eq!(vshr32(1 << 10, -2), 1 << 12);
assert_eq!(vshr32(-1 << 10, 2), -1 << 8);
}
#[test]
fn frac_division_maintains_scaling() {
let num = 1 << 20;
let den = 1 << 15;
let q29 = frac_div32_q29(num, den);
let q = frac_div32(num, den);
assert_eq!(q, q29 << 2);
}
#[test]
fn reciprocal_returns_positive_values() {
for x in (1 << 15)..(1 << 18) {
assert!(celt_rcp(x) > 0);
}
}
#[test]
fn rsqrt_norm_stays_positive() {
for value in (1 << 15)..(1 << 16) {
let r = celt_rsqrt_norm(value);
assert!(r > 0);
}
}
#[test]
fn cos_norm_returns_bounded_values() {
assert_eq!(celt_cos_norm(0), 32_767);
for raw in (0..=1 << 16).step_by(1 << 12) {
let value = i32::from(celt_cos_norm(raw));
assert!((-32_767..=32_767).contains(&value));
}
}
#[test]
fn sqrt_monotonic() {
let mut prev = celt_sqrt(1 << 16);
for x in ((1 << 16) + 1)..((1 << 16) + 1_000) {
let current = celt_sqrt(x);
assert!(current >= prev);
prev = current;
}
}
#[test]
fn maxabs_helpers_match_expected_values() {
let samples_16 = [0i16, -12, 9, 32, -31];
let samples_32 = [0i32, -12_000, 9_000, 32_000, -31_999];
assert_eq!(celt_maxabs16(&samples_16), 32);
assert_eq!(celt_maxabs32(&samples_32), 32_000);
}
}