module Data.RangeMin.Internal.HandyOrdering(Comparator, orderPairBy,
orderPair, minBy, maxBy, maximumBy, minimumBy, comparing, lexCompare, lexComparing, listRangeBy, listRange) where
import Data.Monoid
import Data.List(maximumBy, minimumBy)
import Data.RangeMin.Internal.Combinators(on)
type Comparator e = e -> e -> Ordering
comparing :: Ord b => (a -> b) -> Comparator a
comparing f = compare `on` f
orderPairBy :: Comparator e -> (e, e) -> (e, e)
orderPairBy cmp (x,y) = if cmp x y == GT then (y,x) else (x,y)
orderPair :: Ord e => (e, e) -> (e, e)
orderPair (x,y) = if x <= y then (x, y) else (y, x)
minBy :: Comparator e -> e -> e -> e
minBy cmp x y = if cmp x y == GT then y else x
maxBy :: Comparator e -> e -> e -> e
maxBy cmp x y = if cmp y x == GT then y else x
lexCompare :: Comparator e -> Comparator e -> Comparator e
lexCompare cmp1 cmp2 = \ x y -> cmp1 x y `mappend` cmp2 x y
lexComparing :: (Ord a, Ord b) => (e -> a) -> (e -> b) -> Comparator e
lexComparing f1 f2 = lexCompare (comparing f1) (comparing f2)
minimax :: Comparator e -> e -> (Maybe (e, e), Maybe e) -> (Maybe (e, e), Maybe e)
minimax cmp x (minmax, Nothing) = (minmax, Just x)
minimax cmp x (Nothing, Just y) = (Just (orderPairBy cmp (x, y)), Nothing)
minimax cmp x (Just (mi, ma), Just y)
= let (x', y') = orderPairBy cmp (x, y) in (Just (minBy cmp x' mi, maxBy cmp y' ma), Nothing)
listRangeBy :: Comparator e -> [e] -> Maybe (e, e)
listRangeBy cmp l = case foldr (minimax cmp) (Nothing, Nothing) l of
(Nothing, Just x) -> Just (x, x)
(Nothing, Nothing) -> Nothing
(Just p, Nothing) -> Just p
(Just (mi, ma), Just x) -> Just (if cmp x mi == GT then (mi, maxBy cmp x ma) else (x, ma))
listRange :: Ord e => [e] -> Maybe (e, e)
listRange = listRangeBy compare
listRangeWith :: Ord f => (e -> f) -> [e] -> Maybe (e, e)
listRangeWith = listRangeBy . comparing