Current section

Files

Jump to
fixpoint lib solver store ets_store.ex
Raw

lib/solver/store/ets_store.ex

defmodule CPSolver.Store.ETS do
alias CPSolver.DefaultDomain, as: Domain
use CPSolver.ConstraintStore
alias CPSolver.ConstraintStore
@domain_changes CPSolver.Common.domain_changes()
@impl true
def create(variables, _opts \\ []) do
table_id =
:ets.new(__MODULE__, [:set, :public, read_concurrency: true, write_concurrency: true])
Enum.each(
variables,
fn var ->
:ets.insert(table_id, {var.id, %{domain: Domain.new(var.domain), subscriptions: []}})
end
)
{:ok, table_id}
end
@impl true
def dispose(_store, _vars) do
:ok
end
@impl true
def get_variables(store) do
:ets.tab2list(store) |> Enum.map(fn {_id, var} -> var.id end)
end
@impl true
def get(store, variable, operation, args \\ []) do
store
|> domain(variable)
|> then(fn
:fail -> :fail
domain -> apply(Domain, operation, [domain | args])
end)
end
@impl true
def update_domain(store, variable, operation, args) do
store
|> domain(variable)
|> then(fn
:fail ->
:fail
d ->
apply(Domain, operation, [d | args])
|> then(fn
{domain_change, new_domain} when domain_change in @domain_changes ->
update_variable_domain(store, variable, new_domain)
domain_change
:fail ->
update_variable_domain(store, variable, :fail)
:fail
:no_change ->
:no_change
end)
end)
end
@impl true
def on_change(_store, _variable, _change) do
:ok
end
@impl true
def on_no_change(_store, _variable) do
:ok
end
@impl true
def on_fail(_store, _variable) do
:ok
end
@impl true
def subscribe(store, subscriptions) do
variables = :ets.tab2list(store) |> Map.new()
subscriptions
|> Enum.map(&ConstraintStore.normalize_subscription/1)
|> Enum.group_by(fn s -> s.variable end, fn s -> Map.delete(s, :variable) end)
|> Map.merge(variables, fn _var, new_subscr, variable ->
(new_subscr ++ variable.subscriptions)
|> Enum.uniq_by(fn s -> s.pid end)
|> then(fn updated_subscriptions ->
Map.put(variable, :subscriptions, updated_subscriptions)
end)
end)
|> then(fn updated_variables ->
Enum.each(updated_variables, fn {var_id, var_data} ->
:ets.insert(store, {var_id, var_data})
end)
end)
end
defp update_variable_domain(store, variable, domain) do
:ets.insert(store, {variable.id, Map.put(variable, :domain, domain)})
end
@impl true
def domain(store, variable) do
store
|> lookup(variable)
|> Map.get(:domain)
end
def lookup(store, variable) do
store
|> :ets.lookup(variable.id)
|> hd
|> elem(1)
end
end