Passed
Push — master ( 68fd71...055281 )
by
unknown
16:29 queued 06:03
created

NewtonRaphson::execute()   B

Complexity

Conditions 11
Paths 27

Size

Total Lines 46
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 11.055

Importance

Changes 0
Metric Value
eloc 26
c 0
b 0
f 0
dl 0
loc 46
ccs 24
cts 26
cp 0.9231
rs 7.3166
cc 11
nc 27
nop 1
crap 11.055

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
6
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
7
8
class NewtonRaphson
9
{
10
    private const MAX_ITERATIONS = 256;
11
12
    /** @var callable(float): mixed */
13
    protected $callback;
14
15
    /** @param callable(float): mixed $callback */
16 16
    public function __construct(callable $callback)
17
    {
18 16
        $this->callback = $callback;
19
    }
20
21 16
    public function execute(float $probability): string|int|float
22
    {
23 16
        $xLo = 100;
24 16
        $xHi = 0;
25
26 16
        $dx = 1;
27 16
        $x = $xNew = 1;
28 16
        $i = 0;
29
30 16
        while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
31
            // Apply Newton-Raphson step
32 16
            $result = call_user_func($this->callback, $x);
33 16
            if (!is_float($result)) {
34
                return ExcelError::VALUE();
35
            }
36 16
            $error = $result - $probability;
37
38 16
            if ($error == 0.0) {
39 6
                $dx = 0;
40 16
            } elseif ($error < 0.0) {
41 16
                $xLo = $x;
42
            } else {
43 16
                $xHi = $x;
44
            }
45
46
            // Avoid division by zero
47 16
            if ($result != 0.0) {
48 16
                $dx = $error / $result;
49 16
                $xNew = $x - $dx;
50
            }
51
52
            // If the NR fails to converge (which for example may be the
53
            // case if the initial guess is too rough) we apply a bisection
54
            // step to determine a more narrow interval around the root.
55 16
            if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
56 16
                $xNew = ($xLo + $xHi) / 2;
57 16
                $dx = $xNew - $x;
58
            }
59 16
            $x = $xNew;
60
        }
61
62 16
        if ($i == self::MAX_ITERATIONS) {
63
            return ExcelError::NA();
64
        }
65
66 16
        return $x;
67
    }
68
}
69