Current section
Files
Jump to
Current section
Files
lib/rb_set.ex
defmodule A.RBSet do
@moduledoc false
@type value :: term
@opaque t(value) :: %__MODULE__{root: A.RBTree.Set.tree(value), size: non_neg_integer}
@type t :: t(term)
defstruct root: A.RBTree.Set.empty(), size: 0
@deprecated "Module A.RBSet will be removed"
@spec new :: t
def new(), do: %__MODULE__{}
@deprecated "Module A.RBSet will be removed"
@spec new(Enum.t()) :: t
def new(enumerable)
def new(%__MODULE__{} = rb_set), do: rb_set
def new(enumerable) do
{size, root} = A.RBTree.Set.empty() |> A.RBTree.Set.insert_many(enumerable)
%__MODULE__{root: root, size: size}
end
@spec new(Enum.t(), (term -> val)) :: t(val) when val: value
def new(enumerable, transform) when is_function(transform, 1) do
enumerable
|> Enum.map(transform)
|> new()
end
@spec delete(t(val1), val2) :: t(val1) when val1: value, val2: value
def delete(%__MODULE__{root: root, size: size} = rb_set, value) do
case A.RBTree.Set.delete(root, value) do
:error ->
rb_set
new_root ->
%__MODULE__{root: new_root, size: size - 1}
end
end
@spec difference(t(val), t(val)) :: t(val) when val: value
def difference(rb_set1, rb_set2)
def difference(%__MODULE__{} = rb_set1, %__MODULE__{} = rb_set2) do
A.RBTree.Set.foldl(rb_set2.root, rb_set1, fn elem, acc -> delete(acc, elem) end)
end
@spec disjoint?(t, t) :: boolean
def disjoint?(%__MODULE__{size: size1} = rb_set1, %__MODULE__{size: size2} = rb_set2)
when size1 < size2 do
disjoint?(rb_set2, rb_set1)
end
def disjoint?(%__MODULE__{} = rb_set1, %__MODULE__{} = rb_set2) do
not Enum.any?(rb_set2, fn elem -> member?(rb_set1, elem) end)
end
@spec equal?(t, t) :: boolean
def equal?(%__MODULE__{} = rb_set1, %__MODULE__{} = rb_set2) do
rb_set1.size == rb_set2.size &&
equal_loop(A.RBTree.Set.iterator(rb_set1.root), A.RBTree.Set.iterator(rb_set2.root))
end
defp equal_loop(iterator1, iterator2) do
case {A.RBTree.Set.next(iterator1), A.RBTree.Set.next(iterator2)} do
{nil, nil} ->
true
{{elem1, next_iter1}, {elem2, next_iter2}} when elem1 == elem2 ->
equal_loop(next_iter1, next_iter2)
_ ->
false
end
end
@spec intersection(t(val), t(val)) :: t(val) when val: value
def intersection(%__MODULE__{size: size1} = rb_set1, %__MODULE__{size: size2} = rb_set2)
when size1 < size2 do
intersection(rb_set2, rb_set1)
end
def intersection(%__MODULE__{} = rb_set1, %__MODULE__{} = rb_set2) do
rb_set2
|> Enum.filter(fn elem -> member?(rb_set1, elem) end)
|> new()
end
@spec member?(t, value) :: boolean
def member?(rb_set, value)
def member?(%__MODULE__{root: root}, value) do
A.RBTree.Set.member?(root, value)
end
@spec put(t(val), new_val) :: t(val | new_val) when val: value, new_val: value
def put(rb_set, value)
def put(%__MODULE__{root: root, size: size}, value) do
case A.RBTree.Set.insert(root, value) do
{:new, new_root} -> %__MODULE__{root: new_root, size: size + 1}
{:overwrite, new_root} -> %__MODULE__{root: new_root, size: size}
end
end
@spec size(t) :: non_neg_integer
def size(rb_set)
def size(%__MODULE__{size: size}), do: size
@spec subset?(t, t) :: boolean
def subset?(%__MODULE__{} = rb_set1, %__MODULE__{} = rb_set2) do
rb_set1.size <= rb_set2.size and Enum.all?(rb_set1, fn elem -> member?(rb_set2, elem) end)
end
@spec to_list(t(val)) :: [val] when val: value
def to_list(rb_set)
def to_list(%__MODULE__{root: root}) do
A.RBTree.Set.to_list(root)
end
@spec union(t(val1), t(val2)) :: t(val1 | val2) when val1: value, val2: value
def union(rb_set1, rb_set2)
def union(%__MODULE__{size: size1} = rb_set1, %__MODULE__{size: size2} = rb_set2)
when size1 < size2 do
union(rb_set2, rb_set1)
end
def union(%__MODULE__{} = rb_set1, %__MODULE__{} = rb_set2) do
{size, root} =
A.RBTree.Set.foldl(rb_set2.root, {rb_set1.size, rb_set1.root}, fn elem, {count, tree} ->
{result, new_tree} = A.RBTree.Set.insert(tree, elem)
case result do
:new -> {count + 1, new_tree}
_ -> {count, new_tree}
end
end)
%__MODULE__{root: root, size: size}
end
# Extra tree methods
@spec first(t(val), val | nil) :: val | nil when val: value
def first(rb_set, default \\ nil)
def first(%__MODULE__{root: root}, default) do
case A.RBTree.Set.min(root) do
{:ok, value} -> value
:error -> default
end
end
@spec last(t(val), val | nil) :: val | nil when val: value
def last(rb_set, default \\ nil)
def last(%__MODULE__{root: root}, default) do
case A.RBTree.Set.max(root) do
{:ok, value} -> value
:error -> default
end
end
@spec pop_first(t(val)) :: {val, t(val)} | nil when val: value
def pop_first(rb_set)
def pop_first(%__MODULE__{size: size, root: root}) do
case A.RBTree.Set.pop_min(root) do
{value, new_root} ->
new_rb_set = %__MODULE__{root: new_root, size: size - 1}
{value, new_rb_set}
:error ->
nil
end
end
@spec pop_last(t(val)) :: {val, t(val)} | nil when val: value
def pop_last(rb_set)
def pop_last(%__MODULE__{size: size, root: root}) do
case A.RBTree.Set.pop_max(root) do
{value, new_root} ->
new_rb_set = %__MODULE__{root: new_root, size: size - 1}
{value, new_rb_set}
:error ->
nil
end
end
def foldl(%__MODULE__{} = rb_set, acc, fun) when is_function(fun, 2) do
A.RBTree.Set.foldl(rb_set.root, acc, fun)
end
def foldr(%__MODULE__{} = rb_set, acc, fun) when is_function(fun, 2) do
A.RBTree.Set.foldr(rb_set.root, acc, fun)
end
# Not private, but only exposed for protocols
@doc false
def reduce(%__MODULE__{root: root}, acc, fun), do: A.RBTree.Set.reduce(root, acc, fun)
defimpl Collectable do
def into(set) do
fun = fn
set_acc, {:cont, value} ->
A.RBSet.put(set_acc, value)
set_acc, :done ->
set_acc
_set_acc, :halt ->
:ok
end
{set, fun}
end
end
defimpl Enumerable do
def count(set) do
{:ok, A.RBSet.size(set)}
end
def member?(set, val) do
{:ok, A.RBSet.member?(set, val)}
end
def slice(set) do
size = A.RBSet.size(set)
{:ok, size, &Enumerable.List.slice(A.RBSet.to_list(set), &1, &2, size)}
end
defdelegate reduce(set, acc, fun), to: A.RBSet
end
defimpl Inspect do
import Inspect.Algebra
def inspect(set, opts) do
opts = %Inspect.Opts{opts | charlists: :as_lists}
concat(["#A.RBSet<", Inspect.List.inspect(A.RBSet.to_list(set), opts), ">"])
end
end
if Code.ensure_loaded?(Jason.Encoder) do
defimpl Jason.Encoder do
def encode(set, opts) do
set |> A.RBSet.to_list() |> Jason.Encode.list(opts)
end
end
end
end