{-# LANGUAGE CPP, StandaloneDeriving #-}

-------------------------------------------------------------------------------------------
--- Run length encoded list, to be used as an identification of scope in UHC
-------------------------------------------------------------------------------------------

module UHC.Util.RLList
  ( -- * Run length list
    RLList(..)
  , concat, singleton, empty, toList, fromList
  
    -- * Predicates, observations
  , length, null
  , isPrefixOf
  
    -- * Misc
  , inits, init, initLast
  , headTail
  )
  where

import           Prelude hiding (length, init, null, concat)
import qualified Prelude as P
import           Data.Maybe
import qualified Data.List as L
import           Data.List hiding (concat, init, null, isPrefixOf, length, inits)
import           Control.Monad
import           UHC.Util.Utils
import           UHC.Util.Binary
import           UHC.Util.Serialize

-------------------------------------------------------------------------------------------
--- Run length encoded list
-------------------------------------------------------------------------------------------

newtype RLList a
  = RLList { unRLList :: [(a,Int)] }
  deriving (Eq)

instance Ord a => Ord (RLList a) where
  (RLList [])           `compare` (RLList [])           = EQ
  (RLList [])           `compare` (RLList _ )           = LT
  (RLList _ )           `compare` (RLList [])           = GT
  (RLList ((x1,c1):l1)) `compare` (RLList ((x2,c2):l2)) | x1 == x2 = if c1 == c2
                                                                     then RLList l1 `compare` RLList l2
                                                                     else c1 `compare` c2
                                                        | x1 <  x2 = LT
                                                        | x1 >  x2 = GT

instance Show a => Show (RLList a) where
  show = show . toList

concat :: Eq a => RLList a -> RLList a -> RLList a
concat (RLList []) rll2  = rll2
concat rll1 (RLList [])  = rll1
concat (RLList l1) (RLList l2@(h2@(x2,c2):t2))
                            | x1 == x2  = RLList (h1 ++ [(x1,c1+c2)] ++ t2)
                            | otherwise = RLList (l1 ++ l2)
                            where (h1,t1@(x1,c1)) = fromJust (initlast l1)

empty :: RLList a
empty = RLList []

singleton :: a -> RLList a
singleton x = RLList [(x,1)]

toList :: RLList a -> [a]
toList (RLList l) = concatMap (\(x,c) -> replicate c x) l

fromList :: Eq a => [a] -> RLList a
fromList l = RLList [ (x,L.length g) | g@(x:_) <- group l ]

length :: RLList a -> Int
length (RLList l) = sum $ map snd l

null :: RLList a -> Bool
null (RLList []) = True
null (RLList _ ) = False

isPrefixOf :: Eq a => RLList a -> RLList a -> Bool
isPrefixOf (RLList []) _ = True
isPrefixOf _ (RLList []) = False
isPrefixOf (RLList ((x1,c1):l1)) (RLList ((x2,c2):l2))
                            | x1 == x2  = if c1 < c2
                                          then True
                                          else if c1 > c2
                                          then False
                                          else isPrefixOf (RLList l1) (RLList l2)
                            | otherwise = False

-------------------------------------------------------------------------------------------
--- Misc
-------------------------------------------------------------------------------------------

initLast :: Eq a => RLList a -> Maybe (RLList a,a)
initLast (RLList l ) = il [] l
                        where il acc [(x,1)]    = Just (RLList (reverse acc),x)
                              il acc [(x,c)]    = Just (RLList (reverse ((x,c-1):acc)),x)
                              il acc (a:as)     = il (a:acc) as
                              il _   _          = Nothing

init :: Eq a => RLList a -> RLList a
init = fst . fromJust . initLast

inits :: Eq a => RLList a -> [RLList a]
inits = map fromList . L.inits . toList

headTail :: RLList a -> Maybe (a,RLList a)
headTail (RLList [])        = Nothing
headTail (RLList ((x,1):t)) = Just (x,RLList t)
headTail (RLList ((x,c):t)) = Just (x,RLList ((x,c-1):t))

-------------------------------------------------------------------------------------------
--- Instances: Typeable, Data
-------------------------------------------------------------------------------------------

#if __GLASGOW_HASKELL__ >= 708
deriving instance Typeable  RLList
#else
deriving instance Typeable1 RLList
#endif
-- deriving instance Data x => Data (RLList x)

-------------------------------------------------------------------------------------------
--- Instances: Binary, Serialize
-------------------------------------------------------------------------------------------

instance Binary a => Binary (RLList a) where
  put (RLList a) = put a
  get = liftM RLList get