Passed
Push — master ( 73ddcb...629397 )
by Doug
02:34
created

PackedBoxList   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
wmc 20
eloc 41
c 5
b 0
f 0
dl 0
loc 148
ccs 51
cts 51
cp 1
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A insertFromArray() 0 4 2
A getIterator() 0 8 2
A count() 0 3 1
A getWeightVariance() 0 11 2
A getVolumeUtilisation() 0 16 3
A top() 0 8 2
A insert() 0 4 1
A compare() 0 11 3
A getMeanWeight() 0 10 2
A getMeanItemWeight() 0 10 2
1
<?php
2
/**
3
 * Box packing (3D bin packing, knapsack problem).
4
 *
5
 * @author Doug Wright
6
 */
7
declare(strict_types=1);
8
9
namespace DVDoug\BoxPacker;
10
11
use ArrayIterator;
12
use function count;
13
use Countable;
14
use IteratorAggregate;
15
use function reset;
16
use function round;
17
use Traversable;
18
use function usort;
19
20
/**
21
 * List of packed boxes.
22
 *
23
 * @author Doug Wright
24
 */
25
class PackedBoxList implements IteratorAggregate, Countable
26
{
27
    /**
28
     * List containing boxes.
29
     *
30
     * @var PackedBox[]
31
     */
32
    private $list = [];
33
34
    /**
35
     * Has this list already been sorted?
36
     *
37
     * @var bool
38
     */
39
    private $isSorted = false;
40
41
    /**
42
     * @return Traversable|PackedBox[]
43
     */
44 23
    public function getIterator(): Traversable
45
    {
46 23
        if (!$this->isSorted) {
47 23
            usort($this->list, [$this, 'compare']);
48 23
            $this->isSorted = true;
49
        }
50
51 23
        return new ArrayIterator($this->list);
52
    }
53
54
    /**
55
     * Number of items in list.
56
     */
57 31
    public function count(): int
58
    {
59 31
        return count($this->list);
60
    }
61
62 35
    public function insert(PackedBox $item): void
63
    {
64 35
        $this->list[] = $item;
65 35
        $this->isSorted = false;
66 35
    }
67
68
    /**
69
     * Do a bulk insert.
70
     *
71
     * @internal
72
     *
73
     * @param PackedBox[] $boxes
74
     */
75 10
    public function insertFromArray(array $boxes): void
76
    {
77 10
        foreach ($boxes as $box) {
78 10
            $this->insert($box);
79
        }
80 10
    }
81
82
    /**
83
     * @internal
84
     */
85 6
    public function top(): PackedBox
86
    {
87 6
        if (!$this->isSorted) {
88 6
            usort($this->list, [$this, 'compare']);
89 6
            $this->isSorted = true;
90
        }
91
92 6
        return reset($this->list);
93
    }
94
95 10
    private function compare(PackedBox $boxA, PackedBox $boxB): int
96
    {
97 10
        $choice = $boxB->getItems()->count() <=> $boxA->getItems()->count();
98 10
        if ($choice === 0) {
99 8
            $choice = $boxB->getInnerVolume() <=> $boxA->getInnerVolume();
100
        }
101 10
        if ($choice === 0) {
102 8
            $choice = $boxA->getWeight() <=> $boxB->getWeight();
103
        }
104
105 10
        return $choice;
106
    }
107
108
    /**
109
     * Calculate the average (mean) weight of the boxes.
110
     */
111 11
    public function getMeanWeight(): float
112
    {
113 11
        $meanWeight = 0;
114
115
        /** @var PackedBox $box */
116 11
        foreach ($this->list as $box) {
117 11
            $meanWeight += $box->getWeight();
118
        }
119
120 11
        return $meanWeight / count($this->list);
121
    }
122
123
    /**
124
     * Calculate the average (mean) weight of the boxes.
125
     */
126 9
    public function getMeanItemWeight(): float
127
    {
128 9
        $meanWeight = 0;
129
130
        /** @var PackedBox $box */
131 9
        foreach ($this->list as $box) {
132 9
            $meanWeight += $box->getItemWeight();
133
        }
134
135 9
        return $meanWeight / count($this->list);
136
    }
137
138
    /**
139
     * Calculate the variance in weight between these boxes.
140
     */
141 10
    public function getWeightVariance(): float
142
    {
143 10
        $mean = $this->getMeanWeight();
144
145 10
        $weightVariance = 0;
146
        /** @var PackedBox $box */
147 10
        foreach ($this->list as $box) {
148 10
            $weightVariance += ($box->getWeight() - $mean) ** 2;
149
        }
150
151 10
        return round($weightVariance / count($this->list), 1);
152
    }
153
154
    /**
155
     * Get volume utilisation of the set of packed boxes.
156
     */
157 1
    public function getVolumeUtilisation(): float
158
    {
159 1
        $itemVolume = 0;
160 1
        $boxVolume = 0;
161
162
        /** @var PackedBox $box */
163 1
        foreach ($this as $box) {
164 1
            $boxVolume += $box->getInnerVolume();
165
166
            /** @var PackedItem $item */
167 1
            foreach ($box->getItems() as $item) {
168 1
                $itemVolume += ($item->getItem()->getWidth() * $item->getItem()->getLength() * $item->getItem()->getDepth());
169
            }
170
        }
171
172 1
        return round($itemVolume / $boxVolume * 100, 1);
173
    }
174
}
175