lib/examples/bin_packing/upper_bound.ex

defmodule CPSolver.Examples.BinPacking.UpperBound do
  @moduledoc """
  Precompute upper bounds for bin packing instance
  based on heuristics.
  'First fit decreasing' is the one that is implemented
  """
  alias InPlace.Array

  def first_fit_decreasing(weights, capacity) do
    first_fit(Enum.sort(weights, :desc), capacity)
  end

  def first_fit(weights, capacity) do
    if hd(weights) > capacity do
      throw(:item_weight_over_capacity)
    end

    remaining_bin_space = Array.new(length(weights), capacity)
    Enum.reduce(weights, 1, fn w, count_acc ->
      if place_item(remaining_bin_space, w, count_acc) do
        count_acc
      else
        (count_acc + 1)
        |> tap(fn new_bin_idx ->
          Array.update(remaining_bin_space, new_bin_idx, fn val -> val - w end)
        end)
      end
    end)
  end

  defp place_item(bin_space, weight, bin_count) do
    Enum.reduce_while(1..bin_count, false, fn bin_idx, _acc ->
      if Array.get(bin_space, bin_idx) > weight do
        ## Can place item to this bin
        Array.update(bin_space, bin_idx, fn val -> val - weight end)
        {:halt, true}
      else
        {:cont, false}
      end
    end)
  end
    #     j = 0
    #     while( j < res):
    #         if (bin_rem[j] >= weight[i]):
    #             bin_rem[j] = bin_rem[j] - weight[i]
    #             break
    #         j+=1

    #     # If no bin could accommodate weight[i]
    #     if (j == res):
    #         bin_rem[res] = c - weight[i]
    #         res= res+1
    # return res


  def test() do
    weights = [ 2, 5, 4, 7, 1, 3, 8 ]
    capacity = 10
    first_fit_decreasing(weights, capacity)
  end

end