    -- |
    -- Module      : Data.Vector.Fusion.Stream.Size
    -- Copyright   : (c) Roman Leshchinskiy 2008-2010
    -- License     : BSD-style
    --
    -- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
    -- Stability   : experimental
    -- Portability : portable
    --
    -- Size hints for streams.
    --

    module Data.Vector.Fusion.Stream.Size (
      Size(..), smaller, larger, toMax, upperBound
    ) where

    import Data.Vector.Fusion.Util ( delay_inline )

    -- | Size hint
    data Size = Exact Int          -- ^ Exact size
              | Max   Int          -- ^ Upper bound on the size
              | Unknown            -- ^ Unknown size
            deriving( Eq, Show )

    instance Num Size where
      Exact m + Exact n = Exact (m+n)
      Exact m + Max   n = Max   (m+n)

      Max   m + Exact n = Max   (m+n)
      Max   m + Max   n = Max   (m+n)

      _       + _       = Unknown

      Exact m - Exact n = Exact (m-n)
      Exact m - Max   n = Max   m

      Max   m - Exact n = Max   (m-n)
      Max   m - Max   n = Max   m
      Max   m - Unknown = Max   m

      _       - _       = Unknown

      fromInteger n     = Exact (fromInteger n)

    -- | Minimum of two size hints
    smaller :: Size -> Size -> Size
    {-# INLINE smaller #-}
    smaller (Exact m) (Exact n) = Exact (delay_inline min m n)
    smaller (Exact m) (Max   n) = Max   (delay_inline min m n)
    smaller (Exact m) Unknown   = Max   m
    smaller (Max   m) (Exact n) = Max   (delay_inline min m n)
    smaller (Max   m) (Max   n) = Max   (delay_inline min m n)
    smaller (Max   m) Unknown   = Max   m
    smaller Unknown   (Exact n) = Max   n
    smaller Unknown   (Max   n) = Max   n
    smaller Unknown   Unknown   = Unknown

    -- | Maximum of two size hints
    larger :: Size -> Size -> Size
    {-# INLINE larger #-}
    larger (Exact m) (Exact n)             = Exact (delay_inline max m n)
    larger (Exact m) (Max   n) | m >= n    = Exact m
                               | otherwise = Max   n
    larger (Max   m) (Exact n) | n >= m    = Exact n
                               | otherwise = Max   m
    larger (Max   m) (Max   n)             = Max   (delay_inline max m n)
    larger _         _                     = Unknown

    -- | Convert a size hint to an upper bound
    toMax :: Size -> Size
    toMax (Exact n) = Max n
    toMax (Max   n) = Max n
    toMax Unknown   = Unknown

    -- | Compute the minimum size from a size hint
    lowerBound :: Size -> Int
    lowerBound (Exact n) = n
    lowerBound _         = 0

    -- | Compute the maximum size from a size hint if possible
    upperBound :: Size -> Maybe Int
    upperBound (Exact n) = Just n
    upperBound (Max   n) = Just n
    upperBound Unknown   = Nothing