    {-# LANGUAGE BangPatterns, ExistentialQuantification #-}
    -- |
    -- Module      : Data.Text.Internal.Fusion.Types
    -- Copyright   : (c) Tom Harper 2008-2009,
    --               (c) Bryan O'Sullivan 2009,
    --               (c) Duncan Coutts 2009,
    --               (c) Jasper Van der Jeugt 2011
    --
    -- License     : BSD-style
    -- Maintainer  : bos@serpentine.com
    -- Stability   : experimental
    -- Portability : GHC
    --
    -- /Warning/: this is an internal module, and does not have a stable
    -- API or name. Functions in this module may not check or enforce
    -- preconditions expected by public modules. Use at your own risk!
    --
    -- Core stream fusion functionality for text.

    module Data.Text.Internal.Fusion.Types
        (
          CC(..)
        , PairS(..)
        , Scan(..)
        , RS(..)
        , Step(..)
        , Stream(..)
        , empty
        ) where

    import Data.Text.Internal.Fusion.Size
    import Data.Word (Word8)

    -- | Specialised tuple for case conversion.
    data CC s = CC !s {-# UNPACK #-} !Char {-# UNPACK #-} !Char

    -- | Restreaming state.
    data RS s
        = RS0 !s
        | RS1 !s {-# UNPACK #-} !Word8
        | RS2 !s {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8
        | RS3 !s {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8

    -- | Strict pair.
    data PairS a b = !a :*: !b
                     -- deriving (Eq, Ord, Show)
    infixl 2 :*:

    -- | An intermediate result in a scan.
    data Scan s = Scan1 {-# UNPACK #-} !Char !s
                | Scan2 {-# UNPACK #-} !Char !s

    -- | Intermediate result in a processing pipeline.
    data Step s a = Done
                  | Skip !s
                  | Yield !a !s

    {-
    instance (Show a) => Show (Step s a)
        where show Done        = "Done"
              show (Skip _)    = "Skip"
              show (Yield x _) = "Yield " ++ show x
    -}

    instance (Eq a) => Eq (Stream a) where
        == :: Stream a -> Stream a -> Bool
    (==) = Stream a -> Stream a -> Bool
    forall a. Eq a => Stream a -> Stream a -> Bool
    eq

    instance (Ord a) => Ord (Stream a) where
        compare :: Stream a -> Stream a -> Ordering
    compare = Stream a -> Stream a -> Ordering
    forall a. Ord a => Stream a -> Stream a -> Ordering
    cmp

    -- The length hint in a Stream has two roles.  If its value is zero,
    -- we trust it, and treat the stream as empty.  Otherwise, we treat it
    -- as a hint: it should usually be accurate, so we use it when
    -- unstreaming to decide what size array to allocate.  However, the
    -- unstreaming functions must be able to cope with the hint being too
    -- small or too large.
    --
    -- The size hint tries to track the UTF-16 code units in a stream,
    -- but often counts the number of code points instead.  It can easily
    -- undercount if, for instance, a transformed stream contains astral
    -- plane code points (those above 0x10000).

    data Stream a =
        forall s. Stream
        (s -> Step s a)             -- stepper function
        !s                          -- current state
        !Size                       -- size hint in code units

    -- | /O(n)/ Determines if two streams are equal.
    eq :: (Eq a) => Stream a -> Stream a -> Bool
    eq :: forall a. Eq a => Stream a -> Stream a -> Bool
    eq (Stream s -> Step s a
    next1 s
    s1 Size
    _) (Stream s -> Step s a
    next2 s
    s2 Size
    _) = Step s a -> Step s a -> Bool
    loop (s -> Step s a
    next1 s
    s1) (s -> Step s a
    next2 s
    s2)
        where
          loop :: Step s a -> Step s a -> Bool
    loop Step s a
    Done Step s a
    Done                     = Bool
    True
          loop (Skip s
    s1')     (Skip s
    s2')     = Step s a -> Step s a -> Bool
    loop (s -> Step s a
    next1 s
    s1') (s -> Step s a
    next2 s
    s2')
          loop (Skip s
    s1')     Step s a
    x2             = Step s a -> Step s a -> Bool
    loop (s -> Step s a
    next1 s
    s1') Step s a
    x2
          loop Step s a
    x1             (Skip s
    s2')     = Step s a -> Step s a -> Bool
    loop Step s a
    x1          (s -> Step s a
    next2 s
    s2')
          loop Step s a
    Done Step s a
    _                        = Bool
    False
          loop Step s a
    _    Step s a
    Done                     = Bool
    False
          loop (Yield a
    x1 s
    s1') (Yield a
    x2 s
    s2') = a
    x1 a -> a -> Bool
    forall a. Eq a => a -> a -> Bool
    == a
    x2 Bool -> Bool -> Bool
    &&
                                               Step s a -> Step s a -> Bool
    loop (s -> Step s a
    next1 s
    s1') (s -> Step s a
    next2 s
    s2')
    {-# INLINE [0] eq #-}

    cmp :: (Ord a) => Stream a -> Stream a -> Ordering
    cmp :: forall a. Ord a => Stream a -> Stream a -> Ordering
    cmp (Stream s -> Step s a
    next1 s
    s1 Size
    _) (Stream s -> Step s a
    next2 s
    s2 Size
    _) = Step s a -> Step s a -> Ordering
    loop (s -> Step s a
    next1 s
    s1) (s -> Step s a
    next2 s
    s2)
        where
          loop :: Step s a -> Step s a -> Ordering
    loop Step s a
    Done Step s a
    Done                     = Ordering
    EQ
          loop (Skip s
    s1')     (Skip s
    s2')     = Step s a -> Step s a -> Ordering
    loop (s -> Step s a
    next1 s
    s1') (s -> Step s a
    next2 s
    s2')
          loop (Skip s
    s1')     Step s a
    x2             = Step s a -> Step s a -> Ordering
    loop (s -> Step s a
    next1 s
    s1') Step s a
    x2
          loop Step s a
    x1             (Skip s
    s2')     = Step s a -> Step s a -> Ordering
    loop Step s a
    x1          (s -> Step s a
    next2 s
    s2')
          loop Step s a
    Done Step s a
    _                        = Ordering
    LT
          loop Step s a
    _    Step s a
    Done                     = Ordering
    GT
          loop (Yield a
    x1 s
    s1') (Yield a
    x2 s
    s2') =
              case a -> a -> Ordering
    forall a. Ord a => a -> a -> Ordering
    compare a
    x1 a
    x2 of
                Ordering
    EQ    -> Step s a -> Step s a -> Ordering
    loop (s -> Step s a
    next1 s
    s1') (s -> Step s a
    next2 s
    s2')
                Ordering
    other -> Ordering
    other
    {-# INLINE [0] cmp #-}

    -- | The empty stream.
    empty :: Stream a
    empty :: forall a. Stream a
    empty = (() -> Step () a) -> () -> Size -> Stream a
    forall a s. (s -> Step s a) -> s -> Size -> Stream a
    Stream () -> Step () a
    forall {p} {s} {a}. p -> Step s a
    next () Size
    0
        where next :: p -> Step s a
    next p
    _ = Step s a
    forall s a. Step s a
    Done
    {-# INLINE [0] empty #-}