content: import Data.List (sortBy, groupBy, maximumBy)
  import Data.Ord (comparing)
  
  (x, y) = ((!! 0), (!! 1))
  
  compareFrom
    :: (Num a, Ord a)
    => [a] -> [a] -> [a] -> Ordering
  compareFrom o l r =
    compare ((x l - x o) * (y r - y o)) ((y l - y o) * (x r - x o))
  
  distanceFrom
    :: Floating a
    => [a] -> [a] -> a
  distanceFrom from to = ((x to - x from) ** 2 + (y to - y from) ** 2) ** (1 / 2)
  
  convexHull
    :: (Floating a, Ord a)
    => [[a]] -> [[a]]
  convexHull points =
    let o = minimum points
        presorted = sortBy (compareFrom o) (filter (/= o) points)
        collinears = groupBy (((EQ ==) .) . compareFrom o) presorted
        outmost = maximumBy (comparing (distanceFrom o)) <$> collinears
    in dropConcavities [o] outmost
  
  dropConcavities
    :: (Num a, Ord a)
    => [[a]] -> [[a]] -> [[a]]
  dropConcavities (left:lefter) (right:righter:rightest) =
    case compareFrom left right righter of
      LT -> dropConcavities (right : left : lefter) (righter : rightest)
      EQ -> dropConcavities (left : lefter) (righter : rightest)
      GT -> dropConcavities lefter (left : righter : rightest)
  dropConcavities output lastInput = lastInput ++ output
  
  main :: IO ()
  main =
    mapM_ print $
    convexHull
      [ [16, 3]
      , [12, 17]
      , [0, 6]
      , [-4, -6]
      , [16, 6]
      , [16, -7]
      , [16, -3]
      , [17, -4]
      , [5, 19]
      , [19, -8]
      , [3, 16]
      , [12, 13]
      , [3, -4]
      , [17, 5]
      , [-3, 15]
      , [-3, -9]
      , [0, 11]
      , [-9, -3]
      , [-4, -2]
      , [12, 10]
      ]
  