defmodule CljElixir.Equality do @moduledoc """ Cross-type equality for CljElixir. Handles the case where `(= [1 2 3] '(1 2 3))` should return true — PersistentVector and list are both sequential types with the same elements. """ def equiv(a, b) when a === b, do: true def equiv(%CljElixir.PersistentVector{} = a, %CljElixir.PersistentVector{} = b) do a.cnt == b.cnt and CljElixir.PersistentVector.to_list(a) == CljElixir.PersistentVector.to_list(b) end def equiv(%CljElixir.PersistentVector{} = a, b) when is_list(b) do CljElixir.PersistentVector.to_list(a) == b end def equiv(a, %CljElixir.PersistentVector{} = b) when is_list(a) do a == CljElixir.PersistentVector.to_list(b) end def equiv(%CljElixir.SubVector{} = a, b) do CljElixir.SubVector.sv_to_list(a) |> equiv_list(b) end def equiv(a, %CljElixir.SubVector{} = b) do equiv_list(CljElixir.SubVector.sv_to_list(b), a) end def equiv(a, b), do: a == b defp equiv_list(list, other) when is_list(other), do: list == other defp equiv_list(list, %CljElixir.PersistentVector{} = pv) do list == CljElixir.PersistentVector.to_list(pv) end defp equiv_list(_, _), do: false end defimpl Enumerable, for: CljElixir.PersistentVector do def count(pv), do: {:ok, pv.cnt} def member?(_pv, _value), do: {:error, __MODULE__} def reduce(_pv, {:halt, acc}, _fun), do: {:halted, acc} def reduce(pv, {:suspend, acc}, fun), do: {:suspended, acc, &reduce(pv, &1, fun)} def reduce(pv, {:cont, acc}, fun) do list = CljElixir.PersistentVector.to_list(pv) Enumerable.List.reduce(list, {:cont, acc}, fun) end def slice(pv) do size = pv.cnt {:ok, size, &slice_fun(pv, &1, &2, &3)} end defp slice_fun(pv, start, length, step) do start..(start + (length - 1) * step)//step |> Enum.map(fn i -> CljElixir.PersistentVector.pv_nth(pv, i) end) end end defimpl Collectable, for: CljElixir.PersistentVector do def into(pv) do collector_fun = fn acc, {:cont, elem} -> CljElixir.PersistentVector.pv_conj(acc, elem) acc, :done -> acc _acc, :halt -> :ok end {pv, collector_fun} end end defimpl Enumerable, for: Tuple do def count(t), do: {:ok, tuple_size(t)} def member?(_t, _value), do: {:error, __MODULE__} def reduce(_t, {:halt, acc}, _fun), do: {:halted, acc} def reduce(t, {:suspend, acc}, fun), do: {:suspended, acc, &reduce(t, &1, fun)} def reduce(t, {:cont, acc}, fun) do list = Tuple.to_list(t) Enumerable.List.reduce(list, {:cont, acc}, fun) end def slice(t) do size = tuple_size(t) {:ok, size, &slice_fun(t, &1, &2, &3)} end defp slice_fun(t, start, length, step) do start..(start + (length - 1) * step)//step |> Enum.map(fn i -> elem(t, i) end) end end defimpl Collectable, for: Tuple do def into(t) do collector_fun = fn acc, {:cont, elem} -> :erlang.append_element(acc, elem) acc, :done -> acc _acc, :halt -> :ok end {t, collector_fun} end end