Passed
Push — 3.x ( 0d7474...922c95 )
by Doug
15:19 queued 14:00
created

BoxList::compare()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.576

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
dl 0
loc 18
ccs 6
cts 10
cp 0.6
c 1
b 0
f 0
rs 9.9666
cc 3
nc 3
nop 2
crap 3.576
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 19
    public function getIterator(): Traversable
41
    {
42 19
        if (!$this->isSorted) {
43 19
            usort($this->list, [$this, 'compare']);
44 19
            $this->isSorted = true;
45
        }
46
47 19
        return new ArrayIterator($this->list);
48
    }
49
50 19
    public function insert(Box $item): void
51
    {
52 19
        $this->list[] = $item;
53 19
    }
54
55
    /**
56
     * @param Box $boxA
57
     * @param Box $boxB
58
     */
59 15
    public static function compare($boxA, $boxB): int
60
    {
61 15
        $boxAVolume = $boxA->getInnerWidth() * $boxA->getInnerLength() * $boxA->getInnerDepth();
62 15
        $boxBVolume = $boxB->getInnerWidth() * $boxB->getInnerLength() * $boxB->getInnerDepth();
63
64 15
        $volumeDecider = $boxAVolume <=> $boxBVolume; // try smallest box first
65
66 15
        if ($volumeDecider !== 0) {
67 15
            return $volumeDecider;
68
        }
69
70
        $emptyWeightDecider = $boxA->getEmptyWeight() <=> $boxB->getEmptyWeight(); // with smallest empty weight
71
        if ($emptyWeightDecider !== 0) {
72
            return $emptyWeightDecider;
73
        }
74
75
        // maximum weight capacity as fallback decider
76
        return ($boxA->getMaxWeight() - $boxA->getEmptyWeight()) <=> ($boxB->getMaxWeight() - $boxB->getEmptyWeight());
77
    }
78
}
79