module Data.UpperBoundedPred
    ( UpperBoundedPred
    , applyUpperBoundedPred

    , OC (..)
    , comparator

    , Interval (..)
    , isIn
    , lcro
    , fromInterval
    , intervalUpperBound
    ) where

data UpperBoundedPred a = UpperBoundedPred (a -> Bool) (Maybe a) deriving (Typeable)

applyUpperBoundedPred :: UpperBoundedPred a -> a -> Bool
applyUpperBoundedPred (UpperBoundedPred a _) = a

-- | @
-- if upperBound p < x then applyUBP p x == True
-- @
upperBound :: UpperBoundedPred a -> Maybe a
upperBound (UpperBoundedPred _ a) = a

-- | alias
applyUBP :: UpperBoundedPred a -> a -> Bool
applyUBP = applyUpperBoundedPred


data OC = Open | Closed deriving (Eq, Ord, Enum, Bounded, Show, Read, Data, Typeable)

comparator :: Ord a => OC -> a -> a -> Bool
comparator Closed = (<=)
comparator Open   = (<)

data Interval a = L OC a | R OC a | LR OC a OC a deriving (Eq, Ord, Show, Data, Typeable)

-- | >>> filter (`isIn` LR Closed 3 Open 7) [0..9]
-- [3,4,5,6]
isIn :: Ord a => a -> Interval a -> Bool
x `isIn` (L oc l) = comparator oc l x
x `isIn` (R oc r) = comparator oc x r
x `isIn` (LR loc l roc r) = (x `isIn` L loc l) && (x `isIn` R roc r)

-- | left-closed right-open interval
lcro :: Ord a => a -> a -> Interval a
lcro l r = LR Closed l Open r

fromInterval :: Ord a => Interval a -> UpperBoundedPred a
fromInterval x = UpperBoundedPred (`isIn` x) (intervalUpperBound x)

intervalUpperBound :: Ord a => Interval a -> Maybe (OC, a)
intervalUpperBound (R oc r) = Just (oc, r)
intervalUpperBound (LR _ _ oc r) = Just (oc, r)
intervalUpperBound _ = Nothing
