import Data.List type Name = String type Size = Int data Inode = File Name Size | Folder Name [Inode] deriving (Show) data ICrumb = ICrumb Name [Inode] deriving (Show) type IZipper = (Inode, [ICrumb]) main :: IO () main = do input <- map words . lines <$> readFile "input" let fullFS = walk (tail input) (Folder "/" [], []) let sizeList = getDirSizeList $ upTop fullFS print $ sum $ filter (< 100000) sizeList let toDelete = subtract 40000000 $ getDirSize $ upTop fullFS print $ head $ dropWhile (< toDelete) $ sort sizeList up :: IZipper -> IZipper up (node, ICrumb name nodes:crumbs) = (Folder name (node : nodes), crumbs) upTop :: IZipper -> IZipper upTop (inode, []) = (inode, []) upTop z = upTop (up z) newFile :: Inode -> IZipper -> IZipper newFile node (Folder name nodes, crumbs) = (Folder name (node : nodes), crumbs) goTo :: Name -> IZipper -> IZipper goTo name (Folder fname nodes, crumbs) = let (ls, node:rs) = break (nameIs name) nodes in (node, ICrumb fname (ls ++ rs) : crumbs) nameIs :: Name -> Inode -> Bool nameIs name (Folder folderName _) = name == folderName nameIs name (File fileName _) = name == fileName walk :: [[String]] -> IZipper -> IZipper walk (["$", "cd", name]:xs) z = walk xs $ if name == ".." then up z else goTo name z walk (["$", "ls"]:xs) z = walk xs z walk (["dir", name]:xs) z = walk xs $ newFile (Folder name []) z walk ([size, name]:xs) z = walk xs $ newFile (File name (read size)) z walk _ z = z getDirSize :: IZipper -> Int getDirSize (Folder _ nodes, _) = sum $ map getSize nodes where getSize (File _ size) = size getSize folder = getDirSize (folder, []) getDirSize _ = 0 getDirSizeList :: IZipper -> [Int] getDirSizeList (Folder _ nodes, _) = getDirSize (Folder "" nodes, []) : concat [getDirSizeList (node, []) | node <- nodes] getDirSizeList _ = []