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

NewtonRaphson   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 56
Duplicated Lines 0 %

Test Coverage

Coverage 96.15%

Importance

Changes 0
Metric Value
wmc 11
eloc 28
dl 0
loc 56
ccs 25
cts 26
cp 0.9615
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
B execute() 0 43 10
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