lib/solver/propagators/propagator.ex

defmodule CPSolver.Propagator do
  @callback filter(args :: list()) :: map() | :stable | :failure
  @callback variables(args :: list()) :: list()
  @callback events() :: list()

  alias CPSolver.Variable

  defmacro __using__(_) do
    quote do
      @behaviour CPSolver.Propagator
      def variables(args) do
        args
      end

      ## Events that trigger propagation
      def events() do
        CPSolver.Common.domain_changes()
      end

      defoverridable variables: 1
      defoverridable events: 0
    end
  end

  def normalize(propagators, store, store_impl) when is_list(propagators) do
    propagators
    |> Enum.map(fn p -> normalize(p, store, store_impl) end)
    |> Enum.uniq()
  end

  def normalize({mod, args} = _propagator, store, store_impl) when is_list(args) do
    {mod,
     Enum.map(
       args,
       fn
         %Variable{} = arg ->
           arg
           |> Map.put(:store, store)
           |> Map.put(:store_impl, store_impl)

         const ->
           const
       end
     )}
  end

  def normalize(propagator, store, store_impl) when is_tuple(propagator) do
    [mod | args] = Tuple.to_list(propagator)
    normalize({mod, args}, store, store_impl)
  end

  def filter(mod, args) do
    mod.filter(args)
  end
end