    {-# LANGUAGE CPP #-}
    {-# OPTIONS_GHC -fno-warn-missing-methods #-}
    -- |
    -- Module      : Data.Text.Internal.Fusion.Internal
    -- Copyright   : (c) Roman Leshchinskiy 2008,
    --               (c) Bryan O'Sullivan 2009
    --
    -- License     : BSD-style
    -- Maintainer  : bos@serpentine.com
    -- Stability   : experimental
    -- Portability : portable
    --
    -- /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!
    --
    -- Size hints.

    module Data.Text.Internal.Fusion.Size
        (
          Size
          -- * Sizes
        , exactSize
        , maxSize
        , betweenSize
        , unknownSize
        , unionSize
        , charSize
        , codePointsSize
          -- * Querying sizes
        , exactly
        , smaller
        , larger
        , upperBound
        , lowerBound
        , compareSize
        , isEmpty
        ) where

    import Data.Text.Internal.Encoding.Utf8 (utf8Length)
    import Data.Text.Internal (mul)
    #if defined(ASSERTS)
    import Control.Exception (assert)
    #endif

    -- | A size in UTF-8 code units (which is bytes).
    data Size = Between {-# UNPACK #-} !Int {-# UNPACK #-} !Int -- ^ Lower and upper bounds on size.
              | Unknown                                         -- ^ Unknown size.
                deriving (Size -> Size -> Bool
    (Size -> Size -> Bool) -> (Size -> Size -> Bool) -> Eq Size
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: Size -> Size -> Bool
    == :: Size -> Size -> Bool
    $c/= :: Size -> Size -> Bool
    /= :: Size -> Size -> Bool
    Eq, Int -> Size -> ShowS
    [Size] -> ShowS
    Size -> String
    (Int -> Size -> ShowS)
    -> (Size -> String) -> ([Size] -> ShowS) -> Show Size
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> Size -> ShowS
    showsPrec :: Int -> Size -> ShowS
    $cshow :: Size -> String
    show :: Size -> String
    $cshowList :: [Size] -> ShowS
    showList :: [Size] -> ShowS
    Show)

    exactly :: Size -> Maybe Int
    exactly :: Size -> Maybe Int
    exactly (Between Int
    na Int
    nb) | Int
    na Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    nb = Int -> Maybe Int
    forall a. a -> Maybe a
    Just Int
    na
    exactly Size
    _ = Maybe Int
    forall a. Maybe a
    Nothing
    {-# INLINE exactly #-}

    -- | The 'Size' of the given code point.
    charSize :: Char -> Size
    charSize :: Char -> Size
    charSize = Int -> Size
    exactSize (Int -> Size) -> (Char -> Int) -> Char -> Size
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Char -> Int
    utf8Length

    -- | The 'Size' of @n@ code points.
    codePointsSize :: Int -> Size
    codePointsSize :: Int -> Size
    codePointsSize Int
    n =
    #if defined(ASSERTS)
        assert (n >= 0)
    #endif
        Int -> Int -> Size
    Between Int
    n (Int
    4Int -> Int -> Int
    forall a. Num a => a -> a -> a
    *Int
    n)
    {-# INLINE codePointsSize #-}

    exactSize :: Int -> Size
    exactSize :: Int -> Size
    exactSize Int
    n =
    #if defined(ASSERTS)
        assert (n >= 0)
    #endif
        Int -> Int -> Size
    Between Int
    n Int
    n
    {-# INLINE exactSize #-}

    maxSize :: Int -> Size
    maxSize :: Int -> Size
    maxSize Int
    n =
    #if defined(ASSERTS)
        assert (n >= 0)
    #endif
        Int -> Int -> Size
    Between Int
    0 Int
    n
    {-# INLINE maxSize #-}

    betweenSize :: Int -> Int -> Size
    betweenSize :: Int -> Int -> Size
    betweenSize Int
    m Int
    n =
    #if defined(ASSERTS)
        assert (m >= 0)
        assert (n >= m)
    #endif
        Int -> Int -> Size
    Between Int
    m Int
    n
    {-# INLINE betweenSize #-}

    unionSize :: Size -> Size -> Size
    unionSize :: Size -> Size -> Size
    unionSize (Between Int
    a Int
    b) (Between Int
    c Int
    d) = Int -> Int -> Size
    Between (Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    min Int
    a Int
    c) (Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    max Int
    b Int
    d)
    unionSize Size
    _ Size
    _ = Size
    Unknown

    unknownSize :: Size
    unknownSize :: Size
    unknownSize = Size
    Unknown
    {-# INLINE unknownSize #-}

    instance Num Size where
        + :: Size -> Size -> Size
    (+) = Size -> Size -> Size
    addSize
        (-) = Size -> Size -> Size
    subtractSize
        * :: Size -> Size -> Size
    (*) = Size -> Size -> Size
    mulSize

        fromInteger :: Integer -> Size
    fromInteger = Integer -> Size
    f where f :: Integer -> Size
    f = Int -> Size
    exactSize (Int -> Size) -> (Integer -> Int) -> Integer -> Size
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Integer -> Int
    forall a. Num a => Integer -> a
    fromInteger
                              {-# INLINE f #-}

    add :: Int -> Int -> Int
    add :: Int -> Int -> Int
    add Int
    m Int
    n | Int
    mn Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >=   Int
    0 = Int
    mn
            | Bool
    otherwise = Int
    overflowError
      where mn :: Int
    mn = Int
    m Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    n
    {-# INLINE add #-}

    addSize :: Size -> Size -> Size
    addSize :: Size -> Size -> Size
    addSize (Between Int
    ma Int
    mb) (Between Int
    na Int
    nb) = Int -> Int -> Size
    Between (Int -> Int -> Int
    add Int
    ma Int
    na) (Int -> Int -> Int
    add Int
    mb Int
    nb)
    addSize Size
    _               Size
    _               = Size
    Unknown
    {-# INLINE addSize #-}

    subtractSize :: Size -> Size -> Size
    subtractSize :: Size -> Size -> Size
    subtractSize (Between Int
    ma Int
    mb) (Between Int
    na Int
    nb) = Int -> Int -> Size
    Between (Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    max (Int
    maInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    nb) Int
    0) (Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    max (Int
    mbInt -> Int -> Int
    forall a. Num a => a -> a -> a
    -Int
    na) Int
    0)
    subtractSize a :: Size
    a@(Between Int
    0 Int
    _) Size
    Unknown         = Size
    a
    subtractSize (Between Int
    _ Int
    mb)  Size
    Unknown         = Int -> Int -> Size
    Between Int
    0 Int
    mb
    subtractSize Size
    _               Size
    _               = Size
    Unknown
    {-# INLINE subtractSize #-}

    mulSize :: Size -> Size -> Size
    mulSize :: Size -> Size -> Size
    mulSize (Between Int
    ma Int
    mb) (Between Int
    na Int
    nb) = Int -> Int -> Size
    Between (Int -> Int -> Int
    mul Int
    ma Int
    na) (Int -> Int -> Int
    mul Int
    mb Int
    nb)
    mulSize Size
    _               Size
    _               = Size
    Unknown
    {-# INLINE mulSize #-}

    -- | Minimum of two size hints.
    smaller :: Size -> Size -> Size
    smaller :: Size -> Size -> Size
    smaller a :: Size
    a@(Between Int
    ma Int
    mb) b :: Size
    b@(Between Int
    na Int
    nb)
        | Int
    mb Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    na  = Size
    a
        | Int
    nb Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    ma  = Size
    b
        | Bool
    otherwise = Int -> Int -> Size
    Between (Int
    ma Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    `min` Int
    na) (Int
    mb Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    `min` Int
    nb)
    smaller a :: Size
    a@(Between Int
    0 Int
    _) Size
    Unknown         = Size
    a
    smaller (Between Int
    _ Int
    mb)  Size
    Unknown         = Int -> Int -> Size
    Between Int
    0 Int
    mb
    smaller Size
    Unknown         b :: Size
    b@(Between Int
    0 Int
    _) = Size
    b
    smaller Size
    Unknown         (Between Int
    _ Int
    nb)  = Int -> Int -> Size
    Between Int
    0 Int
    nb
    smaller Size
    Unknown         Size
    Unknown         = Size
    Unknown
    {-# INLINE smaller #-}

    -- | Maximum of two size hints.
    larger :: Size -> Size -> Size
    larger :: Size -> Size -> Size
    larger a :: Size
    a@(Between Int
    ma Int
    mb) b :: Size
    b@(Between Int
    na Int
    nb)
        | Int
    ma Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int
    nb  = Size
    a
        | Int
    na Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int
    mb  = Size
    b
        | Bool
    otherwise = Int -> Int -> Size
    Between (Int
    ma Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    `max` Int
    na) (Int
    mb Int -> Int -> Int
    forall a. Ord a => a -> a -> a
    `max` Int
    nb)
    larger Size
    _ Size
    _ = Size
    Unknown
    {-# INLINE larger #-}

    -- | Compute the maximum size from a size hint, if possible.
    upperBound :: Int -> Size -> Int
    upperBound :: Int -> Size -> Int
    upperBound Int
    _ (Between Int
    _ Int
    n) = Int
    n
    upperBound Int
    k Size
    _             = Int
    k
    {-# INLINE upperBound #-}

    -- | Compute the minimum size from a size hint, if possible.
    lowerBound :: Int -> Size -> Int
    lowerBound :: Int -> Size -> Int
    lowerBound Int
    _ (Between Int
    n Int
    _) = Int
    n
    lowerBound Int
    k Size
    _             = Int
    k
    {-# INLINE lowerBound #-}

    -- | Determine the ordering relationship between two 'Size's, or 'Nothing' in
    -- the indeterminate case.
    compareSize :: Size -> Size -> Maybe Ordering
    compareSize :: Size -> Size -> Maybe Ordering
    compareSize (Between Int
    ma Int
    mb) (Between Int
    na Int
    nb)
      | Int
    mb Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < Int
    na            = Ordering -> Maybe Ordering
    forall a. a -> Maybe a
    Just Ordering
    LT
      | Int
    ma Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    nb            = Ordering -> Maybe Ordering
    forall a. a -> Maybe a
    Just Ordering
    GT
      | Int
    ma Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    mb
      , Int
    ma Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    na
      , Int
    ma Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    nb           = Ordering -> Maybe Ordering
    forall a. a -> Maybe a
    Just Ordering
    EQ
    compareSize Size
    _ Size
    _        = Maybe Ordering
    forall a. Maybe a
    Nothing

    isEmpty :: Size -> Bool
    isEmpty :: Size -> Bool
    isEmpty (Between Int
    _ Int
    n) = Int
    n Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Int
    0
    isEmpty Size
    _             = Bool
    False
    {-# INLINE isEmpty #-}

    overflowError :: Int
    overflowError :: Int
    overflowError = String -> Int
    forall a. HasCallStack => String -> a
    error String
    "Data.Text.Internal.Fusion.Size: size overflow"