box_packer 0.0.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
-
|