Passed
Push — 3.x ( fece24...05a09a )
by Doug
02:30
created

PackedBoxList::getIterator()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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