require 'apportion/algorithm/equal_proportions' require 'apportion/setup' require 'apportion/util' # Top level interface, or API, to distribute a quantity among recipients module Apportion module_function # Distributes a quantity among recipients by dividing proportionally according to relative # weights using the 'equal proportions' algorithm # # @param weights [Hash] relative integer proportions # @param size [Integer] quantity to apportion # @param options [Hash] # @option options [Hash] :prior_portions from a previous apportionment # @option options [Integer] :required_minimum smallest portion for each recipient # @return [Hash] portions # @example # division({a: 41, b: 32, c: 27}, 7) # # => {a: 3, b: 2, c: 2} def division(weights, size, options = {}) portions = Setup.initial_portions(weights.keys, options) sum_portions = Util.hash_values_sum(portions) Setup.validate(size, sum_portions) next_step(weights, portions, sum_portions, size) end # recursively def next_step(weights, portions, sum_portions, size) return portions if sum_portions == size key = Algorithm::EqualProportions.next_recipient(weights, portions) next_portions = portions.merge(key => portions[key] + 1) next_step(weights, next_portions, sum_portions + 1, size) end private_class_method :next_step end