:- module day7. :- interface. :- import_module basics. :- import_module univ. :- pred run(part::in, lines::in, univ::out) is cc_multi. :- implementation. :- import_module int. :- import_module string. :- import_module list. :- type dir ---> dir(size :: int, dirs :: dirs). :- func size(dir) = int. :- type dirs == list(dir). :- pred dir(dir::out, lines::in, lines::out) is nondet. dir(dir(Size, Dirs)) --> [Cd], {append("$ cd ", _, Cd)}, ["$ ls"], files(Files), dirs(Dirs), {Size = Files + sum(map(size, Dirs))}, (["$ cd .."] ; []). :- pred files(int::out, lines::in, lines::out) is multi. files(Size + Rest) --> [Line], {to_int(head(words(Line)), Size)}, files(Rest). files(Size) --> [Dir], {append("dir ", _, Dir)}, files(Size). files(0) --> []. :- pred dirs(dirs::out, lines::in, lines::out) is multi. dirs([D | Ds]) --> dir(D), dirs(Ds). dirs([]) --> []. :- pred find(pred(dir)::(pred(in) is semidet), dir::in, list(dir)::out) is det. find(P, Dir, Out) :- map(find(P), dirs(Dir), Outs), Children = condense(Outs), (if P(Dir) then Out = [Dir | Children] else Out = Children). :- pred size_lte(int::in, dir::in) is semidet. size_lte(Size, Dir) :- size(Dir) =< Size. :- pred size_gte(int::in, dir::in) is semidet. size_gte(Size, Dir) :- size(Dir) >= Size. :- pred go(part::in, dir::in, int::out) is det. go(one, Top, sum(map(size, Found))) :- find(size_lte(100_000), Top, Found). go(two, Top, det_head(sort(map(size, Found)))) :- find(size_gte(size(Top) - 40_000_000), Top, Found). run(Part, Lines, univ(Out)) :- if dir(Top, Lines, []) then go(Part, Top, Out) else die("bad input").