pub fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
if a.len() != b.len() {
tracing::warn!(
"cosine_similarity length mismatch: {} vs {}, returning 0.0",
a.len(),
b.len()
);
return 0.0;
}
let mut dot = 0.0f32;
let mut norm_a = 0.0f32;
let mut norm_b = 0.0f32;
for (&x, &y) in a.iter().zip(b.iter()) {
dot += x * y;
norm_a += x * x;
norm_b += y * y;
}
if norm_a < 1e-8 || norm_b < 1e-8 {
return 0.0;
}
dot / (norm_a.sqrt() * norm_b.sqrt())
}
pub fn l2_normalize(vec: &mut [f32]) {
let norm: f32 = vec.iter().map(|x| x * x).sum::<f32>().sqrt();
if norm > 1e-8 {
for v in vec.iter_mut() {
*v /= norm;
}
}
}
pub fn cosine_similarity_f32_f64(a: &[f32], b: &[f64]) -> f32 {
if a.len() != b.len() {
tracing::warn!(
"cosine_similarity_f32_f64 length mismatch: {} vs {}, returning 0.0",
a.len(),
b.len()
);
return 0.0;
}
let mut dot = 0.0f32;
let mut norm_a = 0.0f32;
let mut norm_b = 0.0f32;
for (&x, &y) in a.iter().zip(b.iter()) {
let y = y as f32;
dot += x * y;
norm_a += x * x;
norm_b += y * y;
}
if norm_a < 1e-8 || norm_b < 1e-8 {
return 0.0;
}
dot / (norm_a.sqrt() * norm_b.sqrt())
}
pub fn mean_vector(vectors: &[Vec<f32>]) -> Option<Vec<f32>> {
if vectors.is_empty() {
return None;
}
let dim = vectors[0].len();
let mut sum = vec![0.0f32; dim];
for v in vectors {
for (s, &x) in sum.iter_mut().zip(v.iter()) {
*s += x;
}
}
let n = vectors.len() as f32;
for s in &mut sum {
*s /= n;
}
Some(sum)
}
use crate::types::Segment;
pub fn merge_segments(segments: Vec<Segment>, max_gap_secs: f64) -> Vec<Segment> {
if segments.is_empty() {
return segments;
}
let mut merged = Vec::new();
let mut current = segments[0].clone();
for next in segments.into_iter().skip(1) {
if current.speaker == next.speaker && next.time.start - current.time.end <= max_gap_secs {
current.time.end = next.time.end;
if let (Some(c1), Some(c2)) = (current.confidence, next.confidence) {
current.confidence = Some((c1 + c2) / 2.0);
}
} else {
merged.push(current);
current = next;
}
}
merged.push(current);
merged
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cosine_similarity_identical() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![1.0, 2.0, 3.0];
let sim = cosine_similarity(&a, &b);
assert!((sim - 1.0).abs() < 1e-5);
}
#[test]
fn test_cosine_similarity_orthogonal() {
let a = vec![1.0, 0.0, 0.0];
let b = vec![0.0, 1.0, 0.0];
let sim = cosine_similarity(&a, &b);
assert!(sim.abs() < 1e-5);
}
#[test]
fn test_cosine_similarity_opposite() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![-1.0, -2.0, -3.0];
let sim = cosine_similarity(&a, &b);
assert!((sim + 1.0).abs() < 1e-5);
}
#[test]
fn test_l2_normalize() {
let mut v = vec![3.0, 4.0];
l2_normalize(&mut v);
assert!((v[0] - 0.6).abs() < 1e-5);
assert!((v[1] - 0.8).abs() < 1e-5);
}
#[test]
fn test_mean_vector() {
let vectors = vec![vec![1.0, 2.0], vec![3.0, 4.0]];
let mean = mean_vector(&vectors).unwrap();
assert!((mean[0] - 2.0).abs() < 1e-5);
assert!((mean[1] - 3.0).abs() < 1e-5);
}
}