module Data.Text.Internal.Fusion.Size
(
Size
, exactly
, exactSize
, maxSize
, betweenSize
, unknownSize
, smaller
, larger
, upperBound
, lowerBound
, compareSize
, isEmpty
) where
#if defined(ASSERTS)
import Control.Exception (assert)
#endif
data Size = Between !Int !Int
| Unknown
deriving (Eq, Show)
exactly :: Size -> Maybe Int
exactly (Between na nb) | na == nb = Just na
exactly _ = Nothing
exactSize :: Int -> Size
exactSize n =
#if defined(ASSERTS)
assert (n >= 0)
#endif
Between n n
maxSize :: Int -> Size
maxSize n =
#if defined(ASSERTS)
assert (n >= 0)
#endif
Between 0 n
betweenSize :: Int -> Int -> Size
betweenSize m n =
#if defined(ASSERTS)
assert (m >= 0)
assert (n >= m)
#endif
Between m n
unknownSize :: Size
unknownSize = Unknown
instance Num Size where
(+) = addSize
() = subtractSize
(*) = mulSize
fromInteger = f where f = exactSize . fromInteger
add :: Int -> Int -> Int
add m n | mn >= 0 = mn
| otherwise = overflowError
where mn = m + n
addSize :: Size -> Size -> Size
addSize (Between ma mb) (Between na nb) = Between (add ma na) (add mb nb)
addSize _ _ = Unknown
subtractSize :: Size -> Size -> Size
subtractSize (Between ma mb) (Between na nb) = Between (max (manb) 0) (max (mbna) 0)
subtractSize a@(Between 0 _) Unknown = a
subtractSize (Between _ mb) Unknown = Between 0 mb
subtractSize _ _ = Unknown
mul :: Int -> Int -> Int
mul m n
| m <= maxBound `quot` n = m * n
| otherwise = overflowError
mulSize :: Size -> Size -> Size
mulSize (Between ma mb) (Between na nb) = Between (mul ma na) (mul mb nb)
mulSize _ _ = Unknown
smaller :: Size -> Size -> Size
smaller a@(Between ma mb) b@(Between na nb)
| mb <= na = a
| nb <= ma = b
| otherwise = Between (ma `min` na) (mb `min` nb)
smaller a@(Between 0 _) Unknown = a
smaller (Between _ mb) Unknown = Between 0 mb
smaller Unknown b@(Between 0 _) = b
smaller Unknown (Between _ nb) = Between 0 nb
smaller Unknown Unknown = Unknown
larger :: Size -> Size -> Size
larger a@(Between ma mb) b@(Between na nb)
| ma >= nb = a
| na >= mb = b
| otherwise = Between (ma `max` na) (mb `max` nb)
larger _ _ = Unknown
upperBound :: Int -> Size -> Int
upperBound _ (Between _ n) = n
upperBound k _ = k
lowerBound :: Int -> Size -> Int
lowerBound _ (Between n _) = n
lowerBound k _ = k
compareSize :: Size -> Int -> Maybe Ordering
compareSize (Between ma mb) n
| mb < n = Just LT
| ma > n = Just GT
| ma == n && mb == n = Just EQ
compareSize _ _ = Nothing
isEmpty :: Size -> Bool
isEmpty (Between _ n) = n <= 0
isEmpty _ = False
overflowError :: Int
overflowError = error "Data.Text.Internal.Fusion.Size: size overflow"