content: import Data.Bool (bool)
  import Data.Tree (Tree (..), drawForest, drawTree, foldTree)
  
  ---------------------- TREE TRAVERSAL --------------------
  
  inorder, postorder, preorder :: a -> [[a]] -> [a]
  inorder x [] = [x]
  inorder x (y : xs) = y <> [x] <> concat xs
  
  postorder x xs = concat xs <> [x]
  
  preorder x xs = x : concat xs
  
  levelOrder :: Tree a -> [a]
  levelOrder = concat . levels
  
  levels :: Tree a -> [[a]]
  levels tree = go tree []
    where
      go (Node x xs) a =
        let (h, t) = case a of
              [] -> ([], [])
              (y : ys) -> (y, ys)
         in (x : h) : foldr go t xs
  
  nodeCount,
    treeDepth,
    treeMax,
    treeMin,
    treeProduct,
    treeSum,
    treeWidth ::
      Int -> [Int] -> Int
  nodeCount = const (succ . sum)
  treeDepth = const (succ . foldr max 1)
  treeMax x xs = maximum (x : xs)
  treeMin x xs = minimum (x : xs)
  treeProduct x xs = x * product xs
  treeSum x xs = x + sum xs
  treeWidth _ [] = 1
  treeWidth _ xs = sum xs
  
  treeLeaves :: Tree a -> [a]
  treeLeaves = foldTree go
    where
      go x [] = [x]
      go _ xs = concat xs
  
  --------------------------- TEST -------------------------
  tree :: Tree Int
  tree =
    Node
      1
      [ Node 2 [Node 4 [Node 7 []], Node 5 []],
        Node 3 [Node 6 [Node 8 [], Node 9 []]]
      ]
  
  main :: IO ()
  main = do
    putStrLn $ drawTree $ fmap show tree
    mapM_
      print
      ( [foldTree]
          <*> [preorder, inorder, postorder]
          <*> [tree]
      )
    print $ levelOrder tree
    putStrLn ""
  
    (putStrLn . unlines)
      ( ( \(k, f) ->
            justifyRight 7 ' ' k
              <> " -> "
              <> justifyLeft 6 ' ' (show $ foldTree f tree)
        )
          <$> [ ("Count", nodeCount),
                ("Layers", treeDepth),
                ("Max", treeMax),
                ("Min", treeMin),
                ("Product", treeProduct),
                ("Sum", treeSum),
                ("Leaves", treeWidth)
              ]
      )
  
  justifyLeft, justifyRight :: Int -> Char -> String -> String
  justifyLeft n c s = take n (s <> replicate n c)
  justifyRight n c = (drop . length) <*> (replicate n c <>)
  