Completed
Pull Request — master (#2)
by Pol
03:22
created

GreedyAlt::fillPartitions()   B

Complexity

Conditions 8
Paths 9

Size

Total Lines 43
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 22
nc 9
nop 3
dl 0
loc 43
rs 8.4444
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace drupol\phpartition;
6
7
use drupol\phpartition\Contract\PartitionItem;
8
use drupol\phpartition\Partition\Partition;
9
use drupol\phpartition\Partitions\Partitions;
10
11
/**
12
 * Class GreedyAlt.
13
 */
14
class GreedyAlt extends Partitioner
15
{
16
    /**
17
     * {@inheritdoc}
18
     */
19
    protected function fillPartitions(Partitions $partitions, Partition $dataset, int $chunks): void
20
    {
21
        // Edge case handling.
22
        if ($dataset->count() === $chunks) {
23
            foreach ($dataset as $key => $item) {
24
                $partitions->partition((int) $key)->exchangeArray([$item]);
25
            }
26
27
            return;
28
        }
29
30
        // Edge case handling.
31
        if (0 === $chunks) {
32
            throw new \InvalidArgumentException('Chunks must be different from 0.');
33
        }
34
35
        // Greedy needs a dataset sorted DESC.
36
        $dataset->uasort(
37
            static function (PartitionItem $left, PartitionItem $right) {
38
                return $right->getWeight() <=> $left->getWeight();
39
            }
40
        );
41
42
        $partitionsArray = [];
43
        $best = $dataset->getWeight() / $chunks;
44
45
        for ($p = 1; $p < $chunks; ++$p) {
46
            $partition = $this->getPartitionFactory()::create();
47
48
            while ($partition->getWeight() < $best && 1 < $dataset->count()) {
49
                $key = $this->findOptimalItemKey($partition, $dataset, $best);
50
                $partition->append($dataset[$key]);
51
                unset($dataset[$key]);
52
                $dataset->exchangeArray(\array_values($dataset->getArrayCopy()));
53
            }
54
55
            $partitionsArray[] = $partition;
56
        }
57
58
        $partitionsArray[] = $dataset;
59
60
        foreach ($partitionsArray as $key => $subset) {
61
            $partitions->partition($key)->exchangeArray($subset);
62
        }
63
    }
64
65
    /**
66
     * @param \drupol\phpartition\Partition\Partition $partition
67
     * @param \drupol\phpartition\Partition\Partition $dataset
68
     * @param float $best
69
     *
70
     * @return null|false|int|string
71
     */
72
    private function findOptimalItemKey(Partition $partition, Partition $dataset, $best)
73
    {
74
        $bounds = $dataset->getBounds();
0 ignored issues
show
Unused Code introduced by
The assignment to $bounds is dead and can be removed.
Loading history...
75
76
        // If the current partition is empty, then use the highest value of the
77
        // dataset.
78
        if (0 === $partition->count()) {
79
            // As the dataset is sorted DESC, return the highest value.
80
            return \key($dataset);
0 ignored issues
show
Bug introduced by
$dataset of type drupol\phpartition\Partition\Partition is incompatible with the type array expected by parameter $array of key(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

80
            return \key(/** @scrutinizer ignore-type */ $dataset);
Loading history...
81
        }
82
83
        // Find which number in the $dataset would be the best fit by checking
84
        // which one is closer to the best solution.
85
        // Best solution is the sum of the dataset values divided by the number
86
        // of chunks we want to have.
87
        $partitionWeightMinusBest = $partition->getWeight() - $best;
88
89
        $solutions = \array_map(
90
            static function (PartitionItem $item) use ($partitionWeightMinusBest) {
91
                return \abs($partitionWeightMinusBest + $item->getWeight());
92
            },
93
            \iterator_to_array($dataset)
94
        );
95
96
        \asort($solutions);
97
98
        return \key($solutions);
99
    }
100
}
101