Module: TypeBalancer::Distributor

Defined in:
lib/type_balancer/distributor.rb

Class Method Summary collapse

Class Method Details

.calculate_target_positions(total_count:, ratio:, available_positions: nil) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/type_balancer/distributor.rb', line 5

def self.calculate_target_positions(total_count:, ratio:, available_positions: nil)
  # Validate inputs
  return [] if total_count <= 0 || ratio <= 0 || ratio > 1

  # Calculate base target count
  target_count = (total_count * ratio).ceil

  # Special case for 3 slots
  if total_count == 3
    target_count = if ratio <= 0.34
                     1
                   elsif ratio <= 0.67
                     2
                   else
                     3
                   end
  end

  return [] if target_count.zero?
  return (0...total_count).to_a if target_count >= total_count

  if available_positions
    # Filter out invalid positions and sort them
    valid_positions = available_positions.select { |pos| pos >= 0 && pos < total_count }.sort
    return [] if valid_positions.empty?

    # For single target position, use first available
    return [valid_positions.first] if target_count == 1

    # For two positions
    if target_count == 2
      # Special case for three slots
      if total_count == 3
        return [valid_positions[0], valid_positions[1]] if valid_positions.size >= 2

        return [valid_positions.first, valid_positions.first + 1]
      end

      # Special case for invalid positions that go beyond total_count
      if available_positions.any? { |pos| pos >= total_count }
        valid_positions = available_positions.select { |pos| pos >= 0 }.sort
        return [valid_positions.first, valid_positions.last]
      end

      # Otherwise use first and last
      return [valid_positions.first, valid_positions.last]
    end

    # If we have fewer or equal positions than needed, use all available up to target_count
    return valid_positions if valid_positions.size <= target_count

    # For more positions, take the first N positions where N is target_count
    return valid_positions.first(target_count) if target_count <= 3

    # For larger target counts, distribute evenly
    target_positions = []
    step = (valid_positions.size - 1).fdiv(target_count - 1)
    (0...target_count).each do |i|
      index = (i * step).round
      target_positions << valid_positions[index]
    end
    target_positions
  else
    # Handle single target position
    return [0] if target_count == 1

    # For two positions
    if target_count == 2
      # Special case for three slots
      return [0, 1] if total_count == 3

      # Otherwise use first and last
      return [0, total_count - 1]
    end

    # Calculate evenly spaced positions for multiple targets
    (0...target_count).map do |i|
      ((total_count - 1) * i.fdiv(target_count - 1)).round
    end
  end
end