use std::cmp::min; use std::collections::HashMap; use std::hash::Hash; use std::sync::{Arc, RwLock};
unsafe impl<T: Clone + Default + PartialEq + Eq + Hash> Send for TrieData<T> {}
unsafe impl<T: Clone + Default + PartialEq + Eq + Hash> Sync for TrieData<T> {}
#[derive(Default, Debug)]
struct TrieNode<T: Default + PartialEq> {
children: HashMap<char, TrieNode<T>>,
word: Option<String>,
data: Vec<T>,
is_end: bool,
}
#[derive(Debug)]
pub(crate) struct TrieData<T: Clone + Default + PartialEq + Eq + Hash> {
root: TrieNode<T>,
data_map: HashMap<T, Vec<*mut TrieNode<T>>>,
}
#[derive(Debug)]
pub struct Trie<T: Clone + Default + PartialEq + Eq + Hash> {
trie_data: Arc<RwLock<TrieData<T>>>,
}
impl<T: Clone + Default + PartialEq + Eq + Hash> Trie<T> {
pub fn new() -> Self {
Trie {
trie_data: Arc::new(RwLock::new(TrieData {
root: TrieNode {
..Default::default()
},
data_map: HashMap::default(),
})),
}
}
pub fn insert(&self, word: &str, data: T) {
let mut trie_data = self.trie_data.write().unwrap();
trie_data.insert(word, data);
}
pub fn search_within_distance(&self, word: &str, max_distance: usize) -> Vec<SearchResult<T>> {
let trie_data = self.trie_data.read().unwrap();
trie_data.search_within_distance(word, max_distance)
}
pub fn search_within_distance_scored(
&self,
word: &str,
max_distance: usize,
) -> Vec<SearchResultWithScore<T>> {
let trie_data = self.trie_data.read().unwrap();
trie_data.search_within_distance_scored(word, max_distance)
}
pub fn remove_all(&self, data: &T) {
let mut trie_data = self.trie_data.write().unwrap();
trie_data.remove_all(data);
}
}
#[derive(Debug)]
pub struct SearchResult<T> {
pub word: String,
pub data: Vec<T>,
}
#[derive(Debug)]
pub struct SearchResultWithScore<T> {
pub word: String,
pub data: Vec<T>,
pub score: f32,
}
impl<T: PartialEq> PartialEq for SearchResult<T> {
fn eq(&self, other: &Self) -> bool {
self.word == other.word && self.data == other.data
}
}
impl<T: Clone + Default + PartialEq + Eq + Hash> TrieData<T> {
fn insert(&mut self, word: &str, data: T) {
let mut current = &mut self.root;
let augmented_word = format!("${}", word);
for c in augmented_word.chars() {
current = current.children.entry(c).or_insert_with(|| TrieNode {
..Default::default()
});
}
if current.word.is_none() {
current.word = Some(word.to_string());
}
current.data.push(data.clone());
current.is_end = true;
self.data_map
.entry(data)
.or_default()
.push(current as *mut _);
}
fn search_within_distance(&self, word: &str, max_distance: usize) -> Vec<SearchResult<T>> {
let augmented_word = format!("${}", word);
let last_row: Vec<usize> = (0..=augmented_word.len()).collect();
let mut results = Vec::new();
self.search_recursive(
&self.root,
'$',
&last_row,
&augmented_word,
max_distance,
&mut results,
true,
);
results
}
fn search_within_distance_scored(
&self,
word: &str,
max_distance: usize,
) -> Vec<SearchResultWithScore<T>> {
self.search_within_distance(word, max_distance)
.into_iter()
.map(|result| {
let score = self.calculate_jaro_winkler_score(word, &result.word);
SearchResultWithScore {
word: result.word.clone(),
data: result.data,
score,
}
})
.collect()
}
fn search_recursive(
&self,
node: &TrieNode<T>,
ch: char,
last_row: &Vec<usize>,
word: &str,
max_distance: usize,
results: &mut Vec<SearchResult<T>>,
is_root: bool,
) {
let row_length = word.len() + 1;
let mut current_row = vec![0; row_length];
current_row[0] = if is_root { 0 } else { last_row[0] + 1 };
for i in 1..row_length {
let insert_or_del = min(current_row[i - 1] + 1, last_row[i] + 1);
let replace = if word.chars().nth(i - 1) == Some(ch) {
last_row[i - 1] } else {
last_row[i - 1] + 1 };
current_row[i] = min(insert_or_del, replace);
}
if node.word.is_some() {
if current_row[row_length - 1] <= max_distance {
collect_all_words_from_this_node(node, results);
return;
}
}
else if current_row[0] >= word.len() - max_distance
&& current_row.last().unwrap() <= &max_distance
{
collect_all_words_from_this_node(node, results);
return;
}
if *current_row.iter().min().unwrap() <= max_distance {
for (next_ch, child) in &node.children {
self.search_recursive(
child,
*next_ch,
¤t_row,
word,
max_distance,
results,
false,
);
}
}
}
fn remove_all(&mut self, data: &T) {
if let Some(nodes) = self.data_map.get_mut(data) {
let mut empty_nodes = Vec::new();
nodes.retain(|&node_ptr| {
let node = unsafe { &mut *node_ptr };
node.data.retain(|d| d != data);
if node.data.is_empty() && node.word.is_some() {
node.word = None;
node.is_end = false;
}
if node.data.is_empty() {
empty_nodes.push(node_ptr);
}
!node.data.is_empty()
});
for &node_ptr in &empty_nodes {
self.remove_node(unsafe { &mut *node_ptr });
}
}
self.data_map.remove(data);
}
fn remove_node(&mut self, node: &mut TrieNode<T>) {
let mut current = node as *mut TrieNode<T>;
loop {
let parent_ptr = {
let current_ref = unsafe { &mut *current };
if !current_ref.children.is_empty()
|| current_ref.word.is_some()
|| !current_ref.data.is_empty()
{
break;
}
self.data_map
.iter()
.find_map(|(_, nodes)| nodes.iter().find(|&&ptr| ptr == current).copied())
};
if let Some(parent_ptr) = parent_ptr {
let parent = unsafe { &mut *parent_ptr };
let node_char = unsafe { &*current }
.word
.as_ref()
.unwrap()
.chars()
.next()
.unwrap();
parent.children.remove(&node_char);
current = parent_ptr;
} else {
break;
}
}
}
}
fn collect_all_words_from_this_node<T: Clone + Default + PartialEq>(
node: &TrieNode<T>,
results: &mut Vec<SearchResult<T>>,
) {
if let Some(ref node_word) = node.word {
results.push(SearchResult {
word: node_word.clone(),
data: node.data.clone(),
});
}
for (_, child) in &node.children {
collect_all_words_from_this_node(child, results);
}
}