use crate::models::Hill;
pub fn cosine_similarity(hill1: &Hill, hill2: &Hill, min_overlap: usize) -> f64 {
let gate_lo = hill1.scan_start.max(hill2.scan_start);
let gate_hi = hill1.scan_end.min(hill2.scan_end);
let overlap = if gate_hi >= gate_lo {
gate_hi - gate_lo + 1
} else {
0
};
if overlap < min_overlap {
return 0.0;
}
let end1 = hill1.scan_start + hill1.intensity_profile.len().saturating_sub(1);
let end2 = hill2.scan_start + hill2.intensity_profile.len().saturating_sub(1);
let ov_start = hill1.scan_start.max(hill2.scan_start);
let ov_end = end1.min(end2);
if ov_end < ov_start {
return 0.0;
}
let norm1: f64 = hill1
.intensity_profile
.iter()
.map(|&x| {
let x = x as f64;
x * x
})
.sum::<f64>()
.sqrt();
let norm2: f64 = hill2
.intensity_profile
.iter()
.map(|&x| {
let x = x as f64;
x * x
})
.sum::<f64>()
.sqrt();
if norm1 == 0.0 || norm2 == 0.0 {
return 0.0;
}
let mut dot = 0.0f64;
for scan in ov_start..=ov_end {
let a = hill1.intensity_profile[scan - hill1.scan_start] as f64;
let b = hill2.intensity_profile[scan - hill2.scan_start] as f64;
dot += a * b;
}
(dot / (norm1 * norm2)).clamp(0.0, 1.0)
}
#[cfg(test)]
#[path = "cosine_tests.rs"]
mod tests;