pub struct Trie {
vec : Vec<(char, Box<Trie>)>,
word : bool,
}
impl Trie {
pub fn new() -> Trie {
Trie {
vec: vec![],
word: false,
}
}
pub fn clear(&mut self) {
self.vec.clear();
self.word = false;
}
pub fn insert(&mut self, str : &str) {
let mut trie_ptr : *mut Trie = &mut *self;
for char in str.chars() {
trie_ptr = get_char_node_for_insert(trie_ptr, char);
}
unsafe { (*trie_ptr).word = true; }
}
pub fn contains(&self, str : &str) -> bool {
let mut trie = self;
for char in str.chars() {
if let Some(trie_) = get_char_node_for_lookup(trie, char) {
trie = trie_;
} else {
return false;
}
}
trie.word
}
pub fn remove(&mut self, str : &str) {
let mut chars = str.chars();
if let Some(char) = chars.next() {
match self.vec.binary_search_by(|&(char_,_)| char_.cmp(&char)) {
Ok(idx) => {
self.vec[idx].1.remove(chars.as_str());
},
Err(_) => {},
}
} else {
self.word = false;
}
}
pub fn to_strings(&self, prefix : &str) -> Vec<String> {
let mut ret = {
if self.word {
vec![prefix.to_owned()]
} else {
vec![]
}
};
for &(c, ref t) in &self.vec {
let mut prefix_ = prefix.to_owned();
prefix_.push(c);
ret.extend_from_slice(&t.to_strings(&prefix_));
}
ret
}
pub fn drop_pfx(&self, prefix : &mut Iterator<Item=char>) -> Vec<String> {
let mut trie = self;
for char in prefix {
if let Some(trie_) = get_char_node_for_lookup(trie, char) {
trie = trie_;
} else {
return vec![];
}
}
trie.to_strings("")
}
}
fn get_char_node_for_insert(trie : *mut Trie, char : char) -> *mut Trie {
let trie_ref : &mut Trie = unsafe { &mut *trie };
match trie_ref.vec.binary_search_by(|&(char_,_)| char_.cmp(&char)) {
Ok(idx) => &mut *trie_ref.vec[idx].1,
Err(idx) => {
trie_ref.vec.insert(idx, (char, Box::new(Trie { vec: vec![], word: false })));
&mut *trie_ref.vec[idx].1
},
}
}
fn get_char_node_for_lookup(trie : &Trie, char : char) -> Option<&Trie> {
match trie.vec.binary_search_by(|&(char_,_)| char_.cmp(&char)) {
Ok(idx) => Some(&trie.vec[idx].1),
Err(_) => None,
}
}
#[cfg(test)]
mod tests {
extern crate test;
use super::*;
#[test]
fn trie_test_1() {
let mut trie = Trie::new();
trie.insert("yada yada");
assert_eq!(vec!["yada yada"], trie.to_strings(""));
}
#[test]
fn trie_test_2() {
let mut trie = Trie::new();
trie.insert("foo");
assert!(trie.contains("foo"));
trie.insert("bar");
assert!(trie.contains("foo"));
assert!(trie.contains("bar"));
trie.insert("baz");
assert!(trie.contains("foo"));
assert!(trie.contains("bar"));
assert!(trie.contains("baz"));
assert_eq!(vec!["bar", "baz", "foo"], trie.to_strings(""));
}
#[test]
fn trie_test_3() {
let mut trie = Trie::new();
trie.insert("foo");
trie.insert("bar");
trie.insert("baz");
assert_eq!(vec!["ar", "az"], trie.drop_pfx(&mut "b".chars()));
}
#[test]
fn trie_test_insert_remove() {
let mut trie = Trie::new();
trie.insert("foo");
trie.insert("bar");
trie.insert("baz");
trie.remove("bar");
assert!(trie.contains("foo"));
assert!(!trie.contains("bar"));
assert!(trie.contains("baz"));
assert_eq!(vec!["az"], trie.drop_pfx(&mut "b".chars()));
}
}
#[cfg(test)]
mod benchs {
extern crate test;
use self::test::Bencher;
use std::fs::File;
use std::io::Read;
use super::*;
#[bench]
fn bench_trie_build(b : &mut Bencher) {
let mut contents = String::new();
let mut words : Vec<&str> = vec![];
{
match File::open("/usr/share/dict/american") {
Err(_) => {
println!("Can't open dictionary file, aborting benchmark.");
return;
}
Ok(mut file) => {
file.read_to_string(&mut contents).unwrap();
words.extend(contents.lines());
}
}
}
b.iter(|| {
let mut trie = Trie::new();
for word in words.iter().rev() {
trie.insert(word);
}
trie
});
}
#[bench]
fn bench_trie_lookup(b : &mut Bencher) {
let mut contents = String::new();
let mut words : Vec<&str> = vec![];
{
match File::open("/usr/share/dict/american") {
Err(_) => {
println!("Can't open dictionary file, aborting benchmark.");
return;
}
Ok(mut file) => {
file.read_to_string(&mut contents).unwrap();
words.extend(contents.lines());
}
}
}
let mut trie = Trie::new();
for word in words {
trie.insert(word);
}
b.iter(|| {
trie.drop_pfx(&mut "abs".chars())
});
}
#[bench]
fn bench_trie_list_all(b : &mut Bencher) {
let mut contents = String::new();
let mut words : Vec<&str> = vec![];
{
match File::open("/usr/share/dict/american") {
Err(_) => {
println!("Can't open dictionary file, aborting benchmark.");
return;
}
Ok(mut file) => {
file.read_to_string(&mut contents).unwrap();
words.extend(contents.lines());
}
}
}
let mut trie = Trie::new();
for word in words {
trie.insert(word);
}
b.iter(|| {
trie.drop_pfx(&mut "".chars())
});
}
}