Passed
Push — master ( 5719ef...74c658 )
by Doug
02:48 queued 11s
created

PackedBoxList::jsonSerialize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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