Test Failed
Push — master ( 1655fb...18f60e )
by Doug
04:26 queued 02:43
created

src/PackedBoxList.php (1 issue)

Labels
Severity
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 Countable;
13
use IteratorAggregate;
14
use Traversable;
15
use function count;
16
use function reset;
17
use function round;
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 Box[]
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
43
     */
44 8
    public function getIterator(): Traversable
45
    {
46 8
        if (!$this->isSorted) {
47 8
            usort($this->list, [$this, 'compare']);
48 8
            $this->isSorted = true;
49
        }
50
51 8
        return new ArrayIterator($this->list);
52
    }
53
54
    /**
55
     * Number of items in list.
56
     *
57
     * @return int
58
     */
59 9
    public function count(): int
60
    {
61 9
        return count($this->list);
62
    }
63
64
    /**
65
     * @param PackedBox $item
66
     */
67 12
    public function insert(PackedBox $item)
68
    {
69 12
        $this->list[] = $item;
70 12
    }
71
72
    /**
73
     * Do a bulk insert.
74
     *
75
     * @internal
76
     *
77
     * @param PackedBox[] $boxes
78
     */
79 2
    public function insertFromArray(array $boxes): void
80
    {
81 2
        foreach ($boxes as $box) {
82 2
            $this->insert($box);
83
        }
84 2
    }
85
86
    /**
87
     * @internal
88
     *
89
     * @return PackedBox
90
     */
91 1
    public function top(): PackedBox
92
    {
93 1
        if (!$this->isSorted) {
94 1
            usort($this->list, [$this, 'compare']);
95 1
            $this->isSorted = true;
96
        }
97
98 1
        return reset($this->list);
99
    }
100
101
    /**
102
     * @param PackedBox $boxA
103
     * @param PackedBox $boxB
104
     *
105
     * @return int
106
     */
107 2
    private function compare(PackedBox $boxA, PackedBox $boxB): int
108
    {
109 2
        $choice = $boxB->getItems()->count() <=> $boxA->getItems()->count();
110 2
        if ($choice === 0) {
111 2
            $choice = $boxB->getInnerVolume() <=> $boxA->getInnerVolume();
112
        }
113 2
        if ($choice === 0) {
114 2
            $choice = $boxA->getWeight() <=> $boxB->getWeight();
115
        }
116
117 2
        return $choice;
118
    }
119
120
    /**
121
     * Calculate the average (mean) weight of the boxes.
122
     *
123
     * @return float
124
     */
125 3
    public function getMeanWeight(): float
126
    {
127 3
        $meanWeight = 0;
128
129
        /** @var PackedBox $box */
130 3
        foreach ($this->list as $box) {
131 3
            $meanWeight += $box->getWeight();
0 ignored issues
show
The method getWeight() does not exist on DVDoug\BoxPacker\Box. Did you maybe mean getMaxWeight()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

131
            $meanWeight += $box->/** @scrutinizer ignore-call */ getWeight();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
132
        }
133
134 3
        return $meanWeight / count($this->list);
135
    }
136
137
    /**
138
     * Calculate the variance in weight between these boxes.
139
     *
140
     * @return float
141
     */
142 2
    public function getWeightVariance(): float
143
    {
144 2
        $mean = $this->getMeanWeight();
145
146 2
        $weightVariance = 0;
147
        /** @var PackedBox $box */
148 2
        foreach ($this->list as $box) {
149 2
            $weightVariance += ($box->getWeight() - $mean) ** 2;
150
        }
151
152 2
        return round($weightVariance / count($this->list), 1);
153
    }
154
155
    /**
156
     * Get volume utilisation of the set of packed boxes.
157
     *
158
     * @return float
159
     */
160 1
    public function getVolumeUtilisation(): float
161
    {
162 1
        $itemVolume = 0;
163 1
        $boxVolume = 0;
164
165
        /** @var PackedBox $box */
166 1
        foreach ($this as $box) {
167 1
            $boxVolume += $box->getInnerVolume();
168
169
            /** @var PackedItem $item */
170 1
            foreach ($box->getItems() as $item) {
171 1
                $itemVolume += ($item->getItem()->getWidth() * $item->getItem()->getLength() * $item->getItem()->getDepth());
172
            }
173
        }
174
175 1
        return round($itemVolume / $boxVolume * 100, 1);
176
    }
177
}
178