{-@ LIQUID "--no-termination" @-} {-@ LIQUID "--short-names" @-} {- LIQUID "--diff" @-} module KMP (search) where import Language.Haskell.Liquid.Prelude (liquidError, liquidAssert) import Data.IORef import Control.Applicative ((<$>)) import qualified Data.Map as M import Prelude hiding (map) {-@ type Upto N = {v:Nat | v < N} @-} --------------------------------------------------------------------------- {-@ search :: pat:String -> str:String -> IO (Maybe (Upto (len str))) @-} --------------------------------------------------------------------------- search :: String -> String -> IO (Maybe Int) search pat str = do p <- ofListIO pat s <- ofListIO str kmpSearch p s --------------------------------------------------------------------------- -- | Do the Search -------------------------------------------------------- --------------------------------------------------------------------------- kmpSearch p@(IOA m _) s@(IOA n _) = do t <- kmpTable p find p s t 0 0 find p@(IOA m _) s@(IOA n _) t = go where go i j | i >= n = return $ Nothing | j >= m = return $ Just (i - m) | otherwise = do si <- getIO s i pj <- getIO p j tj <- getIO t j case () of _ | si == pj -> go (i+1) (j+1) | j == 0 -> go (i+1) j | otherwise -> go i tj --------------------------------------------------------------------------- -- | Make Table ----------------------------------------------------------- --------------------------------------------------------------------------- -- BUG WHAT's going on? {-@ bob :: Nat -> IO () @-} bob n = do t <- newIO (n + 1) (\_ -> 0) setIO t 0 100 r <- getIO t 0 liquidAssert (r == 0) $ return () kmpTable p@(IOA m _) = do t <- newIO m (\_ -> 0) fill p t 1 0 return t fill p t@(IOA m _) = go where go i j | i < m - 1 = do pi <- getIO p (id i) pj <- getIO p j case () of _ | pi == pj -> do let i' = i + 1 let j' = j + 1 setIO t i' j' go i' j' | j == 0 -> do let i' = i + 1 setIO t i' 0 go i' j | otherwise -> do j' <- getIO t j go i j' | otherwise = return t ------------------------------------------------------------------------------- -- | An Imperative Array ------------------------------------------------------ ------------------------------------------------------------------------------- data IOArr a = IOA { size :: Int , pntr :: IORef (Arr a) } {-@ data IOArr a

a -> Prop> = IOA { size :: Nat , pntr :: IORef ({v:Arr

a | alen v = size}) } @-} {-@ newIO :: forall

a -> Prop>. n:Nat -> (i:Upto n -> a

) -> IO ({v: IOArr

a | size v = n}) @-} newIO n f = IOA n <$> newIORef (new n f) {-@ getIO :: forall

a -> Prop>. a:IOArr

a -> i:Upto (size a) -> IO (a

) @-} getIO a@(IOA sz p) i = do arr <- readIORef p return $ (arr ! i) {-@ setIO :: forall

a -> Prop>. a:IOArr

a -> i:Upto (size a) -> a

-> IO () @-} setIO a@(IOA sz p) i v = do arr <- readIORef p let arr' = set arr i v writeIORef p arr' {-@ ofListIO :: xs:[a] -> IO ({v:IOArr a | size v = len xs}) @-} ofListIO xs = newIO n f where n = length xs m = M.fromList $ zip [0..] xs f i = (M.!) m i {-@ mapIO :: (a -> b) -> a:IOArr a -> IO ({v:IOArr b | size v = size a}) @-} mapIO f (IOA n p) = do a <- readIORef p IOA n <$> newIORef (map f a) ------------------------------------------------------------------------------- -- | A Pure Array ------------------------------------------------------------- ------------------------------------------------------------------------------- data Arr a = A { alen :: Int , aval :: Int -> a } {-@ data Arr a

a -> Prop> = A { alen :: Nat , aval :: i:Upto alen -> a

} @-} {-@ new :: forall

a -> Prop>. n:Nat -> (i:Upto n -> a

) -> {v: Arr

a | alen v = n} @-} new n v = A { alen = n , aval = \i -> if (0 <= i && i < n) then v i else liquidError "Out of Bounds!" } {-@ (!) :: forall

a -> Prop>. a:Arr

a -> i:Upto (alen a) -> a

@-} (A _ f) ! i = f i {-@ set :: forall

a -> Prop>. a:Arr

a -> i:Upto (alen a) -> a

-> {v:Arr

a | alen v = alen a} @-} set a@(A _ f) i v = a { aval = \j -> if (i == j) then v else f j } {-@ ofList :: xs:[a] -> {v:Arr a | alen v = len xs} @-} ofList xs = new n f where n = length xs m = M.fromList $ zip [0..] xs f i = (M.!) m i {-@ map :: (a -> b) -> a:Arr a -> {v:Arr b | alen v = alen a} @-} map f a@(A n z) = A n (f . z)