with import ../../util.nix; let # read input cmdOuts = splitString "\n$ " (removePrefix "$ " (readFile ./input)); fileTree = let start = { cwd = []; tree = {}; }; applyCmd = acc: cmdOut: let # parse cmd + cmd output lines = splitString "\n" cmdOut; cmd = splitString " " (head lines); out = tail lines; # apply cmds applyCd = cwd: param: if param == ".." then dropLast cwd else if param == "/" then [] else cwd ++ [param]; applyLs = cwd: param: let parseFileDir = splitLine: if head splitLine == "dir" then { ${last splitLine} = {}; } else { ${last splitLine} = toInt (head splitLine); }; makeSubTree = combineAttrs (map (line: parseFileDir (splitString " " line)) param); in setAttrByPath cwd makeSubTree; in if head cmd == "cd" then { inherit (acc) tree; cwd = applyCd acc.cwd (last cmd); } else # ls { inherit (acc) cwd; tree = recursiveUpdate acc.tree (applyLs acc.cwd out); }; in (foldl applyCmd start cmdOuts).tree; sizeOfDir = dir: sum (map (v: if isAttrs v then sizeOfDir v else v) (attrValues dir)); # a flat list of sizes for all directories dirSizes = recurisveVisitAttrs (n: v: if isAttrs v then sizeOfDir v else 0) fileTree; # a list of sizes for small directories smallDirSizes = foldl (acc: size: if size < 100000 && size != 0 then acc ++ [size] else acc) [] dirSizes; totalUsedSize = sizeOfDir fileTree; # smallest directory to delete to free target size minDirOfAtLeastSize = targetSize: foldl (acc: s: if s >= targetSize && s < acc then s else acc) totalUsedSize dirSizes; totalFsSize = 70000000; fsSizeNeeded = 30000000; availableSpace = totalFsSize - totalUsedSize; spaceNeeded = fsSizeNeeded - availableSpace; in rec { part1 = sum smallDirSizes; part2 = minDirOfAtLeastSize spaceNeeded; }