Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
Illinois
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
72
0.00% covered (danger)
0.00%
0 / 1
 root
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
72
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Math\Solver\Root
8 * @copyright Dennis Eichhorn
9 * @license   OMS License 2.0
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace phpOMS\Math\Solver\Root;
16
17/**
18 * Find the root of a function.
19 *
20 * @package phpOMS\Math\Solver\Root
21 * @license OMS License 2.0
22 * @link    https://jingga.app
23 * @since   1.0.0
24 */
25final class Illinois
26{
27    /**
28     * Epsilon for float comparison.
29     *
30     * @var float
31     * @since 1.0.0
32     */
33    public const EPSILON = 1e-6;
34
35    /**
36     * Perform bisection to find the root of a function
37     *
38     * Iteratively searches for root between two points on the x-axis
39     *
40     * @param Callable $func          Function defintion
41     * @param float    $a             Start value
42     * @param float    $b             End value
43     * @param int      $maxIterations Maximum amount of iterations
44     *
45     * @throws \Exception
46     *
47     * @return float
48     *
49     * @since 1.0.0
50     */
51    public static function root(callable $func, float $a, float $b, int $maxIterations = 100) : float
52    {
53        if ($func($a) * $func($b) >= 0) {
54            throw new \Exception("Function values at endpoints must have opposite signs.");
55        }
56
57        $c         = $b;
58        $iteration = 0;
59        $sign      = 1;
60
61        while (($y = \abs($func($c))) > self::EPSILON && $iteration < $maxIterations) {
62            $fa = $func($a);
63            $fb = $func($b);
64
65            if ($y === 0.0) {
66                return $c;
67            }
68
69            // @todo: c might be wrong, could be that if and else must be switched
70            // @see https://en.wikipedia.org/wiki/Regula_falsi#The_Illinois_algorithm
71            if ($y * $fa < 0) {
72                $c = $sign === (int) ($y >= 0)
73                    ? (0.5 * $a * $fb - $b * $fa) / (0.5 * $fb - $fa)
74                    : ($a * $fb - $b * $fa) / ($fb - $fa);
75
76                $b = $c;
77            } else {
78                $c = $sign === (int) ($y >= 0)
79                    ? ($a * $fb - 0.5 * $b * $fa) / ($fb - 0.5 * $fa)
80                    : ($a * $fb - $b * $fa) / ($fb - $fa);
81
82                $a = $c;
83            }
84
85            $sign = (int) ($y > 0);
86
87            ++$iteration;
88        }
89
90        return ($a + $b) / 2;
91    }
92}