use crate::trie::TrieData;
use std::hash::Hash;
impl<T: Clone + Default + PartialEq + Eq + Hash> TrieData<T> {
pub(crate) fn calculate_jaro_winkler_score(&self, query: &str, word: &str) -> f32 {
let jaro_dist = self.jaro_distance(query, word);
let prefix_length = self.common_prefix_length(query, word);
let prefix_scale = 0.1;
jaro_dist + (prefix_length as f32 * prefix_scale * (1.0 - jaro_dist))
}
pub(crate) fn jaro_distance(&self, s1: &str, s2: &str) -> f32 {
let s1_len = s1.len();
let s2_len = s2.len();
if s1_len == 0 {
return if s2_len == 0 { 1.0 } else { 0.0 };
}
let match_distance = (s1_len.max(s2_len) / 2) - 1;
let mut s1_matches = vec![false; s1_len];
let mut s2_matches = vec![false; s2_len];
let mut matches = 0.0;
let mut transpositions = 0.0;
for i in 0..s1_len {
let start = i.saturating_sub(match_distance);
let end = (i + match_distance + 1).min(s2_len);
for j in start..end {
if s2_matches[j] || s1.chars().nth(i) != s2.chars().nth(j) {
continue;
}
s1_matches[i] = true;
s2_matches[j] = true;
matches += 1.0;
break;
}
}
if matches == 0.0 {
return 0.0;
}
let mut k = 0;
for i in 0..s1_len {
if !s1_matches[i] {
continue;
}
while !s2_matches[k] {
k += 1;
}
if s1.chars().nth(i) != s2.chars().nth(k) {
transpositions += 1.0;
}
k += 1;
}
let jaro = (matches / s1_len as f32
+ matches / s2_len as f32
+ (matches - transpositions / 2.0) / matches)
/ 3.0;
jaro
}
fn common_prefix_length(&self, s1: &str, s2: &str) -> usize {
let max_prefix_length = 4;
let mut length = 0;
for (c1, c2) in s1.chars().zip(s2.chars()) {
if c1 != c2 || length >= max_prefix_length {
break;
}
length += 1;
}
length
}
}