Passed
Push — 3.x ( 05eab4...5fb647 )
by Doug
05:29 queued 03:46
created

PackedBoxList::getVolumeUtilisation()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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