module Extension.Prelude where
import Data.Maybe
import Data.List
import qualified Data.Set as S
import qualified Data.Map as M
import Data.Ord (comparing)
import Data.Function (on)
import Data.Foldable (asum)
import Control.Basics
import System.IO
implies :: Bool -> Bool -> Bool
implies True p = p
implies False _ = True
singleton :: a -> [a]
singleton x = [x]
unique :: Eq a => [a] -> Bool
unique [] = True
unique (x:xs) = x `notElem` xs && unique xs
sortednub :: Ord a => [a] -> [a]
sortednub = sortednubBy compare
sortednubBy :: (a -> a -> Ordering) -> [a] -> [a]
sortednubBy cmp =
mergeAll . sequences
where
sequences (a:xs@(b:xs')) = case a `cmp` b of
GT -> descending b [a] xs'
EQ -> sequences xs
LT -> ascending b (a:) xs
sequences xs = [xs]
descending a as (b:bs)
| a `cmp` b == GT = descending b (a:as) bs
descending a as bs = (a:as): sequences bs
ascending a as (b:bs)
| a `cmp` b == LT = ascending b (\ys -> as (a:ys)) bs
ascending a as bs = as [a] : sequences bs
mergeAll [x] = x
mergeAll xs = mergeAll (mergePairs xs)
mergePairs (a:b:xs) = merge a b: mergePairs xs
mergePairs xs = xs
merge [] bs = bs
merge as [] = as
merge as@(a:as') bs@(b:bs') = case a `cmp` b of
GT -> b : merge as bs'
EQ -> merge as bs'
LT -> a : merge as' bs
sortednubOn :: Ord b => (a -> b) -> [a] -> [a]
sortednubOn proj = sortednubBy (comparing proj)
nubOn :: Eq b => (a -> b) -> [a] -> [a]
nubOn proj = nubBy ((==) `on` proj)
groupOn :: Eq b => (a -> b) -> [a] -> [[a]]
groupOn proj = groupBy ((==) `on` proj)
sortOn :: Ord b => (a -> b) -> [a] -> [a]
sortOn proj = sortBy (comparing proj)
sortOnMemo :: Ord b => (a -> b) -> [a] -> [a]
sortOnMemo proj = map fst . sortOn snd . map (id &&& proj)
groupSortOn :: Ord b => (a -> b) -> [a] -> [[a]]
groupSortOn proj = groupOn proj . sortOn proj
eqClasses :: (Eq b, Ord b) => (a -> b) -> [a] -> [[a]]
eqClasses = eqClassesBy ord
where ord x y | x == y = EQ | x < y = LT | otherwise = GT
eqClassesBy :: (b -> b -> Ordering) -> (a -> b) -> [a] -> [[a]]
eqClassesBy ord proj = groupBy eq . sortBy ord'
where ord' x y = ord (proj x) (proj y)
eq x y = ord' x y == EQ
splitBy :: (a -> Bool) -> [a] -> [[a]]
splitBy p = unfoldr split
where split [] = Nothing
split xs = let ~(w,r) = break p xs in case r of
[] -> Just $ (w,[])
(_:rest) -> Just $ (w,rest)
choose :: Int -> [a] -> [[a]]
choose 0 _ = [[]]
choose _ [] = []
choose n (x:xs) = [x:xs' | xs' <- choose (n1) xs] ++ choose n xs
leaveOneOut :: [a] -> [[a]]
leaveOneOut xs =
zipWith (++) (map init . tail . inits $ xs) (map tail . init . tails $ xs)
keepFirst :: (a -> a -> Bool) -> [a] -> [a]
keepFirst _ [] = []
keepFirst mask (x:xs) = x : keepFirst mask (filter (not . mask x) xs)
swap :: (a, b) -> (b, a)
swap (x, y) = (y, x)
sortPair :: Ord a => (a,a) -> (a,a)
sortPair p@(x,y) | x <= y = p | otherwise = swap p
isRight :: Either a b -> Bool
isRight (Right _) = True
isRight _ = False
isLeft :: Either a b -> Bool
isLeft (Left _) = True
isLeft _ = False
type Named a = (String, a)
flushRightBy :: [a] -> Int -> [a] -> [a]
flushRightBy sep n str = take (max 0 (n length str)) (cycle sep) ++ str
flushRight :: Int -> String -> String
flushRight = flushRightBy " "
flushLeftBy :: [a] -> Int -> [a] -> [a]
flushLeftBy sep n str = str ++ take (max 0 (n length str)) (cycle sep)
flushLeft :: Int -> String -> String
flushLeft = flushLeftBy " "
warning :: String -> String
warning s = "warning: "++s
putErr :: String -> IO ()
putErr = hPutStr stderr
putErrLn :: String -> IO ()
putErrLn = hPutStrLn stderr
oneOfList :: Alternative f => [a] -> f a
oneOfList = asum . map pure
oneOfSet :: Alternative f => S.Set a -> f a
oneOfSet = oneOfList . S.toList
oneOfMap :: Alternative f => M.Map k v -> f (k, v)
oneOfMap = oneOfList . M.toList
ifM :: Monad m => m Bool -> m a -> m a -> m a
ifM p pos neg = do
b <- p
if b then pos else neg
errorFree :: MonadPlus m => [m a] -> m [a]
errorFree ms =
catMaybes `liftM` sequence [(Just `liftM` m) `mplus` return Nothing | m <- ms]
errorFree1 :: MonadPlus m => [m a] -> m [a]
errorFree1 ms = do
ms' <- errorFree ms
if null ms' then mzero else return ms'
unreachable :: String -> a
unreachable location =
error $ "reached the 'unreachable' code in " ++ location