#[cfg(test)]
pub(crate) fn xorshift64(state: &mut u64) -> u64 {
let mut x = *state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
*state = x;
x
}
#[cfg(test)]
pub(crate) fn xorshift_f64(state: &mut u64) -> f64 {
(xorshift64(state) >> 11) as f64 / (1u64 << 53) as f64
}
pub(super) fn tokenize(text: &str) -> Vec<String> {
text.split_whitespace()
.map(|t| {
t.chars()
.filter(|c| c.is_alphanumeric())
.collect::<String>()
.to_lowercase()
})
.filter(|t| !t.is_empty())
.collect()
}
pub fn cosine_similarity(a: &[f64], b: &[f64]) -> f64 {
if a.len() != b.len() || a.is_empty() {
return 0.0;
}
let dot: f64 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
let na = a.iter().map(|x| x * x).sum::<f64>().sqrt();
let nb = b.iter().map(|x| x * x).sum::<f64>().sqrt();
if na < 1e-10 || nb < 1e-10 {
0.0
} else {
(dot / (na * nb)).clamp(-1.0, 1.0)
}
}