defmodule Combinatorics do
# === product ===
@doc ~S"""
Cartesian Product of 2 Enumerables.
(At least 2nd Enumerable should be finite)
## Examples
iex> Combinatorics.product([1, 2, 3], 1..3) |> Enum.to_list
[{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3}]
iex> Stream.iterate(1, &(&1+1)) |> Combinatorics.product(1..3) |> Enum.take(10)
[{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3}, {4, 1}]
"""
def product(enum1, enum2) do
product([enum1, enum2])
end
@doc ~S"""
Cartesian Product of multi Enumerables.
(At least last Enumerable should be finite)
## Examples
iex> Combinatorics.product([1..2, 3..4, 5..6]) |> Enum.to_list
[{1, 3, 5}, {1, 3, 6}, {1, 4, 5}, {1, 4, 6}, {2, 3, 5}, {2, 3, 6}, {2, 4, 5}, {2, 4, 6}]
iex> Combinatorics.product([Stream.iterate(1, &(&1+1)), 1..3]) |> Enum.take(10)
[{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3}, {4, 1}]
"""
def product([]), do: []
def product([it|[]]), do: Stream.map(it, &{&1})
def product(its) when is_list(its) do
do_product(its, [[]]) |> Stream.map(&List.to_tuple(:lists.reverse(&1)))
end
defp do_product([], vals), do: vals
defp do_product([x|xs], vals) do
do_product(xs, Stream.flat_map(vals, fn vs -> Stream.map(x, &[&1|vs]) end))
end
# === combinations ===
@doc ~S"""
Combinations - n-length tuples, in sorted order, no repeated elements.
## Examples
iex> Combinatorics.combinations(1..4, 2) |> Enum.to_list
[{1, 2}, {1, 3}, {1, 4}, {2, 3}, {2, 4}, {3, 4}]
iex> Combinatorics.combinations(1..4, 3) |> Enum.to_list
[{1, 2, 3}, {1, 2, 4}, {1, 3, 4}, {2, 3, 4}]
"""
def combinations(_enum, 0), do: []
def combinations(enum, 1), do: Stream.map(enum, &{&1})
def combinations(enum, n) when is_integer(n) and n > 1 do
case next(enum) do
{:next, v, fun} -> &do_combinations({[fun], [v], :next, n - 1}, &1, &2)
_ -> []
end
end
defp do_combinations(_, {:halt, term}, _fun), do: {:halted, term}
defp do_combinations(v, {:suspend, term}, fun) do
{:suspended, term, &do_combinations(v, &1, fun)}
end
defp do_combinations({[], _, _, _}, {:cont, term}, _), do: {:done, term}
defp do_combinations({fs, vals, _, 0}, {:cont, term}, fun) do
do_combinations({fs, vals, :back, 1}, fun.(List.to_tuple(:lists.reverse(vals)), term), fun)
end
defp do_combinations({funs = [f|fs], vals = [_|vs], :next, n}, acc = {:cont, _}, fun) do
case next(f) do
{:next, v, next_f} -> do_combinations({[next_f|funs], [v|vals], :next, n - 1}, acc, fun)
_ -> do_combinations({fs, vs, :back, n + 2}, acc, fun)
end
end
defp do_combinations({[f|fs], [_|vs], :back, n}, acc = {:cont, _}, fun) do
case next(f) do
{:next, v, next_f} -> do_combinations({[next_f|fs], [v|vs], :next, n - 1}, acc, fun)
_ -> do_combinations({fs, vs, :back, n + 1}, acc, fun)
end
end
# === permutations ===
@doc ~S"""
Permutations - full-length tuples, all possible orderings, no repeated elements.
Notice: parameter `enum` can be a List or a Range.
## Examples
iex> Combinatorics.permutations([1, 2, 3]) |> Enum.to_list
[{1, 2, 3}, {1, 3, 2}, {2, 1, 3}, {2, 3, 1}, {3, 1, 2}, {3, 2, 1}]
iex> Combinatorics.permutations(2..4) |> Enum.to_list
[{2, 3, 4}, {2, 4, 3}, {3, 2, 4}, {3, 4, 2}, {4, 2, 3}, {4, 3, 2}]
"""
def permutations(enum) when is_list(enum) do
permutations(enum, length(enum))
end
def permutations(enum = %Range{}) do
permutations(enum, Enum.count(enum))
end
@doc ~S"""
Permutations - n-length tuples, all possible orderings, no repeated elements.
## Examples
iex> Combinatorics.permutations(1..4, 2) |> Enum.to_list
[{1, 2}, {1, 3}, {1, 4}, {2, 1}, {2, 3}, {2, 4}, {3, 1}, {3, 2}, {3, 4}, {4, 1}, {4, 2}, {4, 3}]
iex> Combinatorics.permutations(1..3, 3) |> Enum.to_list
[{1, 2, 3}, {1, 3, 2}, {2, 1, 3}, {2, 3, 1}, {3, 1, 2}, {3, 2, 1}]
"""
def permutations(_enum, 0), do: []
def permutations(enum, 1), do: Stream.map(enum, &{&1})
def permutations(enum, n) when is_integer(n) and n > 1 do
case next(enum) do
{:next, v, rest} -> &do_permutations({[{v, [], rest}], [v], :next, n - 1}, &1, &2)
_ -> []
end
end
defp do_permutations(_, {:halt, term}, _fun), do: {:halted, term}
defp do_permutations(v, {:suspend, term}, fun) do
{:suspended, term, &do_permutations(v, &1, fun)}
end
defp do_permutations({[], _, _, _}, {:cont, term}, _), do: {:done, term}
defp do_permutations({fs, vals, _, 0}, {:cont, term}, fun) do
do_permutations({fs, vals, :back, 1}, fun.(List.to_tuple(:lists.reverse(vals)), term), fun)
end
defp do_permutations({(fs=[{_, r, s}|_]), vals, :next, n}, acc = {:cont, term}, fun) do
case next({:lists.reverse(r), s}) do
{:next, v, rest} -> do_permutations({[{v, [], rest}|fs], [v|vals], :next, n - 1}, acc, fun)
_ -> {:done, term}
end
end
defp do_permutations({[{o, r, s}|fs], [_|vs], :back, n}, acc = {:cont, _}, fun) do
case next(s) do
{:next, v, rest} -> do_permutations({[{v, [o|r], rest}|fs], [v|vs], :next, n - 1}, acc, fun)
_ -> do_permutations({fs, vs, :back, n + 1}, acc, fun)
end
end
# === Common Private Functions ===
defp reducer(v, _), do: {:suspend, v}
defp next([]), do: :done
defp next([x|xs]), do: {:next, x, xs}
defp next(fun) when is_function(fun, 1) do
case fun.({:cont, nil}) do
{:suspended, v, next_fun} -> {:next, v, next_fun}
_ -> :done
end
end
defp next({a, b}) do
case next(a) do
{:next, v, as} -> {:next, v, {as, b}}
_ -> next(b)
end
end
defp next(it) do
case Enumerable.reduce(it, {:cont, nil}, &reducer/2) do
{:suspended, v, next_fun} -> {:next, v, next_fun}
_ -> :done
end
end
end
ExUnit.start()
defmodule CombinatoricsTest do
use ExUnit.Case
# doctest Combinatorics
test "Combinatorics.product(finite, finite)" do
assert (Combinatorics.
product([1, 2, 3], 1..
3) |> Enum.
to_list) == [{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3}] end
test "Combinatorics.product(infinite, finite)" do
assert (Stream.
iterate(1, &(&1+1)) |> Combinatorics.
product(1..
3) |> Enum.
take(10)) == [{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3}, {4, 1}] end
test "Combinatorics.product([1..2, 3..4, 5..6])" do
assert (Combinatorics.
product([1..
2, 3..
4, 5..
6]) |> Enum.
to_list) == [{1, 3, 5}, {1, 3, 6}, {1, 4, 5}, {1, 4, 6}, {2, 3, 5}, {2, 3, 6}, {2, 4, 5}, {2, 4, 6}] end
test "Combinatorics.product([infinite, finite])" do
assert (Combinatorics.
product([Stream.
iterate(1, &(&1+1)), 1..
3]) |> Enum.
take(10)) == [{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 2}, {2, 3}, {3, 1}, {3, 2}, {3, 3}, {4, 1}] end
test "Combinatorics.combinations(enum, 2)" do
assert (Combinatorics.
combinations(1..
4, 2) |> Enum.
to_list) == [{1, 2}, {1, 3}, {1, 4}, {2, 3}, {2, 4}, {3, 4}] end
test "Combinatorics.combinations(enum, 3)" do
assert (Combinatorics.
combinations(1..
4, 3) |> Enum.
to_list) == [{1, 2, 3}, {1, 2, 4}, {1, 3, 4}, {2, 3, 4}] end
test "Combinatorics.combinations(enum, 0)" do
assert (Combinatorics.
combinations(1..
4, 0) |> Enum.
to_list) == [] end
test "Combinatorics.combinations(enum, 1)" do
assert (Combinatorics.
combinations(1..
4, 1) |> Enum.
to_list) == [{1}, {2}, {3}, {4}] end
test "Combinatorics.combinations(enum, n) when n == Enum.count(enum)" do
assert (Combinatorics.
combinations(1..
4, 4) |> Enum.
to_list) == [{1, 2, 3, 4}] end
test "Combinatorics.combinations(enum, n) when n > Enum.count(enum)" do
assert (Combinatorics.
combinations(1..
4, 5) |> Enum.
to_list) == [] end
test "Combinatorics.permutations([1, 2, 3])" do
assert (Combinatorics.
permutations([1, 2, 3]) |> Enum.
to_list) == [{1, 2, 3}, {1, 3, 2}, {2, 1, 3}, {2, 3, 1}, {3, 1, 2}, {3, 2, 1}] end
test "Combinatorics.permutations(2..4)" do
assert (Combinatorics.
permutations(2..
4) |> Enum.
to_list) == [{2, 3, 4}, {2, 4, 3}, {3, 2, 4}, {3, 4, 2}, {4, 2, 3}, {4, 3, 2}] end
test "Combinatorics.permutations(1..4, 2)" do
assert (Combinatorics.
permutations(1..
4, 2) |> Enum.
to_list) == [{1, 2}, {1, 3}, {1, 4}, {2, 1}, {2, 3}, {2, 4}, {3, 1}, {3, 2}, {3, 4}, {4, 1}, {4, 2}, {4, 3}] end
test "Combinatorics.permutations(1..3, 3)" do
assert (Combinatorics.
permutations(1..
3, 3) |> Enum.
to_list) == [{1, 2, 3}, {1, 3, 2}, {2, 1, 3}, {2, 3, 1}, {3, 1, 2}, {3, 2, 1}] end
test "Combinatorics.permutations(range)" do
assert (Combinatorics.
permutations(3..
1) |> Enum.
to_list) == [{3, 2, 1}, {3, 1, 2}, {2, 3, 1}, {2, 1, 3}, {1, 3, 2}, {1, 2, 3}] end
test "Combinatorics.permutations(enum, 0)" do
assert (Combinatorics.
permutations(1..
4, 0) |> Enum.
to_list) == [] end
test "Combinatorics.permutations(enum, 1)" do
assert (Combinatorics.
permutations(1..
4, 1) |> Enum.
to_list) == [{1}, {2}, {3}, {4}] end
test "Combinatorics.permutations(enum, n) when n > Enum.count(enum)" do
assert (Combinatorics.
permutations(1..
4, 5) |> Enum.
to_list) == [] end
end
ZGVmbW9kdWxlIENvbWJpbmF0b3JpY3MgZG8KCiAgIyA9PT0gcHJvZHVjdCA9PT0KCiAgQGRvYyB+UyIiIgogIENhcnRlc2lhbiBQcm9kdWN0IG9mIDIgRW51bWVyYWJsZXMuCiAgKEF0IGxlYXN0IDJuZCBFbnVtZXJhYmxlIHNob3VsZCBiZSBmaW5pdGUpCgogICMjIEV4YW1wbGVzCiAgCiAgICBpZXg+IENvbWJpbmF0b3JpY3MucHJvZHVjdChbMSwgMiwgM10sIDEuLjMpIHw+IEVudW0udG9fbGlzdAogICAgW3sxLCAxfSwgezEsIDJ9LCB7MSwgM30sIHsyLCAxfSwgezIsIDJ9LCB7MiwgM30sIHszLCAxfSwgezMsIDJ9LCB7MywgM31dCiAgCiAgICBpZXg+IFN0cmVhbS5pdGVyYXRlKDEsICYoJjErMSkpIHw+IENvbWJpbmF0b3JpY3MucHJvZHVjdCgxLi4zKSB8PiBFbnVtLnRha2UoMTApCiAgICBbezEsIDF9LCB7MSwgMn0sIHsxLCAzfSwgezIsIDF9LCB7MiwgMn0sIHsyLCAzfSwgezMsIDF9LCB7MywgMn0sIHszLCAzfSwgezQsIDF9XQogICIiIgogIGRlZiBwcm9kdWN0KGVudW0xLCBlbnVtMikgZG8KICAgIHByb2R1Y3QoW2VudW0xLCBlbnVtMl0pCiAgZW5kCgogIEBkb2MgflMiIiIKICBDYXJ0ZXNpYW4gUHJvZHVjdCBvZiBtdWx0aSBFbnVtZXJhYmxlcy4KICAoQXQgbGVhc3QgbGFzdCBFbnVtZXJhYmxlIHNob3VsZCBiZSBmaW5pdGUpCgogICMjIEV4YW1wbGVzCiAgCiAgICBpZXg+IENvbWJpbmF0b3JpY3MucHJvZHVjdChbMS4uMiwgMy4uNCwgNS4uNl0pIHw+IEVudW0udG9fbGlzdAogICAgW3sxLCAzLCA1fSwgezEsIDMsIDZ9LCB7MSwgNCwgNX0sIHsxLCA0LCA2fSwgezIsIDMsIDV9LCB7MiwgMywgNn0sIHsyLCA0LCA1fSwgezIsIDQsIDZ9XQogIAogICAgaWV4PiBDb21iaW5hdG9yaWNzLnByb2R1Y3QoW1N0cmVhbS5pdGVyYXRlKDEsICYoJjErMSkpLCAxLi4zXSkgfD4gRW51bS50YWtlKDEwKQogICAgW3sxLCAxfSwgezEsIDJ9LCB7MSwgM30sIHsyLCAxfSwgezIsIDJ9LCB7MiwgM30sIHszLCAxfSwgezMsIDJ9LCB7MywgM30sIHs0LCAxfV0KICAiIiIKICBkZWYgcHJvZHVjdChbXSksIGRvOiBbXQogIGRlZiBwcm9kdWN0KFtpdHxbXV0pLCBkbzogU3RyZWFtLm1hcChpdCwgJnsmMX0pCiAgZGVmIHByb2R1Y3QoaXRzKSB3aGVuIGlzX2xpc3QoaXRzKSBkbwogICAgZG9fcHJvZHVjdChpdHMsIFtbXV0pIHw+IFN0cmVhbS5tYXAoJkxpc3QudG9fdHVwbGUoOmxpc3RzLnJldmVyc2UoJjEpKSkKICBlbmQKCiAgZGVmcCBkb19wcm9kdWN0KFtdLCB2YWxzKSwgZG86IHZhbHMKICBkZWZwIGRvX3Byb2R1Y3QoW3h8eHNdLCB2YWxzKSBkbwogICAgZG9fcHJvZHVjdCh4cywgU3RyZWFtLmZsYXRfbWFwKHZhbHMsIGZuIHZzIC0+IFN0cmVhbS5tYXAoeCwgJlsmMXx2c10pIGVuZCkpCiAgZW5kCgogICMgPT09IGNvbWJpbmF0aW9ucyA9PT0KCiAgQGRvYyB+UyIiIgogIENvbWJpbmF0aW9ucyAtIG4tbGVuZ3RoIHR1cGxlcywgaW4gc29ydGVkIG9yZGVyLCBubyByZXBlYXRlZCBlbGVtZW50cy4KCiAgIyMgRXhhbXBsZXMKICAKICAgIGlleD4gQ29tYmluYXRvcmljcy5jb21iaW5hdGlvbnMoMS4uNCwgMikgfD4gRW51bS50b19saXN0CiAgICBbezEsIDJ9LCB7MSwgM30sIHsxLCA0fSwgezIsIDN9LCB7MiwgNH0sIHszLCA0fV0KICAKICAgIGlleD4gQ29tYmluYXRvcmljcy5jb21iaW5hdGlvbnMoMS4uNCwgMykgfD4gRW51bS50b19saXN0CiAgICBbezEsIDIsIDN9LCB7MSwgMiwgNH0sIHsxLCAzLCA0fSwgezIsIDMsIDR9XQogICIiIgogIGRlZiBjb21iaW5hdGlvbnMoX2VudW0sIDApLCBkbzogW10KICBkZWYgY29tYmluYXRpb25zKGVudW0sIDEpLCBkbzogU3RyZWFtLm1hcChlbnVtLCAmeyYxfSkKICBkZWYgY29tYmluYXRpb25zKGVudW0sIG4pIHdoZW4gaXNfaW50ZWdlcihuKSBhbmQgbiA+IDEgZG8KICAgIGNhc2UgbmV4dChlbnVtKSBkbwogICAgICB7Om5leHQsIHYsIGZ1bn0gLT4gJmRvX2NvbWJpbmF0aW9ucyh7W2Z1bl0sIFt2XSwgOm5leHQsIG4gLSAxfSwgJjEsICYyKQogICAgICBfICAtPiBbXQogICAgZW5kCiAgZW5kCgogIGRlZnAgZG9fY29tYmluYXRpb25zKF8sIHs6aGFsdCwgdGVybX0sIF9mdW4pLCBkbzogezpoYWx0ZWQsIHRlcm19CiAgZGVmcCBkb19jb21iaW5hdGlvbnModiwgezpzdXNwZW5kLCB0ZXJtfSwgZnVuKSBkbwogICAgezpzdXNwZW5kZWQsIHRlcm0sICZkb19jb21iaW5hdGlvbnModiwgJjEsIGZ1bil9CiAgZW5kCiAgZGVmcCBkb19jb21iaW5hdGlvbnMoe1tdLCBfLCBfLCBffSwgezpjb250LCB0ZXJtfSwgXyksIGRvOiB7OmRvbmUsIHRlcm19CiAgZGVmcCBkb19jb21iaW5hdGlvbnMoe2ZzLCB2YWxzLCBfLCAwfSwgezpjb250LCB0ZXJtfSwgZnVuKSBkbwogICAgZG9fY29tYmluYXRpb25zKHtmcywgdmFscywgOmJhY2ssIDF9LCBmdW4uKExpc3QudG9fdHVwbGUoOmxpc3RzLnJldmVyc2UodmFscykpLCB0ZXJtKSwgZnVuKQogIGVuZAogIGRlZnAgZG9fY29tYmluYXRpb25zKHtmdW5zID0gW2Z8ZnNdLCB2YWxzID0gW198dnNdLCA6bmV4dCwgbn0sIGFjYyA9IHs6Y29udCwgX30sIGZ1bikgZG8KICAgIGNhc2UgbmV4dChmKSBkbwogICAgICB7Om5leHQsIHYsIG5leHRfZn0gLT4gZG9fY29tYmluYXRpb25zKHtbbmV4dF9mfGZ1bnNdLCBbdnx2YWxzXSwgOm5leHQsIG4gLSAxfSwgYWNjLCBmdW4pCiAgICAgIF8gLT4gZG9fY29tYmluYXRpb25zKHtmcywgdnMsIDpiYWNrLCBuICsgMn0sIGFjYywgZnVuKQogICAgZW5kCiAgZW5kCiAgZGVmcCBkb19jb21iaW5hdGlvbnMoe1tmfGZzXSwgW198dnNdLCA6YmFjaywgbn0sIGFjYyA9IHs6Y29udCwgX30sIGZ1bikgZG8KICAgIGNhc2UgbmV4dChmKSBkbwogICAgICB7Om5leHQsIHYsIG5leHRfZn0gLT4gZG9fY29tYmluYXRpb25zKHtbbmV4dF9mfGZzXSwgW3Z8dnNdLCA6bmV4dCwgbiAtIDF9LCBhY2MsIGZ1bikKICAgICAgXyAtPiBkb19jb21iaW5hdGlvbnMoe2ZzLCB2cywgOmJhY2ssIG4gKyAxfSwgYWNjLCBmdW4pCiAgICBlbmQKICBlbmQKCiAgIyA9PT0gcGVybXV0YXRpb25zID09PQoKICBAZG9jIH5TIiIiCiAgUGVybXV0YXRpb25zIC0gZnVsbC1sZW5ndGggdHVwbGVzLCBhbGwgcG9zc2libGUgb3JkZXJpbmdzLCBubyByZXBlYXRlZCBlbGVtZW50cy4KICBOb3RpY2U6IHBhcmFtZXRlciBgZW51bWAgY2FuIGJlIGEgTGlzdCBvciBhIFJhbmdlLgoKICAjIyBFeGFtcGxlcwogIAogICAgaWV4PiBDb21iaW5hdG9yaWNzLnBlcm11dGF0aW9ucyhbMSwgMiwgM10pIHw+IEVudW0udG9fbGlzdAogICAgW3sxLCAyLCAzfSwgezEsIDMsIDJ9LCB7MiwgMSwgM30sIHsyLCAzLCAxfSwgezMsIDEsIDJ9LCB7MywgMiwgMX1dCiAgCiAgICBpZXg+IENvbWJpbmF0b3JpY3MucGVybXV0YXRpb25zKDIuLjQpIHw+IEVudW0udG9fbGlzdAogICAgW3syLCAzLCA0fSwgezIsIDQsIDN9LCB7MywgMiwgNH0sIHszLCA0LCAyfSwgezQsIDIsIDN9LCB7NCwgMywgMn1dCiAgIiIiCiAgZGVmIHBlcm11dGF0aW9ucyhlbnVtKSB3aGVuIGlzX2xpc3QoZW51bSkgZG8KICAgIHBlcm11dGF0aW9ucyhlbnVtLCBsZW5ndGgoZW51bSkpCiAgZW5kCiAgZGVmIHBlcm11dGF0aW9ucyhlbnVtID0gJVJhbmdle30pIGRvCiAgICBwZXJtdXRhdGlvbnMoZW51bSwgRW51bS5jb3VudChlbnVtKSkKICBlbmQKCiAgQGRvYyB+UyIiIgogIFBlcm11dGF0aW9ucyAtIG4tbGVuZ3RoIHR1cGxlcywgYWxsIHBvc3NpYmxlIG9yZGVyaW5ncywgbm8gcmVwZWF0ZWQgZWxlbWVudHMuCgogICMjIEV4YW1wbGVzCiAgCiAgICBpZXg+IENvbWJpbmF0b3JpY3MucGVybXV0YXRpb25zKDEuLjQsIDIpIHw+IEVudW0udG9fbGlzdAogICAgW3sxLCAyfSwgezEsIDN9LCB7MSwgNH0sIHsyLCAxfSwgezIsIDN9LCB7MiwgNH0sIHszLCAxfSwgezMsIDJ9LCB7MywgNH0sIHs0LCAxfSwgezQsIDJ9LCB7NCwgM31dCiAgCiAgICBpZXg+IENvbWJpbmF0b3JpY3MucGVybXV0YXRpb25zKDEuLjMsIDMpIHw+IEVudW0udG9fbGlzdAogICAgW3sxLCAyLCAzfSwgezEsIDMsIDJ9LCB7MiwgMSwgM30sIHsyLCAzLCAxfSwgezMsIDEsIDJ9LCB7MywgMiwgMX1dCiAgIiIiCiAgZGVmIHBlcm11dGF0aW9ucyhfZW51bSwgMCksIGRvOiBbXQogIGRlZiBwZXJtdXRhdGlvbnMoZW51bSwgMSksIGRvOiBTdHJlYW0ubWFwKGVudW0sICZ7JjF9KQogIGRlZiBwZXJtdXRhdGlvbnMoZW51bSwgbikgd2hlbiBpc19pbnRlZ2VyKG4pIGFuZCBuID4gMSBkbwogICAgY2FzZSBuZXh0KGVudW0pIGRvCiAgICAgIHs6bmV4dCwgdiwgcmVzdH0gLT4gJmRvX3Blcm11dGF0aW9ucyh7W3t2LCBbXSwgcmVzdH1dLCBbdl0sIDpuZXh0LCBuIC0gMX0sICYxLCAmMikKICAgICAgXyAgLT4gW10KICAgIGVuZAogIGVuZAoKICBkZWZwIGRvX3Blcm11dGF0aW9ucyhfLCB7OmhhbHQsIHRlcm19LCBfZnVuKSwgZG86IHs6aGFsdGVkLCB0ZXJtfQogIGRlZnAgZG9fcGVybXV0YXRpb25zKHYsIHs6c3VzcGVuZCwgdGVybX0sIGZ1bikgZG8KICAgIHs6c3VzcGVuZGVkLCB0ZXJtLCAmZG9fcGVybXV0YXRpb25zKHYsICYxLCBmdW4pfQogIGVuZAogIGRlZnAgZG9fcGVybXV0YXRpb25zKHtbXSwgXywgXywgX30sIHs6Y29udCwgdGVybX0sIF8pLCBkbzogezpkb25lLCB0ZXJtfQogIGRlZnAgZG9fcGVybXV0YXRpb25zKHtmcywgdmFscywgXywgMH0sIHs6Y29udCwgdGVybX0sIGZ1bikgZG8KICAgIGRvX3Blcm11dGF0aW9ucyh7ZnMsIHZhbHMsIDpiYWNrLCAxfSwgZnVuLihMaXN0LnRvX3R1cGxlKDpsaXN0cy5yZXZlcnNlKHZhbHMpKSwgdGVybSksIGZ1bikKICBlbmQKICBkZWZwIGRvX3Blcm11dGF0aW9ucyh7KGZzPVt7Xywgciwgc318X10pLCB2YWxzLCA6bmV4dCwgbn0sIGFjYyA9IHs6Y29udCwgdGVybX0sIGZ1bikgZG8KICAgIGNhc2UgbmV4dCh7Omxpc3RzLnJldmVyc2UociksIHN9KSBkbwogICAgICB7Om5leHQsIHYsIHJlc3R9IC0+IGRvX3Blcm11dGF0aW9ucyh7W3t2LCBbXSwgcmVzdH18ZnNdLCBbdnx2YWxzXSwgOm5leHQsIG4gLSAxfSwgYWNjLCBmdW4pCiAgICAgIF8gLT4gezpkb25lLCB0ZXJtfQogICAgZW5kCiAgZW5kCiAgZGVmcCBkb19wZXJtdXRhdGlvbnMoe1t7bywgciwgc318ZnNdLCBbX3x2c10sIDpiYWNrLCBufSwgYWNjID0gezpjb250LCBffSwgZnVuKSBkbwogICAgY2FzZSBuZXh0KHMpIGRvCiAgICAgIHs6bmV4dCwgdiwgcmVzdH0gLT4gZG9fcGVybXV0YXRpb25zKHtbe3YsIFtvfHJdLCByZXN0fXxmc10sIFt2fHZzXSwgOm5leHQsIG4gLSAxfSwgYWNjLCBmdW4pCiAgICAgIF8gLT4gZG9fcGVybXV0YXRpb25zKHtmcywgdnMsIDpiYWNrLCBuICsgMX0sIGFjYywgZnVuKQogICAgZW5kCiAgZW5kCgogICMgPT09IENvbW1vbiBQcml2YXRlIEZ1bmN0aW9ucyA9PT0KICBkZWZwIHJlZHVjZXIodiwgXyksIGRvOiB7OnN1c3BlbmQsIHZ9CgogIGRlZnAgbmV4dChbXSksIGRvOiA6ZG9uZQogIGRlZnAgbmV4dChbeHx4c10pLCBkbzogezpuZXh0LCB4LCB4c30KICBkZWZwIG5leHQoZnVuKSB3aGVuIGlzX2Z1bmN0aW9uKGZ1biwgMSkgZG8KICAgIGNhc2UgZnVuLih7OmNvbnQsIG5pbH0pIGRvCiAgICAgIHs6c3VzcGVuZGVkLCB2LCBuZXh0X2Z1bn0gLT4gezpuZXh0LCB2LCBuZXh0X2Z1bn0KICAgICAgXyAtPiA6ZG9uZQogICAgZW5kCiAgZW5kCiAgZGVmcCBuZXh0KHthLCBifSkgZG8KICAgIGNhc2UgbmV4dChhKSBkbwogICAgICB7Om5leHQsIHYsIGFzfSAtPiB7Om5leHQsIHYsIHthcywgYn19CiAgICAgIF8gLT4gbmV4dChiKQogICAgZW5kCiAgZW5kCiAgZGVmcCBuZXh0KGl0KSBkbwogICAgY2FzZSBFbnVtZXJhYmxlLnJlZHVjZShpdCwgezpjb250LCBuaWx9LCAmcmVkdWNlci8yKSBkbwogICAgICB7OnN1c3BlbmRlZCwgdiwgbmV4dF9mdW59IC0+IHs6bmV4dCwgdiwgbmV4dF9mdW59CiAgICAgIF8gLT4gOmRvbmUKICAgIGVuZAogIGVuZAplbmQKCkV4VW5pdC5zdGFydCgpCgpkZWZtb2R1bGUgQ29tYmluYXRvcmljc1Rlc3QgZG8KICB1c2UgRXhVbml0LkNhc2UKICAjIGRvY3Rlc3QgQ29tYmluYXRvcmljcwoKICB0ZXN0ICJDb21iaW5hdG9yaWNzLnByb2R1Y3QoZmluaXRlLCBmaW5pdGUpIiBkbwogICAgYXNzZXJ0IChDb21iaW5hdG9yaWNzLnByb2R1Y3QoWzEsIDIsIDNdLCAxLi4zKSB8PiBFbnVtLnRvX2xpc3QpID09IFt7MSwgMX0sIHsxLCAyfSwgezEsIDN9LCB7MiwgMX0sIHsyLCAyfSwgezIsIDN9LCB7MywgMX0sIHszLCAyfSwgezMsIDN9XQogIGVuZAoKICB0ZXN0ICJDb21iaW5hdG9yaWNzLnByb2R1Y3QoaW5maW5pdGUsIGZpbml0ZSkiIGRvCiAgICBhc3NlcnQgKFN0cmVhbS5pdGVyYXRlKDEsICYoJjErMSkpIHw+IENvbWJpbmF0b3JpY3MucHJvZHVjdCgxLi4zKSB8PiBFbnVtLnRha2UoMTApKSA9PSBbezEsIDF9LCB7MSwgMn0sIHsxLCAzfSwgezIsIDF9LCB7MiwgMn0sIHsyLCAzfSwgezMsIDF9LCB7MywgMn0sIHszLCAzfSwgezQsIDF9XQogIGVuZAoKICB0ZXN0ICJDb21iaW5hdG9yaWNzLnByb2R1Y3QoWzEuLjIsIDMuLjQsIDUuLjZdKSIgZG8KICAgIGFzc2VydCAoQ29tYmluYXRvcmljcy5wcm9kdWN0KFsxLi4yLCAzLi40LCA1Li42XSkgfD4gRW51bS50b19saXN0KSA9PSBbezEsIDMsIDV9LCB7MSwgMywgNn0sIHsxLCA0LCA1fSwgezEsIDQsIDZ9LCB7MiwgMywgNX0sIHsyLCAzLCA2fSwgezIsIDQsIDV9LCB7MiwgNCwgNn1dCiAgZW5kCgogIHRlc3QgIkNvbWJpbmF0b3JpY3MucHJvZHVjdChbaW5maW5pdGUsIGZpbml0ZV0pIiBkbwogICAgYXNzZXJ0IChDb21iaW5hdG9yaWNzLnByb2R1Y3QoW1N0cmVhbS5pdGVyYXRlKDEsICYoJjErMSkpLCAxLi4zXSkgfD4gRW51bS50YWtlKDEwKSkgPT0gW3sxLCAxfSwgezEsIDJ9LCB7MSwgM30sIHsyLCAxfSwgezIsIDJ9LCB7MiwgM30sIHszLCAxfSwgezMsIDJ9LCB7MywgM30sIHs0LCAxfV0KICBlbmQKCiAgdGVzdCAiQ29tYmluYXRvcmljcy5jb21iaW5hdGlvbnMoZW51bSwgMikiIGRvCiAgICBhc3NlcnQgKENvbWJpbmF0b3JpY3MuY29tYmluYXRpb25zKDEuLjQsIDIpIHw+IEVudW0udG9fbGlzdCkgPT0gW3sxLCAyfSwgezEsIDN9LCB7MSwgNH0sIHsyLCAzfSwgezIsIDR9LCB7MywgNH1dCiAgZW5kCgogIHRlc3QgIkNvbWJpbmF0b3JpY3MuY29tYmluYXRpb25zKGVudW0sIDMpIiBkbwogICAgYXNzZXJ0IChDb21iaW5hdG9yaWNzLmNvbWJpbmF0aW9ucygxLi40LCAzKSB8PiBFbnVtLnRvX2xpc3QpID09IFt7MSwgMiwgM30sIHsxLCAyLCA0fSwgezEsIDMsIDR9LCB7MiwgMywgNH1dCiAgZW5kCgogIHRlc3QgIkNvbWJpbmF0b3JpY3MuY29tYmluYXRpb25zKGVudW0sIDApIiBkbwogICAgYXNzZXJ0IChDb21iaW5hdG9yaWNzLmNvbWJpbmF0aW9ucygxLi40LCAwKSB8PiBFbnVtLnRvX2xpc3QpID09IFtdCiAgZW5kCgogIHRlc3QgIkNvbWJpbmF0b3JpY3MuY29tYmluYXRpb25zKGVudW0sIDEpIiBkbwogICAgYXNzZXJ0IChDb21iaW5hdG9yaWNzLmNvbWJpbmF0aW9ucygxLi40LCAxKSB8PiBFbnVtLnRvX2xpc3QpID09IFt7MX0sIHsyfSwgezN9LCB7NH1dCiAgZW5kCgogIHRlc3QgIkNvbWJpbmF0b3JpY3MuY29tYmluYXRpb25zKGVudW0sIG4pIHdoZW4gbiA9PSBFbnVtLmNvdW50KGVudW0pIiBkbwogICAgYXNzZXJ0IChDb21iaW5hdG9yaWNzLmNvbWJpbmF0aW9ucygxLi40LCA0KSB8PiBFbnVtLnRvX2xpc3QpID09IFt7MSwgMiwgMywgNH1dCiAgZW5kCgogIHRlc3QgIkNvbWJpbmF0b3JpY3MuY29tYmluYXRpb25zKGVudW0sIG4pIHdoZW4gbiA+IEVudW0uY291bnQoZW51bSkiIGRvCiAgICBhc3NlcnQgKENvbWJpbmF0b3JpY3MuY29tYmluYXRpb25zKDEuLjQsIDUpIHw+IEVudW0udG9fbGlzdCkgPT0gW10KICBlbmQKCiAgdGVzdCAiQ29tYmluYXRvcmljcy5wZXJtdXRhdGlvbnMoWzEsIDIsIDNdKSIgZG8KICAgIGFzc2VydCAoQ29tYmluYXRvcmljcy5wZXJtdXRhdGlvbnMoWzEsIDIsIDNdKSB8PiBFbnVtLnRvX2xpc3QpID09IFt7MSwgMiwgM30sIHsxLCAzLCAyfSwgezIsIDEsIDN9LCB7MiwgMywgMX0sIHszLCAxLCAyfSwgezMsIDIsIDF9XQogIGVuZAoKICB0ZXN0ICJDb21iaW5hdG9yaWNzLnBlcm11dGF0aW9ucygyLi40KSIgZG8KICAgIGFzc2VydCAoQ29tYmluYXRvcmljcy5wZXJtdXRhdGlvbnMoMi4uNCkgfD4gRW51bS50b19saXN0KSA9PSBbezIsIDMsIDR9LCB7MiwgNCwgM30sIHszLCAyLCA0fSwgezMsIDQsIDJ9LCB7NCwgMiwgM30sIHs0LCAzLCAyfV0KICBlbmQKCiAgdGVzdCAiQ29tYmluYXRvcmljcy5wZXJtdXRhdGlvbnMoMS4uNCwgMikiIGRvCiAgICBhc3NlcnQgKENvbWJpbmF0b3JpY3MucGVybXV0YXRpb25zKDEuLjQsIDIpIHw+IEVudW0udG9fbGlzdCkgPT0gW3sxLCAyfSwgezEsIDN9LCB7MSwgNH0sIHsyLCAxfSwgezIsIDN9LCB7MiwgNH0sIHszLCAxfSwgezMsIDJ9LCB7MywgNH0sIHs0LCAxfSwgezQsIDJ9LCB7NCwgM31dCiAgZW5kCgogIHRlc3QgIkNvbWJpbmF0b3JpY3MucGVybXV0YXRpb25zKDEuLjMsIDMpIiBkbwogICAgYXNzZXJ0IChDb21iaW5hdG9yaWNzLnBlcm11dGF0aW9ucygxLi4zLCAzKSB8PiBFbnVtLnRvX2xpc3QpID09IFt7MSwgMiwgM30sIHsxLCAzLCAyfSwgezIsIDEsIDN9LCB7MiwgMywgMX0sIHszLCAxLCAyfSwgezMsIDIsIDF9XQogIGVuZAoKICB0ZXN0ICJDb21iaW5hdG9yaWNzLnBlcm11dGF0aW9ucyhyYW5nZSkiIGRvCiAgICBhc3NlcnQgKENvbWJpbmF0b3JpY3MucGVybXV0YXRpb25zKDMuLjEpIHw+IEVudW0udG9fbGlzdCkgPT0gW3szLCAyLCAxfSwgezMsIDEsIDJ9LCB7MiwgMywgMX0sIHsyLCAxLCAzfSwgezEsIDMsIDJ9LCB7MSwgMiwgM31dCiAgZW5kCgogIHRlc3QgIkNvbWJpbmF0b3JpY3MucGVybXV0YXRpb25zKGVudW0sIDApIiBkbwogICAgYXNzZXJ0IChDb21iaW5hdG9yaWNzLnBlcm11dGF0aW9ucygxLi40LCAwKSB8PiBFbnVtLnRvX2xpc3QpID09IFtdCiAgZW5kCgogIHRlc3QgIkNvbWJpbmF0b3JpY3MucGVybXV0YXRpb25zKGVudW0sIDEpIiBkbwogICAgYXNzZXJ0IChDb21iaW5hdG9yaWNzLnBlcm11dGF0aW9ucygxLi40LCAxKSB8PiBFbnVtLnRvX2xpc3QpID09IFt7MX0sIHsyfSwgezN9LCB7NH1dCiAgZW5kCgogIHRlc3QgIkNvbWJpbmF0b3JpY3MucGVybXV0YXRpb25zKGVudW0sIG4pIHdoZW4gbiA+IEVudW0uY291bnQoZW51bSkiIGRvCiAgICBhc3NlcnQgKENvbWJpbmF0b3JpY3MucGVybXV0YXRpb25zKDEuLjQsIDUpIHw+IEVudW0udG9fbGlzdCkgPT0gW10KICBlbmQKZW5k