Passed
Push — master ( f28361...c0f46d )
by Doug
02:17
created

ItemList::compare()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 10
nc 3
nop 2
dl 0
loc 12
ccs 10
cts 10
cp 1
crap 3
rs 9.4285
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 Countable;
13
use IteratorAggregate;
14
use Traversable;
15
16
/**
17
 * List of items to be packed, ordered by volume.
18
 *
19
 * @author Doug Wright
20
 */
21
class ItemList implements Countable, IteratorAggregate
22
{
23
    /**
24
     * List containing items.
25
     *
26
     * @var Item[]
27
     */
28
    private $list = [];
29
30
    /**
31
     * Has this list already been sorted?
32
     *
33
     * @var bool
34
     */
35
    private $isSorted = false;
36
37
    /**
38
     * @param Item $item
39
     */
40 26
    public function insert(Item $item)
41
    {
42 26
        $this->list[] = $item;
43 26
    }
44
45
    /**
46
     * Do a bulk insert.
47
     *
48
     * @internal
49
     *
50
     * @param Item[] $items
51
     */
52
    public function insertFromArray(array $items): void
53
    {
54
        foreach ($items as $item) {
55
            $this->insert($item);
56
        }
57
    }
58
59
    /**
60
     * Remove item from list.
61
     *
62
     * @param Item $item
63
     */
64 6
    public function remove(Item $item)
65
    {
66 6
        foreach ($this->list as $key => $itemToCheck) {
67 6
            if ($itemToCheck === $item) {
68 6
                unset($this->list[$key]);
69 6
                break;
70
            }
71
        }
72 6
    }
73
74
    /**
75
     * @internal
76
     *
77
     * @return Item
78
     */
79 22
    public function extract(): Item
80
    {
81 22
        if (!$this->isSorted) {
82 22
            usort($this->list, [$this, 'compare']);
83 22
            $this->isSorted = true;
84
        }
85
86 22
        return array_shift($this->list);
87
    }
88
89
    /**
90
     * @internal
91
     *
92
     * @return Item
93
     */
94 21
    public function top(): Item
95
    {
96 21
        if (!$this->isSorted) {
97 2
            usort($this->list, [$this, 'compare']);
98 2
            $this->isSorted = true;
99
        }
100 21
        $temp = $this->list;
101
102 21
        return reset($temp);
103
    }
104
105
    /**
106
     * @return Traversable
107
     */
108 3
    public function getIterator(): Traversable
109
    {
110 3
        if (!$this->isSorted) {
111 3
            usort($this->list, [$this, 'compare']);
112 3
            $this->isSorted = true;
113
        }
114
115 3
        return new ArrayIterator($this->list);
116
    }
117
118
    /**
119
     * Number of items in list.
120
     *
121
     * @return int
122
     */
123 24
    public function count(): int
124
    {
125 24
        return count($this->list);
126
    }
127
128
    /**
129
     * @param Item $itemA
130
     * @param Item $itemB
131
     *
132
     * @return int
133
     */
134 22
    private function compare(Item $itemA, Item $itemB): int
135
    {
136 22
        $itemAVolume = $itemA->getWidth() * $itemA->getLength() * $itemA->getDepth();
137 22
        $itemBVolume = $itemB->getWidth() * $itemB->getLength() * $itemB->getDepth();
138 22
        $volumeDecider = $itemBVolume <=> $itemAVolume;
139 22
        $weightDecider = $itemB->getWeight() - $itemA->getWeight();
140 22
        if ($volumeDecider !== 0) {
141 13
            return $volumeDecider;
142 20
        } elseif ($weightDecider !== 0) {
143 3
            return $weightDecider;
144
        } else {
145 17
            return $itemA->getDescription() <=> $itemB->getDescription();
146
        }
147
    }
148
}
149