From 2aa4a28863cbb9b22ee87e637f7d21ef976c00e5 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Fri, 12 Feb 2021 17:55:53 +0100 Subject: [PATCH] Move Bessel function calculations from the Engineering class to a dedicated Engineering\Bessel classes (#1846) * Move Bessel function calculations from the Engineering class to a dedicated Engineering\Bessel class Retain the original methods in the Engineering class as stubs for BC, but deprecate them. They will be removed for PHPSpreadsheet v2 * Some refactoring of the Bessel calculation logic * Fix callable for ConvertUOM() --- .../Calculation/Calculation.php | 10 +- .../Calculation/Engineering.php | 222 ++---------------- .../Calculation/Engineering/BesselI.php | 72 ++++++ .../Calculation/Engineering/BesselJ.php | 71 ++++++ .../Calculation/Engineering/BesselK.php | 104 ++++++++ .../Calculation/Engineering/BesselY.php | 104 ++++++++ 6 files changed, 373 insertions(+), 210 deletions(-) create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/BesselI.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/BesselK.php create mode 100644 src/PhpSpreadsheet/Calculation/Engineering/BesselY.php diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 72aa9a51..00af49a6 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -363,22 +363,22 @@ class Calculation ], 'BESSELI' => [ 'category' => Category::CATEGORY_ENGINEERING, - 'functionCall' => [Engineering::class, 'BESSELI'], + 'functionCall' => [Engineering\BesselI::class, 'BESSELI'], 'argumentCount' => '2', ], 'BESSELJ' => [ 'category' => Category::CATEGORY_ENGINEERING, - 'functionCall' => [Engineering::class, 'BESSELJ'], + 'functionCall' => [Engineering\BesselJ::class, 'BESSELJ'], 'argumentCount' => '2', ], 'BESSELK' => [ 'category' => Category::CATEGORY_ENGINEERING, - 'functionCall' => [Engineering::class, 'BESSELK'], + 'functionCall' => [Engineering\BesselK::class, 'BESSELK'], 'argumentCount' => '2', ], 'BESSELY' => [ 'category' => Category::CATEGORY_ENGINEERING, - 'functionCall' => [Engineering::class, 'BESSELY'], + 'functionCall' => [Engineering\BesselY::class, 'BESSELY'], 'argumentCount' => '2', ], 'BETADIST' => [ @@ -594,7 +594,7 @@ class Calculation ], 'CONVERT' => [ 'category' => Category::CATEGORY_ENGINEERING, - 'functionCall' => [Engineering::class, 'CONVERTUOM'], + 'functionCall' => [Engineering\ConvertUOM::class, 'CONVERT'], 'argumentCount' => '3', ], 'CORREL' => [ diff --git a/src/PhpSpreadsheet/Calculation/Engineering.php b/src/PhpSpreadsheet/Calculation/Engineering.php index 319a2ed6..1db4cdb1 100644 --- a/src/PhpSpreadsheet/Calculation/Engineering.php +++ b/src/PhpSpreadsheet/Calculation/Engineering.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation; use Complex\Complex; use Complex\Exception as ComplexException; +use PhpOffice\PhpSpreadsheet\Calculation\Engineering\Bessel; use PhpOffice\PhpSpreadsheet\Calculation\Engineering\ConvertUOM; class Engineering @@ -73,6 +74,8 @@ class Engineering * Excel Function: * BESSELI(x,ord) * + * @Deprecated 2.0.0 Use the BESSELI() method in the Engineering\BesselI class instead + * * @param float $x The value at which to evaluate the function. * If x is nonnumeric, BESSELI returns the #VALUE! error value. * @param int $ord The order of the Bessel function. @@ -84,38 +87,7 @@ class Engineering */ public static function BESSELI($x, $ord) { - $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x); - $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord); - - if ((is_numeric($x)) && (is_numeric($ord))) { - $ord = floor($ord); - if ($ord < 0) { - return Functions::NAN(); - } - - if (abs($x) <= 30) { - $fResult = $fTerm = ($x / 2) ** $ord / MathTrig::FACT($ord); - $ordK = 1; - $fSqrX = ($x * $x) / 4; - do { - $fTerm *= $fSqrX; - $fTerm /= ($ordK * ($ordK + $ord)); - $fResult += $fTerm; - } while ((abs($fTerm) > 1e-12) && (++$ordK < 100)); - } else { - $f_2_PI = 2 * M_PI; - - $fXAbs = abs($x); - $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs); - if (($ord & 1) && ($x < 0)) { - $fResult = -$fResult; - } - } - - return (is_nan($fResult)) ? Functions::NAN() : $fResult; - } - - return Functions::VALUE(); + return Engineering\BesselI::BESSELI($x, $ord); } /** @@ -126,6 +98,8 @@ class Engineering * Excel Function: * BESSELJ(x,ord) * + * @Deprecated 2.0.0 Use the BESSELJ() method in the Engineering\BesselJ class instead + * * @param float $x The value at which to evaluate the function. * If x is nonnumeric, BESSELJ returns the #VALUE! error value. * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated. @@ -136,76 +110,7 @@ class Engineering */ public static function BESSELJ($x, $ord) { - $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x); - $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord); - - if ((is_numeric($x)) && (is_numeric($ord))) { - $ord = floor($ord); - if ($ord < 0) { - return Functions::NAN(); - } - - $fResult = 0; - if (abs($x) <= 30) { - $fResult = $fTerm = ($x / 2) ** $ord / MathTrig::FACT($ord); - $ordK = 1; - $fSqrX = ($x * $x) / -4; - do { - $fTerm *= $fSqrX; - $fTerm /= ($ordK * ($ordK + $ord)); - $fResult += $fTerm; - } while ((abs($fTerm) > 1e-12) && (++$ordK < 100)); - } else { - $f_PI_DIV_2 = M_PI / 2; - $f_PI_DIV_4 = M_PI / 4; - - $fXAbs = abs($x); - $fResult = sqrt(Functions::M_2DIVPI / $fXAbs) * cos($fXAbs - $ord * $f_PI_DIV_2 - $f_PI_DIV_4); - if (($ord & 1) && ($x < 0)) { - $fResult = -$fResult; - } - } - - return (is_nan($fResult)) ? Functions::NAN() : $fResult; - } - - return Functions::VALUE(); - } - - private static function besselK0($fNum) - { - if ($fNum <= 2) { - $fNum2 = $fNum * 0.5; - $y = ($fNum2 * $fNum2); - $fRet = -log($fNum2) * self::BESSELI($fNum, 0) + - (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y * - (0.10750e-3 + $y * 0.74e-5)))))); - } else { - $y = 2 / $fNum; - $fRet = exp(-$fNum) / sqrt($fNum) * - (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y * - (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3)))))); - } - - return $fRet; - } - - private static function besselK1($fNum) - { - if ($fNum <= 2) { - $fNum2 = $fNum * 0.5; - $y = ($fNum2 * $fNum2); - $fRet = log($fNum2) * self::BESSELI($fNum, 1) + - (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y * - (-0.110404e-2 + $y * (-0.4686e-4))))))) / $fNum; - } else { - $y = 2 / $fNum; - $fRet = exp(-$fNum) / sqrt($fNum) * - (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y * - (0.325614e-2 + $y * (-0.68245e-3))))))); - } - - return $fRet; + return Engineering\BesselJ::BESSELJ($x, $ord); } /** @@ -217,6 +122,8 @@ class Engineering * Excel Function: * BESSELK(x,ord) * + * @Deprecated 2.0.0 Use the BESSELK() method in the Engineering\BesselK class instead + * * @param float $x The value at which to evaluate the function. * If x is nonnumeric, BESSELK returns the #VALUE! error value. * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated. @@ -227,73 +134,7 @@ class Engineering */ public static function BESSELK($x, $ord) { - $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x); - $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord); - - if ((is_numeric($x)) && (is_numeric($ord))) { - if (($ord < 0) || ($x == 0.0)) { - return Functions::NAN(); - } - - switch (floor($ord)) { - case 0: - $fBk = self::besselK0($x); - - break; - case 1: - $fBk = self::besselK1($x); - - break; - default: - $fTox = 2 / $x; - $fBkm = self::besselK0($x); - $fBk = self::besselK1($x); - for ($n = 1; $n < $ord; ++$n) { - $fBkp = $fBkm + $n * $fTox * $fBk; - $fBkm = $fBk; - $fBk = $fBkp; - } - } - - return (is_nan($fBk)) ? Functions::NAN() : $fBk; - } - - return Functions::VALUE(); - } - - private static function besselY0($fNum) - { - if ($fNum < 8.0) { - $y = ($fNum * $fNum); - $f1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y * (-86327.92757 + $y * 228.4622733)))); - $f2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y * (47447.26470 + $y * (226.1030244 + $y)))); - $fRet = $f1 / $f2 + 0.636619772 * self::BESSELJ($fNum, 0) * log($fNum); - } else { - $z = 8.0 / $fNum; - $y = ($z * $z); - $xx = $fNum - 0.785398164; - $f1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6))); - $f2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y * (-0.934945152e-7)))); - $fRet = sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2); - } - - return $fRet; - } - - private static function besselY1($fNum) - { - if ($fNum < 8.0) { - $y = ($fNum * $fNum); - $f1 = $fNum * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y * (0.7349264551e9 + $y * - (-0.4237922726e7 + $y * 0.8511937935e4))))); - $f2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y * - (0.1020426050e6 + $y * (0.3549632885e3 + $y))))); - $fRet = $f1 / $f2 + 0.636619772 * (self::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum); - } else { - $fRet = sqrt(0.636619772 / $fNum) * sin($fNum - 2.356194491); - } - - return $fRet; + return Engineering\BesselK::BESSELK($x, $ord); } /** @@ -304,48 +145,19 @@ class Engineering * Excel Function: * BESSELY(x,ord) * + * @Deprecated 2.0.0 Use the BESSELY() method in the Engineering\BesselY class instead + * * @param float $x The value at which to evaluate the function. - * If x is nonnumeric, BESSELK returns the #VALUE! error value. + * If x is nonnumeric, BESSELY returns the #VALUE! error value. * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated. - * If $ord is nonnumeric, BESSELK returns the #VALUE! error value. - * If $ord < 0, BESSELK returns the #NUM! error value. + * If $ord is nonnumeric, BESSELY returns the #VALUE! error value. + * If $ord < 0, BESSELY returns the #NUM! error value. * * @return float|string Result, or a string containing an error */ public static function BESSELY($x, $ord) { - $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x); - $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord); - - if ((is_numeric($x)) && (is_numeric($ord))) { - if (($ord < 0) || ($x == 0.0)) { - return Functions::NAN(); - } - - switch (floor($ord)) { - case 0: - $fBy = self::besselY0($x); - - break; - case 1: - $fBy = self::besselY1($x); - - break; - default: - $fTox = 2 / $x; - $fBym = self::besselY0($x); - $fBy = self::besselY1($x); - for ($n = 1; $n < $ord; ++$n) { - $fByp = $n * $fTox * $fBy - $fBym; - $fBym = $fBy; - $fBy = $fByp; - } - } - - return (is_nan($fBy)) ? Functions::NAN() : $fBy; - } - - return Functions::VALUE(); + return Engineering\BesselY::BESSELY($x, $ord); } /** @@ -1882,7 +1694,7 @@ class Engineering * getConversionGroups * Returns a list of the different conversion groups for UOM conversions. * - * @Deprecated Use the getConversionCategories() method in the ConvertUOM class instead + * @Deprecated 2.0.0 Use the getConversionCategories() method in the Engineering\ConvertUOM class instead * * @return array */ diff --git a/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php b/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php new file mode 100644 index 00000000..eda5c12b --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Engineering/BesselI.php @@ -0,0 +1,72 @@ + 1e-12) && (++$ordK < 100)); + + return $fResult; + } + + $f_2_PI = 2 * M_PI; + + $fXAbs = abs($x); + $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs); + if (($ord & 1) && ($x < 0)) { + $fResult = -$fResult; + } + + return $fResult; + } +} diff --git a/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php b/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php new file mode 100644 index 00000000..62dd343a --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php @@ -0,0 +1,71 @@ + 1e-12) && (++$ordK < 100)); + + return $fResult; + } + + $f_PI_DIV_2 = M_PI / 2; + $f_PI_DIV_4 = M_PI / 4; + + $fXAbs = abs($x); + $fResult = sqrt(Functions::M_2DIVPI / $fXAbs) * cos($fXAbs - $ord * $f_PI_DIV_2 - $f_PI_DIV_4); + if (($ord & 1) && ($x < 0)) { + $fResult = -$fResult; + } + + return $fResult; + } +} diff --git a/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php b/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php new file mode 100644 index 00000000..f64f38b0 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Engineering/BesselK.php @@ -0,0 +1,104 @@ +