use crate::utils::TextSlice;
#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize)]
pub struct Horspool<'a> {
shift: Vec<usize>,
m: usize,
pattern: TextSlice<'a>,
}
impl<'a> Horspool<'a> {
pub fn new(pattern: TextSlice<'a>) -> Self {
let m = pattern.len();
let mut shift = vec![m; 256];
for (j, &a) in pattern[..m - 1].iter().enumerate() {
shift[a as usize] = m - 1 - j;
}
Horspool { m, shift, pattern }
}
pub fn find_all<'b>(&'b self, text: TextSlice<'b>) -> Matches<'b> {
Matches {
horspool: self,
text,
n: text.len(),
last: self.m - 1,
pattern_last: self.pattern[self.m - 1],
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize)]
pub struct Matches<'a> {
horspool: &'a Horspool<'a>,
text: TextSlice<'a>,
n: usize,
last: usize,
pattern_last: u8,
}
impl<'a> Iterator for Matches<'a> {
type Item = usize;
fn next(&mut self) -> Option<usize> {
loop {
while self.last < self.n && self.text[self.last] != self.pattern_last {
self.last += self.horspool.shift[self.text[self.last] as usize];
}
if self.last >= self.n {
return None;
}
let i = self.last + 1 - self.horspool.m;
let j = self.last;
self.last += self.horspool.shift[self.pattern_last as usize];
if self.text[i..j] == self.horspool.pattern[..self.horspool.m - 1] {
return Some(i);
}
}
}
}
#[cfg(test)]
mod tests {
use super::Horspool;
use itertools::Itertools;
#[test]
fn test_shift() {
let pattern = b"AACB";
let horspool = Horspool::new(pattern);
assert_eq!(horspool.shift[b'A' as usize], 2);
assert_eq!(horspool.shift[b'C' as usize], 1);
assert_eq!(horspool.shift[b'B' as usize], 4);
assert_eq!(horspool.shift[b'X' as usize], 4);
}
#[test]
fn test_find_all() {
let text = b"dhjalkjwqnnnannanaflkjdklfj";
let pattern = b"qnnnannan";
let horspool = Horspool::new(pattern);
assert_eq!(horspool.find_all(text).collect_vec(), [8]);
}
#[test]
fn test_find_all_at_start() {
let text = b"dhjalkjwqnnnannanaflkjdklfj";
let pattern = b"dhjalk";
let horspool = Horspool::new(pattern);
assert_eq!(horspool.find_all(text).collect_vec(), [0]);
}
}