Completed
Push — issue_187 ( fb1b49...8952f9 )
by Doug
158:46 queued 155:58
created

PackedBoxList::getWeightVariance()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 11
ccs 6
cts 6
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 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 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 8
    public function getIterator(): Traversable
42
    {
43 8
        if (!$this->isSorted) {
44 8
            usort($this->list, [$this, 'compare']);
45 8
            $this->isSorted = true;
46
        }
47
48 8
        return new ArrayIterator($this->list);
49
    }
50
51
    /**
52
     * Number of items in list.
53
     */
54 8
    public function count(): int
55
    {
56 8
        return count($this->list);
57
    }
58
59 7
    public function insert(PackedBox $item): void
60
    {
61 7
        $this->list[] = $item;
62 7
    }
63
64
    /**
65
     * Do a bulk insert.
66
     *
67
     * @internal
68
     *
69
     * @param PackedBox[] $boxes
70
     */
71 2
    public function insertFromArray(array $boxes): void
72
    {
73 2
        foreach ($boxes as $box) {
74 2
            $this->insert($box);
75
        }
76 2
    }
77
78
    /**
79
     * @internal
80
     */
81
    public function top(): PackedBox
82
    {
83
        if (!$this->isSorted) {
84
            usort($this->list, [$this, 'compare']);
85
            $this->isSorted = true;
86
        }
87
88
        return reset($this->list);
89
    }
90
91 2
    private function compare(PackedBox $boxA, PackedBox $boxB): int
92
    {
93 2
        $choice = $boxB->getItems()->count() <=> $boxA->getItems()->count();
94 2
        if ($choice === 0) {
95 2
            $choice = $boxB->getInnerVolume() <=> $boxA->getInnerVolume();
96
        }
97 2
        if ($choice === 0) {
98 2
            $choice = $boxA->getWeight() <=> $boxB->getWeight();
99
        }
100
101 2
        return $choice;
102
    }
103
104
    /**
105
     * Calculate the average (mean) weight of the boxes.
106
     */
107 2
    public function getMeanWeight(): float
108
    {
109 2
        $meanWeight = 0;
110
111
        /** @var PackedBox $box */
112 2
        foreach ($this->list as $box) {
113 2
            $meanWeight += $box->getWeight();
0 ignored issues
show
Bug introduced by
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

113
            $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...
114
        }
115
116 2
        return $meanWeight / count($this->list);
117
    }
118
119
    /**
120
     * Calculate the variance in weight between these boxes.
121
     */
122 2
    public function getWeightVariance(): float
123
    {
124 2
        $mean = $this->getMeanWeight();
125
126 2
        $weightVariance = 0;
127
        /** @var PackedBox $box */
128 2
        foreach ($this->list as $box) {
129 2
            $weightVariance += ($box->getWeight() - $mean) ** 2;
130
        }
131
132 2
        return round($weightVariance / count($this->list), 1);
133
    }
134
135
    /**
136
     * Get volume utilisation of the set of packed boxes.
137
     */
138
    public function getVolumeUtilisation(): float
139
    {
140
        $itemVolume = 0;
141
        $boxVolume = 0;
142
143
        /** @var PackedBox $box */
144
        foreach ($this as $box) {
145
            $boxVolume += $box->getInnerVolume();
146
147
            /** @var PackedItem $item */
148
            foreach ($box->getItems() as $item) {
149
                $itemVolume += ($item->getItem()->getWidth() * $item->getItem()->getLength() * $item->getItem()->getDepth());
150
            }
151
        }
152
153
        return round($itemVolume / $boxVolume * 100, 1);
154
    }
155
}
156