defmodule CPSolverTest.Constraint.AllDifferent.Utils do use ExUnit.Case, async: false alias CPSolver.Propagator.AllDifferent.Utils, as: AllDiffUtils describe "Forward checking" do alias CPSolver.IntVariable, as: Variable test "cascading" do domains = [1, 1..2, 1..3, 1..4, 1..5] vars = Enum.map(Enum.shuffle(domains), fn d -> Variable.new(d) end) {unfixed_indices, fixed_values} = AllDiffUtils.forward_checking(vars) assert MapSet.size(fixed_values) == length(vars) ## Everything is fixed assert Enum.empty?(unfixed_indices) assert Enum.all?(vars, fn var -> Variable.fixed?(var) end) ## AllDifferent check assert MapSet.new(vars, fn var -> Variable.min(var) end) |> MapSet.size() == length(vars) end test "pigeonhole" do domains = [1..2, 1..2, 1..2] vars = Enum.map(Enum.shuffle(domains), fn d -> Variable.new(d) end) {unfixed_indices, fixed_values} = AllDiffUtils.forward_checking(vars) ## FWC does not reduce if no fixed variables assert MapSet.size(unfixed_indices) == length(vars) assert Enum.empty?(fixed_values) ## Trigger reduction by fixing one of the variables Variable.fix(Enum.random(vars), Enum.random(1..2)) assert catch_throw(:fail = AllDiffUtils.forward_checking(vars)) end test "reuse" do # An example from Zhang domains = [1, 1..2, 1..4, [1, 2, 4, 5]] vars = Enum.map(domains, fn d -> Variable.new(d) end) {unfixed_indices, fixed_values} = AllDiffUtils.forward_checking(vars) ## First 2 variables fixed with values 1 and 2 assert unfixed_indices == MapSet.new([2, 3]) assert fixed_values == MapSet.new([1, 2]) ## Run FWC again with the data from previous run - no effect assert {unfixed_indices, fixed_values} == AllDiffUtils.forward_checking(vars, unfixed_indices, fixed_values) ## Fix and run FWC again with previous results ## ## domain(var3) = [3,4]; domain(var4) = [4,5] ## Fix shared value (4) for any of the unfixed variables Variable.fix(Enum.at(vars, Enum.random([2,3])), 4) {unfixed_indices2, _fixed_values2} = AllDiffUtils.forward_checking(vars, unfixed_indices, fixed_values) ## Everything is fixed assert Enum.empty?(unfixed_indices2) assert Enum.all?(vars, fn var -> Variable.fixed?(var) end) end end describe "Component locator" do test "build and locate" do vertices = MapSet.new(1..10) components = Enum.map([[1, 3, 5, 7, 9], [2, 4, 6, 8]], fn c -> MapSet.new(c) end) component_locator = AllDiffUtils.build_component_locator(vertices, components) assert AllDiffUtils.get_component(component_locator, 1) == hd(components) assert AllDiffUtils.get_component(component_locator, 4) == List.last(components) refute AllDiffUtils.get_component(component_locator, 10) end end end