Packages
fixpoint
0.3.1
0.22.1
0.21.5
0.21.4
0.21.3
0.21.2
0.21.1
0.21.0
0.20.6
0.20.5
0.20.4
0.20.3
0.20.2
0.20.1
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.18.2
0.18.1
0.17.6
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.9
0.14.8
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.13.5
0.13.4
0.13.2
0.13.1
0.12.9
0.12.8
0.12.7
0.12.6
0.12.5
0.12.4
0.12.2
0.12.1
0.11.8
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.46
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.28
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.1.3
0.1.2
0.1.1
0.1.0
Constraint Programming Solver
Current section
Files
Jump to
Current section
Files
lib/solver/store/local_store.ex
defmodule CPSolver.Store.Local do
alias CPSolver.ConstraintStore
alias CPSolver.Variable
alias CPSolver.Variable.Agent, as: VariableAgent
use ConstraintStore
use GenServer
## Store callbacks
@impl true
def create(variables, opts \\ []) do
variable_map =
Map.new(
variables,
fn var ->
{:ok, pid} = VariableAgent.create(var)
{var.id, %{agent: pid, subscriptions: []}}
end
)
{:ok, _store} = GenServer.start_link(__MODULE__, [variable_map, opts])
end
@impl true
def dispose(store, _variables) do
GenServer.cast(store, :stop)
end
@impl true
def domain(store, var) do
GenServer.call(store, {:domain, var.id})
end
@impl true
def get(store, var, operation, args \\ []) do
GenServer.call(store, {:get, var.id, operation, args})
end
@impl true
def update_domain(store, var, operation, args \\ []) do
GenServer.call(store, {:update, var.id, operation, args})
end
@impl true
def on_change(store, variable, change) do
GenServer.cast(store, {:on_change, variable, change})
end
@impl true
def on_fail(store, variable) do
GenServer.cast(store, {:on_fail, variable})
end
@impl true
def on_no_change(store, variable) do
GenServer.cast(store, {:on_no_change, variable})
end
@impl true
def get_variables(store) do
GenServer.call(store, :get_variables)
end
@impl true
def subscribe(store, subscriptions) do
GenServer.cast(store, {:subscribe, Enum.map(subscriptions, &normalize_subscription/1)})
end
## GenServer callbacks
@impl true
def init([variables, _opts]) do
{:ok, %{variables: variables}}
end
@impl true
def handle_call({:domain, var}, _from, data) do
{:reply,
var
|> get_agent_pid(data)
|> VariableAgent.operation(:domain), data}
end
def handle_call({request_kind, var, operation, args}, _from, data)
when request_kind in [:update, :get] do
## Locate pid
reply =
var
|> get_agent_pid(data)
|> then(fn
nil ->
{:not_found, var}
agent_pid ->
VariableAgent.operation(agent_pid, operation, args)
|> tap(fn result ->
request_kind == :update && notify_subscribers(var, result, data)
end)
end)
{:reply, reply, data}
end
def handle_call(:get_variables, _from, %{variables: variables} = data) do
var_ids = Map.keys(variables)
{:reply, var_ids, data}
end
@impl true
def handle_cast({:on_change, _var, _change}, data) do
{:noreply, data}
end
def handle_cast({:on_fail, _var}, data) do
{:noreply, data}
end
def handle_cast({:on_no_change, _var}, data) do
{:noreply, data}
end
def handle_cast({:subscribe, subscriptions}, %{variables: variables} = data) do
new_data =
subscriptions
|> Enum.group_by(fn s -> s.variable end, fn s -> Map.delete(s, :variable) end)
|> Map.merge(variables, fn _var, new_subscr, agent ->
(new_subscr ++ agent.subscriptions)
|> Enum.uniq_by(fn s -> s.pid end)
|> then(fn updated_subscriptions ->
Map.put(agent, :subscriptions, updated_subscriptions)
end)
end)
|> then(fn updated_variables -> Map.put(data, :variables, Map.new(updated_variables)) end)
{:noreply, new_data}
end
def handle_cast(:stop, %{variables: variables} = data) do
Enum.each(variables, fn {_var_id, %{agent: pid}} = _agent ->
GenServer.stop(pid)
end)
{:stop, :normal, data}
end
defp get_agent_pid(var_id, %{variables: variables} = _data) do
case Map.get(variables, var_id) do
nil -> nil
%{agent: pid} -> pid
end
end
defp normalize_subscription(%{variable: variable, events: events} = subscription) do
%{subscription | variable: variable_id(variable), events: normalize_events(events)}
end
defp variable_id(%Variable{id: id}) do
id
end
defp variable_id(id) do
id
end
defp normalize_events(events) do
## :fixed is mandatory
events
|> Enum.uniq()
|> then(fn deduped -> (Enum.member?(deduped, :fixed) && deduped) || [:fixed | deduped] end)
end
defp notify_subscribers(_var, :no_change, _) do
:ignore
end
defp notify_subscribers(var, event, %{variables: variables} = _data) do
subscriptions = Map.get(variables, var) |> Map.get(:subscriptions)
Enum.each(subscriptions, fn s -> notify(s, var, event) end)
end
defp notify(%{pid: subscriber, events: _events} = _subscription, var, event) do
## TODO: notify based on the list of events
send(subscriber, {event, var})
end
end