defmodule CPSolver.DefaultDomain do
alias CPSolver.Common
@spec new(Enum.t()) :: :gb_sets.set(number())
def new([]) do
throw(:empty_domain)
end
def new(domain) when is_integer(domain) do
:gb_sets.from_list([domain])
end
def new(domain) do
(:gb_sets.is_set(domain) && domain) ||
Enum.reduce(domain, :gb_sets.new(), fn v, acc -> :gb_sets.add_element(v, acc) end)
end
@spec size(:gb_sets.set(number())) :: non_neg_integer
def size(domain) do
:gb_sets.size(domain)
end
@spec fixed?(:gb_sets.set(number())) :: boolean
def fixed?(domain) do
size(domain) == 1
end
@spec min(:gb_sets.set(number())) :: number()
def min(domain) do
:gb_sets.smallest(domain)
end
@spec max(domain :: :gb_sets.set(number())) :: number()
def max(domain) do
:gb_sets.largest(domain)
end
@spec contains?(:gb_sets.set(number()), number()) :: boolean
def contains?(domain, value) do
:gb_sets.is_member(value, domain)
end
@spec remove(:gb_sets.set(number()), number()) ::
:fail
| :no_change
| {Common.domain_change(), :gb_sets.set(number())}
def remove(domain, value) do
:gb_sets.delete_any(value, domain)
|> post_remove(domain, :domain_change)
end
@spec removeAbove(:gb_sets.set(number()), number()) ::
:fail
| :no_change
| {Common.domain_change(), :gb_sets.set(number())}
def removeAbove(domain, value) do
:gb_sets.filter(fn v -> v <= value end, domain)
|> post_remove(domain, :max_change)
end
@spec removeBelow(:gb_sets.set(number()), number()) ::
:fail | :no_change | {Common.domain_change(), :gb_sets.set(number())}
def removeBelow(domain, value) do
:gb_sets.filter(fn v -> v >= value end, domain)
|> post_remove(domain, :min_change)
end
@spec fix(:gb_sets.set(any), number()) :: :fail | {:fixed, :gb_sets.set(number())}
def fix(domain, value) do
if contains?(domain, value) do
{:fixed, :gb_sets.from_list([value])}
else
:no_change
end
end
defp post_remove(new_domain, domain, change_kind) do
case size(new_domain) do
0 ->
:fail
new_size ->
case size(domain) do
old_size when old_size == new_size ->
:no_change
old_size when old_size > new_size ->
{(new_size == 1 && :fixed) || change_kind, new_domain}
end
end
end
end