module Algorithms.DFA.KMP where
import Data.Bifunctor (bimap)
import Data.Maybe (isNothing)
import Data.Array (listArray , (!))
import Data.List (mapAccumL)
newtype DFA a = DFA (a -> Maybe (Either (DFA a) (DFA a)))
step :: Eq a
=> (Int -> DFA a)
-> [(a,Int)]
-> Either Int Int
-> [a]
-> a
-> Maybe (Either (DFA a) (DFA a))
step m rs l ns c
| c `elem` ns = Nothing
| True = Just $ maybe (bimap m m l) (Right . m) $ lookup c rs
run :: DFA a -> [a] -> Maybe (DFA a)
run m [] = Just m
run (DFA m) (x:xs) = m x >>= either (flip run $ x:xs) (flip run xs)
mkDFA :: Eq a => [(a,Int)] -> DFA a
mkDFA xs = let
a = listArray (0,length xs) ys
m = step (a !)
ys = map DFA $ from xs
from [(x,0)] = [m [] (Right 0) [x]]
from ((x,0):xs) = m [(x,1)] (Right 0) [] : core (zip [2..] xs)
core [(_,(x,i))] = [m [] (Left i) [x]]
core ((n,(x,i)) : xs) = m [(x,n)] (Left i) [] : core xs
in a ! 0
prefix :: Eq a => [a] -> [Int]
prefix xs = (0:) . snd . mapAccumL f (0,xs) $ tail xs where
f (n, z:zs) x
| x == z = ((n + 1,zs),n)
| otherwise = ((0,xs),n)
match :: Eq a => [a] -> [a] -> Bool
match p s = isNothing $ run (mkDFA $ zip <*> prefix $ p) s