-module(day_2). -export([part_1/0, part_2/0]). part_1() -> Filename = open_file(), part_1(Filename). part_1(Filename) -> Reports = parse_input(Filename), SafeReports = lists:filter(fun is_safe/1, Reports), length(SafeReports). part_2() -> Filename = open_file(), part_2(Filename). part_2(Filename) -> Reports = parse_input(Filename), SafeReports = lists:filter(fun is_tolerably_safe/1, Reports), length(SafeReports). %% Private Functions open_file() -> Filename = filename:absname("./priv/input/2.txt"), open_file(Filename). open_file(Filename) -> {ok, File} = file:open(Filename, read), File. parse_input(Filename) -> parse_input(Filename, []). parse_input(Filename, Reports) -> case file:read_line(Filename) of {ok, Line} -> Trimmed = string:trim(Line), Chunks = string:split(Trimmed, " ", all), NonEmpty = lists:filter(fun(L) -> length(L) > 0 end, Chunks), Report = lists:map(fun list_to_integer/1, NonEmpty), parse_input(Filename, [Report | Reports]); eof -> Reports end. check_report([_Whatever | []]) -> safe; check_report([A | [B | _] = Rest]) when A < B andalso B - A < 4 -> check_report(Rest, asc); check_report([A | [B | _] = Rest]) when A > B andalso A - B < 4 -> check_report(Rest, desc); check_report(Rest) -> unsafe. check_report([_A], _Whatever) -> safe; check_report([A, B], asc) when A < B andalso B - A < 4 -> safe; check_report([A, B], desc) when A > B andalso A - B < 4 -> safe; check_report([A | [B | _] = Rest], asc) when A < B andalso B - A < 4 -> check_report(Rest, asc); check_report([A | [B | _] = Rest], desc) when A > B andalso A - B < 4 -> check_report(Rest, desc); check_report(_Rest, _Direction) -> unsafe. is_safe(Report) -> safe == check_report(Report). check_report_with_tolerance(Report) -> case check_report(Report) of safe -> safe; unsafe -> check_report_by_removing_levels(Report) end. check_report_by_removing_levels([H | Tail]) -> case check_report(Tail) of safe -> safe; unsafe -> check_report_by_removing_levels([H], Tail) end. check_report_by_removing_levels(_Init, []) -> unsafe; check_report_by_removing_levels(Init, [H | Tail]) -> case check_report(Init ++ Tail) of safe -> safe; unsafe -> check_report_by_removing_levels(Init ++ [H], Tail) end. is_tolerably_safe(Report) -> safe == check_report_with_tolerance(Report). -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). part_1_test() -> TestFile = test_data(), ?assertEqual(2, part_1(TestFile)). part_2_test() -> TestFile = test_data(), ?assertEqual(4, part_2(TestFile)). is_safe_4_test() -> ?assertEqual(safe, check_report([7, 6, 4, 2, 1])). is_safe_5_test() -> ?assertEqual(unsafe, check_report([1, 2, 7, 8, 9])). is_safe_6_test() -> ?assertEqual(unsafe, check_report([9, 7, 6, 2, 1])). is_safe_7_test() -> ?assertEqual(unsafe, check_report([1, 3, 2, 4, 5])). is_safe_8_test() -> ?assertEqual(unsafe, check_report([8, 6, 4, 4, 1])). is_safe_9_test() -> ?assertEqual(safe, check_report([1, 3, 6, 7, 9])). is_safe_10_test() -> ?assertEqual(safe, check_report([40, 42, 44, 46, 49, 51])). test_data() -> open_file("./priv/test_input/2.txt"). -endif.