{-# language DeriveTraversable #-}
{-# language ScopedTypeVariables #-}
{-# language BangPatterns #-}
{-# language MagicHash #-}
{-# language UnboxedTuples #-}
{-# language PatternSynonyms #-}
{-# language ViewPatterns #-}
{-# language Trustworthy #-}
{-# language TypeFamilies #-}
{-# language FlexibleContexts #-}
{-# language LambdaCase #-}
{- OPTIONS_GHC -Wall #-}
{- OPTIONS_GHC -ddump-simpl #-}

{- |
Space-efficient deques with amortized \( O(\log n) \) operations.  These
directly use an underlying array-based implementation, without doing any
special optimization for the first few and last few elements of the deque.
-}

module Data.CompactSequence.Deque.Simple.Internal
  ( Deque (.., Empty, (:<), (:>))
  , (|>)
  , (<|)
  , empty
  , cons
  , snoc
  , uncons
  , unsnoc
--  , take
  , fromList
  , fromListN
  ) where

import qualified Data.CompactSequence.Deque.Internal as D
import qualified Data.CompactSequence.Internal.Array as A
import qualified Data.CompactSequence.Internal.Size as Sz
import Data.CompactSequence.Internal.Size (Size)
import qualified Data.CompactSequence.Internal.Numbers as N
import qualified Data.Foldable as F
import qualified GHC.Exts as Exts
import Control.Monad.State.Strict
import qualified Prelude as P
import Prelude hiding (take)

-- | A deque.
newtype Deque a = Deque (D.Deque Sz.Sz1 a)
  deriving (Functor, Traversable, Eq, Ord)

-- | The empty deque.
empty :: Deque a
empty = Deque D.empty

-- | Enqueue an element at the front of a deque.
cons :: a -> Deque a -> Deque a
cons a (Deque q) = Deque $ D.consA Sz.one (A.singleton a) q

-- | Enqueue an element at the rear of a deque.
snoc :: Deque a -> a -> Deque a
snoc (Deque q) a = Deque $ D.snocA Sz.one q (A.singleton a)

-- | An infix synonym for 'snoc'.
(|>) :: Deque a -> a -> Deque a
(|>) = snoc

-- | An infix synonym for 'cons'.
(<|) :: a -> Deque a -> Deque a
(<|) = cons

-- | Dequeue an element from the front of a deque.
uncons :: Deque a -> Maybe (a, Deque a)
uncons (Deque q) = case D.viewLA Sz.one q of
  D.EmptyL -> Nothing
  D.ConsL sa q'
    | (# a #) <- A.getSingleton# sa
    -> Just (a, Deque q')

-- | Dequeue an element from the rear of a deque.
unsnoc :: Deque a -> Maybe (Deque a, a)
unsnoc (Deque q) = case D.viewRA Sz.one q of
  D.EmptyR -> Nothing
  D.SnocR q' ta
    | (# a #) <- A.getSingleton# ta
    -> Just (Deque q', a)

infixr 5 :<, `cons`
infixl 4 `snoc`, |>

-- | A bidirectional pattern synonym for manipulating the
-- front of a deque.
pattern (:<) :: a -> Deque a -> Deque a
pattern x :< xs <- (uncons -> Just (x, xs))
  where
    x :< xs = x `cons` xs

-- | A bidirectional pattern synonym for manipulating the
-- rear of a deque.
pattern (:>) :: Deque a -> a -> Deque a
pattern xs :> x <- (unsnoc -> Just (xs, x))
  where
    xs :> x = xs `snoc` x

-- | A bidirectional pattern synonym for the empty deque.
pattern Empty :: Deque a
pattern Empty = Deque D.Empty
{-# COMPLETE (:<), Empty #-}
{-# COMPLETE (:>), Empty #-}

instance Foldable Deque where
  -- TODO: Implement more methods?
  foldMap f (Deque q) = foldMap f q
  foldr c n (Deque q) = foldr c n q
  foldr' c n (Deque q) = F.foldr' c n q
  foldl f b (Deque q) = foldl f b q
  foldl' f b (Deque q) = F.foldl' f b q

  null (Deque D.Empty) = True
  null _ = False

  -- Note: length only does O(log n) *unshared* work, but it does O(n) amortized
  -- work because it has to force the entire spine. We could avoid
  -- this, of course, by storing the size with the dequeue.
  length (Deque q) = go 0 Sz.one q
    where
      go :: Int -> Size m -> D.Deque m a -> Int
      go !acc !_s D.Empty = acc
      go !acc !s (D.Shallow _) = acc + Sz.getSize s
      go !acc !s (D.Deep pr m sf) = go (acc + ld pr + ld sf) (Sz.twice s) m
        where
          ld = \case
                  D.One{} -> Sz.getSize s
                  D.Two{} -> 2*Sz.getSize s
                  D.Three{} -> 3*Sz.getSize s
                  D.Four{} -> 4*Sz.getSize s

instance Show a => Show (Deque a) where
    showsPrec p xs = showParen (p > 10) $
        showString "fromList " . shows (F.toList xs)

instance Exts.IsList (Deque a) where
  type Item (Deque a) = a
  toList = F.toList
  fromList = fromList
  fromListN = fromListN

instance Semigroup (Deque a) where
  -- This gives us O(m + n) append. Can we do better?
  -- I suspect O(min(m,n)) might be possible.
  Empty <> q = q
  q <> Empty = q
  q <> r = fromListN (length q + length r) (F.toList q ++ F.toList r)

instance Monoid (Deque a) where
  mempty = empty

-- | \( O(n \log n) \). Convert a list to a 'Deque', with the head of the
-- list at the front of the deque.
fromList :: [a] -> Deque a
fromList = F.foldl' snoc empty

-- | \( O(n) \). Convert a list of the given size to a 'Deque', with the
-- head of the list at the front of the deque.
fromListN :: Int -> [a] -> Deque a
fromListN n xs
  = Deque $ evalState (D.fromListNM Sz.one n) xs