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