115 lines
2.4 KiB
Elixir
115 lines
2.4 KiB
Elixir
defmodule Day9 do
|
|
@example """
|
|
35
|
|
20
|
|
15
|
|
25
|
|
47
|
|
40
|
|
62
|
|
55
|
|
65
|
|
95
|
|
102
|
|
117
|
|
150
|
|
182
|
|
127
|
|
219
|
|
299
|
|
277
|
|
309
|
|
576
|
|
"""
|
|
@example_premable_length 5
|
|
|
|
def part1(example \\ false) do
|
|
input = if example, do: @example, else: File.read!("lib/day9/input.txt")
|
|
length = if example, do: @example_premable_length, else: 25
|
|
|
|
input
|
|
|> parse_input()
|
|
|> find_first_non_sum(length)
|
|
end
|
|
|
|
def part2(example \\ false) do
|
|
input = if example, do: @example, else: File.read!("lib/day9/input.txt")
|
|
length = if example, do: @example_premable_length, else: 25
|
|
|
|
input = parse_input(input)
|
|
|
|
non_sum = find_first_non_sum(input, length)
|
|
|
|
{start, length} = find_range_that_sums_to(input, non_sum)
|
|
{_, after_start} = Enum.split(input, start)
|
|
{range, _} = Enum.split(after_start, length)
|
|
Enum.min(range) + Enum.max(range)
|
|
end
|
|
|
|
def parse_input(input) do
|
|
input
|
|
|> String.trim()
|
|
|> String.split("\n")
|
|
|> Enum.map(&String.to_integer/1)
|
|
end
|
|
|
|
def find_first_non_sum([_ | rest] = input, length) do
|
|
if is_number_sum_of_pairs_first(input, length) do
|
|
find_first_non_sum(rest, length)
|
|
else
|
|
{_, [non_sum | _]} = Enum.split(input, length)
|
|
non_sum
|
|
end
|
|
end
|
|
|
|
def is_number_sum_of_pairs_first(inputs, count) do
|
|
{before, [first | _]} = Enum.split(inputs, count)
|
|
|
|
before
|
|
|> all_pairs()
|
|
|> Enum.any?(fn {a, b} ->
|
|
# IO.puts("check if #{a} + #{b} == #{first}")
|
|
a + b == first
|
|
end)
|
|
end
|
|
|
|
def all_pairs(inputs) do
|
|
inputs
|
|
|> Enum.drop(-1)
|
|
|> Enum.with_index()
|
|
|> Enum.flat_map(fn {a, a_index} ->
|
|
{_, rest} = Enum.split(inputs, a_index + 1)
|
|
|
|
Enum.map(rest, fn b ->
|
|
{a, b}
|
|
end)
|
|
end)
|
|
end
|
|
|
|
def find_range_that_sums_to([_ | rest] = numbers, target, start \\ 0) do
|
|
case do_find_leading_range_that_sums_to(numbers, target) do
|
|
false ->
|
|
find_range_that_sums_to(rest, target, start + 1)
|
|
|
|
count when is_integer(count) ->
|
|
{start, count}
|
|
end
|
|
end
|
|
|
|
def do_find_leading_range_that_sums_to(numbers, remaining, count \\ 0)
|
|
|
|
def do_find_leading_range_that_sums_to(_, 0, count) do
|
|
count
|
|
end
|
|
|
|
def do_find_leading_range_that_sums_to([head | rest], remaining, count) do
|
|
# IO.puts("trying start: #{start} count: #{count}, target: #{target}")
|
|
|
|
if head <= remaining do
|
|
do_find_leading_range_that_sums_to(rest, remaining - head, count + 1)
|
|
else
|
|
false
|
|
end
|
|
end
|
|
end
|