import Data.List (intercalate, maximumBy, sort)
import Data.Ord (comparing)



consolidated :: [(Float, Float)] -> [(Float, Float)]
consolidated = foldr go [] . sort . fmap ab
  where
    go xy [] = [xy]
    go xy@(x, y) abetc@((a, b): etc)
      | y >= b = xy: etc
      | y >= a = (x, b): etc
      | otherwise = xy: abetc
    ab (a, b)
      | a <= b = (a, b)
      | otherwise = (b, a)



tests :: [[(Float, Float)]]
tests =
  [ [],
    [(1.1, 2.2)],
    [(6.1, 7.2), (7.2, 8.3)],
    [(4, 3), (2, 1)],
    [(4, 3), (2, 1), (-1, -2), (3.9, 10)],
    [(1, 3), (-6, -1), (-4, -5), (8, 2), (-6, -6)]
  ]

main :: IO ()
main =
  putStrLn $
    tabulated
      "Range consolidations:"
      showPairs
      showPairs
      consolidated
      tests



tabulated ::
  String ->
  (a -> String) ->
  (b -> String) ->
  (a -> b) ->
  [a] ->
  String
tabulated s xShow fxShow f xs =
  let w =
        length $
          maximumBy
            (comparing length)
            (xShow <$> xs)
      rjust n c s = drop (length s) (replicate n c <> s)
   in unlines $
        s:
        fmap
          ( ((<>) . rjust w ' ' . xShow)
              <*> ((" -> " <>) . fxShow . f)
          )
          xs

showPairs :: [(Float, Float)] -> String
showPairs xs
  | null xs = "[]"
  | otherwise =
    '[':
    intercalate
      ", "
      (showPair <$> xs)
      <> "]"

showPair :: (Float, Float) -> String
showPair (a, b) =
  '(':
  showNum a
    <> ", "
    <> showNum b
    <> ")"

showNum :: Float -> String
showNum n
  | 0 == (n - fromIntegral (round n)) = show (round n)
  | otherwise = show n