module GeomAlg.External.OrderStat (
selectBy, select, medianBy, median,
naiveSelectBy, naiveSelect, naiveMedianBy, naiveMedian
) where
import GeomAlg.External.Sorting (sortBy, partition, isortBy, partition3)
import GeomAlg.External.Utilities (isSingleton, leqRel, longerThan, splitsAt)
selectBy :: (a -> a -> Ordering) -> Int -> [a] -> a
selectBy cmp i xs
| xs `longerThan` 5 = if i < k then selectBy cmp i ls
else if i < l then head es
else selectBy cmp (il) hs
| otherwise = sort xs !! i
where
m = medianOfMedians xs
(ls, es, hs) = partition3 (flip cmp (medianOfMedians xs)) xs
k = length ls
l = k + length es
leq = leqRel cmp
sort = isortBy leq
medianOfMedians = head . until isSingleton (map (median5 . sort) . splitsAt 5)
median5 [_,_,x,_,_] = x
median5 [_,x,y,_] = if x `leq` y then x else y
median5 [_,x,_] = x
median5 [x,y] = if x `leq` y then x else y
median5 [x] = x
select :: Ord a => Int -> [a] -> a
select = selectBy compare
medianBy :: (a -> a -> Ordering) -> [a] -> a
medianBy cmp xs = selectBy cmp (length xs `div` 2) xs
median :: Ord a => [a] -> a
median = medianBy compare
naiveSelectBy :: (a -> a -> Ordering) -> Int -> [a] -> a
naiveSelectBy cmp k xs = sortBy (leqRel cmp) xs !! k
naiveSelect :: Ord a => Int -> [a] -> a
naiveSelect = naiveSelectBy compare
naiveMedianBy :: (a -> a -> Ordering) -> [a] -> a
naiveMedianBy cmp xs = naiveSelectBy cmp (length xs `div` 2) xs
naiveMedian :: Ord a => [a] -> a
naiveMedian = naiveMedianBy compare