{-# LANGUAGE CPP         #-}
{-# LANGUAGE Trustworthy #-}

-- | Utility functions to work with lists.

module List
       ( list
       , ordNub
       , sortBy
       , sortOn
       , sortWith
       , unzip
       , unzip3
#if ( __GLASGOW_HASKELL__ >= 800 )
       , whenNotNull
       , whenNotNullM
#endif
       , zip
       , zip3
       ) where

import           Data.Functor        (fmap)
import           Data.List           (sortBy, sortOn, unzip, unzip3, zip, zip3)
import           Data.Ord            (Ord)
import qualified Data.Set            as Set
import           GHC.Exts            (sortWith)

#if ( __GLASGOW_HASKELL__ >= 800 )
import           Control.Applicative (Applicative)
import           Control.Monad       (Monad (..))
import           Data.List.NonEmpty  as X (NonEmpty (..))

import           Applicative         (pass)
#endif

-- | Like 'Prelude.nub' but runs in @O(n * log n)@ time and requires 'Ord'.
ordNub :: (Ord a) => [a] -> [a]
ordNub l = go Set.empty l
  where
    go _ []     = []
    go s (x:xs) =
      if x `Set.member` s
      then go s xs
      else x : go (Set.insert x s) xs

-- | Returns default list if given list is empty.
-- Otherwise applies given function to every element.
--
-- >>> list [True] even []
-- [True]
-- >>> list [True] even [1..5]
-- [False,True,False,True,False]
list :: [b] -> (a -> b) -> [a] -> [b]
list def f xs = case xs of
    [] -> def
    _  -> fmap f xs

#if ( __GLASGOW_HASKELL__ >= 800 )
-- | Performs given action over 'NonEmpty' list if given list is non empty.
whenNotNull :: Applicative f => [a] -> (NonEmpty a -> f ()) -> f ()
whenNotNull []     _ = pass
whenNotNull (x:xs) f = f (x :| xs)
{-# INLINE whenNotNull #-}

-- | Monadic version of 'whenNotNull'.
whenNotNullM :: Monad m => m [a] -> (NonEmpty a -> m ()) -> m ()
whenNotNullM ml f = ml >>= \l -> whenNotNull l f
{-# INLINE whenNotNullM #-}
#endif