use crate::kmp_table;
pub fn kmp_find<N, H>(needle: &[N], haystack: &[H]) -> Option<usize>
where
N: PartialEq,
H: PartialEq<N>,
{
kmp_find_with_lsp_table(needle, haystack, &kmp_table(needle))
}
#[allow(clippy::indexing_slicing)]
pub fn kmp_find_with_lsp_table<N, H>(needle: &[N], haystack: &[H], lsp: &[usize]) -> Option<usize>
where
N: PartialEq,
H: PartialEq<N>,
{
if needle.is_empty() {
return Some(0);
}
if needle.len() > haystack.len() {
return None;
}
let mut needle_pos: usize = 0;
#[allow(clippy::integer_arithmetic)]
for (haystack_pos, haystack_char) in haystack.iter().enumerate() {
while needle_pos > 0 && *haystack_char != needle[needle_pos] {
needle_pos = lsp[needle_pos - 1];
}
if *haystack_char == needle[needle_pos] {
needle_pos += 1;
if needle_pos == needle.len() {
return Some(haystack_pos - (needle_pos - 1));
}
}
}
None
}
#[cfg(test)]
mod tests {
use proptest::prelude::*;
use crate::kmp_find;
#[test]
fn basic() {
assert_eq!(
Some(6),
kmp_find(
&['a', 'a', 'a', 'b'],
&['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'b']
)
)
}
#[test]
fn empty_needle() {
assert_eq!(Some(0), kmp_find(&[], &['a', 'b', 'c', 'd', 'e']));
}
#[test]
fn empty_haystack() {
let empty_haystack: &[char; 0] = &[];
assert_eq!(None, kmp_find(&['a', 'b', 'c'], empty_haystack));
}
#[test]
fn empty_both() {
let empty_needle: &[char; 0] = &[];
let empty_haystack: &[char; 0] = &[];
assert_eq!(Some(0), kmp_find(empty_needle, empty_haystack));
}
#[test]
fn needle_longer_haystack() {
assert_eq!(None, kmp_find(&['a', 'b', 'c'], &['a', 'b']));
}
proptest! {
#[ignore]
#[test]
fn fuzz_input(needle in prop::collection::vec(".*", 0..100), haystack in prop::collection::vec(".*", 0..100)) {
kmp_find(&needle, &haystack);
}
}
}