module HERMIT.Utilities
    ( -- * Utilities
      nodups
    , dups
    , dupsBy
    , soleElement
    , equivalentBy
    , equivalent
    , whenJust
    , maybeM
    ) where

------------------------------------------------------------------------------

-- | Determine if a list contains no duplicated elements.
nodups :: Eq a => [a] -> Bool
nodups []     = True
nodups (a:as) = (a `notElem` as) && nodups as

-- | Generalisation of 'dups' to an arbitrary equality predicate.
dupsBy :: (a -> a -> Bool) -> [a] -> [a]
dupsBy _ []     = []
dupsBy p (a:as) = let ds = dupsBy p as
                   in if any (p a) as
                        then a : ds
                        else ds

-- | Discard the last occurrence of each element in the list.  Thus the returned list contains only the duplicated elements.
dups :: Eq a => [a] -> [a]
dups = dupsBy (==)

soleElement :: Monad m => [a] -> m a
soleElement []  = fail "soleElement: list is empty."
soleElement [a] = return a
soleElement _   = fail "soleElement: multiple elements found."

------------------------------------------------------------------------------

-- Drew: surely this exists generally somewhere?
-- for instance:
--      equivalentBy ((==) `on` length) :: [[a]] -> Bool
-- checks if all lists have the same length

-- | A generalisation of 'equivalent' to any equivalence relation.
--   @equivalent = equivalentBy (==)@
equivalentBy :: (a -> a -> Bool) -> [a] -> Bool
equivalentBy _  []     = True
equivalentBy eq (x:xs) = all (eq x) xs

-- | Determine if all elements of a list are equal.
equivalent :: Eq a => [a] -> Bool
equivalent = equivalentBy (==)

------------------------------------------------------------------------------

-- | Perform the monadic action only in the 'Just' case.
whenJust :: Monad m => (a -> m ()) -> Maybe a -> m ()
whenJust f = maybe (return ()) f

-- | Lift a 'Maybe' into an arbitrary monad, using 'return' or 'fail'.
maybeM :: Monad m => String -> Maybe a -> m a
maybeM msg = maybe (fail msg) return

------------------------------------------------------------------------------