Passed
Push — infallible_packer ( a041c8 )
by Doug
02:32
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 25
    public function insert(Item $item)
41
    {
42 25
        $this->list[] = $item;
43 25
    }
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 5
    public function remove(Item $item) {
65 5
        foreach ($this->list as $key => $itemToCheck) {
66 5
            if ($itemToCheck === $item) {
67 5
                unset($this->list[$key]);
68 5
                break;
69
            }
70
        }
71 5
    }
72
73
    /**
74
     * @internal
75
     *
76
     * @return Item
77
     */
78 21
    public function extract(): Item
79
    {
80 21
        if (!$this->isSorted) {
81 21
            usort($this->list, [$this, 'compare']);
82 21
            $this->isSorted = true;
83
        }
84
85 21
        return array_shift($this->list);
86
    }
87
88
    /**
89
     * @internal
90
     *
91
     * @return Item
92
     */
93 20
    public function top(): Item
94
    {
95 20
        if (!$this->isSorted) {
96 1
            usort($this->list, [$this, 'compare']);
97 1
            $this->isSorted = true;
98
        }
99 20
        $temp = $this->list;
100
101 20
        return reset($temp);
102
    }
103
104
    /**
105
     * @return Traversable
106
     */
107 2
    public function getIterator(): Traversable
108
    {
109 2
        if (!$this->isSorted) {
110 2
            usort($this->list, [$this, 'compare']);
111 2
            $this->isSorted = true;
112
        }
113
114 2
        return new ArrayIterator($this->list);
115
    }
116
117
    /**
118
     * Number of items in list.
119
     *
120
     * @return int
121
     */
122 23
    public function count(): int
123
    {
124 23
        return count($this->list);
125
    }
126
127
    /**
128
     * @param Item $itemA
129
     * @param Item $itemB
130
     *
131
     * @return int
132
     */
133 21
    private function compare(Item $itemA, Item $itemB): int
134
    {
135 21
        $itemAVolume = $itemA->getWidth() * $itemA->getLength() * $itemA->getDepth();
136 21
        $itemBVolume = $itemB->getWidth() * $itemB->getLength() * $itemB->getDepth();
137 21
        $volumeDecider = $itemBVolume <=> $itemAVolume;
138 21
        $weightDecider = $itemB->getWeight() - $itemA->getWeight();
139 21
        if ($volumeDecider !== 0) {
140 12
            return $volumeDecider;
141 19
        } elseif ($weightDecider !== 0) {
142 3
            return $weightDecider;
143
        } else {
144 16
            return $itemA->getDescription() <=> $itemB->getDescription();
145
        }
146
    }
147
}
148