defmodule Ursula.Combinations do

  @keys ~w(a b c d e)

  @doc """
  Returns any combination of the elements in `enum` with exactly `k` elements.
  Repeated elements are handled intelligently.
  ## Examples
      iex> combinations([1, 2, 3], 2) |> Enum.to_list
      [[1, 2], [1, 3], [2, 3]]
      iex> combinations([1, 1, 2], 2) |> Enum.to_list
      [[1, 1], [1, 2]]
  """
  def combinations(enum, k) do
    List.last(do_combinations(enum, k))
    |> Enum.uniq
  end

  defp do_combinations(enum, k) do
    combinations_by_length = [[[]]|List.duplicate([], k)]

    list = Enum.to_list(enum)
    List.foldr list, combinations_by_length, fn x, next ->
      sub = :lists.droplast(next)
      step = [[]|(for l <- sub, do: (for s <- l, do: [x|s]))]
      :lists.zipwith(&:lists.append/2, step, next)
    end
  end

  @doc """
  Returns all possible combinations of Charlie keys
  """
  def explode_keys do
    1..Enum.count(@keys)
    |> Enum.flat_map(&combinations(@keys, &1))
  end

  def keys, do: @keys
end