lib/solver/propagation/propagator_variable.ex

defmodule CPSolver.Propagator.Variable do
  alias CPSolver.Variable
  alias CPSolver.ConstraintStore

  @variable_op_results_key :variable_op_results
  @store_impl_key :store_impl
  defdelegate domain(var), to: Variable
  defdelegate size(var), to: Variable
  defdelegate min(var), to: Variable
  defdelegate max(var), to: Variable
  defdelegate fixed?(var), to: Variable
  defdelegate contains?(var, val), to: Variable

  def remove(var, val) do
    wrap(:remove, var, val)
  end

  def removeAbove(var, val) do
    wrap(:removeAbove, var, val)
  end

  def removeBelow(var, val) do
    wrap(:removeBelow, var, val)
  end

  def fix(var, val) do
    wrap(:fix, var, val)
  end

  defp wrap(op, var, val) do
    save_in_dict(var, apply(Variable, op, [var, val]))
  end

  defp save_in_dict(var, result) do
    result
    |> tap(fn
      :fail ->
        Process.put(@variable_op_results_key, {:fail, var.id})

      _ ->
        current = get_variable_ops()
        Process.put(@variable_op_results_key, Map.put(current, var.id, result))
    end)
  end

  def get_variable_ops() do
    Process.get(@variable_op_results_key, Map.new())
  end

  def set_store_impl(store_impl) do
    Process.put(@store_impl_key, store_impl)
  end

  def get_store_impl() do
    Process.get(@store_impl_key) || ConstraintStore.default_store()
  end

  def reset_variable_ops() do
    Process.delete(@variable_op_results_key)
  end

  def plus(:fail, _) do
    :fail
  end

  def plus(_, :fail) do
    :fail
  end

  def plus(a, b) do
    a + b
  end
end