-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathocr_numbers.exs
68 lines (61 loc) · 1.85 KB
/
ocr_numbers.exs
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
defmodule OcrNumbers do
@moduledoc false
@ocr_numbers %{
' _ | ||_|' => '0',
' | |' => '1',
' _ _||_ ' => '2',
' _ _| _|' => '3',
' |_| |' => '4',
' _ |_ _|' => '5',
' _ |_ |_|' => '6',
' _ | |' => '7',
' _ |_||_|' => '8',
' _ |_| _|' => '9'
}
@doc """
Given a 3 x 4 grid of pipes, underscores, and spaces, determine which number is represented, or
whether it is garbled.
"""
@spec convert([String.t()]) :: String.t()
def convert(input) do
with :ok <- analyze_rows(input),
:ok <- analyze_columns(input) do
{:ok,
input
|> Stream.transform(0, &((&2 < 3 && {[&1], &2 + 1}) || {[], 0}))
|> Task.async_stream(&analyze_line/1, max_concurrency: 4)
|> Stream.map(&elem(&1, 1))
|> Stream.chunk_every(3)
|> Task.async_stream(&analyze_digits/1, max_concurrency: 4)
|> Stream.map(&elem(&1, 1))
|> Stream.intersperse(',')
|> Stream.concat()
|> Enum.to_list()
|> to_string()}
end
end
defp analyze_rows(input) when rem(length(input), 4) > 0, do: {:error, 'invalid line count'}
defp analyze_rows(_), do: :ok
defp analyze_columns([h | _]) when rem(byte_size(h), 3) > 0, do: {:error, 'invalid column count'}
defp analyze_columns([_ | t]), do: analyze_columns(t)
defp analyze_columns([]), do: :ok
defp analyze_line(line) do
line
|> String.to_charlist()
|> Stream.chunk_every(3)
|> Enum.to_list()
end
defp analyze_digits(lines) do
lines
|> Stream.zip()
|> Stream.map(&Tuple.to_list/1)
|> Stream.map(&Enum.concat/1)
|> Stream.map(&charlist_to_digit/1)
|> Stream.concat()
|> Enum.to_list()
end
for ocr_number <- Map.keys(@ocr_numbers) do
defp charlist_to_digit(unquote(ocr_number)), do: unquote(@ocr_numbers[ocr_number])
end
defp charlist_to_digit(_), do: '?'
end