packages feed

anagrep-0.1.0.0: Text/Regex/Anagram/Util.hs

{-# LANGUAGE TupleSections #-}

module Text.Regex.Anagram.Util
  where

import           Control.Applicative (Alternative, empty)
import           Data.Foldable (foldlM)
import           Data.Function (on)
import qualified Data.IntMap.Strict as M
import qualified Data.IntSet as S
import           Data.List (group, groupBy, sortOn)
import qualified Data.Vector as V
import qualified Data.Vector.Fusion.Bundle as VB
import qualified Data.Vector.Fusion.Bundle.Size as VBS
import qualified Data.Vector.Fusion.Stream.Monadic as VS
import qualified Data.Vector.Generic as VG

import Text.Regex.Anagram.Types

guard' :: Alternative m => Bool -> a -> m a
guard' True = pure
guard' False = const empty

foldMapM :: (Monad m, Monoid b) => (a -> m b) -> [a] -> m b
-- foldMapM f = fmap fold . mapM f
foldMapM f = foldlM (\b a -> (b <>) <$> f a) mempty

concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
-- concatMapM = foldMapM
concatMapM f = fmap concat . mapM f

{-# INLINE withRLE #-}
withRLE :: (f (RL a) -> g (RL b)) -> RLEof f a -> RLEof g b
withRLE f = RLE . f . unRLE

rleLength :: RLE a -> Int
rleLength = foldl (\l (RL _ r) -> l + r) 0 . unRLE

rle :: Eq a => [a] -> RLE a
rle = RLE . map (\(x:l) -> RL x (succ $ length l)) . group

rleV :: Eq a => V.Vector a -> RLEV a
rleV = RLE . VG.unstream . VB.inplace rles VBS.toMax . VG.stream where
  rles (VS.Stream step st) = VS.Stream step' (Nothing, st) where
    step' (m, s) = do
      t <- step s
      case t of
        VS.Yield x s' -> case m of
          Nothing -> return $ VS.Skip (Just (RL x 1), s')
          Just r@(RL y n)
            | x == y -> return $ VS.Skip (Just (RL x $ succ n), s')
            | otherwise -> return $ VS.Yield r (Just (RL x 1), s')
        VS.Skip s' -> return $ VS.Skip (m, s')
        VS.Done -> return $ maybe VS.Done (\r -> VS.Yield r (Nothing, s)) m

sortRLE :: Ord a => RLE a -> RLE a
sortRLE = withRLE $ map (\(RL x r:l) -> RL x (r + rleLength (RLE l))) . groupBy ((==) `on` unRL) . sortOn unRL

filterRLE :: (a -> Bool) -> RLE a -> RLE a
filterRLE f = withRLE $ filter (f . unRL)

chrStr :: [Chr] -> ChrStr
chrStr = M.fromListWith (+) . map (, 1)

chrStrRLE :: ChrStr -> RLE Chr
chrStrRLE = RLE . map (uncurry RL) . M.toList

nullChar :: PatChar -> Bool
nullChar (PatSet s) = S.null s
nullChar _ = False

notChar :: PatChar -> PatChar
notChar (PatChr c) = PatNot (S.singleton c)
notChar (PatSet s) = PatNot s
notChar (PatNot s) = PatSet s

intersectChrStr :: PatChar -> ChrStr -> ChrStr
intersectChrStr (PatSet s) t = M.restrictKeys t s
intersectChrStr (PatNot n) t = M.withoutKeys t n
intersectChrStr (PatChr c) t = foldMap (M.singleton c) $ M.lookup c t

allChrs :: PatChar -> ChrStr -> Bool
allChrs p = M.null . intersectChrStr (notChar p)

intersectChr :: ChrSet -> PatChar -> PatChar
intersectChr s p@(PatChr c)
  | S.member c s = p
  | otherwise = mempty
intersectChr s (PatSet t) = PatSet $ S.intersection s t
intersectChr s (PatNot n) = PatSet $ S.difference s n

differenceChr :: ChrSet -> PatChar -> PatChar
differenceChr n p@(PatChr c)
  | S.member c n = mempty
  | otherwise = p
differenceChr n (PatSet s) = PatSet $ S.difference s n
differenceChr n (PatNot m) = PatNot $ S.union m n

intersectChar :: PatChar -> PatChar -> PatChar
intersectChar (PatSet s) p =  intersectChr s p
intersectChar (PatNot n) p = differenceChr n p
intersectChar p@(PatChr c) (PatChr d)
  | c == d = p
  | otherwise = mempty
intersectChar a b = intersectChar b a