defmodule CPSolver.Examples.XKCD.NP do @doc """ xkcd-np. """ alias CPSolver.IntVariable, as: Variable alias CPSolver.Model alias CPSolver.Constraint.Sum import CPSolver.Variable.View.Factory def model() do appetizers = [ {:mixed_fruit, 215}, {:french_fries, 275}, {:side_salad, 335}, {:hot_wings, 355}, {:mozarella_sticks, 420}, {:sampler_plate, 580} ] total = 1505 quantities = Enum.map(appetizers, fn {name, price} -> mul(Variable.new(0..div(total, price), name: name), price) end) total_var = Variable.new([total], name: :total) Model.new(quantities, [Sum.new(total_var, quantities)], extra: %{appetizers: appetizers, total: total} ) end def check_solution(solution, %{extra: %{appetizers: appetizers, total: total}} = _model) do appetizers |> Enum.zip(solution |> Enum.take(length(appetizers))) |> Enum.reduce(0, fn {{_name, price}, quantity}, acc -> acc + price * quantity end) |> then(fn sum -> sum == total end) end def solve() do model = model() num_appetizers = length(model.extra.appetizers) {:ok, res} = CPSolver.solve_sync(model) Enum.map_join(res.solutions, "\n OR \n", fn sol -> sol |> Enum.zip(res.variables) |> Enum.take(num_appetizers) |> Enum.reject(fn {q, _name} -> q == 0 end) |> Enum.map_join(", ", fn {q, name} -> "#{name} : #{q}" end) end) |> IO.puts() end end