{-# LANGUAGE DataKinds                 #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE KindSignatures            #-}
{-# LANGUAGE BangPatterns              #-}
{-# LANGUAGE MultiParamTypeClasses     #-}
{-# LANGUAGE FlexibleInstances         #-}

-- | Erase the size parameter on a size-indexed heap, using existentials.
module Data.Queue.Indexed.Erased
  (ErasedSize(..))
  where

import           GHC.TypeLits

import           Data.Queue.Class
import           Data.Queue.Indexed.Class (IndexedQueue, MeldableIndexedQueue)
import qualified Data.Queue.Indexed.Class as Indexed

-- | This type contains a size-indexed heap, however the size index is
-- hidden. This allows it to act like a standard heap, while maintaining
-- the proven invariants of the size-indexed version.
data ErasedSize f a = forall (n :: Nat). ErasedSize
    { runErasedSize :: f n a
    }

instance IndexedQueue h a =>
         Queue (ErasedSize h) a where
    insert x (ErasedSize xs) = ErasedSize (Indexed.insert x xs)
    empty = ErasedSize Indexed.empty
    minView (ErasedSize xs) =
        Indexed.minViewMay
            xs
            Nothing
            (\y ys ->
                  Just (y, ErasedSize ys))
    fromList = go Indexed.empty
      where
        go
            :: forall h n a.
               (IndexedQueue h a)
            => h n a -> [a] -> ErasedSize h a
        go !h [] = ErasedSize h
        go !h (x:xs) = go (Indexed.insert x h) xs


instance MeldableIndexedQueue h a => MeldableQueue (ErasedSize h) a where
    merge (ErasedSize xs) (ErasedSize ys) = ErasedSize (Indexed.merge xs ys)