Completed
Push — behat ( bbfb72...a6a57d )
by Doug
08:56
created

PackedBoxList::compare()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12
Code Lines 7

Duplication

Lines 11
Ratio 91.67 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 11
loc 12
c 0
b 0
f 0
ccs 7
cts 7
cp 1
rs 9.4285
cc 3
eloc 7
nc 4
nop 2
crap 3
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 IteratorAggregate;
13
use Traversable;
14
15
/**
16
 * List of packed boxes.
17
 *
18
 * @author Doug Wright
19
 */
20
class PackedBoxList implements IteratorAggregate
21
{
22
    /**
23
     * List containing boxes.
24
     *
25
     * @var Box[]
26
     */
27
    private $list = [];
28
29
    /**
30
     * Has this list already been sorted?
31
     *
32
     * @var bool
33
     */
34
    private $isSorted = false;
35
36
    /**
37
     * @return Traversable
38
     */
39 6 View Code Duplication
    public function getIterator(): Traversable
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
40
    {
41 6
        if (!$this->isSorted) {
42 6
            usort($this->list, [$this, 'compare']);
43 6
            $this->isSorted = true;
44
        }
45
46 6
        return new ArrayIterator($this->list);
47
    }
48
49
    /**
50
     * Number of items in list.
51
     *
52
     * @return int
53
     */
54 7
    public function count(): int
55
    {
56 7
        return count($this->list);
57
    }
58
59
    /**
60
     * @param PackedBox $item
61
     */
62 11
    public function insert(PackedBox $item)
63
    {
64 11
        $this->list[] = $item;
65 11
    }
66
67
    /**
68
     * Do a bulk insert.
69
     *
70
     * @internal
71
     *
72
     * @param PackedBox[] $boxes
73
     */
74 2
    public function insertFromArray(array $boxes): void
75
    {
76 2
        foreach ($boxes as $box) {
77 2
            $this->insert($box);
78
        }
79 2
    }
80
81
    /**
82
     * @internal
83
     *
84
     * @return PackedBox
85
     */
86 1 View Code Duplication
    public function top(): PackedBox
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
87
    {
88 1
        if (!$this->isSorted) {
89 1
            usort($this->list, [$this, 'compare']);
90 1
            $this->isSorted = true;
91
        }
92
93 1
        return reset($this->list);
94
    }
95
96
    /**
97
     * @param PackedBox $boxA
98
     * @param PackedBox $boxB
99
     *
100
     * @return int
101
     */
102 2 View Code Duplication
    private function compare(PackedBox $boxA, PackedBox $boxB): int
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
103
    {
104 2
        $choice = $boxB->getItems()->count() <=> $boxA->getItems()->count();
105 2
        if ($choice === 0) {
106 2
            $choice = $boxB->getInnerVolume() <=> $boxA->getInnerVolume();
107
        }
108 2
        if ($choice === 0) {
109 2
            $choice = $boxA->getWeight() <=> $boxB->getWeight();
110
        }
111
112 2
        return $choice;
113
    }
114
115
    /**
116
     * Calculate the average (mean) weight of the boxes.
117
     *
118
     * @return float
119
     */
120 3
    public function getMeanWeight(): float
121
    {
122 3
        $meanWeight = 0;
123
124
        /** @var PackedBox $box */
125 3
        foreach ($this->list as $box) {
126 3
            $meanWeight += $box->getWeight();
0 ignored issues
show
Bug introduced by
The method getWeight() does not seem to exist on object<DVDoug\BoxPacker\Box>.

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