Passed
Pull Request — master (#3302)
by Mark
12:42
created

NewtonRaphson::execute()   B

Complexity

Conditions 10
Paths 26

Size

Total Lines 43
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 10.0071

Importance

Changes 0
Metric Value
cc 10
eloc 24
nc 26
nop 1
dl 0
loc 43
ccs 23
cts 24
cp 0.9583
crap 10.0071
rs 7.6666
c 0
b 0
f 0

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 */
13
    protected $callback;
14
15 16
    public function __construct(callable $callback)
16
    {
17 16
        $this->callback = $callback;
18
    }
19
20
    /** @return float|string */
21 16
    public function execute(float $probability)
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
            $error = $result - $probability;
34
35 16
            if ($error == 0.0) {
36 5
                $dx = 0;
37 16
            } elseif ($error < 0.0) {
38 16
                $xLo = $x;
39
            } else {
40 16
                $xHi = $x;
41
            }
42
43
            // Avoid division by zero
44 16
            if ($result != 0.0) {
45 16
                $dx = $error / $result;
46 16
                $xNew = $x - $dx;
47
            }
48
49
            // If the NR fails to converge (which for example may be the
50
            // case if the initial guess is too rough) we apply a bisection
51
            // step to determine a more narrow interval around the root.
52 16
            if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
53 16
                $xNew = ($xLo + $xHi) / 2;
54 16
                $dx = $xNew - $x;
55
            }
56 16
            $x = $xNew;
57
        }
58
59 16
        if ($i == self::MAX_ITERATIONS) {
60
            return ExcelError::NA();
61
        }
62
63 16
        return $x;
64
    }
65
}
66