box_packer 0.0.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/.gitignore +0 -16
- data/.rspec +2 -0
- data/README.md +70 -23
- data/box_packer.gemspec +14 -11
- data/lib/box_packer.rb +3 -196
- data/lib/box_packer/box.rb +39 -0
- data/lib/box_packer/builder.rb +22 -0
- data/lib/box_packer/container.rb +90 -0
- data/lib/box_packer/dimensions.rb +37 -0
- data/lib/box_packer/item.rb +33 -0
- data/lib/box_packer/packer.rb +58 -0
- data/lib/box_packer/packing.rb +40 -0
- data/lib/box_packer/position.rb +11 -0
- data/lib/box_packer/version.rb +1 -1
- data/spec/lib/box_spec.rb +53 -0
- data/spec/lib/container_spec.rb +88 -0
- data/spec/lib/dimensions_spec.rb +52 -0
- data/spec/lib/item_spec.rb +30 -0
- data/spec/lib/packer_spec.rb +81 -0
- data/spec/lib/packing_spec.rb +73 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support/matchers/box_packer.rb +40 -0
- metadata +61 -9
- data/lib/tc_box_packer.rb +0 -171
@@ -0,0 +1,30 @@
|
|
1
|
+
module BoxPacker
|
2
|
+
describe Item do
|
3
|
+
subject(:item){ Item.new(dimensions) }
|
4
|
+
let(:dimensions){ [12, 5, 7] }
|
5
|
+
|
6
|
+
describe '#fit_into' do
|
7
|
+
|
8
|
+
context 'when box is larger than box' do
|
9
|
+
let(:box) { Box.new(Dimensions[6, 15, 9]) }
|
10
|
+
let(:rotated_dimensions) { Dimensions[5, 12, 7] }
|
11
|
+
|
12
|
+
it 'fits and orientation is rotated to fit' do
|
13
|
+
expect(item.fit_into?(box)).to be(true)
|
14
|
+
expect(item.dimensions).to eql(rotated_dimensions)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when other box has a smaller side than box' do
|
19
|
+
let(:box) { Box.new(Dimensions[11, 6, 7]) }
|
20
|
+
|
21
|
+
it 'does not fit and orientation is unchanged' do
|
22
|
+
expect(item.fit_into?(box)).to be(false)
|
23
|
+
expect(item.dimensions).to eql(Dimensions[*dimensions])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module BoxPacker
|
2
|
+
describe Packer do
|
3
|
+
subject(:container) { Container.new(dimensions) }
|
4
|
+
let(:dimensions) { [10, 15, 5] }
|
5
|
+
let(:opts) { nil }
|
6
|
+
let(:items){[
|
7
|
+
Item.new([1,1,1], weight: 1),
|
8
|
+
Item.new([3,1,1], weight: 6),
|
9
|
+
Item.new([5,5,1], weight: 15),
|
10
|
+
Item.new([5,5,5], weight: 20)
|
11
|
+
]}
|
12
|
+
|
13
|
+
describe '#pack' do
|
14
|
+
|
15
|
+
context 'with items that fit exactly in one packing' do
|
16
|
+
before do
|
17
|
+
20.times { container.items << items[0].dup }
|
18
|
+
10.times { container.items << items[1].dup }
|
19
|
+
8.times { container.items << items[2].dup }
|
20
|
+
4.times { container.items << items[3].dup }
|
21
|
+
end
|
22
|
+
|
23
|
+
it do
|
24
|
+
expect(container.pack!).to eql(1)
|
25
|
+
expect(container.packed_successfully).to be(true)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with items that fit exactly in three packings' do
|
30
|
+
before do
|
31
|
+
35.times { container.items << items[0].dup }
|
32
|
+
30.times { container.items << items[1].dup }
|
33
|
+
10.times { container.items << items[2].dup }
|
34
|
+
15.times { container.items << items[3].dup }
|
35
|
+
end
|
36
|
+
|
37
|
+
it do
|
38
|
+
expect(container.pack!).to eql(3)
|
39
|
+
expect(container.packed_successfully).to be(true)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with a packing limit of one and too many items' do
|
44
|
+
before do
|
45
|
+
container.packings_limit = 1
|
46
|
+
35.times { container.items << items[0].dup }
|
47
|
+
30.times { container.items << items[1].dup }
|
48
|
+
10.times { container.items << items[2].dup }
|
49
|
+
15.times { container.items << items[3].dup }
|
50
|
+
end
|
51
|
+
|
52
|
+
it do
|
53
|
+
expect(container.pack!).to eql(0)
|
54
|
+
expect(container.packed_successfully).to be(false)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with random container and random items' do
|
59
|
+
let(:dimensions) { [x, y, z] }
|
60
|
+
let(:x) { rand(1..75) }
|
61
|
+
let(:y) { rand(1..75) }
|
62
|
+
let(:z) { rand(1..75) }
|
63
|
+
|
64
|
+
let(:items) do
|
65
|
+
(1..rand(1..100)).map do
|
66
|
+
Item.new([x / rand(1..5), y / rand(1..5), z / rand(1..5)])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
before do
|
71
|
+
container.items = items
|
72
|
+
container.pack!
|
73
|
+
end
|
74
|
+
|
75
|
+
it { expect(container.packed_successfully).to be(true) }
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module BoxPacker
|
2
|
+
describe Packing do
|
3
|
+
subject(:packing) { Packing.new(total_volume, total_weight) }
|
4
|
+
let(:total_volume) { 200 }
|
5
|
+
let(:total_weight) { 50 }
|
6
|
+
let(:items) {[
|
7
|
+
Item.new([2, 4, 1], weight: 5),
|
8
|
+
Item.new([5, 2, 7], weight: 6),
|
9
|
+
Item.new([8, 4, 2], weight: 3)
|
10
|
+
]}
|
11
|
+
|
12
|
+
it 'defaults to empty' do
|
13
|
+
expect(packing).to eql([])
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#<<' do
|
17
|
+
before { items.each{|i| packing << i} }
|
18
|
+
|
19
|
+
it { expect(packing.remaining_volume).to eql(58) }
|
20
|
+
it { expect(packing.remaining_weight).to eql(36) }
|
21
|
+
|
22
|
+
context 'with items that do not have weight' do
|
23
|
+
let(:items) {[
|
24
|
+
Item.new([2, 4, 1]),
|
25
|
+
Item.new([5, 2, 7]),
|
26
|
+
Item.new([8, 4, 2])
|
27
|
+
]}
|
28
|
+
|
29
|
+
it { expect(packing.remaining_weight).to be(50) }
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'with total_weight nil' do
|
33
|
+
let(:total_weight) { nil }
|
34
|
+
it { expect(packing.remaining_weight).to be(nil) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#fit?' do
|
39
|
+
before { items.each{|i| packing << i} }
|
40
|
+
|
41
|
+
context 'with item that fits' do
|
42
|
+
let(:item) { Item.new([1, 5, 5], weight: 5)}
|
43
|
+
it { expect(packing.fit?(item)).to be(true) }
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with item thats already packed' do
|
47
|
+
it { expect(packing.fit?(items[0])).to be(false) }
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'with item thats too large' do
|
51
|
+
let(:item) { Item.new([3, 5, 5], weight: 25)}
|
52
|
+
it { expect(packing.fit?(item)).to be(false) }
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'with item thats too heavy' do
|
56
|
+
let(:item) { Item.new([1, 5, 5], weight: 45)}
|
57
|
+
it { expect(packing.fit?(item)).to be(false) }
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'with total_weight nil and item that fits' do
|
61
|
+
let(:total_weight) { nil }
|
62
|
+
let(:item) { Item.new([1, 5, 5], weight: 5)}
|
63
|
+
it { expect(packing.fit?(item)).to be(true) }
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'with item that has no weight but fits' do
|
67
|
+
let(:item) { Item.new([1, 5, 5])}
|
68
|
+
it { expect(packing.fit?(item)).to be(true) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
RSpec.configure do |config|
|
2
|
+
|
3
|
+
config.expect_with :rspec do |expectations|
|
4
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
5
|
+
end
|
6
|
+
|
7
|
+
config.mock_with :rspec do |mocks|
|
8
|
+
mocks.verify_partial_doubles = true
|
9
|
+
end
|
10
|
+
|
11
|
+
require_relative '../lib/box_packer'
|
12
|
+
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
|
13
|
+
config.include BoxPacker::Matchers
|
14
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'attr_extras'
|
2
|
+
require 'rspec/matchers/built_in/yield'
|
3
|
+
|
4
|
+
module BoxPacker
|
5
|
+
module Matchers
|
6
|
+
class YieldEachOnce
|
7
|
+
pattr_initialize :expected
|
8
|
+
|
9
|
+
def matches?(block)
|
10
|
+
@probe = RSpec::Matchers::BuiltIn::YieldProbe.probe(block)
|
11
|
+
@actual = @probe.successive_yield_args
|
12
|
+
|
13
|
+
@actual.each do |value|
|
14
|
+
unless expected.delete(value)
|
15
|
+
@failure_value = value
|
16
|
+
return false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def supports_block_expectations?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def description
|
27
|
+
'be in expected array'
|
28
|
+
end
|
29
|
+
|
30
|
+
def failure_message
|
31
|
+
"value #{@failure_value.to_a} was not in expected array"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def yield_each_once(expected)
|
37
|
+
YieldEachOnce.new(expected)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: box_packer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Max White
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -28,14 +28,42 @@ dependencies:
|
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - '>='
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: attr_extras
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
39
67
|
- !ruby/object:Gem::Version
|
40
68
|
version: '0'
|
41
69
|
description: A Heuristic First-Fit 3D Bin Packing Algorithm with Weight Limit
|
@@ -46,14 +74,30 @@ extensions: []
|
|
46
74
|
extra_rdoc_files: []
|
47
75
|
files:
|
48
76
|
- .gitignore
|
77
|
+
- .rspec
|
49
78
|
- Gemfile
|
50
79
|
- LICENSE.txt
|
51
80
|
- README.md
|
52
81
|
- Rakefile
|
53
82
|
- box_packer.gemspec
|
54
83
|
- lib/box_packer.rb
|
84
|
+
- lib/box_packer/box.rb
|
85
|
+
- lib/box_packer/builder.rb
|
86
|
+
- lib/box_packer/container.rb
|
87
|
+
- lib/box_packer/dimensions.rb
|
88
|
+
- lib/box_packer/item.rb
|
89
|
+
- lib/box_packer/packer.rb
|
90
|
+
- lib/box_packer/packing.rb
|
91
|
+
- lib/box_packer/position.rb
|
55
92
|
- lib/box_packer/version.rb
|
56
|
-
- lib/
|
93
|
+
- spec/lib/box_spec.rb
|
94
|
+
- spec/lib/container_spec.rb
|
95
|
+
- spec/lib/dimensions_spec.rb
|
96
|
+
- spec/lib/item_spec.rb
|
97
|
+
- spec/lib/packer_spec.rb
|
98
|
+
- spec/lib/packing_spec.rb
|
99
|
+
- spec/spec_helper.rb
|
100
|
+
- spec/support/matchers/box_packer.rb
|
57
101
|
homepage: ''
|
58
102
|
licenses:
|
59
103
|
- MIT
|
@@ -64,18 +108,26 @@ require_paths:
|
|
64
108
|
- lib
|
65
109
|
required_ruby_version: !ruby/object:Gem::Requirement
|
66
110
|
requirements:
|
67
|
-
- -
|
111
|
+
- - '>='
|
68
112
|
- !ruby/object:Gem::Version
|
69
113
|
version: '0'
|
70
114
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
115
|
requirements:
|
72
|
-
- -
|
116
|
+
- - '>='
|
73
117
|
- !ruby/object:Gem::Version
|
74
118
|
version: '0'
|
75
119
|
requirements: []
|
76
120
|
rubyforge_project:
|
77
|
-
rubygems_version: 2.
|
121
|
+
rubygems_version: 2.4.1
|
78
122
|
signing_key:
|
79
123
|
specification_version: 4
|
80
124
|
summary: A Heuristic First-Fit 3D Bin Packing Algorithm with Weight Limit
|
81
|
-
test_files:
|
125
|
+
test_files:
|
126
|
+
- spec/lib/box_spec.rb
|
127
|
+
- spec/lib/container_spec.rb
|
128
|
+
- spec/lib/dimensions_spec.rb
|
129
|
+
- spec/lib/item_spec.rb
|
130
|
+
- spec/lib/packer_spec.rb
|
131
|
+
- spec/lib/packing_spec.rb
|
132
|
+
- spec/spec_helper.rb
|
133
|
+
- spec/support/matchers/box_packer.rb
|
data/lib/tc_box_packer.rb
DELETED
@@ -1,171 +0,0 @@
|
|
1
|
-
require_relative "box_packer"
|
2
|
-
require "test/unit"
|
3
|
-
require 'benchmark'
|
4
|
-
|
5
|
-
class TestBoxPacker < Test::Unit::TestCase
|
6
|
-
|
7
|
-
def setup
|
8
|
-
@containers = []
|
9
|
-
@containers[0] = BoxPacker::Container.new("A",[10, 1, 1],15)
|
10
|
-
@containers[1] = BoxPacker::Container.new("B",[1, 10, 5],50)
|
11
|
-
@containers[2] = BoxPacker::Container.new("C",[10, 15, 5],325)
|
12
|
-
|
13
|
-
@items = []
|
14
|
-
@items[0] = BoxPacker::Item.new("a",[1, 1, 1],1)
|
15
|
-
@items[1] = BoxPacker::Item.new("b",[3, 1, 1],6)
|
16
|
-
@items[2] = BoxPacker::Item.new("c",[5, 5, 1],15)
|
17
|
-
@items[3] = BoxPacker::Item.new("d",[5, 5, 5],20)
|
18
|
-
|
19
|
-
@skip_items_count = false
|
20
|
-
end
|
21
|
-
|
22
|
-
def teardown
|
23
|
-
@containers.delete_if{ |container| container.packings.empty? }
|
24
|
-
unless @containers.empty?
|
25
|
-
assert(@containers[0].packings.map(&:count).reduce(:+) == @containers[0].items.count, "Packed items less than container's list") unless @skip_items_count
|
26
|
-
assert(@containers[0].packings.map(&:remaining_weight).all? { |rw| rw >= 0 }, "Packing too heavy")
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_no_items
|
31
|
-
assert_nil(@containers[0].pack)
|
32
|
-
end
|
33
|
-
|
34
|
-
def test_items_too_heavy
|
35
|
-
@items[0].weight = 16
|
36
|
-
@containers[0].items << @items[0]
|
37
|
-
assert_nil(@containers[0].pack)
|
38
|
-
end
|
39
|
-
|
40
|
-
def test_items_too_big
|
41
|
-
@containers[0].items << @items[2]
|
42
|
-
assert_nil(@containers[0].pack)
|
43
|
-
end
|
44
|
-
|
45
|
-
def test_too_many_packings
|
46
|
-
@containers[0].packings_limit = 1
|
47
|
-
11.times { @containers[0].items << @items[0] }
|
48
|
-
assert_nil(@containers[0].pack)
|
49
|
-
@skip_items_count = true
|
50
|
-
end
|
51
|
-
|
52
|
-
def test_1d_one_full_packing
|
53
|
-
7.times { @containers[0].items << @items[0] }
|
54
|
-
@containers[0].items << @items[1]
|
55
|
-
assert_equal(1, @containers[0].pack)
|
56
|
-
assert_equal(0, @containers[0].packings[0].remaining_volume)
|
57
|
-
end
|
58
|
-
|
59
|
-
def test_1d_two_packings_due_to_weight_limit
|
60
|
-
3.times { @containers[0].items << @items[1] }
|
61
|
-
assert_equal(2, @containers[0].pack)
|
62
|
-
end
|
63
|
-
|
64
|
-
def test_1d_three_full_packings
|
65
|
-
@containers[0].packings_limit = 4
|
66
|
-
3.times { @containers[0].items << @items[0] << @items[1] }
|
67
|
-
18.times { @containers[0].items << @items[0] }
|
68
|
-
assert_equal(3, @containers[0].pack)
|
69
|
-
end
|
70
|
-
|
71
|
-
def test_2d_one_full_packing_two_identical_items
|
72
|
-
2.times { @containers[1].items << @items[2] }
|
73
|
-
assert_equal(1, @containers[1].pack)
|
74
|
-
assert_equal(0, @containers[1].packings[0].remaining_volume)
|
75
|
-
end
|
76
|
-
|
77
|
-
def test_2d_one_full_packing_multiple_items
|
78
|
-
4.times { @containers[1].items << @items[0] }
|
79
|
-
7.times { @containers[1].items << @items[1] }
|
80
|
-
@containers[1].items << @items[2]
|
81
|
-
check_pack_in_between(@containers[1], 1, 2)
|
82
|
-
end
|
83
|
-
|
84
|
-
def test_2d_three_full_packings
|
85
|
-
@containers[1].packings_limit = 5
|
86
|
-
8.times { @containers[1].items << @items[0] }
|
87
|
-
14.times { @containers[1].items << @items[1] }
|
88
|
-
4.times { @containers[1].items << @items[2] }
|
89
|
-
check_pack_in_between(@containers[1], 3, 4)
|
90
|
-
end
|
91
|
-
|
92
|
-
def test_3d_one_full_packing_with_identical_items
|
93
|
-
6.times { @containers[2].items << @items[3] }
|
94
|
-
assert_equal(1, @containers[2].pack)
|
95
|
-
assert_equal(0, @containers[2].packings[0].remaining_volume)
|
96
|
-
end
|
97
|
-
|
98
|
-
def test_3d_one_full_packing_with_multiple_items
|
99
|
-
20.times { @containers[2].items << @items[0] }
|
100
|
-
10.times { @containers[2].items << @items[1] }
|
101
|
-
8.times { @containers[2].items << @items[2] }
|
102
|
-
4.times { @containers[2].items << @items[3] }
|
103
|
-
assert_equal(1, @containers[2].pack)
|
104
|
-
assert_equal(0, @containers[2].packings[0].remaining_volume)
|
105
|
-
end
|
106
|
-
|
107
|
-
def test_3d_three_full_packings
|
108
|
-
@containers[2].packings_limit = 5
|
109
|
-
35.times { @containers[2].items << @items[0] }
|
110
|
-
30.times { @containers[2].items << @items[1] }
|
111
|
-
10.times { @containers[2].items << @items[2] }
|
112
|
-
15.times { @containers[2].items << @items[3] }
|
113
|
-
check_pack_in_between(@containers[2], 3, 4)
|
114
|
-
end
|
115
|
-
|
116
|
-
def test_benchmark
|
117
|
-
puts "\n\nBenchmarking\n============"
|
118
|
-
iterations = 500
|
119
|
-
containers = []
|
120
|
-
|
121
|
-
(1..iterations).each do |i|
|
122
|
-
container_dimensions = [1 + rand(100), 1 + rand(50), 1 + rand(10)]
|
123
|
-
container_weight_limit = 1 + rand(1000)
|
124
|
-
container = BoxPacker::Container.new("c#{i}", container_dimensions, container_weight_limit)
|
125
|
-
container.packings_limit = 50
|
126
|
-
|
127
|
-
(1..(1+rand(40))).each do |j|
|
128
|
-
item_dimensions = container_dimensions.map { |c_dimension| 1 + rand(c_dimension) / (1 + rand(5)) }
|
129
|
-
item_weight = 1 + rand(container_weight_limit / (1 + rand(10)))
|
130
|
-
container.items << BoxPacker::Item.new("i#{j}", item_dimensions, item_weight)
|
131
|
-
end
|
132
|
-
containers << container
|
133
|
-
end
|
134
|
-
|
135
|
-
Benchmark.bm(15) do |bm|
|
136
|
-
bm.report('Approx packings') do
|
137
|
-
containers.each{ |container| container.pack }
|
138
|
-
end
|
139
|
-
bm.report('Volume') do
|
140
|
-
containers.each{ |container| container.pack(:sort_by_volume) }
|
141
|
-
end
|
142
|
-
bm.report('Shuffled') do
|
143
|
-
containers.each{ |container| container.pack(:sort_by_shuffle) }
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
puts "\nBenchmark data\n=============="
|
148
|
-
puts "iterations: #{iterations}"
|
149
|
-
benchmark_stats = Hash.new{0}
|
150
|
-
containers.each do |container|
|
151
|
-
benchmark_stats[:avg_container_volume] += container.volume
|
152
|
-
benchmark_stats[:avg_packings_count] += container.packings.count
|
153
|
-
benchmark_stats[:avg_items_count] += container.items.count
|
154
|
-
benchmark_stats[:avg_item_volume] += container.items.map(&:volume).reduce(:+) / container.items.count.to_f
|
155
|
-
end
|
156
|
-
benchmark_stats.each do |k, v|
|
157
|
-
benchmark_stats[k] = v / containers.count.to_f
|
158
|
-
puts "#{k}: #{benchmark_stats[k]}"
|
159
|
-
end
|
160
|
-
puts "\n"
|
161
|
-
end
|
162
|
-
|
163
|
-
private
|
164
|
-
|
165
|
-
def check_pack_in_between(container, lower_bound, upper_bound)
|
166
|
-
assert(container.pack.between?(lower_bound, upper_bound), "Did not pack into #{lower_bound} (or #{upper_bound}) packings")
|
167
|
-
assert(container.packings.map(&:remaining_volume).reduce(:+) % container.volume == 0 , "Total remaining volume doesn't add up")
|
168
|
-
end
|
169
|
-
|
170
|
-
end
|
171
|
-
|