Passed
Push — master ( 73ddcb...629397 )
by Doug
02:34
created

BoxList   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 56
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 6
eloc 16
c 1
b 0
f 0
dl 0
loc 56
ccs 18
cts 18
cp 1
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getIterator() 0 8 2
A insert() 0 3 1
A compare() 0 18 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
use function usort;
15
16
/**
17
 * List of boxes available to put items into, ordered by volume.
18
 *
19
 * @author Doug Wright
20
 */
21
class BoxList implements IteratorAggregate
22
{
23
    /**
24
     * List containing boxes.
25
     *
26
     * @var Box[]
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
     * @return Traversable|Box[]
39
     */
40 37
    public function getIterator(): Traversable
41
    {
42 37
        if (!$this->isSorted) {
43 37
            usort($this->list, [$this, 'compare']);
44 37
            $this->isSorted = true;
45
        }
46
47 37
        return new ArrayIterator($this->list);
48
    }
49
50 36
    public function insert(Box $item): void
51
    {
52 36
        $this->list[] = $item;
53 36
    }
54
55
    /**
56
     * @param Box $boxA
57
     * @param Box $boxB
58
     */
59 22
    public static function compare($boxA, $boxB): int
60
    {
61 22
        $boxAVolume = $boxA->getInnerWidth() * $boxA->getInnerLength() * $boxA->getInnerDepth();
62 22
        $boxBVolume = $boxB->getInnerWidth() * $boxB->getInnerLength() * $boxB->getInnerDepth();
63
64 22
        $volumeDecider = $boxAVolume <=> $boxBVolume; // try smallest box first
65
66 22
        if ($volumeDecider !== 0) {
67 18
            return $volumeDecider;
68
        }
69
70 4
        $emptyWeightDecider = $boxA->getEmptyWeight() <=> $boxB->getEmptyWeight(); // with smallest empty weight
71 4
        if ($emptyWeightDecider !== 0) {
72 4
            return $emptyWeightDecider;
73
        }
74
75
        // maximum weight capacity as fallback decider
76 1
        return ($boxA->getMaxWeight() - $boxA->getEmptyWeight()) <=> ($boxB->getMaxWeight() - $boxB->getEmptyWeight());
77
    }
78
}
79