Passed
Push — master ( e0fc2c...aef815 )
by Kevin
02:28
created

LargestRemainder::normalize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Aeq\LargestRemainder\Math;
4
5
use Aeq\LargestRemainder\Exception\AlreadyNormalizedException;
6
use Aeq\LargestRemainder\Exception\NotANumberException;
7
use Aeq\LargestRemainder\Exception\NotYetNormalizedException;
8
use Aeq\LargestRemainder\Math\Number as LargestRemainderNumber;
9
10
class LargestRemainder
11
{
12
    /**
13
     * @var array
14
     */
15
    private $numbers = [];
16
17
    /**
18
     * @var int
19
     */
20
    private $precision = 0;
21
22
    /**
23
     * @param array $numbers
24
     */
25 11
    public function __construct(array $numbers)
26
    {
27 11
        $this->numbers = $numbers;
28 11
    }
29
30
    /**
31
     * @param int $precision
32
     */
33 6
    public function setPrecision(int $precision): void
34
    {
35 6
        $this->precision = $precision;
36 6
    }
37
38
    /**
39
     * @return array
40
     * @throws AlreadyNormalizedException
41
     * @throws NotANumberException
42
     * @throws NotYetNormalizedException
43
     */
44 10
    public function round(): array
45
    {
46 10
        return $this->uround(
47
            function ($item) {
48 10
                return $item;
49 10
            },
50
            function (&$item, $value) {
51 9
                $item = $value;
52 10
            }
53
        );
54
    }
55
56
    /**
57
     * @param callable $get
58
     * @param callable $set
59
     * @return array
60
     * @throws NotANumberException
61
     * @throws AlreadyNormalizedException
62
     * @throws NotYetNormalizedException
63
     */
64 11
    public function uround(callable $get, callable $set): array
65
    {
66 11
        $originalOrder = array_keys($this->numbers);
67
68
        $sum = array_sum(array_map(function ($item) use ($get) {
69 11
            return $this->getNumber($get, $item)->floor()->value();
70 11
        }, $this->numbers));
71
72 10
        $diff = 100 - $sum;
73
74
        uasort($this->numbers, function ($a, $b) use ($get) {
75 10
            $aNumber = $this->getNumber($get, $a);
76 10
            $bNumber = $this->getNumber($get, $b);
77 10
            return $aNumber->value() - $aNumber->floor()->value() < $bNumber->value() - $bNumber->floor()->value();
78 10
        });
79
80 10
        $index = 0;
81 10
        foreach ($this->numbers as &$item) {
82 10
            $number = $this->getNumber($get, $item);
83 10
            if ($index < $diff) {
84 5
                $this->setNumber($set, $item, $number->add(1)->floor());
85 5
                $index++;
86 5
                continue;
87
            }
88 9
            if ($diff < 0 && $index < $diff * (-1)) {
89 4
                $this->setNumber($set, $item, $number->sub(1)->floor());
90 4
                $index++;
91 4
                continue;
92
            }
93 8
            $this->setNumber($set, $item, $number->floor());
94 8
            $index++;
95 8
            continue;
96
        }
97
98 10
        return array_replace(array_flip($originalOrder), $this->numbers);
99
    }
100
101
    /**
102
     * @param callable $get
103
     * @param $val
104
     * @return LargestRemainderNumber
105
     * @throws NotANumberException
106
     * @throws AlreadyNormalizedException
107
     */
108 11
    private function getNumber(callable $get, $val): LargestRemainderNumber
109
    {
110 11
        $resolved = call_user_func_array($get, [$val]);
111 11
        if (false === is_numeric($resolved)) {
112 1
            throw new NotANumberException($val, 1538927918);
113
        }
114 11
        return (new Number($resolved, $this->precision))->normalize();
115
    }
116
117
    /**
118
     * @param callable $set
119
     * @param $item
120
     * @param LargestRemainderNumber $number
121
     * @throws NotYetNormalizedException
122
     */
123 10
    private function setNumber(callable $set, &$item, LargestRemainderNumber $number): void
124
    {
125 10
        call_user_func_array($set, [&$item, $number->denormalize()->value()]);
126 10
    }
127
}
128