module Util.HasSize where

-- this point of this module is not only to share the 'size' syntax, but to
-- provide optimally lazy versions of size comparasin functions when dealing
-- with lazy structures. This is especially useful when having to compare the
-- size of possibly long lists.

-- it is up to each instance to decide what 'size' means

import qualified Data.Map(Map,size,null)
import qualified Data.Set(Set,size,null)
import qualified Data.IntMap(IntMap,size,null)
import qualified Data.IntSet(IntSet,size,null)


class IsEmpty a where
    isEmpty :: a -> Bool

class HasSize a where
    size :: a -> Int
    sizeEQ :: Int -> a -> Bool
    sizeGT :: Int -> a -> Bool
    sizeLT :: Int -> a -> Bool
    sizeGTE :: Int -> a -> Bool
    sizeLTE :: Int -> a -> Bool
    sizeEQ s x = size x == s
    sizeGT s x = size x > s
    sizeLT s x = size x < s
    sizeGTE s x = not $ sizeLT s x
    sizeLTE s x = not $ sizeGT s x

genSize :: (Integral b,HasSize a) => a -> b
genSize = fromIntegral . Util.HasSize.size

instance HasSize [x] where
    size = length
    sizeEQ n _ | n < 0 = False
    sizeEQ n xs = f n xs where
        f 0 [] = True
        f _ [] = False
        f 0 _ = False
        f n (_:xs) = sizeEQ (n - 1) xs
    sizeGT n _ | n < 0 = True
    sizeGT n xs = f n xs where
        f 0 (_:_) = True
        f n [] = False
        f n (_:xs) = f (n - 1) xs
    sizeLT n _ | n <= 0 = False
    sizeLT n xs = f n xs where
        f 0 _ = False
        f _ [] = True
        f n (_:xs) = f (n - 1) xs


instance HasSize (Data.Map.Map a b) where
    size = Data.Map.size
instance HasSize (Data.Set.Set a) where
    size = Data.Set.size
instance HasSize (Data.IntMap.IntMap v) where
    size = Data.IntMap.size
instance HasSize Data.IntSet.IntSet where
    size = Data.IntSet.size

instance (HasSize a,HasSize b) => HasSize (Either a b) where
    size (Left x) = size x
    size (Right y) = size y
    sizeEQ s (Left x)  = sizeEQ s x
    sizeEQ s (Right x)  = sizeEQ s x
    sizeLT s (Left x)  = sizeLT s x
    sizeLT s (Right x)  = sizeLT s x
    sizeGT s (Left x)  = sizeGT s x
    sizeGT s (Right x)  = sizeGT s x

instance (HasSize a,HasSize b) => HasSize (a,b) where
    size (x,y) = size x + size y
instance (HasSize a,HasSize b,HasSize c) => HasSize (a,b,c) where
    size (x,y,z) = size x + size y  + size z

instance IsEmpty [x] where
    isEmpty = null

instance IsEmpty (Data.Map.Map a b) where
    isEmpty = Data.Map.null
instance IsEmpty (Data.Set.Set a) where
    isEmpty = Data.Set.null
instance IsEmpty (Data.IntMap.IntMap v) where
    isEmpty = Data.IntMap.null
instance IsEmpty Data.IntSet.IntSet where
    isEmpty = Data.IntSet.null

instance (IsEmpty a,IsEmpty b) => IsEmpty (a,b) where
    isEmpty (x,y) = isEmpty x && isEmpty y
instance (IsEmpty a,IsEmpty b,IsEmpty c) => IsEmpty (a,b,c) where
    isEmpty (x,y,z) = isEmpty x && isEmpty y  && isEmpty z