Skip to content

Commit

Permalink
to_d and allocate
Browse files Browse the repository at this point in the history
  • Loading branch information
drKreso committed Apr 18, 2012
1 parent ad9423e commit c752462
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 0 deletions.
26 changes: 26 additions & 0 deletions README.md
Expand Up @@ -54,6 +54,32 @@ end

Somehow for my convoluted brain the later reads better.

BigDecimal
-----------
Not using BigDecimal is asking for trouble, but using it is way to verbose:

```
amount = BigDecimal.new("100.10210")/BigDecimal.new(200.12)
```

It is more succint to put it like this:

```
amount = 100.10210.to_d/200.12/to_d
Allocate
---------
I belive allocate is missing from standard library.
```
90.allocate_evenly(3) #=> [30, 30, 30]
100.allocate_evenly(3) #=> [33.33, 33.33, 33.34]

100.allocate([1.to_d/2, 1/to_d/2]) #=> [50, 50]
100.allocate([30, 30, 30]) #=> [33.33, 33.33, 33.34]
```
Contributing to guerrilla_patch
-------------------------------
Expand Down
31 changes: 31 additions & 0 deletions lib/guerrilla_patch/allocate.rb
@@ -0,0 +1,31 @@
class Allocate
attr_accessor :amount
attr_accessor :ratios

def initialize(amount = 0 , ratios = [])
@amount = amount
@ratios = ratios
end

def divided
divided_ratios = @ratios.map { |ratio| ratio.to_d/ratios.sum.to_d }
compensate_last_slice( divided_ratios.map { |ratio| (@amount * ratio).round(2) } )
end

def compensate_last_slice(rates)
rates.tap do |rates|
rates[-1] = rates[-1] + (amount - rates.sum).round(2)
if ( amount > 0 && ( rates.select {|item| item <= 0 }.count > 0 ) ) ||
( amount < 0 && ( rates.select {|item| item >= 0 }.count > 0 ) )
raise "Number is too small to be allocated on that number of slices(#@amount on #{@ratios.size} slices)."
end
end
end

def self.evenly(amount, number_of_slices)
Allocate.new.tap do |a|
a.amount = amount
a.ratios = (1..number_of_slices).map { 1.to_d/number_of_slices }
end.divided
end
end #Allocate
20 changes: 20 additions & 0 deletions lib/guerrilla_patch/kernel.rb
@@ -1,3 +1,6 @@
require 'bigdecimal'
require 'guerrilla_patch/allocate'

module Kernel
def let(name, &block)
define_method(name, &block)
Expand Down Expand Up @@ -48,3 +51,20 @@ def sum(name = nil)
self.map(&name).reduce(0, :+)
end
end

module Kernel
def to_d
BigDecimal.new(self.to_s)
end

def allocate_evenly(number_of_slices)
Allocate.evenly(self.to_d, number_of_slices)
end

def allocate(ratios)
Allocate.new(self.to_d, ratios).divided
end
end



67 changes: 67 additions & 0 deletions spec/guerrilla_patch/allocate_spec.rb
@@ -0,0 +1,67 @@
require 'guerrilla_patch/allocate'

describe Allocate do
it 'should divide whole numbers' do
subject.amount = 100
subject.ratios = [0.1,0.9]
subject.divided.should == [10, 90]
end

it 'should divide equally on two decimals' do
subject.amount = 100
subject.ratios = [1.0/3, 1.0/3,1.0/3]
subject.divided.should == [33.33, 33.33, 33.34]
subject.divided.inject(0) { | sum, item | sum += item }.should == subject.amount
end

it 'should calculate ratios if they are over 1' do
subject.amount = 100
subject.ratios = [50,70,90]
subject.divided.should == [23.81, 33.33, 42.86 ]
subject.divided.inject(0) { | sum, item | sum += item }.should == subject.amount
end

it 'should calculade real example' do
subject.amount = 2297.19
subject.ratios = [2170.29, 126.90 ]
subject.divided.should == [2170.29, 126.90 ]
end

it 'should divide equally on number of slices' do
Allocate.evenly(100,3).should == [33.33,33.33,33.34]
end

it 'should divide equally on number of slices for small number' do
Allocate.evenly(0.1,3).should == [0.03,0.03,0.04]
end

it 'should divide equally on number of slices for really small number' do
Allocate.evenly(0.1,9).should == [0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.02]
end

it 'should divide equally on two decimals negative numbers' do
subject.amount = -100
subject.ratios = [1.0/3, 1.0/3,1.0/3]
subject.divided.should == [-33.33, -33.33, -33.34]
subject.divided.inject(0) { | sum, item | sum += item }.should == subject.amount
end

it 'should raise exception for number that is too small' do
lambda do
Allocate.evenly(0.1,20)
end.should raise_error 'Number is too small to be allocated on that number of slices(0.1 on 20 slices).'
end

it 'should raise exception for number that is too small negative' do
lambda do
Allocate.evenly(-0.1,20)
end.should raise_error 'Number is too small to be allocated on that number of slices(-0.1 on 20 slices).'
end

it 'should support costructor based params' do
subject = Allocate.new( 100, [1.0/3, 1.0/3,1.0/3] )
subject.divided.should == [33.33, 33.33, 33.34]
subject.divided.inject(0) { | sum, item | sum += item }.should == subject.amount
end

end
10 changes: 10 additions & 0 deletions spec/guerrilla_patch/kernel_spec.rb
Expand Up @@ -63,6 +63,16 @@

end

it 'can allocate a number evenly' do
100.allocate_evenly(2).should == [50, 50]
100.allocate_evenly(3).should == [33.33, 33.33, 33.34]
end

it 'can allocate a number proportionaly' do
100.allocate([1.to_d/2, 1.to_d/2]).should == [50, 50]
100.allocate([30,30,30]).should == [33.33, 33.33, 33.34]
end

end

describe Array do
Expand Down

0 comments on commit c752462

Please sign in to comment.