1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! Inner-product helpers mirrored from the reference SILK implementation.
//!
//! The C sources provide two closely related helpers:
//! `silk_inner_prod_aligned`, which performs an unscaled dot product between
//! two 16-bit vectors, and `silk_inner_prod_aligned_scale`, which right-shifts
//! each partial product before accumulating. This module exposes Rust
//! equivalents used by higher-level DSP kernels such as the correlation matrix
//! builder, stereo predictor search, and resampler primitives.
/// Computes the unscaled inner product between two 16-bit vectors.
///
/// Mirrors `silk_inner_prod_aligned`; the `arch` parameter is accepted for API
/// parity with the C code but is currently unused (architecture-specific SIMD
/// paths have not been ported yet).
///
/// # Panics
///
/// * If the input slices have different lengths.
#[must_use]
pub fn inner_prod_aligned(in_vec1: &[i16], in_vec2: &[i16], arch: i32) -> i32 {
let _ = arch;
assert_eq!(
in_vec1.len(),
in_vec2.len(),
"input vectors must have identical lengths"
);
let mut sum = 0i32;
for (&a, &b) in in_vec1.iter().zip(in_vec2.iter()) {
sum = sum.wrapping_add(i32::from(a) * i32::from(b));
}
sum
}
/// Computes the scaled inner product of `in_vec1` and `in_vec2`.
///
/// Each product term is right-shifted by `scale` bits prior to accumulation,
/// matching the behaviour of the reference implementation's
/// `silk_inner_prod_aligned_scale` routine.
///
/// # Panics
///
/// * If the input slices have different lengths.
/// * If `scale` falls outside the range `0..32` (i.e. `0 <= scale < 32`).
pub fn inner_prod_aligned_scale(in_vec1: &[i16], in_vec2: &[i16], scale: i32) -> i32 {
assert!((0..32).contains(&scale), "scale must be in the range 0..32");
assert_eq!(
in_vec1.len(),
in_vec2.len(),
"input vectors must have identical lengths"
);
let mut sum = 0i32;
for (&a, &b) in in_vec1.iter().zip(in_vec2.iter()) {
let product = i32::from(a) * i32::from(b);
let shifted = product >> scale;
sum = sum.wrapping_add(shifted);
}
sum
}
#[cfg(test)]
mod tests {
use super::{inner_prod_aligned, inner_prod_aligned_scale};
#[test]
fn matches_unscaled_inner_product() {
let vec1 = [1, 2, 3, 4];
let vec2 = [-1, 2, -3, 4];
let expected: i32 = vec1
.iter()
.zip(vec2.iter())
.map(|(&a, &b)| i32::from(a) * i32::from(b))
.sum();
assert_eq!(inner_prod_aligned_scale(&vec1, &vec2, 0), expected);
assert_eq!(inner_prod_aligned(&vec1, &vec2, 0), expected);
}
#[test]
fn applies_right_shift_before_accumulation() {
let vec1 = [300, -400, 500, -600];
let vec2 = [-7, 9, -11, 13];
// Reference calculation using 32-bit arithmetic with an arithmetic shift.
let expected =
((300 * -7) >> 2) + ((-400 * 9) >> 2) + ((500 * -11) >> 2) + ((-600 * 13) >> 2);
assert_eq!(inner_prod_aligned_scale(&vec1, &vec2, 2), expected);
}
#[test]
fn handles_large_scale_values() {
let vec1 = [12345, -23456];
let vec2 = [-3210, 4321];
let expected = ((12345 * -3210) >> 8) + ((-23456 * 4321) >> 8);
assert_eq!(inner_prod_aligned_scale(&vec1, &vec2, 8), expected);
}
#[test]
fn empty_vectors_return_zero() {
let vec: [i16; 0] = [];
assert_eq!(inner_prod_aligned_scale(&vec, &vec, 0), 0);
assert_eq!(inner_prod_aligned(&vec, &vec, 0), 0);
}
#[test]
fn inner_prod_aligned_handles_arch_parameter() {
let vec1 = [123, -456, 789];
let vec2 = [-111, 222, -333];
let expected: i32 = vec1
.iter()
.zip(vec2.iter())
.map(|(&a, &b)| i32::from(a) * i32::from(b))
.sum();
assert_eq!(inner_prod_aligned(&vec1, &vec2, 1), expected);
}
}