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 [getSize node | node<-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 _ = []