-
Notifications
You must be signed in to change notification settings - Fork 0
/
Day07.idr
126 lines (102 loc) · 2.98 KB
/
Day07.idr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
module Day07
import AocUtils
import Data.List1
import Data.Maybe
import Data.Morphisms
import Data.SortedMap
import Data.String
%default total
-- Parsing
data DirOutput
= DirLine String
| FileLine String Nat
data CmdOutput
= CdRoot
| CdParent
| Cd String
| Ls (List DirOutput)
Show DirOutput where
show (DirLine str) = "dir \{str}"
show (FileLine str k) = "\{show k} \{str}"
Show CmdOutput where
show CdRoot = "cd /"
show CdParent = "cd .."
show (Cd str) = "cd \{str}"
show (Ls output) = "ls \n\{unlines $ map show output}"
Input : Type
Input = List CmdOutput
partial
parseCmdOutput : List String -> CmdOutput
parseCmdOutput ["cd /"] = CdRoot
parseCmdOutput ["cd .."] = CdParent
parseCmdOutput ("ls" :: output) = Ls (map (parseDirOutput . words) output)
where
parseDirOutput : List String -> DirOutput
parseDirOutput ["dir", name] = DirLine name
parseDirOutput [size, name] = FileLine name (cast size)
parseCmdOutput [cdCmd] = parseCd $ words cdCmd
where
parseCd : List String -> CmdOutput
parseCd ["cd", dirName] = Cd dirName
partial
parse : String -> Input
parse = map parseCmdOutput . map (lines . trim) . tail . split (== '$')
-- Part 1
record DirTree where
constructor MkDirTree
dirs : SortedMap String DirTree
files : List (String, Nat)
emptyDir : DirTree
emptyDir = MkDirTree empty []
covering
formTree : (acc : DirTree) -> Input -> (DirTree, Input)
formTree acc [] = (acc, [])
formTree acc (CdRoot :: rest) = formTree emptyDir rest
formTree acc (CdParent :: rest) = (acc, rest)
formTree (MkDirTree dirs files) (Cd dir :: rest) =
let (subTree, rest') =
formTree (fromMaybe emptyDir $ lookup dir dirs) rest in
let acc' = MkDirTree (insert dir subTree dirs) files in
formTree acc' rest'
formTree acc (Ls items :: rest) =
formTree (foldl update acc items) rest
where
update : DirTree -> DirOutput -> DirTree
update tree (DirLine name) =
{ dirs $= insert name emptyDir } tree
update tree (FileLine name size) =
{ files $= ((name, size) ::) } tree
%hint
additive : Num a => Monoid a
additive = Additive
covering
dirSize : DirTree -> Nat
dirSize (MkDirTree dirs files) =
foldMap dirSize (values dirs) + foldMap snd files
covering
filterDirs : (Nat -> Bool) -> DirTree -> List Nat
filterDirs p tree =
let subDirs = toList tree.dirs >>= filterDirs p in
let size = dirSize tree in
if p size then size :: subDirs else subDirs
covering
solve1 : Input -> Nat
solve1 = sum . filterDirs (<= 100_000) . fst . formTree emptyDir
covering
part1 : Input ~> String
part1 = Mor $ ("Part 1: " ++) . show . solve1
-- Part 2
covering
solve2 : Input -> Nat
solve2 input =
let tree = fst $ formTree emptyDir input in
let totalSize = dirSize tree in
let minSize = cast totalSize - 40_000_000 in
min' $ totalSize ::: filterDirs (>= cast minSize) tree
covering
part2 : Input ~> String
part2 = Mor $ ("Part 2: " ++) . show . solve2
-- Plumbing
partial
main : IO ()
main = interact (unlines . (applyMor . sequence) [part1, part2] . parse)