Module: Amount::Allocation

Included in:
Amount
Defined in:
lib/amount/allocation.rb

Overview

Splitting and proportional allocation. Both operations return ‘[parts, remainder]`, preserving the invariant that the parts plus remainder always sum back to the receiver’s atomic value (including the sign).

Instance Method Summary collapse

Instance Method Details

#allocate(weights) ⇒ Array<(Array<Amount>, Amount)>

Allocates proportionally by integer weights and returns the leftover explicitly.

Examples:

parts, remainder = Amount.new(10, :LOGS).allocate([1, 1, 2])
parts.map(&:atomic)
# => [2, 2, 5]
remainder.atomic
# => 1

Parameters:

  • weights (Array<Integer>)

Returns:

Raises:

  • (ArgumentError)

    if weights are empty, negative, or sum to zero



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/amount/allocation.rb', line 41

def allocate(weights)
  raise ArgumentError, "weights must be non-empty" if weights.empty?
  raise ArgumentError, "weights must be non-negative integers" unless weights.all? { |weight| weight.is_a?(Integer) && weight >= 0 }

  total = weights.sum
  raise ArgumentError, "weights must sum to positive value" unless total.positive?

  sign = atomic_sign
  absolute_atomic = @atomic.abs
  allocations = weights.map { |weight| absolute_atomic * weight / total }
  remainder = absolute_atomic - allocations.sum

  parts = allocations.map { |allocation| build(sign * allocation) }
  [parts, build(sign * remainder)]
end

#split(n) ⇒ Array<(Array<Amount>, Amount)>

Splits into equal parts and returns the leftover explicitly.

Examples:

parts, remainder = Amount.new(10, :LOGS).split(3)
parts.map(&:atomic)
# => [3, 3, 3]
remainder.atomic
# => 1

Parameters:

  • n (Integer)

Returns:

Raises:

  • (ArgumentError)

    if ‘n` is not a positive integer



20
21
22
23
24
25
26
27
28
# File 'lib/amount/allocation.rb', line 20

def split(n)
  raise ArgumentError, "n must be positive" unless n.is_a?(Integer) && n.positive?

  sign = atomic_sign
  base, remainder = @atomic.abs.divmod(n)
  parts = Array.new(n) { build(sign * base) }

  [parts, build(sign * remainder)]
end