--- /dev/null
+-- Writeup at https://work.njae.me.uk/2022/12/07/advent-of-code-2022-day-7/
+
+import AoC
+import Data.Text (Text)
+import qualified Data.Text.IO as TIO
+import Data.Attoparsec.Text hiding (take)
+import Control.Applicative
+import Data.Char
+import Data.Maybe
+import Data.Tree
+import Data.Tree.Zipper hiding (tree)
+import qualified Data.Map.Strict as M
+-- import Data.Map.Strict ((!), (!?))
+import Data.List (foldl', sort)
+
+data ParsedObject = CD String
+ | LS
+ | PDirectory String
+ | PFile Int String
+ deriving Show
+
+data Directory = Dir String (M.Map String Int)
+ deriving (Show, Eq)
+
+data ContainedSize = CSize String Integer
+ deriving (Show, Eq)
+
+type DTree = Tree Directory
+type ZDTree = TreePos Full Directory
+type STree = Tree ContainedSize
+
+reportingThreshold, spaceAvailable, spaceRequired :: Integer
+reportingThreshold = 100000
+spaceAvailable = 70000000
+spaceRequired = 30000000
+
+main :: IO ()
+main =
+ do dataFileName <- getDataFileName
+ text <- TIO.readFile dataFileName
+ let trace = successfulParse text
+ let tree = mkTree trace emptyTree
+ let sizedTree = transitiveSizes $ containingSizes tree
+ print $ part1 sizedTree
+ print $ part2 sizedTree
+
+part1, part2 :: STree -> Integer
+part1 = foldTree (\x xs -> sum (x:xs)) . fmap cancelLarge
+
+part2 tree = spaceFreed
+ where nodes = fmap extractCSize $ flatten tree
+ spaceUsed = extractCSize $ rootLabel tree
+ spaceUnused = spaceAvailable - spaceUsed
+ spaceToFree = spaceRequired - spaceUnused
+ viableNodes = filter (>= spaceToFree) nodes
+ spaceFreed = head $ sort viableNodes
+
+emptyTree :: DTree
+emptyTree = Node {rootLabel = (Dir "/" M.empty), subForest = []}
+
+mkTree :: [ParsedObject] -> DTree -> DTree
+mkTree trace tree = toTree $ root $ makeTree trace $ fromTree tree
+
+makeTree :: [ParsedObject] -> ZDTree -> ZDTree
+makeTree trace tree = foldl' processCommand tree trace
+
+processCommand :: ZDTree -> ParsedObject -> ZDTree
+processCommand tree (CD name)
+ | name == "/" = root tree
+ | name == ".." = fromJust $ parent tree
+ | otherwise = fromJust $ childWithName name tree
+processCommand tree LS = tree
+processCommand tree (PFile size name) =
+ modifyLabel (\ (Dir n fs) -> Dir n (M.insert name size fs)) tree
+processCommand tree (PDirectory name) =
+ if (isJust $ childWithName name tree)
+ then tree
+ else fromJust $ parent $ insert (Node { rootLabel = (Dir name M.empty)
+ , subForest = []
+ }) $ children tree
+
+
+childWithName :: String -> ZDTree -> Maybe ZDTree
+childWithName name tree = searchForChild name (firstChild tree)
+
+searchForChild :: String -> Maybe ZDTree -> Maybe ZDTree
+searchForChild _name Nothing = Nothing
+searchForChild name (Just tree)
+ | name == labelName = Just tree
+ | otherwise = searchForChild name (next tree)
+ where (Dir labelName _) = label tree
+
+containingSizes :: DTree -> STree
+containingSizes (Node {rootLabel = (Dir name files), subForest = sf}) =
+ (Node {rootLabel = (CSize name sizeHere), subForest = sizedTrees})
+ where sizeHere = M.foldl (+) 0 $ M.map fromIntegral files
+ sizedTrees = fmap containingSizes sf
+
+transitiveSizes :: STree -> STree
+transitiveSizes (Node {rootLabel = (CSize name sizeHere), subForest = sf}) =
+ (Node {rootLabel = (CSize name (sizeHere + subSizes)), subForest = sizedTrees })
+ where sizedTrees = fmap transitiveSizes sf
+ subSizes = sum $ fmap (extractCSize . rootLabel) sizedTrees
+
+extractCSize, cancelLarge :: ContainedSize -> Integer
+extractCSize (CSize _ s) = s
+
+cancelLarge (CSize _ s)
+ | s <= reportingThreshold = s
+ | otherwise = 0
+
+-- Parse the input file
+
+traceP :: Parser [ParsedObject]
+lineP :: Parser ParsedObject
+cdP :: Parser ParsedObject
+lsP :: Parser ParsedObject
+fileP :: Parser ParsedObject
+directoryP :: Parser ParsedObject
+letterP :: Parser Char
+nameP :: Parser String
+
+traceP = lineP `sepBy` endOfLine
+
+lineP = cdP <|> lsP <|> directoryP <|> fileP
+cdP = CD <$> ("$ cd " *> nameP)
+lsP = LS <$ "$ ls"
+fileP = PFile <$> (decimal <* " ") <*> nameP
+directoryP = PDirectory <$> ("dir " *> nameP)
+
+nameP = many1 letterP
+letterP = satisfy (not . isSpace)
+
+successfulParse :: Text -> [ParsedObject]
+successfulParse input =
+ case parseOnly traceP input of
+ Left _err -> [] -- TIO.putStr $ T.pack $ parseErrorPretty err
+ Right commands -> commands
--- /dev/null
+<!DOCTYPE html>
+<html lang="en-us">
+<head>
+<meta charset="utf-8"/>
+<title>Day 7 - Advent of Code 2022</title>
+<!--[if lt IE 9]><script src="/static/html5.js"></script><![endif]-->
+<link href='//fonts.googleapis.com/css?family=Source+Code+Pro:300&subset=latin,latin-ext' rel='stylesheet' type='text/css'/>
+<link rel="stylesheet" type="text/css" href="/static/style.css?30"/>
+<link rel="stylesheet alternate" type="text/css" href="/static/highcontrast.css?0" title="High Contrast"/>
+<link rel="shortcut icon" href="/favicon.png"/>
+<script>window.addEventListener('click', function(e,s,r){if(e.target.nodeName==='CODE'&&e.detail===3){s=window.getSelection();s.removeAllRanges();r=document.createRange();r.selectNodeContents(e.target);s.addRange(r);}});</script>
+</head><!--
+
+
+
+
+Oh, hello! Funny seeing you here.
+
+I appreciate your enthusiasm, but you aren't going to find much down here.
+There certainly aren't clues to any of the puzzles. The best surprises don't
+even appear in the source until you unlock them for real.
+
+Please be careful with automated requests; I'm not a massive company, and I can
+only take so much traffic. Please be considerate so that everyone gets to play.
+
+If you're curious about how Advent of Code works, it's running on some custom
+Perl code. Other than a few integrations (auth, analytics, social media), I
+built the whole thing myself, including the design, animations, prose, and all
+of the puzzles.
+
+The puzzles are most of the work; preparing a new calendar and a new set of
+puzzles each year takes all of my free time for 4-5 months. A lot of effort
+went into building this thing - I hope you're enjoying playing it as much as I
+enjoyed making it for you!
+
+If you'd like to hang out, I'm @ericwastl on Twitter.
+
+- Eric Wastl
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+-->
+<body>
+<header><div><h1 class="title-global"><a href="/">Advent of Code</a></h1><nav><ul><li><a href="/2022/about">[About]</a></li><li><a href="/2022/events">[Events]</a></li><li><a href="https://teespring.com/stores/advent-of-code" target="_blank">[Shop]</a></li><li><a href="/2022/settings">[Settings]</a></li><li><a href="/2022/auth/logout">[Log Out]</a></li></ul></nav><div class="user">Neil Smith <a href="/2022/support" class="supporter-badge" title="Advent of Code Supporter">(AoC++)</a> <span class="star-count">14*</span></div></div><div><h1 class="title-event"> <span class="title-event-wrap">0xffff&</span><a href="/2022">2022</a><span class="title-event-wrap"></span></h1><nav><ul><li><a href="/2022">[Calendar]</a></li><li><a href="/2022/support">[AoC++]</a></li><li><a href="/2022/sponsors">[Sponsors]</a></li><li><a href="/2022/leaderboard">[Leaderboard]</a></li><li><a href="/2022/stats">[Stats]</a></li></ul></nav></div></header>
+
+<div id="sidebar">
+<div id="sponsor"><div class="quiet">Our <a href="/2022/sponsors">sponsors</a> help make Advent of Code possible:</div><div class="sponsor"><a href="https://www.twilio.com/quest" target="_blank" onclick="if(ga)ga('send','event','sponsor','sidebar',this.href);" rel="noopener">TwilioQuest</a> - Discover your power to change the world with code with TwilioQuest, an educational RPG. Learn foundational programming skills, JavaScript and Python while you explore The Cloud alongside our community of developers.</div></div>
+</div><!--/sidebar-->
+
+<main>
+<article class="day-desc"><h2>--- Day 7: No Space Left On Device ---</h2><p>You can hear birds chirping and raindrops hitting leaves as the expedition proceeds. Occasionally, you can even hear much louder sounds in the distance; how big do the animals get out here, anyway?</p>
+<p>The device the Elves gave you has problems with more than just its communication system. You try to run a system update:</p>
+<pre><code>$ system-update --please --pretty-please-with-sugar-on-top
+<span title="E099 PROGRAMMER IS OVERLY POLITE">Error</span>: No space left on device
+</code></pre>
+<p>Perhaps you can delete some files to make space for the update?</p>
+<p>You browse around the filesystem to assess the situation and save the resulting terminal output (your puzzle input). For example:</p>
+<pre><code>$ cd /
+$ ls
+dir a
+14848514 b.txt
+8504156 c.dat
+dir d
+$ cd a
+$ ls
+dir e
+29116 f
+2557 g
+62596 h.lst
+$ cd e
+$ ls
+584 i
+$ cd ..
+$ cd ..
+$ cd d
+$ ls
+4060174 j
+8033020 d.log
+5626152 d.ext
+7214296 k
+</code></pre>
+<p>The filesystem consists of a tree of files (plain data) and directories (which can contain other directories or files). The outermost directory is called <code>/</code>. You can navigate around the filesystem, moving into or out of directories and listing the contents of the directory you're currently in.</p>
+<p>Within the terminal output, lines that begin with <code>$</code> are <em>commands you executed</em>, very much like some modern computers:</p>
+<ul>
+<li><code>cd</code> means <em>change directory</em>. This changes which directory is the current directory, but the specific result depends on the argument:
+ <ul>
+ <li><code>cd x</code> moves <em>in</em> one level: it looks in the current directory for the directory named <code>x</code> and makes it the current directory.</li>
+ <li><code>cd ..</code> moves <em>out</em> one level: it finds the directory that contains the current directory, then makes that directory the current directory.</li>
+ <li><code>cd /</code> switches the current directory to the outermost directory, <code>/</code>.</li>
+ </ul>
+</li>
+<li><code>ls</code> means <em>list</em>. It prints out all of the files and directories immediately contained by the current directory:
+ <ul>
+ <li><code>123 abc</code> means that the current directory contains a file named <code>abc</code> with size <code>123</code>.</li>
+ <li><code>dir xyz</code> means that the current directory contains a directory named <code>xyz</code>.</li>
+ </ul>
+</li>
+</ul>
+<p>Given the commands and output in the example above, you can determine that the filesystem looks visually like this:</p>
+<pre><code>- / (dir)
+ - a (dir)
+ - e (dir)
+ - i (file, size=584)
+ - f (file, size=29116)
+ - g (file, size=2557)
+ - h.lst (file, size=62596)
+ - b.txt (file, size=14848514)
+ - c.dat (file, size=8504156)
+ - d (dir)
+ - j (file, size=4060174)
+ - d.log (file, size=8033020)
+ - d.ext (file, size=5626152)
+ - k (file, size=7214296)
+</code></pre>
+<p>Here, there are four directories: <code>/</code> (the outermost directory), <code>a</code> and <code>d</code> (which are in <code>/</code>), and <code>e</code> (which is in <code>a</code>). These directories also contain files of various sizes.</p>
+<p>Since the disk is full, your first step should probably be to find directories that are good candidates for deletion. To do this, you need to determine the <em>total size</em> of each directory. The total size of a directory is the sum of the sizes of the files it contains, directly or indirectly. (Directories themselves do not count as having any intrinsic size.)</p>
+<p>The total sizes of the directories above can be found as follows:</p>
+<ul>
+<li>The total size of directory <code>e</code> is <em>584</em> because it contains a single file <code>i</code> of size 584 and no other directories.</li>
+<li>The directory <code>a</code> has total size <em>94853</em> because it contains files <code>f</code> (size 29116), <code>g</code> (size 2557), and <code>h.lst</code> (size 62596), plus file <code>i</code> indirectly (<code>a</code> contains <code>e</code> which contains <code>i</code>).</li>
+<li>Directory <code>d</code> has total size <em>24933642</em>.</li>
+<li>As the outermost directory, <code>/</code> contains every file. Its total size is <em>48381165</em>, the sum of the size of every file.</li>
+</ul>
+<p>To begin, find all of the directories with a total size of <em>at most 100000</em>, then calculate the sum of their total sizes. In the example above, these directories are <code>a</code> and <code>e</code>; the sum of their total sizes is <code><em>95437</em></code> (94853 + 584). (As in this example, this process can count files more than once!)</p>
+<p>Find all of the directories with a total size of at most 100000. <em>What is the sum of the total sizes of those directories?</em></p>
+</article>
+<p>Your puzzle answer was <code>1084134</code>.</p><article class="day-desc"><h2 id="part2">--- Part Two ---</h2><p>Now, you're ready to choose a directory to delete.</p>
+<p>The total disk space available to the filesystem is <code><em>70000000</em></code>. To run the update, you need unused space of at least <code><em>30000000</em></code>. You need to find a directory you can delete that will <em>free up enough space</em> to run the update.</p>
+<p>In the example above, the total size of the outermost directory (and thus the total amount of used space) is <code>48381165</code>; this means that the size of the <em>unused</em> space must currently be <code>21618835</code>, which isn't quite the <code>30000000</code> required by the update. Therefore, the update still requires a directory with total size of at least <code>8381165</code> to be deleted before it can run.</p>
+<p>To achieve this, you have the following options:</p>
+<ul>
+<li>Delete directory <code>e</code>, which would increase unused space by <code>584</code>.</li>
+<li>Delete directory <code>a</code>, which would increase unused space by <code>94853</code>.</li>
+<li>Delete directory <code>d</code>, which would increase unused space by <code>24933642</code>.</li>
+<li>Delete directory <code>/</code>, which would increase unused space by <code>48381165</code>.</li>
+</ul>
+<p>Directories <code>e</code> and <code>a</code> are both too small; deleting them would not free up enough space. However, directories <code>d</code> and <code>/</code> are both big enough! Between these, choose the <em>smallest</em>: <code>d</code>, increasing unused space by <code><em>24933642</em></code>.</p>
+<p>Find the smallest directory that, if deleted, would free up enough space on the filesystem to run the update. <em>What is the total size of that directory?</em></p>
+</article>
+<p>Your puzzle answer was <code>6183184</code>.</p><p class="day-success">Both parts of this puzzle are complete! They provide two gold stars: **</p>
+<p>At this point, you should <a href="/2022">return to your Advent calendar</a> and try another puzzle.</p>
+<p>If you still want to see it, you can <a href="7/input" target="_blank">get your puzzle input</a>.</p>
+<p>You can also <span class="share">[Share<span class="share-content">on
+ <a href="https://twitter.com/intent/tweet?text=I%27ve+completed+%22No+Space+Left+On+Device%22+%2D+Day+7+%2D+Advent+of+Code+2022&url=https%3A%2F%2Fadventofcode%2Ecom%2F2022%2Fday%2F7&related=ericwastl&hashtags=AdventOfCode" target="_blank">Twitter</a>
+ <a href="javascript:void(0);" onclick="var mastodon_instance=prompt('Mastodon Instance / Server Name?'); if(typeof mastodon_instance==='string' && mastodon_instance.length){this.href='https://'+mastodon_instance+'/share?text=I%27ve+completed+%22No+Space+Left+On+Device%22+%2D+Day+7+%2D+Advent+of+Code+2022+%23AdventOfCode+https%3A%2F%2Fadventofcode%2Ecom%2F2022%2Fday%2F7'}else{return false;}" target="_blank">Mastodon</a
+></span>]</span> this puzzle.</p>
+</main>
+
+<!-- ga -->
+<script>
+(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+ga('create', 'UA-69522494-1', 'auto');
+ga('set', 'anonymizeIp', true);
+ga('send', 'pageview');
+</script>
+<!-- /ga -->
+</body>
+</html>
\ No newline at end of file