-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathday_10.ex
119 lines (98 loc) · 2.8 KB
/
day_10.ex
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
defmodule AdventOfCode.Y2017.Day10 do
@moduledoc """
--- Day 10: Knot Hash ---
Problem Link: https://adventofcode.com/2017/day/10
Difficulty: s
Tags: hash
"""
require Bitwise
alias AdventOfCode.Helpers.{InputReader, Transformers}
def input, do: InputReader.read_from_file(2017, 10)
@lst 0..255
@size Enum.count(@lst)
@suffix_lengths [17, 31, 73, 47, 23]
def run(input \\ input()) do
{lengths, bytes, list_map} = parse(input)
{run_1(lengths, list_map), run_2(bytes, list_map)}
end
@doc """
Computes knot hash for given `key_string`
## Example
iex> Solution.compute_knot_hash("")
"a2582a3a0e66e6e86e3812dcb672a272"
iex> Solution.compute_knot_hash("AoC 2017")
"33efeb34ea91902bb2f59c9920caa6cd"
iex> Solution.compute_knot_hash("1,2,3")
"3efbe78a8d82f29979031a4aa0b16a9d"
iex> Solution.compute_knot_hash("1,2,4")
"63960835bcdc130f0b66d7ff4f6a5a8e"
"""
def compute_knot_hash(key_string) do
{bytes, list_map} = parse_bytes(key_string)
run_2(bytes, list_map)
end
def parse(data) do
{bytes, list_map} = parse_bytes(data)
{Transformers.int_words(data, ","), bytes, list_map}
end
def parse_bytes(data) do
{
String.to_charlist(data) ++ @suffix_lengths,
to_map_list(@lst)
}
end
defp run_1(lengths, list_map) do
lengths
|> knot_hash(list_map, 0, 0)
|> then(fn {list_map, _, _} -> list_map end)
|> Map.take([0, 1])
|> Map.values()
|> Enum.product()
end
defp run_2(bytes, list_map) do
1..64
|> Enum.reduce({list_map, 0, 0}, fn _, {list_map, pos, skip} ->
knot_hash(bytes, list_map, pos, skip)
end)
|> then(fn {list_map, _, _} -> list_map end)
|> Enum.sort_by(fn {k, _} -> k end)
|> Enum.map(fn {_, v} -> v end)
|> to_hash()
|> to_hex()
end
defp knot_hash([], list_map, pos, skip), do: {list_map, pos, skip}
defp knot_hash([len | rest], list_map, pos, skip) do
knot_hash(
rest,
reverse_slice(list_map, pos, len),
pos + len + skip,
skip + 1
)
end
defp reverse_slice(list_map, pos, len) do
pos..(pos + len - 1)
|> Enum.split_with(&(&1 >= @size))
|> then(fn {a, b} ->
indices = b ++ Enum.map(a, &rem(&1, @size))
Enum.zip(indices, Enum.reverse(indices))
end)
|> Map.new(fn {before, later} -> {later, list_map[before]} end)
|> Map.merge(list_map, fn _, v, _ -> v end)
end
defp to_map_list(list) do
list
|> Enum.with_index(0)
|> Map.new(fn {v, k} -> {k, v} end)
end
defp to_hash(bytes) do
bytes
|> Enum.chunk_every(16)
|> Enum.map(fn bits -> Enum.reduce(bits, &Bitwise.bxor/2) end)
end
defp to_hex(hash) do
hash
|> Enum.map(&Integer.to_string(&1, 16))
|> Enum.map_join(&String.pad_leading(&1, 2, "0"))
|> String.downcase()
end
end