A series of minor description text changes for clarity and consistency.

Lines 909 and 259 were missing words. Other changes are made for minor grammatical clarity and consistency.

defprotocol Enumerable do
  @moduledoc """
  This is the protocol used by the `Enum` module.

  Usually, when you invoke a function in the module `Enum`, the first argument
  passed to it is a collection which is forwarded to this protocol in order to
  perform operations on the collection.

  For example, in the expression

      Enum.map([1, 2, 3], &(&1 * 2))

  `Enum.map/2` invokes `Enumerable.reduce/3` to perform the reducing operation
  that builds a mapped list by calling the mapping function `&(&1 * 2)` on every
  element in the collection and cons'ing the element with the accumulated list.
  """

  @doc """
  This function performs the reducing operation on a given collection. It
  returns the accumulated value of applying the given function `fun` on every
  element with the accumulated value.

  As an example, here is the implementation of `reduce` for lists:

      def reduce([h|t], acc, fun), do: reduce(t, fun.(h, acc), fun)
      def reduce([], acc, _fun),   do: acc

  As an additional example, here is the implementation of `Enum.map/2` with
  `Enumerable`:

      def map(collection, fun) do
        Enumerable.reduce(collection, [], fn(entry, acc) ->
          [fun.(entry)|acc]
        end) |> :lists.reverse
      end
  """
  def reduce(collection, acc, fun)

  @doc """
  This function is used to check if a value exists within the collection.
  Membership should be tested with the match (`===`) operator.
  """
  def member?(collection, value)

  @doc """
  This function is used to retrieve the collection's size.
  """
  def count(collection)
end

defmodule Enum do
  import Kernel, except: [max: 2, min: 2]

  @moduledoc """
  Provides a set of algorithms that enumerate over collections according to the
  `Enumerable` protocol:

      iex> Enum.map([1, 2, 3], fn(x) -> x * 2 end)
      [2,4,6]

  Some particular types, like dictionaries, yield a specific format on
  enumeration. For dicts, the argument is always a `{ key, value }` tuple:

      iex> dict = HashDict.new [a: 1, b: 2]
      iex> Enum.map(dict, fn { k, v } -> { k, v * 2 } end)
      [a: 2, b: 4]

  Note the functions in the `Enum` module are eager: they always start
  the enumeration of the given collection. The `Stream` module allows
  lazy enumeration of collections and provides infinite streams.

  Since the majority of the functions in `Enum` enumerate the whole
  collection and return a list as result, infinite streams need to
  be carefully used with such functions, as they can potentially run
  forever. For example:

      Enum.each Stream.cycle([1,2,3]), &IO.puts(&1)

  """

  @compile :inline_list_funcs

  @type t :: Enumerable.t
  @type element :: any
  @type index :: non_neg_integer
  @type default :: any

  @doc """
  Checks if `value` exists within the `collection`.

  Membership is tested with the match (`===`) operator, although
  enumerables like ranges may include floats inside the given
  range.

  ## Examples

      iex> Enum.member?(1..10, 5)
      true
      iex> Enum.member?([:a, :b, :c], :d)
      false

  """
  @spec member?(t, element) :: boolean
  def member?(collection, value) do
    Enumerable.member?(collection, value)
  end

  @doc """
  Returns the collection's size.

  ## Examples

      iex> Enum.count([1, 2, 3])
      3

  """
  @spec count(t) :: non_neg_integer
  def count(collection) do
    Enumerable.count(collection)
  end

  @doc """
  Returns the count of items in the collection for which
  `fun` returns `true`.

  ## Examples
      iex> Enum.count([1, 2, 3, 4, 5], fn(x) -> rem(x, 2) == 0 end)
      2

  """
  @spec count(t, (element -> as_boolean(term))) :: non_neg_integer
  def count(collection, fun) do
    Enumerable.reduce(collection, 0, fn(entry, acc) ->
      if fun.(entry), do: acc + 1, else: acc
    end)
  end

  @doc """
  Invokes the given `fun` for each item in the `collection` and returns `false`
  if at least one invocation returns `false`. Otherwise returns `true`.

  ## Examples

      iex> Enum.all?([2, 4, 6], fn(x) -> rem(x, 2) == 0 end)
      true

      iex> Enum.all?([2, 3, 4], fn(x) -> rem(x, 2) == 0 end)
      false

  If no function is given, it defaults to checking if
  all items in the collection evaluate to `true`.

      iex> Enum.all?([1, 2, 3])
      true
      iex> Enum.all?([1, nil, 3])
      false

  """
  @spec all?(t) :: boolean
  @spec all?(t, (element -> as_boolean(term))) :: boolean

  def all?(collection, fun // fn(x) -> x end)

  def all?(collection, fun) when is_list(collection) do
    do_all?(collection, fun)
  end

  def all?(collection, fun) do
    Enumerable.reduce(collection, true, fn(entry, _) ->
      if fun.(entry), do: true, else: throw(:enum_all?)
    end)
  catch
    :enum_all? -> false
  end

  @doc """
  Invokes the given `fun` for each item in the `collection` and returns `true` if
  at least one invocation returns `true`. Returns `false` otherwise.

  ## Examples

      iex> Enum.any?([2, 4, 6], fn(x) -> rem(x, 2) == 1 end)
      false

      iex> Enum.any?([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
      true

  If no function is given, it defaults to checking if
  at least one item in the collection evaluates to `true`.

      iex> Enum.any?([false, false, false])
      false
      iex> Enum.any?([false, true, false])
      true

  """
  @spec any?(t) :: boolean
  @spec any?(t, (element -> as_boolean(term))) :: boolean

  def any?(collection, fun // fn(x) -> x end)

  def any?(collection, fun) when is_list(collection) do
    do_any?(collection, fun)
  end

  def any?(collection, fun) do
    Enumerable.reduce(collection, false, fn(entry, _) ->
      if fun.(entry), do: throw(:enum_any?), else: false
    end)
  catch
    :enum_any? -> true
  end

  @doc """
  Finds the element at the given index (zero-based).
  Returns `default` if index is out of bounds.

  ## Examples

      iex> Enum.at([2, 4, 6], 0)
      2
      iex> Enum.at([2, 4, 6], 2)
      6
      iex> Enum.at([2, 4, 6], 4)
      nil
      iex> Enum.at([2, 4, 6], 4, :none)
      :none

  """
  @spec at(t, integer) :: element | nil
  @spec at(t, integer, default) :: element | default
  def at(collection, n, default // nil) do
    case fetch(collection, n) do
      { :ok, h } -> h
      :error     -> default
    end
  end

  @doc """
  Given an enumerable of enumerables, concatenate the enumerables into a single list.

  ## Examples

      iex> Enum.concat([1..3, 4..6, 7..9])
      [1,2,3,4,5,6,7,8,9]

      iex> Enum.concat([[1, [2], 3], [4], [5, 6]])
      [1,[2],3,4,5,6]

  """
  @spec concat(t) :: t
  def concat(enumerables) do
    do_concat(enumerables)
  end

  @doc """
  Concatenates the enumerable on the right with the enumerable on the left.

  This function produces the same result as the `++` operator for lists.

  ## Examples

      iex> Enum.concat(1..3, 4..6)
      [1,2,3,4,5,6]

      iex> Enum.concat([1, 2, 3], [4, 5, 6])
      [1,2,3,4,5,6]

  """
  @spec concat(t, t) :: t
  def concat(left, right) when is_list(left) and is_list(right) do
    left ++ right
  end

  def concat(left, right) do
    do_concat([left, right])
  end

  defp do_concat(enumerable, acc // []) do
    fun = &[&1|&2]
    Enumerable.reduce(enumerable, acc, &Enumerable.reduce(&1, &2, fun))
      |> :lists.reverse
  end

  @doc """
  Drops the first `count` items from `collection`.

  If a negative value `count` is given, the last `count`
  values will be dropped. The collection is enumerated
  once to retrieve the proper index and the remaining
  calculation is performed from the end.

  ## Examples

      iex> Enum.drop([1, 2, 3], 2)
      [3]
      iex> Enum.drop([1, 2, 3], 10)
      []
      iex> Enum.drop([1, 2, 3], 0)
      [1,2,3]
      iex> Enum.drop([1, 2, 3], -1)
      [1,2]

  """
  @spec drop(t, integer) :: list
  def drop(collection, count) when is_list(collection) and count >= 0 do
    do_drop(collection, count)
  end

  def drop(collection, count) when count >= 0 do
    { list, _ } = Enumerable.reduce(collection, { [], count }, fn
      _, { _, count } when count > 0 -> { [], count - 1 }
      entry, { list, count }         -> { [entry|list], count }
    end)
    :lists.reverse(list)
  end

  def drop(collection, count) when count < 0 do
    do_drop(reverse(collection), abs(count)) |> :lists.reverse
  end

  @doc """
  Drops items at the beginning of `collection` while `fun` returns `true`.

  ## Examples

      iex> Enum.drop_while([1, 2, 3, 4, 5], fn(x) -> x < 3 end)
      [3,4,5]
  """
  @spec drop_while(t, (element -> as_boolean(term))) :: list
  def drop_while(collection, fun) when is_list(collection) do
    do_drop_while(collection, fun)
  end

  def drop_while(collection, fun) do
    Enumerable.reduce(collection, [], fn
      entry, []  -> if fun.(entry), do: [], else: [entry]
      entry, acc -> [entry|acc]
    end) |> :lists.reverse
  end

  @doc """
  Invokes the given `fun` for each item in the `collection`.
  Returns `:ok`.

  ## Examples

      Enum.each(["some", "example"], fn(x) -> IO.puts x end)
      "some"
      "example"
      #=> :ok

  """
  @spec each(t, (element -> any)) :: :ok
  def each(collection, fun) when is_list(collection) do
    :lists.foreach(fun, collection)
    :ok
  end

  def each(collection, fun) do
    Enumerable.reduce(collection, nil, fn(entry, _) ->
      fun.(entry)
      nil
    end)
    :ok
  end

  @doc """
  Returns `true` if the collection is empty, otherwise `false`.

  ## Examples

      iex> Enum.empty?([])
      true
      iex> Enum.empty?([1, 2, 3])
      false

  """
  @spec empty?(t) :: boolean
  def empty?(collection) when is_list(collection) do
    collection == []
  end

  def empty?(collection) do
    Enumerable.reduce(collection, true, fn(_, _) -> throw(:enum_empty?) end)
  catch
    :enum_empty? -> false
  end

  @doc """
  Finds the element at the given index (zero-based).
  Returns `{ :ok, element }` if found, otherwise `:error`.

  A negative index can be passed, which means the collection is
  enumerated once and the index is counted from the end (i.e.
  `-1` fetches the last element).

  ## Examples

      iex> Enum.fetch([2, 4, 6], 0)
      { :ok, 2 }
      iex> Enum.fetch([2, 4, 6], 2)
      { :ok, 6 }
      iex> Enum.fetch([2, 4, 6], 4)
      :error

  """
  @spec fetch(t, integer) :: { :ok, element } | :error
  def fetch(collection, n) when is_list(collection) and n >= 0 do
    do_fetch(collection, n)
  end

  def fetch(collection, n) when n >= 0 do
    Enumerable.reduce(collection, 0, fn(entry, acc) ->
      if acc == n do
        throw({ :enum_fetch, entry })
      else
        acc + 1
      end
    end)

    :error
  catch
    { :enum_fetch, entry } -> { :ok, entry }
  end

  def fetch(collection, n) when n < 0 do
    fetch(reverse(collection), abs(n + 1))
  end

  @doc """
  Finds the element at the given index (zero-based).
  Raises `OutOfBoundsError` if the given position
  is outside the range of the collection.

  ## Examples

      iex> Enum.fetch!([2, 4, 6], 0)
      2

      iex> Enum.fetch!([2, 4, 6], 2)
      6

      iex> Enum.fetch!([2, 4, 6], 4)
      ** (Enum.OutOfBoundsError) out of bounds error

  """
  @spec fetch!(t, integer) :: element | no_return
  def fetch!(collection, n) do
    case fetch(collection, n) do
      { :ok, h } -> h
      :error     -> raise Enum.OutOfBoundsError
    end
  end

  @doc """
  Filters the collection, i.e. returns only those elements
  for which `fun` returns `true`.

  ## Examples

      iex> Enum.filter([1, 2, 3], fn(x) -> rem(x, 2) == 0 end)
      [2]

  """
  @spec filter(t, (element -> as_boolean(term))) :: list
  def filter(collection, fun) when is_list(collection) do
    lc item inlist collection, fun.(item), do: item
  end

  def filter(collection, fun) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      if fun.(entry), do: [entry|acc], else: acc
    end) |> :lists.reverse
  end

  @doc """
  Filters the collection and maps its values in one pass.

  ## Examples

      iex> Enum.filter_map([1, 2, 3], fn(x) -> rem(x, 2) == 0 end, &(&1 * 2))
      [4]

  """
  @spec filter_map(t, (element -> as_boolean(term)), (element -> element)) :: list
  def filter_map(collection, filter, mapper) when is_list(collection) do
    lc item inlist collection, filter.(item), do: mapper.(item)
  end

  def filter_map(collection, filter, mapper) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      if filter.(entry), do: [mapper.(entry)|acc], else: acc
    end) |> :lists.reverse
  end

  @doc """
  Returns the first item for which `fun` returns a truthy value. If no such
  item is found, returns `ifnone`.

  ## Examples

      iex> Enum.find([2, 4, 6], fn(x) -> rem(x, 2) == 1 end)
      nil

      iex> Enum.find([2, 4, 6], 0, fn(x) -> rem(x, 2) == 1 end)
      0

      iex> Enum.find([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
      3

  """
  @spec find(t, (element -> any)) :: element | nil
  @spec find(t, default, (element -> any)) :: element | default

  def find(collection, ifnone // nil, fun)

  def find(collection, ifnone, fun) when is_list(collection) do
    do_find(collection, ifnone, fun)
  end

  def find(collection, ifnone, fun) do
    Enumerable.reduce(collection, ifnone, fn(entry, ifnone) ->
      if fun.(entry), do: throw({ :enum_find, entry }), else: ifnone
    end)
  catch
    { :enum_find, entry } -> entry
  end

  @doc """
  Similar to `find/3`, but returns the value of the function
  invocation instead of the element itself.

  ## Examples

      iex> Enum.find_value([2, 4, 6], fn(x) -> rem(x, 2) == 1 end)
      nil

      iex> Enum.find_value([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
      true

  """
  @spec find_value(t, (element -> any)) :: any | :nil
  @spec find_value(t, any, (element -> any)) :: any | :nil

  def find_value(collection, ifnone // nil, fun)

  def find_value(collection, ifnone, fun) when is_list(collection) do
    do_find_value(collection, ifnone, fun)
  end

  def find_value(collection, ifnone, fun) do
    Enumerable.reduce(collection, ifnone, fn(entry, _) ->
      fun_entry = fun.(entry)
      if fun_entry, do: throw({ :enum_find, fun_entry })
    end)
  catch
    { :enum_find, entry } -> entry
  end

  @doc """
  Similar to `find/3`, but returns the index (zero-based)
  of the element instead of the element itself.

  ## Examples

      iex> Enum.find_index([2, 4, 6], fn(x) -> rem(x, 2) == 1 end)
      nil

      iex> Enum.find_index([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
      1

  """
  @spec find_index(t, (element -> any)) :: index | :nil
  def find_index(collection, fun) when is_list(collection) do
    do_find_index(collection, 0, fun)
  end

  def find_index(collection, fun) do
    Enumerable.reduce(collection, 0, fn(entry, acc) ->
      if fun.(entry), do: throw({ :enum_find_index, acc }), else: acc + 1
    end)
    nil
  catch
    { :enum_find_index, idx } -> idx
  end

  @doc """
  Returns the first item in the collection or `nil` otherwise.

  ## Examples

      iex> Enum.first([])
      nil
      iex> Enum.first([1, 2, 3])
      1

  """
  @spec first(t) :: :nil | element
  def first([]),    do: nil
  def first([h|_]), do: h

  def first(collection) do
    Enumerable.reduce(collection, nil, fn(entry, _) ->
      throw({ :enum_first, entry})
    end)
  catch
    { :enum_first, entry } -> entry
  end

  @doc """
  Returns a new collection appending the result of invoking `fun`
  on each corresponding item of `collection`.

  Given function should return a list.

  ## Examples

      iex> Enum.flat_map([:a, :b, :c], fn(x) -> [x, x] end)
      [:a, :a, :b, :b, :c, :c]

  """
  @spec flat_map(t, (element -> any)) :: list
  def flat_map([], _fun), do: []

  def flat_map(collection, fun) when is_list(collection) do
    :lists.flatmap(fun, collection)
  end

  def flat_map(collection, fun) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      acc ++ fun.(entry)
    end)
  end

  @doc """
  Intersperses the `element` between each element of the enumeration.

  Complexity: O(n)

  ## Examples

      iex> Enum.intersperse([1, 2, 3], 0)
      [1, 0, 2, 0, 3]
      iex> Enum.intersperse([1], 0)
      [1]
      iex> Enum.intersperse([], 0)
      []

  """
  @spec intersperse(t, element) :: list
  def intersperse(collection, element) do
    l = Enumerable.reduce(collection, [], fn(x, acc) -> [x | [element | acc]] end)
        |> :lists.reverse()
    case l do
      []      -> []
      [_ | t] -> t  # Head is a superfluous intersperser element
    end
  end

  @doc """
  Joins the given `collection` according to `joiner`.
  `joiner` can be either a binary or a list and the
  result will be of the same type as `joiner`. If
  `joiner` is not passed at all, it defaults to an
  empty binary.

  All items in the collection must be convertible
  to a binary, otherwise an error is raised.

  ## Examples

      iex> Enum.join([1, 2, 3])
      "123"
      iex> Enum.join([1, 2, 3], " = ")
      "1 = 2 = 3"

  """
  @spec join(t) :: String.t
  @spec join(t, String.t) :: String.t
  def join(collection, joiner // "")

  def join(collection, joiner) when is_binary(joiner) do
    Enumerable.reduce(collection, "", fn
      entry, ""  -> to_string(entry)
      entry, acc -> acc <> joiner <> to_string(entry)
    end)
  end

  @doc """
  Returns a new collection, where each item is the result
  of invoking `fun` on each corresponding item of `collection`.

  For dicts, the function accepts a key-value tuple.

  ## Examples

      iex> Enum.map([1, 2, 3], fn(x) -> x * 2 end)
      [2, 4, 6]

      iex> Enum.map([a: 1, b: 2], fn({k, v}) -> { k, -v } end)
      [a: -1, b: -2]

  """
  @spec map(t, (element -> any)) :: list
  def map(collection, fun) when is_list(collection) do
    lc item inlist collection, do: fun.(item)
  end

  def map(collection, fun) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      [fun.(entry)|acc]
    end) |> :lists.reverse
  end

  @doc """
  Maps and joins the given `collection` in one pass.
  `joiner` can be either a binary or a list and the
  result will be of the same type as `joiner`. If
  `joiner` is not passed at all, it defaults to an
  empty binary.

  All items in the collection must be convertible
  to a binary, otherwise an error is raised.

  ## Examples

      iex> Enum.map_join([1, 2, 3], &(&1 * 2))
      "246"
      iex> Enum.map_join([1, 2, 3], " = ", &(&1 * 2))
      "2 = 4 = 6"

  """
  @spec map_join(t, (element -> any)) :: String.t
  @spec map_join(t, String.t, (element -> any)) :: String.t
  def map_join(collection, joiner // "", mapper)

  def map_join(collection, joiner, mapper) when is_binary(joiner) do
    Enumerable.reduce(collection, "", fn
      entry, ""  -> to_string(mapper, entry)
      entry, acc -> acc <> joiner <> to_string(mapper, entry)
    end)
  end

  @doc """
  Invokes the given `fun` for each item in the `collection`
  while also keeping an accumulator. Returns a tuple where
  the first element is the mapped collection and the second
  one is the final accumulator.

  For dicts, the first tuple element must be a `{ key, value }`
  tuple.

  ## Examples

      iex> Enum.map_reduce([1, 2, 3], 0, fn(x, acc) -> { x * 2, x + acc } end)
      { [2, 4, 6], 6 }

  """
  @spec map_reduce(t, any, (element, any -> any)) :: any
  def map_reduce(collection, acc, fun) do
    { list, acc } = Enumerable.reduce(collection, { [], acc }, fn(entry, { list, acc }) ->
      { new_entry, acc } = fun.(entry, acc)
      { [new_entry|list], acc }
    end)
    { :lists.reverse(list), acc }
  end

  @doc """
  Partitions `collection` into two collections, where the first one contains elements
  for which `fun` returns a truthy value, and the second one -- for which `fun`
  returns `false` or `nil`.

  ## Examples

      iex> Enum.partition([1, 2, 3], fn(x) -> rem(x, 2) == 0 end)
      { [2], [1,3] }

  """
  @spec partition(t, (element -> any)) :: {list, list}
  def partition(collection, fun) do
    { acc1, acc2 } = Enumerable.reduce(collection, { [], [] }, fn(entry, { acc1, acc2 }) ->
      if fun.(entry) do
        { [entry|acc1], acc2 }
      else
        { acc1, [entry|acc2] }
      end
    end)

    { :lists.reverse(acc1), :lists.reverse(acc2) }
  end

  @doc """
  Invokes `fun` for each element in the collection passing that element and the
  accumulator `acc` as arguments. `fun`'s return value is stored in `acc`.
  Returns the accumulator.

  ## Examples

      iex> Enum.reduce([1, 2, 3], 0, fn(x, acc) -> x + acc end)
      6

  """
  @spec reduce(t, any, (element, any -> any)) :: any
  def reduce(collection, acc, fun) do
    Enumerable.reduce(collection, acc, fun)
  end

  @doc """
  Invokes `fun` for each element in the collection passing that element and the
  accumulator `acc` as arguments. `fun`'s return value is stored in `acc`.
  The first element of the collection is used as the initial value of `acc`.
  Returns the accumulator.

  ## Examples

      iex> Enum.reduce([1, 2, 3, 4], fn(x, acc) -> x * acc end)
      24

  """
  @spec reduce(t, (element, any -> any)) :: any
  def reduce([h|t], fun) do
    Enum.reduce(t, h, fun)
  end

  def reduce([], _fun) do
    raise Enum.EmptyError
  end

  def reduce(collection, fun) do
    result = Enumerable.reduce(collection, :first, fn
      entry, :first -> { :reduce, entry }
      entry, { :reduce, acc } -> { :reduce, fun.(entry, acc) }
    end)

    case result do
      :first -> raise Enum.EmptyError
      { :reduce, acc } -> acc
    end
  end

  @doc """
  Returns elements of collection for which `fun` returns `false`.

  ## Examples

      iex> Enum.reject([1, 2, 3], fn(x) -> rem(x, 2) == 0 end)
      [1, 3]

  """
  @spec reject(t, (element -> as_boolean(term))) :: list
  def reject(collection, fun) when is_list(collection) do
    lc item inlist collection, !fun.(item), do: item
  end

  def reject(collection, fun) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      unless fun.(entry), do: [entry|acc], else: acc
    end) |> :lists.reverse
  end

  @doc """
  Reverses the collection.

  ## Examples

      iex> Enum.reverse([1, 2, 3])
      [3, 2, 1]

  """
  @spec reverse(t) :: list
  def reverse(collection) when is_list(collection) do
    :lists.reverse(collection)
  end

  def reverse(collection) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      [entry|acc]
    end)
  end

  @doc """
  Reverses the collection and appends the tail.
  This is an optimization for
  `Enum.concat(Enum.reverse(collection), tail)`.

  ## Examples

      iex> Enum.reverse([1, 2, 3], [4, 5, 6])
      [3, 2, 1, 4, 5, 6]
  """
  @spec reverse(t, t) :: list
  def reverse(collection, tail) when is_list(collection) and is_list(tail) do
    :lists.reverse(collection, tail)
  end

  def reverse(collection, tail) do
    Enumerable.reduce(collection, to_list(tail), fn(entry, acc) ->
      [entry|acc]
    end)
  end

  @doc """
  Returns a list of collection elements shuffled.

  Notice you need to explicitly call `:random.seed/1` and
  set a seed value for the random algorithm. Otherwise, the
  default seed will be set which will always return the same
  result. For example, one could do the following to set a seed
  dynamically:

      :random.seed(:erlang.now)

  ## Examples

      iex(1)> Enum.shuffle([1, 2, 3])
      [3, 2, 1]
      iex(2)> Enum.shuffle([1, 2, 3])
      [3, 1, 2]

  """
  @spec shuffle(t) :: list
  def shuffle(collection) do
    randomized = Enumerable.reduce(collection, [], fn x, acc ->
      [{ :random.uniform, x }|acc]
    end)
    unwrap(:lists.keysort(1, randomized), [])
  end

  @doc """
  Returns a sorted list of collection elements. Uses the merge sort algorithm.

  ## Examples

      iex> Enum.sort([3, 2, 1])
      [1,2,3]

  """
  @spec sort(t) :: list
  def sort(collection) when is_list(collection) do
    :lists.sort(collection)
  end

  def sort(collection) do
    fun = &(&1 <= &2)
    Enumerable.reduce(collection, [], &sort_reducer(&1, &2, fun)) |>
      sort_terminator(fun)
  end

  @doc """
  Returns a sorted list of collection elements. Uses the merge sort algorithm.

  ## Examples

      iex> Enum.sort([1, 2, 3], &(&1 > &2))
      [3,2,1]

  """
  @spec sort(t, (element, element -> boolean)) :: list
  def sort(collection, fun) when is_list(collection) do
    :lists.sort(fun, collection)
  end

  def sort(collection, fun) do
    Enumerable.reduce(collection, [], &sort_reducer(&1, &2, fun)) |>
      sort_terminator(fun)
  end

  @doc """
  Splits the enumerable into two collections, leaving `count`
  elements in the first one. If `count` is a negative number,
  it starts counting from the back to the beginning of the
  collection.

  Be aware that a negative `count` implies the collection
  will be enumerated twice: once to calculate the position, and
  a second time to do the actual splitting.

  ## Examples

      iex> Enum.split([1, 2, 3], 2)
      { [1,2], [3] }
      iex> Enum.split([1, 2, 3], 10)
      { [1,2,3], [] }
      iex> Enum.split([1, 2, 3], 0)
      { [], [1,2,3] }
      iex> Enum.split([1, 2, 3], -1)
      { [1,2], [3] }
      iex> Enum.split([1, 2, 3], -5)
      { [], [1,2,3] }

  """
  @spec split(t, integer) :: {list, list}
  def split(collection, count) when is_list(collection) and count >= 0 do
    do_split(collection, count, [])
  end

  def split(collection, count) when count >= 0 do
    { _, list1, list2 } =
      Enumerable.reduce(collection, { count, [], [] }, fn(entry, { counter, acc1, acc2 }) ->
        if counter > 0 do
          { counter - 1, [entry|acc1], acc2 }
        else
          { counter, acc1, [entry|acc2] }
        end
      end)

    { :lists.reverse(list1), :lists.reverse(list2) }
  end

  def split(collection, count) when count < 0 do
    do_split_reverse(reverse(collection), abs(count), [])
  end

  @doc """
  Splits `collection` in two while `fun` returns `true`.

  ## Examples

      iex> Enum.split_while([1, 2, 3, 4], fn(x) -> x < 3 end)
      { [1, 2], [3, 4] }
  """
  @spec split_while(t, (element -> as_boolean(term))) :: {list, list}
  def split_while(collection, fun) when is_list(collection) do
    do_split_while(collection, fun, [])
  end

  def split_while(collection, fun) do
    { list1, list2 } = Enumerable.reduce(collection, { [], [] }, fn
      entry, { acc1, [] } ->
        if fun.(entry), do: { [entry|acc1], [] }, else: { acc1, [entry] }
      entry, { acc1, acc2 } ->
        { acc1, [entry|acc2] }
    end)

    { :lists.reverse(list1), :lists.reverse(list2) }
  end

  @doc """
  Takes the first `count` items from the collection.

  If a negative value `count` is given, the last `count`
  values will be taken. The collection is enumerated
  once to retrieve the proper index. The remaining
  calculation is performed from the end.

  ## Examples

      iex> Enum.take([1, 2, 3], 2)
      [1,2]
      iex> Enum.take([1, 2, 3], 10)
      [1,2,3]
      iex> Enum.take([1, 2, 3], 0)
      []
      iex> Enum.take([1, 2, 3], -1)
      [3]

  """
  @spec take(t, integer) :: list

  def take(_collection, 0) do
    []
  end

  def take(collection, count) when is_list(collection) and count > 0 do
    do_take(collection, count)
  end

  def take(collection, count) when count > 0 do
    { list, _ } = Enumerable.reduce(collection, { [], count }, fn(entry, { list, count }) ->
      if count > 1 do
        { [entry|list], count - 1 }
      else
        throw({ :enum_take, [entry|list] })
      end
    end)
    :lists.reverse(list)
  catch
    { :enum_take, list } -> :lists.reverse(list)
  end

  def take(collection, count) when count < 0 do
    do_take_reverse(reverse(collection), abs(count), [])
  end

  @doc """
  Returns a collection of every `nth` items in the collection,
  starting with the first.

  ## Examples

      iex> Enum.take_every(1..10, 2)
      [1, 3, 5, 7, 9]

  """
  @spec take_every(t, integer) :: list
  def take_every(_collection, 0), do: []
  def take_every(collection, nth) do
    res = Enumerable.reduce(collection, nil, fn
      x, { acc, ^nth } -> { [x | acc], 1 }
      _, { acc, count } -> { acc, count + 1 }
      x, nil -> { [x], 1 }
    end)

    case res do
      nil -> []
      { list, _ } -> :lists.reverse(list)
    end
  end

  @doc """
  Takes the items at the beginning of `collection` while `fun` returns `true`.

  ## Examples

      iex> Enum.take_while([1, 2, 3], fn(x) -> x < 3 end)
      [1, 2]

  """
  @spec take_while(t, (element -> as_boolean(term))) :: list
  def take_while(collection, fun) when is_list(collection) do
    do_take_while(collection, fun)
  end

  def take_while(collection, fun) do
    Enumerable.reduce(collection, [], fn(entry, list) ->
      if fun.(entry) do
        [entry|list]
      else
        throw({ :enum_take, list })
      end
    end) |> :lists.reverse
  catch
    { :enum_take, list } -> :lists.reverse(list)
  end

  @doc """
  Convert `collection` to a list.

  ## Examples

      iex> Enum.to_list(1 .. 3)
      [1, 2, 3]

  """
  @spec to_list(t) :: [term]
  def to_list(collection) when is_list collection do
    collection
  end

  def to_list(collection) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      [entry|acc]
    end) |> :lists.reverse
  end

  @doc """
  Iterates the enumerable, removing all duplicated items.

  ## Examples

      iex> Enum.uniq([1, 2, 3, 2, 1])
      [1, 2, 3]

      iex> Enum.uniq([{1, :x}, {2, :y}, {1, :z}], fn {x, _} -> x end)
      [{1,:x}, {2,:y}]

  """
  @spec uniq(t) :: list
  @spec uniq(t, (element -> term)) :: list
  def uniq(collection, fun // fn x -> x end)

  def uniq(collection, fun) when is_list(collection) do
    do_uniq(collection, [], fun)
  end

  def uniq(collection, fun) do
    { list, _ } = Enumerable.reduce(collection, { [], [] }, fn(entry, { acc, fun_acc }) ->
      fun_entry = fun.(entry)
      if :lists.member(fun_entry, fun_acc) do
        { acc, fun_acc }
      else
        { [entry|acc], [fun_entry|fun_acc] }
      end
    end)

    :lists.reverse(list)
  end

  @doc """
  Zips corresponding elements from two collections into one list
  of tuples. The number of elements in the resulting list is
  dictated by the first enum. If the second list is shorter,
  values are filled with `nil`.

  ## Examples

      iex> Enum.zip([1, 2, 3], [:a, :b, :c])
      [{1,:a},{2,:b},{3,:c}]

      iex> Enum.zip([1,2,3,4,5], [:a, :b, :c])
      [{1,:a},{2,:b},{3,:c},{4,nil},{5,nil}]

  """
  @spec zip(t, t) :: [{any, any}]
  def zip(coll1, coll2) when is_list(coll1) and is_list(coll2) do
    do_zip(coll1, coll2)
  end

  def zip(coll1, coll2) do
    list = to_list(coll1)
    { zipped, rest } = Enumerable.reduce(coll2, { [], list }, fn
      entry, { acc, [h|t] } -> { [{h, entry}|acc], t }
      _,     { acc, [] }    -> throw { :enum_zip, acc }
    end)

    list = :lists.foldl(fn(x, acc) -> [{ x, nil }|acc] end, zipped, rest)
    :lists.reverse(list)
  catch
    { :enum_zip, rest } -> :lists.reverse(rest)
  end

  @doc """
  Returns the maximum value.
  Raises `EmptyError` if the collection is empty.

  ## Examples

      iex> Enum.max([1, 2, 3])
      3

  """
  @spec max(t) :: element | no_return
  def max(collection) do
    reduce(collection, &Kernel.max(&1, &2))
  end

  @doc """
  Returns the maximum value as calculated by the given function.
  Raises `EmptyError` if the collection is empty.

  ## Examples

      iex> Enum.max_by(["a", "aa", "aaa"], fn(x) -> String.length(x) end)
      "aaa"

  """
  @spec max_by(t, (element -> any)) :: element | no_return
  def max_by([h|t], fun) do
    { max, _ } = Enumerable.reduce(t, { h, fun.(h) }, fn(entry, { _, fun_max } = old) ->
      fun_entry = fun.(entry)
      if fun_entry > fun_max, do: { entry, fun_entry }, else: old
    end)

    max
  end

  def max_by([], _fun) do
    raise Enum.EmptyError
  end

  def max_by(collection, fun) do
    result = Enumerable.reduce(collection, :first, fn
      entry, :first ->
        { :reduce, entry, fun.(entry) }
      entry, { :reduce, _, fun_max } = old ->
        fun_entry = fun.(entry)
        if fun_entry > fun_max, do: { :reduce, entry, fun_entry }, else: old
    end)

    case result do
      :first                -> raise Enum.EmptyError
      { :reduce, entry, _ } -> entry
    end
  end

  @doc """
  Returns the minimum value.
  Raises `EmptyError` if the collection is empty.

  ## Examples

      iex> Enum.min([1, 2, 3])
      1

  """
  @spec min(t) :: element | no_return
  def min(collection) do
    reduce(collection, &Kernel.min(&1, &2))
  end

  @doc """
  Returns the minimum value as calculated by the given function.
  Raises `EmptyError` if the collection is empty.

  ## Examples

      iex> Enum.min_by(["a", "aa", "aaa"], fn(x) -> String.length(x) end)
      "a"

  """
  @spec min_by(t, (element -> any)) :: element | no_return
  def min_by([h|t], fun) do
    { min, _ } = Enumerable.reduce(t, { h, fun.(h) }, fn(entry, { _, fun_min } = old) ->
      fun_entry = fun.(entry)
      if fun_entry < fun_min, do: { entry, fun_entry }, else: old
    end)

    min
  end

  def min_by([], _fun) do
    raise Enum.EmptyError
  end

  def min_by(collection, fun) do
    result = Enumerable.reduce(collection, :first, fn
      entry, :first ->
        { :reduce, entry, fun.(entry) }
      entry, { :reduce, _, fun_min } = old ->
        fun_entry = fun.(entry)
        if fun_entry < fun_min, do: { :reduce, entry, fun_entry }, else: old
    end)

    case result do
      :first                -> raise Enum.EmptyError
      { :reduce, entry, _ } -> entry
    end
  end

  @doc """
  Returns the collection with each element wrapped in a tuple
  alongside its index.

  ## Examples

      iex> Enum.with_index [1,2,3]
      [{1,0},{2,1},{3,2}]

  """
  @spec with_index(t) :: list({ element, non_neg_integer })
  def with_index(collection) do
    map_reduce(collection, 0, fn x, acc ->
      { { x, acc }, acc + 1 }
    end) |> elem(0)
  end

  @doc """
  Shortcut to `chunks(coll, n, n)`.
  """
  @spec chunks(t, non_neg_integer) :: [list]
  def chunks(coll, n), do: chunks(coll, n, n, nil)

  @doc """
  Returns a collection of lists containing `n` items each, where
  each new chunk starts `step` elements into the collection.
  `step` is optional and, if not passed, defaults to `n`, i.e.
  chunks do not overlap. If the final chunk does not have `n`
  elements to fill the chunk, elements are taken as necessary
  from `pad` if it was passed. If `pad` is passed and does not
  have enough elements to fill the chunk, then the chunk is
  returned anyway with less than `n` elements. If `pad` is not
  passed at all or is nil, then the partial chunk is discarded
  from the result.

  ## Examples

      iex> Enum.chunks([1, 2, 3, 4, 5, 6], 2)
      [[1, 2], [3, 4], [5, 6]]
      iex> Enum.chunks([1, 2, 3, 4, 5, 6], 3, 2)
      [[1, 2, 3], [3, 4, 5]]
      iex> Enum.chunks([1, 2, 3, 4, 5, 6], 3, 2, [7])
      [[1, 2, 3], [3, 4, 5], [5, 6, 7]]
      iex> Enum.chunks([1, 2, 3, 4, 5, 6], 3, 3, [])
      [[1, 2, 3], [4, 5, 6]]

  """
  @spec chunks(t, non_neg_integer, non_neg_integer) :: [list]
  @spec chunks(t, non_neg_integer, non_neg_integer, list | nil) :: [list]
  def chunks(coll, n, step, pad // nil) when n > 0 and step > 0 do
    { acc, buffer, i } =
      Enumerable.reduce(coll, { [], [], 0 }, fn
        x, { acc, buffer, i } when i < n ->
          chunks_n(acc, [x|buffer], i + 1, n, step)
        x, { acc, buffer, i } when i < step ->
          chunks_step(acc, [x|buffer], i + 1, step)
      end)

    if nil?(pad) || i == 0 do
      :lists.reverse(acc)
    else
      buffer = :lists.reverse(buffer) ++ take(pad, n - i)
      :lists.reverse([buffer|acc])
    end
  end

  @doc """
  Splits `coll` on every element for which `fun` returns a new value.

  ## Examples

      iex> Enum.chunks_by([1, 2, 2, 3, 4, 4, 6, 7, 7], &(rem(&1, 2) == 1))
      [[1], [2, 2], [3], [4, 4, 6], [7, 7]]

  """
  @spec chunks_by(t, (element -> any)) :: [list]
  def chunks_by(coll, fun) do
    res =
      Enumerable.reduce(coll, nil, fn
        x, {acc, buffer, value} ->
          new_value = fun.(x)
          if new_value == value do
            {acc, [x | buffer], value}
          else
            {[:lists.reverse(buffer) | acc], [x], new_value}
          end
        x, nil -> {[], [x], fun.(x)}
      end)
    if nil?(res) do
      []
    else
      {acc, buffer, _} = res
      :lists.reverse([:lists.reverse(buffer) | acc])
    end
  end

  @doc """
  Returns a subset list of the given collection. Drops elements
  until element position `start`, then takes `count` elements.

  ## Examples

      iex> Enum.slice(1..100, 5, 10)
      [6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

  """
  @spec slice(t, integer, non_neg_integer) :: list

  def slice(coll, start, count) when start < 0 do
    { list, new_start } = enumerate_and_count(coll, start)
    if new_start >= 0, do: slice(list, new_start, count)
  end

  def slice(coll, start, count) when is_list(coll) and start >= 0 and count > 0 do
    do_slice(coll, start, count)
  end

  def slice(coll, start, count) when start >= 0 and count > 0 do
    { start, _, list } = Enumerable.reduce(coll, { start, count, [] }, fn
      _entry, { start, count, _list } when start > 0 ->
        { start-1, count, [] }
      entry, { start, count, list } when count > 1 ->
        { start, count-1, [entry|list] }
      entry, { _start, _count, list } ->
        throw { :enum_slice, [entry|list] }
    end)

    if start <= 0, do: :lists.reverse(list)
  catch
    { :enum_slice, list } -> :lists.reverse(list)
  end

  def slice(coll, start, 0) do
    Enumerable.reduce(coll, start, fn _, start ->
      if start > 0, do: start-1, else: throw :enum_slice
    end)
    nil
  catch
    :enum_slice -> []
  end

  @doc """
  Returns a subset list of the given collection. Drops elements
  until element position `range.first`, then takes elements until element
  position `range.last` (inclusive).

  Positions are calculated by adding the number of items in the collection to
  negative positions (so position -3 in a collection with count 5 becomes
  position 2).

  The first position (after adding count to negative positions) must be smaller
  or equal to the last position.

  ## Examples

      iex> Enum.slice(1..100, 5..10)
      [6, 7, 8, 9, 10, 11]

  """
  def slice(coll, first..last) when first >= 0 and last >= 0 do
    # Simple case, which works on infinite collections
    if last - first >= 0 do
      slice(coll, first, last - first + 1)
    end
  end

  def slice(coll, first..last) do
    { list, count } = enumerate_and_count(coll, 0)
    corr_first = if first >= 0, do: first, else: first + count
    corr_last = if last >= 0, do: last, else: last + count
    length = corr_last - corr_first + 1
    if corr_first >= 0 and length > 0 do
      slice(list, corr_first, length)
    end
  end

  ## Helpers

  @compile { :inline, chunks_n: 5, chunks_step: 4, to_string: 2 }

  defp chunks_n(acc, buffer, i, n, step) when i == n do
    acc = [:lists.reverse(buffer)|acc]
    chunks_step(acc, buffer, i, step)
  end

  defp chunks_n(acc, buffer, i, _n, _step) do
    { acc, buffer, i }
  end

  defp chunks_step(acc, buffer, i, step) when i >= step do
    left = i - step
    { acc, take(buffer, left), left }
  end

  defp chunks_step(acc, buffer, i, _step) do
    { acc, buffer, i }
  end

  defp enumerate_and_count(collection, count) when is_list(collection) do
    { collection, length(collection) - abs(count) }
  end

  defp enumerate_and_count(collection, count) do
    map_reduce(collection, -abs(count), fn(x, acc) -> { x, acc + 1 } end)
  end

  defp to_string(mapper, entry) do
    case mapper.(entry) do
      x when is_binary(x) -> x
      o -> String.Chars.to_string(o)
    end
  end

  ## Implementations

  ## all?

  defp do_all?([h|t], fun) do
    if fun.(h) do
      do_all?(t, fun)
    else
      false
    end
  end

  defp do_all?([], _) do
    true
  end

  ## any?

  defp do_any?([h|t], fun) do
    if fun.(h) do
      true
    else
      do_any?(t, fun)
    end
  end

  defp do_any?([], _) do
    false
  end

  ## fetch

  defp do_fetch([h|_], 0), do: { :ok, h }
  defp do_fetch([_|t], n), do: do_fetch(t, n - 1)
  defp do_fetch([], _),    do: :error

  ## drop

  defp do_drop([_|t], counter) when counter > 0 do
    do_drop(t, counter - 1)
  end

  defp do_drop(list, 0) do
    list
  end

  defp do_drop([], _) do
    []
  end

  ## drop_while

  defp do_drop_while([h|t], fun) do
    if fun.(h) do
      do_drop_while(t, fun)
    else
      [h|t]
    end
  end

  defp do_drop_while([], _) do
    []
  end

  ## find

  defp do_find([h|t], ifnone, fun) do
    if fun.(h) do
      h
    else
      do_find(t, ifnone, fun)
    end
  end

  defp do_find([], ifnone, _) do
    ifnone
  end

  ## find_index

  defp do_find_index([h|t], counter, fun) do
    if fun.(h) do
      counter
    else
      do_find_index(t, counter + 1, fun)
    end
  end

  defp do_find_index([], _, _) do
    nil
  end

  ## find_value

  defp do_find_value([h|t], ifnone, fun) do
    fun.(h) || do_find_value(t, ifnone, fun)
  end

  defp do_find_value([], ifnone, _) do
    ifnone
  end

  ## shuffle

  defp unwrap([{_, h} | collection], t) do
    unwrap(collection, [h|t])
  end

  defp unwrap([], t), do: t

  ## sort

  defp sort_reducer(entry, { :split, y, x, r, rs, bool }, fun) do
    cond do
      fun.(y, entry) == bool ->
        { :split, entry, y, [x|r], rs, bool }
      fun.(x, entry) == bool ->
        { :split, y, entry, [x|r], rs, bool }
      r == [] ->
        { :split, y, x, [entry], rs, bool }
      true ->
        { :pivot, y, x, r, rs, entry, bool }
    end
  end

  defp sort_reducer(entry, { :pivot, y, x, r, rs, s, bool }, fun) do
    cond do
      fun.(y, entry) == bool ->
        { :pivot, entry, y, [x | r], rs, s, bool }
      fun.(x, entry) == bool ->
        { :pivot, y, entry, [x | r], rs, s, bool }
      fun.(s, entry) == bool ->
        { :split, entry, s, [], [[y, x | r] | rs], bool }
      true ->
        { :split, s, entry, [], [[y, x | r] | rs], bool }
    end
  end

  defp sort_reducer(entry, [x], fun) do
    { :split, entry, x, [], [], fun.(x, entry) }
  end

  defp sort_reducer(entry, acc, _fun) do
    [entry|acc]
  end

  defp sort_terminator({ :split, y, x, r, rs, bool }, fun) do
    sort_merge([[y, x | r] | rs], fun, bool)
  end

  defp sort_terminator({ :pivot, y, x, r, rs, s, bool }, fun) do
    sort_merge([[s], [y, x | r] | rs], fun, bool)
  end

  defp sort_terminator(acc, _fun) do
    acc
  end

  defp sort_merge(list, fun, true), do:
    reverse_sort_merge(list, [], fun, true)

  defp sort_merge(list, fun, false), do:
    sort_merge(list, [], fun, false)

  defp sort_merge([t1, [h2 | t2] | l], acc, fun, true), do:
    sort_merge(l, [sort_merge_1(t1, h2, t2, [], fun, false) | acc], fun, true)

  defp sort_merge([[h2 | t2], t1 | l], acc, fun, false), do:
    sort_merge(l, [sort_merge_1(t1, h2, t2, [], fun, false) | acc], fun, false)

  defp sort_merge([l], [], _fun, _bool), do: l

  defp sort_merge([l], acc, fun, bool), do:
    reverse_sort_merge([:lists.reverse(l, []) | acc], [], fun, bool)

  defp sort_merge([], acc, fun, bool), do:
    reverse_sort_merge(acc, [], fun, bool)

  defp reverse_sort_merge([[h2 | t2], t1 | l], acc, fun, true), do:
    reverse_sort_merge(l, [sort_merge_1(t1, h2, t2, [], fun, true) | acc], fun, true)

  defp reverse_sort_merge([t1, [h2 | t2] | l], acc, fun, false), do:
    reverse_sort_merge(l, [sort_merge_1(t1, h2, t2, [], fun, true) | acc], fun, false)

  defp reverse_sort_merge([l], acc, fun, bool), do:
    sort_merge([:lists.reverse(l, []) | acc], [], fun, bool)

  defp reverse_sort_merge([], acc, fun, bool), do:
    sort_merge(acc, [], fun, bool)

  defp sort_merge_1([h1 | t1], h2, t2, m, fun, bool) do
    if fun.(h1, h2) == bool do
      sort_merge_2(h1, t1, t2, [h2 | m], fun, bool)
    else
      sort_merge_1(t1, h2, t2, [h1 | m], fun, bool)
    end
  end

  defp sort_merge_1([], h2, t2, m, _fun, _bool), do:
    :lists.reverse(t2, [h2 | m])

  defp sort_merge_2(h1, t1, [h2 | t2], m, fun, bool) do
    if fun.(h1, h2) == bool do
      sort_merge_2(h1, t1, t2, [h2 | m], fun, bool)
    else
      sort_merge_1(t1, h2, t2, [h1 | m], fun, bool)
    end
  end

  defp sort_merge_2(h1, t1, [], m, _fun, _bool), do:
    :lists.reverse(t1, [h1 | m])

  ## split

  defp do_split([h|t], counter, acc) when counter > 0 do
    do_split(t, counter - 1, [h|acc])
  end

  defp do_split(list, 0, acc) do
    { :lists.reverse(acc), list }
  end

  defp do_split([], _, acc) do
    { :lists.reverse(acc), [] }
  end

  defp do_split_reverse([h|t], counter, acc) when counter > 0 do
    do_split_reverse(t, counter - 1, [h|acc])
  end

  defp do_split_reverse(list, 0, acc) do
    { :lists.reverse(list), acc }
  end

  defp do_split_reverse([], _, acc) do
    { [], acc }
  end

  ## split_while

  defp do_split_while([h|t], fun, acc) do
    if fun.(h) do
      do_split_while(t, fun, [h|acc])
    else
      { :lists.reverse(acc), [h|t] }
    end
  end

  defp do_split_while([], _, acc) do
    { :lists.reverse(acc), [] }
  end

  ## take

  defp do_take([h|t], counter) when counter > 0 do
    [h|do_take(t, counter - 1)]
  end

  defp do_take(_list, 0) do
    []
  end

  defp do_take([], _) do
    []
  end

  defp do_take_reverse([h|t], counter, acc) when counter > 0 do
    do_take_reverse(t, counter - 1, [h|acc])
  end

  defp do_take_reverse(_list, 0, acc) do
    acc
  end

  defp do_take_reverse([], _, acc) do
    acc
  end

  ## take_while

  defp do_take_while([h|t], fun) do
    if fun.(h) do
      [h|do_take_while(t, fun)]
    else
      []
    end
  end

  defp do_take_while([], _) do
    []
  end

  ## uniq

  defp do_uniq([h|t], acc, fun) do
    fun_h = fun.(h)
    case :lists.member(fun_h, acc) do
      true  -> do_uniq(t, acc, fun)
      false -> [h|do_uniq(t, [fun_h|acc], fun)]
    end
  end

  defp do_uniq([], _acc, _fun) do
    []
  end

  ## zip

  defp do_zip([h1|next1], other) do
    { h2, next2 } = do_zip_next(other)
    [{ h1, h2 }|do_zip(next1, next2)]
  end

  defp do_zip([], _) do
    []
  end

  defp do_zip_next([h|t]), do: { h, t }
  defp do_zip_next([]),    do: { nil, [] }

  ## slice

  defp do_slice([], start, _count) do
    if start == 0, do: []
  end

  defp do_slice(list, start, 0) do
    if start < length(list), do: []
  end

  defp do_slice([h|t], 0, count) do
    [h|do_slice(t, 0, count-1)]
  end

  defp do_slice([_|t], start, count) do
    do_slice(t, start-1, count)
  end
end

defimpl Enumerable, for: List do
  def reduce([h|t], acc, fun) do
    reduce(t, fun.(h, acc), fun)
  end

  def reduce([], acc, _fun) do
    acc
  end

  def member?([], _),       do: false
  def member?(list, value), do: :lists.member(value, list)

  def count(list), do: length(list)
end

defimpl Enumerable, for: Function do
  def reduce(function, acc, fun) do
    function.(acc, fun)
  end

  def member?(function, value) do
    function.(false, fn
      ^value, _ -> throw(:function_member?)
      _, _ -> false
    end)
  catch
    :function_member? -> true
  end

  def count(function) do
    function.(0, fn(_, acc) -> acc + 1 end)
  end
end

defprotocol Enumerable do
  @moduledoc """
  This is the protocol used by the `Enum` module.

  Usually, when you invoke a function in the module `Enum`, the first argument
  passed to it is a collection which is forwarded to this protocol in order to
  perform operations on the collection.

  For example, in the expression

      Enum.map([1, 2, 3], &(&1 * 2))

  `Enum.map/2` invokes `Enumerable.reduce/3` to perform the reducing operation
  that builds a mapped list by calling the mapping function `&(&1 * 2)` on every
  element in the collection and cons'ing the element with the accumulated list.
  """

  @doc """
  This function performs the reducing operation on a given collection. It
  returns the accumulated value of applying the given function `fun` on every
  element with the accumulated value.

  As an example, here is the implementation of `reduce` for lists:

      def reduce([h|t], acc, fun), do: reduce(t, fun.(h, acc), fun)
      def reduce([], acc, _fun),   do: acc

  As an additional example, here is the implementation of `Enum.map/2` with
  `Enumerable`:

      def map(collection, fun) do
        Enumerable.reduce(collection, [], fn(entry, acc) ->
          [fun.(entry)|acc]
        end) |> :lists.reverse
      end
  """
  def reduce(collection, acc, fun)

  @doc """
  The function is used to check if a value exists within the collection.
  Membership should be tested with the match (`===`) operator.
  """
  def member?(collection, value)

  @doc """
  The function is used to retrieve the collection's size.
  """
  def count(collection)
end

defmodule Enum do
  import Kernel, except: [max: 2, min: 2]

  @moduledoc """
  Provides a set of algorithms that enumerate over collections according to the
  `Enumerable` protocol:

      iex> Enum.map([1, 2, 3], fn(x) -> x * 2 end)
      [2,4,6]

  Some particular types, like dictionaries, yield a specific format on
  enumeration. For dicts, the argument is always a `{ key, value }` tuple:

      iex> dict = HashDict.new [a: 1, b: 2]
      iex> Enum.map(dict, fn { k, v } -> { k, v * 2 } end)
      [a: 2, b: 4]

  Note the functions in the `Enum` module are eager: they always start
  the enumeration of the given collection. The `Stream` module allows
  lazy enumeration of collections and also provides infinite streams.

  Since the majority of the functions in `Enum` enumerate the whole
  collection and return a list as result, infinite streams need to
  be carefully used with such functions, as they can potentially run
  forever, for example:

      Enum.each Stream.cycle([1,2,3]), &IO.puts(&1)

  """

  @compile :inline_list_funcs

  @type t :: Enumerable.t
  @type element :: any
  @type index :: non_neg_integer
  @type default :: any

  @doc """
  Checks if `value` exists within the `collection`.

  Membership is tested with the match (`===`) operator although
  enumerables like ranges may include floats inside the given
  range.

  ## Examples

      iex> Enum.member?(1..10, 5)
      true
      iex> Enum.member?([:a, :b, :c], :d)
      false

  """
  @spec member?(t, element) :: boolean
  def member?(collection, value) do
    Enumerable.member?(collection, value)
  end

  @doc """
  Returns the collection's size.

  ## Examples

      iex> Enum.count([1, 2, 3])
      3

  """
  @spec count(t) :: non_neg_integer
  def count(collection) do
    Enumerable.count(collection)
  end

  @doc """
  Returns the count of items in the collection for which
  `fun` returns `true`.

  ## Examples
      iex> Enum.count([1, 2, 3, 4, 5], fn(x) -> rem(x, 2) == 0 end)
      2

  """
  @spec count(t, (element -> as_boolean(term))) :: non_neg_integer
  def count(collection, fun) do
    Enumerable.reduce(collection, 0, fn(entry, acc) ->
      if fun.(entry), do: acc + 1, else: acc
    end)
  end

  @doc """
  Invokes the given `fun` for each item in the `collection` and returns `false`
  if at least one invocation returns `false`. Otherwise returns `true`.

  ## Examples

      iex> Enum.all?([2, 4, 6], fn(x) -> rem(x, 2) == 0 end)
      true

      iex> Enum.all?([2, 3, 4], fn(x) -> rem(x, 2) == 0 end)
      false

  If no function is given, it defaults to checking if
  all items in the collection evaluate to `true`.

      iex> Enum.all?([1, 2, 3])
      true
      iex> Enum.all?([1, nil, 3])
      false

  """
  @spec all?(t) :: boolean
  @spec all?(t, (element -> as_boolean(term))) :: boolean

  def all?(collection, fun // fn(x) -> x end)

  def all?(collection, fun) when is_list(collection) do
    do_all?(collection, fun)
  end

  def all?(collection, fun) do
    Enumerable.reduce(collection, true, fn(entry, _) ->
      if fun.(entry), do: true, else: throw(:enum_all?)
    end)
  catch
    :enum_all? -> false
  end

  @doc """
  Invokes the given `fun` for each item in the `collection` and returns `true` if
  at least one invocation returns `true`. Returns `false` otherwise.

  ## Examples

      iex> Enum.any?([2, 4, 6], fn(x) -> rem(x, 2) == 1 end)
      false

      iex> Enum.any?([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
      true

  If no function is given, it defaults to checking if
  at least one item in the collection evaluates to `true`.

      iex> Enum.any?([false, false, false])
      false
      iex> Enum.any?([false, true, false])
      true

  """
  @spec any?(t) :: boolean
  @spec any?(t, (element -> as_boolean(term))) :: boolean

  def any?(collection, fun // fn(x) -> x end)

  def any?(collection, fun) when is_list(collection) do
    do_any?(collection, fun)
  end

  def any?(collection, fun) do
    Enumerable.reduce(collection, false, fn(entry, _) ->
      if fun.(entry), do: throw(:enum_any?), else: false
    end)
  catch
    :enum_any? -> true
  end

  @doc """
  Finds the element at the given index (zero-based).
  Returns `default` if index is out of bounds.

  ## Examples

      iex> Enum.at([2, 4, 6], 0)
      2
      iex> Enum.at([2, 4, 6], 2)
      6
      iex> Enum.at([2, 4, 6], 4)
      nil
      iex> Enum.at([2, 4, 6], 4, :none)
      :none

  """
  @spec at(t, integer) :: element | nil
  @spec at(t, integer, default) :: element | default
  def at(collection, n, default // nil) do
    case fetch(collection, n) do
      { :ok, h } -> h
      :error     -> default
    end
  end

  @doc """
  Given an enumerable of enumerables, concatenate the enumerables into a single list.

  ## Examples

      iex> Enum.concat([1..3, 4..6, 7..9])
      [1,2,3,4,5,6,7,8,9]

      iex> Enum.concat([[1, [2], 3], [4], [5, 6]])
      [1,[2],3,4,5,6]

  """
  @spec concat(t) :: t
  def concat(enumerables) do
    do_concat(enumerables)
  end

  @doc """
  Concatenates the enumerable on the right with the enumerable on the left.

  This function produces the same result the `++` operator for lists.

  ## Examples

      iex> Enum.concat(1..3, 4..6)
      [1,2,3,4,5,6]

      iex> Enum.concat([1, 2, 3], [4, 5, 6])
      [1,2,3,4,5,6]

  """
  @spec concat(t, t) :: t
  def concat(left, right) when is_list(left) and is_list(right) do
    left ++ right
  end

  def concat(left, right) do
    do_concat([left, right])
  end

  defp do_concat(enumerable, acc // []) do
    fun = &[&1|&2]
    Enumerable.reduce(enumerable, acc, &Enumerable.reduce(&1, &2, fun))
      |> :lists.reverse
  end

  @doc """
  Drops the first `count` items from `collection`.

  If a negative value `count` is given the last `count`
  values will be dropped. The collection is enumerated
  once to retrieve the proper index and the remaining
  calculation is performed from the end.

  ## Examples

      iex> Enum.drop([1, 2, 3], 2)
      [3]
      iex> Enum.drop([1, 2, 3], 10)
      []
      iex> Enum.drop([1, 2, 3], 0)
      [1,2,3]
      iex> Enum.drop([1, 2, 3], -1)
      [1,2]

  """
  @spec drop(t, integer) :: list
  def drop(collection, count) when is_list(collection) and count >= 0 do
    do_drop(collection, count)
  end

  def drop(collection, count) when count >= 0 do
    { list, _ } = Enumerable.reduce(collection, { [], count }, fn
      _, { _, count } when count > 0 -> { [], count - 1 }
      entry, { list, count }         -> { [entry|list], count }
    end)
    :lists.reverse(list)
  end

  def drop(collection, count) when count < 0 do
    do_drop(reverse(collection), abs(count)) |> :lists.reverse
  end

  @doc """
  Drops items at the beginning of `collection` while `fun` returns `true`.

  ## Examples

      iex> Enum.drop_while([1, 2, 3, 4, 5], fn(x) -> x < 3 end)
      [3,4,5]
  """
  @spec drop_while(t, (element -> as_boolean(term))) :: list
  def drop_while(collection, fun) when is_list(collection) do
    do_drop_while(collection, fun)
  end

  def drop_while(collection, fun) do
    Enumerable.reduce(collection, [], fn
      entry, []  -> if fun.(entry), do: [], else: [entry]
      entry, acc -> [entry|acc]
    end) |> :lists.reverse
  end

  @doc """
  Invokes the given `fun` for each item in the `collection`.
  Returns `:ok`.

  ## Examples

      Enum.each(["some", "example"], fn(x) -> IO.puts x end)
      "some"
      "example"
      #=> :ok

  """
  @spec each(t, (element -> any)) :: :ok
  def each(collection, fun) when is_list(collection) do
    :lists.foreach(fun, collection)
    :ok
  end

  def each(collection, fun) do
    Enumerable.reduce(collection, nil, fn(entry, _) ->
      fun.(entry)
      nil
    end)
    :ok
  end

  @doc """
  Returns `true` if the collection is empty, otherwise `false`.

  ## Examples

      iex> Enum.empty?([])
      true
      iex> Enum.empty?([1, 2, 3])
      false

  """
  @spec empty?(t) :: boolean
  def empty?(collection) when is_list(collection) do
    collection == []
  end

  def empty?(collection) do
    Enumerable.reduce(collection, true, fn(_, _) -> throw(:enum_empty?) end)
  catch
    :enum_empty? -> false
  end

  @doc """
  Finds the element at the given index (zero-based).
  Returns `{ :ok, element }` if found, otherwise `:error`.

  A negative index can be passed, which means the collection is
  enumerated once and the index is counted from the end (i.e.
  `-1` fetches the last element).

  ## Examples

      iex> Enum.fetch([2, 4, 6], 0)
      { :ok, 2 }
      iex> Enum.fetch([2, 4, 6], 2)
      { :ok, 6 }
      iex> Enum.fetch([2, 4, 6], 4)
      :error

  """
  @spec fetch(t, integer) :: { :ok, element } | :error
  def fetch(collection, n) when is_list(collection) and n >= 0 do
    do_fetch(collection, n)
  end

  def fetch(collection, n) when n >= 0 do
    Enumerable.reduce(collection, 0, fn(entry, acc) ->
      if acc == n do
        throw({ :enum_fetch, entry })
      else
        acc + 1
      end
    end)

    :error
  catch
    { :enum_fetch, entry } -> { :ok, entry }
  end

  def fetch(collection, n) when n < 0 do
    fetch(reverse(collection), abs(n + 1))
  end

  @doc """
  Finds the element at the given index (zero-based).
  Raises `OutOfBoundsError` if the given position
  is outside the range of the collection.

  ## Examples

      iex> Enum.fetch!([2, 4, 6], 0)
      2

      iex> Enum.fetch!([2, 4, 6], 2)
      6

      iex> Enum.fetch!([2, 4, 6], 4)
      ** (Enum.OutOfBoundsError) out of bounds error

  """
  @spec fetch!(t, integer) :: element | no_return
  def fetch!(collection, n) do
    case fetch(collection, n) do
      { :ok, h } -> h
      :error     -> raise Enum.OutOfBoundsError
    end
  end

  @doc """
  Filters the collection, i.e. returns only those elements
  for which `fun` returns `true`.

  ## Examples

      iex> Enum.filter([1, 2, 3], fn(x) -> rem(x, 2) == 0 end)
      [2]

  """
  @spec filter(t, (element -> as_boolean(term))) :: list
  def filter(collection, fun) when is_list(collection) do
    lc item inlist collection, fun.(item), do: item
  end

  def filter(collection, fun) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      if fun.(entry), do: [entry|acc], else: acc
    end) |> :lists.reverse
  end

  @doc """
  Filters the collection and maps its values in one pass.

  ## Examples

      iex> Enum.filter_map([1, 2, 3], fn(x) -> rem(x, 2) == 0 end, &(&1 * 2))
      [4]

  """
  @spec filter_map(t, (element -> as_boolean(term)), (element -> element)) :: list
  def filter_map(collection, filter, mapper) when is_list(collection) do
    lc item inlist collection, filter.(item), do: mapper.(item)
  end

  def filter_map(collection, filter, mapper) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      if filter.(entry), do: [mapper.(entry)|acc], else: acc
    end) |> :lists.reverse
  end

  @doc """
  Returns the first item for which `fun` returns a truthy value. If no such
  item is found, returns `ifnone`.

  ## Examples

      iex> Enum.find([2, 4, 6], fn(x) -> rem(x, 2) == 1 end)
      nil

      iex> Enum.find([2, 4, 6], 0, fn(x) -> rem(x, 2) == 1 end)
      0

      iex> Enum.find([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
      3

  """
  @spec find(t, (element -> any)) :: element | nil
  @spec find(t, default, (element -> any)) :: element | default

  def find(collection, ifnone // nil, fun)

  def find(collection, ifnone, fun) when is_list(collection) do
    do_find(collection, ifnone, fun)
  end

  def find(collection, ifnone, fun) do
    Enumerable.reduce(collection, ifnone, fn(entry, ifnone) ->
      if fun.(entry), do: throw({ :enum_find, entry }), else: ifnone
    end)
  catch
    { :enum_find, entry } -> entry
  end

  @doc """
  Similar to `find/3`, but returns the value of the function
  invocation instead of the element itself.

  ## Examples

      iex> Enum.find_value([2, 4, 6], fn(x) -> rem(x, 2) == 1 end)
      nil

      iex> Enum.find_value([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
      true

  """
  @spec find_value(t, (element -> any)) :: any | :nil
  @spec find_value(t, any, (element -> any)) :: any | :nil

  def find_value(collection, ifnone // nil, fun)

  def find_value(collection, ifnone, fun) when is_list(collection) do
    do_find_value(collection, ifnone, fun)
  end

  def find_value(collection, ifnone, fun) do
    Enumerable.reduce(collection, ifnone, fn(entry, _) ->
      fun_entry = fun.(entry)
      if fun_entry, do: throw({ :enum_find, fun_entry })
    end)
  catch
    { :enum_find, entry } -> entry
  end

  @doc """
  Similar to `find/3`, but returns the index (zero-based)
  of the element instead of the element itself.

  ## Examples

      iex> Enum.find_index([2, 4, 6], fn(x) -> rem(x, 2) == 1 end)
      nil

      iex> Enum.find_index([2, 3, 4], fn(x) -> rem(x, 2) == 1 end)
      1

  """
  @spec find_index(t, (element -> any)) :: index | :nil
  def find_index(collection, fun) when is_list(collection) do
    do_find_index(collection, 0, fun)
  end

  def find_index(collection, fun) do
    Enumerable.reduce(collection, 0, fn(entry, acc) ->
      if fun.(entry), do: throw({ :enum_find_index, acc }), else: acc + 1
    end)
    nil
  catch
    { :enum_find_index, idx } -> idx
  end

  @doc """
  Returns the first item in the collection or `nil` otherwise.

  ## Examples

      iex> Enum.first([])
      nil
      iex> Enum.first([1, 2, 3])
      1

  """
  @spec first(t) :: :nil | element
  def first([]),    do: nil
  def first([h|_]), do: h

  def first(collection) do
    Enumerable.reduce(collection, nil, fn(entry, _) ->
      throw({ :enum_first, entry})
    end)
  catch
    { :enum_first, entry } -> entry
  end

  @doc """
  Returns a new collection appending the result of invoking `fun`
  on each corresponding item of `collection`.

  Given function should return a list.

  ## Examples

      iex> Enum.flat_map([:a, :b, :c], fn(x) -> [x, x] end)
      [:a, :a, :b, :b, :c, :c]

  """
  @spec flat_map(t, (element -> any)) :: list
  def flat_map([], _fun), do: []

  def flat_map(collection, fun) when is_list(collection) do
    :lists.flatmap(fun, collection)
  end

  def flat_map(collection, fun) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      acc ++ fun.(entry)
    end)
  end

  @doc """
  Intersperses the `element` between each element of the enumeration.

  Complexity: O(n)

  ## Examples

      iex> Enum.intersperse([1, 2, 3], 0)
      [1, 0, 2, 0, 3]
      iex> Enum.intersperse([1], 0)
      [1]
      iex> Enum.intersperse([], 0)
      []

  """
  @spec intersperse(t, element) :: list
  def intersperse(collection, element) do
    l = Enumerable.reduce(collection, [], fn(x, acc) -> [x | [element | acc]] end)
        |> :lists.reverse()
    case l do
      []      -> []
      [_ | t] -> t  # Head is a superfluous intersperser element
    end
  end

  @doc """
  Joins the given `collection` according to `joiner`.
  `joiner` can be either a binary or a list and the
  result will be of the same type as `joiner`. If
  `joiner` is not passed at all, it defaults to an
  empty binary.

  All items in the collection must be convertible
  to a binary, otherwise an error is raised.

  ## Examples

      iex> Enum.join([1, 2, 3])
      "123"
      iex> Enum.join([1, 2, 3], " = ")
      "1 = 2 = 3"

  """
  @spec join(t) :: String.t
  @spec join(t, String.t) :: String.t
  def join(collection, joiner // "")

  def join(collection, joiner) when is_binary(joiner) do
    Enumerable.reduce(collection, "", fn
      entry, ""  -> to_string(entry)
      entry, acc -> acc <> joiner <> to_string(entry)
    end)
  end

  @doc """
  Returns a new collection, where each item is the result
  of invoking `fun` on each corresponding item of `collection`.

  For dicts, the function accepts a key-value tuple.

  ## Examples

      iex> Enum.map([1, 2, 3], fn(x) -> x * 2 end)
      [2, 4, 6]

      iex> Enum.map([a: 1, b: 2], fn({k, v}) -> { k, -v } end)
      [a: -1, b: -2]

  """
  @spec map(t, (element -> any)) :: list
  def map(collection, fun) when is_list(collection) do
    lc item inlist collection, do: fun.(item)
  end

  def map(collection, fun) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      [fun.(entry)|acc]
    end) |> :lists.reverse
  end

  @doc """
  Maps and joins the given `collection` in one pass.
  `joiner` can be either a binary or a list and the
  result will be of the same type as `joiner`. If
  `joiner` is not passed at all, it defaults to an
  empty binary.

  All items in the collection must be convertible
  to a binary, otherwise an error is raised.

  ## Examples

      iex> Enum.map_join([1, 2, 3], &(&1 * 2))
      "246"
      iex> Enum.map_join([1, 2, 3], " = ", &(&1 * 2))
      "2 = 4 = 6"

  """
  @spec map_join(t, (element -> any)) :: String.t
  @spec map_join(t, String.t, (element -> any)) :: String.t
  def map_join(collection, joiner // "", mapper)

  def map_join(collection, joiner, mapper) when is_binary(joiner) do
    Enumerable.reduce(collection, "", fn
      entry, ""  -> to_string(mapper, entry)
      entry, acc -> acc <> joiner <> to_string(mapper, entry)
    end)
  end

  @doc """
  Invokes the given `fun` for each item in the `collection`
  while also keeping an accumulator. Returns a tuple where
  the first element is the mapped collection and the second
  one is the final accumulator.

  For dicts, the first tuple element must be a `{ key, value }`
  tuple.

  ## Examples

      iex> Enum.map_reduce([1, 2, 3], 0, fn(x, acc) -> { x * 2, x + acc } end)
      { [2, 4, 6], 6 }

  """
  @spec map_reduce(t, any, (element, any -> any)) :: any
  def map_reduce(collection, acc, fun) do
    { list, acc } = Enumerable.reduce(collection, { [], acc }, fn(entry, { list, acc }) ->
      { new_entry, acc } = fun.(entry, acc)
      { [new_entry|list], acc }
    end)
    { :lists.reverse(list), acc }
  end

  @doc """
  Partitions `collection` into two collections, where the first one contains elements
  for which `fun` returns a truthy value, and the second one -- for which `fun`
  returns `false` or `nil`.

  ## Examples

      iex> Enum.partition([1, 2, 3], fn(x) -> rem(x, 2) == 0 end)
      { [2], [1,3] }

  """
  @spec partition(t, (element -> any)) :: {list, list}
  def partition(collection, fun) do
    { acc1, acc2 } = Enumerable.reduce(collection, { [], [] }, fn(entry, { acc1, acc2 }) ->
      if fun.(entry) do
        { [entry|acc1], acc2 }
      else
        { acc1, [entry|acc2] }
      end
    end)

    { :lists.reverse(acc1), :lists.reverse(acc2) }
  end

  @doc """
  Invokes `fun` for each element in the collection passing that element and the
  accumulator `acc` as arguments. `fun`'s return value is stored in `acc`.
  Returns the accumulator.

  ## Examples

      iex> Enum.reduce([1, 2, 3], 0, fn(x, acc) -> x + acc end)
      6

  """
  @spec reduce(t, any, (element, any -> any)) :: any
  def reduce(collection, acc, fun) do
    Enumerable.reduce(collection, acc, fun)
  end

  @doc """
  Invokes `fun` for each element in the collection passing that element and the
  accumulator `acc` as arguments. `fun`'s return value is stored in `acc`.
  The first element of the collection is used as the initial value of `acc`.
  Returns the accumulator.

  ## Examples

      iex> Enum.reduce([1, 2, 3, 4], fn(x, acc) -> x * acc end)
      24

  """
  @spec reduce(t, (element, any -> any)) :: any
  def reduce([h|t], fun) do
    Enum.reduce(t, h, fun)
  end

  def reduce([], _fun) do
    raise Enum.EmptyError
  end

  def reduce(collection, fun) do
    result = Enumerable.reduce(collection, :first, fn
      entry, :first -> { :reduce, entry }
      entry, { :reduce, acc } -> { :reduce, fun.(entry, acc) }
    end)

    case result do
      :first -> raise Enum.EmptyError
      { :reduce, acc } -> acc
    end
  end

  @doc """
  Returns elements of collection for which `fun` returns `false`.

  ## Examples

      iex> Enum.reject([1, 2, 3], fn(x) -> rem(x, 2) == 0 end)
      [1, 3]

  """
  @spec reject(t, (element -> as_boolean(term))) :: list
  def reject(collection, fun) when is_list(collection) do
    lc item inlist collection, !fun.(item), do: item
  end

  def reject(collection, fun) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      unless fun.(entry), do: [entry|acc], else: acc
    end) |> :lists.reverse
  end

  @doc """
  Reverses the collection.

  ## Examples

      iex> Enum.reverse([1, 2, 3])
      [3, 2, 1]

  """
  @spec reverse(t) :: list
  def reverse(collection) when is_list(collection) do
    :lists.reverse(collection)
  end

  def reverse(collection) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      [entry|acc]
    end)
  end

  @doc """
  Reverses the collection and appends the tail.
  This is an optimization for
  `Enum.concat(Enum.reverse(collection), tail)`.

  ## Examples

      iex> Enum.reverse([1, 2, 3], [4, 5, 6])
      [3, 2, 1, 4, 5, 6]
  """
  @spec reverse(t, t) :: list
  def reverse(collection, tail) when is_list(collection) and is_list(tail) do
    :lists.reverse(collection, tail)
  end

  def reverse(collection, tail) do
    Enumerable.reduce(collection, to_list(tail), fn(entry, acc) ->
      [entry|acc]
    end)
  end

  @doc """
  Returns a list of collection elements shuffled.

  Notice you need to explicitly call `:random.seed/1` and
  set a seed value for the random algorithm. Otherwise, the
  default seed will be set which will always return the same
  result. For example, one could the following to set a seed
  dynamically:

      :random.seed(:erlang.now)

  ## Examples

      iex(1)> Enum.shuffle([1, 2, 3])
      [3, 2, 1]
      iex(2)> Enum.shuffle([1, 2, 3])
      [3, 1, 2]

  """
  @spec shuffle(t) :: list
  def shuffle(collection) do
    randomized = Enumerable.reduce(collection, [], fn x, acc ->
      [{ :random.uniform, x }|acc]
    end)
    unwrap(:lists.keysort(1, randomized), [])
  end

  @doc """
  Returns a sorted list of collection elements. Uses the merge sort algorithm.

  ## Examples

      iex> Enum.sort([3, 2, 1])
      [1,2,3]

  """
  @spec sort(t) :: list
  def sort(collection) when is_list(collection) do
    :lists.sort(collection)
  end

  def sort(collection) do
    fun = &(&1 <= &2)
    Enumerable.reduce(collection, [], &sort_reducer(&1, &2, fun)) |>
      sort_terminator(fun)
  end

  @doc """
  Returns a sorted list of collection elements. Uses the merge sort algorithm.

  ## Examples

      iex> Enum.sort([1, 2, 3], &(&1 > &2))
      [3,2,1]

  """
  @spec sort(t, (element, element -> boolean)) :: list
  def sort(collection, fun) when is_list(collection) do
    :lists.sort(fun, collection)
  end

  def sort(collection, fun) do
    Enumerable.reduce(collection, [], &sort_reducer(&1, &2, fun)) |>
      sort_terminator(fun)
  end

  @doc """
  Splits the enumerable into two collections, leaving `count`
  elements in the first one. If `count` is a negative number,
  it starts counting from the back to the beginning of the
  collection.

  Be aware that a negative `count` implies the collection
  will be enumerated twice. Once to calculate the position and
  a second time to do the actual splitting.

  ## Examples

      iex> Enum.split([1, 2, 3], 2)
      { [1,2], [3] }
      iex> Enum.split([1, 2, 3], 10)
      { [1,2,3], [] }
      iex> Enum.split([1, 2, 3], 0)
      { [], [1,2,3] }
      iex> Enum.split([1, 2, 3], -1)
      { [1,2], [3] }
      iex> Enum.split([1, 2, 3], -5)
      { [], [1,2,3] }

  """
  @spec split(t, integer) :: {list, list}
  def split(collection, count) when is_list(collection) and count >= 0 do
    do_split(collection, count, [])
  end

  def split(collection, count) when count >= 0 do
    { _, list1, list2 } =
      Enumerable.reduce(collection, { count, [], [] }, fn(entry, { counter, acc1, acc2 }) ->
        if counter > 0 do
          { counter - 1, [entry|acc1], acc2 }
        else
          { counter, acc1, [entry|acc2] }
        end
      end)

    { :lists.reverse(list1), :lists.reverse(list2) }
  end

  def split(collection, count) when count < 0 do
    do_split_reverse(reverse(collection), abs(count), [])
  end

  @doc """
  Splits `collection` in two while `fun` returns `true`.

  ## Examples

      iex> Enum.split_while([1, 2, 3, 4], fn(x) -> x < 3 end)
      { [1, 2], [3, 4] }
  """
  @spec split_while(t, (element -> as_boolean(term))) :: {list, list}
  def split_while(collection, fun) when is_list(collection) do
    do_split_while(collection, fun, [])
  end

  def split_while(collection, fun) do
    { list1, list2 } = Enumerable.reduce(collection, { [], [] }, fn
      entry, { acc1, [] } ->
        if fun.(entry), do: { [entry|acc1], [] }, else: { acc1, [entry] }
      entry, { acc1, acc2 } ->
        { acc1, [entry|acc2] }
    end)

    { :lists.reverse(list1), :lists.reverse(list2) }
  end

  @doc """
  Takes the first `count` items from the collection.

  If a negative value `count` is given the last `count`
  values will be taken. The collection is enumerated
  once to retrieve the proper index and the remaining
  calculation is performed from the end.

  ## Examples

      iex> Enum.take([1, 2, 3], 2)
      [1,2]
      iex> Enum.take([1, 2, 3], 10)
      [1,2,3]
      iex> Enum.take([1, 2, 3], 0)
      []
      iex> Enum.take([1, 2, 3], -1)
      [3]

  """
  @spec take(t, integer) :: list

  def take(_collection, 0) do
    []
  end

  def take(collection, count) when is_list(collection) and count > 0 do
    do_take(collection, count)
  end

  def take(collection, count) when count > 0 do
    { list, _ } = Enumerable.reduce(collection, { [], count }, fn(entry, { list, count }) ->
      if count > 1 do
        { [entry|list], count - 1 }
      else
        throw({ :enum_take, [entry|list] })
      end
    end)
    :lists.reverse(list)
  catch
    { :enum_take, list } -> :lists.reverse(list)
  end

  def take(collection, count) when count < 0 do
    do_take_reverse(reverse(collection), abs(count), [])
  end

  @doc """
  Returns a collection of every `nth` items in the collection,
  starting with the first.

  ## Examples

      iex> Enum.take_every(1..10, 2)
      [1, 3, 5, 7, 9]

  """
  @spec take_every(t, integer) :: list
  def take_every(_collection, 0), do: []
  def take_every(collection, nth) do
    res = Enumerable.reduce(collection, nil, fn
      x, { acc, ^nth } -> { [x | acc], 1 }
      _, { acc, count } -> { acc, count + 1 }
      x, nil -> { [x], 1 }
    end)

    case res do
      nil -> []
      { list, _ } -> :lists.reverse(list)
    end
  end

  @doc """
  Takes the items at the beginning of `collection` while `fun` returns `true`.

  ## Examples

      iex> Enum.take_while([1, 2, 3], fn(x) -> x < 3 end)
      [1, 2]

  """
  @spec take_while(t, (element -> as_boolean(term))) :: list
  def take_while(collection, fun) when is_list(collection) do
    do_take_while(collection, fun)
  end

  def take_while(collection, fun) do
    Enumerable.reduce(collection, [], fn(entry, list) ->
      if fun.(entry) do
        [entry|list]
      else
        throw({ :enum_take, list })
      end
    end) |> :lists.reverse
  catch
    { :enum_take, list } -> :lists.reverse(list)
  end

  @doc """
  Convert `collection` to a list.

  ## Examples

      iex> Enum.to_list(1 .. 3)
      [1, 2, 3]

  """
  @spec to_list(t) :: [term]
  def to_list(collection) when is_list collection do
    collection
  end

  def to_list(collection) do
    Enumerable.reduce(collection, [], fn(entry, acc) ->
      [entry|acc]
    end) |> :lists.reverse
  end

  @doc """
  Iterates the enumerable removing all duplicated items.

  ## Examples

      iex> Enum.uniq([1, 2, 3, 2, 1])
      [1, 2, 3]

      iex> Enum.uniq([{1, :x}, {2, :y}, {1, :z}], fn {x, _} -> x end)
      [{1,:x}, {2,:y}]

  """
  @spec uniq(t) :: list
  @spec uniq(t, (element -> term)) :: list
  def uniq(collection, fun // fn x -> x end)

  def uniq(collection, fun) when is_list(collection) do
    do_uniq(collection, [], fun)
  end

  def uniq(collection, fun) do
    { list, _ } = Enumerable.reduce(collection, { [], [] }, fn(entry, { acc, fun_acc }) ->
      fun_entry = fun.(entry)
      if :lists.member(fun_entry, fun_acc) do
        { acc, fun_acc }
      else
        { [entry|acc], [fun_entry|fun_acc] }
      end
    end)

    :lists.reverse(list)
  end

  @doc """
  Zips corresponding elements from two collections into one list
  of tuples. The number of elements in the resulting list is
  dictated by the first enum. If the second list is shorter,
  values are filled with `nil`.

  ## Examples

      iex> Enum.zip([1, 2, 3], [:a, :b, :c])
      [{1,:a},{2,:b},{3,:c}]

      iex> Enum.zip([1,2,3,4,5], [:a, :b, :c])
      [{1,:a},{2,:b},{3,:c},{4,nil},{5,nil}]

  """
  @spec zip(t, t) :: [{any, any}]
  def zip(coll1, coll2) when is_list(coll1) and is_list(coll2) do
    do_zip(coll1, coll2)
  end

  def zip(coll1, coll2) do
    list = to_list(coll1)
    { zipped, rest } = Enumerable.reduce(coll2, { [], list }, fn
      entry, { acc, [h|t] } -> { [{h, entry}|acc], t }
      _,     { acc, [] }    -> throw { :enum_zip, acc }
    end)

    list = :lists.foldl(fn(x, acc) -> [{ x, nil }|acc] end, zipped, rest)
    :lists.reverse(list)
  catch
    { :enum_zip, rest } -> :lists.reverse(rest)
  end

  @doc """
  Returns the maximum value.
  Raises `EmptyError` if the collection is empty.

  ## Examples

      iex> Enum.max([1, 2, 3])
      3

  """
  @spec max(t) :: element | no_return
  def max(collection) do
    reduce(collection, &Kernel.max(&1, &2))
  end

  @doc """
  Returns the maximum value as calculated by the given function.
  Raises `EmptyError` if the collection is empty.

  ## Examples

      iex> Enum.max_by(["a", "aa", "aaa"], fn(x) -> String.length(x) end)
      "aaa"

  """
  @spec max_by(t, (element -> any)) :: element | no_return
  def max_by([h|t], fun) do
    { max, _ } = Enumerable.reduce(t, { h, fun.(h) }, fn(entry, { _, fun_max } = old) ->
      fun_entry = fun.(entry)
      if fun_entry > fun_max, do: { entry, fun_entry }, else: old
    end)

    max
  end

  def max_by([], _fun) do
    raise Enum.EmptyError
  end

  def max_by(collection, fun) do
    result = Enumerable.reduce(collection, :first, fn
      entry, :first ->
        { :reduce, entry, fun.(entry) }
      entry, { :reduce, _, fun_max } = old ->
        fun_entry = fun.(entry)
        if fun_entry > fun_max, do: { :reduce, entry, fun_entry }, else: old
    end)

    case result do
      :first                -> raise Enum.EmptyError
      { :reduce, entry, _ } -> entry
    end
  end

  @doc """
  Returns the minimum value.
  Raises `EmptyError` if the collection is empty.

  ## Examples

      iex> Enum.min([1, 2, 3])
      1

  """
  @spec min(t) :: element | no_return
  def min(collection) do
    reduce(collection, &Kernel.min(&1, &2))
  end

  @doc """
  Returns the minimum value as calculated by the given function.
  Raises `EmptyError` if the collection is empty.

  ## Examples

      iex> Enum.min_by(["a", "aa", "aaa"], fn(x) -> String.length(x) end)
      "a"

  """
  @spec min_by(t, (element -> any)) :: element | no_return
  def min_by([h|t], fun) do
    { min, _ } = Enumerable.reduce(t, { h, fun.(h) }, fn(entry, { _, fun_min } = old) ->
      fun_entry = fun.(entry)
      if fun_entry < fun_min, do: { entry, fun_entry }, else: old
    end)

    min
  end

  def min_by([], _fun) do
    raise Enum.EmptyError
  end

  def min_by(collection, fun) do
    result = Enumerable.reduce(collection, :first, fn
      entry, :first ->
        { :reduce, entry, fun.(entry) }
      entry, { :reduce, _, fun_min } = old ->
        fun_entry = fun.(entry)
        if fun_entry < fun_min, do: { :reduce, entry, fun_entry }, else: old
    end)

    case result do
      :first                -> raise Enum.EmptyError
      { :reduce, entry, _ } -> entry
    end
  end

  @doc """
  Returns the collection with each element wrapped in a tuple
  along side its index.

  ## Examples

      iex> Enum.with_index [1,2,3]
      [{1,0},{2,1},{3,2}]

  """
  @spec with_index(t) :: list({ element, non_neg_integer })
  def with_index(collection) do
    map_reduce(collection, 0, fn x, acc ->
      { { x, acc }, acc + 1 }
    end) |> elem(0)
  end

  @doc """
  Shortcut to `chunks(coll, n, n)`.
  """
  @spec chunks(t, non_neg_integer) :: [list]
  def chunks(coll, n), do: chunks(coll, n, n, nil)

  @doc """
  Returns a collection of lists containing `n` items each, where
  each new chunk starts `step` elements into the collection.
  `step` is optional and if not passed, defaults to `n`, i.e.
  chunks do not overlap. If the final chunk does not have `n`
  elements to fill the chunk, elements are taken as necessary
  from `pad` if it was passed. If `pad` is passed and does not
  have enough elements to fill the chunk, then the chunk is
  returned anyway with less than `n` elements. If `pad` is not
  passed at all or is nil, then the partial chunk is discarded
  from the result.

  ## Examples

      iex> Enum.chunks([1, 2, 3, 4, 5, 6], 2)
      [[1, 2], [3, 4], [5, 6]]
      iex> Enum.chunks([1, 2, 3, 4, 5, 6], 3, 2)
      [[1, 2, 3], [3, 4, 5]]
      iex> Enum.chunks([1, 2, 3, 4, 5, 6], 3, 2, [7])
      [[1, 2, 3], [3, 4, 5], [5, 6, 7]]
      iex> Enum.chunks([1, 2, 3, 4, 5, 6], 3, 3, [])
      [[1, 2, 3], [4, 5, 6]]

  """
  @spec chunks(t, non_neg_integer, non_neg_integer) :: [list]
  @spec chunks(t, non_neg_integer, non_neg_integer, list | nil) :: [list]
  def chunks(coll, n, step, pad // nil) when n > 0 and step > 0 do
    { acc, buffer, i } =
      Enumerable.reduce(coll, { [], [], 0 }, fn
        x, { acc, buffer, i } when i < n ->
          chunks_n(acc, [x|buffer], i + 1, n, step)
        x, { acc, buffer, i } when i < step ->
          chunks_step(acc, [x|buffer], i + 1, step)
      end)

    if nil?(pad) || i == 0 do
      :lists.reverse(acc)
    else
      buffer = :lists.reverse(buffer) ++ take(pad, n - i)
      :lists.reverse([buffer|acc])
    end
  end

  @doc """
  Splits `coll` on every element for which `fun` returns a new value.

  ## Examples

      iex> Enum.chunks_by([1, 2, 2, 3, 4, 4, 6, 7, 7], &(rem(&1, 2) == 1))
      [[1], [2, 2], [3], [4, 4, 6], [7, 7]]

  """
  @spec chunks_by(t, (element -> any)) :: [list]
  def chunks_by(coll, fun) do
    res =
      Enumerable.reduce(coll, nil, fn
        x, {acc, buffer, value} ->
          new_value = fun.(x)
          if new_value == value do
            {acc, [x | buffer], value}
          else
            {[:lists.reverse(buffer) | acc], [x], new_value}
          end
        x, nil -> {[], [x], fun.(x)}
      end)
    if nil?(res) do
      []
    else
      {acc, buffer, _} = res
      :lists.reverse([:lists.reverse(buffer) | acc])
    end
  end

  @doc """
  Returns a subset list of the given collection. Dropping elements
  until element position `start`, then taking `count` elements.

  ## Examples

      iex> Enum.slice(1..100, 5, 10)
      [6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

  """
  @spec slice(t, integer, non_neg_integer) :: list

  def slice(coll, start, count) when start < 0 do
    { list, new_start } = enumerate_and_count(coll, start)
    if new_start >= 0, do: slice(list, new_start, count)
  end

  def slice(coll, start, count) when is_list(coll) and start >= 0 and count > 0 do
    do_slice(coll, start, count)
  end

  def slice(coll, start, count) when start >= 0 and count > 0 do
    { start, _, list } = Enumerable.reduce(coll, { start, count, [] }, fn
      _entry, { start, count, _list } when start > 0 ->
        { start-1, count, [] }
      entry, { start, count, list } when count > 1 ->
        { start, count-1, [entry|list] }
      entry, { _start, _count, list } ->
        throw { :enum_slice, [entry|list] }
    end)

    if start <= 0, do: :lists.reverse(list)
  catch
    { :enum_slice, list } -> :lists.reverse(list)
  end

  def slice(coll, start, 0) do
    Enumerable.reduce(coll, start, fn _, start ->
      if start > 0, do: start-1, else: throw :enum_slice
    end)
    nil
  catch
    :enum_slice -> []
  end

  @doc """
  Returns a subset list of the given collection. Dropping elements
  until element position `range.first`, then taking elements until element
  position `range.last` (inclusive).

  Positions are calculated by adding the number of items in the collection to
  negative positions (so position -3 in a collection with count 5 becomes
  position 2).

  The first position (after adding count to negative positions) must be smaller
  or equal to the last position.

  ## Examples

      iex> Enum.slice(1..100, 5..10)
      [6, 7, 8, 9, 10, 11]

  """
  def slice(coll, first..last) when first >= 0 and last >= 0 do
    # Simple case, which works on infinite collections
    if last - first >= 0 do
      slice(coll, first, last - first + 1)
    end
  end

  def slice(coll, first..last) do
    { list, count } = enumerate_and_count(coll, 0)
    corr_first = if first >= 0, do: first, else: first + count
    corr_last = if last >= 0, do: last, else: last + count
    length = corr_last - corr_first + 1
    if corr_first >= 0 and length > 0 do
      slice(list, corr_first, length)
    end
  end

  ## Helpers

  @compile { :inline, chunks_n: 5, chunks_step: 4, to_string: 2 }

  defp chunks_n(acc, buffer, i, n, step) when i == n do
    acc = [:lists.reverse(buffer)|acc]
    chunks_step(acc, buffer, i, step)
  end

  defp chunks_n(acc, buffer, i, _n, _step) do
    { acc, buffer, i }
  end

  defp chunks_step(acc, buffer, i, step) when i >= step do
    left = i - step
    { acc, take(buffer, left), left }
  end

  defp chunks_step(acc, buffer, i, _step) do
    { acc, buffer, i }
  end

  defp enumerate_and_count(collection, count) when is_list(collection) do
    { collection, length(collection) - abs(count) }
  end

  defp enumerate_and_count(collection, count) do
    map_reduce(collection, -abs(count), fn(x, acc) -> { x, acc + 1 } end)
  end

  defp to_string(mapper, entry) do
    case mapper.(entry) do
      x when is_binary(x) -> x
      o -> String.Chars.to_string(o)
    end
  end

  ## Implementations

  ## all?

  defp do_all?([h|t], fun) do
    if fun.(h) do
      do_all?(t, fun)
    else
      false
    end
  end

  defp do_all?([], _) do
    true
  end

  ## any?

  defp do_any?([h|t], fun) do
    if fun.(h) do
      true
    else
      do_any?(t, fun)
    end
  end

  defp do_any?([], _) do
    false
  end

  ## fetch

  defp do_fetch([h|_], 0), do: { :ok, h }
  defp do_fetch([_|t], n), do: do_fetch(t, n - 1)
  defp do_fetch([], _),    do: :error

  ## drop

  defp do_drop([_|t], counter) when counter > 0 do
    do_drop(t, counter - 1)
  end

  defp do_drop(list, 0) do
    list
  end

  defp do_drop([], _) do
    []
  end

  ## drop_while

  defp do_drop_while([h|t], fun) do
    if fun.(h) do
      do_drop_while(t, fun)
    else
      [h|t]
    end
  end

  defp do_drop_while([], _) do
    []
  end

  ## find

  defp do_find([h|t], ifnone, fun) do
    if fun.(h) do
      h
    else
      do_find(t, ifnone, fun)
    end
  end

  defp do_find([], ifnone, _) do
    ifnone
  end

  ## find_index

  defp do_find_index([h|t], counter, fun) do
    if fun.(h) do
      counter
    else
      do_find_index(t, counter + 1, fun)
    end
  end

  defp do_find_index([], _, _) do
    nil
  end

  ## find_value

  defp do_find_value([h|t], ifnone, fun) do
    fun.(h) || do_find_value(t, ifnone, fun)
  end

  defp do_find_value([], ifnone, _) do
    ifnone
  end

  ## shuffle

  defp unwrap([{_, h} | collection], t) do
    unwrap(collection, [h|t])
  end

  defp unwrap([], t), do: t

  ## sort

  defp sort_reducer(entry, { :split, y, x, r, rs, bool }, fun) do
    cond do
      fun.(y, entry) == bool ->
        { :split, entry, y, [x|r], rs, bool }
      fun.(x, entry) == bool ->
        { :split, y, entry, [x|r], rs, bool }
      r == [] ->
        { :split, y, x, [entry], rs, bool }
      true ->
        { :pivot, y, x, r, rs, entry, bool }
    end
  end

  defp sort_reducer(entry, { :pivot, y, x, r, rs, s, bool }, fun) do
    cond do
      fun.(y, entry) == bool ->
        { :pivot, entry, y, [x | r], rs, s, bool }
      fun.(x, entry) == bool ->
        { :pivot, y, entry, [x | r], rs, s, bool }
      fun.(s, entry) == bool ->
        { :split, entry, s, [], [[y, x | r] | rs], bool }
      true ->
        { :split, s, entry, [], [[y, x | r] | rs], bool }
    end
  end

  defp sort_reducer(entry, [x], fun) do
    { :split, entry, x, [], [], fun.(x, entry) }
  end

  defp sort_reducer(entry, acc, _fun) do
    [entry|acc]
  end

  defp sort_terminator({ :split, y, x, r, rs, bool }, fun) do
    sort_merge([[y, x | r] | rs], fun, bool)
  end

  defp sort_terminator({ :pivot, y, x, r, rs, s, bool }, fun) do
    sort_merge([[s], [y, x | r] | rs], fun, bool)
  end

  defp sort_terminator(acc, _fun) do
    acc
  end

  defp sort_merge(list, fun, true), do:
    reverse_sort_merge(list, [], fun, true)

  defp sort_merge(list, fun, false), do:
    sort_merge(list, [], fun, false)

  defp sort_merge([t1, [h2 | t2] | l], acc, fun, true), do:
    sort_merge(l, [sort_merge_1(t1, h2, t2, [], fun, false) | acc], fun, true)

  defp sort_merge([[h2 | t2], t1 | l], acc, fun, false), do:
    sort_merge(l, [sort_merge_1(t1, h2, t2, [], fun, false) | acc], fun, false)

  defp sort_merge([l], [], _fun, _bool), do: l

  defp sort_merge([l], acc, fun, bool), do:
    reverse_sort_merge([:lists.reverse(l, []) | acc], [], fun, bool)

  defp sort_merge([], acc, fun, bool), do:
    reverse_sort_merge(acc, [], fun, bool)

  defp reverse_sort_merge([[h2 | t2], t1 | l], acc, fun, true), do:
    reverse_sort_merge(l, [sort_merge_1(t1, h2, t2, [], fun, true) | acc], fun, true)

  defp reverse_sort_merge([t1, [h2 | t2] | l], acc, fun, false), do:
    reverse_sort_merge(l, [sort_merge_1(t1, h2, t2, [], fun, true) | acc], fun, false)

  defp reverse_sort_merge([l], acc, fun, bool), do:
    sort_merge([:lists.reverse(l, []) | acc], [], fun, bool)

  defp reverse_sort_merge([], acc, fun, bool), do:
    sort_merge(acc, [], fun, bool)

  defp sort_merge_1([h1 | t1], h2, t2, m, fun, bool) do
    if fun.(h1, h2) == bool do
      sort_merge_2(h1, t1, t2, [h2 | m], fun, bool)
    else
      sort_merge_1(t1, h2, t2, [h1 | m], fun, bool)
    end
  end

  defp sort_merge_1([], h2, t2, m, _fun, _bool), do:
    :lists.reverse(t2, [h2 | m])

  defp sort_merge_2(h1, t1, [h2 | t2], m, fun, bool) do
    if fun.(h1, h2) == bool do
      sort_merge_2(h1, t1, t2, [h2 | m], fun, bool)
    else
      sort_merge_1(t1, h2, t2, [h1 | m], fun, bool)
    end
  end

  defp sort_merge_2(h1, t1, [], m, _fun, _bool), do:
    :lists.reverse(t1, [h1 | m])

  ## split

  defp do_split([h|t], counter, acc) when counter > 0 do
    do_split(t, counter - 1, [h|acc])
  end

  defp do_split(list, 0, acc) do
    { :lists.reverse(acc), list }
  end

  defp do_split([], _, acc) do
    { :lists.reverse(acc), [] }
  end

  defp do_split_reverse([h|t], counter, acc) when counter > 0 do
    do_split_reverse(t, counter - 1, [h|acc])
  end

  defp do_split_reverse(list, 0, acc) do
    { :lists.reverse(list), acc }
  end

  defp do_split_reverse([], _, acc) do
    { [], acc }
  end

  ## split_while

  defp do_split_while([h|t], fun, acc) do
    if fun.(h) do
      do_split_while(t, fun, [h|acc])
    else
      { :lists.reverse(acc), [h|t] }
    end
  end

  defp do_split_while([], _, acc) do
    { :lists.reverse(acc), [] }
  end

  ## take

  defp do_take([h|t], counter) when counter > 0 do
    [h|do_take(t, counter - 1)]
  end

  defp do_take(_list, 0) do
    []
  end

  defp do_take([], _) do
    []
  end

  defp do_take_reverse([h|t], counter, acc) when counter > 0 do
    do_take_reverse(t, counter - 1, [h|acc])
  end

  defp do_take_reverse(_list, 0, acc) do
    acc
  end

  defp do_take_reverse([], _, acc) do
    acc
  end

  ## take_while

  defp do_take_while([h|t], fun) do
    if fun.(h) do
      [h|do_take_while(t, fun)]
    else
      []
    end
  end

  defp do_take_while([], _) do
    []
  end

  ## uniq

  defp do_uniq([h|t], acc, fun) do
    fun_h = fun.(h)
    case :lists.member(fun_h, acc) do
      true  -> do_uniq(t, acc, fun)
      false -> [h|do_uniq(t, [fun_h|acc], fun)]
    end
  end

  defp do_uniq([], _acc, _fun) do
    []
  end

  ## zip

  defp do_zip([h1|next1], other) do
    { h2, next2 } = do_zip_next(other)
    [{ h1, h2 }|do_zip(next1, next2)]
  end

  defp do_zip([], _) do
    []
  end

  defp do_zip_next([h|t]), do: { h, t }
  defp do_zip_next([]),    do: { nil, [] }

  ## slice

  defp do_slice([], start, _count) do
    if start == 0, do: []
  end

  defp do_slice(list, start, 0) do
    if start < length(list), do: []
  end

  defp do_slice([h|t], 0, count) do
    [h|do_slice(t, 0, count-1)]
  end

  defp do_slice([_|t], start, count) do
    do_slice(t, start-1, count)
  end
end

defimpl Enumerable, for: List do
  def reduce([h|t], acc, fun) do
    reduce(t, fun.(h, acc), fun)
  end

  def reduce([], acc, _fun) do
    acc
  end

  def member?([], _),       do: false
  def member?(list, value), do: :lists.member(value, list)

  def count(list), do: length(list)
end

defimpl Enumerable, for: Function do
  def reduce(function, acc, fun) do
    function.(acc, fun)
  end

  def member?(function, value) do
    function.(false, fn
      ^value, _ -> throw(:function_member?)
      _, _ -> false
    end)
  catch
    :function_member? -> true
  end

  def count(function) do
    function.(0, fn(_, acc) -> acc + 1 end)
  end
end


Adjust stats output to match readme

Adjust stats output to match readme

defmodule BeeBee.ShortenedURL do
  alias BeeBee.{Redis, ShortTag}

  @namespace "beebee"

  def add(%{"url" => url, "short_tag" => short_tag}) do
    url_key = "#{@namespace}:#{short_tag}:url"
    count_key = "#{@namespace}:#{short_tag}:count"

    case Redis.query(["EXISTS", url_key]) do
      "0" ->
        Redis.query(["SET", url_key, url])
        Redis.query(["SET", count_key, "0"])

        {:ok, short_tag}
      "1" ->
        {:error, "Short tag already in use"}
      _ ->
        {:error, "Server error"}
    end
  end

  def add(%{"url" => url}) do
    add(%{"url" => url, "short_tag" => ShortTag.generate})
  end

  def find(short_tag) do
    url_key = "#{@namespace}:#{short_tag}:url"
    count_key = "#{@namespace}:#{short_tag}:count"

    case Redis.query(["GET", url_key]) do
      :undefined ->
        :not_found
      url ->
        Redis.query(["INCR", count_key])
        {:ok, url}
    end
  end

  def stats do
    scan_keys
    |> Enum.reduce(%{}, fn key, memo ->
      [_, short_tag, type] = String.split key, ":"
      value = Redis.query(["GET", key])

      memo = Map.put_new(memo, short_tag, %{})
      put_in memo, [short_tag, type], value
    end)
    |> Enum.map(fn {short_tag, data} ->
      Dict.put data, "short_tag", short_tag
    end)
  end

  defp scan_keys, do: scan_keys(nil, [])

  defp scan_keys("0", keys), do: Enum.uniq(keys)
  defp scan_keys(cursor, keys) do
    [new_cursor, new_keys] = Redis.query(["SCAN", (cursor || 0), "MATCH", "#{@namespace}:*"])

    scan_keys(new_cursor, keys ++ new_keys)
  end
end

defmodule BeeBee.ShortenedURL do
  alias BeeBee.{Redis, ShortTag}

  @namespace "beebee"

  def add(%{"url" => url, "short_tag" => short_tag}) do
    url_key = "#{@namespace}:#{short_tag}:url"
    count_key = "#{@namespace}:#{short_tag}:count"

    case Redis.query(["EXISTS", url_key]) do
      "0" ->
        Redis.query(["SET", url_key, url])
        Redis.query(["SET", count_key, "0"])

        {:ok, short_tag}
      "1" ->
        {:error, "Short tag already in use"}
      _ ->
        {:error, "Server error"}
    end
  end

  def add(%{"url" => url}) do
    add(%{"url" => url, "short_tag" => ShortTag.generate})
  end

  def find(short_tag) do
    url_key = "#{@namespace}:#{short_tag}:url"
    count_key = "#{@namespace}:#{short_tag}:count"

    case Redis.query(["GET", url_key]) do
      :undefined ->
        :not_found
      url ->
        Redis.query(["INCR", count_key])
        {:ok, url}
    end
  end

  def stats do
    keys = scan_keys

    Enum.reduce keys, %{}, fn key, memo ->
      [_, short_tag, type] = String.split key, ":"
      value = Redis.query(["GET", key])

      memo = Map.put_new(memo, short_tag, %{})
      put_in memo, [short_tag, type], value
    end
  end

  defp scan_keys, do: scan_keys(nil, [])

  defp scan_keys("0", keys), do: Enum.uniq(keys)
  defp scan_keys(cursor, keys) do
    [new_cursor, new_keys] = Redis.query(["SCAN", (cursor || 0), "MATCH", "#{@namespace}:*"])

    scan_keys(new_cursor, keys ++ new_keys)
  end
end