import System.IO import Data.List import Data.List.Split import Debug.Trace data Node = File String Int | Dir String Int [Node] deriving (Show) instance Eq Node where Dir n1 _ _ == Dir n2 _ _ = n1 == n2 File n1 _ == File n2 _ = n1 == n2 instance Ord Node where Dir _ s1 _ `compare` Dir _ s2 _ = s1 `compare` s2 File _ s1 `compare` File _ s2 = s1 `compare` s2 nodeSize :: Node -> Int nodeSize (File _ s) = s nodeSize (Dir _ s _) = s nodeInfo :: Node -> String nodeInfo (File n s) = n ++ "(file, size=" ++ show s ++ ")" nodeInfo (Dir n s _) = n ++ "(dir, size=" ++ show s ++ ")" printNode :: Int -> Node -> String printNode lvl n = case n of File n s -> sp ++ "- " ++ n ++ " (file, size=" ++ show s ++ ")\n" Dir n s c -> sp ++ "- " ++ n ++ " (dir, size=" ++ show s ++ ")\n" ++ printNodes (lvl+1) c where sp = replicate (2*lvl) ' ' printNodes :: Int -> [Node] -> String printNodes a = concat . map (printNode a) --parseDir' :: [String] -> [Node] --parseDir' (x:xs) = dirInNodes :: String -> [Node] -> (Bool,Node,[Node]) dirInNodes name onodes = (exists, instanc, removed) where matches = filter (\node@(Dir n _ _) -> n == name) onodes exists = (length matches) > 0 instanc = if exists then head matches else File "" 0 removed = delete instanc onodes parseDir :: [Node] -> [Node] -> [String] -> ([Node],[String]) parseDir onodes nodes (x:xs) | "$ cd " `isPrefixOf` x = ((Dir (drop 5 x) (sum $ map nodeSize nodes) nodes:onodes), xs) | "dir " `isPrefixOf` x = case dirInNodes (drop 4 x) onodes of (True,node,restnodes) -> parseDir restnodes (node:nodes) xs (False,_,_) -> parseDir onodes nodes xs | otherwise = parseDir onodes (File fname fsize:nodes) xs where [fsize',fname] = splitOn " " x fsize = read fsize' :: Int parseCli :: [Node] -> [String] -> [String] -> [Node] parseCli nodes [] [] = [] parseCli nodes stack [] = onodes ++ parseCli onodes reststack [] where (onodes,reststack) = parseDir nodes [] $ stack parseCli nodes stack (x:xs) | x == "$ cd .." = onodes ++ parseCli onodes reststack xs | otherwise = parseCli nodes (x:stack) xs where (onodes,reststack) = parseDir nodes [] $ stack prepCli :: [String] -> [String] prepCli = filter (/= "$ ls") findNodes :: (Node -> Bool) -> Node -> [Node] findNodes f file@(File _ _) = if (f file) then [file] else [] findNodes f dir@(Dir _ _ c) = (if (f dir) then [dir] else []) ++ (concat $ map (findNodes f) c) dirSize :: (Int -> Bool) -> Node -> Bool dirSize _ (File _ _) = False dirSize f (Dir _ s _) = f s findDeletable :: Int -> Int -> Node -> Node findDeletable fss req root@(Dir _ used _) = smallest where tbf = req - (fss-used) candidates = findNodes (dirSize (>= tbf)) root smallest = head $ sort candidates handler :: String -> String handler s = (show sumUnder) ++ "\n" ++ (show savsize) ++ "\n" where rootNode = last $ parseCli [] [] $ prepCli $ lines s under100k = findNodes (dirSize (<= 100000)) rootNode sumUnder = sum $ map (\(Dir _ s _) -> s) under100k (Dir _ savsize _) = findDeletable 70000000 30000000 rootNode main :: IO () main = do interact handler