module Data.Ranged.Boundaries (
DiscreteOrdered (..),
enumAdjacent,
boundedAdjacent,
boundedBelow,
Boundary (..),
above,
(/>/)
) where
import Data.Ratio
import Test.QuickCheck
infix 4 />/
class Ord a => DiscreteOrdered a where
adjacent :: a -> a -> Bool
adjacentBelow :: a -> Maybe a
instance DiscreteOrdered Bool where
adjacent = boundedAdjacent
adjacentBelow = boundedBelow
instance DiscreteOrdered Ordering where
adjacent = boundedAdjacent
adjacentBelow = boundedBelow
instance DiscreteOrdered Char where
adjacent = boundedAdjacent
adjacentBelow = boundedBelow
instance DiscreteOrdered Int where
adjacent = boundedAdjacent
adjacentBelow = boundedBelow
instance DiscreteOrdered Integer where
adjacent = enumAdjacent
adjacentBelow = Just . pred
instance DiscreteOrdered Double where
adjacent _ _ = False
adjacentBelow = const Nothing
instance DiscreteOrdered Float where
adjacent _ _ = False
adjacentBelow = const Nothing
instance (Integral a) => DiscreteOrdered (Ratio a) where
adjacent _ _ = False
adjacentBelow = const Nothing
instance Ord a => DiscreteOrdered [a] where
adjacent _ _ = False
adjacentBelow = const Nothing
instance (Ord a, DiscreteOrdered b) => DiscreteOrdered (a, b)
where
adjacent (x1, x2) (y1, y2) = (x1 == y1) && adjacent x2 y2
adjacentBelow (x1, x2) = do
x2' <- adjacentBelow x2
return (x1, x2')
instance (Ord a, Ord b, DiscreteOrdered c) => DiscreteOrdered (a, b, c)
where
adjacent (x1, x2, x3) (y1, y2, y3) =
(x1 == y1) && (x2 == y2) && adjacent x3 y3
adjacentBelow (x1, x2, x3) = do
x3' <- adjacentBelow x3
return (x1, x2, x3')
instance (Ord a, Ord b, Ord c, DiscreteOrdered d) =>
DiscreteOrdered (a, b, c, d)
where
adjacent (x1, x2, x3, x4) (y1, y2, y3, y4) =
(x1 == y1) && (x2 == y2) && (x3 == y3) && adjacent x4 y4
adjacentBelow (x1, x2, x3, x4) = do
x4' <- adjacentBelow x4
return (x1, x2, x3, x4')
enumAdjacent :: (Ord a, Enum a) => a -> a -> Bool
enumAdjacent x y = (succ x == y)
boundedAdjacent :: (Ord a, Enum a) => a -> a -> Bool
boundedAdjacent x y = if x < y then succ x == y else False
boundedBelow :: (Eq a, Enum a, Bounded a) => a -> Maybe a
boundedBelow x = if x == minBound then Nothing else Just $ pred x
data Boundary a =
BoundaryAbove a |
BoundaryBelow a |
BoundaryAboveAll |
BoundaryBelowAll
deriving (Show)
above :: Ord v => Boundary v -> v -> Bool
above (BoundaryAbove b) v = v > b
above (BoundaryBelow b) v = v >= b
above BoundaryAboveAll _ = False
above BoundaryBelowAll _ = True
(/>/) :: Ord v => v -> Boundary v -> Bool
(/>/) = flip above
instance (DiscreteOrdered a) => Eq (Boundary a) where
b1 == b2 = compare b1 b2 == EQ
instance (DiscreteOrdered a) => Ord (Boundary a) where
compare boundary1 boundary2 =
case boundary1 of
BoundaryAbove b1 ->
case boundary2 of
BoundaryAbove b2 -> compare b1 b2
BoundaryBelow b2 ->
if b1 < b2
then
if adjacent b1 b2 then EQ else LT
else GT
BoundaryAboveAll -> LT
BoundaryBelowAll -> GT
BoundaryBelow b1 ->
case boundary2 of
BoundaryAbove b2 ->
if b1 > b2
then
if adjacent b2 b1 then EQ else GT
else LT
BoundaryBelow b2 -> compare b1 b2
BoundaryAboveAll -> LT
BoundaryBelowAll -> GT
BoundaryAboveAll ->
case boundary2 of
BoundaryAboveAll -> EQ
_ -> GT
BoundaryBelowAll ->
case boundary2 of
BoundaryBelowAll -> EQ
_ -> LT
instance Arbitrary a => Arbitrary (Boundary a) where
arbitrary = frequency [
(1, return BoundaryAboveAll),
(1, return BoundaryBelowAll),
(18, do
v <- arbitrary
oneof [return $ BoundaryAbove v, return $ BoundaryBelow v]
)]
instance CoArbitrary a => CoArbitrary (Boundary a) where
coarbitrary BoundaryBelowAll = variant (0 :: Int)
coarbitrary BoundaryAboveAll = variant (1 :: Int)
coarbitrary (BoundaryBelow v) = variant (2 :: Int) . coarbitrary v
coarbitrary (BoundaryAbove v) = variant (3 :: Int) . coarbitrary v