Module: Equalshares::Tie

Defined in:
lib/equalshares/tie_breaking.rb

Overview

Tie-breaking facade. Composes the TieBreaker strategies named in params.tie_breaking to either resolve a single tie (break_ties) or build a total order (total_order).

Class Method Summary collapse

Class Method Details

.break_ties(_project_ids, cost, approvers, params, choices) ⇒ Object

Resolve a tie among choices by applying each tie-breaking strategy in priority order, narrowing the set. Returns the surviving candidates (callers treat more than one as an unresolved tie).

Raises:



12
13
14
15
16
17
18
19
20
21
# File 'lib/equalshares/tie_breaking.rb', line 12

def break_ties(_project_ids, cost, approvers, params, choices)
  ctx = TieBreaker::Context.new(cost, approvers)
  remaining = params.tie_breaking.reduce(choices.dup) do |current, method|
    TieBreaker.for(method).filter(current, ctx)
  end

  raise ComputeError, "Tie-breaking failed in a way that should not happen: #{choices}" if remaining.empty?

  remaining
end

.resolve_one(project_ids, cost, approvers, params, choices) ⇒ Object

Resolve a tie down to a single winner, raising if the tie-breaking rules leave more than one candidate. Used by the sequential rules.



25
26
27
28
29
30
31
32
33
# File 'lib/equalshares/tie_breaking.rb', line 25

def resolve_one(project_ids, cost, approvers, params, choices)
  resolved = break_ties(project_ids, cost, approvers, params, choices)
  if resolved.length > 1
    raise ComputeError,
          "Tie-breaking failed: tie between projects #{resolved.join(', ')} could not be resolved. " \
          "Another tie-breaking needs to be added."
  end
  resolved[0]
end

.total_order(project_ids, cost, approvers, params) ⇒ Object

A total order of all projects induced by params.tie_breaking, used by rules that need a full ordering (e.g. greedy welfare). Projects are sorted by each strategy's sort key in priority order, falling back to their original (JS Object.keys) order.



38
39
40
41
42
43
44
45
46
# File 'lib/equalshares/tie_breaking.rb', line 38

def total_order(project_ids, cost, approvers, params)
  ctx = TieBreaker::Context.new(cost, approvers)
  strategies = params.tie_breaking.map { |method| TieBreaker.for(method) }
  base_index = project_ids.each_with_index.to_h

  project_ids.sort_by do |c|
    strategies.map { |strategy| strategy.sort_key(c, ctx) } + [base_index[c]]
  end
end