Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
 
3
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
4
 
5
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
6
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
7
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
8
 
9
class BesselI
10
{
11
    use ArrayEnabled;
12
 
13
    /**
14
     * BESSELI.
15
     *
16
     *    Returns the modified Bessel function In(x), which is equivalent to the Bessel function evaluated
17
     *        for purely imaginary arguments
18
     *
19
     *    Excel Function:
20
     *        BESSELI(x,ord)
21
     *
22
     * NOTE: The MS Excel implementation of the BESSELI function is still not accurate.
23
     *       This code provides a more accurate calculation
24
     *
25
     * @param mixed $x A float value at which to evaluate the function.
26
     *                                If x is nonnumeric, BESSELI returns the #VALUE! error value.
27
     *                      Or can be an array of values
28
     * @param mixed $ord The integer order of the Bessel function.
29
     *                                If ord is not an integer, it is truncated.
30
     *                                If $ord is nonnumeric, BESSELI returns the #VALUE! error value.
31
     *                                If $ord < 0, BESSELI returns the #NUM! error value.
32
     *                      Or can be an array of values
33
     *
34
     * @return array|float|string Result, or a string containing an error
35
     *         If an array of numbers is passed as an argument, then the returned result will also be an array
36
     *            with the same dimensions
37
     */
38
    public static function BESSELI(mixed $x, mixed $ord): array|string|float
39
    {
40
        if (is_array($x) || is_array($ord)) {
41
            return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
42
        }
43
 
44
        try {
45
            $x = EngineeringValidations::validateFloat($x);
46
            $ord = EngineeringValidations::validateInt($ord);
47
        } catch (Exception $e) {
48
            return $e->getMessage();
49
        }
50
 
51
        if ($ord < 0) {
52
            return ExcelError::NAN();
53
        }
54
 
55
        $fResult = self::calculate($x, $ord);
56
 
57
        return (is_nan($fResult)) ? ExcelError::NAN() : $fResult;
58
    }
59
 
60
    private static function calculate(float $x, int $ord): float
61
    {
62
        return match ($ord) {
63
 
64
            1 => self::besselI1($x),
65
            default => self::besselI2($x, $ord),
66
        };
67
    }
68
 
69
    private static function besselI0(float $x): float
70
    {
71
        $ax = abs($x);
72
 
73
        if ($ax < 3.75) {
74
            $y = $x / 3.75;
75
            $y = $y * $y;
76
 
77
            return 1.0 + $y * (3.5156229 + $y * (3.0899424 + $y * (1.2067492
78
                                + $y * (0.2659732 + $y * (0.360768e-1 + $y * 0.45813e-2)))));
79
        }
80
 
81
        $y = 3.75 / $ax;
82
 
83
        return (exp($ax) / sqrt($ax)) * (0.39894228 + $y * (0.1328592e-1 + $y * (0.225319e-2 + $y * (-0.157565e-2
84
                            + $y * (0.916281e-2 + $y * (-0.2057706e-1 + $y * (0.2635537e-1
85
                                        + $y * (-0.1647633e-1 + $y * 0.392377e-2))))))));
86
    }
87
 
88
    private static function besselI1(float $x): float
89
    {
90
        $ax = abs($x);
91
 
92
        if ($ax < 3.75) {
93
            $y = $x / 3.75;
94
            $y = $y * $y;
95
            $ans = $ax * (0.5 + $y * (0.87890594 + $y * (0.51498869 + $y * (0.15084934 + $y * (0.2658733e-1
96
                                    + $y * (0.301532e-2 + $y * 0.32411e-3))))));
97
 
98
            return ($x < 0.0) ? -$ans : $ans;
99
        }
100
 
101
        $y = 3.75 / $ax;
102
        $ans = 0.2282967e-1 + $y * (-0.2895312e-1 + $y * (0.1787654e-1 - $y * 0.420059e-2));
103
        $ans = 0.39894228 + $y * (-0.3988024e-1 + $y * (-0.362018e-2 + $y * (0.163801e-2
104
                        + $y * (-0.1031555e-1 + $y * $ans))));
105
        $ans *= exp($ax) / sqrt($ax);
106
 
107
        return ($x < 0.0) ? -$ans : $ans;
108
    }
109
 
110
    private static function besselI2(float $x, int $ord): float
111
    {
112
        if ($x === 0.0) {
113
            return 0.0;
114
        }
115
 
116
        $tox = 2.0 / abs($x);
117
        $bip = 0;
118
        $ans = 0.0;
119
        $bi = 1.0;
120
 
121
        for ($j = 2 * ($ord + (int) sqrt(40.0 * $ord)); $j > 0; --$j) {
122
            $bim = $bip + $j * $tox * $bi;
123
            $bip = $bi;
124
            $bi = $bim;
125
 
126
            if (abs($bi) > 1.0e+12) {
127
                $ans *= 1.0e-12;
128
                $bi *= 1.0e-12;
129
                $bip *= 1.0e-12;
130
            }
131
 
132
            if ($j === $ord) {
133
                $ans = $bip;
134
            }
135
        }
136
 
137
        $ans *= self::besselI0($x) / $bi;
138
 
139
        return ($x < 0.0 && (($ord % 2) === 1)) ? -$ans : $ans;
140
    }
141
}