12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649 |
- <?php
- namespace PhpOffice\PhpSpreadsheet\Calculation;
- use Matrix\Exception as MatrixException;
- use Matrix\Matrix;
- class MathTrig
- {
- //
- // Private method to return an array of the factors of the input value
- //
- private static function factors($value)
- {
- $startVal = floor(sqrt($value));
- $factorArray = [];
- for ($i = $startVal; $i > 1; --$i) {
- if (($value % $i) == 0) {
- $factorArray = array_merge($factorArray, self::factors($value / $i));
- $factorArray = array_merge($factorArray, self::factors($i));
- if ($i <= sqrt($value)) {
- break;
- }
- }
- }
- if (!empty($factorArray)) {
- rsort($factorArray);
- return $factorArray;
- }
- return [(int) $value];
- }
- private static function romanCut($num, $n)
- {
- return ($num - ($num % $n)) / $n;
- }
- /**
- * ATAN2.
- *
- * This function calculates the arc tangent of the two variables x and y. It is similar to
- * calculating the arc tangent of y ÷ x, except that the signs of both arguments are used
- * to determine the quadrant of the result.
- * The arctangent is the angle from the x-axis to a line containing the origin (0, 0) and a
- * point with coordinates (xCoordinate, yCoordinate). The angle is given in radians between
- * -pi and pi, excluding -pi.
- *
- * Note that the Excel ATAN2() function accepts its arguments in the reverse order to the standard
- * PHP atan2() function, so we need to reverse them here before calling the PHP atan() function.
- *
- * Excel Function:
- * ATAN2(xCoordinate,yCoordinate)
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param float $xCoordinate the x-coordinate of the point
- * @param float $yCoordinate the y-coordinate of the point
- *
- * @return float the inverse tangent of the specified x- and y-coordinates
- */
- public static function ATAN2($xCoordinate = null, $yCoordinate = null)
- {
- $xCoordinate = Functions::flattenSingleValue($xCoordinate);
- $yCoordinate = Functions::flattenSingleValue($yCoordinate);
- $xCoordinate = ($xCoordinate !== null) ? $xCoordinate : 0.0;
- $yCoordinate = ($yCoordinate !== null) ? $yCoordinate : 0.0;
- if (((is_numeric($xCoordinate)) || (is_bool($xCoordinate))) &&
- ((is_numeric($yCoordinate))) || (is_bool($yCoordinate))) {
- $xCoordinate = (float) $xCoordinate;
- $yCoordinate = (float) $yCoordinate;
- if (($xCoordinate == 0) && ($yCoordinate == 0)) {
- return Functions::DIV0();
- }
- return atan2($yCoordinate, $xCoordinate);
- }
- return Functions::VALUE();
- }
- /**
- * CEILING.
- *
- * Returns number rounded up, away from zero, to the nearest multiple of significance.
- * For example, if you want to avoid using pennies in your prices and your product is
- * priced at $4.42, use the formula =CEILING(4.42,0.05) to round prices up to the
- * nearest nickel.
- *
- * Excel Function:
- * CEILING(number[,significance])
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param float $number the number you want to round
- * @param float $significance the multiple to which you want to round
- *
- * @return float Rounded Number
- */
- public static function CEILING($number, $significance = null)
- {
- $number = Functions::flattenSingleValue($number);
- $significance = Functions::flattenSingleValue($significance);
- if (($significance === null) &&
- (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)) {
- $significance = $number / abs($number);
- }
- if ((is_numeric($number)) && (is_numeric($significance))) {
- if (($number == 0.0) || ($significance == 0.0)) {
- return 0.0;
- } elseif (self::SIGN($number) == self::SIGN($significance)) {
- return ceil($number / $significance) * $significance;
- }
- return Functions::NAN();
- }
- return Functions::VALUE();
- }
- /**
- * COMBIN.
- *
- * Returns the number of combinations for a given number of items. Use COMBIN to
- * determine the total possible number of groups for a given number of items.
- *
- * Excel Function:
- * COMBIN(numObjs,numInSet)
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param int $numObjs Number of different objects
- * @param int $numInSet Number of objects in each combination
- *
- * @return int Number of combinations
- */
- public static function COMBIN($numObjs, $numInSet)
- {
- $numObjs = Functions::flattenSingleValue($numObjs);
- $numInSet = Functions::flattenSingleValue($numInSet);
- if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
- if ($numObjs < $numInSet) {
- return Functions::NAN();
- } elseif ($numInSet < 0) {
- return Functions::NAN();
- }
- return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet)) / self::FACT($numInSet);
- }
- return Functions::VALUE();
- }
- /**
- * EVEN.
- *
- * Returns number rounded up to the nearest even integer.
- * You can use this function for processing items that come in twos. For example,
- * a packing crate accepts rows of one or two items. The crate is full when
- * the number of items, rounded up to the nearest two, matches the crate's
- * capacity.
- *
- * Excel Function:
- * EVEN(number)
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param float $number Number to round
- *
- * @return int Rounded Number
- */
- public static function EVEN($number)
- {
- $number = Functions::flattenSingleValue($number);
- if ($number === null) {
- return 0;
- } elseif (is_bool($number)) {
- $number = (int) $number;
- }
- if (is_numeric($number)) {
- $significance = 2 * self::SIGN($number);
- return (int) self::CEILING($number, $significance);
- }
- return Functions::VALUE();
- }
- /**
- * FACT.
- *
- * Returns the factorial of a number.
- * The factorial of a number is equal to 1*2*3*...* number.
- *
- * Excel Function:
- * FACT(factVal)
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param float $factVal Factorial Value
- *
- * @return int Factorial
- */
- public static function FACT($factVal)
- {
- $factVal = Functions::flattenSingleValue($factVal);
- if (is_numeric($factVal)) {
- if ($factVal < 0) {
- return Functions::NAN();
- }
- $factLoop = floor($factVal);
- if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
- if ($factVal > $factLoop) {
- return Functions::NAN();
- }
- }
- $factorial = 1;
- while ($factLoop > 1) {
- $factorial *= $factLoop--;
- }
- return $factorial;
- }
- return Functions::VALUE();
- }
- /**
- * FACTDOUBLE.
- *
- * Returns the double factorial of a number.
- *
- * Excel Function:
- * FACTDOUBLE(factVal)
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param float $factVal Factorial Value
- *
- * @return int Double Factorial
- */
- public static function FACTDOUBLE($factVal)
- {
- $factLoop = Functions::flattenSingleValue($factVal);
- if (is_numeric($factLoop)) {
- $factLoop = floor($factLoop);
- if ($factVal < 0) {
- return Functions::NAN();
- }
- $factorial = 1;
- while ($factLoop > 1) {
- $factorial *= $factLoop--;
- --$factLoop;
- }
- return $factorial;
- }
- return Functions::VALUE();
- }
- /**
- * FLOOR.
- *
- * Rounds number down, toward zero, to the nearest multiple of significance.
- *
- * Excel Function:
- * FLOOR(number[,significance])
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param float $number Number to round
- * @param float $significance Significance
- *
- * @return float Rounded Number
- */
- public static function FLOOR($number, $significance = null)
- {
- $number = Functions::flattenSingleValue($number);
- $significance = Functions::flattenSingleValue($significance);
- if (($significance === null) &&
- (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)) {
- $significance = $number / abs($number);
- }
- if ((is_numeric($number)) && (is_numeric($significance))) {
- if ($significance == 0.0) {
- return Functions::DIV0();
- } elseif ($number == 0.0) {
- return 0.0;
- } elseif (self::SIGN($number) == self::SIGN($significance)) {
- return floor($number / $significance) * $significance;
- }
- return Functions::NAN();
- }
- return Functions::VALUE();
- }
- private static function evaluateGCD($a, $b)
- {
- return $b ? self::evaluateGCD($b, $a % $b) : $a;
- }
- /**
- * GCD.
- *
- * Returns the greatest common divisor of a series of numbers.
- * The greatest common divisor is the largest integer that divides both
- * number1 and number2 without a remainder.
- *
- * Excel Function:
- * GCD(number1[,number2[, ...]])
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param mixed ...$args Data values
- *
- * @return int Greatest Common Divisor
- */
- public static function GCD(...$args)
- {
- $args = Functions::flattenArray($args);
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $value) {
- if (!is_numeric($value)) {
- return Functions::VALUE();
- } elseif ($value < 0) {
- return Functions::NAN();
- }
- }
- $gcd = (int) array_pop($args);
- do {
- $gcd = self::evaluateGCD($gcd, (int) array_pop($args));
- } while (!empty($args));
- return $gcd;
- }
- /**
- * INT.
- *
- * Casts a floating point value to an integer
- *
- * Excel Function:
- * INT(number)
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param float $number Number to cast to an integer
- *
- * @return int Integer value
- */
- public static function INT($number)
- {
- $number = Functions::flattenSingleValue($number);
- if ($number === null) {
- return 0;
- } elseif (is_bool($number)) {
- return (int) $number;
- }
- if (is_numeric($number)) {
- return (int) floor($number);
- }
- return Functions::VALUE();
- }
- /**
- * LCM.
- *
- * Returns the lowest common multiplier of a series of numbers
- * The least common multiple is the smallest positive integer that is a multiple
- * of all integer arguments number1, number2, and so on. Use LCM to add fractions
- * with different denominators.
- *
- * Excel Function:
- * LCM(number1[,number2[, ...]])
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param mixed ...$args Data values
- *
- * @return int Lowest Common Multiplier
- */
- public static function LCM(...$args)
- {
- $returnValue = 1;
- $allPoweredFactors = [];
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $value) {
- if (!is_numeric($value)) {
- return Functions::VALUE();
- }
- if ($value == 0) {
- return 0;
- } elseif ($value < 0) {
- return Functions::NAN();
- }
- $myFactors = self::factors(floor($value));
- $myCountedFactors = array_count_values($myFactors);
- $myPoweredFactors = [];
- foreach ($myCountedFactors as $myCountedFactor => $myCountedPower) {
- $myPoweredFactors[$myCountedFactor] = pow($myCountedFactor, $myCountedPower);
- }
- foreach ($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
- if (isset($allPoweredFactors[$myPoweredValue])) {
- if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
- $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
- }
- } else {
- $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
- }
- }
- }
- foreach ($allPoweredFactors as $allPoweredFactor) {
- $returnValue *= (int) $allPoweredFactor;
- }
- return $returnValue;
- }
- /**
- * LOG_BASE.
- *
- * Returns the logarithm of a number to a specified base. The default base is 10.
- *
- * Excel Function:
- * LOG(number[,base])
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param float $number The positive real number for which you want the logarithm
- * @param float $base The base of the logarithm. If base is omitted, it is assumed to be 10.
- *
- * @return float
- */
- public static function logBase($number = null, $base = 10)
- {
- $number = Functions::flattenSingleValue($number);
- $base = ($base === null) ? 10 : (float) Functions::flattenSingleValue($base);
- if ((!is_numeric($base)) || (!is_numeric($number))) {
- return Functions::VALUE();
- }
- if (($base <= 0) || ($number <= 0)) {
- return Functions::NAN();
- }
- return log($number, $base);
- }
- /**
- * MDETERM.
- *
- * Returns the matrix determinant of an array.
- *
- * Excel Function:
- * MDETERM(array)
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param array $matrixValues A matrix of values
- *
- * @return float
- */
- public static function MDETERM($matrixValues)
- {
- $matrixData = [];
- if (!is_array($matrixValues)) {
- $matrixValues = [[$matrixValues]];
- }
- $row = $maxColumn = 0;
- foreach ($matrixValues as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $column = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((is_string($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixData[$row][$column] = $matrixCell;
- ++$column;
- }
- if ($column > $maxColumn) {
- $maxColumn = $column;
- }
- ++$row;
- }
- $matrix = new Matrix($matrixData);
- if (!$matrix->isSquare()) {
- return Functions::VALUE();
- }
- try {
- return $matrix->determinant();
- } catch (MatrixException $ex) {
- return Functions::VALUE();
- }
- }
- /**
- * MINVERSE.
- *
- * Returns the inverse matrix for the matrix stored in an array.
- *
- * Excel Function:
- * MINVERSE(array)
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param array $matrixValues A matrix of values
- *
- * @return array
- */
- public static function MINVERSE($matrixValues)
- {
- $matrixData = [];
- if (!is_array($matrixValues)) {
- $matrixValues = [[$matrixValues]];
- }
- $row = $maxColumn = 0;
- foreach ($matrixValues as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $column = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((is_string($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixData[$row][$column] = $matrixCell;
- ++$column;
- }
- if ($column > $maxColumn) {
- $maxColumn = $column;
- }
- ++$row;
- }
- $matrix = new Matrix($matrixData);
- if (!$matrix->isSquare()) {
- return Functions::VALUE();
- }
- if ($matrix->determinant() == 0.0) {
- return Functions::NAN();
- }
- try {
- return $matrix->inverse()->toArray();
- } catch (MatrixException $ex) {
- return Functions::VALUE();
- }
- }
- /**
- * MMULT.
- *
- * @param array $matrixData1 A matrix of values
- * @param array $matrixData2 A matrix of values
- *
- * @return array
- */
- public static function MMULT($matrixData1, $matrixData2)
- {
- $matrixAData = $matrixBData = [];
- if (!is_array($matrixData1)) {
- $matrixData1 = [[$matrixData1]];
- }
- if (!is_array($matrixData2)) {
- $matrixData2 = [[$matrixData2]];
- }
- try {
- $rowA = 0;
- foreach ($matrixData1 as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $columnA = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((!is_numeric($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixAData[$rowA][$columnA] = $matrixCell;
- ++$columnA;
- }
- ++$rowA;
- }
- $matrixA = new Matrix($matrixAData);
- $rowB = 0;
- foreach ($matrixData2 as $matrixRow) {
- if (!is_array($matrixRow)) {
- $matrixRow = [$matrixRow];
- }
- $columnB = 0;
- foreach ($matrixRow as $matrixCell) {
- if ((!is_numeric($matrixCell)) || ($matrixCell === null)) {
- return Functions::VALUE();
- }
- $matrixBData[$rowB][$columnB] = $matrixCell;
- ++$columnB;
- }
- ++$rowB;
- }
- $matrixB = new Matrix($matrixBData);
- if ($columnA != $rowB) {
- return Functions::VALUE();
- }
- return $matrixA->multiply($matrixB)->toArray();
- } catch (MatrixException $ex) {
- return Functions::VALUE();
- }
- }
- /**
- * MOD.
- *
- * @param int $a Dividend
- * @param int $b Divisor
- *
- * @return int Remainder
- */
- public static function MOD($a = 1, $b = 1)
- {
- $a = (float) Functions::flattenSingleValue($a);
- $b = (float) Functions::flattenSingleValue($b);
- if ($b == 0.0) {
- return Functions::DIV0();
- } elseif (($a < 0.0) && ($b > 0.0)) {
- return $b - fmod(abs($a), $b);
- } elseif (($a > 0.0) && ($b < 0.0)) {
- return $b + fmod($a, abs($b));
- }
- return fmod($a, $b);
- }
- /**
- * MROUND.
- *
- * Rounds a number to the nearest multiple of a specified value
- *
- * @param float $number Number to round
- * @param int $multiple Multiple to which you want to round $number
- *
- * @return float Rounded Number
- */
- public static function MROUND($number, $multiple)
- {
- $number = Functions::flattenSingleValue($number);
- $multiple = Functions::flattenSingleValue($multiple);
- if ((is_numeric($number)) && (is_numeric($multiple))) {
- if ($multiple == 0) {
- return 0;
- }
- if ((self::SIGN($number)) == (self::SIGN($multiple))) {
- $multiplier = 1 / $multiple;
- return round($number * $multiplier) / $multiplier;
- }
- return Functions::NAN();
- }
- return Functions::VALUE();
- }
- /**
- * MULTINOMIAL.
- *
- * Returns the ratio of the factorial of a sum of values to the product of factorials.
- *
- * @param array of mixed Data Series
- *
- * @return float
- */
- public static function MULTINOMIAL(...$args)
- {
- $summer = 0;
- $divisor = 1;
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if (is_numeric($arg)) {
- if ($arg < 1) {
- return Functions::NAN();
- }
- $summer += floor($arg);
- $divisor *= self::FACT($arg);
- } else {
- return Functions::VALUE();
- }
- }
- // Return
- if ($summer > 0) {
- $summer = self::FACT($summer);
- return $summer / $divisor;
- }
- return 0;
- }
- /**
- * ODD.
- *
- * Returns number rounded up to the nearest odd integer.
- *
- * @param float $number Number to round
- *
- * @return int Rounded Number
- */
- public static function ODD($number)
- {
- $number = Functions::flattenSingleValue($number);
- if ($number === null) {
- return 1;
- } elseif (is_bool($number)) {
- return 1;
- } elseif (is_numeric($number)) {
- $significance = self::SIGN($number);
- if ($significance == 0) {
- return 1;
- }
- $result = self::CEILING($number, $significance);
- if ($result == self::EVEN($result)) {
- $result += $significance;
- }
- return (int) $result;
- }
- return Functions::VALUE();
- }
- /**
- * POWER.
- *
- * Computes x raised to the power y.
- *
- * @param float $x
- * @param float $y
- *
- * @return float
- */
- public static function POWER($x = 0, $y = 2)
- {
- $x = Functions::flattenSingleValue($x);
- $y = Functions::flattenSingleValue($y);
- // Validate parameters
- if ($x == 0.0 && $y == 0.0) {
- return Functions::NAN();
- } elseif ($x == 0.0 && $y < 0.0) {
- return Functions::DIV0();
- }
- // Return
- $result = pow($x, $y);
- return (!is_nan($result) && !is_infinite($result)) ? $result : Functions::NAN();
- }
- /**
- * PRODUCT.
- *
- * PRODUCT returns the product of all the values and cells referenced in the argument list.
- *
- * Excel Function:
- * PRODUCT(value1[,value2[, ...]])
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param mixed ...$args Data values
- *
- * @return float
- */
- public static function PRODUCT(...$args)
- {
- // Return value
- $returnValue = null;
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = $arg;
- } else {
- $returnValue *= $arg;
- }
- }
- }
- // Return
- if ($returnValue === null) {
- return 0;
- }
- return $returnValue;
- }
- /**
- * QUOTIENT.
- *
- * QUOTIENT function returns the integer portion of a division. Numerator is the divided number
- * and denominator is the divisor.
- *
- * Excel Function:
- * QUOTIENT(value1[,value2[, ...]])
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param mixed ...$args Data values
- *
- * @return float
- */
- public static function QUOTIENT(...$args)
- {
- // Return value
- $returnValue = null;
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- if ($returnValue === null) {
- $returnValue = ($arg == 0) ? 0 : $arg;
- } else {
- if (($returnValue == 0) || ($arg == 0)) {
- $returnValue = 0;
- } else {
- $returnValue /= $arg;
- }
- }
- }
- }
- // Return
- return (int) $returnValue;
- }
- /**
- * RAND.
- *
- * @param int $min Minimal value
- * @param int $max Maximal value
- *
- * @return int Random number
- */
- public static function RAND($min = 0, $max = 0)
- {
- $min = Functions::flattenSingleValue($min);
- $max = Functions::flattenSingleValue($max);
- if ($min == 0 && $max == 0) {
- return (mt_rand(0, 10000000)) / 10000000;
- }
- return mt_rand($min, $max);
- }
- public static function ROMAN($aValue, $style = 0)
- {
- $aValue = Functions::flattenSingleValue($aValue);
- $style = ($style === null) ? 0 : (int) Functions::flattenSingleValue($style);
- if ((!is_numeric($aValue)) || ($aValue < 0) || ($aValue >= 4000)) {
- return Functions::VALUE();
- }
- $aValue = (int) $aValue;
- if ($aValue == 0) {
- return '';
- }
- $mill = ['', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM'];
- $cent = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'];
- $tens = ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'];
- $ones = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
- $roman = '';
- while ($aValue > 5999) {
- $roman .= 'M';
- $aValue -= 1000;
- }
- $m = self::romanCut($aValue, 1000);
- $aValue %= 1000;
- $c = self::romanCut($aValue, 100);
- $aValue %= 100;
- $t = self::romanCut($aValue, 10);
- $aValue %= 10;
- return $roman . $mill[$m] . $cent[$c] . $tens[$t] . $ones[$aValue];
- }
- /**
- * ROUNDUP.
- *
- * Rounds a number up to a specified number of decimal places
- *
- * @param float $number Number to round
- * @param int $digits Number of digits to which you want to round $number
- *
- * @return float Rounded Number
- */
- public static function ROUNDUP($number, $digits)
- {
- $number = Functions::flattenSingleValue($number);
- $digits = Functions::flattenSingleValue($digits);
- if ((is_numeric($number)) && (is_numeric($digits))) {
- $significance = pow(10, (int) $digits);
- if ($number < 0.0) {
- return floor($number * $significance) / $significance;
- }
- return ceil($number * $significance) / $significance;
- }
- return Functions::VALUE();
- }
- /**
- * ROUNDDOWN.
- *
- * Rounds a number down to a specified number of decimal places
- *
- * @param float $number Number to round
- * @param int $digits Number of digits to which you want to round $number
- *
- * @return float Rounded Number
- */
- public static function ROUNDDOWN($number, $digits)
- {
- $number = Functions::flattenSingleValue($number);
- $digits = Functions::flattenSingleValue($digits);
- if ((is_numeric($number)) && (is_numeric($digits))) {
- $significance = pow(10, (int) $digits);
- if ($number < 0.0) {
- return ceil($number * $significance) / $significance;
- }
- return floor($number * $significance) / $significance;
- }
- return Functions::VALUE();
- }
- /**
- * SERIESSUM.
- *
- * Returns the sum of a power series
- *
- * @param float $x Input value to the power series
- * @param float $n Initial power to which you want to raise $x
- * @param float $m Step by which to increase $n for each term in the series
- * @param array of mixed Data Series
- *
- * @return float
- */
- public static function SERIESSUM(...$args)
- {
- $returnValue = 0;
- // Loop through arguments
- $aArgs = Functions::flattenArray($args);
- $x = array_shift($aArgs);
- $n = array_shift($aArgs);
- $m = array_shift($aArgs);
- if ((is_numeric($x)) && (is_numeric($n)) && (is_numeric($m))) {
- // Calculate
- $i = 0;
- foreach ($aArgs as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $returnValue += $arg * pow($x, $n + ($m * $i++));
- } else {
- return Functions::VALUE();
- }
- }
- return $returnValue;
- }
- return Functions::VALUE();
- }
- /**
- * SIGN.
- *
- * Determines the sign of a number. Returns 1 if the number is positive, zero (0)
- * if the number is 0, and -1 if the number is negative.
- *
- * @param float $number Number to round
- *
- * @return int sign value
- */
- public static function SIGN($number)
- {
- $number = Functions::flattenSingleValue($number);
- if (is_bool($number)) {
- return (int) $number;
- }
- if (is_numeric($number)) {
- if ($number == 0.0) {
- return 0;
- }
- return $number / abs($number);
- }
- return Functions::VALUE();
- }
- /**
- * SQRTPI.
- *
- * Returns the square root of (number * pi).
- *
- * @param float $number Number
- *
- * @return float Square Root of Number * Pi
- */
- public static function SQRTPI($number)
- {
- $number = Functions::flattenSingleValue($number);
- if (is_numeric($number)) {
- if ($number < 0) {
- return Functions::NAN();
- }
- return sqrt($number * M_PI);
- }
- return Functions::VALUE();
- }
- protected static function filterHiddenArgs($cellReference, $args)
- {
- return array_filter(
- $args,
- function ($index) use ($cellReference) {
- list(, $row, $column) = explode('.', $index);
- return $cellReference->getWorksheet()->getRowDimension($row)->getVisible() &&
- $cellReference->getWorksheet()->getColumnDimension($column)->getVisible();
- },
- ARRAY_FILTER_USE_KEY
- );
- }
- protected static function filterFormulaArgs($cellReference, $args)
- {
- return array_filter(
- $args,
- function ($index) use ($cellReference) {
- list(, $row, $column) = explode('.', $index);
- if ($cellReference->getWorksheet()->cellExists($column . $row)) {
- //take this cell out if it contains the SUBTOTAL or AGGREGATE functions in a formula
- $isFormula = $cellReference->getWorksheet()->getCell($column . $row)->isFormula();
- $cellFormula = !preg_match('/^=.*\b(SUBTOTAL|AGGREGATE)\s*\(/i', $cellReference->getWorksheet()->getCell($column . $row)->getValue());
- return !$isFormula || $cellFormula;
- }
- return true;
- },
- ARRAY_FILTER_USE_KEY
- );
- }
- /**
- * SUBTOTAL.
- *
- * Returns a subtotal in a list or database.
- *
- * @param int the number 1 to 11 that specifies which function to
- * use in calculating subtotals within a range
- * list
- * Numbers 101 to 111 shadow the functions of 1 to 11
- * but ignore any values in the range that are
- * in hidden rows or columns
- * @param array of mixed Data Series
- *
- * @return float
- */
- public static function SUBTOTAL(...$args)
- {
- $cellReference = array_pop($args);
- $aArgs = Functions::flattenArrayIndexed($args);
- $subtotal = array_shift($aArgs);
- // Calculate
- if ((is_numeric($subtotal)) && (!is_string($subtotal))) {
- if ($subtotal > 100) {
- $aArgs = self::filterHiddenArgs($cellReference, $aArgs);
- $subtotal -= 100;
- }
- $aArgs = self::filterFormulaArgs($cellReference, $aArgs);
- switch ($subtotal) {
- case 1:
- return Statistical::AVERAGE($aArgs);
- case 2:
- return Statistical::COUNT($aArgs);
- case 3:
- return Statistical::COUNTA($aArgs);
- case 4:
- return Statistical::MAX($aArgs);
- case 5:
- return Statistical::MIN($aArgs);
- case 6:
- return self::PRODUCT($aArgs);
- case 7:
- return Statistical::STDEV($aArgs);
- case 8:
- return Statistical::STDEVP($aArgs);
- case 9:
- return self::SUM($aArgs);
- case 10:
- return Statistical::VARFunc($aArgs);
- case 11:
- return Statistical::VARP($aArgs);
- }
- }
- return Functions::VALUE();
- }
- /**
- * SUM.
- *
- * SUM computes the sum of all the values and cells referenced in the argument list.
- *
- * Excel Function:
- * SUM(value1[,value2[, ...]])
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param mixed ...$args Data values
- *
- * @return float
- */
- public static function SUM(...$args)
- {
- $returnValue = 0;
- // Loop through the arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $returnValue += $arg;
- }
- }
- return $returnValue;
- }
- /**
- * SUMIF.
- *
- * Counts the number of cells that contain numbers within the list of arguments
- *
- * Excel Function:
- * SUMIF(value1[,value2[, ...]],condition)
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param mixed $aArgs Data values
- * @param string $condition the criteria that defines which cells will be summed
- * @param mixed $sumArgs
- *
- * @return float
- */
- public static function SUMIF($aArgs, $condition, $sumArgs = [])
- {
- $returnValue = 0;
- $aArgs = Functions::flattenArray($aArgs);
- $sumArgs = Functions::flattenArray($sumArgs);
- if (empty($sumArgs)) {
- $sumArgs = $aArgs;
- }
- $condition = Functions::ifCondition($condition);
- // Loop through arguments
- foreach ($aArgs as $key => $arg) {
- if (!is_numeric($arg)) {
- $arg = str_replace('"', '""', $arg);
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
- $testCondition = '=' . $arg . $condition;
- if (is_numeric($sumArgs[$key]) &&
- Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is it a value within our criteria and only numeric can be added to the result
- $returnValue += $sumArgs[$key];
- }
- }
- return $returnValue;
- }
- /**
- * SUMIFS.
- *
- * Counts the number of cells that contain numbers within the list of arguments
- *
- * Excel Function:
- * SUMIFS(value1[,value2[, ...]],condition)
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param mixed $args Data values
- * @param string $condition the criteria that defines which cells will be summed
- *
- * @return float
- */
- public static function SUMIFS(...$args)
- {
- $arrayList = $args;
- // Return value
- $returnValue = 0;
- $sumArgs = Functions::flattenArray(array_shift($arrayList));
- $aArgsArray = [];
- $conditions = [];
- while (count($arrayList) > 0) {
- $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
- $conditions[] = Functions::ifCondition(array_shift($arrayList));
- }
- // Loop through each sum and see if arguments and conditions are true
- foreach ($sumArgs as $index => $value) {
- $valid = true;
- foreach ($conditions as $cidx => $condition) {
- $arg = $aArgsArray[$cidx][$index];
- // Loop through arguments
- if (!is_numeric($arg)) {
- $arg = Calculation::wrapResult(strtoupper($arg));
- }
- $testCondition = '=' . $arg . $condition;
- if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
- // Is not a value within our criteria
- $valid = false;
- break; // if false found, don't need to check other conditions
- }
- }
- if ($valid) {
- $returnValue += $value;
- }
- }
- // Return
- return $returnValue;
- }
- /**
- * SUMPRODUCT.
- *
- * Excel Function:
- * SUMPRODUCT(value1[,value2[, ...]])
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param mixed ...$args Data values
- *
- * @return float
- */
- public static function SUMPRODUCT(...$args)
- {
- $arrayList = $args;
- $wrkArray = Functions::flattenArray(array_shift($arrayList));
- $wrkCellCount = count($wrkArray);
- for ($i = 0; $i < $wrkCellCount; ++$i) {
- if ((!is_numeric($wrkArray[$i])) || (is_string($wrkArray[$i]))) {
- $wrkArray[$i] = 0;
- }
- }
- foreach ($arrayList as $matrixData) {
- $array2 = Functions::flattenArray($matrixData);
- $count = count($array2);
- if ($wrkCellCount != $count) {
- return Functions::VALUE();
- }
- foreach ($array2 as $i => $val) {
- if ((!is_numeric($val)) || (is_string($val))) {
- $val = 0;
- }
- $wrkArray[$i] *= $val;
- }
- }
- return array_sum($wrkArray);
- }
- /**
- * SUMSQ.
- *
- * SUMSQ returns the sum of the squares of the arguments
- *
- * Excel Function:
- * SUMSQ(value1[,value2[, ...]])
- *
- * @category Mathematical and Trigonometric Functions
- *
- * @param mixed ...$args Data values
- *
- * @return float
- */
- public static function SUMSQ(...$args)
- {
- $returnValue = 0;
- // Loop through arguments
- foreach (Functions::flattenArray($args) as $arg) {
- // Is it a numeric value?
- if ((is_numeric($arg)) && (!is_string($arg))) {
- $returnValue += ($arg * $arg);
- }
- }
- return $returnValue;
- }
- /**
- * SUMX2MY2.
- *
- * @param mixed[] $matrixData1 Matrix #1
- * @param mixed[] $matrixData2 Matrix #2
- *
- * @return float
- */
- public static function SUMX2MY2($matrixData1, $matrixData2)
- {
- $array1 = Functions::flattenArray($matrixData1);
- $array2 = Functions::flattenArray($matrixData2);
- $count = min(count($array1), count($array2));
- $result = 0;
- for ($i = 0; $i < $count; ++$i) {
- if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
- ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
- $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
- }
- }
- return $result;
- }
- /**
- * SUMX2PY2.
- *
- * @param mixed[] $matrixData1 Matrix #1
- * @param mixed[] $matrixData2 Matrix #2
- *
- * @return float
- */
- public static function SUMX2PY2($matrixData1, $matrixData2)
- {
- $array1 = Functions::flattenArray($matrixData1);
- $array2 = Functions::flattenArray($matrixData2);
- $count = min(count($array1), count($array2));
- $result = 0;
- for ($i = 0; $i < $count; ++$i) {
- if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
- ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
- $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
- }
- }
- return $result;
- }
- /**
- * SUMXMY2.
- *
- * @param mixed[] $matrixData1 Matrix #1
- * @param mixed[] $matrixData2 Matrix #2
- *
- * @return float
- */
- public static function SUMXMY2($matrixData1, $matrixData2)
- {
- $array1 = Functions::flattenArray($matrixData1);
- $array2 = Functions::flattenArray($matrixData2);
- $count = min(count($array1), count($array2));
- $result = 0;
- for ($i = 0; $i < $count; ++$i) {
- if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
- ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
- $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
- }
- }
- return $result;
- }
- /**
- * TRUNC.
- *
- * Truncates value to the number of fractional digits by number_digits.
- *
- * @param float $value
- * @param int $digits
- *
- * @return float Truncated value
- */
- public static function TRUNC($value = 0, $digits = 0)
- {
- $value = Functions::flattenSingleValue($value);
- $digits = Functions::flattenSingleValue($digits);
- // Validate parameters
- if ((!is_numeric($value)) || (!is_numeric($digits))) {
- return Functions::VALUE();
- }
- $digits = floor($digits);
- // Truncate
- $adjust = pow(10, $digits);
- if (($digits > 0) && (rtrim((int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) {
- return $value;
- }
- return ((int) ($value * $adjust)) / $adjust;
- }
- /**
- * SEC.
- *
- * Returns the secant of an angle.
- *
- * @param float $angle Number
- *
- * @return float|string The secant of the angle
- */
- public static function SEC($angle)
- {
- $angle = Functions::flattenSingleValue($angle);
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
- $result = cos($angle);
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
- }
- /**
- * SECH.
- *
- * Returns the hyperbolic secant of an angle.
- *
- * @param float $angle Number
- *
- * @return float|string The hyperbolic secant of the angle
- */
- public static function SECH($angle)
- {
- $angle = Functions::flattenSingleValue($angle);
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
- $result = cosh($angle);
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
- }
- /**
- * CSC.
- *
- * Returns the cosecant of an angle.
- *
- * @param float $angle Number
- *
- * @return float|string The cosecant of the angle
- */
- public static function CSC($angle)
- {
- $angle = Functions::flattenSingleValue($angle);
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
- $result = sin($angle);
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
- }
- /**
- * CSCH.
- *
- * Returns the hyperbolic cosecant of an angle.
- *
- * @param float $angle Number
- *
- * @return float|string The hyperbolic cosecant of the angle
- */
- public static function CSCH($angle)
- {
- $angle = Functions::flattenSingleValue($angle);
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
- $result = sinh($angle);
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
- }
- /**
- * COT.
- *
- * Returns the cotangent of an angle.
- *
- * @param float $angle Number
- *
- * @return float|string The cotangent of the angle
- */
- public static function COT($angle)
- {
- $angle = Functions::flattenSingleValue($angle);
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
- $result = tan($angle);
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
- }
- /**
- * COTH.
- *
- * Returns the hyperbolic cotangent of an angle.
- *
- * @param float $angle Number
- *
- * @return float|string The hyperbolic cotangent of the angle
- */
- public static function COTH($angle)
- {
- $angle = Functions::flattenSingleValue($angle);
- if (!is_numeric($angle)) {
- return Functions::VALUE();
- }
- $result = tanh($angle);
- return ($result == 0.0) ? Functions::DIV0() : 1 / $result;
- }
- /**
- * ACOT.
- *
- * Returns the arccotangent of a number.
- *
- * @param float $number Number
- *
- * @return float|string The arccotangent of the number
- */
- public static function ACOT($number)
- {
- $number = Functions::flattenSingleValue($number);
- if (!is_numeric($number)) {
- return Functions::VALUE();
- }
- return (M_PI / 2) - atan($number);
- }
- /**
- * ACOTH.
- *
- * Returns the hyperbolic arccotangent of a number.
- *
- * @param float $number Number
- *
- * @return float|string The hyperbolic arccotangent of the number
- */
- public static function ACOTH($number)
- {
- $number = Functions::flattenSingleValue($number);
- if (!is_numeric($number)) {
- return Functions::VALUE();
- }
- $result = log(($number + 1) / ($number - 1)) / 2;
- return is_nan($result) ? Functions::NAN() : $result;
- }
- }
|