use crate::silk::energy_flp::energy;
use crate::silk::inner_product_flp::inner_product_flp;
#[allow(clippy::too_many_arguments)]
pub fn corr_vector_flp(xt: &mut [f32], x: &[f32], t: &[f32], l: usize, order: usize, _arch: i32) {
assert!(order > 0, "order must be positive");
assert!(l > 0, "vector length must be positive");
assert!(
xt.len() >= order,
"output slice must hold at least `order` entries"
);
let history = order - 1;
assert!(
x.len() >= l + history,
"`x` must contain at least l + order - 1 samples"
);
assert!(t.len() >= l, "`t` must contain at least `l` samples");
let target = &t[..l];
let mut column_start = history;
for slot in xt.iter_mut().take(order) {
let column = &x[column_start..column_start + l];
*slot = inner_product_flp(column, target) as f32;
column_start = column_start.saturating_sub(1);
}
}
pub fn corr_matrix_flp(xx: &mut [f32], x: &[f32], l: usize, order: usize, _arch: i32) {
assert!(order > 0, "order must be positive");
assert!(l > 0, "vector length must be positive");
let history = order - 1;
assert!(
x.len() >= l + history,
"`x` must contain at least l + order - 1 samples"
);
assert!(
xx.len() >= order * order,
"matrix slice must hold order × order entries"
);
let start = history;
let column0 = &x[start..start + l];
let mut diag_energy = energy(column0);
set_matrix(xx, order, 0, 0, diag_energy as f32);
for j in 1..order {
let entering = start - j;
let leaving = start + l - j;
diag_energy += f64::from(x[entering]) * f64::from(x[entering])
- f64::from(x[leaving]) * f64::from(x[leaving]);
set_matrix(xx, order, j, j, diag_energy as f32);
}
if order == 1 {
return;
}
let mut lag_start = history - 1;
for lag in 1..order {
let mut cross = inner_product_flp(column0, &x[lag_start..lag_start + l]);
set_symmetric(xx, order, lag, 0, cross as f32);
for j in 1..(order - lag) {
let leave_a = start + l - j;
let leave_b = lag_start + l - j;
let enter_a = start - j;
let enter_b = lag_start - j;
cross += f64::from(x[enter_a]) * f64::from(x[enter_b])
- f64::from(x[leave_a]) * f64::from(x[leave_b]);
set_symmetric(xx, order, lag + j, j, cross as f32);
}
if lag_start == 0 {
break;
}
lag_start -= 1;
}
}
#[inline]
fn set_symmetric(matrix: &mut [f32], order: usize, row: usize, col: usize, value: f32) {
set_matrix(matrix, order, row, col, value);
set_matrix(matrix, order, col, row, value);
}
#[inline]
fn set_matrix(matrix: &mut [f32], order: usize, row: usize, col: usize, value: f32) {
let index = row * order + col;
matrix[index] = value;
}
#[cfg(test)]
mod tests {
use super::{corr_matrix_flp, corr_vector_flp};
use crate::silk::inner_product_flp::inner_product_flp;
use alloc::vec;
use alloc::vec::Vec;
fn reference_corr_vector(x: &[f32], t: &[f32], l: usize, order: usize) -> Vec<f32> {
let mut result = Vec::with_capacity(order);
for lag in 0..order {
let start = order - 1 - lag;
let column = &x[start..start + l];
result.push(inner_product_flp(column, &t[..l]) as f32);
}
result
}
fn reference_corr_matrix(x: &[f32], l: usize, order: usize) -> Vec<f32> {
let mut matrix = vec![0.0f32; order * order];
for row in 0..order {
let row_ptr = order - 1 - row;
let row_column = &x[row_ptr..row_ptr + l];
for col in 0..order {
let col_ptr = order - 1 - col;
let col_column = &x[col_ptr..col_ptr + l];
let dot = inner_product_flp(row_column, col_column) as f32;
matrix[row * order + col] = dot;
}
}
matrix
}
fn assert_close(lhs: &[f32], rhs: &[f32]) {
assert_eq!(lhs.len(), rhs.len(), "slices must match in length");
for (index, (&a, &b)) in lhs.iter().zip(rhs.iter()).enumerate() {
let diff = (a - b).abs();
assert!(
diff < 1e-5,
"difference at index {index} exceeds tolerance: {diff}"
);
}
}
#[test]
fn corr_vector_matches_reference() {
let x = [0.5f32, -1.25, 2.0, 3.5, -0.75, 1.25, -2.5];
let t = [1.0f32, -0.5, 0.25, -0.125, 0.0625];
let l = 5;
let order = 3;
let mut actual = [0.0f32; 3];
corr_vector_flp(&mut actual, &x, &t, l, order, 0);
let expected = reference_corr_vector(&x, &t, l, order);
assert_close(&actual, &expected);
}
#[test]
fn corr_matrix_matches_reference() {
let x = [0.5f32, -1.0, 2.5, 3.0, -0.75, 0.25, 1.0, -2.0];
let l = 5;
let order = 4;
let mut actual = [0.0f32; 16];
corr_matrix_flp(&mut actual, &x, l, order, 0);
let expected = reference_corr_matrix(&x, l, order);
assert_close(&actual, &expected);
}
#[test]
fn handles_single_tap_order() {
let x = [1.0f32, 2.0, 3.0];
let l = 2;
let order = 1;
let mut xt = [0.0f32; 1];
corr_vector_flp(&mut xt, &x, &x[1..], l, order, 0);
assert!((xt[0] - inner_product_flp(&x[0..2], &x[1..3]) as f32).abs() < 1e-6);
let mut xx = [0.0f32; 1];
corr_matrix_flp(&mut xx, &x, l, order, 0);
assert!((xx[0] - 5.0).abs() < 1e-6);
}
}