init
This commit is contained in:
commit
72a26edcff
22092 changed files with 2101903 additions and 0 deletions
141
lib/PhpSpreadsheet/Calculation/Engineering/BesselI.php
Normal file
141
lib/PhpSpreadsheet/Calculation/Engineering/BesselI.php
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class BesselI
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
/**
|
||||
* BESSELI.
|
||||
*
|
||||
* Returns the modified Bessel function In(x), which is equivalent to the Bessel function evaluated
|
||||
* for purely imaginary arguments
|
||||
*
|
||||
* Excel Function:
|
||||
* BESSELI(x,ord)
|
||||
*
|
||||
* NOTE: The MS Excel implementation of the BESSELI function is still not accurate.
|
||||
* This code provides a more accurate calculation
|
||||
*
|
||||
* @param mixed $x A float value at which to evaluate the function.
|
||||
* If x is nonnumeric, BESSELI returns the #VALUE! error value.
|
||||
* Or can be an array of values
|
||||
* @param mixed $ord The integer order of the Bessel function.
|
||||
* If ord is not an integer, it is truncated.
|
||||
* If $ord is nonnumeric, BESSELI returns the #VALUE! error value.
|
||||
* If $ord < 0, BESSELI returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BESSELI(mixed $x, mixed $ord): array|string|float
|
||||
{
|
||||
if (is_array($x) || is_array($ord)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
|
||||
}
|
||||
|
||||
try {
|
||||
$x = EngineeringValidations::validateFloat($x);
|
||||
$ord = EngineeringValidations::validateInt($ord);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if ($ord < 0) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
$fResult = self::calculate($x, $ord);
|
||||
|
||||
return (is_nan($fResult)) ? ExcelError::NAN() : $fResult;
|
||||
}
|
||||
|
||||
private static function calculate(float $x, int $ord): float
|
||||
{
|
||||
return match ($ord) {
|
||||
0 => self::besselI0($x),
|
||||
1 => self::besselI1($x),
|
||||
default => self::besselI2($x, $ord),
|
||||
};
|
||||
}
|
||||
|
||||
private static function besselI0(float $x): float
|
||||
{
|
||||
$ax = abs($x);
|
||||
|
||||
if ($ax < 3.75) {
|
||||
$y = $x / 3.75;
|
||||
$y = $y * $y;
|
||||
|
||||
return 1.0 + $y * (3.5156229 + $y * (3.0899424 + $y * (1.2067492
|
||||
+ $y * (0.2659732 + $y * (0.360768e-1 + $y * 0.45813e-2)))));
|
||||
}
|
||||
|
||||
$y = 3.75 / $ax;
|
||||
|
||||
return (exp($ax) / sqrt($ax)) * (0.39894228 + $y * (0.1328592e-1 + $y * (0.225319e-2 + $y * (-0.157565e-2
|
||||
+ $y * (0.916281e-2 + $y * (-0.2057706e-1 + $y * (0.2635537e-1
|
||||
+ $y * (-0.1647633e-1 + $y * 0.392377e-2))))))));
|
||||
}
|
||||
|
||||
private static function besselI1(float $x): float
|
||||
{
|
||||
$ax = abs($x);
|
||||
|
||||
if ($ax < 3.75) {
|
||||
$y = $x / 3.75;
|
||||
$y = $y * $y;
|
||||
$ans = $ax * (0.5 + $y * (0.87890594 + $y * (0.51498869 + $y * (0.15084934 + $y * (0.2658733e-1
|
||||
+ $y * (0.301532e-2 + $y * 0.32411e-3))))));
|
||||
|
||||
return ($x < 0.0) ? -$ans : $ans;
|
||||
}
|
||||
|
||||
$y = 3.75 / $ax;
|
||||
$ans = 0.2282967e-1 + $y * (-0.2895312e-1 + $y * (0.1787654e-1 - $y * 0.420059e-2));
|
||||
$ans = 0.39894228 + $y * (-0.3988024e-1 + $y * (-0.362018e-2 + $y * (0.163801e-2
|
||||
+ $y * (-0.1031555e-1 + $y * $ans))));
|
||||
$ans *= exp($ax) / sqrt($ax);
|
||||
|
||||
return ($x < 0.0) ? -$ans : $ans;
|
||||
}
|
||||
|
||||
private static function besselI2(float $x, int $ord): float
|
||||
{
|
||||
if ($x === 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$tox = 2.0 / abs($x);
|
||||
$bip = 0;
|
||||
$ans = 0.0;
|
||||
$bi = 1.0;
|
||||
|
||||
for ($j = 2 * ($ord + (int) sqrt(40.0 * $ord)); $j > 0; --$j) {
|
||||
$bim = $bip + $j * $tox * $bi;
|
||||
$bip = $bi;
|
||||
$bi = $bim;
|
||||
|
||||
if (abs($bi) > 1.0e+12) {
|
||||
$ans *= 1.0e-12;
|
||||
$bi *= 1.0e-12;
|
||||
$bip *= 1.0e-12;
|
||||
}
|
||||
|
||||
if ($j === $ord) {
|
||||
$ans = $bip;
|
||||
}
|
||||
}
|
||||
|
||||
$ans *= self::besselI0($x) / $bi;
|
||||
|
||||
return ($x < 0.0 && (($ord % 2) === 1)) ? -$ans : $ans;
|
||||
}
|
||||
}
|
||||
176
lib/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
Normal file
176
lib/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class BesselJ
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
/**
|
||||
* BESSELJ.
|
||||
*
|
||||
* Returns the Bessel function
|
||||
*
|
||||
* Excel Function:
|
||||
* BESSELJ(x,ord)
|
||||
*
|
||||
* NOTE: The MS Excel implementation of the BESSELJ function is still not accurate, particularly for higher order
|
||||
* values with x < -8 and x > 8. This code provides a more accurate calculation
|
||||
*
|
||||
* @param mixed $x A float value at which to evaluate the function.
|
||||
* If x is nonnumeric, BESSELJ returns the #VALUE! error value.
|
||||
* Or can be an array of values
|
||||
* @param mixed $ord The integer order of the Bessel function.
|
||||
* If ord is not an integer, it is truncated.
|
||||
* If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
|
||||
* If $ord < 0, BESSELJ returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BESSELJ(mixed $x, mixed $ord): array|string|float
|
||||
{
|
||||
if (is_array($x) || is_array($ord)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
|
||||
}
|
||||
|
||||
try {
|
||||
$x = EngineeringValidations::validateFloat($x);
|
||||
$ord = EngineeringValidations::validateInt($ord);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if ($ord < 0) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
$fResult = self::calculate($x, $ord);
|
||||
|
||||
return (is_nan($fResult)) ? ExcelError::NAN() : $fResult;
|
||||
}
|
||||
|
||||
private static function calculate(float $x, int $ord): float
|
||||
{
|
||||
return match ($ord) {
|
||||
0 => self::besselJ0($x),
|
||||
1 => self::besselJ1($x),
|
||||
default => self::besselJ2($x, $ord),
|
||||
};
|
||||
}
|
||||
|
||||
private static function besselJ0(float $x): float
|
||||
{
|
||||
$ax = abs($x);
|
||||
|
||||
if ($ax < 8.0) {
|
||||
$y = $x * $x;
|
||||
$ans1 = 57568490574.0 + $y * (-13362590354.0 + $y * (651619640.7 + $y * (-11214424.18 + $y
|
||||
* (77392.33017 + $y * (-184.9052456)))));
|
||||
$ans2 = 57568490411.0 + $y * (1029532985.0 + $y * (9494680.718 + $y * (59272.64853 + $y
|
||||
* (267.8532712 + $y * 1.0))));
|
||||
|
||||
return $ans1 / $ans2;
|
||||
}
|
||||
|
||||
$z = 8.0 / $ax;
|
||||
$y = $z * $z;
|
||||
$xx = $ax - 0.785398164;
|
||||
$ans1 = 1.0 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
|
||||
$ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y
|
||||
* (0.7621095161e-6 - $y * 0.934935152e-7)));
|
||||
|
||||
return sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
|
||||
}
|
||||
|
||||
private static function besselJ1(float $x): float
|
||||
{
|
||||
$ax = abs($x);
|
||||
|
||||
if ($ax < 8.0) {
|
||||
$y = $x * $x;
|
||||
$ans1 = $x * (72362614232.0 + $y * (-7895059235.0 + $y * (242396853.1 + $y
|
||||
* (-2972611.439 + $y * (15704.48260 + $y * (-30.16036606))))));
|
||||
$ans2 = 144725228442.0 + $y * (2300535178.0 + $y * (18583304.74 + $y * (99447.43394 + $y
|
||||
* (376.9991397 + $y * 1.0))));
|
||||
|
||||
return $ans1 / $ans2;
|
||||
}
|
||||
|
||||
$z = 8.0 / $ax;
|
||||
$y = $z * $z;
|
||||
$xx = $ax - 2.356194491;
|
||||
|
||||
$ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
|
||||
$ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y
|
||||
* (-0.88228987e-6 + $y * 0.105787412e-6)));
|
||||
$ans = sqrt(0.636619772 / $ax) * (cos($xx) * $ans1 - $z * sin($xx) * $ans2);
|
||||
|
||||
return ($x < 0.0) ? -$ans : $ans;
|
||||
}
|
||||
|
||||
private static function besselJ2(float $x, int $ord): float
|
||||
{
|
||||
$ax = abs($x);
|
||||
if ($ax === 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if ($ax > $ord) {
|
||||
return self::besselj2a($ax, $ord, $x);
|
||||
}
|
||||
|
||||
return self::besselj2b($ax, $ord, $x);
|
||||
}
|
||||
|
||||
private static function besselj2a(float $ax, int $ord, float $x): float
|
||||
{
|
||||
$tox = 2.0 / $ax;
|
||||
$bjm = self::besselJ0($ax);
|
||||
$bj = self::besselJ1($ax);
|
||||
for ($j = 1; $j < $ord; ++$j) {
|
||||
$bjp = $j * $tox * $bj - $bjm;
|
||||
$bjm = $bj;
|
||||
$bj = $bjp;
|
||||
}
|
||||
$ans = $bj;
|
||||
|
||||
return ($x < 0.0 && ($ord % 2) == 1) ? -$ans : $ans;
|
||||
}
|
||||
|
||||
private static function besselj2b(float $ax, int $ord, float $x): float
|
||||
{
|
||||
$tox = 2.0 / $ax;
|
||||
$jsum = false;
|
||||
$bjp = $ans = $sum = 0.0;
|
||||
$bj = 1.0;
|
||||
for ($j = 2 * ($ord + (int) sqrt(40.0 * $ord)); $j > 0; --$j) {
|
||||
$bjm = $j * $tox * $bj - $bjp;
|
||||
$bjp = $bj;
|
||||
$bj = $bjm;
|
||||
if (abs($bj) > 1.0e+10) {
|
||||
$bj *= 1.0e-10;
|
||||
$bjp *= 1.0e-10;
|
||||
$ans *= 1.0e-10;
|
||||
$sum *= 1.0e-10;
|
||||
}
|
||||
if ($jsum === true) {
|
||||
$sum += $bj;
|
||||
}
|
||||
$jsum = $jsum === false;
|
||||
if ($j === $ord) {
|
||||
$ans = $bjp;
|
||||
}
|
||||
}
|
||||
$sum = 2.0 * $sum - $bj;
|
||||
$ans /= $sum;
|
||||
|
||||
return ($x < 0.0 && ($ord % 2) === 1) ? -$ans : $ans;
|
||||
}
|
||||
}
|
||||
130
lib/PhpSpreadsheet/Calculation/Engineering/BesselK.php
Normal file
130
lib/PhpSpreadsheet/Calculation/Engineering/BesselK.php
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class BesselK
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
/**
|
||||
* BESSELK.
|
||||
*
|
||||
* Returns the modified Bessel function Kn(x), which is equivalent to the Bessel functions evaluated
|
||||
* for purely imaginary arguments.
|
||||
*
|
||||
* Excel Function:
|
||||
* BESSELK(x,ord)
|
||||
*
|
||||
* @param mixed $x A float value at which to evaluate the function.
|
||||
* If x is nonnumeric, BESSELK returns the #VALUE! error value.
|
||||
* Or can be an array of values
|
||||
* @param mixed $ord The integer order of the Bessel function.
|
||||
* If ord is not an integer, it is truncated.
|
||||
* If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
|
||||
* If $ord < 0, BESSELKI returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BESSELK(mixed $x, mixed $ord): array|string|float
|
||||
{
|
||||
if (is_array($x) || is_array($ord)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
|
||||
}
|
||||
|
||||
try {
|
||||
$x = EngineeringValidations::validateFloat($x);
|
||||
$ord = EngineeringValidations::validateInt($ord);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if (($ord < 0) || ($x <= 0.0)) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
$fBk = self::calculate($x, $ord);
|
||||
|
||||
return (is_nan($fBk)) ? ExcelError::NAN() : $fBk;
|
||||
}
|
||||
|
||||
private static function calculate(float $x, int $ord): float
|
||||
{
|
||||
return match ($ord) {
|
||||
0 => self::besselK0($x),
|
||||
1 => self::besselK1($x),
|
||||
default => self::besselK2($x, $ord),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mollify Phpstan.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private static function callBesselI(float $x, int $ord): float
|
||||
{
|
||||
$rslt = BesselI::BESSELI($x, $ord);
|
||||
if (!is_float($rslt)) {
|
||||
throw new Exception('Unexpected array or string');
|
||||
}
|
||||
|
||||
return $rslt;
|
||||
}
|
||||
|
||||
private static function besselK0(float $x): float
|
||||
{
|
||||
if ($x <= 2) {
|
||||
$fNum2 = $x * 0.5;
|
||||
$y = ($fNum2 * $fNum2);
|
||||
|
||||
return -log($fNum2) * self::callBesselI($x, 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))))));
|
||||
}
|
||||
|
||||
$y = 2 / $x;
|
||||
|
||||
return exp(-$x) / sqrt($x)
|
||||
* (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))))));
|
||||
}
|
||||
|
||||
private static function besselK1(float $x): float
|
||||
{
|
||||
if ($x <= 2) {
|
||||
$fNum2 = $x * 0.5;
|
||||
$y = ($fNum2 * $fNum2);
|
||||
|
||||
return log($fNum2) * self::callBesselI($x, 1)
|
||||
+ (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y
|
||||
* (-0.110404e-2 + $y * (-0.4686e-4))))))) / $x;
|
||||
}
|
||||
|
||||
$y = 2 / $x;
|
||||
|
||||
return exp(-$x) / sqrt($x)
|
||||
* (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)))))));
|
||||
}
|
||||
|
||||
private static function besselK2(float $x, int $ord): float
|
||||
{
|
||||
$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 $fBk;
|
||||
}
|
||||
}
|
||||
137
lib/PhpSpreadsheet/Calculation/Engineering/BesselY.php
Normal file
137
lib/PhpSpreadsheet/Calculation/Engineering/BesselY.php
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class BesselY
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
/**
|
||||
* BESSELY.
|
||||
*
|
||||
* Returns the Bessel function, which is also called the Weber function or the Neumann function.
|
||||
*
|
||||
* Excel Function:
|
||||
* BESSELY(x,ord)
|
||||
*
|
||||
* @param mixed $x A float value at which to evaluate the function.
|
||||
* If x is nonnumeric, BESSELY returns the #VALUE! error value.
|
||||
* Or can be an array of values
|
||||
* @param mixed $ord The integer order of the Bessel function.
|
||||
* If ord is not an integer, it is truncated.
|
||||
* If $ord is nonnumeric, BESSELY returns the #VALUE! error value.
|
||||
* If $ord < 0, BESSELY returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BESSELY(mixed $x, mixed $ord): array|string|float
|
||||
{
|
||||
if (is_array($x) || is_array($ord)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $x, $ord);
|
||||
}
|
||||
|
||||
try {
|
||||
$x = EngineeringValidations::validateFloat($x);
|
||||
$ord = EngineeringValidations::validateInt($ord);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if (($ord < 0) || ($x <= 0.0)) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
$fBy = self::calculate($x, $ord);
|
||||
|
||||
return (is_nan($fBy)) ? ExcelError::NAN() : $fBy;
|
||||
}
|
||||
|
||||
private static function calculate(float $x, int $ord): float
|
||||
{
|
||||
return match ($ord) {
|
||||
0 => self::besselY0($x),
|
||||
1 => self::besselY1($x),
|
||||
default => self::besselY2($x, $ord),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mollify Phpstan.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private static function callBesselJ(float $x, int $ord): float
|
||||
{
|
||||
$rslt = BesselJ::BESSELJ($x, $ord);
|
||||
if (!is_float($rslt)) {
|
||||
throw new Exception('Unexpected array or string');
|
||||
}
|
||||
|
||||
return $rslt;
|
||||
}
|
||||
|
||||
private static function besselY0(float $x): float
|
||||
{
|
||||
if ($x < 8.0) {
|
||||
$y = ($x * $x);
|
||||
$ans1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y
|
||||
* (-86327.92757 + $y * 228.4622733))));
|
||||
$ans2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y
|
||||
* (47447.26470 + $y * (226.1030244 + $y))));
|
||||
|
||||
return $ans1 / $ans2 + 0.636619772 * self::callBesselJ($x, 0) * log($x);
|
||||
}
|
||||
|
||||
$z = 8.0 / $x;
|
||||
$y = ($z * $z);
|
||||
$xx = $x - 0.785398164;
|
||||
$ans1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
|
||||
$ans2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y
|
||||
* (-0.934945152e-7))));
|
||||
|
||||
return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
|
||||
}
|
||||
|
||||
private static function besselY1(float $x): float
|
||||
{
|
||||
if ($x < 8.0) {
|
||||
$y = ($x * $x);
|
||||
$ans1 = $x * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y
|
||||
* (0.7349264551e9 + $y * (-0.4237922726e7 + $y * 0.8511937935e4)))));
|
||||
$ans2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y
|
||||
* (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
|
||||
|
||||
return ($ans1 / $ans2) + 0.636619772 * (self::callBesselJ($x, 1) * log($x) - 1 / $x);
|
||||
}
|
||||
|
||||
$z = 8.0 / $x;
|
||||
$y = $z * $z;
|
||||
$xx = $x - 2.356194491;
|
||||
$ans1 = 1.0 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e-6))));
|
||||
$ans2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y
|
||||
* (-0.88228987e-6 + $y * 0.105787412e-6)));
|
||||
|
||||
return sqrt(0.636619772 / $x) * (sin($xx) * $ans1 + $z * cos($xx) * $ans2);
|
||||
}
|
||||
|
||||
private static function besselY2(float $x, int $ord): float
|
||||
{
|
||||
$fTox = 2.0 / $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 $fBy;
|
||||
}
|
||||
}
|
||||
247
lib/PhpSpreadsheet/Calculation/Engineering/BitWise.php
Normal file
247
lib/PhpSpreadsheet/Calculation/Engineering/BitWise.php
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class BitWise
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
const SPLIT_DIVISOR = 2 ** 24;
|
||||
|
||||
/**
|
||||
* Split a number into upper and lower portions for full 32-bit support.
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
private static function splitNumber(float|int $number): array
|
||||
{
|
||||
return [(int) floor($number / self::SPLIT_DIVISOR), (int) fmod($number, self::SPLIT_DIVISOR)];
|
||||
}
|
||||
|
||||
/**
|
||||
* BITAND.
|
||||
*
|
||||
* Returns the bitwise AND of two integer values.
|
||||
*
|
||||
* Excel Function:
|
||||
* BITAND(number1, number2)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $number2 Or can be an array of values
|
||||
*
|
||||
* @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITAND(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int
|
||||
{
|
||||
if (is_array($number1) || is_array($number2)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
|
||||
}
|
||||
|
||||
try {
|
||||
$number1 = self::validateBitwiseArgument($number1);
|
||||
$number2 = self::validateBitwiseArgument($number2);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
$split1 = self::splitNumber($number1);
|
||||
$split2 = self::splitNumber($number2);
|
||||
|
||||
return self::SPLIT_DIVISOR * ($split1[0] & $split2[0]) + ($split1[1] & $split2[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* BITOR.
|
||||
*
|
||||
* Returns the bitwise OR of two integer values.
|
||||
*
|
||||
* Excel Function:
|
||||
* BITOR(number1, number2)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $number2 Or can be an array of values
|
||||
*
|
||||
* @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int
|
||||
{
|
||||
if (is_array($number1) || is_array($number2)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
|
||||
}
|
||||
|
||||
try {
|
||||
$number1 = self::validateBitwiseArgument($number1);
|
||||
$number2 = self::validateBitwiseArgument($number2);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$split1 = self::splitNumber($number1);
|
||||
$split2 = self::splitNumber($number2);
|
||||
|
||||
return self::SPLIT_DIVISOR * ($split1[0] | $split2[0]) + ($split1[1] | $split2[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* BITXOR.
|
||||
*
|
||||
* Returns the bitwise XOR of two integer values.
|
||||
*
|
||||
* Excel Function:
|
||||
* BITXOR(number1, number2)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number1 Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $number2 Or can be an array of values
|
||||
*
|
||||
* @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITXOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int
|
||||
{
|
||||
if (is_array($number1) || is_array($number2)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);
|
||||
}
|
||||
|
||||
try {
|
||||
$number1 = self::validateBitwiseArgument($number1);
|
||||
$number2 = self::validateBitwiseArgument($number2);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$split1 = self::splitNumber($number1);
|
||||
$split2 = self::splitNumber($number2);
|
||||
|
||||
return self::SPLIT_DIVISOR * ($split1[0] ^ $split2[0]) + ($split1[1] ^ $split2[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* BITLSHIFT.
|
||||
*
|
||||
* Returns the number value shifted left by shift_amount bits.
|
||||
*
|
||||
* Excel Function:
|
||||
* BITLSHIFT(number, shift_amount)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $shiftAmount Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITLSHIFT(null|array|bool|float|int|string $number, null|array|bool|float|int|string $shiftAmount): array|string|float
|
||||
{
|
||||
if (is_array($number) || is_array($shiftAmount)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
|
||||
}
|
||||
|
||||
try {
|
||||
$number = self::validateBitwiseArgument($number);
|
||||
$shiftAmount = self::validateShiftAmount($shiftAmount);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$result = floor($number * (2 ** $shiftAmount));
|
||||
if ($result > 2 ** 48 - 1) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* BITRSHIFT.
|
||||
*
|
||||
* Returns the number value shifted right by shift_amount bits.
|
||||
*
|
||||
* Excel Function:
|
||||
* BITRSHIFT(number, shift_amount)
|
||||
*
|
||||
* @param null|array|bool|float|int|string $number Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $shiftAmount Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function BITRSHIFT(null|array|bool|float|int|string $number, null|array|bool|float|int|string $shiftAmount): array|string|float
|
||||
{
|
||||
if (is_array($number) || is_array($shiftAmount)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $shiftAmount);
|
||||
}
|
||||
|
||||
try {
|
||||
$number = self::validateBitwiseArgument($number);
|
||||
$shiftAmount = self::validateShiftAmount($shiftAmount);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$result = floor($number / (2 ** $shiftAmount));
|
||||
if ($result > 2 ** 48 - 1) { // possible because shiftAmount can be negative
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate arguments passed to the bitwise functions.
|
||||
*/
|
||||
private static function validateBitwiseArgument(mixed $value): float
|
||||
{
|
||||
$value = self::nullFalseTrueToNumber($value);
|
||||
|
||||
if (is_numeric($value)) {
|
||||
$value = (float) $value;
|
||||
if ($value == floor($value)) {
|
||||
if (($value > 2 ** 48 - 1) || ($value < 0)) {
|
||||
throw new Exception(ExcelError::NAN());
|
||||
}
|
||||
|
||||
return floor($value);
|
||||
}
|
||||
|
||||
throw new Exception(ExcelError::NAN());
|
||||
}
|
||||
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate arguments passed to the bitwise functions.
|
||||
*/
|
||||
private static function validateShiftAmount(mixed $value): int
|
||||
{
|
||||
$value = self::nullFalseTrueToNumber($value);
|
||||
|
||||
if (is_numeric($value)) {
|
||||
if (abs($value) > 53) {
|
||||
throw new Exception(ExcelError::NAN());
|
||||
}
|
||||
|
||||
return (int) $value;
|
||||
}
|
||||
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
|
||||
/**
|
||||
* Many functions accept null/false/true argument treated as 0/0/1.
|
||||
*/
|
||||
private static function nullFalseTrueToNumber(mixed &$number): mixed
|
||||
{
|
||||
if ($number === null) {
|
||||
$number = 0;
|
||||
} elseif (is_bool($number)) {
|
||||
$number = (int) $number;
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
}
|
||||
82
lib/PhpSpreadsheet/Calculation/Engineering/Compare.php
Normal file
82
lib/PhpSpreadsheet/Calculation/Engineering/Compare.php
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
|
||||
class Compare
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
/**
|
||||
* DELTA.
|
||||
*
|
||||
* Excel Function:
|
||||
* DELTA(a[,b])
|
||||
*
|
||||
* Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
|
||||
* Use this function to filter a set of values. For example, by summing several DELTA
|
||||
* functions you calculate the count of equal pairs. This function is also known as the
|
||||
* Kronecker Delta function.
|
||||
*
|
||||
* @param array|bool|float|int|string $a the first number
|
||||
* Or can be an array of values
|
||||
* @param array|bool|float|int|string $b The second number. If omitted, b is assumed to be zero.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|int|string (string in the event of an error)
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function DELTA(array|float|bool|string|int $a, array|float|bool|string|int $b = 0.0): array|string|int
|
||||
{
|
||||
if (is_array($a) || is_array($b)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $a, $b);
|
||||
}
|
||||
|
||||
try {
|
||||
$a = EngineeringValidations::validateFloat($a);
|
||||
$b = EngineeringValidations::validateFloat($b);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
return (int) (abs($a - $b) < 1.0e-15);
|
||||
}
|
||||
|
||||
/**
|
||||
* GESTEP.
|
||||
*
|
||||
* Excel Function:
|
||||
* GESTEP(number[,step])
|
||||
*
|
||||
* Returns 1 if number >= step; returns 0 (zero) otherwise
|
||||
* Use this function to filter a set of values. For example, by summing several GESTEP
|
||||
* functions you calculate the count of values that exceed a threshold.
|
||||
*
|
||||
* @param array|bool|float|int|string $number the value to test against step
|
||||
* Or can be an array of values
|
||||
* @param null|array|bool|float|int|string $step The threshold value. If you omit a value for step, GESTEP uses zero.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|int|string (string in the event of an error)
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function GESTEP(array|float|bool|string|int $number, $step = 0.0): array|string|int
|
||||
{
|
||||
if (is_array($number) || is_array($step)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $step);
|
||||
}
|
||||
|
||||
try {
|
||||
$number = EngineeringValidations::validateFloat($number);
|
||||
$step = EngineeringValidations::validateFloat($step ?? 0.0);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
return (int) ($number >= $step);
|
||||
}
|
||||
}
|
||||
120
lib/PhpSpreadsheet/Calculation/Engineering/Complex.php
Normal file
120
lib/PhpSpreadsheet/Calculation/Engineering/Complex.php
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use Complex\Complex as ComplexObject;
|
||||
use Complex\Exception as ComplexException;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class Complex
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
/**
|
||||
* COMPLEX.
|
||||
*
|
||||
* Converts real and imaginary coefficients into a complex number of the form x +/- yi or x +/- yj.
|
||||
*
|
||||
* Excel Function:
|
||||
* COMPLEX(realNumber,imaginary[,suffix])
|
||||
*
|
||||
* @param mixed $realNumber the real float coefficient of the complex number
|
||||
* Or can be an array of values
|
||||
* @param mixed $imaginary the imaginary float coefficient of the complex number
|
||||
* Or can be an array of values
|
||||
* @param mixed $suffix The character suffix for the imaginary component of the complex number.
|
||||
* If omitted, the suffix is assumed to be "i".
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function COMPLEX(mixed $realNumber = 0.0, mixed $imaginary = 0.0, mixed $suffix = 'i'): array|string
|
||||
{
|
||||
if (is_array($realNumber) || is_array($imaginary) || is_array($suffix)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $realNumber, $imaginary, $suffix);
|
||||
}
|
||||
|
||||
$realNumber = $realNumber ?? 0.0;
|
||||
$imaginary = $imaginary ?? 0.0;
|
||||
$suffix = $suffix ?? 'i';
|
||||
|
||||
try {
|
||||
$realNumber = EngineeringValidations::validateFloat($realNumber);
|
||||
$imaginary = EngineeringValidations::validateFloat($imaginary);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if (($suffix === 'i') || ($suffix === 'j') || ($suffix === '')) {
|
||||
$complex = new ComplexObject($realNumber, $imaginary, $suffix);
|
||||
|
||||
return (string) $complex;
|
||||
}
|
||||
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMAGINARY.
|
||||
*
|
||||
* Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMAGINARY(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the imaginary
|
||||
* coefficient
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string (string if an error)
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMAGINARY($complexNumber): array|string|float
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return $complex->getImaginary();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMREAL.
|
||||
*
|
||||
* Returns the real coefficient of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMREAL(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the real coefficient
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string (string if an error)
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMREAL($complexNumber): array|string|float
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return $complex->getReal();
|
||||
}
|
||||
}
|
||||
592
lib/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
Normal file
592
lib/PhpSpreadsheet/Calculation/Engineering/ComplexFunctions.php
Normal file
|
|
@ -0,0 +1,592 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use Complex\Complex as ComplexObject;
|
||||
use Complex\Exception as ComplexException;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class ComplexFunctions
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
/**
|
||||
* IMABS.
|
||||
*
|
||||
* Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMABS(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the absolute value
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMABS(array|string $complexNumber): array|float|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return $complex->abs();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMARGUMENT.
|
||||
*
|
||||
* Returns the argument theta of a complex number, i.e. the angle in radians from the real
|
||||
* axis to the representation of the number in polar coordinates.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMARGUMENT(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the argument theta
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMARGUMENT(array|string $complexNumber): array|float|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
|
||||
return ExcelError::DIV0();
|
||||
}
|
||||
|
||||
return $complex->argument();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMCONJUGATE.
|
||||
*
|
||||
* Returns the complex conjugate of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMCONJUGATE(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the conjugate
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCONJUGATE(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->conjugate();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMCOS.
|
||||
*
|
||||
* Returns the cosine of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMCOS(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the cosine
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCOS(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->cos();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMCOSH.
|
||||
*
|
||||
* Returns the hyperbolic cosine of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMCOSH(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the hyperbolic cosine
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCOSH(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->cosh();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMCOT.
|
||||
*
|
||||
* Returns the cotangent of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMCOT(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the cotangent
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCOT(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->cot();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMCSC.
|
||||
*
|
||||
* Returns the cosecant of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMCSC(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the cosecant
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCSC(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->csc();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMCSCH.
|
||||
*
|
||||
* Returns the hyperbolic cosecant of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMCSCH(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the hyperbolic cosecant
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMCSCH(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->csch();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMSIN.
|
||||
*
|
||||
* Returns the sine of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMSIN(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the sine
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSIN(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->sin();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMSINH.
|
||||
*
|
||||
* Returns the hyperbolic sine of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMSINH(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the hyperbolic sine
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSINH(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->sinh();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMSEC.
|
||||
*
|
||||
* Returns the secant of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMSEC(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the secant
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSEC(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->sec();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMSECH.
|
||||
*
|
||||
* Returns the hyperbolic secant of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMSECH(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the hyperbolic secant
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSECH(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->sech();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMTAN.
|
||||
*
|
||||
* Returns the tangent of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMTAN(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the tangent
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMTAN(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->tan();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMSQRT.
|
||||
*
|
||||
* Returns the square root of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMSQRT(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the square root
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSQRT(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
$theta = self::IMARGUMENT($complexNumber);
|
||||
if ($theta === ExcelError::DIV0()) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
return (string) $complex->sqrt();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMLN.
|
||||
*
|
||||
* Returns the natural logarithm of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMLN(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the natural logarithm
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMLN(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->ln();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMLOG10.
|
||||
*
|
||||
* Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMLOG10(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the common logarithm
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMLOG10(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->log10();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMLOG2.
|
||||
*
|
||||
* Returns the base-2 logarithm of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMLOG2(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the base-2 logarithm
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMLOG2(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
if ($complex->getReal() == 0.0 && $complex->getImaginary() == 0.0) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->log2();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMEXP.
|
||||
*
|
||||
* Returns the exponential of a complex number in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMEXP(complexNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number for which you want the exponential
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMEXP(array|string $complexNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $complexNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $complex->exp();
|
||||
}
|
||||
|
||||
/**
|
||||
* IMPOWER.
|
||||
*
|
||||
* Returns a complex number in x + yi or x + yj text format raised to a power.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMPOWER(complexNumber,realNumber)
|
||||
*
|
||||
* @param array|string $complexNumber the complex number you want to raise to a power
|
||||
* Or can be an array of values
|
||||
* @param array|float|int|string $realNumber the power to which you want to raise the complex number
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMPOWER(array|string $complexNumber, array|float|int|string $realNumber): array|string
|
||||
{
|
||||
if (is_array($complexNumber) || is_array($realNumber)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber, $realNumber);
|
||||
}
|
||||
|
||||
try {
|
||||
$complex = new ComplexObject($complexNumber);
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
if (!is_numeric($realNumber)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
return (string) $complex->pow((float) $realNumber);
|
||||
}
|
||||
}
|
||||
128
lib/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
Normal file
128
lib/PhpSpreadsheet/Calculation/Engineering/ComplexOperations.php
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use Complex\Complex as ComplexObject;
|
||||
use Complex\Exception as ComplexException;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class ComplexOperations
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
/**
|
||||
* IMDIV.
|
||||
*
|
||||
* Returns the quotient of two complex numbers in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMDIV(complexDividend,complexDivisor)
|
||||
*
|
||||
* @param array|string $complexDividend the complex numerator or dividend
|
||||
* Or can be an array of values
|
||||
* @param array|string $complexDivisor the complex denominator or divisor
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMDIV(array|string $complexDividend, array|string $complexDivisor): array|string
|
||||
{
|
||||
if (is_array($complexDividend) || is_array($complexDivisor)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexDividend, $complexDivisor);
|
||||
}
|
||||
|
||||
try {
|
||||
return (string) (new ComplexObject($complexDividend))->divideby(new ComplexObject($complexDivisor));
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IMSUB.
|
||||
*
|
||||
* Returns the difference of two complex numbers in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMSUB(complexNumber1,complexNumber2)
|
||||
*
|
||||
* @param array|string $complexNumber1 the complex number from which to subtract complexNumber2
|
||||
* Or can be an array of values
|
||||
* @param array|string $complexNumber2 the complex number to subtract from complexNumber1
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function IMSUB(array|string $complexNumber1, array|string $complexNumber2): array|string
|
||||
{
|
||||
if (is_array($complexNumber1) || is_array($complexNumber2)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $complexNumber1, $complexNumber2);
|
||||
}
|
||||
|
||||
try {
|
||||
return (string) (new ComplexObject($complexNumber1))->subtract(new ComplexObject($complexNumber2));
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IMSUM.
|
||||
*
|
||||
* Returns the sum of two or more complex numbers in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMSUM(complexNumber[,complexNumber[,...]])
|
||||
*
|
||||
* @param string ...$complexNumbers Series of complex numbers to add
|
||||
*/
|
||||
public static function IMSUM(...$complexNumbers): string
|
||||
{
|
||||
// Return value
|
||||
$returnValue = new ComplexObject(0.0);
|
||||
$aArgs = Functions::flattenArray($complexNumbers);
|
||||
|
||||
try {
|
||||
// Loop through the arguments
|
||||
foreach ($aArgs as $complex) {
|
||||
$returnValue = $returnValue->add(new ComplexObject($complex));
|
||||
}
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* IMPRODUCT.
|
||||
*
|
||||
* Returns the product of two or more complex numbers in x + yi or x + yj text format.
|
||||
*
|
||||
* Excel Function:
|
||||
* IMPRODUCT(complexNumber[,complexNumber[,...]])
|
||||
*
|
||||
* @param string ...$complexNumbers Series of complex numbers to multiply
|
||||
*/
|
||||
public static function IMPRODUCT(...$complexNumbers): string
|
||||
{
|
||||
// Return value
|
||||
$returnValue = new ComplexObject(1.0);
|
||||
$aArgs = Functions::flattenArray($complexNumbers);
|
||||
|
||||
try {
|
||||
// Loop through the arguments
|
||||
foreach ($aArgs as $complex) {
|
||||
$returnValue = $returnValue->multiply(new ComplexObject($complex));
|
||||
}
|
||||
} catch (ComplexException) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return (string) $returnValue;
|
||||
}
|
||||
}
|
||||
11
lib/PhpSpreadsheet/Calculation/Engineering/Constants.php
Normal file
11
lib/PhpSpreadsheet/Calculation/Engineering/Constants.php
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
class Constants
|
||||
{
|
||||
/**
|
||||
* EULER.
|
||||
*/
|
||||
public const EULER = 2.71828182845904523536;
|
||||
}
|
||||
69
lib/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php
Normal file
69
lib/PhpSpreadsheet/Calculation/Engineering/ConvertBase.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
abstract class ConvertBase
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
protected static function validateValue(mixed $value): string
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) {
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
$value = (int) $value;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
|
||||
$value = floor((float) $value);
|
||||
}
|
||||
}
|
||||
|
||||
return strtoupper((string) $value);
|
||||
}
|
||||
|
||||
protected static function validatePlaces(mixed $places = null): ?int
|
||||
{
|
||||
if ($places === null) {
|
||||
return $places;
|
||||
}
|
||||
|
||||
if (is_numeric($places)) {
|
||||
if ($places < 0 || $places > 10) {
|
||||
throw new Exception(ExcelError::NAN());
|
||||
}
|
||||
|
||||
return (int) $places;
|
||||
}
|
||||
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a number base string value with leading zeroes.
|
||||
*
|
||||
* @param string $value The "number" to pad
|
||||
* @param ?int $places The length that we want to pad this value
|
||||
*
|
||||
* @return string The padded "number"
|
||||
*/
|
||||
protected static function nbrConversionFormat(string $value, ?int $places): string
|
||||
{
|
||||
if ($places !== null) {
|
||||
if (strlen($value) <= $places) {
|
||||
return substr(str_pad($value, $places, '0', STR_PAD_LEFT), -10);
|
||||
}
|
||||
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
return substr($value, -10);
|
||||
}
|
||||
}
|
||||
163
lib/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
Normal file
163
lib/PhpSpreadsheet/Calculation/Engineering/ConvertBinary.php
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class ConvertBinary extends ConvertBase
|
||||
{
|
||||
/**
|
||||
* toDecimal.
|
||||
*
|
||||
* Return a binary value as decimal.
|
||||
*
|
||||
* Excel Function:
|
||||
* BIN2DEC(x)
|
||||
*
|
||||
* @param array|bool|float|int|string $value The binary number (as a string) that you want to convert. The number
|
||||
* cannot contain more than 10 characters (10 bits). The most significant
|
||||
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
|
||||
* Negative numbers are represented using two's-complement notation.
|
||||
* If number is not a valid binary number, or if number contains more than
|
||||
* 10 characters (10 bits), BIN2DEC returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function toDecimal($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::validateValue($value);
|
||||
$value = self::validateBinary($value);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if (strlen($value) == 10 && $value[0] === '1') {
|
||||
// Two's Complement
|
||||
$value = substr($value, -9);
|
||||
|
||||
return '-' . (512 - bindec($value));
|
||||
}
|
||||
|
||||
return (string) bindec($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* toHex.
|
||||
*
|
||||
* Return a binary value as hex.
|
||||
*
|
||||
* Excel Function:
|
||||
* BIN2HEX(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The binary number (as a string) that you want to convert. The number
|
||||
* cannot contain more than 10 characters (10 bits). The most significant
|
||||
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
|
||||
* Negative numbers are represented using two's-complement notation.
|
||||
* If number is not a valid binary number, or if number contains more than
|
||||
* 10 characters (10 bits), BIN2HEX returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param null|array|float|int|string $places The number of characters to use. If places is omitted, BIN2HEX uses the
|
||||
* minimum number of characters necessary. Places is useful for padding the
|
||||
* return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
* If places is nonnumeric, BIN2HEX returns the #VALUE! error value.
|
||||
* If places is negative, BIN2HEX returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function toHex($value, $places = null): array|string
|
||||
{
|
||||
if (is_array($value) || is_array($places)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::validateValue($value);
|
||||
$value = self::validateBinary($value);
|
||||
$places = self::validatePlaces($places);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if (strlen($value) == 10 && $value[0] === '1') {
|
||||
$high2 = substr($value, 0, 2);
|
||||
$low8 = substr($value, 2);
|
||||
$xarr = ['00' => '00000000', '01' => '00000001', '10' => 'FFFFFFFE', '11' => 'FFFFFFFF'];
|
||||
|
||||
return $xarr[$high2] . strtoupper(substr('0' . dechex((int) bindec($low8)), -2));
|
||||
}
|
||||
$hexVal = (string) strtoupper(dechex((int) bindec($value)));
|
||||
|
||||
return self::nbrConversionFormat($hexVal, $places);
|
||||
}
|
||||
|
||||
/**
|
||||
* toOctal.
|
||||
*
|
||||
* Return a binary value as octal.
|
||||
*
|
||||
* Excel Function:
|
||||
* BIN2OCT(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The binary number (as a string) that you want to convert. The number
|
||||
* cannot contain more than 10 characters (10 bits). The most significant
|
||||
* bit of number is the sign bit. The remaining 9 bits are magnitude bits.
|
||||
* Negative numbers are represented using two's-complement notation.
|
||||
* If number is not a valid binary number, or if number contains more than
|
||||
* 10 characters (10 bits), BIN2OCT returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param null|array|float|int|string $places The number of characters to use. If places is omitted, BIN2OCT uses the
|
||||
* minimum number of characters necessary. Places is useful for padding the
|
||||
* return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
* If places is nonnumeric, BIN2OCT returns the #VALUE! error value.
|
||||
* If places is negative, BIN2OCT returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function toOctal($value, $places = null): array|string
|
||||
{
|
||||
if (is_array($value) || is_array($places)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::validateValue($value);
|
||||
$value = self::validateBinary($value);
|
||||
$places = self::validatePlaces($places);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if (strlen($value) == 10 && $value[0] === '1') { // Two's Complement
|
||||
return str_repeat('7', 6) . strtoupper(decoct((int) bindec("11$value")));
|
||||
}
|
||||
$octVal = (string) decoct((int) bindec($value));
|
||||
|
||||
return self::nbrConversionFormat($octVal, $places);
|
||||
}
|
||||
|
||||
protected static function validateBinary(string $value): string
|
||||
{
|
||||
if ((strlen($value) > preg_match_all('/[01]/', $value)) || (strlen($value) > 10)) {
|
||||
throw new Exception(ExcelError::NAN());
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
213
lib/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
Normal file
213
lib/PhpSpreadsheet/Calculation/Engineering/ConvertDecimal.php
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class ConvertDecimal extends ConvertBase
|
||||
{
|
||||
const LARGEST_OCTAL_IN_DECIMAL = 536870911;
|
||||
const SMALLEST_OCTAL_IN_DECIMAL = -536870912;
|
||||
const LARGEST_BINARY_IN_DECIMAL = 511;
|
||||
const SMALLEST_BINARY_IN_DECIMAL = -512;
|
||||
const LARGEST_HEX_IN_DECIMAL = 549755813887;
|
||||
const SMALLEST_HEX_IN_DECIMAL = -549755813888;
|
||||
|
||||
/**
|
||||
* toBinary.
|
||||
*
|
||||
* Return a decimal value as binary.
|
||||
*
|
||||
* Excel Function:
|
||||
* DEC2BIN(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The decimal integer you want to convert. If number is negative,
|
||||
* valid place values are ignored and DEC2BIN returns a 10-character
|
||||
* (10-bit) binary number in which the most significant bit is the sign
|
||||
* bit. The remaining 9 bits are magnitude bits. Negative numbers are
|
||||
* represented using two's-complement notation.
|
||||
* If number < -512 or if number > 511, DEC2BIN returns the #NUM! error
|
||||
* value.
|
||||
* If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
|
||||
* If DEC2BIN requires more than places characters, it returns the #NUM!
|
||||
* error value.
|
||||
* Or can be an array of values
|
||||
* @param null|array|float|int|string $places The number of characters to use. If places is omitted, DEC2BIN uses
|
||||
* the minimum number of characters necessary. Places is useful for
|
||||
* padding the return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
* If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
|
||||
* If places is zero or negative, DEC2BIN returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function toBinary($value, $places = null): array|string
|
||||
{
|
||||
if (is_array($value) || is_array($places)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::validateValue($value);
|
||||
$value = self::validateDecimal($value);
|
||||
$places = self::validatePlaces($places);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$value = (int) floor((float) $value);
|
||||
if ($value > self::LARGEST_BINARY_IN_DECIMAL || $value < self::SMALLEST_BINARY_IN_DECIMAL) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
$r = decbin($value);
|
||||
// Two's Complement
|
||||
$r = substr($r, -10);
|
||||
|
||||
return self::nbrConversionFormat($r, $places);
|
||||
}
|
||||
|
||||
/**
|
||||
* toHex.
|
||||
*
|
||||
* Return a decimal value as hex.
|
||||
*
|
||||
* Excel Function:
|
||||
* DEC2HEX(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The decimal integer you want to convert. If number is negative,
|
||||
* places is ignored and DEC2HEX returns a 10-character (40-bit)
|
||||
* hexadecimal number in which the most significant bit is the sign
|
||||
* bit. The remaining 39 bits are magnitude bits. Negative numbers
|
||||
* are represented using two's-complement notation.
|
||||
* If number < -549,755,813,888 or if number > 549,755,813,887,
|
||||
* DEC2HEX returns the #NUM! error value.
|
||||
* If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
|
||||
* If DEC2HEX requires more than places characters, it returns the
|
||||
* #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param null|array|float|int|string $places The number of characters to use. If places is omitted, DEC2HEX uses
|
||||
* the minimum number of characters necessary. Places is useful for
|
||||
* padding the return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
* If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
|
||||
* If places is zero or negative, DEC2HEX returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function toHex($value, $places = null): array|string
|
||||
{
|
||||
if (is_array($value) || is_array($places)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::validateValue($value);
|
||||
$value = self::validateDecimal($value);
|
||||
$places = self::validatePlaces($places);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$value = floor((float) $value);
|
||||
if ($value > self::LARGEST_HEX_IN_DECIMAL || $value < self::SMALLEST_HEX_IN_DECIMAL) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
$r = strtoupper(dechex((int) $value));
|
||||
$r = self::hex32bit($value, $r);
|
||||
|
||||
return self::nbrConversionFormat($r, $places);
|
||||
}
|
||||
|
||||
public static function hex32bit(float $value, string $hexstr, bool $force = false): string
|
||||
{
|
||||
if (PHP_INT_SIZE === 4 || $force) {
|
||||
if ($value >= 2 ** 32) {
|
||||
$quotient = (int) ($value / (2 ** 32));
|
||||
|
||||
return strtoupper(substr('0' . dechex($quotient), -2) . $hexstr);
|
||||
}
|
||||
if ($value < -(2 ** 32)) {
|
||||
$quotient = 256 - (int) ceil((-$value) / (2 ** 32));
|
||||
|
||||
return strtoupper(substr('0' . dechex($quotient), -2) . substr("00000000$hexstr", -8));
|
||||
}
|
||||
if ($value < 0) {
|
||||
return "FF$hexstr";
|
||||
}
|
||||
}
|
||||
|
||||
return $hexstr;
|
||||
}
|
||||
|
||||
/**
|
||||
* toOctal.
|
||||
*
|
||||
* Return an decimal value as octal.
|
||||
*
|
||||
* Excel Function:
|
||||
* DEC2OCT(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The decimal integer you want to convert. If number is negative,
|
||||
* places is ignored and DEC2OCT returns a 10-character (30-bit)
|
||||
* octal number in which the most significant bit is the sign bit.
|
||||
* The remaining 29 bits are magnitude bits. Negative numbers are
|
||||
* represented using two's-complement notation.
|
||||
* If number < -536,870,912 or if number > 536,870,911, DEC2OCT
|
||||
* returns the #NUM! error value.
|
||||
* If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
|
||||
* If DEC2OCT requires more than places characters, it returns the
|
||||
* #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted, DEC2OCT uses
|
||||
* the minimum number of characters necessary. Places is useful for
|
||||
* padding the return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
* If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
|
||||
* If places is zero or negative, DEC2OCT returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function toOctal($value, $places = null): array|string
|
||||
{
|
||||
if (is_array($value) || is_array($places)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::validateValue($value);
|
||||
$value = self::validateDecimal($value);
|
||||
$places = self::validatePlaces($places);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$value = (int) floor((float) $value);
|
||||
if ($value > self::LARGEST_OCTAL_IN_DECIMAL || $value < self::SMALLEST_OCTAL_IN_DECIMAL) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
$r = decoct($value);
|
||||
$r = substr($r, -10);
|
||||
|
||||
return self::nbrConversionFormat($r, $places);
|
||||
}
|
||||
|
||||
protected static function validateDecimal(string $value): string
|
||||
{
|
||||
if (strlen($value) > preg_match_all('/[-0123456789.]/', $value)) {
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
175
lib/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
Normal file
175
lib/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class ConvertHex extends ConvertBase
|
||||
{
|
||||
/**
|
||||
* toBinary.
|
||||
*
|
||||
* Return a hex value as binary.
|
||||
*
|
||||
* Excel Function:
|
||||
* HEX2BIN(x[,places])
|
||||
*
|
||||
* @param array|bool|float|string $value The hexadecimal number you want to convert.
|
||||
* Number cannot contain more than 10 characters.
|
||||
* The most significant bit of number is the sign bit (40th bit from the right).
|
||||
* The remaining 9 bits are magnitude bits.
|
||||
* Negative numbers are represented using two's-complement notation.
|
||||
* If number is negative, HEX2BIN ignores places and returns a 10-character binary number.
|
||||
* If number is negative, it cannot be less than FFFFFFFE00,
|
||||
* and if number is positive, it cannot be greater than 1FF.
|
||||
* If number is not a valid hexadecimal number, HEX2BIN returns the #NUM! error value.
|
||||
* If HEX2BIN requires more than places characters, it returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted,
|
||||
* HEX2BIN uses the minimum number of characters necessary. Places
|
||||
* is useful for padding the return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
* If places is nonnumeric, HEX2BIN returns the #VALUE! error value.
|
||||
* If places is negative, HEX2BIN returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function toBinary($value, $places = null): array|string
|
||||
{
|
||||
if (is_array($value) || is_array($places)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::validateValue($value);
|
||||
$value = self::validateHex($value);
|
||||
$places = self::validatePlaces($places);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$dec = self::toDecimal($value);
|
||||
|
||||
return ConvertDecimal::toBinary($dec, $places);
|
||||
}
|
||||
|
||||
/**
|
||||
* toDecimal.
|
||||
*
|
||||
* Return a hex value as decimal.
|
||||
*
|
||||
* Excel Function:
|
||||
* HEX2DEC(x)
|
||||
*
|
||||
* @param array|bool|float|int|string $value The hexadecimal number you want to convert. This number cannot
|
||||
* contain more than 10 characters (40 bits). The most significant
|
||||
* bit of number is the sign bit. The remaining 39 bits are magnitude
|
||||
* bits. Negative numbers are represented using two's-complement
|
||||
* notation.
|
||||
* If number is not a valid hexadecimal number, HEX2DEC returns the
|
||||
* #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function toDecimal($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::validateValue($value);
|
||||
$value = self::validateHex($value);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
if (strlen($value) > 10) {
|
||||
return ExcelError::NAN();
|
||||
}
|
||||
|
||||
$binX = '';
|
||||
foreach (str_split($value) as $char) {
|
||||
$binX .= str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
if (strlen($binX) == 40 && $binX[0] == '1') {
|
||||
for ($i = 0; $i < 40; ++$i) {
|
||||
$binX[$i] = ($binX[$i] == '1' ? '0' : '1');
|
||||
}
|
||||
|
||||
return (string) ((bindec($binX) + 1) * -1);
|
||||
}
|
||||
|
||||
return (string) bindec($binX);
|
||||
}
|
||||
|
||||
/**
|
||||
* toOctal.
|
||||
*
|
||||
* Return a hex value as octal.
|
||||
*
|
||||
* Excel Function:
|
||||
* HEX2OCT(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The hexadecimal number you want to convert. Number cannot
|
||||
* contain more than 10 characters. The most significant bit of
|
||||
* number is the sign bit. The remaining 39 bits are magnitude
|
||||
* bits. Negative numbers are represented using two's-complement
|
||||
* notation.
|
||||
* If number is negative, HEX2OCT ignores places and returns a
|
||||
* 10-character octal number.
|
||||
* If number is negative, it cannot be less than FFE0000000, and
|
||||
* if number is positive, it cannot be greater than 1FFFFFFF.
|
||||
* If number is not a valid hexadecimal number, HEX2OCT returns
|
||||
* the #NUM! error value.
|
||||
* If HEX2OCT requires more than places characters, it returns
|
||||
* the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted, HEX2OCT
|
||||
* uses the minimum number of characters necessary. Places is
|
||||
* useful for padding the return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
* If places is nonnumeric, HEX2OCT returns the #VALUE! error
|
||||
* value.
|
||||
* If places is negative, HEX2OCT returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function toOctal($value, $places = null): array|string
|
||||
{
|
||||
if (is_array($value) || is_array($places)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::validateValue($value);
|
||||
$value = self::validateHex($value);
|
||||
$places = self::validatePlaces($places);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$decimal = self::toDecimal($value);
|
||||
|
||||
return ConvertDecimal::toOctal($decimal, $places);
|
||||
}
|
||||
|
||||
protected static function validateHex(string $value): string
|
||||
{
|
||||
if (strlen($value) > preg_match_all('/[0123456789ABCDEF]/', $value)) {
|
||||
throw new Exception(ExcelError::NAN());
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
174
lib/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
Normal file
174
lib/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class ConvertOctal extends ConvertBase
|
||||
{
|
||||
/**
|
||||
* toBinary.
|
||||
*
|
||||
* Return an octal value as binary.
|
||||
*
|
||||
* Excel Function:
|
||||
* OCT2BIN(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The octal number you want to convert. Number may not
|
||||
* contain more than 10 characters. The most significant
|
||||
* bit of number is the sign bit. The remaining 29 bits
|
||||
* are magnitude bits. Negative numbers are represented
|
||||
* using two's-complement notation.
|
||||
* If number is negative, OCT2BIN ignores places and returns
|
||||
* a 10-character binary number.
|
||||
* If number is negative, it cannot be less than 7777777000,
|
||||
* and if number is positive, it cannot be greater than 777.
|
||||
* If number is not a valid octal number, OCT2BIN returns
|
||||
* the #NUM! error value.
|
||||
* If OCT2BIN requires more than places characters, it
|
||||
* returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted,
|
||||
* OCT2BIN uses the minimum number of characters necessary.
|
||||
* Places is useful for padding the return value with
|
||||
* leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
* If places is nonnumeric, OCT2BIN returns the #VALUE!
|
||||
* error value.
|
||||
* If places is negative, OCT2BIN returns the #NUM! error
|
||||
* value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function toBinary($value, $places = null): array|string
|
||||
{
|
||||
if (is_array($value) || is_array($places)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::validateValue($value);
|
||||
$value = self::validateOctal($value);
|
||||
$places = self::validatePlaces($places);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
return ConvertDecimal::toBinary(self::toDecimal($value), $places);
|
||||
}
|
||||
|
||||
/**
|
||||
* toDecimal.
|
||||
*
|
||||
* Return an octal value as decimal.
|
||||
*
|
||||
* Excel Function:
|
||||
* OCT2DEC(x)
|
||||
*
|
||||
* @param array|bool|float|int|string $value The octal number you want to convert. Number may not contain
|
||||
* more than 10 octal characters (30 bits). The most significant
|
||||
* bit of number is the sign bit. The remaining 29 bits are
|
||||
* magnitude bits. Negative numbers are represented using
|
||||
* two's-complement notation.
|
||||
* If number is not a valid octal number, OCT2DEC returns the
|
||||
* #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function toDecimal($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::validateValue($value);
|
||||
$value = self::validateOctal($value);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$binX = '';
|
||||
foreach (str_split($value) as $char) {
|
||||
$binX .= str_pad(decbin((int) $char), 3, '0', STR_PAD_LEFT);
|
||||
}
|
||||
if (strlen($binX) == 30 && $binX[0] == '1') {
|
||||
for ($i = 0; $i < 30; ++$i) {
|
||||
$binX[$i] = ($binX[$i] == '1' ? '0' : '1');
|
||||
}
|
||||
|
||||
return (string) ((bindec($binX) + 1) * -1);
|
||||
}
|
||||
|
||||
return (string) bindec($binX);
|
||||
}
|
||||
|
||||
/**
|
||||
* toHex.
|
||||
*
|
||||
* Return an octal value as hex.
|
||||
*
|
||||
* Excel Function:
|
||||
* OCT2HEX(x[,places])
|
||||
*
|
||||
* @param array|bool|float|int|string $value The octal number you want to convert. Number may not contain
|
||||
* more than 10 octal characters (30 bits). The most significant
|
||||
* bit of number is the sign bit. The remaining 29 bits are
|
||||
* magnitude bits. Negative numbers are represented using
|
||||
* two's-complement notation.
|
||||
* If number is negative, OCT2HEX ignores places and returns a
|
||||
* 10-character hexadecimal number.
|
||||
* If number is not a valid octal number, OCT2HEX returns the
|
||||
* #NUM! error value.
|
||||
* If OCT2HEX requires more than places characters, it returns
|
||||
* the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
* @param array|int $places The number of characters to use. If places is omitted, OCT2HEX
|
||||
* uses the minimum number of characters necessary. Places is useful
|
||||
* for padding the return value with leading 0s (zeros).
|
||||
* If places is not an integer, it is truncated.
|
||||
* If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
|
||||
* If places is negative, OCT2HEX returns the #NUM! error value.
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|string Result, or an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function toHex($value, $places = null): array|string
|
||||
{
|
||||
if (is_array($value) || is_array($places)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $places);
|
||||
}
|
||||
|
||||
try {
|
||||
$value = self::validateValue($value);
|
||||
$value = self::validateOctal($value);
|
||||
$places = self::validatePlaces($places);
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$hexVal = strtoupper(dechex((int) self::toDecimal($value)));
|
||||
$hexVal = (PHP_INT_SIZE === 4 && strlen($value) === 10 && $value[0] >= '4') ? "FF{$hexVal}" : $hexVal;
|
||||
|
||||
return self::nbrConversionFormat($hexVal, $places);
|
||||
}
|
||||
|
||||
protected static function validateOctal(string $value): string
|
||||
{
|
||||
$numDigits = (int) preg_match_all('/[01234567]/', $value);
|
||||
if (strlen($value) > $numDigits || $numDigits > 10) {
|
||||
throw new Exception(ExcelError::NAN());
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
679
lib/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
Normal file
679
lib/PhpSpreadsheet/Calculation/Engineering/ConvertUOM.php
Normal file
|
|
@ -0,0 +1,679 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class ConvertUOM
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
public const CATEGORY_WEIGHT_AND_MASS = 'Weight and Mass';
|
||||
public const CATEGORY_DISTANCE = 'Distance';
|
||||
public const CATEGORY_TIME = 'Time';
|
||||
public const CATEGORY_PRESSURE = 'Pressure';
|
||||
public const CATEGORY_FORCE = 'Force';
|
||||
public const CATEGORY_ENERGY = 'Energy';
|
||||
public const CATEGORY_POWER = 'Power';
|
||||
public const CATEGORY_MAGNETISM = 'Magnetism';
|
||||
public const CATEGORY_TEMPERATURE = 'Temperature';
|
||||
public const CATEGORY_VOLUME = 'Volume and Liquid Measure';
|
||||
public const CATEGORY_AREA = 'Area';
|
||||
public const CATEGORY_INFORMATION = 'Information';
|
||||
public const CATEGORY_SPEED = 'Speed';
|
||||
|
||||
/**
|
||||
* Details of the Units of measure that can be used in CONVERTUOM().
|
||||
*
|
||||
* @var mixed[]
|
||||
*/
|
||||
private static array $conversionUnits = [
|
||||
// Weight and Mass
|
||||
'g' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Gram', 'AllowPrefix' => true],
|
||||
'sg' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Slug', 'AllowPrefix' => false],
|
||||
'lbm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false],
|
||||
'u' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => true],
|
||||
'ozm' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false],
|
||||
'grain' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Grain', 'AllowPrefix' => false],
|
||||
'cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
|
||||
'shweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'U.S. (short) hundredweight', 'AllowPrefix' => false],
|
||||
'uk_cwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
|
||||
'lcwt' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
|
||||
'hweight' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial hundredweight', 'AllowPrefix' => false],
|
||||
'stone' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Stone', 'AllowPrefix' => false],
|
||||
'ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Ton', 'AllowPrefix' => false],
|
||||
'uk_ton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
|
||||
'LTON' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
|
||||
'brton' => ['Group' => self::CATEGORY_WEIGHT_AND_MASS, 'Unit Name' => 'Imperial ton', 'AllowPrefix' => false],
|
||||
// Distance
|
||||
'm' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Meter', 'AllowPrefix' => true],
|
||||
'mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Statute mile', 'AllowPrefix' => false],
|
||||
'Nmi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Nautical mile', 'AllowPrefix' => false],
|
||||
'in' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Inch', 'AllowPrefix' => false],
|
||||
'ft' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Foot', 'AllowPrefix' => false],
|
||||
'yd' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Yard', 'AllowPrefix' => false],
|
||||
'ang' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Angstrom', 'AllowPrefix' => true],
|
||||
'ell' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Ell', 'AllowPrefix' => false],
|
||||
'ly' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Light Year', 'AllowPrefix' => false],
|
||||
'parsec' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false],
|
||||
'pc' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Parsec', 'AllowPrefix' => false],
|
||||
'Pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
|
||||
'Picapt' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
|
||||
'pica' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'Pica (1/6 in)', 'AllowPrefix' => false],
|
||||
'survey_mi' => ['Group' => self::CATEGORY_DISTANCE, 'Unit Name' => 'U.S survey mile (statute mile)', 'AllowPrefix' => false],
|
||||
// Time
|
||||
'yr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Year', 'AllowPrefix' => false],
|
||||
'day' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false],
|
||||
'd' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Day', 'AllowPrefix' => false],
|
||||
'hr' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Hour', 'AllowPrefix' => false],
|
||||
'mn' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false],
|
||||
'min' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Minute', 'AllowPrefix' => false],
|
||||
'sec' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true],
|
||||
's' => ['Group' => self::CATEGORY_TIME, 'Unit Name' => 'Second', 'AllowPrefix' => true],
|
||||
// Pressure
|
||||
'Pa' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
|
||||
'p' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
|
||||
'atm' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
|
||||
'at' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
|
||||
'mmHg' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => true],
|
||||
'psi' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'PSI', 'AllowPrefix' => true],
|
||||
'Torr' => ['Group' => self::CATEGORY_PRESSURE, 'Unit Name' => 'Torr', 'AllowPrefix' => true],
|
||||
// Force
|
||||
'N' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Newton', 'AllowPrefix' => true],
|
||||
'dyn' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
|
||||
'dy' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
|
||||
'lbf' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pound force', 'AllowPrefix' => false],
|
||||
'pond' => ['Group' => self::CATEGORY_FORCE, 'Unit Name' => 'Pond', 'AllowPrefix' => true],
|
||||
// Energy
|
||||
'J' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Joule', 'AllowPrefix' => true],
|
||||
'e' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Erg', 'AllowPrefix' => true],
|
||||
'c' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => true],
|
||||
'cal' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'IT calorie', 'AllowPrefix' => true],
|
||||
'eV' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
|
||||
'ev' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
|
||||
'HPh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
|
||||
'hh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
|
||||
'Wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
|
||||
'wh' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
|
||||
'flb' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'Foot-pound', 'AllowPrefix' => false],
|
||||
'BTU' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false],
|
||||
'btu' => ['Group' => self::CATEGORY_ENERGY, 'Unit Name' => 'BTU', 'AllowPrefix' => false],
|
||||
// Power
|
||||
'HP' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
|
||||
'h' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
|
||||
'W' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true],
|
||||
'w' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Watt', 'AllowPrefix' => true],
|
||||
'PS' => ['Group' => self::CATEGORY_POWER, 'Unit Name' => 'Pferdestärke', 'AllowPrefix' => false],
|
||||
// Magnetism
|
||||
'T' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Tesla', 'AllowPrefix' => true],
|
||||
'ga' => ['Group' => self::CATEGORY_MAGNETISM, 'Unit Name' => 'Gauss', 'AllowPrefix' => true],
|
||||
// Temperature
|
||||
'C' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false],
|
||||
'cel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Celsius', 'AllowPrefix' => false],
|
||||
'F' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
|
||||
'fah' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Fahrenheit', 'AllowPrefix' => false],
|
||||
'K' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
|
||||
'kel' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
|
||||
'Rank' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Rankine', 'AllowPrefix' => false],
|
||||
'Reau' => ['Group' => self::CATEGORY_TEMPERATURE, 'Unit Name' => 'Degrees Réaumur', 'AllowPrefix' => false],
|
||||
// Volume
|
||||
'l' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
|
||||
'L' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
|
||||
'lt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Litre', 'AllowPrefix' => true],
|
||||
'tsp' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Teaspoon', 'AllowPrefix' => false],
|
||||
'tspm' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Modern Teaspoon', 'AllowPrefix' => false],
|
||||
'tbs' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Tablespoon', 'AllowPrefix' => false],
|
||||
'oz' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => false],
|
||||
'cup' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cup', 'AllowPrefix' => false],
|
||||
'pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
|
||||
'us_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
|
||||
'uk_pt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => false],
|
||||
'qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Quart', 'AllowPrefix' => false],
|
||||
'uk_qt' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Quart (UK)', 'AllowPrefix' => false],
|
||||
'gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gallon', 'AllowPrefix' => false],
|
||||
'uk_gal' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Imperial Gallon (UK)', 'AllowPrefix' => false],
|
||||
'ang3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true],
|
||||
'ang^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Angstrom', 'AllowPrefix' => true],
|
||||
'barrel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Oil Barrel', 'AllowPrefix' => false],
|
||||
'bushel' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'US Bushel', 'AllowPrefix' => false],
|
||||
'in3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false],
|
||||
'in^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Inch', 'AllowPrefix' => false],
|
||||
'ft3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false],
|
||||
'ft^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Foot', 'AllowPrefix' => false],
|
||||
'ly3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false],
|
||||
'ly^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Light Year', 'AllowPrefix' => false],
|
||||
'm3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true],
|
||||
'm^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Meter', 'AllowPrefix' => true],
|
||||
'mi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false],
|
||||
'mi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Mile', 'AllowPrefix' => false],
|
||||
'yd3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false],
|
||||
'yd^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Yard', 'AllowPrefix' => false],
|
||||
'Nmi3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
|
||||
'Nmi^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Nautical Mile', 'AllowPrefix' => false],
|
||||
'Pica3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
|
||||
'Pica^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
|
||||
'Picapt3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
|
||||
'Picapt^3' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Cubic Pica', 'AllowPrefix' => false],
|
||||
'GRT' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false],
|
||||
'regton' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Gross Registered Ton', 'AllowPrefix' => false],
|
||||
'MTON' => ['Group' => self::CATEGORY_VOLUME, 'Unit Name' => 'Measurement Ton (Freight Ton)', 'AllowPrefix' => false],
|
||||
// Area
|
||||
'ha' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Hectare', 'AllowPrefix' => true],
|
||||
'uk_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'International Acre', 'AllowPrefix' => false],
|
||||
'us_acre' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'US Survey/Statute Acre', 'AllowPrefix' => false],
|
||||
'ang2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true],
|
||||
'ang^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Angstrom', 'AllowPrefix' => true],
|
||||
'ar' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Are', 'AllowPrefix' => true],
|
||||
'ft2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false],
|
||||
'ft^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Feet', 'AllowPrefix' => false],
|
||||
'in2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false],
|
||||
'in^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Inches', 'AllowPrefix' => false],
|
||||
'ly2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false],
|
||||
'ly^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Light Years', 'AllowPrefix' => false],
|
||||
'm2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true],
|
||||
'm^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Meters', 'AllowPrefix' => true],
|
||||
'Morgen' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Morgen', 'AllowPrefix' => false],
|
||||
'mi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false],
|
||||
'mi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Miles', 'AllowPrefix' => false],
|
||||
'Nmi2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false],
|
||||
'Nmi^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Nautical Miles', 'AllowPrefix' => false],
|
||||
'Pica2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
|
||||
'Pica^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
|
||||
'Picapt2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
|
||||
'Picapt^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Pica', 'AllowPrefix' => false],
|
||||
'yd2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false],
|
||||
'yd^2' => ['Group' => self::CATEGORY_AREA, 'Unit Name' => 'Square Yards', 'AllowPrefix' => false],
|
||||
// Information
|
||||
'byte' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Byte', 'AllowPrefix' => true],
|
||||
'bit' => ['Group' => self::CATEGORY_INFORMATION, 'Unit Name' => 'Bit', 'AllowPrefix' => true],
|
||||
// Speed
|
||||
'm/s' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true],
|
||||
'm/sec' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per second', 'AllowPrefix' => true],
|
||||
'm/h' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true],
|
||||
'm/hr' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Meters per hour', 'AllowPrefix' => true],
|
||||
'mph' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Miles per hour', 'AllowPrefix' => false],
|
||||
'admkn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Admiralty Knot', 'AllowPrefix' => false],
|
||||
'kn' => ['Group' => self::CATEGORY_SPEED, 'Unit Name' => 'Knot', 'AllowPrefix' => false],
|
||||
];
|
||||
|
||||
/**
|
||||
* Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
|
||||
*
|
||||
* @var mixed[]
|
||||
*/
|
||||
private static array $conversionMultipliers = [
|
||||
'Y' => ['multiplier' => 1E24, 'name' => 'yotta'],
|
||||
'Z' => ['multiplier' => 1E21, 'name' => 'zetta'],
|
||||
'E' => ['multiplier' => 1E18, 'name' => 'exa'],
|
||||
'P' => ['multiplier' => 1E15, 'name' => 'peta'],
|
||||
'T' => ['multiplier' => 1E12, 'name' => 'tera'],
|
||||
'G' => ['multiplier' => 1E9, 'name' => 'giga'],
|
||||
'M' => ['multiplier' => 1E6, 'name' => 'mega'],
|
||||
'k' => ['multiplier' => 1E3, 'name' => 'kilo'],
|
||||
'h' => ['multiplier' => 1E2, 'name' => 'hecto'],
|
||||
'e' => ['multiplier' => 1E1, 'name' => 'dekao'],
|
||||
'da' => ['multiplier' => 1E1, 'name' => 'dekao'],
|
||||
'd' => ['multiplier' => 1E-1, 'name' => 'deci'],
|
||||
'c' => ['multiplier' => 1E-2, 'name' => 'centi'],
|
||||
'm' => ['multiplier' => 1E-3, 'name' => 'milli'],
|
||||
'u' => ['multiplier' => 1E-6, 'name' => 'micro'],
|
||||
'n' => ['multiplier' => 1E-9, 'name' => 'nano'],
|
||||
'p' => ['multiplier' => 1E-12, 'name' => 'pico'],
|
||||
'f' => ['multiplier' => 1E-15, 'name' => 'femto'],
|
||||
'a' => ['multiplier' => 1E-18, 'name' => 'atto'],
|
||||
'z' => ['multiplier' => 1E-21, 'name' => 'zepto'],
|
||||
'y' => ['multiplier' => 1E-24, 'name' => 'yocto'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
|
||||
*
|
||||
* @var mixed[]
|
||||
*/
|
||||
private static array $binaryConversionMultipliers = [
|
||||
'Yi' => ['multiplier' => 2 ** 80, 'name' => 'yobi'],
|
||||
'Zi' => ['multiplier' => 2 ** 70, 'name' => 'zebi'],
|
||||
'Ei' => ['multiplier' => 2 ** 60, 'name' => 'exbi'],
|
||||
'Pi' => ['multiplier' => 2 ** 50, 'name' => 'pebi'],
|
||||
'Ti' => ['multiplier' => 2 ** 40, 'name' => 'tebi'],
|
||||
'Gi' => ['multiplier' => 2 ** 30, 'name' => 'gibi'],
|
||||
'Mi' => ['multiplier' => 2 ** 20, 'name' => 'mebi'],
|
||||
'ki' => ['multiplier' => 2 ** 10, 'name' => 'kibi'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Details of the Units of measure conversion factors, organised by group.
|
||||
*
|
||||
* @var mixed[]
|
||||
*/
|
||||
private static array $unitConversions = [
|
||||
// Conversion uses gram (g) as an intermediate unit
|
||||
self::CATEGORY_WEIGHT_AND_MASS => [
|
||||
'g' => 1.0,
|
||||
'sg' => 6.85217658567918E-05,
|
||||
'lbm' => 2.20462262184878E-03,
|
||||
'u' => 6.02214179421676E+23,
|
||||
'ozm' => 3.52739619495804E-02,
|
||||
'grain' => 1.54323583529414E+01,
|
||||
'cwt' => 2.20462262184878E-05,
|
||||
'shweight' => 2.20462262184878E-05,
|
||||
'uk_cwt' => 1.96841305522212E-05,
|
||||
'lcwt' => 1.96841305522212E-05,
|
||||
'hweight' => 1.96841305522212E-05,
|
||||
'stone' => 1.57473044417770E-04,
|
||||
'ton' => 1.10231131092439E-06,
|
||||
'uk_ton' => 9.84206527611061E-07,
|
||||
'LTON' => 9.84206527611061E-07,
|
||||
'brton' => 9.84206527611061E-07,
|
||||
],
|
||||
// Conversion uses meter (m) as an intermediate unit
|
||||
self::CATEGORY_DISTANCE => [
|
||||
'm' => 1.0,
|
||||
'mi' => 6.21371192237334E-04,
|
||||
'Nmi' => 5.39956803455724E-04,
|
||||
'in' => 3.93700787401575E+01,
|
||||
'ft' => 3.28083989501312E+00,
|
||||
'yd' => 1.09361329833771E+00,
|
||||
'ang' => 1.0E+10,
|
||||
'ell' => 8.74890638670166E-01,
|
||||
'ly' => 1.05700083402462E-16,
|
||||
'parsec' => 3.24077928966473E-17,
|
||||
'pc' => 3.24077928966473E-17,
|
||||
'Pica' => 2.83464566929134E+03,
|
||||
'Picapt' => 2.83464566929134E+03,
|
||||
'pica' => 2.36220472440945E+02,
|
||||
'survey_mi' => 6.21369949494950E-04,
|
||||
],
|
||||
// Conversion uses second (s) as an intermediate unit
|
||||
self::CATEGORY_TIME => [
|
||||
'yr' => 3.16880878140289E-08,
|
||||
'day' => 1.15740740740741E-05,
|
||||
'd' => 1.15740740740741E-05,
|
||||
'hr' => 2.77777777777778E-04,
|
||||
'mn' => 1.66666666666667E-02,
|
||||
'min' => 1.66666666666667E-02,
|
||||
'sec' => 1.0,
|
||||
's' => 1.0,
|
||||
],
|
||||
// Conversion uses Pascal (Pa) as an intermediate unit
|
||||
self::CATEGORY_PRESSURE => [
|
||||
'Pa' => 1.0,
|
||||
'p' => 1.0,
|
||||
'atm' => 9.86923266716013E-06,
|
||||
'at' => 9.86923266716013E-06,
|
||||
'mmHg' => 7.50063755419211E-03,
|
||||
'psi' => 1.45037737730209E-04,
|
||||
'Torr' => 7.50061682704170E-03,
|
||||
],
|
||||
// Conversion uses Newton (N) as an intermediate unit
|
||||
self::CATEGORY_FORCE => [
|
||||
'N' => 1.0,
|
||||
'dyn' => 1.0E+5,
|
||||
'dy' => 1.0E+5,
|
||||
'lbf' => 2.24808923655339E-01,
|
||||
'pond' => 1.01971621297793E+02,
|
||||
],
|
||||
// Conversion uses Joule (J) as an intermediate unit
|
||||
self::CATEGORY_ENERGY => [
|
||||
'J' => 1.0,
|
||||
'e' => 9.99999519343231E+06,
|
||||
'c' => 2.39006249473467E-01,
|
||||
'cal' => 2.38846190642017E-01,
|
||||
'eV' => 6.24145700000000E+18,
|
||||
'ev' => 6.24145700000000E+18,
|
||||
'HPh' => 3.72506430801000E-07,
|
||||
'hh' => 3.72506430801000E-07,
|
||||
'Wh' => 2.77777916238711E-04,
|
||||
'wh' => 2.77777916238711E-04,
|
||||
'flb' => 2.37304222192651E+01,
|
||||
'BTU' => 9.47815067349015E-04,
|
||||
'btu' => 9.47815067349015E-04,
|
||||
],
|
||||
// Conversion uses Horsepower (HP) as an intermediate unit
|
||||
self::CATEGORY_POWER => [
|
||||
'HP' => 1.0,
|
||||
'h' => 1.0,
|
||||
'W' => 7.45699871582270E+02,
|
||||
'w' => 7.45699871582270E+02,
|
||||
'PS' => 1.01386966542400E+00,
|
||||
],
|
||||
// Conversion uses Tesla (T) as an intermediate unit
|
||||
self::CATEGORY_MAGNETISM => [
|
||||
'T' => 1.0,
|
||||
'ga' => 10000.0,
|
||||
],
|
||||
// Conversion uses litre (l) as an intermediate unit
|
||||
self::CATEGORY_VOLUME => [
|
||||
'l' => 1.0,
|
||||
'L' => 1.0,
|
||||
'lt' => 1.0,
|
||||
'tsp' => 2.02884136211058E+02,
|
||||
'tspm' => 2.0E+02,
|
||||
'tbs' => 6.76280454036860E+01,
|
||||
'oz' => 3.38140227018430E+01,
|
||||
'cup' => 4.22675283773038E+00,
|
||||
'pt' => 2.11337641886519E+00,
|
||||
'us_pt' => 2.11337641886519E+00,
|
||||
'uk_pt' => 1.75975398639270E+00,
|
||||
'qt' => 1.05668820943259E+00,
|
||||
'uk_qt' => 8.79876993196351E-01,
|
||||
'gal' => 2.64172052358148E-01,
|
||||
'uk_gal' => 2.19969248299088E-01,
|
||||
'ang3' => 1.0E+27,
|
||||
'ang^3' => 1.0E+27,
|
||||
'barrel' => 6.28981077043211E-03,
|
||||
'bushel' => 2.83775932584017E-02,
|
||||
'in3' => 6.10237440947323E+01,
|
||||
'in^3' => 6.10237440947323E+01,
|
||||
'ft3' => 3.53146667214886E-02,
|
||||
'ft^3' => 3.53146667214886E-02,
|
||||
'ly3' => 1.18093498844171E-51,
|
||||
'ly^3' => 1.18093498844171E-51,
|
||||
'm3' => 1.0E-03,
|
||||
'm^3' => 1.0E-03,
|
||||
'mi3' => 2.39912758578928E-13,
|
||||
'mi^3' => 2.39912758578928E-13,
|
||||
'yd3' => 1.30795061931439E-03,
|
||||
'yd^3' => 1.30795061931439E-03,
|
||||
'Nmi3' => 1.57426214685811E-13,
|
||||
'Nmi^3' => 1.57426214685811E-13,
|
||||
'Pica3' => 2.27769904358706E+07,
|
||||
'Pica^3' => 2.27769904358706E+07,
|
||||
'Picapt3' => 2.27769904358706E+07,
|
||||
'Picapt^3' => 2.27769904358706E+07,
|
||||
'GRT' => 3.53146667214886E-04,
|
||||
'regton' => 3.53146667214886E-04,
|
||||
'MTON' => 8.82866668037215E-04,
|
||||
],
|
||||
// Conversion uses hectare (ha) as an intermediate unit
|
||||
self::CATEGORY_AREA => [
|
||||
'ha' => 1.0,
|
||||
'uk_acre' => 2.47105381467165E+00,
|
||||
'us_acre' => 2.47104393046628E+00,
|
||||
'ang2' => 1.0E+24,
|
||||
'ang^2' => 1.0E+24,
|
||||
'ar' => 1.0E+02,
|
||||
'ft2' => 1.07639104167097E+05,
|
||||
'ft^2' => 1.07639104167097E+05,
|
||||
'in2' => 1.55000310000620E+07,
|
||||
'in^2' => 1.55000310000620E+07,
|
||||
'ly2' => 1.11725076312873E-28,
|
||||
'ly^2' => 1.11725076312873E-28,
|
||||
'm2' => 1.0E+04,
|
||||
'm^2' => 1.0E+04,
|
||||
'Morgen' => 4.0E+00,
|
||||
'mi2' => 3.86102158542446E-03,
|
||||
'mi^2' => 3.86102158542446E-03,
|
||||
'Nmi2' => 2.91553349598123E-03,
|
||||
'Nmi^2' => 2.91553349598123E-03,
|
||||
'Pica2' => 8.03521607043214E+10,
|
||||
'Pica^2' => 8.03521607043214E+10,
|
||||
'Picapt2' => 8.03521607043214E+10,
|
||||
'Picapt^2' => 8.03521607043214E+10,
|
||||
'yd2' => 1.19599004630108E+04,
|
||||
'yd^2' => 1.19599004630108E+04,
|
||||
],
|
||||
// Conversion uses bit (bit) as an intermediate unit
|
||||
self::CATEGORY_INFORMATION => [
|
||||
'bit' => 1.0,
|
||||
'byte' => 0.125,
|
||||
],
|
||||
// Conversion uses Meters per Second (m/s) as an intermediate unit
|
||||
self::CATEGORY_SPEED => [
|
||||
'm/s' => 1.0,
|
||||
'm/sec' => 1.0,
|
||||
'm/h' => 3.60E+03,
|
||||
'm/hr' => 3.60E+03,
|
||||
'mph' => 2.23693629205440E+00,
|
||||
'admkn' => 1.94260256941567E+00,
|
||||
'kn' => 1.94384449244060E+00,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* getConversionGroups
|
||||
* Returns a list of the different conversion groups for UOM conversions.
|
||||
*/
|
||||
public static function getConversionCategories(): array
|
||||
{
|
||||
$conversionGroups = [];
|
||||
foreach (self::$conversionUnits as $conversionUnit) {
|
||||
$conversionGroups[] = $conversionUnit['Group'];
|
||||
}
|
||||
|
||||
return array_merge(array_unique($conversionGroups));
|
||||
}
|
||||
|
||||
/**
|
||||
* getConversionGroupUnits
|
||||
* Returns an array of units of measure, for a specified conversion group, or for all groups.
|
||||
*
|
||||
* @param ?string $category The group whose units of measure you want to retrieve
|
||||
*/
|
||||
public static function getConversionCategoryUnits(?string $category = null): array
|
||||
{
|
||||
$conversionGroups = [];
|
||||
foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
|
||||
if (($category === null) || ($conversionGroup['Group'] == $category)) {
|
||||
$conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
|
||||
}
|
||||
}
|
||||
|
||||
return $conversionGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* getConversionGroupUnitDetails.
|
||||
*
|
||||
* @param ?string $category The group whose units of measure you want to retrieve
|
||||
*/
|
||||
public static function getConversionCategoryUnitDetails(?string $category = null): array
|
||||
{
|
||||
$conversionGroups = [];
|
||||
foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
|
||||
if (($category === null) || ($conversionGroup['Group'] == $category)) {
|
||||
$conversionGroups[$conversionGroup['Group']][] = [
|
||||
'unit' => $conversionUnit,
|
||||
'description' => $conversionGroup['Unit Name'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $conversionGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* getConversionMultipliers
|
||||
* Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function getConversionMultipliers(): array
|
||||
{
|
||||
return self::$conversionMultipliers;
|
||||
}
|
||||
|
||||
/**
|
||||
* getBinaryConversionMultipliers
|
||||
* Returns an array of the additional Multiplier prefixes that can be used with Information Units of Measure in CONVERTUOM().
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public static function getBinaryConversionMultipliers(): array
|
||||
{
|
||||
return self::$binaryConversionMultipliers;
|
||||
}
|
||||
|
||||
/**
|
||||
* CONVERT.
|
||||
*
|
||||
* Converts a number from one measurement system to another.
|
||||
* For example, CONVERT can translate a table of distances in miles to a table of distances
|
||||
* in kilometers.
|
||||
*
|
||||
* Excel Function:
|
||||
* CONVERT(value,fromUOM,toUOM)
|
||||
*
|
||||
* @param array|float|int|string $value the value in fromUOM to convert
|
||||
* Or can be an array of values
|
||||
* @param array|string $fromUOM the units for value
|
||||
* Or can be an array of values
|
||||
* @param array|string $toUOM the units for the result
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string Result, or a string containing an error
|
||||
* If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function CONVERT($value, $fromUOM, $toUOM)
|
||||
{
|
||||
if (is_array($value) || is_array($fromUOM) || is_array($toUOM)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $fromUOM, $toUOM);
|
||||
}
|
||||
|
||||
if (!is_numeric($value)) {
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
try {
|
||||
[$fromUOM, $fromCategory, $fromMultiplier] = self::getUOMDetails($fromUOM);
|
||||
[$toUOM, $toCategory, $toMultiplier] = self::getUOMDetails($toUOM);
|
||||
} catch (Exception) {
|
||||
return ExcelError::NA();
|
||||
}
|
||||
|
||||
if ($fromCategory !== $toCategory) {
|
||||
return ExcelError::NA();
|
||||
}
|
||||
|
||||
// @var float $value
|
||||
$value *= $fromMultiplier;
|
||||
|
||||
if (($fromUOM === $toUOM) && ($fromMultiplier === $toMultiplier)) {
|
||||
// We've already factored $fromMultiplier into the value, so we need
|
||||
// to reverse it again
|
||||
return $value / $fromMultiplier;
|
||||
} elseif ($fromUOM === $toUOM) {
|
||||
return $value / $toMultiplier;
|
||||
} elseif ($fromCategory === self::CATEGORY_TEMPERATURE) {
|
||||
return self::convertTemperature($fromUOM, $toUOM, $value);
|
||||
}
|
||||
|
||||
$baseValue = $value * (1.0 / self::$unitConversions[$fromCategory][$fromUOM]);
|
||||
|
||||
return ($baseValue * self::$unitConversions[$fromCategory][$toUOM]) / $toMultiplier;
|
||||
}
|
||||
|
||||
private static function getUOMDetails(string $uom): array
|
||||
{
|
||||
if (isset(self::$conversionUnits[$uom])) {
|
||||
$unitCategory = self::$conversionUnits[$uom]['Group'];
|
||||
|
||||
return [$uom, $unitCategory, 1.0];
|
||||
}
|
||||
|
||||
// Check 1-character standard metric multiplier prefixes
|
||||
$multiplierType = substr($uom, 0, 1);
|
||||
$uom = substr($uom, 1);
|
||||
if (isset(self::$conversionUnits[$uom], self::$conversionMultipliers[$multiplierType])) {
|
||||
if (self::$conversionUnits[$uom]['AllowPrefix'] === false) {
|
||||
throw new Exception('Prefix not allowed for UoM');
|
||||
}
|
||||
$unitCategory = self::$conversionUnits[$uom]['Group'];
|
||||
|
||||
return [$uom, $unitCategory, self::$conversionMultipliers[$multiplierType]['multiplier']];
|
||||
}
|
||||
|
||||
$multiplierType .= substr($uom, 0, 1);
|
||||
$uom = substr($uom, 1);
|
||||
|
||||
// Check 2-character standard metric multiplier prefixes
|
||||
if (isset(self::$conversionUnits[$uom], self::$conversionMultipliers[$multiplierType])) {
|
||||
if (self::$conversionUnits[$uom]['AllowPrefix'] === false) {
|
||||
throw new Exception('Prefix not allowed for UoM');
|
||||
}
|
||||
$unitCategory = self::$conversionUnits[$uom]['Group'];
|
||||
|
||||
return [$uom, $unitCategory, self::$conversionMultipliers[$multiplierType]['multiplier']];
|
||||
}
|
||||
|
||||
// Check 2-character binary multiplier prefixes
|
||||
if (isset(self::$conversionUnits[$uom], self::$binaryConversionMultipliers[$multiplierType])) {
|
||||
if (self::$conversionUnits[$uom]['AllowPrefix'] === false) {
|
||||
throw new Exception('Prefix not allowed for UoM');
|
||||
}
|
||||
$unitCategory = self::$conversionUnits[$uom]['Group'];
|
||||
if ($unitCategory !== 'Information') {
|
||||
throw new Exception('Binary Prefix is only allowed for Information UoM');
|
||||
}
|
||||
|
||||
return [$uom, $unitCategory, self::$binaryConversionMultipliers[$multiplierType]['multiplier']];
|
||||
}
|
||||
|
||||
throw new Exception('UoM Not Found');
|
||||
}
|
||||
|
||||
protected static function convertTemperature(string $fromUOM, string $toUOM, float|int $value): float|int
|
||||
{
|
||||
$fromUOM = self::resolveTemperatureSynonyms($fromUOM);
|
||||
$toUOM = self::resolveTemperatureSynonyms($toUOM);
|
||||
|
||||
if ($fromUOM === $toUOM) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Convert to Kelvin
|
||||
switch ($fromUOM) {
|
||||
case 'F':
|
||||
$value = ($value - 32) / 1.8 + 273.15;
|
||||
|
||||
break;
|
||||
case 'C':
|
||||
$value += 273.15;
|
||||
|
||||
break;
|
||||
case 'Rank':
|
||||
$value /= 1.8;
|
||||
|
||||
break;
|
||||
case 'Reau':
|
||||
$value = $value * 1.25 + 273.15;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Convert from Kelvin
|
||||
switch ($toUOM) {
|
||||
case 'F':
|
||||
$value = ($value - 273.15) * 1.8 + 32.00;
|
||||
|
||||
break;
|
||||
case 'C':
|
||||
$value -= 273.15;
|
||||
|
||||
break;
|
||||
case 'Rank':
|
||||
$value *= 1.8;
|
||||
|
||||
break;
|
||||
case 'Reau':
|
||||
$value = ($value - 273.15) * 0.80000;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
private static function resolveTemperatureSynonyms(string $uom): string
|
||||
{
|
||||
return match ($uom) {
|
||||
'fah' => 'F',
|
||||
'cel' => 'C',
|
||||
'kel' => 'K',
|
||||
default => $uom,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class EngineeringValidations
|
||||
{
|
||||
public static function validateFloat(mixed $value): float
|
||||
{
|
||||
if (!is_numeric($value)) {
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
|
||||
return (float) $value;
|
||||
}
|
||||
|
||||
public static function validateInt(mixed $value): int
|
||||
{
|
||||
if (!is_numeric($value)) {
|
||||
throw new Exception(ExcelError::VALUE());
|
||||
}
|
||||
|
||||
return (int) floor((float) $value);
|
||||
}
|
||||
}
|
||||
109
lib/PhpSpreadsheet/Calculation/Engineering/Erf.php
Normal file
109
lib/PhpSpreadsheet/Calculation/Engineering/Erf.php
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class Erf
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
private const TWO_SQRT_PI = 1.128379167095512574;
|
||||
|
||||
/**
|
||||
* ERF.
|
||||
*
|
||||
* Returns the error function integrated between the lower and upper bound arguments.
|
||||
*
|
||||
* Note: In Excel 2007 or earlier, if you input a negative value for the upper or lower bound arguments,
|
||||
* the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
|
||||
* improved, so that it can now calculate the function for both positive and negative ranges.
|
||||
* PhpSpreadsheet follows Excel 2010 behaviour, and accepts negative arguments.
|
||||
*
|
||||
* Excel Function:
|
||||
* ERF(lower[,upper])
|
||||
*
|
||||
* @param mixed $lower Lower bound float for integrating ERF
|
||||
* Or can be an array of values
|
||||
* @param mixed $upper Upper bound float for integrating ERF.
|
||||
* If omitted, ERF integrates between zero and lower_limit
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function ERF(mixed $lower, mixed $upper = null): array|float|string
|
||||
{
|
||||
if (is_array($lower) || is_array($upper)) {
|
||||
return self::evaluateArrayArguments([self::class, __FUNCTION__], $lower, $upper);
|
||||
}
|
||||
|
||||
if (is_numeric($lower)) {
|
||||
if ($upper === null) {
|
||||
return self::erfValue($lower);
|
||||
}
|
||||
if (is_numeric($upper)) {
|
||||
return self::erfValue($upper) - self::erfValue($lower);
|
||||
}
|
||||
}
|
||||
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
/**
|
||||
* ERFPRECISE.
|
||||
*
|
||||
* Returns the error function integrated between the lower and upper bound arguments.
|
||||
*
|
||||
* Excel Function:
|
||||
* ERF.PRECISE(limit)
|
||||
*
|
||||
* @param mixed $limit Float bound for integrating ERF, other bound is zero
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function ERFPRECISE(mixed $limit)
|
||||
{
|
||||
if (is_array($limit)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $limit);
|
||||
}
|
||||
|
||||
return self::ERF($limit);
|
||||
}
|
||||
|
||||
private static function makeFloat(mixed $value): float
|
||||
{
|
||||
return is_numeric($value) ? ((float) $value) : 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to calculate the erf value.
|
||||
*/
|
||||
public static function erfValue(float|int|string $value): float
|
||||
{
|
||||
$value = (float) $value;
|
||||
if (abs($value) > 2.2) {
|
||||
return 1 - self::makeFloat(ErfC::ERFC($value));
|
||||
}
|
||||
$sum = $term = $value;
|
||||
$xsqr = ($value * $value);
|
||||
$j = 1;
|
||||
do {
|
||||
$term *= $xsqr / $j;
|
||||
$sum -= $term / (2 * $j + 1);
|
||||
++$j;
|
||||
$term *= $xsqr / $j;
|
||||
$sum += $term / (2 * $j + 1);
|
||||
++$j;
|
||||
if ($sum == 0.0) {
|
||||
break;
|
||||
}
|
||||
} while (abs($term / $sum) > Functions::PRECISION);
|
||||
|
||||
return self::TWO_SQRT_PI * $sum;
|
||||
}
|
||||
}
|
||||
77
lib/PhpSpreadsheet/Calculation/Engineering/ErfC.php
Normal file
77
lib/PhpSpreadsheet/Calculation/Engineering/ErfC.php
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\Engineering;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
|
||||
class ErfC
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
/**
|
||||
* ERFC.
|
||||
*
|
||||
* Returns the complementary ERF function integrated between x and infinity
|
||||
*
|
||||
* Note: In Excel 2007 or earlier, if you input a negative value for the lower bound argument,
|
||||
* the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
|
||||
* improved, so that it can now calculate the function for both positive and negative x values.
|
||||
* PhpSpreadsheet follows Excel 2010 behaviour, and accepts nagative arguments.
|
||||
*
|
||||
* Excel Function:
|
||||
* ERFC(x)
|
||||
*
|
||||
* @param mixed $value The float lower bound for integrating ERFC
|
||||
* Or can be an array of values
|
||||
*
|
||||
* @return array|float|string If an array of numbers is passed as an argument, then the returned result will also be an array
|
||||
* with the same dimensions
|
||||
*/
|
||||
public static function ERFC(mixed $value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return self::erfcValue($value);
|
||||
}
|
||||
|
||||
return ExcelError::VALUE();
|
||||
}
|
||||
|
||||
private const ONE_SQRT_PI = 0.564189583547756287;
|
||||
|
||||
/**
|
||||
* Method to calculate the erfc value.
|
||||
*/
|
||||
private static function erfcValue(float|int|string $value): float|int
|
||||
{
|
||||
$value = (float) $value;
|
||||
if (abs($value) < 2.2) {
|
||||
return 1 - Erf::erfValue($value);
|
||||
}
|
||||
if ($value < 0) {
|
||||
return 2 - self::erfcValue(-$value);
|
||||
}
|
||||
$a = $n = 1;
|
||||
$b = $c = $value;
|
||||
$d = ($value * $value) + 0.5;
|
||||
$q2 = $b / $d;
|
||||
do {
|
||||
$t = $a * $n + $b * $value;
|
||||
$a = $b;
|
||||
$b = $t;
|
||||
$t = $c * $n + $d * $value;
|
||||
$c = $d;
|
||||
$d = $t;
|
||||
$n += 0.5;
|
||||
$q1 = $q2;
|
||||
$q2 = $b / $d;
|
||||
} while ((abs($q1 - $q2) / $q2) > Functions::PRECISION);
|
||||
|
||||
return self::ONE_SQRT_PI * exp(-$value * $value) * $q2;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue