module Data.Interval (Interval (..), size, overlap, hull, isPoint) where

import Prelude hiding (Eq, Ord (..), Num (..), max, min, null)

import Algebra
import Control.Applicative
import Control.Monad (guard)
import Data.Function (on)
import Data.Maybe (fromMaybe)
import Data.Ord (Down (..))
import Data.Semigroup (Sum (..))
import Relation.Binary.Comparison

infix 5 :–:
data Interval a = Maybe a :–: Maybe a
    deriving (Read, Show,
              Functor, Foldable, Traversable)

instance Preord a => Preord (Interval a) where
    a₁ :–: b₁  a₂ :–: b₂ = Lexical a₁  Lexical a₂ && (() `on` Down . Lexical . fmap Down) b₁ b₂
instance PartialEq a => PartialEq (Interval a) where a₁ :–: b₁  a₂ :–: b₂ = (a₁, b₁)  (a₂, b₂)
instance (PartialOrd a, PartialEq a) => PartialOrd (Interval a)
instance Eq a => Eq (Interval a)

size :: Group (Sum a) => Interval a -> Maybe a
size (a :–: b) = liftA2 (-) b a

overlap :: Ord a => Interval a -> Interval a -> Maybe (Interval a)
overlap (a₁ :–: b₁) (a₂ :–: b₂) = z <$ (guard . not . null) z
  where z = fmap unMax (fmap Max a₁ <> fmap Max a₂) :–:
            fmap unMin (fmap Min b₁ <> fmap Min b₂)

hull :: Ord a => Interval a -> Interval a -> Interval a
hull (a₁ :–: b₁) (a₂ :–: b₂) = liftA2 min a₁ a₂ :–: liftA2 max b₁ b₂

null :: Ord a => Interval a -> Bool
null (a :–: b) = fromMaybe False $ liftA2 (>) a b

isPoint :: Eq a => Interval a -> Maybe a
isPoint (a :–: b) = a <* guard (a  b)