#![allow(dead_code)]
use alloc::vec;
use core::convert::TryFrom;
use core::f32::consts::FRAC_2_PI;
use crate::celt::cwrs::{decode_pulses, encode_pulses};
use crate::celt::entcode::celt_udiv;
#[cfg(feature = "fixed_point")]
use crate::celt::entcode::ec_ilog;
use crate::celt::entdec::EcDec;
use crate::celt::entenc::EcEnc;
#[cfg(feature = "fixed_point")]
use crate::celt::fixed_arch::{EPSILON as FIXED_EPSILON, Q15_ONE as Q15_ONE_FIXED};
#[cfg(feature = "fixed_point")]
use crate::celt::fixed_ops::{
add32, extract16, mac16_16, mult16_16, mult16_16_q15, mult16_32_q16, mult32_32_q31, pshr32,
shr32, vshr32,
};
#[cfg(feature = "fixed_point")]
use crate::celt::math::celt_ilog2;
use crate::celt::math::{
celt_cos_norm, celt_div, celt_rcp, celt_rsqrt_norm, celt_sqrt, fast_atan2f,
};
#[cfg(feature = "fixed_point")]
use crate::celt::math_fixed::{
celt_cos_norm as celt_cos_norm_fixed, celt_rcp as celt_rcp_fixed,
celt_rsqrt_norm as celt_rsqrt_norm_fixed,
};
use crate::celt::pitch::celt_inner_prod;
#[cfg(feature = "fixed_point")]
use crate::celt::pitch::celt_inner_prod_fixed;
#[cfg(feature = "fixed_point")]
use crate::celt::types::{FixedOpusVal16, FixedOpusVal32};
use crate::celt::types::{OpusInt32, OpusVal16, OpusVal32};
use libm::floorf;
pub(crate) const SPREAD_NONE: i32 = 0;
pub(crate) const SPREAD_LIGHT: i32 = 1;
pub(crate) const SPREAD_NORMAL: i32 = 2;
pub(crate) const SPREAD_AGGRESSIVE: i32 = 3;
const SPREAD_FACTOR: [i32; 3] = [15, 10, 5];
const Q15_ONE: OpusVal16 = 1.0;
const EPSILON: OpusVal32 = 1e-15;
const MAX_PVQ_BAND_SIZE: usize = 208;
#[inline]
fn select_pvq_candidate_float(
best_id: &mut usize,
best_den: &mut OpusVal16,
best_num: &mut OpusVal16,
candidate_id: usize,
candidate_den: OpusVal16,
candidate_num: OpusVal16,
) {
if *best_den * candidate_num > candidate_den * *best_num {
*best_id = candidate_id;
*best_den = candidate_den;
*best_num = candidate_num;
}
}
#[cfg(feature = "fixed_point")]
#[inline]
fn select_pvq_candidate_fixed(
best_id: &mut usize,
best_den: &mut FixedOpusVal16,
best_num: &mut OpusInt32,
candidate_id: usize,
candidate_den: FixedOpusVal16,
candidate_num: FixedOpusVal16,
) {
let left = mult16_16(*best_den, candidate_num);
let right = i32::from(candidate_den).wrapping_mul(*best_num);
if left > right {
*best_id = candidate_id;
*best_den = candidate_den;
*best_num = i32::from(candidate_num);
}
}
#[cfg(feature = "fixed_point")]
#[inline]
fn add16(a: FixedOpusVal16, b: FixedOpusVal16) -> FixedOpusVal16 {
a.wrapping_add(b)
}
#[cfg(feature = "fixed_point")]
#[inline]
fn sub16(a: FixedOpusVal16, b: FixedOpusVal16) -> FixedOpusVal16 {
a.wrapping_sub(b)
}
#[cfg(feature = "fixed_point")]
#[inline]
fn neg16(a: FixedOpusVal16) -> FixedOpusVal16 {
a.wrapping_neg()
}
fn exp_rotation1(x: &mut [OpusVal16], stride: usize, c: OpusVal16, s: OpusVal16) {
let len = x.len();
if stride == 0 || len <= stride {
return;
}
let ms = -s;
for i in 0..(len - stride) {
let x1 = x[i];
let x2 = x[i + stride];
x[i + stride] = c * x2 + s * x1;
x[i] = c * x1 + ms * x2;
}
if len > 2 * stride {
let limit = len - 2 * stride - 1;
for i in (0..=limit).rev() {
let x1 = x[i];
let x2 = x[i + stride];
x[i + stride] = c * x2 + s * x1;
x[i] = c * x1 + ms * x2;
}
}
}
pub(crate) fn exp_rotation(
x: &mut [OpusVal16],
len: usize,
dir: i32,
stride: usize,
k: i32,
spread: i32,
) {
if len == 0 || stride == 0 {
return;
}
let slice_len = len.min(x.len());
let x = &mut x[..slice_len];
if 2 * k >= slice_len as i32 || spread == SPREAD_NONE {
return;
}
let spread_index = match spread {
SPREAD_LIGHT => 0,
SPREAD_NORMAL => 1,
SPREAD_AGGRESSIVE => 2,
_ => return,
};
let factor = SPREAD_FACTOR[spread_index];
let gain = celt_div(
Q15_ONE * slice_len as OpusVal16,
(slice_len + (factor * k) as usize) as OpusVal16,
);
let theta = 0.5 * gain * gain;
let c = celt_cos_norm(theta);
let s = celt_cos_norm(Q15_ONE - theta);
let mut stride2 = 0usize;
if slice_len >= 8 * stride {
stride2 = 1;
while (stride2 * stride2 + stride2) * stride + (stride >> 2) < slice_len {
stride2 += 1;
}
}
let len_div = slice_len / stride;
if len_div == 0 {
return;
}
for band in 0..stride {
let start = band * len_div;
let end = start + len_div;
let band_slice = &mut x[start..end];
if dir < 0 {
if stride2 > 0 {
exp_rotation1(band_slice, stride2, s, c);
}
exp_rotation1(band_slice, 1, c, s);
} else {
exp_rotation1(band_slice, 1, c, -s);
if stride2 > 0 {
exp_rotation1(band_slice, stride2, s, -c);
}
}
}
}
#[cfg(feature = "fixed_point")]
fn exp_rotation1_fixed(
x: &mut [FixedOpusVal16],
stride: usize,
c: FixedOpusVal16,
s: FixedOpusVal16,
) {
let len = x.len();
if stride == 0 || len <= stride {
return;
}
let ms = neg16(s);
for i in 0..(len - stride) {
let x1 = x[i];
let x2 = x[i + stride];
let acc1 = mac16_16(mult16_16(c, x2), s, x1);
let acc2 = mac16_16(mult16_16(c, x1), ms, x2);
x[i + stride] = extract16(pshr32(acc1, 15));
x[i] = extract16(pshr32(acc2, 15));
}
if len > 2 * stride {
let limit = len - 2 * stride - 1;
for i in (0..=limit).rev() {
let x1 = x[i];
let x2 = x[i + stride];
let acc1 = mac16_16(mult16_16(c, x2), s, x1);
let acc2 = mac16_16(mult16_16(c, x1), ms, x2);
x[i + stride] = extract16(pshr32(acc1, 15));
x[i] = extract16(pshr32(acc2, 15));
}
}
}
#[cfg(feature = "fixed_point")]
pub(crate) fn exp_rotation_fixed(
x: &mut [FixedOpusVal16],
len: usize,
dir: i32,
stride: usize,
k: i32,
spread: i32,
) {
if len == 0 || stride == 0 {
return;
}
let slice_len = len.min(x.len());
let x = &mut x[..slice_len];
if 2 * k >= slice_len as i32 || spread == SPREAD_NONE {
return;
}
let spread_index = match spread {
SPREAD_LIGHT => 0,
SPREAD_NORMAL => 1,
SPREAD_AGGRESSIVE => 2,
_ => return,
};
let factor = SPREAD_FACTOR[spread_index];
let denom = slice_len as i32 + factor * k;
let numer = mult16_16(Q15_ONE_FIXED, slice_len as i16);
let gain = extract16(mult32_32_q31(numer, celt_rcp_fixed(denom)));
let theta = (mult16_16_q15(gain, gain) >> 1) as i16;
let c = celt_cos_norm_fixed(i32::from(theta));
let s = celt_cos_norm_fixed(i32::from(sub16(Q15_ONE_FIXED, theta)));
let mut stride2 = 0usize;
if slice_len >= 8 * stride {
stride2 = 1;
while (stride2 * stride2 + stride2) * stride + (stride >> 2) < slice_len {
stride2 += 1;
}
}
let len_div = celt_udiv(slice_len as u32, stride as u32) as usize;
if len_div == 0 {
return;
}
for band in 0..stride {
let start = band * len_div;
let end = start + len_div;
let band_slice = &mut x[start..end];
if dir < 0 {
if stride2 > 0 {
exp_rotation1_fixed(band_slice, stride2, s, c);
}
exp_rotation1_fixed(band_slice, 1, c, s);
} else {
exp_rotation1_fixed(band_slice, 1, c, neg16(s));
if stride2 > 0 {
exp_rotation1_fixed(band_slice, stride2, s, neg16(c));
}
}
}
}
pub(crate) fn normalise_residual(
pulses: &[i32],
x: &mut [OpusVal16],
n: usize,
ryy: OpusVal32,
gain: OpusVal32,
) {
if n == 0 {
return;
}
debug_assert!(pulses.len() >= n, "pulse buffer shorter than band size");
debug_assert!(x.len() >= n, "output buffer shorter than band size");
let len = n.min(pulses.len()).min(x.len());
if len == 0 {
return;
}
let scale = celt_rsqrt_norm(ryy) * gain;
for (dst, &pulse) in x.iter_mut().take(len).zip(pulses.iter()) {
*dst = scale * pulse as OpusVal16;
}
}
#[cfg(feature = "fixed_point")]
pub(crate) fn normalise_residual_fixed(
pulses: &[i32],
x: &mut [FixedOpusVal16],
n: usize,
ryy: FixedOpusVal32,
gain: FixedOpusVal32,
) {
if n == 0 {
return;
}
debug_assert!(pulses.len() >= n, "pulse buffer shorter than band size");
debug_assert!(x.len() >= n, "output buffer shorter than band size");
let len = n.min(pulses.len()).min(x.len());
if len == 0 || ryy == 0 {
return;
}
let k = celt_ilog2(ryy) >> 1;
let t = vshr32(ryy, 2 * (k - 7));
let g = mult32_32_q31(i32::from(celt_rsqrt_norm_fixed(t)), gain) as i16;
for (dst, &pulse) in x.iter_mut().take(len).zip(pulses.iter()) {
*dst = extract16(pshr32(mult16_16(g, pulse as i16), (k + 1) as u32));
}
}
pub(crate) fn op_pvq_search(
x: &mut [OpusVal16],
pulses: &mut [OpusInt32],
n: usize,
k: i32,
_arch: i32,
) -> OpusVal32 {
assert!(n > 0, "vector dimension must be positive");
assert!(k >= 0, "pulse count must be non-negative");
assert!(x.len() >= n, "coefficient buffer shorter than band size");
assert!(pulses.len() >= n, "pulse buffer shorter than band size");
let mut y = vec![0.0f32; n];
let mut sign = vec![false; n];
for (idx, sample) in x.iter_mut().enumerate().take(n) {
let value = *sample;
sign[idx] = value < 0.0;
*sample = value.abs();
pulses[idx] = 0;
y[idx] = 0.0;
}
let mut xy = 0.0f32;
let mut yy = 0.0f32;
let mut pulses_left = k;
if k > ((n as i32) >> 1) {
let mut sum = 0.0f32;
for &sample in x.iter().take(n) {
sum += sample;
}
if !(sum > EPSILON && sum < 64.0) {
if n > 0 {
x[0] = 1.0;
for coeff in x.iter_mut().take(n).skip(1) {
*coeff = 0.0;
}
}
sum = 1.0;
}
let rcp = (k as OpusVal32 + 0.8) * celt_rcp(sum);
for idx in 0..n {
let projected = floorf(rcp * x[idx]);
let pulse = projected as OpusInt32;
pulses[idx] = pulse;
let val = pulse as OpusVal16;
y[idx] = val;
yy += val * val;
xy += x[idx] * val;
y[idx] *= 2.0;
pulses_left -= pulse;
}
}
debug_assert!(pulses_left >= 0, "pulse allocation exceeded target count");
if pulses_left < 0 {
pulses_left = 0;
}
if pulses_left > n as i32 + 3 {
let tmp = pulses_left as OpusVal16;
yy += tmp * tmp;
yy += tmp * y[0];
pulses[0] += pulses_left;
pulses_left = 0;
}
for _ in 0..pulses_left {
yy += 1.0;
let mut best_id = 0usize;
let mut best_den = yy + y[0];
let mut best_num = (xy + x[0]) * (xy + x[0]);
let x_tail = &x[1..n];
let y_tail = &y[1..n];
let mut x_chunks = x_tail.chunks_exact(4);
let mut y_chunks = y_tail.chunks_exact(4);
for (chunk_idx, (x_chunk, y_chunk)) in (&mut x_chunks).zip(&mut y_chunks).enumerate() {
let base = 1 + chunk_idx * 4;
let rxy0 = xy + x_chunk[0];
let ryy0 = yy + y_chunk[0];
let num0 = rxy0 * rxy0;
select_pvq_candidate_float(
&mut best_id,
&mut best_den,
&mut best_num,
base,
ryy0,
num0,
);
let rxy1 = xy + x_chunk[1];
let ryy1 = yy + y_chunk[1];
let num1 = rxy1 * rxy1;
select_pvq_candidate_float(
&mut best_id,
&mut best_den,
&mut best_num,
base + 1,
ryy1,
num1,
);
let rxy2 = xy + x_chunk[2];
let ryy2 = yy + y_chunk[2];
let num2 = rxy2 * rxy2;
select_pvq_candidate_float(
&mut best_id,
&mut best_den,
&mut best_num,
base + 2,
ryy2,
num2,
);
let rxy3 = xy + x_chunk[3];
let ryy3 = yy + y_chunk[3];
let num3 = rxy3 * rxy3;
select_pvq_candidate_float(
&mut best_id,
&mut best_den,
&mut best_num,
base + 3,
ryy3,
num3,
);
}
let rem_start = n - x_chunks.remainder().len();
for idx in rem_start..n {
let rxy = xy + x[idx];
let ryy = yy + y[idx];
let num = rxy * rxy;
select_pvq_candidate_float(&mut best_id, &mut best_den, &mut best_num, idx, ryy, num);
}
xy += x[best_id];
yy += y[best_id];
y[best_id] += 2.0;
pulses[best_id] += 1;
}
for (idx, pulse) in pulses.iter_mut().take(n).enumerate() {
if sign[idx] {
*pulse = -*pulse;
}
}
yy
}
#[cfg(feature = "fixed_point")]
pub(crate) fn op_pvq_search_fixed(
x: &mut [FixedOpusVal16],
pulses: &mut [OpusInt32],
n: usize,
k: i32,
_arch: i32,
) -> FixedOpusVal16 {
assert!(n > 0, "vector dimension must be positive");
assert!(k >= 0, "pulse count must be non-negative");
assert!(x.len() >= n, "coefficient buffer shorter than band size");
assert!(pulses.len() >= n, "pulse buffer shorter than band size");
let mut y = vec![0i16; n];
let mut signx = vec![0i32; n];
for j in 0..n {
let value = x[j];
signx[j] = i32::from(value < 0);
x[j] = if value < 0 {
value.wrapping_neg()
} else {
value
};
pulses[j] = 0;
y[j] = 0;
}
let mut xy: i32 = 0;
let mut yy: i16 = 0;
let mut pulses_left = k;
if k > ((n as i32) >> 1) {
let mut sum: i32 = 0;
for &sample in x.iter().take(n) {
sum = sum.wrapping_add(sample as i32);
}
if sum <= k {
if n > 0 {
x[0] = 16_384;
for coeff in x.iter_mut().take(n).skip(1) {
*coeff = 0;
}
}
sum = 16_384;
}
let rcp = extract16(mult16_32_q16(k as i16, celt_rcp_fixed(sum)));
for j in 0..n {
let iy = mult16_16_q15(x[j], rcp) as i32;
pulses[j] = iy;
let yj = iy as i16;
y[j] = yj;
yy = extract16(mac16_16(i32::from(yy), yj, yj));
xy = mac16_16(xy, x[j], yj);
y[j] = y[j].wrapping_shl(1);
pulses_left -= iy;
}
}
debug_assert!(pulses_left >= 0, "pulse allocation exceeded target count");
if pulses_left < 0 {
pulses_left = 0;
}
if pulses_left > n as i32 + 3 {
let tmp = pulses_left as i16;
yy = extract16(mac16_16(i32::from(yy), tmp, tmp));
yy = extract16(mac16_16(i32::from(yy), tmp, y[0]));
pulses[0] += pulses_left;
pulses_left = 0;
}
for i in 0..pulses_left {
let rshift = 1 + celt_ilog2(k - pulses_left + i + 1);
yy = add16(yy, 1);
let mut best_id = 0usize;
let rxy = extract16(shr32(add32(xy, i32::from(x[0])), rshift as u32));
let ryy = add16(yy, y[0]);
let rxy_sq = mult16_16_q15(rxy, rxy);
let mut best_den = ryy;
let mut best_num: i32 = i32::from(rxy_sq);
let x_tail = &x[1..n];
let y_tail = &y[1..n];
let mut x_chunks = x_tail.chunks_exact(4);
let mut y_chunks = y_tail.chunks_exact(4);
for (chunk_idx, (x_chunk, y_chunk)) in (&mut x_chunks).zip(&mut y_chunks).enumerate() {
let base = 1 + chunk_idx * 4;
let rxy0 = extract16(shr32(add32(xy, i32::from(x_chunk[0])), rshift as u32));
let ryy0 = add16(yy, y_chunk[0]);
let num0 = mult16_16_q15(rxy0, rxy0);
select_pvq_candidate_fixed(
&mut best_id,
&mut best_den,
&mut best_num,
base,
ryy0,
num0,
);
let rxy1 = extract16(shr32(add32(xy, i32::from(x_chunk[1])), rshift as u32));
let ryy1 = add16(yy, y_chunk[1]);
let num1 = mult16_16_q15(rxy1, rxy1);
select_pvq_candidate_fixed(
&mut best_id,
&mut best_den,
&mut best_num,
base + 1,
ryy1,
num1,
);
let rxy2 = extract16(shr32(add32(xy, i32::from(x_chunk[2])), rshift as u32));
let ryy2 = add16(yy, y_chunk[2]);
let num2 = mult16_16_q15(rxy2, rxy2);
select_pvq_candidate_fixed(
&mut best_id,
&mut best_den,
&mut best_num,
base + 2,
ryy2,
num2,
);
let rxy3 = extract16(shr32(add32(xy, i32::from(x_chunk[3])), rshift as u32));
let ryy3 = add16(yy, y_chunk[3]);
let num3 = mult16_16_q15(rxy3, rxy3);
select_pvq_candidate_fixed(
&mut best_id,
&mut best_den,
&mut best_num,
base + 3,
ryy3,
num3,
);
}
let rem_start = n - x_chunks.remainder().len();
for j in rem_start..n {
let rxy = extract16(shr32(add32(xy, i32::from(x[j])), rshift as u32));
let ryy = add16(yy, y[j]);
let rxy_sq = mult16_16_q15(rxy, rxy);
select_pvq_candidate_fixed(&mut best_id, &mut best_den, &mut best_num, j, ryy, rxy_sq);
}
xy = add32(xy, i32::from(x[best_id]));
yy = add16(yy, y[best_id]);
y[best_id] = y[best_id].wrapping_add(2);
pulses[best_id] += 1;
}
for j in 0..n {
let sign = -signx[j];
pulses[j] = (pulses[j] ^ sign) + signx[j];
}
yy
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn alg_quant(
x: &mut [OpusVal16],
n: usize,
k: i32,
spread: i32,
b: usize,
enc: &mut EcEnc<'_>,
gain: OpusVal32,
resynth: bool,
arch: i32,
) -> u32 {
assert!(k > 0, "alg_quant requires at least one pulse");
assert!(n > 1, "alg_quant requires at least two dimensions");
assert!(x.len() >= n, "input vector shorter than band size");
let mut pulses = vec![0i32; n + 3];
exp_rotation(x, n, 1, b, k, spread);
let yy = op_pvq_search(x, &mut pulses, n, k, arch);
let total_pulses = usize::try_from(k).expect("pulse count must fit in usize");
encode_pulses(&pulses[..n], n, total_pulses, enc);
if resynth {
normalise_residual(&pulses[..n], x, n, yy, gain);
exp_rotation(x, n, -1, b, k, spread);
}
extract_collapse_mask(&pulses[..n], n, b)
}
#[cfg(feature = "fixed_point")]
#[allow(clippy::too_many_arguments)]
pub(crate) fn alg_quant_fixed(
x: &mut [FixedOpusVal16],
n: usize,
k: i32,
spread: i32,
b: usize,
enc: &mut EcEnc<'_>,
gain: FixedOpusVal32,
resynth: bool,
arch: i32,
) -> u32 {
assert!(k > 0, "alg_quant requires at least one pulse");
assert!(n > 1, "alg_quant requires at least two dimensions");
assert!(x.len() >= n, "input vector shorter than band size");
let mut pulses = vec![0i32; n + 3];
exp_rotation_fixed(x, n, 1, b, k, spread);
let yy = op_pvq_search_fixed(x, &mut pulses, n, k, arch);
let total_pulses = usize::try_from(k).expect("pulse count must fit in usize");
encode_pulses(&pulses[..n], n, total_pulses, enc);
if resynth {
normalise_residual_fixed(&pulses[..n], x, n, i32::from(yy), gain);
exp_rotation_fixed(x, n, -1, b, k, spread);
}
extract_collapse_mask(&pulses[..n], n, b)
}
pub(crate) fn alg_unquant(
x: &mut [OpusVal16],
n: usize,
k: i32,
spread: i32,
b: usize,
dec: &mut EcDec<'_>,
gain: OpusVal32,
) -> u32 {
assert!(k > 0, "alg_unquant requires at least one pulse");
assert!(n > 1, "alg_unquant requires at least two dimensions");
assert!(x.len() >= n, "input vector shorter than band size");
assert!(
n <= MAX_PVQ_BAND_SIZE,
"alg_unquant band size exceeds CELT maximum"
);
let mut pulses = [0i32; MAX_PVQ_BAND_SIZE];
let pulses = &mut pulses[..n];
let total_pulses = usize::try_from(k).expect("pulse count must fit in usize");
let ryy = decode_pulses(pulses, n, total_pulses, dec);
normalise_residual(pulses, x, n, ryy, gain);
exp_rotation(x, n, -1, b, k, spread);
extract_collapse_mask(pulses, n, b)
}
#[cfg(feature = "fixed_point")]
pub(crate) fn alg_unquant_fixed(
x: &mut [FixedOpusVal16],
n: usize,
k: i32,
spread: i32,
b: usize,
dec: &mut EcDec<'_>,
gain: FixedOpusVal32,
) -> u32 {
assert!(k > 0, "alg_unquant requires at least one pulse");
assert!(n > 1, "alg_unquant requires at least two dimensions");
assert!(x.len() >= n, "input vector shorter than band size");
assert!(
n <= MAX_PVQ_BAND_SIZE,
"alg_unquant band size exceeds CELT maximum"
);
let mut pulses = [0i32; MAX_PVQ_BAND_SIZE];
let pulses = &mut pulses[..n];
let total_pulses = usize::try_from(k).expect("pulse count must fit in usize");
let _ = decode_pulses(pulses, n, total_pulses, dec);
let mut ryy: i32 = 0;
for &pulse in pulses.iter() {
ryy = mac16_16(ryy, pulse as i16, pulse as i16);
}
normalise_residual_fixed(pulses, x, n, ryy, gain);
exp_rotation_fixed(x, n, -1, b, k, spread);
extract_collapse_mask(pulses, n, b)
}
pub(crate) fn renormalise_vector(x: &mut [OpusVal16], n: usize, gain: OpusVal32, arch: i32) {
assert!(x.len() >= n, "input vector shorter than band size");
let len = n.min(x.len());
let slice = &mut x[..len];
let energy = EPSILON + celt_inner_prod(slice, slice);
let scale = celt_rsqrt_norm(energy) * gain;
if arch != 0 {
let _ = arch;
}
for sample in slice.iter_mut() {
*sample *= scale;
}
}
#[cfg(feature = "fixed_point")]
pub(crate) fn renormalise_vector_fixed(
x: &mut [FixedOpusVal16],
n: usize,
gain: FixedOpusVal32,
arch: i32,
) {
assert!(x.len() >= n, "input vector shorter than band size");
let len = n.min(x.len());
let slice = &mut x[..len];
let mut energy = i32::from(FIXED_EPSILON);
energy = energy.wrapping_add(celt_inner_prod_fixed(slice, slice));
let k = (ec_ilog(energy as u32) - 1) >> 1;
let t = vshr32(energy, 2 * (k - 7));
let g = mult32_32_q31(i32::from(celt_rsqrt_norm_fixed(t)), gain) as i16;
if arch != 0 {
let _ = arch;
}
for sample in slice.iter_mut() {
let scaled = mult16_16(g, *sample);
*sample = extract16(pshr32(scaled, (k + 1) as u32));
}
}
pub(crate) fn stereo_itheta(
x: &[OpusVal16],
y: &[OpusVal16],
stereo: bool,
n: usize,
arch: i32,
) -> i32 {
assert!(x.len() >= n, "mid channel shorter than requested length");
assert!(y.len() >= n, "side channel shorter than requested length");
let len = n.min(x.len()).min(y.len());
let mut emid = EPSILON;
let mut eside = EPSILON;
if stereo {
for i in 0..len {
let m = 0.5 * (x[i] + y[i]);
let s = 0.5 * (x[i] - y[i]);
emid += m * m;
eside += s * s;
}
} else {
let mid = &x[..len];
let side = &y[..len];
emid += celt_inner_prod(mid, mid);
eside += celt_inner_prod(side, side);
}
if arch != 0 {
let _ = arch;
}
let mid = celt_sqrt(emid);
let side = celt_sqrt(eside);
let angle = fast_atan2f(side, mid);
floorf(0.5 + 16_384.0 * FRAC_2_PI * angle) as i32
}
#[must_use]
pub(crate) fn extract_collapse_mask(pulses: &[i32], n: usize, b: usize) -> u32 {
if b <= 1 {
return 1;
}
if n == 0 {
return 0;
}
debug_assert!(pulses.len() >= n, "pulse buffer shorter than band size");
let n0 = celt_udiv(n as u32, b as u32) as usize;
debug_assert!(n0 > 0, "sub-band width must be non-zero");
let mut collapse_mask = 0u32;
for band in 0..b {
let start = band * n0;
let end = (start + n0).min(pulses.len());
let mut accumulator = 0;
for &value in &pulses[start..end] {
accumulator |= value;
}
if accumulator != 0 {
collapse_mask |= 1 << band;
}
}
collapse_mask
}
#[cfg(test)]
mod tests {
use alloc::vec;
use alloc::vec::Vec;
#[cfg(feature = "fixed_point")]
use super::normalise_residual_fixed;
use super::{
SPREAD_NORMAL, alg_quant, alg_unquant, exp_rotation, extract_collapse_mask,
normalise_residual, renormalise_vector, stereo_itheta,
};
use crate::celt::entdec::EcDec;
use crate::celt::entenc::EcEnc;
fn seed_samples(len: usize) -> Vec<f32> {
let mut seed = 0x1234_5678u32;
let mut out = Vec::with_capacity(len);
for _ in 0..len {
seed = seed.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
let sample = ((seed >> 16) & 0x7fff) as i32 - 16_384;
out.push(sample as f32);
}
out
}
fn snr_db(original: &[f32], processed: &[f32]) -> f64 {
let mut err = 0.0;
let mut ener = 0.0;
for (&orig, &proc) in original.iter().zip(processed.iter()) {
let diff = f64::from(orig - proc);
err += diff * diff;
ener += f64::from(orig) * f64::from(orig);
}
if err == 0.0 {
return f64::INFINITY;
}
20.0 * (ener / err).log10()
}
fn rotation_case(len: usize, k: i32) {
let baseline = seed_samples(len);
let mut rotated = baseline.clone();
exp_rotation(&mut rotated, len, 1, 1, k, SPREAD_NORMAL);
let forward_snr = snr_db(&baseline, &rotated);
exp_rotation(&mut rotated, len, -1, 1, k, SPREAD_NORMAL);
let inverse_snr = snr_db(&baseline, &rotated);
assert!(inverse_snr > 60.0, "inverse SNR too low: {inverse_snr}");
assert!(
forward_snr < 20.0,
"forward SNR unexpectedly high: {forward_snr}"
);
}
#[test]
fn rotation_matches_reference_behaviour() {
for &(len, k) in &[(15, 3), (23, 5), (50, 3), (80, 1)] {
rotation_case(len, k);
}
}
#[test]
fn residual_normalisation_scales_by_gain() {
let pulses = [2, -1, 0, 3];
let mut output = [0.0f32; 4];
let ryy = 25.0;
let gain = 0.5;
normalise_residual(&pulses, &mut output, pulses.len(), ryy, gain);
let expected_scale = gain * (1.0 / ryy.sqrt());
for (value, &pulse) in output.iter().zip(&pulses) {
let expected = expected_scale * pulse as f32;
assert!(
(value - expected).abs() <= 1e-6,
"expected {expected}, found {value}"
);
}
}
#[test]
fn collapse_mask_sets_bits_for_active_bands() {
let pulses = [0, 0, 1, 0, 0, 0, 2, 3];
let mask = extract_collapse_mask(&pulses, pulses.len(), 4);
assert_eq!(mask, 0b1010);
}
#[test]
fn collapse_mask_for_single_band_is_one() {
let pulses = [0, 0, 0, 0];
assert_eq!(extract_collapse_mask(&pulses, pulses.len(), 1), 1);
}
#[test]
fn op_pvq_search_distributes_requested_pulses() {
let mut coeffs = [0.6f32, -0.4, 0.2, 0.1];
let mut pulses = vec![0i32; coeffs.len()];
let len = coeffs.len();
let energy = super::op_pvq_search(&mut coeffs, &mut pulses, len, 5, 0);
let total: i32 = pulses.iter().map(|&p| p.abs()).sum();
assert_eq!(total, 5);
assert!(energy > 0.0);
}
#[test]
fn alg_quant_and_unquant_round_trip() {
let coeffs = seed_samples(6);
let mut encoded = coeffs.clone();
let gain = 1.0f32;
let n = coeffs.len();
let k = 5;
let spread = SPREAD_NORMAL;
let b = 2usize;
let arch = 0;
let mut buffer = vec![0u8; 64];
let mask;
{
let mut enc = EcEnc::new(&mut buffer);
mask = alg_quant(&mut encoded, n, k, spread, b, &mut enc, gain, true, arch);
enc.enc_done();
}
let mut decoded = coeffs.clone();
let mut dec = EcDec::new(&mut buffer);
let mask_dec = alg_unquant(&mut decoded, n, k, spread, b, &mut dec, gain);
assert_eq!(mask, mask_dec);
let tolerance = 1e-6;
for (a, b) in encoded.iter().zip(decoded.iter()) {
assert!((a - b).abs() < tolerance);
}
}
#[test]
fn renormalise_vector_matches_expected_gain() {
let mut data = seed_samples(8);
let gain = 0.75f32;
let len = data.len();
renormalise_vector(&mut data, len, gain, 0);
let energy: f32 = data.iter().map(|&v| v * v).sum();
assert!((energy - gain * gain).abs() < 1e-5);
}
#[test]
fn stereo_itheta_returns_expected_half_pi_value() {
let x = [0.0f32; 8];
let y = [1.0f32; 8];
let angle = stereo_itheta(&x, &y, false, x.len(), 0);
assert!((angle - 16_384).abs() <= 1);
}
#[cfg(feature = "fixed_point")]
#[test]
fn fixed_residual_normalisation_preserves_signs() {
let pulses = [3, -2, 1, 0];
let mut output = [0i16; 4];
let ryy = 1 << 14;
let gain = 1 << 14;
normalise_residual_fixed(&pulses, &mut output, pulses.len(), ryy, gain);
for (value, &pulse) in output.iter().zip(&pulses) {
if pulse == 0 {
assert_eq!(*value, 0);
} else {
let sign = i32::from(value.signum());
assert!(sign == 0 || sign == pulse.signum());
}
}
}
#[cfg(feature = "fixed_point")]
mod fixed_tests {
use crate::celt::entdec::EcDec;
use crate::celt::entenc::EcEnc;
use crate::celt::fixed_arch::Q15_ONE as Q15_ONE_FIXED;
use crate::celt::fixed_arch::Q31_ONE;
use crate::celt::fixed_ops::{extract16, mult16_16, mult16_16_q15, mult32_32_q31};
use crate::celt::math_fixed::{
celt_cos_norm as celt_cos_norm_fixed, celt_rcp as celt_rcp_fixed,
};
use crate::celt::vq::normalise_residual_fixed;
use crate::celt::vq::{
SPREAD_AGGRESSIVE, SPREAD_LIGHT, SPREAD_NONE, SPREAD_NORMAL, alg_quant_fixed,
alg_unquant_fixed, exp_rotation_fixed, op_pvq_search_fixed, renormalise_vector_fixed,
};
fn assert_i16_slice(label: &str, got: &[i16], expected: &[i16]) {
assert_eq!(got.len(), expected.len(), "{label} length mismatch");
for (idx, (&g, &e)) in got.iter().zip(expected.iter()).enumerate() {
assert_eq!(g, e, "{label}[{idx}] mismatch: {g} != {e}");
}
}
fn assert_i32_slice(label: &str, got: &[i32], expected: &[i32]) {
assert_eq!(got.len(), expected.len(), "{label} length mismatch");
for (idx, (&g, &e)) in got.iter().zip(expected.iter()).enumerate() {
assert_eq!(g, e, "{label}[{idx}] mismatch: {g} != {e}");
}
}
#[test]
fn fixed_normalise_residual_matches_reference_case() {
let pulses = [1, -1, 1, 0, 0, 0, 0, 0];
let mut output = [0i16; 8];
normalise_residual_fixed(&pulses, &mut output, pulses.len(), 3, Q31_ONE);
let expected = [9459, -9458, 9459, 0, 0, 0, 0, 0];
assert_i16_slice("normalise_case1", &output, &expected);
}
#[test]
fn fixed_normalise_and_rotation_match_band0_decoder_reference_case() {
let pulses = [-1, 1, 0, -1, 0, 1, 0, -1];
let mut output = [0i16; 8];
normalise_residual_fixed(&pulses, &mut output, pulses.len(), 5, Q31_ONE);
let len = output.len();
exp_rotation_fixed(&mut output, len, -1, 8, 5, SPREAD_NORMAL);
let expected = [-7327, 7327, 0, -7327, 0, 7327, 0, -7327];
assert_i16_slice("band0_decode_case", &output, &expected);
}
#[test]
fn fixed_exp_rotation_coefficients_match_reference_case() {
let len = 8i32;
let k = 3i32;
let factor = 10i32;
let denom = len + factor * k;
let numer = mult16_16(Q15_ONE_FIXED, len as i16);
let gain = extract16(mult32_32_q31(numer, celt_rcp_fixed(denom)));
let theta = (mult16_16_q15(gain, gain) >> 1) as i16;
let c = celt_cos_norm_fixed(i32::from(theta));
let s = celt_cos_norm_fixed(i32::from(Q15_ONE_FIXED.wrapping_sub(theta)));
assert_eq!(gain, 6898);
assert_eq!(theta, 726);
assert_eq!(c, 32748);
assert_eq!(s, 1142);
}
#[test]
fn fixed_exp_rotation_forward_matches_reference_case() {
let mut data = [12000, -8000, 6000, -4000, 2000, 0, -1000, 500];
let len = data.len();
exp_rotation_fixed(&mut data, len, 1, 2, 3, SPREAD_NORMAL);
let expected = [11429, -8383, 6429, -4217, 1993, -209, -975, 534];
assert_i16_slice("exp_rotation_forward_case1", &data, &expected);
}
#[test]
fn fixed_exp_rotation_inverse_matches_reference_case() {
let mut data = [9459, -9458, 9459, 0, 0, 0, 0, 0];
let len = data.len();
exp_rotation_fixed(&mut data, len, -1, 2, 3, SPREAD_NORMAL);
let expected = [10117, -9412, 8794, 318, 0, 0, 0, 0];
assert_i16_slice("exp_rotation_case1", &data, &expected);
}
#[test]
fn fixed_renormalise_vector_matches_reference_cases() {
let mut zeros = [0i16; 8];
let expected_zeros = [0i16; 8];
let len = zeros.len();
renormalise_vector_fixed(&mut zeros, len, Q31_ONE, 0);
assert_i16_slice("renorm_zero", &zeros, &expected_zeros);
let mut mixed = [1000, -2000, 3000, -4000, 500, -600, 700, -800];
let expected_mixed = [2908, -5816, 8724, -11632, 1454, -1745, 2036, -2326];
let len = mixed.len();
renormalise_vector_fixed(&mut mixed, len, Q31_ONE, 0);
assert_i16_slice("renorm_mixed", &mixed, &expected_mixed);
let mut large = [30000, -30000, 20000, -10000];
let expected_large = [10248, -10248, 6832, -3416];
let len = large.len();
renormalise_vector_fixed(&mut large, len, Q31_ONE, 0);
assert_i16_slice("renorm_large", &large, &expected_large);
let mut half_gain = [30000, -30000, 20000, -10000];
let expected_half = [5124, -5124, 3416, -1708];
let len = half_gain.len();
renormalise_vector_fixed(&mut half_gain, len, 1_073_741_824, 0);
assert_i16_slice("renorm_gain_half", &half_gain, &expected_half);
}
#[test]
fn fixed_alg_quant_and_unquant_match_reference_cases() {
{
let mut buffer = [0u8; 256];
let mut x = [12000, -8000, 6000, -4000, 2000, 0, -1000, 500];
let expected = [10117, -9412, 8794, 318, 0, 0, 0, 0];
let mask;
{
let n = x.len();
let mut enc = EcEnc::new(&mut buffer);
mask =
alg_quant_fixed(&mut x, n, 3, SPREAD_NORMAL, 2, &mut enc, Q31_ONE, true, 0);
enc.enc_done();
}
let mut xu = [0i16; 8];
let n = xu.len();
let mut dec = EcDec::new(&mut buffer);
let umask = alg_unquant_fixed(&mut xu, n, 3, SPREAD_NORMAL, 2, &mut dec, Q31_ONE);
assert_eq!(mask, 1);
assert_eq!(umask, 1);
assert_i16_slice("alg_case1_q", &x, &expected);
assert_i16_slice("alg_case1_u", &xu, &expected);
}
{
let mut buffer = [0u8; 256];
let mut x = [12000, -8000, 6000, -4000, 2000, 0, -1000, 500];
let expected_q = [11429, 8383, 6429, 4217, 1993, 209, 975, 534];
let expected_u = [10117, -9412, 8794, 318, 0, 0, 0, 0];
let mask;
{
let n = x.len();
let mut enc = EcEnc::new(&mut buffer);
mask = alg_quant_fixed(
&mut x,
n,
3,
SPREAD_NORMAL,
2,
&mut enc,
Q31_ONE,
false,
0,
);
enc.enc_done();
}
let mut xu = [0i16; 8];
let n = xu.len();
let mut dec = EcDec::new(&mut buffer);
let umask = alg_unquant_fixed(&mut xu, n, 3, SPREAD_NORMAL, 2, &mut dec, Q31_ONE);
assert_eq!(mask, 1);
assert_eq!(umask, 1);
assert_i16_slice("alg_case1_resynth0_q", &x, &expected_q);
assert_i16_slice("alg_case1_resynth0_u", &xu, &expected_u);
}
{
let mut buffer = [0u8; 256];
let mut x = [0, 16000, -16000, 8000, -8000, 4000, 0, 2000, -2000, 0];
let expected = [0, 9459, -9458, 9459, 0, 0, 0, 0, 0, 0];
let mask;
{
let n = x.len();
let mut enc = EcEnc::new(&mut buffer);
mask =
alg_quant_fixed(&mut x, n, 3, SPREAD_NONE, 5, &mut enc, Q31_ONE, true, 0);
enc.enc_done();
}
let mut xu = [0i16; 10];
let n = xu.len();
let mut dec = EcDec::new(&mut buffer);
let umask = alg_unquant_fixed(&mut xu, n, 3, SPREAD_NONE, 5, &mut dec, Q31_ONE);
assert_eq!(mask, 3);
assert_eq!(umask, 3);
assert_i16_slice("alg_case2_q", &x, &expected);
assert_i16_slice("alg_case2_u", &xu, &expected);
}
{
let mut buffer = [0u8; 256];
let mut x = [8000, -4000, 2000, -1000];
let expected = [11585, -11584, 0, 0];
let mask;
{
let n = x.len();
let mut enc = EcEnc::new(&mut buffer);
mask =
alg_quant_fixed(&mut x, n, 2, SPREAD_NORMAL, 1, &mut enc, Q31_ONE, true, 0);
enc.enc_done();
}
let mut xu = [0i16; 4];
let n = xu.len();
let mut dec = EcDec::new(&mut buffer);
let umask = alg_unquant_fixed(&mut xu, n, 2, SPREAD_NORMAL, 1, &mut dec, Q31_ONE);
assert_eq!(mask, 1);
assert_eq!(umask, 1);
assert_i16_slice("alg_case3_q", &x, &expected);
assert_i16_slice("alg_case3_u", &xu, &expected);
}
{
let mut buffer = [0u8; 256];
let mut x = [12000, -8000];
let expected = [16383, 0];
let mask;
{
let n = x.len();
let mut enc = EcEnc::new(&mut buffer);
mask = alg_quant_fixed(
&mut x,
n,
1,
SPREAD_AGGRESSIVE,
1,
&mut enc,
Q31_ONE,
true,
0,
);
enc.enc_done();
}
let mut xu = [0i16; 2];
let n = xu.len();
let mut dec = EcDec::new(&mut buffer);
let umask =
alg_unquant_fixed(&mut xu, n, 1, SPREAD_AGGRESSIVE, 1, &mut dec, Q31_ONE);
assert_eq!(mask, 1);
assert_eq!(umask, 1);
assert_i16_slice("alg_case4_q", &x, &expected);
assert_i16_slice("alg_case4_u", &xu, &expected);
}
{
let mut buffer = [0u8; 256];
let mut x = [
5000, -4000, 3000, -2000, 1000, -500, 250, -125, 6000, -5000, 4000, -3000,
2000, -1000, 500, -250,
];
let expected = [
9376, 1011, 108, -501, -51, -57, 507, 28, 10390, -8309, -901, -611, 449, -5,
538, -483,
];
let mask;
{
let n = x.len();
let mut enc = EcEnc::new(&mut buffer);
mask =
alg_quant_fixed(&mut x, n, 3, SPREAD_LIGHT, 2, &mut enc, Q31_ONE, true, 0);
enc.enc_done();
}
let mut xu = [0i16; 16];
let n = xu.len();
let mut dec = EcDec::new(&mut buffer);
let umask = alg_unquant_fixed(&mut xu, n, 3, SPREAD_LIGHT, 2, &mut dec, Q31_ONE);
assert_eq!(mask, 3);
assert_eq!(umask, 3);
assert_i16_slice("alg_case5_q", &x, &expected);
assert_i16_slice("alg_case5_u", &xu, &expected);
}
{
let mut buffer = [0u8; 256];
let mut x = [
5000, -4000, 3000, -2000, 1000, -500, 250, -125, 6000, -5000, 4000, -3000,
2000, -1000, 500, -250,
];
let expected = [9416, 897, 0, 0, 0, 0, 0, 0, 10314, -8518, 0, 0, 0, 0, 0, 0];
let mask;
{
let n = x.len();
let mut enc = EcEnc::new(&mut buffer);
mask =
alg_quant_fixed(&mut x, n, 3, SPREAD_NORMAL, 8, &mut enc, Q31_ONE, true, 0);
enc.enc_done();
}
let mut xu = [0i16; 16];
let n = xu.len();
let mut dec = EcDec::new(&mut buffer);
let umask = alg_unquant_fixed(&mut xu, n, 3, SPREAD_NORMAL, 8, &mut dec, Q31_ONE);
assert_eq!(mask, 17);
assert_eq!(umask, 17);
assert_i16_slice("alg_case_b8_q", &x, &expected);
assert_i16_slice("alg_case_b8_u", &xu, &expected);
}
{
let mut buffer = [0u8; 256];
let mut x = [
0, 10000, -9000, 8000, -7000, 6000, -5000, 4000, -3000, 2000, -1000, 500, -250,
125, -60, 30,
];
let expected = [
-1153, 6368, -3849, 2819, 157, 1165, -687, 805, 0, 0, 0, 0, 0, 0, 0, 0,
];
let mask;
{
let n = x.len();
let mut enc = EcEnc::new(&mut buffer);
mask = alg_quant_fixed(
&mut x,
n,
3,
SPREAD_AGGRESSIVE,
2,
&mut enc,
1_073_741_824,
true,
0,
);
enc.enc_done();
}
let mut xu = [0i16; 16];
let n = xu.len();
let mut dec = EcDec::new(&mut buffer);
let umask =
alg_unquant_fixed(&mut xu, n, 3, SPREAD_AGGRESSIVE, 2, &mut dec, 1_073_741_824);
assert_eq!(mask, 1);
assert_eq!(umask, 1);
assert_i16_slice("alg_case6_q", &x, &expected);
assert_i16_slice("alg_case6_u", &xu, &expected);
}
{
let mut buffer = [0u8; 256];
let mut x = [16000, 8000, 4000, 2000, 1000, 500, 250, 125];
let expected = [13135, 8757, 4378, 0, 0, 0, 0, 0];
let mask;
{
let n = x.len();
let mut enc = EcEnc::new(&mut buffer);
mask =
alg_quant_fixed(&mut x, n, 6, SPREAD_NONE, 2, &mut enc, Q31_ONE, true, 0);
enc.enc_done();
}
let mut xu = [0i16; 8];
let n = xu.len();
let mut dec = EcDec::new(&mut buffer);
let umask = alg_unquant_fixed(&mut xu, n, 6, SPREAD_NONE, 2, &mut dec, Q31_ONE);
assert_eq!(mask, 1);
assert_eq!(umask, 1);
assert_i16_slice("alg_case7_q", &x, &expected);
assert_i16_slice("alg_case7_u", &xu, &expected);
}
{
let mut buffer = [0u8; 256];
let mut x = [6000, -6000, 6000, -6000, 6000, -6000, 6000, -6000];
let expected = [5792, -5792, 5792, -5792, 5792, -5792, 5792, -5792];
let mask;
{
let n = x.len();
let mut enc = EcEnc::new(&mut buffer);
mask =
alg_quant_fixed(&mut x, n, 8, SPREAD_NONE, 4, &mut enc, Q31_ONE, true, 0);
enc.enc_done();
}
let mut xu = [0i16; 8];
let n = xu.len();
let mut dec = EcDec::new(&mut buffer);
let umask = alg_unquant_fixed(&mut xu, n, 8, SPREAD_NONE, 4, &mut dec, Q31_ONE);
assert_eq!(mask, 15);
assert_eq!(umask, 15);
assert_i16_slice("alg_case8_q", &x, &expected);
assert_i16_slice("alg_case8_u", &xu, &expected);
}
{
let mut buffer = [0u8; 256];
let mut x = [0, 0, 0, 0, 0, 0, 12000, -8000];
let expected = [0, 0, 0, 0, 0, 0, 14654, -7327];
let mask;
{
let n = x.len();
let mut enc = EcEnc::new(&mut buffer);
mask =
alg_quant_fixed(&mut x, n, 3, SPREAD_NONE, 4, &mut enc, Q31_ONE, true, 0);
enc.enc_done();
}
let mut xu = [0i16; 8];
let n = xu.len();
let mut dec = EcDec::new(&mut buffer);
let umask = alg_unquant_fixed(&mut xu, n, 3, SPREAD_NONE, 4, &mut dec, Q31_ONE);
assert_eq!(mask, 8);
assert_eq!(umask, 8);
assert_i16_slice("alg_case9_q", &x, &expected);
assert_i16_slice("alg_case9_u", &xu, &expected);
}
}
#[test]
fn fixed_op_pvq_search_matches_reference_cases() {
{
let mut x = [0i16; 8];
let mut pulses = [0i32; 8];
let yy = op_pvq_search_fixed(&mut x, &mut pulses, 8, 6, 0);
let expected_x = [16384, 0, 0, 0, 0, 0, 0, 0];
let expected_pulses = [6, 0, 0, 0, 0, 0, 0, 0];
assert_eq!(yy, 36);
assert_i16_slice("op_silence_x", &x, &expected_x);
assert_i32_slice("op_silence_iy", &pulses, &expected_pulses);
}
{
let mut x = [0i16; 2];
let mut pulses = [0i32; 2];
let yy = op_pvq_search_fixed(&mut x, &mut pulses, 2, 10, 0);
let expected_x = [16384, 0];
let expected_pulses = [10, 0];
assert_eq!(yy, 100);
assert_i16_slice("op_bigk_x", &x, &expected_x);
assert_i32_slice("op_bigk_iy", &pulses, &expected_pulses);
}
{
let mut x = [100, -50, 25, -12, 6, -3, 2, -1];
let mut pulses = [0i32; 8];
let yy = op_pvq_search_fixed(&mut x, &mut pulses, 8, 6, 0);
let expected_x = [100, 50, 25, 12, 6, 3, 2, 1];
let expected_pulses = [5, -1, 0, 0, 0, 0, 0, 0];
assert_eq!(yy, 26);
assert_i16_slice("op_small_x", &x, &expected_x);
assert_i32_slice("op_small_iy", &pulses, &expected_pulses);
}
{
let mut x = [11429, -8383, 6429, -4217, 1993, -209, -975, 534];
let mut pulses = [0i32; 8];
let yy = op_pvq_search_fixed(&mut x, &mut pulses, 8, 3, 0);
let expected_x = [11429, 8383, 6429, 4217, 1993, 209, 975, 534];
let expected_pulses = [1, -1, 1, 0, 0, 0, 0, 0];
assert_eq!(yy, 3);
assert_i16_slice("op_case1_x", &x, &expected_x);
assert_i32_slice("op_case1_iy", &pulses, &expected_pulses);
}
}
}
}