PageRenderTime 41ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/branches/v1.6.1/Classes/PHPExcel/Calculation/Functions.php

#
PHP | 5602 lines | 3313 code | 482 blank | 1807 comment | 1019 complexity | ba2d4a9164f0f3ed843fd40332fa5f29 MD5 | raw file
Possible License(s): AGPL-1.0, LGPL-2.0, LGPL-2.1, GPL-3.0, LGPL-3.0
  1. <?php
  2. /**
  3. * PHPExcel
  4. *
  5. * Copyright (c) 2006 - 2008 PHPExcel
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * @category PHPExcel
  22. * @package PHPExcel_Calculation
  23. * @copyright Copyright (c) 2006 - 2008 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version ##VERSION##, ##DATE##
  26. */
  27. define('EPS', 2.22e-16);
  28. define('MAX_VALUE', 1.2e308);
  29. define('LOG_GAMMA_X_MAX_VALUE', 2.55e305);
  30. define('SQRT2PI', 2.5066282746310005024157652848110452530069867406099);
  31. define('XMININ', 2.23e-308);
  32. define('MAX_ITERATIONS', 150);
  33. define('PRECISION', 8.88E-016);
  34. /** PHPExcel_Cell */
  35. require_once 'PHPExcel/Cell.php';
  36. /** PHPExcel_Cell_DataType */
  37. require_once 'PHPExcel/Cell/DataType.php';
  38. /** PHPExcel_Shared_Date */
  39. require_once 'PHPExcel/Shared/Date.php';
  40. /**
  41. * PHPExcel_Calculation_Functions
  42. *
  43. * @category PHPExcel
  44. * @package PHPExcel_Calculation
  45. * @copyright Copyright (c) 2006 - 2008 PHPExcel (http://www.codeplex.com/PHPExcel)
  46. */
  47. class PHPExcel_Calculation_Functions {
  48. /** constants */
  49. const COMPATIBILITY_EXCEL = 'Excel';
  50. const COMPATIBILITY_GNUMERIC = 'Gnumeric';
  51. const COMPATIBILITY_OPENOFFICE = 'OpenOfficeCalc';
  52. /**
  53. * Compatibility mode to use for error checking and responses
  54. *
  55. * @var string
  56. */
  57. private static $compatibilityMode = self::COMPATIBILITY_EXCEL;
  58. /**
  59. * List of error codes
  60. *
  61. * @var array
  62. */
  63. private static $_errorCodes = array( 'null' => '#NULL!',
  64. 'divisionbyzero' => '#DIV/0!',
  65. 'value' => '#VALUE!',
  66. 'reference' => '#REF!',
  67. 'name' => '#NAME?',
  68. 'num' => '#NUM!',
  69. 'na' => '#N/A'
  70. );
  71. /**
  72. * Set the Compatibility Mode
  73. *
  74. * @param string $compatibilityMode Compatibility Mode
  75. * @return boolean (Success or Failure)
  76. */
  77. public static function setCompatibilityMode($compatibilityMode) {
  78. if (($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
  79. ($compatibilityMode == self::COMPATIBILITY_GNUMERIC) ||
  80. ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  81. self::$compatibilityMode = $compatibilityMode;
  82. return True;
  83. }
  84. return False;
  85. }
  86. /**
  87. * Return the Compatibility Mode
  88. *
  89. * @return string $compatibilityMode Compatibility Mode
  90. */
  91. public static function getCompatibilityMode() {
  92. return self::$compatibilityMode;
  93. }
  94. /**
  95. * DUMMY
  96. *
  97. * @return string #NAME?
  98. */
  99. public static function DUMMY() {
  100. return self::$_errorCodes['name'];
  101. }
  102. /**
  103. * NA
  104. *
  105. * @return string #N/A!
  106. */
  107. public static function NA() {
  108. return self::$_errorCodes['na'];
  109. }
  110. /**
  111. * LOGICAL_AND
  112. *
  113. * Returns boolean TRUE if all its arguments are TRUE; returns FALSE if one or more argument is FALSE.
  114. *
  115. * Booleans arguments are treated as True or False as appropriate
  116. * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
  117. * If any argument value is a string, or a Null, it is ignored
  118. *
  119. * Quirk of Excel:
  120. * String values passed directly to the function rather than through a cell reference
  121. * e.g.=AND(1,"A",1)
  122. * will return a #VALUE! error, _not_ ignoring the string.
  123. * This behaviour is not replicated
  124. *
  125. * @param array of mixed Data Series
  126. * @return boolean
  127. */
  128. public static function LOGICAL_AND() {
  129. // Return value
  130. $returnValue = True;
  131. // Loop through the arguments
  132. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  133. $argCount = 0;
  134. foreach ($aArgs as $arg) {
  135. // Is it a boolean value?
  136. if (is_bool($arg)) {
  137. $returnValue = $returnValue && $arg;
  138. $argCount++;
  139. } elseif ((is_numeric($arg)) && (!is_string($arg))) {
  140. $returnValue = $returnValue && ($arg != 0);
  141. $argCount++;
  142. }
  143. }
  144. // Return
  145. if ($argCount == 0) {
  146. return self::$_errorCodes['value'];
  147. }
  148. return $returnValue;
  149. }
  150. /**
  151. * LOGICAL_OR
  152. *
  153. * Returns boolean TRUE if any argument is TRUE; returns FALSE if all arguments are FALSE.
  154. *
  155. * Booleans arguments are treated as True or False as appropriate
  156. * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
  157. * If any argument value is a string, or a Null, it is ignored
  158. *
  159. * @param array of mixed Data Series
  160. * @return boolean
  161. */
  162. public static function LOGICAL_OR() {
  163. // Return value
  164. $returnValue = False;
  165. // Loop through the arguments
  166. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  167. $argCount = 0;
  168. foreach ($aArgs as $arg) {
  169. // Is it a boolean value?
  170. if (is_bool($arg)) {
  171. $returnValue = $returnValue || $arg;
  172. $argCount++;
  173. } elseif ((is_numeric($arg)) && (!is_string($arg))) {
  174. $returnValue = $returnValue || ($arg != 0);
  175. $argCount++;
  176. }
  177. }
  178. // Return
  179. if ($argCount == 0) {
  180. return self::$_errorCodes['value'];
  181. }
  182. return $returnValue;
  183. }
  184. /**
  185. * LOGICAL_FALSE
  186. *
  187. * Returns FALSE.
  188. *
  189. * @return boolean
  190. */
  191. public static function LOGICAL_FALSE() {
  192. return False;
  193. }
  194. /**
  195. * LOGICAL_TRUE
  196. *
  197. * Returns TRUE.
  198. *
  199. * @return boolean
  200. */
  201. public static function LOGICAL_TRUE() {
  202. return True;
  203. }
  204. /**
  205. * SUM
  206. *
  207. * SUM computes the sum of all the values and cells referenced in the argument list.
  208. *
  209. * @param array of mixed Data Series
  210. * @return float
  211. */
  212. public static function SUM() {
  213. // Return value
  214. $returnValue = 0;
  215. // Loop through the arguments
  216. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  217. foreach ($aArgs as $arg) {
  218. // Is it a numeric value?
  219. if ((is_numeric($arg)) && (!is_string($arg))) {
  220. $returnValue += $arg;
  221. }
  222. }
  223. // Return
  224. return $returnValue;
  225. }
  226. /**
  227. * SUMSQ
  228. *
  229. * Returns the sum of the squares of the arguments
  230. *
  231. * @param array of mixed Data Series
  232. * @return float
  233. */
  234. public static function SUMSQ() {
  235. // Return value
  236. $returnValue = 0;
  237. // Loop trough arguments
  238. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  239. foreach ($aArgs as $arg) {
  240. // Is it a numeric value?
  241. if ((is_numeric($arg)) && (!is_string($arg))) {
  242. $returnValue += pow($arg,2);
  243. }
  244. }
  245. // Return
  246. return $returnValue;
  247. }
  248. /**
  249. * PRODUCT
  250. *
  251. * PRODUCT returns the product of all the values and cells referenced in the argument list.
  252. *
  253. * @param array of mixed Data Series
  254. * @return float
  255. */
  256. public static function PRODUCT() {
  257. // Return value
  258. $returnValue = null;
  259. // Loop trough arguments
  260. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  261. foreach ($aArgs as $arg) {
  262. // Is it a numeric value?
  263. if ((is_numeric($arg)) && (!is_string($arg))) {
  264. if (is_null($returnValue)) {
  265. $returnValue = $arg;
  266. } else {
  267. $returnValue *= $arg;
  268. }
  269. }
  270. }
  271. // Return
  272. if (is_null($returnValue)) {
  273. return 0;
  274. }
  275. return $returnValue;
  276. }
  277. /**
  278. * QUOTIENT
  279. *
  280. * QUOTIENT function returns the integer portion of a division.numerator is the divided number
  281. * and denominator is the divisor.
  282. *
  283. * @param array of mixed Data Series
  284. * @return float
  285. */
  286. public static function QUOTIENT() {
  287. // Return value
  288. $returnValue = null;
  289. // Loop trough arguments
  290. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  291. foreach ($aArgs as $arg) {
  292. // Is it a numeric value?
  293. if ((is_numeric($arg)) && (!is_string($arg))) {
  294. if (is_null($returnValue)) {
  295. if (($returnValue == 0) || ($arg == 0)) {
  296. $returnValue = 0;
  297. } else {
  298. $returnValue = $arg;
  299. }
  300. } else {
  301. if (($returnValue == 0) || ($arg == 0)) {
  302. $returnValue = 0;
  303. } else {
  304. $returnValue /= $arg;
  305. }
  306. }
  307. }
  308. }
  309. // Return
  310. return intval($returnValue);
  311. }
  312. /**
  313. * MIN
  314. *
  315. * MIN returns the value of the element of the values passed that has the smallest value,
  316. * with negative numbers considered smaller than positive numbers.
  317. *
  318. * @param array of mixed Data Series
  319. * @return float
  320. */
  321. public static function MIN() {
  322. // Return value
  323. $returnValue = null;
  324. // Loop trough arguments
  325. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  326. foreach ($aArgs as $arg) {
  327. // Is it a numeric value?
  328. if ((is_numeric($arg)) && (!is_string($arg))) {
  329. if ((is_null($returnValue)) || ($arg < $returnValue)) {
  330. $returnValue = $arg;
  331. }
  332. }
  333. }
  334. // Return
  335. if(is_null($returnValue)) {
  336. return 0;
  337. }
  338. return $returnValue;
  339. }
  340. /**
  341. * MINA
  342. *
  343. * Returns the smallest value in a list of arguments, including numbers, text, and logical values
  344. *
  345. * @param array of mixed Data Series
  346. * @return float
  347. */
  348. public static function MINA() {
  349. // Return value
  350. $returnValue = null;
  351. // Loop through arguments
  352. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  353. foreach ($aArgs as $arg) {
  354. // Is it a numeric value?
  355. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
  356. if (is_bool($arg)) {
  357. $arg = (integer) $arg;
  358. } elseif (is_string($arg)) {
  359. $arg = 0;
  360. }
  361. if ((is_null($returnValue)) || ($arg < $returnValue)) {
  362. $returnValue = $arg;
  363. }
  364. }
  365. }
  366. // Return
  367. if(is_null($returnValue)) {
  368. return 0;
  369. }
  370. return $returnValue;
  371. }
  372. /**
  373. * SMALL
  374. *
  375. * Returns the nth smallest value in a data set. You can use this function to
  376. * select a value based on its relative standing.
  377. *
  378. * @param array of mixed Data Series
  379. * @param float Entry in the series to return
  380. * @return float
  381. */
  382. public static function SMALL() {
  383. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  384. // Calculate
  385. $n = array_pop($aArgs);
  386. if ((is_numeric($n)) && (!is_string($n))) {
  387. $mArgs = array();
  388. foreach ($aArgs as $arg) {
  389. // Is it a numeric value?
  390. if ((is_numeric($arg)) && (!is_string($arg))) {
  391. $mArgs[] = $arg;
  392. }
  393. }
  394. $count = self::COUNT($mArgs);
  395. $n = floor(--$n);
  396. if (($n < 0) || ($n >= $count) || ($count == 0)) {
  397. return self::$_errorCodes['num'];
  398. }
  399. sort($mArgs);
  400. return $mArgs[$n];
  401. }
  402. return self::$_errorCodes['value'];
  403. }
  404. /**
  405. * MAX
  406. *
  407. * MAX returns the value of the element of the values passed that has the highest value,
  408. * with negative numbers considered smaller than positive numbers.
  409. *
  410. * @param array of mixed Data Series
  411. * @return float
  412. */
  413. public static function MAX() {
  414. // Return value
  415. $returnValue = null;
  416. // Loop trough arguments
  417. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  418. foreach ($aArgs as $arg) {
  419. // Is it a numeric value?
  420. if ((is_numeric($arg)) && (!is_string($arg))) {
  421. if ((is_null($returnValue)) || ($arg > $returnValue)) {
  422. $returnValue = $arg;
  423. }
  424. }
  425. }
  426. // Return
  427. if(is_null($returnValue)) {
  428. return 0;
  429. }
  430. return $returnValue;
  431. }
  432. /**
  433. * MAXA
  434. *
  435. * Returns the greatest value in a list of arguments, including numbers, text, and logical values
  436. *
  437. * @param array of mixed Data Series
  438. * @return float
  439. */
  440. public static function MAXA() {
  441. // Return value
  442. $returnValue = null;
  443. // Loop through arguments
  444. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  445. foreach ($aArgs as $arg) {
  446. // Is it a numeric value?
  447. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
  448. if (is_bool($arg)) {
  449. $arg = (integer) $arg;
  450. } elseif (is_string($arg)) {
  451. $arg = 0;
  452. }
  453. if ((is_null($returnValue)) || ($arg > $returnValue)) {
  454. $returnValue = $arg;
  455. }
  456. }
  457. }
  458. // Return
  459. if(is_null($returnValue)) {
  460. return 0;
  461. }
  462. return $returnValue;
  463. }
  464. /**
  465. * LARGE
  466. *
  467. * Returns the nth largest value in a data set. You can use this function to
  468. * select a value based on its relative standing.
  469. *
  470. * @param array of mixed Data Series
  471. * @param float Entry in the series to return
  472. * @return float
  473. *
  474. */
  475. public static function LARGE() {
  476. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  477. // Calculate
  478. $n = floor(array_pop($aArgs));
  479. if ((is_numeric($n)) && (!is_string($n))) {
  480. $mArgs = array();
  481. foreach ($aArgs as $arg) {
  482. // Is it a numeric value?
  483. if ((is_numeric($arg)) && (!is_string($arg))) {
  484. $mArgs[] = $arg;
  485. }
  486. }
  487. $count = self::COUNT($mArgs);
  488. $n = floor(--$n);
  489. if (($n < 0) || ($n >= $count) || ($count == 0)) {
  490. return self::$_errorCodes['num'];
  491. }
  492. rsort($mArgs);
  493. return $mArgs[$n];
  494. }
  495. return self::$_errorCodes['value'];
  496. }
  497. /**
  498. * PERCENTILE
  499. *
  500. * Returns the nth percentile of values in a range..
  501. *
  502. * @param array of mixed Data Series
  503. * @param float $entry Entry in the series to return
  504. * @return float
  505. */
  506. public static function PERCENTILE() {
  507. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  508. // Calculate
  509. $entry = array_pop($aArgs);
  510. if ((is_numeric($entry)) && (!is_string($entry))) {
  511. if (($entry < 0) || ($entry > 1)) {
  512. return self::$_errorCodes['num'];
  513. }
  514. $mArgs = array();
  515. foreach ($aArgs as $arg) {
  516. // Is it a numeric value?
  517. if ((is_numeric($arg)) && (!is_string($arg))) {
  518. $mArgs[] = $arg;
  519. }
  520. }
  521. $mValueCount = count($mArgs);
  522. if ($mValueCount > 0) {
  523. sort($mArgs);
  524. $count = self::COUNT($mArgs);
  525. $index = $entry * ($count-1);
  526. $iBase = floor($index);
  527. if ($index == $iBase) {
  528. return $mArgs[$index];
  529. } else {
  530. $iNext = $iBase + 1;
  531. $iProportion = $index - $iBase;
  532. return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion) ;
  533. }
  534. }
  535. }
  536. return self::$_errorCodes['value'];
  537. }
  538. /**
  539. * QUARTILE
  540. *
  541. * Returns the quartile of a data set.
  542. *
  543. * @param array of mixed Data Series
  544. * @param float $entry Entry in the series to return
  545. * @return float
  546. */
  547. public static function QUARTILE() {
  548. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  549. // Calculate
  550. $entry = floor(array_pop($aArgs));
  551. if ((is_numeric($entry)) && (!is_string($entry))) {
  552. $entry /= 4;
  553. if (($entry < 0) || ($entry > 1)) {
  554. return self::$_errorCodes['num'];
  555. }
  556. return self::PERCENTILE($aArgs,$entry);
  557. }
  558. return self::$_errorCodes['value'];
  559. }
  560. /**
  561. * COUNT
  562. *
  563. * Counts the number of cells that contain numbers within the list of arguments
  564. *
  565. * @param array of mixed Data Series
  566. * @return int
  567. */
  568. public static function COUNT() {
  569. // Return value
  570. $returnValue = 0;
  571. // Loop trough arguments
  572. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  573. foreach ($aArgs as $arg) {
  574. if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  575. $arg = (int) $arg;
  576. }
  577. // Is it a numeric value?
  578. if ((is_numeric($arg)) && (!is_string($arg))) {
  579. ++$returnValue;
  580. }
  581. }
  582. // Return
  583. return $returnValue;
  584. }
  585. /**
  586. * COUNTBLANK
  587. *
  588. * Counts the number of empty cells within the list of arguments
  589. *
  590. * @param array of mixed Data Series
  591. * @return int
  592. */
  593. public static function COUNTBLANK() {
  594. // Return value
  595. $returnValue = 0;
  596. // Loop trough arguments
  597. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  598. foreach ($aArgs as $arg) {
  599. // Is it a blank cell?
  600. if ((is_null($arg)) || ((is_string($arg)) && ($arg == ''))) {
  601. ++$returnValue;
  602. }
  603. }
  604. // Return
  605. return $returnValue;
  606. }
  607. /**
  608. * COUNTA
  609. *
  610. * Counts the number of cells that are not empty within the list of arguments
  611. *
  612. * @param array of mixed Data Series
  613. * @return int
  614. */
  615. public static function COUNTA() {
  616. // Return value
  617. $returnValue = 0;
  618. // Loop through arguments
  619. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  620. foreach ($aArgs as $arg) {
  621. // Is it a numeric, boolean or string value?
  622. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
  623. ++$returnValue;
  624. }
  625. }
  626. // Return
  627. return $returnValue;
  628. }
  629. /**
  630. * AVERAGE
  631. *
  632. * Returns the average (arithmetic mean) of the arguments
  633. *
  634. * @param array of mixed Data Series
  635. * @return float
  636. */
  637. public static function AVERAGE() {
  638. // Return value
  639. $returnValue = 0;
  640. // Loop through arguments
  641. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  642. $aCount = 0;
  643. foreach ($aArgs as $arg) {
  644. if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  645. $arg = (integer) $arg;
  646. }
  647. // Is it a numeric value?
  648. if ((is_numeric($arg)) && (!is_string($arg))) {
  649. if (is_null($returnValue)) {
  650. $returnValue = $arg;
  651. } else {
  652. $returnValue += $arg;
  653. }
  654. ++$aCount;
  655. }
  656. }
  657. // Return
  658. if ($aCount > 0) {
  659. return $returnValue / $aCount;
  660. } else {
  661. return self::$_errorCodes['divisionbyzero'];
  662. }
  663. }
  664. /**
  665. * AVERAGEA
  666. *
  667. * Returns the average of its arguments, including numbers, text, and logical values
  668. *
  669. * @param array of mixed Data Series
  670. * @return float
  671. */
  672. public static function AVERAGEA() {
  673. // Return value
  674. $returnValue = null;
  675. // Loop through arguments
  676. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  677. $aCount = 0;
  678. foreach ($aArgs as $arg) {
  679. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
  680. if (is_bool($arg)) {
  681. $arg = (integer) $arg;
  682. } elseif (is_string($arg)) {
  683. $arg = 0;
  684. }
  685. if (is_null($returnValue)) {
  686. $returnValue = $arg;
  687. } else {
  688. $returnValue += $arg;
  689. }
  690. ++$aCount;
  691. }
  692. }
  693. // Return
  694. if ($aCount > 0) {
  695. return $returnValue / $aCount;
  696. } else {
  697. return self::$_errorCodes['divisionbyzero'];
  698. }
  699. }
  700. /**
  701. * MEDIAN
  702. *
  703. * Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
  704. *
  705. * @param array of mixed Data Series
  706. * @return float
  707. */
  708. public static function MEDIAN() {
  709. // Return value
  710. $returnValue = self::$_errorCodes['num'];
  711. $mArgs = array();
  712. // Loop through arguments
  713. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  714. foreach ($aArgs as $arg) {
  715. // Is it a numeric value?
  716. if ((is_numeric($arg)) && (!is_string($arg))) {
  717. $mArgs[] = $arg;
  718. }
  719. }
  720. $mValueCount = count($mArgs);
  721. if ($mValueCount > 0) {
  722. sort($mArgs,SORT_NUMERIC);
  723. $mValueCount = $mValueCount / 2;
  724. if ($mValueCount == floor($mValueCount)) {
  725. $returnValue = ($mArgs[$mValueCount--] + $mArgs[$mValueCount]) / 2;
  726. } else {
  727. $mValueCount == floor($mValueCount);
  728. $returnValue = $mArgs[$mValueCount];
  729. }
  730. }
  731. // Return
  732. return $returnValue;
  733. }
  734. //
  735. // Special variant of array_count_values that isn't limited to strings and integers,
  736. // but can work with floating point numbers as values
  737. //
  738. private static function modeCalc($data) {
  739. $frequencyArray = array();
  740. foreach($data as $datum) {
  741. $found = False;
  742. foreach($frequencyArray as $key => $value) {
  743. if ((string)$value['value'] == (string)$datum) {
  744. ++$frequencyArray[$key]['frequency'];
  745. $found = True;
  746. break;
  747. }
  748. }
  749. if (!$found) {
  750. $frequencyArray[] = array('value' => $datum,
  751. 'frequency' => 1 );
  752. }
  753. }
  754. foreach($frequencyArray as $key => $value) {
  755. $frequencyList[$key] = $value['frequency'];
  756. $valueList[$key] = $value['value'];
  757. }
  758. array_multisort($frequencyList, SORT_DESC, $valueList, SORT_ASC, SORT_NUMERIC, $frequencyArray);
  759. if ($frequencyArray[0]['frequency'] == 1) {
  760. return self::NA();
  761. }
  762. return $frequencyArray[0]['value'];
  763. }
  764. /**
  765. * MODE
  766. *
  767. * Returns the most frequently occurring, or repetitive, value in an array or range of data
  768. *
  769. * @param array of mixed Data Series
  770. * @return float
  771. */
  772. public static function MODE() {
  773. // Return value
  774. $returnValue = PHPExcel_Calculation_Functions::NA();
  775. // Loop through arguments
  776. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  777. $mArgs = array();
  778. foreach ($aArgs as $arg) {
  779. // Is it a numeric value?
  780. if ((is_numeric($arg)) && (!is_string($arg))) {
  781. $mArgs[] = $arg;
  782. }
  783. }
  784. if (count($mArgs) > 0) {
  785. return self::modeCalc($mArgs);
  786. }
  787. // Return
  788. return $returnValue;
  789. }
  790. /**
  791. * DEVSQ
  792. *
  793. * Returns the sum of squares of deviations of data points from their sample mean.
  794. *
  795. * @param array of mixed Data Series
  796. * @return float
  797. */
  798. public static function DEVSQ() {
  799. // Return value
  800. $returnValue = null;
  801. $aMean = PHPExcel_Calculation_Functions::AVERAGE(func_get_args());
  802. if (!is_null($aMean)) {
  803. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  804. $aCount = -1;
  805. foreach ($aArgs as $arg) {
  806. // Is it a numeric value?
  807. if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  808. $arg = (int) $arg;
  809. }
  810. if ((is_numeric($arg)) && (!is_string($arg))) {
  811. if (is_null($returnValue)) {
  812. $returnValue = pow(($arg - $aMean),2);
  813. } else {
  814. $returnValue += pow(($arg - $aMean),2);
  815. }
  816. ++$aCount;
  817. }
  818. }
  819. // Return
  820. if (is_null($returnValue)) {
  821. return self::$_errorCodes['num'];
  822. } else {
  823. return $returnValue;
  824. }
  825. }
  826. return self::NA();
  827. }
  828. /**
  829. * AVEDEV
  830. *
  831. * Returns the average of the absolute deviations of data points from their mean.
  832. * AVEDEV is a measure of the variability in a data set.
  833. *
  834. * @param array of mixed Data Series
  835. * @return float
  836. */
  837. public static function AVEDEV() {
  838. // Return value
  839. $returnValue = null;
  840. $aMean = PHPExcel_Calculation_Functions::AVERAGE(func_get_args());
  841. if ($aMean != self::$_errorCodes['divisionbyzero']) {
  842. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  843. $aCount = 0;
  844. foreach ($aArgs as $arg) {
  845. if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  846. $arg = (integer) $arg;
  847. }
  848. // Is it a numeric value?
  849. if ((is_numeric($arg)) && (!is_string($arg))) {
  850. if (is_null($returnValue)) {
  851. $returnValue = abs($arg - $aMean);
  852. } else {
  853. $returnValue += abs($arg - $aMean);
  854. }
  855. ++$aCount;
  856. }
  857. }
  858. // Return
  859. return $returnValue / $aCount ;
  860. }
  861. return self::$_errorCodes['num'];
  862. }
  863. /**
  864. * GEOMEAN
  865. *
  866. * Returns the geometric mean of an array or range of positive data. For example, you
  867. * can use GEOMEAN to calculate average growth rate given compound interest with
  868. * variable rates.
  869. *
  870. * @param array of mixed Data Series
  871. * @return float
  872. */
  873. public static function GEOMEAN() {
  874. $aMean = PHPExcel_Calculation_Functions::PRODUCT(func_get_args());
  875. if (is_numeric($aMean) && ($aMean > 0)) {
  876. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  877. $aCount = PHPExcel_Calculation_Functions::COUNT($aArgs) ;
  878. if (PHPExcel_Calculation_Functions::MIN($aArgs) > 0) {
  879. return pow($aMean, (1 / $aCount));
  880. }
  881. }
  882. return self::$_errorCodes['num'];
  883. }
  884. /**
  885. * HARMEAN
  886. *
  887. * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
  888. * arithmetic mean of reciprocals.
  889. *
  890. * @param array of mixed Data Series
  891. * @return float
  892. */
  893. public static function HARMEAN() {
  894. // Return value
  895. $returnValue = self::NA();
  896. // Loop through arguments
  897. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  898. if (PHPExcel_Calculation_Functions::MIN($aArgs) < 0) {
  899. return self::$_errorCodes['num'];
  900. }
  901. $aCount = 0;
  902. foreach ($aArgs as $arg) {
  903. // Is it a numeric value?
  904. if ((is_numeric($arg)) && (!is_string($arg))) {
  905. if ($arg <= 0) {
  906. return self::$_errorCodes['num'];
  907. }
  908. if (is_null($returnValue)) {
  909. $returnValue = (1 / $arg);
  910. } else {
  911. $returnValue += (1 / $arg);
  912. }
  913. ++$aCount;
  914. }
  915. }
  916. // Return
  917. if ($aCount > 0) {
  918. return 1 / ($returnValue / $aCount);
  919. } else {
  920. return $returnValue;
  921. }
  922. }
  923. /**
  924. * TRIMMEAN
  925. *
  926. * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
  927. * taken by excluding a percentage of data points from the top and bottom tails
  928. * of a data set.
  929. *
  930. * @param array of mixed Data Series
  931. * @param float Percentage to discard
  932. * @return float
  933. */
  934. public static function TRIMMEAN() {
  935. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  936. // Calculate
  937. $percent = array_pop($aArgs);
  938. if ((is_numeric($percent)) && (!is_string($percent))) {
  939. if (($percent < 0) || ($percent > 1)) {
  940. return self::$_errorCodes['num'];
  941. }
  942. $mArgs = array();
  943. foreach ($aArgs as $arg) {
  944. // Is it a numeric value?
  945. if ((is_numeric($arg)) && (!is_string($arg))) {
  946. $mArgs[] = $arg;
  947. }
  948. }
  949. $discard = floor(self::COUNT($mArgs) * $percent / 2);
  950. sort($mArgs);
  951. for ($i=0; $i < $discard; ++$i) {
  952. array_pop($mArgs);
  953. array_shift($mArgs);
  954. }
  955. return self::AVERAGE($mArgs);
  956. }
  957. return self::$_errorCodes['value'];
  958. }
  959. /**
  960. * STDEV
  961. *
  962. * Estimates standard deviation based on a sample. The standard deviation is a measure of how
  963. * widely values are dispersed from the average value (the mean).
  964. *
  965. * @param array of mixed Data Series
  966. * @return float
  967. */
  968. public static function STDEV() {
  969. // Return value
  970. $returnValue = null;
  971. $aMean = PHPExcel_Calculation_Functions::AVERAGE(func_get_args());
  972. if (!is_null($aMean)) {
  973. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  974. $aCount = -1;
  975. foreach ($aArgs as $arg) {
  976. // Is it a numeric value?
  977. if ((is_numeric($arg)) && (!is_string($arg))) {
  978. if (is_null($returnValue)) {
  979. $returnValue = pow(($arg - $aMean),2);
  980. } else {
  981. $returnValue += pow(($arg - $aMean),2);
  982. }
  983. ++$aCount;
  984. }
  985. }
  986. // Return
  987. if (($aCount > 0) && ($returnValue > 0)) {
  988. return sqrt($returnValue / $aCount);
  989. }
  990. }
  991. return self::$_errorCodes['divisionbyzero'];
  992. }
  993. /**
  994. * STDEVA
  995. *
  996. * Estimates standard deviation based on a sample, including numbers, text, and logical values
  997. *
  998. * @param array of mixed Data Series
  999. * @return float
  1000. */
  1001. public static function STDEVA() {
  1002. // Return value
  1003. $returnValue = null;
  1004. $aMean = PHPExcel_Calculation_Functions::AVERAGEA(func_get_args());
  1005. if (!is_null($aMean)) {
  1006. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  1007. $aCount = -1;
  1008. foreach ($aArgs as $arg) {
  1009. // Is it a numeric value?
  1010. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
  1011. if (is_bool($arg)) {
  1012. $arg = (integer) $arg;
  1013. } elseif (is_string($arg)) {
  1014. $arg = 0;
  1015. }
  1016. if (is_null($returnValue)) {
  1017. $returnValue = pow(($arg - $aMean),2);
  1018. } else {
  1019. $returnValue += pow(($arg - $aMean),2);
  1020. }
  1021. ++$aCount;
  1022. }
  1023. }
  1024. // Return
  1025. if (($aCount > 0) && ($returnValue > 0)) {
  1026. return sqrt($returnValue / $aCount);
  1027. }
  1028. }
  1029. return self::$_errorCodes['divisionbyzero'];
  1030. }
  1031. /**
  1032. * STDEVP
  1033. *
  1034. * Calculates standard deviation based on the entire population
  1035. *
  1036. * @param array of mixed Data Series
  1037. * @return float
  1038. */
  1039. public static function STDEVP() {
  1040. // Return value
  1041. $returnValue = null;
  1042. $aMean = PHPExcel_Calculation_Functions::AVERAGE(func_get_args());
  1043. if (!is_null($aMean)) {
  1044. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  1045. $aCount = 0;
  1046. foreach ($aArgs as $arg) {
  1047. // Is it a numeric value?
  1048. if ((is_numeric($arg)) && (!is_string($arg))) {
  1049. if (is_null($returnValue)) {
  1050. $returnValue = pow(($arg - $aMean),2);
  1051. } else {
  1052. $returnValue += pow(($arg - $aMean),2);
  1053. }
  1054. ++$aCount;
  1055. }
  1056. }
  1057. // Return
  1058. if (($aCount > 0) && ($returnValue > 0)) {
  1059. return sqrt($returnValue / $aCount);
  1060. }
  1061. }
  1062. return self::$_errorCodes['divisionbyzero'];
  1063. }
  1064. /**
  1065. * STDEVPA
  1066. *
  1067. * Calculates standard deviation based on the entire population, including numbers, text, and logical values
  1068. *
  1069. * @param array of mixed Data Series
  1070. * @return float
  1071. */
  1072. public static function STDEVPA() {
  1073. // Return value
  1074. $returnValue = null;
  1075. $aMean = PHPExcel_Calculation_Functions::AVERAGEA(func_get_args());
  1076. if (!is_null($aMean)) {
  1077. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  1078. $aCount = 0;
  1079. foreach ($aArgs as $arg) {
  1080. // Is it a numeric value?
  1081. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
  1082. if (is_bool($arg)) {
  1083. $arg = (integer) $arg;
  1084. } elseif (is_string($arg)) {
  1085. $arg = 0;
  1086. }
  1087. if (is_null($returnValue)) {
  1088. $returnValue = pow(($arg - $aMean),2);
  1089. } else {
  1090. $returnValue += pow(($arg - $aMean),2);
  1091. }
  1092. ++$aCount;
  1093. }
  1094. }
  1095. // Return
  1096. if (($aCount > 0) && ($returnValue > 0)) {
  1097. return sqrt($returnValue / $aCount);
  1098. }
  1099. }
  1100. return self::$_errorCodes['divisionbyzero'];
  1101. }
  1102. /**
  1103. * VARFunc
  1104. *
  1105. * Estimates variance based on a sample.
  1106. *
  1107. * @param array of mixed Data Series
  1108. * @return float
  1109. */
  1110. public static function VARFunc() {
  1111. // Return value
  1112. $returnValue = self::$_errorCodes['divisionbyzero'];
  1113. $summerA = $summerB = 0;
  1114. // Loop through arguments
  1115. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  1116. $aCount = 0;
  1117. foreach ($aArgs as $arg) {
  1118. // Is it a numeric value?
  1119. if ((is_numeric($arg)) && (!is_string($arg))) {
  1120. $summerA += ($arg * $arg);
  1121. $summerB += $arg;
  1122. ++$aCount;
  1123. }
  1124. }
  1125. // Return
  1126. if ($aCount > 1) {
  1127. $summerA = $summerA * $aCount;
  1128. $summerB = ($summerB * $summerB);
  1129. $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
  1130. }
  1131. return $returnValue;
  1132. }
  1133. /**
  1134. * VARA
  1135. *
  1136. * Estimates variance based on a sample, including numbers, text, and logical values
  1137. *
  1138. * @param array of mixed Data Series
  1139. * @return float
  1140. */
  1141. public static function VARA() {
  1142. // Return value
  1143. $returnValue = self::$_errorCodes['divisionbyzero'];
  1144. $summerA = $summerB = 0;
  1145. // Loop through arguments
  1146. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  1147. $aCount = 0;
  1148. foreach ($aArgs as $arg) {
  1149. // Is it a numeric value?
  1150. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
  1151. if (is_bool($arg)) {
  1152. $arg = (integer) $arg;
  1153. } elseif (is_string($arg)) {
  1154. $arg = 0;
  1155. }
  1156. $summerA += ($arg * $arg);
  1157. $summerB += $arg;
  1158. ++$aCount;
  1159. }
  1160. }
  1161. // Return
  1162. if ($aCount > 1) {
  1163. $summerA = $summerA * $aCount;
  1164. $summerB = ($summerB * $summerB);
  1165. $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
  1166. }
  1167. return $returnValue;
  1168. }
  1169. /**
  1170. * VARP
  1171. *
  1172. * Calculates variance based on the entire population
  1173. *
  1174. * @param array of mixed Data Series
  1175. * @return float
  1176. */
  1177. public static function VARP() {
  1178. // Return value
  1179. $returnValue = self::$_errorCodes['divisionbyzero'];
  1180. $summerA = $summerB = 0;
  1181. // Loop through arguments
  1182. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  1183. $aCount = 0;
  1184. foreach ($aArgs as $arg) {
  1185. // Is it a numeric value?
  1186. if ((is_numeric($arg)) && (!is_string($arg))) {
  1187. $summerA += ($arg * $arg);
  1188. $summerB += $arg;
  1189. ++$aCount;
  1190. }
  1191. }
  1192. // Return
  1193. if ($aCount > 0) {
  1194. $summerA = $summerA * $aCount;
  1195. $summerB = ($summerB * $summerB);
  1196. $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
  1197. }
  1198. return $returnValue;
  1199. }
  1200. /**
  1201. * VARPA
  1202. *
  1203. * Calculates variance based on the entire population, including numbers, text, and logical values
  1204. *
  1205. * @param array of mixed Data Series
  1206. * @return float
  1207. */
  1208. public static function VARPA() {
  1209. // Return value
  1210. $returnValue = self::$_errorCodes['divisionbyzero'];
  1211. $summerA = $summerB = 0;
  1212. // Loop through arguments
  1213. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  1214. $aCount = 0;
  1215. foreach ($aArgs as $arg) {
  1216. // Is it a numeric value?
  1217. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
  1218. if (is_bool($arg)) {
  1219. $arg = (integer) $arg;
  1220. } elseif (is_string($arg)) {
  1221. $arg = 0;
  1222. }
  1223. $summerA += ($arg * $arg);
  1224. $summerB += $arg;
  1225. ++$aCount;
  1226. }
  1227. }
  1228. // Return
  1229. if ($aCount > 0) {
  1230. $summerA = $summerA * $aCount;
  1231. $summerB = ($summerB * $summerB);
  1232. $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
  1233. }
  1234. return $returnValue;
  1235. }
  1236. /**
  1237. * SUBTOTAL
  1238. *
  1239. * Returns a subtotal in a list or database.
  1240. *
  1241. * @param int the number 1 to 11 that specifies which function to
  1242. * use in calculating subtotals within a list.
  1243. * @param array of mixed Data Series
  1244. * @return float
  1245. */
  1246. public static function SUBTOTAL() {
  1247. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  1248. // Calculate
  1249. $subtotal = array_shift($aArgs);
  1250. if ((is_numeric($subtotal)) && (!is_string($subtotal))) {
  1251. switch($subtotal) {
  1252. case 1 :
  1253. return self::AVERAGE($aArgs);
  1254. break;
  1255. case 2 :
  1256. return self::COUNT($aArgs);
  1257. break;
  1258. case 3 :
  1259. return self::COUNTA($aArgs);
  1260. break;
  1261. case 4 :
  1262. return self::MAX($aArgs);
  1263. break;
  1264. case 5 :
  1265. return self::MIN($aArgs);
  1266. break;
  1267. case 6 :
  1268. return self::PRODUCT($aArgs);
  1269. break;
  1270. case 7 :
  1271. return self::STDEV($aArgs);
  1272. break;
  1273. case 8 :
  1274. return self::STDEVP($aArgs);
  1275. break;
  1276. case 9 :
  1277. return self::SUM($aArgs);
  1278. break;
  1279. case 10 :
  1280. return self::VARFunc($aArgs);
  1281. break;
  1282. case 11 :
  1283. return self::VARP($aArgs);
  1284. break;
  1285. }
  1286. }
  1287. return self::$_errorCodes['value'];
  1288. }
  1289. /**
  1290. * SQRTPI
  1291. *
  1292. * Returns the square root of (number * pi).
  1293. *
  1294. * @param float $number Number
  1295. * @return float Square Root of Number * Pi
  1296. */
  1297. public static function SQRTPI($number) {
  1298. $number = self::flattenSingleValue($number);
  1299. if (is_numeric($number)) {
  1300. if ($number < 0) {
  1301. return self::$_errorCodes['num'];
  1302. }
  1303. return sqrt($number * pi()) ;
  1304. }
  1305. return self::$_errorCodes['value'];
  1306. }
  1307. /**
  1308. * FACT
  1309. *
  1310. * Returns the factorial of a number.
  1311. *
  1312. * @param float $factVal Factorial Value
  1313. * @return int Factorial
  1314. */
  1315. public static function FACT($factVal) {
  1316. $factVal = self::flattenSingleValue($factVal);
  1317. if (is_numeric($factVal)) {
  1318. if ($factVal < 0) {
  1319. return self::$_errorCodes['num'];
  1320. }
  1321. $factLoop = floor($factVal);
  1322. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  1323. if ($factVal > $factLoop) {
  1324. return self::$_errorCodes['num'];
  1325. }
  1326. }
  1327. $factorial = 1;
  1328. while ($factLoop > 1) {
  1329. $factorial *= $factLoop--;
  1330. }
  1331. return $factorial ;
  1332. }
  1333. return self::$_errorCodes['value'];
  1334. }
  1335. /**
  1336. * FACTDOUBLE
  1337. *
  1338. * Returns the double factorial of a number.
  1339. *
  1340. * @param float $factVal Factorial Value
  1341. * @return int Double Factorial
  1342. */
  1343. public static function FACTDOUBLE($factVal) {
  1344. $factLoop = floor(self::flattenSingleValue($factVal));
  1345. if (is_numeric($factLoop)) {
  1346. if ($factVal < 0) {
  1347. return self::$_errorCodes['num'];
  1348. }
  1349. $factorial = 1;
  1350. while ($factLoop > 1) {
  1351. $factorial *= $factLoop--;
  1352. --$factLoop;
  1353. }
  1354. return $factorial ;
  1355. }
  1356. return self::$_errorCodes['value'];
  1357. }
  1358. /**
  1359. * MULTINOMIAL
  1360. *
  1361. * Returns the ratio of the factorial of a sum of values to the product of factorials.
  1362. *
  1363. * @param array of mixed Data Series
  1364. * @return float
  1365. */
  1366. public static function MULTINOMIAL() {
  1367. // Loop through arguments
  1368. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  1369. $summer = 0;
  1370. $divisor = 1;
  1371. foreach ($aArgs as $arg) {
  1372. // Is it a numeric value?
  1373. if (is_numeric($arg)) {
  1374. if ($arg < 1) {
  1375. return self::$_errorCodes['num'];
  1376. }
  1377. $summer += floor($arg);
  1378. $divisor *= self::FACT($arg);
  1379. } else {
  1380. return self::$_errorCodes['value'];
  1381. }
  1382. }
  1383. // Return
  1384. if ($summer > 0) {
  1385. $summer = self::FACT($summer);
  1386. return $summer / $divisor;
  1387. }
  1388. return 0;
  1389. }
  1390. /**
  1391. * CEILING
  1392. *
  1393. * Returns number rounded up, away from zero, to the nearest multiple of significance.
  1394. *
  1395. * @param float $number Number to round
  1396. * @param float $significance Significance
  1397. * @return float Rounded Number
  1398. */
  1399. public static function CEILING($number,$significance) {
  1400. $number = self::flattenSingleValue($number);
  1401. $significance = self::flattenSingleValue($significance);
  1402. if ((is_numeric($number)) && (is_numeric($significance))) {
  1403. if ($significance == 0.0) {
  1404. return 0;
  1405. }
  1406. return ceil($number / $significance) * $significance;
  1407. }
  1408. return self::$_errorCodes['value'];
  1409. }
  1410. /**
  1411. * EVEN
  1412. *
  1413. * Returns number rounded up to the nearest even integer.
  1414. *
  1415. * @param float $number Number to round
  1416. * @return int Rounded Number
  1417. */
  1418. public static function EVEN($number) {
  1419. $number = self::flattenSingleValue($number);
  1420. if (is_numeric($number)) {
  1421. $significance = 2 * self::SIGN($number);
  1422. return self::CEILING($number,$significance);
  1423. }
  1424. return self::$_errorCodes['value'];
  1425. }
  1426. /**
  1427. * ODD
  1428. *
  1429. * Returns number rounded up to the nearest odd integer.
  1430. *
  1431. * @param float $number Number to round
  1432. * @return int Rounded Number
  1433. */
  1434. public static function ODD($number) {
  1435. $number = self::flattenSingleValue($number);
  1436. if (is_numeric($number)) {
  1437. $significance = self::SIGN($number);
  1438. if ($significance == 0) {
  1439. return 1;
  1440. }
  1441. $result = self::CEILING($number,$significance);
  1442. if (self::IS_EVEN($result)) {
  1443. $result += $significance;
  1444. }
  1445. return $result;
  1446. }
  1447. return self::$_errorCodes['value'];
  1448. }
  1449. /**
  1450. * ROUNDUP
  1451. *
  1452. * Rounds a number up to a specified number of decimal places
  1453. *
  1454. * @param float $number Number to round
  1455. * @param int $digits Number of digits to which you want to round $number
  1456. * @return float Rounded Number
  1457. */
  1458. public static function ROUNDUP($number,$digits) {
  1459. $number = self::flattenSingleValue($number);
  1460. $digits = self::flattenSingleValue($digits);
  1461. if (is_numeric($number)) {
  1462. if ((is_numeric($digits)) && ($digits >= 0)) {
  1463. $significance = pow(10,$digits);
  1464. return ceil($number * $significance) / $significance;
  1465. }
  1466. }
  1467. return self::$_errorCodes['value'];
  1468. }
  1469. /**
  1470. * ROUNDDOWN
  1471. *
  1472. * Rounds a number down to a specified number of decimal places
  1473. *
  1474. * @param float $number Number to round
  1475. * @param int $digits Number of digits to which you want to round $number
  1476. * @return float Rounded Number
  1477. */
  1478. public static function ROUNDDOWN($number,$digits) {
  1479. $number = self::flattenSingleValue($number);
  1480. $digits = self::flattenSingleValue($digits);
  1481. if (is_numeric($number)) {
  1482. if ((is_numeric($digits)) && ($digits >= 0)) {
  1483. $significance = pow(10,$digits);
  1484. return floor($number * $significance) / $significance;
  1485. }
  1486. }
  1487. return self::$_errorCodes['value'];
  1488. }
  1489. /**
  1490. * MROUND
  1491. *
  1492. * Rounds a number to the nearest multiple of a specified value
  1493. *
  1494. * @param float $number Number to round
  1495. * @param int $multiple Multiple to which you want to round $number
  1496. * @return float Rounded Number
  1497. */
  1498. public static function MROUND($number,$multiple) {
  1499. $number = self::flattenSingleValue($number);
  1500. $multiple = self::flattenSingleValue($multiple);
  1501. if ((is_numeric($number)) && (is_numeric($multiple))) {
  1502. if ((self::SIGN($number)) == (self::SIGN($multiple))) {
  1503. $lowerVal = floor($number / $multiple) * $multiple;
  1504. $upperVal = ceil($number / $multiple) * $multiple;
  1505. $adjustUp = abs($number - $upperVal);
  1506. $adjustDown = abs($number - $lowerVal) + PRECISION;
  1507. if ($adjustDown < $adjustUp) {
  1508. return $lowerVal;
  1509. }
  1510. return $upperVal;
  1511. }
  1512. return self::$_errorCodes['num'];
  1513. }
  1514. return self::$_errorCodes['value'];
  1515. }
  1516. /**
  1517. * SIGN
  1518. *
  1519. * Determines the sign of a number. Returns 1 if the number is positive, zero (0)
  1520. * if the number is 0, and -1 if the number is negative.
  1521. *
  1522. * @param float $number Number to round
  1523. * @return int sign value
  1524. */
  1525. public static function SIGN($number) {
  1526. $number = self::flattenSingleValue($number);
  1527. if (is_numeric($number)) {
  1528. if ($number == 0.0) {
  1529. return 0;
  1530. }
  1531. return $number / abs($number);
  1532. }
  1533. return self::$_errorCodes['value'];
  1534. }
  1535. /**
  1536. * FLOOR
  1537. *
  1538. * Rounds number down, toward zero, to the nearest multiple of significance.
  1539. *
  1540. * @param float $number Number to round
  1541. * @param float $significance Significance
  1542. * @return float Rounded Number
  1543. */
  1544. public static function FLOOR($number,$significance) {
  1545. $number = self::flattenSingleValue($number);
  1546. $significance = self::flattenSingleValue($significance);
  1547. if ((is_numeric($number)) && (is_numeric($significance))) {
  1548. return floor($number / $significance) * $significance;
  1549. }
  1550. return self::$_errorCodes['value'];
  1551. }
  1552. /**
  1553. * PERMUT
  1554. *
  1555. * Returns the number of permutations for a given number of objects that can be
  1556. * selected from number objects. A permutation is any set or subset of objects or
  1557. * events where internal order is significant. Permutations are different from
  1558. * combinations, for which the internal order is not significant. Use this function
  1559. * for lottery-style probability calculations.
  1560. *
  1561. * @param int $numObjs Number of different objects
  1562. * @param int $numInSet Number of objects in each permutation
  1563. * @return int Number of permutations
  1564. */
  1565. public static function PERMUT($numObjs,$numInSet) {
  1566. $numObjs = self::flattenSingleValue($numObjs);
  1567. $numInSet = self::flattenSingleValue($numInSet);
  1568. if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
  1569. if ($numObjs < $numInSet) {
  1570. return self::$_errorCodes['num'];
  1571. }
  1572. return self::FACT($numObjs) / self::FACT($numObjs - $numInSet);
  1573. }
  1574. return self::$_errorCodes['value'];
  1575. }
  1576. /**
  1577. * COMBIN
  1578. *
  1579. * Returns the number of combinations for a given number of items. Use COMBIN to
  1580. * determine the total possible number of groups for a given number of items.
  1581. *
  1582. * @param int $numObjs Number of different objects
  1583. * @param int $numInSet Number of objects in each combination
  1584. * @return int Number of combinations
  1585. */
  1586. public static function COMBIN($numObjs,$numInSet) {
  1587. $numObjs = self::flattenSingleValue($numObjs);
  1588. $numInSet = self::flattenSingleValue($numInSet);
  1589. if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
  1590. if ($numObjs < $numInSet) {
  1591. return self::$_errorCodes['num'];
  1592. }
  1593. return (self::FACT($numObjs) / self::FACT($numObjs - $numInSet)) / self::FACT($numInSet);
  1594. }
  1595. return self::$_errorCodes['value'];
  1596. }
  1597. /**
  1598. * SERIESSUM
  1599. *
  1600. * Returns the sum of a power series
  1601. *
  1602. * @param float $x Input value to the power series
  1603. * @param float $n Initial power to which you want to raise $x
  1604. * @param float $m Step by which to increase $n for each term in the series
  1605. * @param array of mixed Data Series
  1606. * @return float
  1607. */
  1608. public static function SERIESSUM() {
  1609. // Return value
  1610. $returnValue = 0;
  1611. // Loop trough arguments
  1612. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  1613. $x = array_shift($aArgs);
  1614. $n = array_shift($aArgs);
  1615. $m = array_shift($aArgs);
  1616. if ((is_numeric($x)) && (is_numeric($n)) && (is_numeric($m))) {
  1617. // Calculate
  1618. $i = 0;
  1619. foreach($aArgs as $arg) {
  1620. // Is it a numeric value?
  1621. if ((is_numeric($arg)) && (!is_string($arg))) {
  1622. $returnValue += $arg * pow($x,$n + ($m * $i++));
  1623. } else {
  1624. return self::$_errorCodes['value'];
  1625. }
  1626. }
  1627. // Return
  1628. return $returnValue;
  1629. }
  1630. return self::$_errorCodes['value'];
  1631. }
  1632. /**
  1633. * STANDARDIZE
  1634. *
  1635. * Returns a normalized value from a distribution characterized by mean and standard_dev.
  1636. *
  1637. * @param float $value Value to normalize
  1638. * @param float $mean Mean Value
  1639. * @param float $stdDev Standard Deviation
  1640. * @return float Standardized value
  1641. */
  1642. public static function STANDARDIZE($value,$mean,$stdDev) {
  1643. $value = self::flattenSingleValue($value);
  1644. $mean = self::flattenSingleValue($mean);
  1645. $stdDev = self::flattenSingleValue($stdDev);
  1646. if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  1647. if ($stdDev <= 0) {
  1648. return self::$_errorCodes['num'];
  1649. }
  1650. return ($value - $mean) / $stdDev ;
  1651. }
  1652. return self::$_errorCodes['value'];
  1653. }
  1654. //
  1655. // Private method to return an array of the factors of the input value
  1656. //
  1657. private static function factors($value) {
  1658. $startVal = floor($value/2);
  1659. $factorArray = array();
  1660. for($i=$startVal; $i>1; --$i) {
  1661. if (($value/$i) == floor($value/$i)) {
  1662. $subFactors = self::factors($i);
  1663. if ($i == sqrt($value)) {
  1664. $factorArray = array_merge($factorArray,$subFactors,$subFactors);
  1665. } else {
  1666. $value /= $i;
  1667. $factorArray = array_merge($factorArray,$subFactors);
  1668. }
  1669. }
  1670. }
  1671. if (count($factorArray) > 0) {
  1672. return $factorArray;
  1673. } else {
  1674. return array((integer)$value);
  1675. }
  1676. }
  1677. /**
  1678. * LCM
  1679. *
  1680. * Returns the lowest common multiplier of a series of numbers
  1681. *
  1682. * @param $array Values to calculate the Lowest Common Multiplier
  1683. * @return int Lowest Common Multiplier
  1684. */
  1685. public static function LCM() {
  1686. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  1687. $returnValue = 1;
  1688. $allPoweredFactors = array();
  1689. foreach($aArgs as $value) {
  1690. if (!is_numeric($value)) {
  1691. return self::$_errorCodes['value'];
  1692. }
  1693. if ($value < 1) {
  1694. return self::$_errorCodes['num'];
  1695. }
  1696. $myFactors = self::factors(floor($value));
  1697. $myCountedFactors = array_count_values($myFactors);
  1698. $myPoweredFactors = array();
  1699. foreach($myCountedFactors as $myCountedFactor => $myCountedPower) {
  1700. $myPoweredFactors[$myCountedFactor] = pow($myCountedFactor,$myCountedPower);
  1701. }
  1702. foreach($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
  1703. if (array_key_exists($myPoweredValue,$allPoweredFactors)) {
  1704. if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
  1705. $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
  1706. }
  1707. } else {
  1708. $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
  1709. }
  1710. }
  1711. }
  1712. foreach($allPoweredFactors as $allPoweredFactor) {
  1713. $returnValue *= (integer) $allPoweredFactor;
  1714. }
  1715. return $returnValue;
  1716. }
  1717. /**
  1718. * GCD
  1719. *
  1720. * Returns the greatest common divisor of a series of numbers
  1721. *
  1722. * @param $array Values to calculate the Greatest Common Divisor
  1723. * @return int Greatest Common Divisor
  1724. */
  1725. public static function GCD() {
  1726. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  1727. $returnValue = 1;
  1728. $allPoweredFactors = array();
  1729. foreach($aArgs as $value) {
  1730. if ($value == 0) {
  1731. return 0;
  1732. }
  1733. $myFactors = self::factors($value);
  1734. $myCountedFactors = array_count_values($myFactors);
  1735. $allValuesFactors[] = $myCountedFactors;
  1736. }
  1737. $allValuesCount = count($allValuesFactors);
  1738. $mergedArray = $allValuesFactors[0];
  1739. for ($i=1;$i < $allValuesCount; ++$i) {
  1740. $mergedArray = array_intersect_key($mergedArray,$allValuesFactors[$i]);
  1741. }
  1742. $mergedArrayValues = count($mergedArray);
  1743. if ($mergedArrayValues == 0) {
  1744. return $returnValue;
  1745. } elseif ($mergedArrayValues > 1) {
  1746. foreach($mergedArray as $mergedKey => $mergedValue) {
  1747. foreach($allValuesFactors as $highestPowerTest) {
  1748. foreach($highestPowerTest as $testKey => $testValue) {
  1749. if (($testKey == $mergedKey) && ($testValue < $mergedValue)) {
  1750. $mergedArray[$mergedKey] = $testValue;
  1751. $mergedValue = $testValue;
  1752. }
  1753. }
  1754. }
  1755. }
  1756. $returnValue = 1;
  1757. foreach($mergedArray as $key => $value) {
  1758. $returnValue *= pow($key,$value);
  1759. }
  1760. return $returnValue;
  1761. } else {
  1762. $keys = array_keys($mergedArray);
  1763. $key = $keys[0];
  1764. $value = $mergedArray[$key];
  1765. foreach($allValuesFactors as $testValue) {
  1766. foreach($testValue as $mergedKey => $mergedValue) {
  1767. if (($mergedKey == $key) && ($mergedValue < $value)) {
  1768. $value = $mergedValue;
  1769. }
  1770. }
  1771. }
  1772. return pow($key,$value);
  1773. }
  1774. }
  1775. /**
  1776. * BINOMDIST
  1777. *
  1778. * Returns the individual term binomial distribution probability. Use BINOMDIST in problems with
  1779. * a fixed number of tests or trials, when the outcomes of any trial are only success or failure,
  1780. * when trials are independent, and when the probability of success is constant throughout the
  1781. * experiment. For example, BINOMDIST can calculate the probability that two of the next three
  1782. * babies born are male.
  1783. *
  1784. * @param float $value Number of successes in trials
  1785. * @param float $trials Number of trials
  1786. * @param float $probability Probability of success on each trial
  1787. * @param boolean $cumulative
  1788. * @return float
  1789. *
  1790. * @todo Cumulative distribution function
  1791. *
  1792. */
  1793. public static function BINOMDIST($value, $trials, $probability, $cumulative) {
  1794. $value = floor(self::flattenSingleValue($value));
  1795. $trials = floor(self::flattenSingleValue($trials));
  1796. $probability = self::flattenSingleValue($probability);
  1797. if ((is_numeric($value)) && (is_numeric($trials)) && (is_numeric($probability))) {
  1798. if (($value < 0) || ($value > $trials)) {
  1799. return self::$_errorCodes['num'];
  1800. }
  1801. if (($probability < 0) || ($probability > 1)) {
  1802. return self::$_errorCodes['num'];
  1803. }
  1804. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  1805. if ($cumulative) {
  1806. $summer = 0;
  1807. for ($i = 0; $i <= $value; ++$i) {
  1808. $summer += self::COMBIN($trials,$i) * pow($probability,$i) * pow(1 - $probability,$trials - $i);
  1809. }
  1810. return $summer;
  1811. } else {
  1812. return self::COMBIN($trials,$value) * pow($probability,$value) * pow(1 - $probability,$trials - $value) ;
  1813. }
  1814. }
  1815. }
  1816. return self::$_errorCodes['value'];
  1817. }
  1818. /**
  1819. * NEGBINOMDIST
  1820. *
  1821. * Returns the negative binomial distribution. NEGBINOMDIST returns the probability that
  1822. * there will be number_f failures before the number_s-th success, when the constant
  1823. * probability of a success is probability_s. This function is similar to the binomial
  1824. * distribution, except that the number of successes is fixed, and the number of trials is
  1825. * variable. Like the binomial, trials are assumed to be independent.
  1826. *
  1827. * @param float $failures Number of Failures
  1828. * @param float $successes Threshold number of Successes
  1829. * @param float $probability Probability of success on each trial
  1830. * @return float
  1831. *
  1832. */
  1833. public static function NEGBINOMDIST($failures, $successes, $probability) {
  1834. $failures = floor(self::flattenSingleValue($failures));
  1835. $successes = floor(self::flattenSingleValue($successes));
  1836. $probability = self::flattenSingleValue($probability);
  1837. if ((is_numeric($failures)) && (is_numeric($successes)) && (is_numeric($probability))) {
  1838. if (($failures < 0) || ($successes < 1)) {
  1839. return self::$_errorCodes['num'];
  1840. }
  1841. if (($probability < 0) || ($probability > 1)) {
  1842. return self::$_errorCodes['num'];
  1843. }
  1844. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  1845. if (($failures + $successes - 1) <= 0) {
  1846. return self::$_errorCodes['num'];
  1847. }
  1848. }
  1849. return (self::COMBIN($failures + $successes - 1,$successes - 1)) * (pow($probability,$successes)) * (pow(1 - $probability,$failures)) ;
  1850. }
  1851. return self::$_errorCodes['value'];
  1852. }
  1853. /**
  1854. * CRITBINOM
  1855. *
  1856. * Returns the smallest value for which the cumulative binomial distribution is greater
  1857. * than or equal to a criterion value
  1858. *
  1859. * See http://support.microsoft.com/kb/828117/ for details of the algorithm used
  1860. *
  1861. * @param float $trials number of Bernoulli trials
  1862. * @param float $probability probability of a success on each trial
  1863. * @param float $alpha criterion value
  1864. * @return int
  1865. *
  1866. * @todo Warning. This implementation differs from the algorithm detailed on the MS
  1867. * web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess
  1868. * This eliminates a potential endless loop error, but may have an adverse affect on the
  1869. * accuracy of the function (although all my tests have so far returned correct results).
  1870. *
  1871. */
  1872. public static function CRITBINOM($trials, $probability, $alpha) {
  1873. $trials = floor(self::flattenSingleValue($trials));
  1874. $probability = self::flattenSingleValue($probability);
  1875. $alpha = self::flattenSingleValue($alpha);
  1876. if ((is_numeric($trials)) && (is_numeric($probability)) && (is_numeric($alpha))) {
  1877. if ($trials < 0) {
  1878. return self::$_errorCodes['num'];
  1879. }
  1880. if (($probability < 0) || ($probability > 1)) {
  1881. return self::$_errorCodes['num'];
  1882. }
  1883. if (($alpha < 0) || ($alpha > 1)) {
  1884. return self::$_errorCodes['num'];
  1885. }
  1886. if ($alpha <= 0.5) {
  1887. $t = sqrt(log(1 / pow($alpha,2)));
  1888. $trialsApprox = 0 - ($t + (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t));
  1889. } else {
  1890. $t = sqrt(log(1 / pow(1 - $alpha,2)));
  1891. $trialsApprox = $t - (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t);
  1892. }
  1893. $Guess = floor($trials * $probability + $trialsApprox * sqrt($trials * $probability * (1 - $probability)));
  1894. if ($Guess < 0) {
  1895. $Guess = 0;
  1896. } elseif ($Guess > $trials) {
  1897. $Guess = $trials;
  1898. }
  1899. $TotalUnscaledProbability = $UnscaledPGuess = $UnscaledCumPGuess = 0.0;
  1900. $EssentiallyZero = 10e-12;
  1901. $m = floor($trials * $probability);
  1902. ++$TotalUnscaledProbability;
  1903. if ($m == $Guess) { ++$UnscaledPGuess; }
  1904. if ($m <= $Guess) { ++$UnscaledCumPGuess; }
  1905. $PreviousValue = 1;
  1906. $Done = False;
  1907. $k = $m + 1;
  1908. while ((!$Done) && ($k <= $trials)) {
  1909. $CurrentValue = $PreviousValue * ($trials - $k + 1) * $probability / ($k * (1 - $probability));
  1910. $TotalUnscaledProbability += $CurrentValue;
  1911. if ($k == $Guess) { $UnscaledPGuess += $CurrentValue; }
  1912. if ($k <= $Guess) { $UnscaledCumPGuess += $CurrentValue; }
  1913. if ($CurrentValue <= $EssentiallyZero) { $Done = True; }
  1914. $PreviousValue = $CurrentValue;
  1915. ++$k;
  1916. }
  1917. $PreviousValue = 1;
  1918. $Done = False;
  1919. $k = $m - 1;
  1920. while ((!$Done) && ($k >= 0)) {
  1921. $CurrentValue = $PreviousValue * $k + 1 * (1 - $probability) / (($trials - $k) * $probability);
  1922. $TotalUnscaledProbability += $CurrentValue;
  1923. if ($k == $Guess) { $UnscaledPGuess += $CurrentValue; }
  1924. if ($k <= $Guess) { $UnscaledCumPGuess += $CurrentValue; }
  1925. if (CurrentValue <= EssentiallyZero) { $Done = True; }
  1926. $PreviousValue = $CurrentValue;
  1927. --$k;
  1928. }
  1929. $PGuess = $UnscaledPGuess / $TotalUnscaledProbability;
  1930. $CumPGuess = $UnscaledCumPGuess / $TotalUnscaledProbability;
  1931. // $CumPGuessMinus1 = $CumPGuess - $PGuess;
  1932. $CumPGuessMinus1 = $CumPGuess - 1;
  1933. while (True) {
  1934. if (($CumPGuessMinus1 < $alpha) && ($CumPGuess >= $alpha)) {
  1935. return $Guess;
  1936. } elseif (($CumPGuessMinus1 < $alpha) && ($CumPGuess < $alpha)) {
  1937. $PGuessPlus1 = $PGuess * ($trials - $Guess) * $probability / $Guess / (1 - $probability);
  1938. $CumPGuessMinus1 = $CumPGuess;
  1939. $CumPGuess = $CumPGuess + $PGuessPlus1;
  1940. $PGuess = $PGuessPlus1;
  1941. ++$Guess;
  1942. } elseif (($CumPGuessMinus1 >= $alpha) && ($CumPGuess >= $alpha)) {
  1943. $PGuessMinus1 = $PGuess * $Guess * (1 - $probability) / ($trials - $Guess + 1) / $probability;
  1944. $CumPGuess = $CumPGuessMinus1;
  1945. $CumPGuessMinus1 = $CumPGuessMinus1 - $PGuess;
  1946. $PGuess = $PGuessMinus1;
  1947. --$Guess;
  1948. }
  1949. }
  1950. }
  1951. return self::$_errorCodes['value'];
  1952. }
  1953. /**
  1954. * CHIDIST
  1955. *
  1956. * Returns the one-tailed probability of the chi-squared distribution.
  1957. *
  1958. * @param float $value Value for the function
  1959. * @param float $degrees degrees of freedom
  1960. * @return float
  1961. */
  1962. public static function CHIDIST($value, $degrees) {
  1963. $value = self::flattenSingleValue($value);
  1964. $degrees = floor(self::flattenSingleValue($degrees));
  1965. if ((is_numeric($value)) && (is_numeric($degrees))) {
  1966. if ($degrees < 1) {
  1967. return self::$_errorCodes['num'];
  1968. }
  1969. if ($value < 0) {
  1970. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  1971. return 1;
  1972. }
  1973. return self::$_errorCodes['num'];
  1974. }
  1975. return 1 - (self::incompleteGamma($degrees/2,$value/2) / self::gamma($degrees/2));
  1976. }
  1977. return self::$_errorCodes['value'];
  1978. }
  1979. /**
  1980. * CHIINV
  1981. *
  1982. * Returns the one-tailed probability of the chi-squared distribution.
  1983. *
  1984. * @param float $probability Probability for the function
  1985. * @param float $degrees degrees of freedom
  1986. * @return float
  1987. */
  1988. public static function CHIINV($probability, $degrees) {
  1989. $probability = self::flattenSingleValue($probability);
  1990. $degrees = floor(self::flattenSingleValue($degrees));
  1991. if ((is_numeric($probability)) && (is_numeric($degrees))) {
  1992. $xLo = 100;
  1993. $xHi = 0;
  1994. $maxIteration = 100;
  1995. $x = $xNew = 1;
  1996. $dx = 1;
  1997. $i = 0;
  1998. while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
  1999. // Apply Newton-Raphson step
  2000. $result = self::CHIDIST($x, $degrees);
  2001. $error = $result - $probability;
  2002. if ($error == 0.0) {
  2003. $dx = 0;
  2004. } elseif ($error < 0.0) {
  2005. $xLo = $x;
  2006. } else {
  2007. $xHi = $x;
  2008. }
  2009. // Avoid division by zero
  2010. if ($result != 0.0) {
  2011. $dx = $error / $result;
  2012. $xNew = $x - $dx;
  2013. }
  2014. // If the NR fails to converge (which for example may be the
  2015. // case if the initial guess is too rough) we apply a bisection
  2016. // step to determine a more narrow interval around the root.
  2017. if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
  2018. $xNew = ($xLo + $xHi) / 2;
  2019. $dx = $xNew - $x;
  2020. }
  2021. $x = $xNew;
  2022. }
  2023. if ($i == MAX_ITERATIONS) {
  2024. return self::$_errorCodes['na'];
  2025. }
  2026. return round($x,12);
  2027. }
  2028. return self::$_errorCodes['value'];
  2029. }
  2030. /**
  2031. * EXPONDIST
  2032. *
  2033. * Returns the exponential distribution. Use EXPONDIST to model the time between events,
  2034. * such as how long an automated bank teller takes to deliver cash. For example, you can
  2035. * use EXPONDIST to determine the probability that the process takes at most 1 minute.
  2036. *
  2037. * @param float $value Value of the function
  2038. * @param float $lambda The parameter value
  2039. * @param boolean $cumulative
  2040. * @return float
  2041. */
  2042. public static function EXPONDIST($value, $lambda, $cumulative) {
  2043. $value = self::flattenSingleValue($value);
  2044. $lambda = self::flattenSingleValue($lambda);
  2045. $cumulative = self::flattenSingleValue($cumulative);
  2046. if ((is_numeric($value)) && (is_numeric($lambda))) {
  2047. if (($value < 0) || ($lambda < 0)) {
  2048. return self::$_errorCodes['num'];
  2049. }
  2050. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  2051. if ($cumulative) {
  2052. return 1 - exp(0-$value*$lambda);
  2053. } else {
  2054. return $lambda * exp(0-$value*$lambda);
  2055. }
  2056. }
  2057. }
  2058. return self::$_errorCodes['value'];
  2059. }
  2060. /**
  2061. * FISHER
  2062. *
  2063. * Returns the Fisher transformation at x. This transformation produces a function that
  2064. * is normally distributed rather than skewed. Use this function to perform hypothesis
  2065. * testing on the correlation coefficient.
  2066. *
  2067. * @param float $value
  2068. * @return float
  2069. */
  2070. public static function FISHER($value) {
  2071. $value = self::flattenSingleValue($value);
  2072. if (is_numeric($value)) {
  2073. if (($value <= -1) || ($lambda >= 1)) {
  2074. return self::$_errorCodes['num'];
  2075. }
  2076. return 0.5 * log((1+$value)/(1-$value));
  2077. }
  2078. return self::$_errorCodes['value'];
  2079. }
  2080. /**
  2081. * FISHERINV
  2082. *
  2083. * Returns the inverse of the Fisher transformation. Use this transformation when
  2084. * analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
  2085. * FISHERINV(y) = x.
  2086. *
  2087. * @param float $value
  2088. * @return float
  2089. */
  2090. public static function FISHERINV($value) {
  2091. $value = self::flattenSingleValue($value);
  2092. if (is_numeric($value)) {
  2093. return (exp(2 * $value) - 1) / (exp(2 * $value) + 1);
  2094. }
  2095. return self::$_errorCodes['value'];
  2096. }
  2097. // Function cache for logBeta
  2098. private static $logBetaCache_p = 0.0;
  2099. private static $logBetaCache_q = 0.0;
  2100. private static $logBetaCache_result = 0.0;
  2101. /**
  2102. * The natural logarithm of the beta function.
  2103. * @param p require p>0
  2104. * @param q require q>0
  2105. * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
  2106. * @author Jaco van Kooten
  2107. */
  2108. private static function logBeta($p, $q) {
  2109. if ($p != self::$logBetaCache_p || $q != self::$logBetaCache_q) {
  2110. self::$logBetaCache_p = $p;
  2111. self::$logBetaCache_q = $q;
  2112. if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
  2113. self::$logBetaCache_result = 0.0;
  2114. } else {
  2115. self::$logBetaCache_result = self::logGamma($p) + self::logGamma($q) - self::logGamma($p + $q);
  2116. }
  2117. }
  2118. return self::$logBetaCache_result;
  2119. }
  2120. /**
  2121. * Evaluates of continued fraction part of incomplete beta function.
  2122. * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
  2123. * @author Jaco van Kooten
  2124. */
  2125. private static function betaFraction($x, $p, $q) {
  2126. $c = 1.0;
  2127. $sum_pq = $p + $q;
  2128. $p_plus = $p + 1.0;
  2129. $p_minus = $p - 1.0;
  2130. $h = 1.0 - $sum_pq * $x / $p_plus;
  2131. if (abs($h) < XMININ) {
  2132. $h = XMININ;
  2133. }
  2134. $h = 1.0 / $h;
  2135. $frac = $h;
  2136. $m = 1;
  2137. $delta = 0.0;
  2138. while ($m <= MAX_ITERATIONS && abs($delta-1.0) > PRECISION ) {
  2139. $m2 = 2 * $m;
  2140. // even index for d
  2141. $d = $m * ($q - $m) * $x / ( ($p_minus + $m2) * ($p + $m2));
  2142. $h = 1.0 + $d * $h;
  2143. if (abs($h) < XMININ) {
  2144. $h = XMININ;
  2145. }
  2146. $h = 1.0 / $h;
  2147. $c = 1.0 + $d / $c;
  2148. if (abs($c) < XMININ) {
  2149. $c = XMININ;
  2150. }
  2151. $frac *= $h * $c;
  2152. // odd index for d
  2153. $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
  2154. $h = 1.0 + $d * $h;
  2155. if (abs($h) < XMININ) {
  2156. $h = XMININ;
  2157. }
  2158. $h = 1.0 / $h;
  2159. $c = 1.0 + $d / $c;
  2160. if (abs($c) < XMININ) {
  2161. $c = XMININ;
  2162. }
  2163. $delta = $h * $c;
  2164. $frac *= $delta;
  2165. ++$m;
  2166. }
  2167. return $frac;
  2168. }
  2169. /**
  2170. * logGamma function
  2171. *
  2172. * @version 1.1
  2173. * @author Jaco van Kooten
  2174. *
  2175. * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
  2176. *
  2177. * The natural logarithm of the gamma function. <br />
  2178. * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz <br />
  2179. * Applied Mathematics Division <br />
  2180. * Argonne National Laboratory <br />
  2181. * Argonne, IL 60439 <br />
  2182. * <p>
  2183. * References:
  2184. * <ol>
  2185. * <li>W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural
  2186. * Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.</li>
  2187. * <li>K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.</li>
  2188. * <li>Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.</li>
  2189. * </ol>
  2190. * </p>
  2191. * <p>
  2192. * From the original documentation:
  2193. * </p>
  2194. * <p>
  2195. * This routine calculates the LOG(GAMMA) function for a positive real argument X.
  2196. * Computation is based on an algorithm outlined in references 1 and 2.
  2197. * The program uses rational functions that theoretically approximate LOG(GAMMA)
  2198. * to at least 18 significant decimal digits. The approximation for X > 12 is from
  2199. * reference 3, while approximations for X < 12.0 are similar to those in reference
  2200. * 1, but are unpublished. The accuracy achieved depends on the arithmetic system,
  2201. * the compiler, the intrinsic functions, and proper selection of the
  2202. * machine-dependent constants.
  2203. * </p>
  2204. * <p>
  2205. * Error returns: <br />
  2206. * The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
  2207. * The computation is believed to be free of underflow and overflow.
  2208. * </p>
  2209. * @return MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
  2210. */
  2211. // Function cache for logGamma
  2212. private static $logGammaCache_result = 0.0;
  2213. private static $logGammaCache_x = 0.0;
  2214. private static function logGamma($x) {
  2215. // Log Gamma related constants
  2216. static $lg_d1 = -0.5772156649015328605195174;
  2217. static $lg_d2 = 0.4227843350984671393993777;
  2218. static $lg_d4 = 1.791759469228055000094023;
  2219. static $lg_p1 = array( 4.945235359296727046734888,
  2220. 201.8112620856775083915565,
  2221. 2290.838373831346393026739,
  2222. 11319.67205903380828685045,
  2223. 28557.24635671635335736389,
  2224. 38484.96228443793359990269,
  2225. 26377.48787624195437963534,
  2226. 7225.813979700288197698961 );
  2227. static $lg_p2 = array( 4.974607845568932035012064,
  2228. 542.4138599891070494101986,
  2229. 15506.93864978364947665077,
  2230. 184793.2904445632425417223,
  2231. 1088204.76946882876749847,
  2232. 3338152.967987029735917223,
  2233. 5106661.678927352456275255,
  2234. 3074109.054850539556250927 );
  2235. static $lg_p4 = array( 14745.02166059939948905062,
  2236. 2426813.369486704502836312,
  2237. 121475557.4045093227939592,
  2238. 2663432449.630976949898078,
  2239. 29403789566.34553899906876,
  2240. 170266573776.5398868392998,
  2241. 492612579337.743088758812,
  2242. 560625185622.3951465078242 );
  2243. static $lg_q1 = array( 67.48212550303777196073036,
  2244. 1113.332393857199323513008,
  2245. 7738.757056935398733233834,
  2246. 27639.87074403340708898585,
  2247. 54993.10206226157329794414,
  2248. 61611.22180066002127833352,
  2249. 36351.27591501940507276287,
  2250. 8785.536302431013170870835 );
  2251. static $lg_q2 = array( 183.0328399370592604055942,
  2252. 7765.049321445005871323047,
  2253. 133190.3827966074194402448,
  2254. 1136705.821321969608938755,
  2255. 5267964.117437946917577538,
  2256. 13467014.54311101692290052,
  2257. 17827365.30353274213975932,
  2258. 9533095.591844353613395747 );
  2259. static $lg_q4 = array( 2690.530175870899333379843,
  2260. 639388.5654300092398984238,
  2261. 41355999.30241388052042842,
  2262. 1120872109.61614794137657,
  2263. 14886137286.78813811542398,
  2264. 101680358627.2438228077304,
  2265. 341747634550.7377132798597,
  2266. 446315818741.9713286462081 );
  2267. static $lg_c = array( -0.001910444077728,
  2268. 8.4171387781295e-4,
  2269. -5.952379913043012e-4,
  2270. 7.93650793500350248e-4,
  2271. -0.002777777777777681622553,
  2272. 0.08333333333333333331554247,
  2273. 0.0057083835261 );
  2274. // Rough estimate of the fourth root of logGamma_xBig
  2275. static $lg_frtbig = 2.25e76;
  2276. static $pnt68 = 0.6796875;
  2277. if ($x == self::$logGammaCache_x) {
  2278. return self::$logGammaCache_result;
  2279. }
  2280. $y = $x;
  2281. if ($y > 0.0 && $y <= LOG_GAMMA_X_MAX_VALUE) {
  2282. if ($y <= EPS) {
  2283. $res = -log(y);
  2284. } else if ($y <= 1.5) {
  2285. // ---------------------
  2286. // EPS .LT. X .LE. 1.5
  2287. // ---------------------
  2288. if ($y < $pnt68) {
  2289. $corr = -log($y);
  2290. $xm1 = $y;
  2291. } else {
  2292. $corr = 0.0;
  2293. $xm1 = $y - 1.0;
  2294. }
  2295. if ($y <= 0.5 || $y >= $pnt68) {
  2296. $xden = 1.0;
  2297. $xnum = 0.0;
  2298. for ($i = 0; $i < 8; ++$i) {
  2299. $xnum = $xnum * $xm1 + $lg_p1[$i];
  2300. $xden = $xden * $xm1 + $lg_q1[$i];
  2301. }
  2302. $res = $corr + $xm1 * ($lg_d1 + $xm1 * ($xnum / $xden));
  2303. } else {
  2304. $xm2 = $y - 1.0;
  2305. $xden = 1.0;
  2306. $xnum = 0.0;
  2307. for ($i = 0; $i < 8; ++$i) {
  2308. $xnum = $xnum * $xm2 + $lg_p2[$i];
  2309. $xden = $xden * $xm2 + $lg_q2[$i];
  2310. }
  2311. $res = $corr + $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
  2312. }
  2313. } else if ($y <= 4.0) {
  2314. // ---------------------
  2315. // 1.5 .LT. X .LE. 4.0
  2316. // ---------------------
  2317. $xm2 = $y - 2.0;
  2318. $xden = 1.0;
  2319. $xnum = 0.0;
  2320. for ($i = 0; $i < 8; ++$i) {
  2321. $xnum = $xnum * $xm2 + $lg_p2[$i];
  2322. $xden = $xden * $xm2 + $lg_q2[$i];
  2323. }
  2324. $res = $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
  2325. } else if ($y <= 12.0) {
  2326. // ----------------------
  2327. // 4.0 .LT. X .LE. 12.0
  2328. // ----------------------
  2329. $xm4 = $y - 4.0;
  2330. $xden = -1.0;
  2331. $xnum = 0.0;
  2332. for ($i = 0; $i < 8; ++$i) {
  2333. $xnum = $xnum * $xm4 + $lg_p4[$i];
  2334. $xden = $xden * $xm4 + $lg_q4[$i];
  2335. }
  2336. $res = $lg_d4 + $xm4 * ($xnum / $xden);
  2337. } else {
  2338. // ---------------------------------
  2339. // Evaluate for argument .GE. 12.0
  2340. // ---------------------------------
  2341. $res = 0.0;
  2342. if ($y <= $lg_frtbig) {
  2343. $res = $lg_c[6];
  2344. $ysq = $y * $y;
  2345. for ($i = 0; $i < 6; ++$i)
  2346. $res = $res / $ysq + $lg_c[$i];
  2347. }
  2348. $res /= $y;
  2349. $corr = log($y);
  2350. $res = $res + log(SQRT2PI) - 0.5 * $corr;
  2351. $res += $y * ($corr - 1.0);
  2352. }
  2353. } else {
  2354. // --------------------------
  2355. // Return for bad arguments
  2356. // --------------------------
  2357. $res = MAX_VALUE;
  2358. }
  2359. // ------------------------------
  2360. // Final adjustments and return
  2361. // ------------------------------
  2362. self::$logGammaCache_x = $x;
  2363. self::$logGammaCache_result = $res;
  2364. return $res;
  2365. }
  2366. /**
  2367. * Beta function.
  2368. *
  2369. * @author Jaco van Kooten
  2370. *
  2371. * @param p require p>0
  2372. * @param q require q>0
  2373. * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
  2374. */
  2375. private static function beta($p, $q) {
  2376. if ($p <= 0.0 || $q <= 0.0 || ($p + $q) > LOG_GAMMA_X_MAX_VALUE) {
  2377. return 0.0;
  2378. } else {
  2379. return exp(self::logBeta($p, $q));
  2380. }
  2381. }
  2382. /**
  2383. * Incomplete beta function
  2384. *
  2385. * @author Jaco van Kooten
  2386. * @author Paul Meagher
  2387. *
  2388. * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
  2389. * @param x require 0<=x<=1
  2390. * @param p require p>0
  2391. * @param q require q>0
  2392. * @return 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
  2393. */
  2394. private static function incompleteBeta($x, $p, $q) {
  2395. if ($x <= 0.0) {
  2396. return 0.0;
  2397. } elseif ($x >= 1.0) {
  2398. return 1.0;
  2399. } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
  2400. return 0.0;
  2401. }
  2402. $beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
  2403. if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
  2404. return $beta_gam * self::betaFraction($x, $p, $q) / $p;
  2405. } else {
  2406. return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q);
  2407. }
  2408. }
  2409. /**
  2410. * BETADIST
  2411. *
  2412. * Returns the beta distribution.
  2413. *
  2414. * @param float $value Value at which you want to evaluate the distribution
  2415. * @param float $alpha Parameter to the distribution
  2416. * @param float $beta Parameter to the distribution
  2417. * @param boolean $cumulative
  2418. * @return float
  2419. *
  2420. */
  2421. public static function BETADIST($value,$alpha,$beta,$rMin=0,$rMax=1) {
  2422. $value = self::flattenSingleValue($value);
  2423. $alpha = self::flattenSingleValue($alpha);
  2424. $beta = self::flattenSingleValue($beta);
  2425. $rMin = self::flattenSingleValue($rMin);
  2426. $rMax = self::flattenSingleValue($rMax);
  2427. if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
  2428. if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
  2429. return self::$_errorCodes['num'];
  2430. }
  2431. if ($rMin > $rMax) {
  2432. $tmp = $rMin;
  2433. $rMin = $rMax;
  2434. $rMax = $tmp;
  2435. }
  2436. $value -= $rMin;
  2437. $value /= ($rMax - $rMin);
  2438. return self::incompleteBeta($value,$alpha,$beta);
  2439. }
  2440. return self::$_errorCodes['value'];
  2441. }
  2442. /**
  2443. * BETAINV
  2444. *
  2445. * Returns the inverse of the beta distribution.
  2446. *
  2447. * @param float $probability Probability at which you want to evaluate the distribution
  2448. * @param float $alpha Parameter to the distribution
  2449. * @param float $beta Parameter to the distribution
  2450. * @param boolean $cumulative
  2451. * @return float
  2452. *
  2453. */
  2454. public static function BETAINV($probability,$alpha,$beta,$rMin=0,$rMax=1) {
  2455. $probability = self::flattenSingleValue($probability);
  2456. $alpha = self::flattenSingleValue($alpha);
  2457. $beta = self::flattenSingleValue($beta);
  2458. $rMin = self::flattenSingleValue($rMin);
  2459. $rMax = self::flattenSingleValue($rMax);
  2460. if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
  2461. if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0) || ($probability > 1)) {
  2462. return self::$_errorCodes['num'];
  2463. }
  2464. if ($rMin > $rMax) {
  2465. $tmp = $rMin;
  2466. $rMin = $rMax;
  2467. $rMax = $tmp;
  2468. }
  2469. $a = 0;
  2470. $b = 2;
  2471. $maxIteration = 100;
  2472. $i = 0;
  2473. while ((($b - $a) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
  2474. $guess = ($a + $b) / 2;
  2475. $result = self::BETADIST($guess, $alpha, $beta);
  2476. if (($result == $probability) || ($result == 0)) {
  2477. $b = $a;
  2478. } elseif ($result > $probability) {
  2479. $b = $guess;
  2480. } else {
  2481. $a = $guess;
  2482. }
  2483. }
  2484. if ($i == MAX_ITERATIONS) {
  2485. return self::$_errorCodes['na'];
  2486. }
  2487. return round($rMin + $guess * ($rMax - $rMin),12);
  2488. }
  2489. return self::$_errorCodes['value'];
  2490. }
  2491. //
  2492. // Private implementation of the incomplete Gamma function
  2493. //
  2494. private static function incompleteGamma($a,$x) {
  2495. static $max = 32;
  2496. $summer = 0;
  2497. for ($n=0; $n<=$max; ++$n) {
  2498. $divisor = $a;
  2499. for ($i=1; $i<=$n; ++$i) {
  2500. $divisor *= ($a + $i);
  2501. }
  2502. $summer += (pow($x,$n) / $divisor);
  2503. }
  2504. return pow($x,$a) * exp(0-$x) * $summer;
  2505. }
  2506. //
  2507. // Private implementation of the Gamma function
  2508. //
  2509. private static function gamma($data) {
  2510. if ($data == 0.0) return 0;
  2511. static $p0 = 1.000000000190015;
  2512. static $p = array ( 1 => 76.18009172947146,
  2513. 2 => -86.50532032941677,
  2514. 3 => 24.01409824083091,
  2515. 4 => -1.231739572450155,
  2516. 5 => 1.208650973866179e-3,
  2517. 6 => -5.395239384953e-6
  2518. );
  2519. $y = $x = $data;
  2520. $tmp = $x + 5.5;
  2521. $tmp -= ($x + 0.5) * log($tmp);
  2522. $summer = $p0;
  2523. for ($j=1;$j<=6;++$j) {
  2524. $summer += ($p[$j] / ++$y);
  2525. }
  2526. return exp(0 - $tmp + log(2.5066282746310005 * $summer / $x));
  2527. }
  2528. /**
  2529. * GAMMADIST
  2530. *
  2531. * Returns the gamma distribution.
  2532. *
  2533. * @param float $value Value at which you want to evaluate the distribution
  2534. * @param float $a Parameter to the distribution
  2535. * @param float $b Parameter to the distribution
  2536. * @param boolean $cumulative
  2537. * @return float
  2538. *
  2539. */
  2540. public static function GAMMADIST($value,$a,$b,$cumulative) {
  2541. $value = self::flattenSingleValue($value);
  2542. $a = self::flattenSingleValue($a);
  2543. $b = self::flattenSingleValue($b);
  2544. if ((is_numeric($value)) && (is_numeric($a)) && (is_numeric($b))) {
  2545. if (($value < 0) || ($a <= 0) || ($b <= 0)) {
  2546. return self::$_errorCodes['num'];
  2547. }
  2548. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  2549. if ($cumulative) {
  2550. return self::incompleteGamma($a,$value / $b) / self::gamma($a);
  2551. } else {
  2552. return (1 / (pow($b,$a) * self::gamma($a))) * pow($value,$a-1) * exp(0-($value / $b));
  2553. }
  2554. }
  2555. }
  2556. return self::$_errorCodes['value'];
  2557. }
  2558. /**
  2559. * GAMMAINV
  2560. *
  2561. * Returns the inverse of the beta distribution.
  2562. *
  2563. * @param float $probability Probability at which you want to evaluate the distribution
  2564. * @param float $alpha Parameter to the distribution
  2565. * @param float $beta Parameter to the distribution
  2566. * @param boolean $cumulative
  2567. * @return float
  2568. *
  2569. */
  2570. public static function GAMMAINV($probability,$alpha,$beta) {
  2571. $probability = self::flattenSingleValue($probability);
  2572. $alpha = self::flattenSingleValue($alpha);
  2573. $beta = self::flattenSingleValue($beta);
  2574. $rMin = self::flattenSingleValue($rMin);
  2575. $rMax = self::flattenSingleValue($rMax);
  2576. if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta))) {
  2577. if (($alpha <= 0) || ($beta <= 0) || ($probability <= 0) || ($probability > 1)) {
  2578. return self::$_errorCodes['num'];
  2579. }
  2580. $xLo = 0;
  2581. $xHi = 100;
  2582. $maxIteration = 100;
  2583. $x = $xNew = 1;
  2584. $dx = 1;
  2585. $i = 0;
  2586. while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
  2587. // Apply Newton-Raphson step
  2588. $result = self::GAMMADIST($x, $alpha, $beta, True);
  2589. $error = $result - $probability;
  2590. if ($error == 0.0) {
  2591. $dx = 0;
  2592. } elseif ($error < 0.0) {
  2593. $xLo = $x;
  2594. } else {
  2595. $xHi = $x;
  2596. }
  2597. // Avoid division by zero
  2598. if ($result != 0.0) {
  2599. $dx = $error / $result;
  2600. $xNew = $x - $dx;
  2601. }
  2602. // If the NR fails to converge (which for example may be the
  2603. // case if the initial guess is too rough) we apply a bisection
  2604. // step to determine a more narrow interval around the root.
  2605. if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
  2606. $xNew = ($xLo + $xHi) / 2;
  2607. $dx = $xNew - $x;
  2608. }
  2609. $x = $xNew;
  2610. }
  2611. if ($i == MAX_ITERATIONS) {
  2612. return self::$_errorCodes['na'];
  2613. }
  2614. return round($x,12);
  2615. }
  2616. return self::$_errorCodes['value'];
  2617. }
  2618. /**
  2619. * GAMMALN
  2620. *
  2621. * Returns the natural logarithm of the gamma function.
  2622. *
  2623. * @param float $value
  2624. * @return float
  2625. */
  2626. public static function GAMMALN($value) {
  2627. $value = self::flattenSingleValue($value);
  2628. if (is_numeric($value)) {
  2629. if ($value <= 0) {
  2630. return self::$_errorCodes['num'];
  2631. }
  2632. return log(self::gamma($value));
  2633. }
  2634. return self::$_errorCodes['value'];
  2635. }
  2636. /**
  2637. * NORMDIST
  2638. *
  2639. * Returns the normal distribution for the specified mean and standard deviation. This
  2640. * function has a very wide range of applications in statistics, including hypothesis
  2641. * testing.
  2642. *
  2643. * @param float $value
  2644. * @param float $mean Mean Value
  2645. * @param float $stdDev Standard Deviation
  2646. * @param boolean $cumulative
  2647. * @return float
  2648. *
  2649. */
  2650. public static function NORMDIST($value, $mean, $stdDev, $cumulative) {
  2651. $value = self::flattenSingleValue($value);
  2652. $mean = self::flattenSingleValue($mean);
  2653. $stdDev = self::flattenSingleValue($stdDev);
  2654. if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  2655. if ($stdDev < 0) {
  2656. return self::$_errorCodes['num'];
  2657. }
  2658. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  2659. if ($cumulative) {
  2660. return 0.5 * (1 + self::erfVal(($value - $mean) / ($stdDev * sqrt(2))));
  2661. } else {
  2662. return (1 / (SQRT2PI * $stdDev)) * exp(0 - (pow($value - $mean,2) / (2 * pow($stdDev,2))));
  2663. }
  2664. }
  2665. }
  2666. return self::$_errorCodes['value'];
  2667. }
  2668. /**
  2669. * NORMSDIST
  2670. *
  2671. * Returns the standard normal cumulative distribution function. The distribution has
  2672. * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
  2673. * table of standard normal curve areas.
  2674. *
  2675. * @param float $value
  2676. * @return float
  2677. */
  2678. public static function NORMSDIST($value) {
  2679. $value = self::flattenSingleValue($value);
  2680. return self::NORMDIST($value, 0, 1, True);
  2681. }
  2682. /**
  2683. * LOGNORMDIST
  2684. *
  2685. * Returns the cumulative lognormal distribution of x, where ln(x) is normally distributed
  2686. * with parameters mean and standard_dev.
  2687. *
  2688. * @param float $value
  2689. * @return float
  2690. */
  2691. public static function LOGNORMDIST($value, $mean, $stdDev) {
  2692. $value = self::flattenSingleValue($value);
  2693. $mean = self::flattenSingleValue($mean);
  2694. $stdDev = self::flattenSingleValue($stdDev);
  2695. if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  2696. if (($value <= 0) || ($stdDev <= 0)) {
  2697. return self::$_errorCodes['num'];
  2698. }
  2699. return self::NORMSDIST((log($value) - $mean) / $stdDev);
  2700. }
  2701. return self::$_errorCodes['value'];
  2702. }
  2703. /***************************************************************************
  2704. * inverse_ncdf.php
  2705. * -------------------
  2706. * begin : Friday, January 16, 2004
  2707. * copyright : (C) 2004 Michael Nickerson
  2708. * email : nickersonm@yahoo.com
  2709. *
  2710. ***************************************************************************/
  2711. private static function inverse_ncdf($p) {
  2712. // Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
  2713. // PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
  2714. // a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
  2715. // I have not checked the accuracy of this implementation. Be aware that PHP
  2716. // will truncate the coeficcients to 14 digits.
  2717. // You have permission to use and distribute this function freely for
  2718. // whatever purpose you want, but please show common courtesy and give credit
  2719. // where credit is due.
  2720. // Input paramater is $p - probability - where 0 < p < 1.
  2721. // Coefficients in rational approximations
  2722. static $a = array( 1 => -3.969683028665376e+01,
  2723. 2 => 2.209460984245205e+02,
  2724. 3 => -2.759285104469687e+02,
  2725. 4 => 1.383577518672690e+02,
  2726. 5 => -3.066479806614716e+01,
  2727. 6 => 2.506628277459239e+00
  2728. );
  2729. static $b = array( 1 => -5.447609879822406e+01,
  2730. 2 => 1.615858368580409e+02,
  2731. 3 => -1.556989798598866e+02,
  2732. 4 => 6.680131188771972e+01,
  2733. 5 => -1.328068155288572e+01
  2734. );
  2735. static $c = array( 1 => -7.784894002430293e-03,
  2736. 2 => -3.223964580411365e-01,
  2737. 3 => -2.400758277161838e+00,
  2738. 4 => -2.549732539343734e+00,
  2739. 5 => 4.374664141464968e+00,
  2740. 6 => 2.938163982698783e+00
  2741. );
  2742. static $d = array( 1 => 7.784695709041462e-03,
  2743. 2 => 3.224671290700398e-01,
  2744. 3 => 2.445134137142996e+00,
  2745. 4 => 3.754408661907416e+00
  2746. );
  2747. // Define lower and upper region break-points.
  2748. $p_low = 0.02425; //Use lower region approx. below this
  2749. $p_high = 1 - $p_low; //Use upper region approx. above this
  2750. if (0 < $p && $p < $p_low) {
  2751. // Rational approximation for lower region.
  2752. $q = sqrt(-2 * log($p));
  2753. return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
  2754. (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
  2755. } elseif ($p_low <= $p && $p <= $p_high) {
  2756. // Rational approximation for central region.
  2757. $q = $p - 0.5;
  2758. $r = $q * $q;
  2759. return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
  2760. ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
  2761. } elseif ($p_high < $p && $p < 1) {
  2762. // Rational approximation for upper region.
  2763. $q = sqrt(-2 * log(1 - $p));
  2764. return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
  2765. (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
  2766. }
  2767. // If 0 < p < 1, return a null value
  2768. return self::$_errorCodes['null'];
  2769. }
  2770. private static function inverse_ncdf2($prob) {
  2771. // Approximation of inverse standard normal CDF developed by
  2772. // B. Moro, "The Full Monte," Risk 8(2), Feb 1995, 57-58.
  2773. $a1 = 2.50662823884;
  2774. $a2 = -18.61500062529;
  2775. $a3 = 41.39119773534;
  2776. $a4 = -25.44106049637;
  2777. $b1 = -8.4735109309;
  2778. $b2 = 23.08336743743;
  2779. $b3 = -21.06224101826;
  2780. $b4 = 3.13082909833;
  2781. $c1 = 0.337475482272615;
  2782. $c2 = 0.976169019091719;
  2783. $c3 = 0.160797971491821;
  2784. $c4 = 2.76438810333863E-02;
  2785. $c5 = 3.8405729373609E-03;
  2786. $c6 = 3.951896511919E-04;
  2787. $c7 = 3.21767881768E-05;
  2788. $c8 = 2.888167364E-07;
  2789. $c9 = 3.960315187E-07;
  2790. $y = $prob - 0.5;
  2791. if (abs($y) < 0.42) {
  2792. $z = pow($y,2);
  2793. $z = $y * ((($a4 * $z + $a3) * $z + $a2) * $z + $a1) / (((($b4 * $z + $b3) * $z + $b2) * $z + $b1) * $z + 1);
  2794. } else {
  2795. if ($y > 0) {
  2796. $z = log(-log(1 - $prob));
  2797. } else {
  2798. $z = log(-log($prob));
  2799. }
  2800. $z = $c1 + $z * ($c2 + $z * ($c3 + $z * ($c4 + $z * ($c5 + $z * ($c6 + $z * ($c7 + $z * ($c8 + $z * $c9)))))));
  2801. if ($y < 0) {
  2802. $z = -$z;
  2803. }
  2804. }
  2805. return $z;
  2806. }
  2807. private static function inverse_ncdf3($p) {
  2808. // ALGORITHM AS241 APPL. STATIST. (1988) VOL. 37, NO. 3.
  2809. // Produces the normal deviate Z corresponding to a given lower
  2810. // tail area of P; Z is accurate to about 1 part in 10**16.
  2811. //
  2812. // This is a PHP version of the original FORTRAN code that can
  2813. // be found at http://lib.stat.cmu.edu/apstat/
  2814. $split1 = 0.425;
  2815. $split2 = 5;
  2816. $const1 = 0.180625;
  2817. $const2 = 1.6;
  2818. // coefficients for p close to 0.5
  2819. $a0 = 3.3871328727963666080;
  2820. $a1 = 1.3314166789178437745E+2;
  2821. $a2 = 1.9715909503065514427E+3;
  2822. $a3 = 1.3731693765509461125E+4;
  2823. $a4 = 4.5921953931549871457E+4;
  2824. $a5 = 6.7265770927008700853E+4;
  2825. $a6 = 3.3430575583588128105E+4;
  2826. $a7 = 2.5090809287301226727E+3;
  2827. $b1 = 4.2313330701600911252E+1;
  2828. $b2 = 6.8718700749205790830E+2;
  2829. $b3 = 5.3941960214247511077E+3;
  2830. $b4 = 2.1213794301586595867E+4;
  2831. $b5 = 3.9307895800092710610E+4;
  2832. $b6 = 2.8729085735721942674E+4;
  2833. $b7 = 5.2264952788528545610E+3;
  2834. // coefficients for p not close to 0, 0.5 or 1.
  2835. $c0 = 1.42343711074968357734;
  2836. $c1 = 4.63033784615654529590;
  2837. $c2 = 5.76949722146069140550;
  2838. $c3 = 3.64784832476320460504;
  2839. $c4 = 1.27045825245236838258;
  2840. $c5 = 2.41780725177450611770E-1;
  2841. $c6 = 2.27238449892691845833E-2;
  2842. $c7 = 7.74545014278341407640E-4;
  2843. $d1 = 2.05319162663775882187;
  2844. $d2 = 1.67638483018380384940;
  2845. $d3 = 6.89767334985100004550E-1;
  2846. $d4 = 1.48103976427480074590E-1;
  2847. $d5 = 1.51986665636164571966E-2;
  2848. $d6 = 5.47593808499534494600E-4;
  2849. $d7 = 1.05075007164441684324E-9;
  2850. // coefficients for p near 0 or 1.
  2851. $e0 = 6.65790464350110377720;
  2852. $e1 = 5.46378491116411436990;
  2853. $e2 = 1.78482653991729133580;
  2854. $e3 = 2.96560571828504891230E-1;
  2855. $e4 = 2.65321895265761230930E-2;
  2856. $e5 = 1.24266094738807843860E-3;
  2857. $e6 = 2.71155556874348757815E-5;
  2858. $e7 = 2.01033439929228813265E-7;
  2859. $f1 = 5.99832206555887937690E-1;
  2860. $f2 = 1.36929880922735805310E-1;
  2861. $f3 = 1.48753612908506148525E-2;
  2862. $f4 = 7.86869131145613259100E-4;
  2863. $f5 = 1.84631831751005468180E-5;
  2864. $f6 = 1.42151175831644588870E-7;
  2865. $f7 = 2.04426310338993978564E-15;
  2866. $q = $p - 0.5;
  2867. // computation for p close to 0.5
  2868. if (abs($q) <= split1) {
  2869. $R = $const1 - $q * $q;
  2870. $z = $q * ((((((($a7 * $R + $a6) * $R + $a5) * $R + $a4) * $R + $a3) * $R + $a2) * $R + $a1) * $R + $a0) /
  2871. ((((((($b7 * $R + $b6) * $R + $b5) * $R + $b4) * $R + $b3) * $R + $b2) * $R + $b1) * $R + 1);
  2872. } else {
  2873. if ($q < 0) {
  2874. $R = $p;
  2875. } else {
  2876. $R = 1 - $p;
  2877. }
  2878. $R = pow(-log($R),2);
  2879. // computation for p not close to 0, 0.5 or 1.
  2880. If ($R <= $split2) {
  2881. $R = $R - $const2;
  2882. $z = ((((((($c7 * $R + $c6) * $R + $c5) * $R + $c4) * $R + $c3) * $R + $c2) * $R + $c1) * $R + $c0) /
  2883. ((((((($d7 * $R + $d6) * $R + $d5) * $R + $d4) * $R + $d3) * $R + $d2) * $R + $d1) * $R + 1);
  2884. } else {
  2885. // computation for p near 0 or 1.
  2886. $R = $R - $split2;
  2887. $z = ((((((($e7 * $R + $e6) * $R + $e5) * $R + $e4) * $R + $e3) * $R + $e2) * $R + $e1) * $R + $e0) /
  2888. ((((((($f7 * $R + $f6) * $R + $f5) * $R + $f4) * $R + $f3) * $R + $f2) * $R + $f1) * $R + 1);
  2889. }
  2890. if ($q < 0) {
  2891. $z = -$z;
  2892. }
  2893. }
  2894. return $z;
  2895. }
  2896. /**
  2897. * NORMINV
  2898. *
  2899. * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
  2900. *
  2901. * @param float $value
  2902. * @param float $mean Mean Value
  2903. * @param float $stdDev Standard Deviation
  2904. * @return float
  2905. *
  2906. */
  2907. public static function NORMINV($probability,$mean,$stdDev) {
  2908. $probability = self::flattenSingleValue($probability);
  2909. $mean = self::flattenSingleValue($mean);
  2910. $stdDev = self::flattenSingleValue($stdDev);
  2911. if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  2912. if (($probability < 0) || ($probability > 1)) {
  2913. return self::$_errorCodes['num'];
  2914. }
  2915. if ($stdDev < 0) {
  2916. return self::$_errorCodes['num'];
  2917. }
  2918. return (self::inverse_ncdf($probability) * $stdDev) + $mean;
  2919. }
  2920. return self::$_errorCodes['value'];
  2921. }
  2922. /**
  2923. * NORMSINV
  2924. *
  2925. * Returns the inverse of the standard normal cumulative distribution
  2926. *
  2927. * @param float $value
  2928. * @return float
  2929. */
  2930. public static function NORMSINV($value) {
  2931. return self::NORMINV($value, 0, 1);
  2932. }
  2933. /**
  2934. * LOGINV
  2935. *
  2936. * Returns the inverse of the normal cumulative distribution
  2937. *
  2938. * @param float $value
  2939. * @return float
  2940. *
  2941. * @todo Try implementing P J Acklam's refinement algorithm for greater
  2942. * accuracy if I can get my head round the mathematics
  2943. * (as described at) http://home.online.no/~pjacklam/notes/invnorm/
  2944. */
  2945. public static function LOGINV($probability, $mean, $stdDev) {
  2946. $probability = self::flattenSingleValue($probability);
  2947. $mean = self::flattenSingleValue($mean);
  2948. $stdDev = self::flattenSingleValue($stdDev);
  2949. if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  2950. if (($probability < 0) || ($probability > 1) || ($stdDev <= 0)) {
  2951. return self::$_errorCodes['num'];
  2952. }
  2953. return exp($mean + $stdDev * self::NORMSINV($probability));
  2954. }
  2955. return self::$_errorCodes['value'];
  2956. }
  2957. /**
  2958. * HYPGEOMDIST
  2959. *
  2960. * Returns the hypergeometric distribution. HYPGEOMDIST returns the probability of a given number of
  2961. * sample successes, given the sample size, population successes, and population size.
  2962. *
  2963. * @param float $sampleSuccesses Number of successes in the sample
  2964. * @param float $sampleNumber Size of the sample
  2965. * @param float $populationSuccesses Number of successes in the population
  2966. * @param float $populationNumber Population size
  2967. * @return float
  2968. *
  2969. */
  2970. public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber) {
  2971. $sampleSuccesses = floor(self::flattenSingleValue($sampleSuccesses));
  2972. $sampleNumber = floor(self::flattenSingleValue($sampleNumber));
  2973. $populationSuccesses = floor(self::flattenSingleValue($populationSuccesses));
  2974. $populationNumber = floor(self::flattenSingleValue($populationNumber));
  2975. if ((is_numeric($sampleSuccesses)) && (is_numeric($sampleNumber)) && (is_numeric($populationSuccesses)) && (is_numeric($populationNumber))) {
  2976. if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
  2977. return self::$_errorCodes['num'];
  2978. }
  2979. if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
  2980. return self::$_errorCodes['num'];
  2981. }
  2982. if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
  2983. return self::$_errorCodes['num'];
  2984. }
  2985. return self::COMBIN($populationSuccesses,$sampleSuccesses) *
  2986. self::COMBIN($populationNumber - $populationSuccesses,$sampleNumber - $sampleSuccesses) /
  2987. self::COMBIN($populationNumber,$sampleNumber);
  2988. }
  2989. return self::$_errorCodes['value'];
  2990. }
  2991. public static function hypGeom($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber) {
  2992. return self::COMBIN($populationSuccesses,$sampleSuccesses) *
  2993. self::COMBIN($populationNumber - $populationSuccesses,$sampleNumber - $sampleSuccesses) /
  2994. self::COMBIN($populationNumber,$sampleNumber);
  2995. }
  2996. /**
  2997. * TDIST
  2998. *
  2999. * Returns the probability of Student's T distribution.
  3000. *
  3001. * @param float $value Value for the function
  3002. * @param float $degrees degrees of freedom
  3003. * @param float $tails number of tails (1 or 2)
  3004. * @return float
  3005. */
  3006. public static function TDIST($value, $degrees, $tails) {
  3007. $value = self::flattenSingleValue($value);
  3008. $degrees = floor(self::flattenSingleValue($degrees));
  3009. $tails = floor(self::flattenSingleValue($tails));
  3010. if ((is_numeric($value)) && (is_numeric($degrees)) && (is_numeric($tails))) {
  3011. if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
  3012. return self::$_errorCodes['num'];
  3013. }
  3014. // tdist, which finds the probability that corresponds to a given value
  3015. // of t with k degrees of freedom. This algorithm is translated from a
  3016. // pascal function on p81 of "Statistical Computing in Pascal" by D
  3017. // Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
  3018. // London). The above Pascal algorithm is itself a translation of the
  3019. // fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
  3020. // Laboratory as reported in (among other places) "Applied Statistics
  3021. // Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
  3022. // Horwood Ltd.; W. Sussex, England).
  3023. // $ta = 2 / pi();
  3024. $ta = 0.636619772367581;
  3025. $tterm = $degrees;
  3026. $ttheta = atan2($value,sqrt($tterm));
  3027. $tc = cos($ttheta);
  3028. $ts = sin($ttheta);
  3029. $tsum = 0;
  3030. if (($degrees % 2) == 1) {
  3031. $ti = 3;
  3032. $tterm = $tc;
  3033. } else {
  3034. $ti = 2;
  3035. $tterm = 1;
  3036. }
  3037. $tsum = $tterm;
  3038. while ($ti < $degrees) {
  3039. $tterm *= $tc * $tc * ($ti - 1) / $ti;
  3040. $tsum += $tterm;
  3041. $ti += 2;
  3042. }
  3043. $tsum *= $ts;
  3044. if (($degrees % 2) == 1) { $tsum = $ta * ($tsum + $ttheta); }
  3045. $tValue = 0.5 * (1 + $tsum);
  3046. if ($tails == 1) {
  3047. return 1 - abs($tValue);
  3048. } else {
  3049. return 1 - abs((1 - $tValue) - $tValue);
  3050. }
  3051. }
  3052. return self::$_errorCodes['value'];
  3053. }
  3054. /**
  3055. * TINV
  3056. *
  3057. * Returns the one-tailed probability of the chi-squared distribution.
  3058. *
  3059. * @param float $probability Probability for the function
  3060. * @param float $degrees degrees of freedom
  3061. * @return float
  3062. */
  3063. public static function TINV($probability, $degrees) {
  3064. $probability = self::flattenSingleValue($probability);
  3065. $degrees = floor(self::flattenSingleValue($degrees));
  3066. if ((is_numeric($probability)) && (is_numeric($degrees))) {
  3067. $xLo = 100;
  3068. $xHi = 0;
  3069. $maxIteration = 100;
  3070. $x = $xNew = 1;
  3071. $dx = 1;
  3072. $i = 0;
  3073. while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
  3074. // Apply Newton-Raphson step
  3075. $result = self::TDIST($x, $degrees, 2);
  3076. $error = $result - $probability;
  3077. if ($error == 0.0) {
  3078. $dx = 0;
  3079. } elseif ($error < 0.0) {
  3080. $xLo = $x;
  3081. } else {
  3082. $xHi = $x;
  3083. }
  3084. // Avoid division by zero
  3085. if ($result != 0.0) {
  3086. $dx = $error / $result;
  3087. $xNew = $x - $dx;
  3088. }
  3089. // If the NR fails to converge (which for example may be the
  3090. // case if the initial guess is too rough) we apply a bisection
  3091. // step to determine a more narrow interval around the root.
  3092. if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
  3093. $xNew = ($xLo + $xHi) / 2;
  3094. $dx = $xNew - $x;
  3095. }
  3096. $x = $xNew;
  3097. }
  3098. if ($i == MAX_ITERATIONS) {
  3099. return self::$_errorCodes['na'];
  3100. }
  3101. return round($x,12);
  3102. }
  3103. return self::$_errorCodes['value'];
  3104. }
  3105. /**
  3106. * CONFIDENCE
  3107. *
  3108. * Returns the confidence interval for a population mean
  3109. *
  3110. * @param float $alpha
  3111. * @param float $stdDev Standard Deviation
  3112. * @param float $size
  3113. * @return float
  3114. *
  3115. */
  3116. public static function CONFIDENCE($alpha,$stdDev,$size) {
  3117. $alpha = self::flattenSingleValue($alpha);
  3118. $stdDev = self::flattenSingleValue($stdDev);
  3119. $size = floor(self::flattenSingleValue($size));
  3120. if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
  3121. if (($alpha <= 0) || ($alpha >= 1)) {
  3122. return self::$_errorCodes['num'];
  3123. }
  3124. if (($stdDev <= 0) || ($size < 1)) {
  3125. return self::$_errorCodes['num'];
  3126. }
  3127. return self::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size);
  3128. }
  3129. return self::$_errorCodes['value'];
  3130. }
  3131. /**
  3132. * POISSON
  3133. *
  3134. * Returns the Poisson distribution. A common application of the Poisson distribution
  3135. * is predicting the number of events over a specific time, such as the number of
  3136. * cars arriving at a toll plaza in 1 minute.
  3137. *
  3138. * @param float $value
  3139. * @param float $mean Mean Value
  3140. * @param boolean $cumulative
  3141. * @return float
  3142. *
  3143. */
  3144. public static function POISSON($value, $mean, $cumulative) {
  3145. $value = self::flattenSingleValue($value);
  3146. $mean = self::flattenSingleValue($mean);
  3147. if ((is_numeric($value)) && (is_numeric($mean))) {
  3148. if (($value <= 0) || ($mean <= 0)) {
  3149. return self::$_errorCodes['num'];
  3150. }
  3151. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  3152. if ($cumulative) {
  3153. $summer = 0;
  3154. for ($i = 0; $i <= floor($value); ++$i) {
  3155. $summer += pow($mean,$i) / self::FACT($i);
  3156. }
  3157. return exp(0-$mean) * $summer;
  3158. } else {
  3159. return (exp(0-$mean) * pow($mean,$value)) / self::FACT($value);
  3160. }
  3161. }
  3162. }
  3163. return self::$_errorCodes['value'];
  3164. }
  3165. /**
  3166. * WEIBULL
  3167. *
  3168. * Returns the Weibull distribution. Use this distribution in reliability
  3169. * analysis, such as calculating a device's mean time to failure.
  3170. *
  3171. * @param float $value
  3172. * @param float $alpha Alpha Parameter
  3173. * @param float $beta Beta Parameter
  3174. * @param boolean $cumulative
  3175. * @return float
  3176. *
  3177. */
  3178. public static function WEIBULL($value, $alpha, $beta, $cumulative) {
  3179. $value = self::flattenSingleValue($value);
  3180. $alpha = self::flattenSingleValue($alpha);
  3181. $beta = self::flattenSingleValue($beta);
  3182. if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta))) {
  3183. if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
  3184. return self::$_errorCodes['num'];
  3185. }
  3186. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  3187. if ($cumulative) {
  3188. return 1 - exp(0 - pow($value / $beta,$alpha));
  3189. } else {
  3190. return ($alpha / pow($beta,$alpha)) * pow($value,$alpha - 1) * exp(0 - pow($value / $beta,$alpha));
  3191. }
  3192. }
  3193. }
  3194. return self::$_errorCodes['value'];
  3195. }
  3196. /**
  3197. * SKEW
  3198. *
  3199. * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
  3200. * of a distribution around its mean. Positive skewness indicates a distribution with an
  3201. * asymmetric tail extending toward more positive values. Negative skewness indicates a
  3202. * distribution with an asymmetric tail extending toward more negative values.
  3203. *
  3204. * @param array Data Series
  3205. * @return float
  3206. */
  3207. public static function SKEW() {
  3208. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  3209. $mean = PHPExcel_Calculation_Functions::AVERAGE($aArgs);
  3210. $stdDev = PHPExcel_Calculation_Functions::STDEV($aArgs);
  3211. $count = $summer = 0;
  3212. // Loop through arguments
  3213. foreach ($aArgs as $arg) {
  3214. // Is it a numeric value?
  3215. if ((is_numeric($arg)) && (!is_string($arg))) {
  3216. $summer += pow((($arg - $mean) / $stdDev),3) ;
  3217. ++$count;
  3218. }
  3219. }
  3220. // Return
  3221. if ($count > 2) {
  3222. return $summer * ($count / (($count-1) * ($count-2)));
  3223. }
  3224. return self::$_errorCodes['divisionbyzero'];
  3225. }
  3226. /**
  3227. * KURT
  3228. *
  3229. * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
  3230. * or flatness of a distribution compared with the normal distribution. Positive
  3231. * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
  3232. * relatively flat distribution.
  3233. *
  3234. * @param array Data Series
  3235. * @return float
  3236. */
  3237. public static function KURT() {
  3238. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  3239. $mean = PHPExcel_Calculation_Functions::AVERAGE($aArgs);
  3240. $stdDev = PHPExcel_Calculation_Functions::STDEV($aArgs);
  3241. if ($stdDev > 0) {
  3242. $count = $summer = 0;
  3243. // Loop through arguments
  3244. foreach ($aArgs as $arg) {
  3245. // Is it a numeric value?
  3246. if ((is_numeric($arg)) && (!is_string($arg))) {
  3247. $summer += pow((($arg - $mean) / $stdDev),4) ;
  3248. ++$count;
  3249. }
  3250. }
  3251. // Return
  3252. if ($count > 3) {
  3253. return $summer * ($count * ($count+1) / (($count-1) * ($count-2) * ($count-3))) - (3 * pow($count-1,2) / (($count-2) * ($count-3)));
  3254. }
  3255. }
  3256. return self::$_errorCodes['divisionbyzero'];
  3257. }
  3258. /**
  3259. * RAND
  3260. *
  3261. * @param int $min Minimal value
  3262. * @param int $max Maximal value
  3263. * @return int Random number
  3264. */
  3265. public static function RAND($min = 0, $max = 0) {
  3266. $min = self::flattenSingleValue($min);
  3267. $max = self::flattenSingleValue($max);
  3268. if ($min == 0 && $max == 0) {
  3269. return (rand(0,10000000)) / 10000000;
  3270. } else {
  3271. return rand($min, $max);
  3272. }
  3273. }
  3274. /**
  3275. * MOD
  3276. *
  3277. * @param int $a Dividend
  3278. * @param int $b Divisor
  3279. * @return int Remainder
  3280. */
  3281. public static function MOD($a = 1, $b = 1) {
  3282. $a = self::flattenSingleValue($a);
  3283. $b = self::flattenSingleValue($b);
  3284. return $a % $b;
  3285. }
  3286. /**
  3287. * ASCIICODE
  3288. *
  3289. * @param string $character Value
  3290. * @return int
  3291. */
  3292. public static function ASCIICODE($characters) {
  3293. $characters = self::flattenSingleValue($characters);
  3294. if (is_bool($characters)) {
  3295. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  3296. $characters = (int) $characters;
  3297. } else {
  3298. if ($characters) {
  3299. $characters = 'True';
  3300. } else {
  3301. $characters = 'False';
  3302. }
  3303. }
  3304. }
  3305. if (strlen($characters) > 0) {
  3306. return ord(substr($characters, 0, 1));
  3307. }
  3308. return self::$_errorCodes['value'];
  3309. }
  3310. /**
  3311. * CONCATENATE
  3312. *
  3313. * @return string
  3314. */
  3315. public static function CONCATENATE() {
  3316. // Return value
  3317. $returnValue = '';
  3318. // Loop trough arguments
  3319. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  3320. foreach ($aArgs as $arg) {
  3321. if (is_bool($arg)) {
  3322. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  3323. $arg = (int) $arg;
  3324. } else {
  3325. if ($arg) {
  3326. $arg = 'TRUE';
  3327. } else {
  3328. $arg = 'FALSE';
  3329. }
  3330. }
  3331. }
  3332. $returnValue .= $arg;
  3333. }
  3334. // Return
  3335. return $returnValue;
  3336. }
  3337. /**
  3338. * SEARCHSENSITIVE
  3339. *
  3340. * @param string $needle The string to look for
  3341. * @param string $haystack The string in which to look
  3342. * @param int $offset Offset within $haystack
  3343. * @return string
  3344. */
  3345. public static function SEARCHSENSITIVE($needle,$haystack,$offset=1) {
  3346. $needle = (string) self::flattenSingleValue($needle);
  3347. $haystack = (string) self::flattenSingleValue($haystack);
  3348. $offset = self::flattenSingleValue($offset);
  3349. if (($offset > 0) && (strlen($haystack) > $offset)) {
  3350. $pos = strpos($haystack, $needle, --$offset);
  3351. if ($pos !== false) {
  3352. return ++$pos;
  3353. }
  3354. }
  3355. return self::$_errorCodes['value'];
  3356. }
  3357. /**
  3358. * SEARCHINSENSITIVE
  3359. *
  3360. * @param string $needle The string to look for
  3361. * @param string $haystack The string in which to look
  3362. * @param int $offset Offset within $haystack
  3363. * @return string
  3364. */
  3365. public static function SEARCHINSENSITIVE($needle,$haystack,$offset=1) {
  3366. $needle = (string) self::flattenSingleValue($needle);
  3367. $haystack = (string) self::flattenSingleValue($haystack);
  3368. $offset = self::flattenSingleValue($offset);
  3369. if (($offset > 0) && (strlen($haystack) > $offset)) {
  3370. $pos = stripos($haystack, $needle, --$offset);
  3371. if ($pos !== false) {
  3372. return ++$pos;
  3373. }
  3374. }
  3375. return self::$_errorCodes['value'];
  3376. }
  3377. /**
  3378. * LEFT
  3379. *
  3380. * @param string $value Value
  3381. * @param int $chars Number of characters
  3382. * @return string
  3383. */
  3384. public static function LEFT($value = '', $chars = null) {
  3385. $value = self::flattenSingleValue($value);
  3386. $chars = self::flattenSingleValue($chars);
  3387. return substr($value, 0, $chars);
  3388. }
  3389. /**
  3390. * RIGHT
  3391. *
  3392. * @param string $value Value
  3393. * @param int $chars Number of characters
  3394. * @return string
  3395. */
  3396. public static function RIGHT($value = '', $chars = null) {
  3397. $value = self::flattenSingleValue($value);
  3398. $chars = self::flattenSingleValue($chars);
  3399. return substr($value, strlen($value) - $chars);
  3400. }
  3401. /**
  3402. * MID
  3403. *
  3404. * @param string $value Value
  3405. * @param int $start Start character
  3406. * @param int $chars Number of characters
  3407. * @return string
  3408. */
  3409. public static function MID($value = '', $start = 1, $chars = null) {
  3410. $value = self::flattenSingleValue($value);
  3411. $start = self::flattenSingleValue($start);
  3412. $chars = self::flattenSingleValue($chars);
  3413. return substr($value, --$start, $chars);
  3414. }
  3415. /**
  3416. * RETURNSTRING
  3417. *
  3418. * @param mixed $value Value to check
  3419. * @return boolean
  3420. */
  3421. public static function RETURNSTRING($testValue = '') {
  3422. $testValue = self::flattenSingleValue($testValue);
  3423. if (is_string($testValue)) {
  3424. return $testValue;
  3425. }
  3426. return Null;
  3427. }
  3428. /**
  3429. * TRIMSPACES
  3430. *
  3431. * @param mixed $value Value to check
  3432. * @return boolean
  3433. */
  3434. public static function TRIMSPACES($stringValue = '') {
  3435. $stringValue = self::flattenSingleValue($stringValue);
  3436. if (is_string($stringValue)) {
  3437. return str_replace(trim($stringValue),' ',' ');
  3438. }
  3439. return Null;
  3440. }
  3441. /**
  3442. * IS_BLANK
  3443. *
  3444. * @param mixed $value Value to check
  3445. * @return boolean
  3446. */
  3447. public static function IS_BLANK($value = '') {
  3448. $value = self::flattenSingleValue($value);
  3449. return (is_null($value) || (is_string($value) && ($value == '')));
  3450. }
  3451. /**
  3452. * IS_ERR
  3453. *
  3454. * @param mixed $value Value to check
  3455. * @return boolean
  3456. */
  3457. public static function IS_ERR($value = '') {
  3458. $value = self::flattenSingleValue($value);
  3459. return self::IS_ERROR($value) && (!self::IS_NA($value));
  3460. }
  3461. /**
  3462. * IS_ERROR
  3463. *
  3464. * @param mixed $value Value to check
  3465. * @return boolean
  3466. */
  3467. public static function IS_ERROR($value = '') {
  3468. $value = self::flattenSingleValue($value);
  3469. return in_array($value, array_values(self::$_errorCodes));
  3470. }
  3471. /**
  3472. * IS_NA
  3473. *
  3474. * @param mixed $value Value to check
  3475. * @return boolean
  3476. */
  3477. public static function IS_NA($value = '') {
  3478. $value = self::flattenSingleValue($value);
  3479. return ($value == self::$_errorCodes['na']);
  3480. }
  3481. /**
  3482. * IS_EVEN
  3483. *
  3484. * @param mixed $value Value to check
  3485. * @return boolean
  3486. */
  3487. public static function IS_EVEN($value = 0) {
  3488. $value = self::flattenSingleValue($value);
  3489. while (intval($value) != $value) {
  3490. $value *= 10;
  3491. }
  3492. return ($value % 2 == 0);
  3493. }
  3494. /**
  3495. * IS_NUMBER
  3496. *
  3497. * @param mixed $value Value to check
  3498. * @return boolean
  3499. */
  3500. public static function IS_NUMBER($value = 0) {
  3501. $value = self::flattenSingleValue($value);
  3502. return is_numeric($value);
  3503. }
  3504. /**
  3505. * STATEMENT_IF
  3506. *
  3507. * @param mixed $value Value to check
  3508. * @param mixed $truepart Value when true
  3509. * @param mixed $falsepart Value when false
  3510. * @return mixed
  3511. */
  3512. public static function STATEMENT_IF($value = true, $truepart = '', $falsepart = '') {
  3513. $value = self::flattenSingleValue($value);
  3514. $truepart = self::flattenSingleValue($truepart);
  3515. $falsepart = self::flattenSingleValue($falsepart);
  3516. return ($value ? $truepart : $falsepart);
  3517. }
  3518. /**
  3519. * STATEMENT_IFERROR
  3520. *
  3521. * @param mixed $value Value to check , is also value when no error
  3522. * @param mixed $errorpart Value when error
  3523. * @return mixed
  3524. */
  3525. public static function STATEMENT_IFERROR($value = '', $errorpart = '') {
  3526. return self::STATEMENT_IF(self::IS_ERROR($value), $errorpart, $value);
  3527. }
  3528. /**
  3529. * VERSION
  3530. *
  3531. * @return string Version information
  3532. */
  3533. public static function VERSION() {
  3534. return 'PHPExcel ##VERSION##, ##DATE##';
  3535. }
  3536. /**
  3537. * DATE
  3538. *
  3539. * @param long $year
  3540. * @param long $month
  3541. * @param long $day
  3542. * @return long Excel date serial value
  3543. */
  3544. public static function DATE($year = 0, $month = 1, $day = 1) {
  3545. $year = (integer) self::flattenSingleValue($year);
  3546. $month = (integer) self::flattenSingleValue($month);
  3547. $day = (integer) self::flattenSingleValue($day);
  3548. // Validate parameters
  3549. if (($year < 0) || ($year >= 10000)) {
  3550. return self::$_errorCodes['num'];
  3551. }
  3552. if ($year < 1900) {
  3553. $year += 1900;
  3554. }
  3555. if (($month < 1) || ($month > 12)) {
  3556. $year += floor($month / 12);
  3557. if ($month > 1) {
  3558. $month = $month % 12;
  3559. } else {
  3560. $month = 12 - abs($month % 12);
  3561. }
  3562. }
  3563. // Execute function
  3564. return PHPExcel_Shared_Date::FormattedPHPToExcel($year, $month, $day);
  3565. }
  3566. /**
  3567. * TIME
  3568. *
  3569. * @param long $hour
  3570. * @param long $minute
  3571. * @param long $second
  3572. * @return long Excel time serial value
  3573. */
  3574. public static function TIME($hour = 0, $minute = 0, $second = 0) {
  3575. $hour = (integer) self::flattenSingleValue($hour);
  3576. $minute = (integer) self::flattenSingleValue($minute);
  3577. $second = (integer) self::flattenSingleValue($second);
  3578. // Validate parameters
  3579. if (($hour < 0) || ($minute < 0) || ($second < 0)) {
  3580. return self::$_errorCodes['num'];
  3581. }
  3582. if ($second >= 60) {
  3583. $minute += floor($second / 60);
  3584. $second = $second % 60;
  3585. }
  3586. if ($minute >= 60) {
  3587. $hour += floor($minute / 60);
  3588. $minute = $minute % 60;
  3589. }
  3590. if ($hour >= 24) {
  3591. $hour = $hour % 24;
  3592. }
  3593. // Execute function
  3594. return PHPExcel_Shared_Date::FormattedPHPToExcel(1900, 1, 1, $hour, $minute, $second) - 1;
  3595. }
  3596. /**
  3597. * DATEVALUE
  3598. *
  3599. * @param string $dateValue
  3600. * @return long Excel date serial value
  3601. */
  3602. public static function DATEVALUE($dateValue = 1) {
  3603. $dateValue = self::flattenSingleValue($dateValue);
  3604. $PHPDateArray = date_parse($dateValue);
  3605. if (($PHPDateArray === False) || ($PHPDateArray['error_count'] > 0)) {
  3606. $testVal1 = strtok($dateValue,'/-');
  3607. if ($testVal1 !== False) {
  3608. $testVal2 = strtok('/-');
  3609. if ($testVal2 !== False) {
  3610. if ((is_numeric($testVal1)) && (is_numeric($testVal2))) {
  3611. if (($testVal1 == $PHPDateArray['day']) && (substr($testVal2,-1) == $PHPDateArray['month'])) {
  3612. $PHPDateArray['day'] = $testVal2;
  3613. $PHPDateArray['month'] = $testVal1;
  3614. $PHPDateArray['error_count']--;
  3615. } elseif (($testVal2 == $PHPDateArray['day']) && (substr($testVal1,-1) == $PHPDateArray['month'])) {
  3616. $PHPDateArray['day'] = $testVal1;
  3617. $PHPDateArray['month'] = $testVal2;
  3618. $PHPDateArray['error_count']--;
  3619. } else {
  3620. return self::$_errorCodes['value'];
  3621. }
  3622. } else {
  3623. return self::$_errorCodes['value'];
  3624. }
  3625. } else {
  3626. return self::$_errorCodes['value'];
  3627. }
  3628. } else {
  3629. return self::$_errorCodes['value'];
  3630. }
  3631. }
  3632. if (($PHPDateArray !== False) && ($PHPDateArray['error_count'] == 0)) {
  3633. if ($PHPDateArray['year'] == '') {
  3634. $PHPDateArray['year'] = strftime('%Y');
  3635. }
  3636. if ($PHPDateArray['month'] == '') {
  3637. $PHPDateArray['month'] = strftime('%m');
  3638. }
  3639. if ($PHPDateArray['day'] == '') {
  3640. $PHPDateArray['day'] = strftime('%d');
  3641. }
  3642. return floor(PHPExcel_Shared_Date::FormattedPHPToExcel($PHPDateArray['year'],$PHPDateArray['month'],$PHPDateArray['day'],$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']));
  3643. }
  3644. return self::$_errorCodes['value'];
  3645. }
  3646. /**
  3647. * TIMEVALUE
  3648. *
  3649. * @param string $timeValue
  3650. * @return long Excel time serial value
  3651. */
  3652. public static function TIMEVALUE($timeValue) {
  3653. $timeValue = self::flattenSingleValue($timeValue);
  3654. if ((($PHPDateArray = date_parse($timeValue)) !== False) && ($PHPDateArray['error_count'] == 0)) {
  3655. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  3656. return PHPExcel_Shared_Date::FormattedPHPToExcel($PHPDateArray['year'],$PHPDateArray['month'],$PHPDateArray['day'],$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']);
  3657. } else {
  3658. return PHPExcel_Shared_Date::FormattedPHPToExcel(1900,1,1,$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']) - 1;
  3659. }
  3660. }
  3661. return self::$_errorCodes['value'];
  3662. }
  3663. /**
  3664. * DATETIMENOW
  3665. *
  3666. * @return long Excel date/time serial value
  3667. */
  3668. public static function DATETIMENOW() {
  3669. return PHPExcel_Shared_Date::PHPToExcel(time());
  3670. }
  3671. /**
  3672. * DATENOW
  3673. *
  3674. * @return long Excel date/time serial value
  3675. */
  3676. public static function DATENOW() {
  3677. return floor(PHPExcel_Shared_Date::PHPToExcel(time()));
  3678. }
  3679. private static function isLeapYear($year) {
  3680. return ((($year % 4) == 0) && (($year % 100) != 0) || (($year % 400) == 0));
  3681. }
  3682. private static function dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, $methodUS) {
  3683. if ($startDay == 31) {
  3684. $startDay--;
  3685. } else if ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !self::isLeapYear($startYear))))) {
  3686. $startDay = 30;
  3687. }
  3688. if ($endDay == 31) {
  3689. if ($methodUS && $startDay != 30) {
  3690. $endDay = 1;
  3691. if ($endMonth == 12) {
  3692. $endYear++;
  3693. $endMonth = 1;
  3694. } else {
  3695. $endMonth++;
  3696. }
  3697. } else {
  3698. $endDay = 30;
  3699. }
  3700. }
  3701. return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
  3702. }
  3703. /**
  3704. * DAYS360
  3705. *
  3706. * @param long $startDate Excel date serial value
  3707. * @param long $endDate Excel date serial value
  3708. * @param boolean $method US or European Method
  3709. * @return long PHP date/time serial
  3710. */
  3711. public static function DAYS360($startDate = 0, $endDate = 0, $method = false) {
  3712. $startDate = self::flattenSingleValue($startDate);
  3713. $endDate = self::flattenSingleValue($endDate);
  3714. if (!is_numeric($startDate)) {
  3715. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  3716. return self::$_errorCodes['value'];
  3717. }
  3718. $startDate = self::DATEVALUE($startDate);
  3719. if (is_string($startDate)) {
  3720. return self::$_errorCodes['value'];
  3721. }
  3722. }
  3723. if (!is_numeric($endDate)) {
  3724. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  3725. return self::$_errorCodes['value'];
  3726. }
  3727. $endDate = self::DATEVALUE($endDate);
  3728. if (is_string($endDate)) {
  3729. return self::$_errorCodes['value'];
  3730. }
  3731. }
  3732. // Execute function
  3733. $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
  3734. $startDay = $PHPStartDateObject->format('j');
  3735. $startMonth = $PHPStartDateObject->format('n');
  3736. $startYear = $PHPStartDateObject->format('Y');
  3737. $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  3738. $endDay = $PHPEndDateObject->format('j');
  3739. $endMonth = $PHPEndDateObject->format('n');
  3740. $endYear = $PHPEndDateObject->format('Y');
  3741. return self::dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
  3742. }
  3743. /**
  3744. * DATEDIF
  3745. *
  3746. * @param long $startDate Excel date serial value
  3747. * @param long $endDate Excel date serial value
  3748. * @param string $unit
  3749. * @return long Interval between the dates
  3750. */
  3751. public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D') {
  3752. $startDate = self::flattenSingleValue($startDate);
  3753. $endDate = self::flattenSingleValue($endDate);
  3754. $unit = strtoupper(self::flattenSingleValue($unit));
  3755. if (!is_numeric($startDate)) {
  3756. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  3757. return self::$_errorCodes['value'];
  3758. }
  3759. $startDate = self::DATEVALUE($startDate);
  3760. if (is_string($startDate)) {
  3761. return self::$_errorCodes['value'];
  3762. }
  3763. }
  3764. if (!is_numeric($endDate)) {
  3765. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  3766. return self::$_errorCodes['value'];
  3767. }
  3768. $endDate = self::DATEVALUE($endDate);
  3769. if (is_string($endDate)) {
  3770. return self::$_errorCodes['value'];
  3771. }
  3772. }
  3773. // Validate parameters
  3774. if ($startDate >= $endDate) {
  3775. return self::$_errorCodes['num'];
  3776. }
  3777. // Execute function
  3778. $difference = $endDate - $startDate;
  3779. $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
  3780. $startDays = $PHPStartDateObject->format('j');
  3781. $startMonths = $PHPStartDateObject->format('n');
  3782. $startYears = $PHPStartDateObject->format('Y');
  3783. $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  3784. $endDays = $PHPEndDateObject->format('j');
  3785. $endMonths = $PHPEndDateObject->format('n');
  3786. $endYears = $PHPEndDateObject->format('Y');
  3787. $retVal = self::$_errorCodes['num'];
  3788. switch ($unit) {
  3789. case 'D':
  3790. $retVal = intval($difference);
  3791. break;
  3792. case 'M':
  3793. $retVal = intval($endMonths - $startMonths) + (intval($endYears - $startYears) * 12);
  3794. // We're only interested in full months
  3795. if ($endDays < $startDays) {
  3796. $retVal--;
  3797. }
  3798. break;
  3799. case 'Y':
  3800. $retVal = intval($endYears - $startYears);
  3801. // We're only interested in full months
  3802. if ($endMonths < $startMonths) {
  3803. $retVal--;
  3804. } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) {
  3805. $retVal--;
  3806. }
  3807. break;
  3808. case 'MD':
  3809. if ($endDays < $startDays) {
  3810. $retVal = $endDays;
  3811. $PHPEndDateObject->modify('-'.$endDays.' days');
  3812. $adjustDays = $PHPEndDateObject->format('j');
  3813. if ($adjustDays > $startDays) {
  3814. $retVal += ($adjustDays - $startDays);
  3815. }
  3816. } else {
  3817. $retVal = $endDays - $startDays;
  3818. }
  3819. break;
  3820. case 'YM':
  3821. $retVal = abs(intval($endMonths - $startMonths));
  3822. // We're only interested in full months
  3823. if ($endDays < $startDays) {
  3824. $retVal--;
  3825. }
  3826. break;
  3827. case 'YD':
  3828. $retVal = intval($difference);
  3829. if ($endYears > $startYears) {
  3830. while ($endYears > $startYears) {
  3831. $PHPEndDateObject->modify('-1 year');
  3832. $endYears = $PHPEndDateObject->format('Y');
  3833. }
  3834. $retVal = abs($PHPEndDateObject->format('z') - $PHPStartDateObject->format('z'));
  3835. }
  3836. break;
  3837. }
  3838. return $retVal;
  3839. }
  3840. /**
  3841. * DAYOFMONTH
  3842. *
  3843. * @param long $dateValue Excel date serial value or a standard date string
  3844. * @return int Day
  3845. */
  3846. public static function DAYOFMONTH($dateValue = 1) {
  3847. $dateValue = self::flattenSingleValue($dateValue);
  3848. if (!is_numeric($dateValue)) {
  3849. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  3850. return self::$_errorCodes['value'];
  3851. }
  3852. $dateValue = self::DATEVALUE($dateValue);
  3853. if (is_string($dateValue)) {
  3854. return self::$_errorCodes['value'];
  3855. }
  3856. }
  3857. // Execute function
  3858. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  3859. return $PHPDateObject->format('j');
  3860. }
  3861. /**
  3862. * DAYOFWEEK
  3863. *
  3864. * @param long $dateValue Excel date serial value or a standard date string
  3865. * @return int Day
  3866. */
  3867. public static function DAYOFWEEK($dateValue = 1, $style = 1) {
  3868. $dateValue = self::flattenSingleValue($dateValue);
  3869. $style = floor(self::flattenSingleValue($style));
  3870. if (!is_numeric($dateValue)) {
  3871. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  3872. return self::$_errorCodes['value'];
  3873. }
  3874. $dateValue = self::DATEVALUE($dateValue);
  3875. if (is_string($dateValue)) {
  3876. return self::$_errorCodes['value'];
  3877. }
  3878. }
  3879. // Execute function
  3880. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  3881. $DoW = $PHPDateObject->format('w');
  3882. $firstDay = 1;
  3883. switch ($style) {
  3884. case 1: ++$DoW;
  3885. break;
  3886. case 2: if ($DoW == 0) { $DoW = 7; }
  3887. break;
  3888. case 3: if ($DoW == 0) { $DoW = 7; }
  3889. $firstDay = 0;
  3890. --$DoW;
  3891. break;
  3892. default:
  3893. }
  3894. if (self::$compatibilityMode == self::COMPATIBILITY_EXCEL) {
  3895. // Test for Excel's 1900 leap year, and introduce the error as required
  3896. if (($PHPDateObject->format('Y') == 1900) && ($PHPDateObject->format('n') <= 2)) {
  3897. --$DoW;
  3898. if ($DoW < $firstDay) {
  3899. $DoW += 7;
  3900. }
  3901. }
  3902. }
  3903. return $DoW;
  3904. }
  3905. /**
  3906. * WEEKOFYEAR
  3907. *
  3908. * @param long $dateValue Excel date serial value or a standard date string
  3909. * @param boolean $method Week begins on Sunday or Monday
  3910. * @return int Week Number
  3911. */
  3912. public static function WEEKOFYEAR($dateValue = 1, $method = 1) {
  3913. $dateValue = self::flattenSingleValue($dateValue);
  3914. $method = floor(self::flattenSingleValue($method));
  3915. if (!is_numeric($method)) {
  3916. return self::$_errorCodes['value'];
  3917. } elseif (($method < 1) || ($method > 2)) {
  3918. return self::$_errorCodes['num'];
  3919. }
  3920. if (!is_numeric($dateValue)) {
  3921. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  3922. return self::$_errorCodes['value'];
  3923. }
  3924. $dateValue = self::DATEVALUE($dateValue);
  3925. if (is_string($dateValue)) {
  3926. return self::$_errorCodes['value'];
  3927. }
  3928. }
  3929. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  3930. $dayOfYear = $PHPDateObject->format('z');
  3931. $dow = $PHPDateObject->format('w');
  3932. $PHPDateObject->modify('-'.$dayOfYear.' days');
  3933. $dow = $PHPDateObject->format('w');
  3934. $daysInFirstWeek = 7 - (($dow + (2 - $method)) % 7);
  3935. $dayOfYear -= $daysInFirstWeek;
  3936. $weekOfYear = ceil($dayOfYear / 7) + 1;
  3937. return $weekOfYear;
  3938. }
  3939. /**
  3940. * MONTHOFYEAR
  3941. *
  3942. * @param long $dateValue Excel date serial value or a standard date string
  3943. * @return int Month
  3944. */
  3945. public static function MONTHOFYEAR($dateValue = 1) {
  3946. $dateValue = self::flattenSingleValue($dateValue);
  3947. if (!is_numeric($dateValue)) {
  3948. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  3949. return self::$_errorCodes['value'];
  3950. }
  3951. $dateValue = self::DATEVALUE($dateValue);
  3952. if (is_string($dateValue)) {
  3953. return self::$_errorCodes['value'];
  3954. }
  3955. }
  3956. // Execute function
  3957. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  3958. return $PHPDateObject->format('n');
  3959. }
  3960. /**
  3961. * YEAR
  3962. *
  3963. * @param long $dateValue Excel date serial value or a standard date string
  3964. * @return int Year
  3965. */
  3966. public static function YEAR($dateValue = 1) {
  3967. $dateValue = self::flattenSingleValue($dateValue);
  3968. if (!is_numeric($dateValue)) {
  3969. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  3970. return self::$_errorCodes['value'];
  3971. }
  3972. $dateValue = self::DATEVALUE($dateValue);
  3973. if (is_string($dateValue)) {
  3974. return self::$_errorCodes['value'];
  3975. }
  3976. }
  3977. // Execute function
  3978. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  3979. return $PHPDateObject->format('Y');
  3980. }
  3981. /**
  3982. * HOUROFDAY
  3983. *
  3984. * @param long $timeValue Excel time serial value or a standard time string
  3985. * @return int Hour
  3986. */
  3987. public static function HOUROFDAY($timeValue = 0) {
  3988. $timeValue = self::flattenSingleValue($timeValue);
  3989. if (!is_numeric($timeValue)) {
  3990. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  3991. $testVal = strtok($timeValue,'/-');
  3992. if (strlen($testVal) < strlen($timeValue)) {
  3993. return self::$_errorCodes['value'];
  3994. }
  3995. }
  3996. $timeValue = self::TIMEVALUE($timeValue);
  3997. if (is_string($timeValue)) {
  3998. return self::$_errorCodes['value'];
  3999. }
  4000. }
  4001. // Execute function
  4002. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($timeValue);
  4003. return $PHPDateObject->format('G');
  4004. }
  4005. /**
  4006. * MINUTEOFHOUR
  4007. *
  4008. * @param long $timeValue Excel time serial value or a standard time string
  4009. * @return int Minute
  4010. */
  4011. public static function MINUTEOFHOUR($timeValue = 0) {
  4012. $timeValue = $timeTester = self::flattenSingleValue($timeValue);
  4013. if (!is_numeric($timeValue)) {
  4014. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  4015. $testVal = strtok($timeValue,'/-');
  4016. if (strlen($testVal) < strlen($timeValue)) {
  4017. return self::$_errorCodes['value'];
  4018. }
  4019. }
  4020. $timeValue = self::TIMEVALUE($timeValue);
  4021. if (is_string($timeValue)) {
  4022. return self::$_errorCodes['value'];
  4023. }
  4024. }
  4025. // Execute function
  4026. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($timeValue);
  4027. $minutes = $PHPDateObject->format('i');
  4028. $seconds += $PHPDateObject->format('s');
  4029. if ((is_numeric($timeTester)) || (substr_count($timeTester,':') < 2)) {
  4030. if ($seconds > 30) {
  4031. $minutes++;
  4032. }
  4033. if ($minutes == 60) {
  4034. return 0;
  4035. }
  4036. }
  4037. return $minutes;
  4038. }
  4039. /**
  4040. * SECONDOFMINUTE
  4041. *
  4042. * @param long $timeValue Excel time serial value or a standard time string
  4043. * @return int Second
  4044. */
  4045. public static function SECONDOFMINUTE($timeValue = 0) {
  4046. $timeValue = self::flattenSingleValue($timeValue);
  4047. if (!is_numeric($timeValue)) {
  4048. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  4049. $testVal = strtok($timeValue,'/-');
  4050. if (strlen($testVal) < strlen($timeValue)) {
  4051. return self::$_errorCodes['value'];
  4052. }
  4053. }
  4054. $timeValue = self::TIMEVALUE($timeValue);
  4055. if (is_string($timeValue)) {
  4056. return self::$_errorCodes['value'];
  4057. }
  4058. }
  4059. // Execute function
  4060. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($timeValue);
  4061. return (int) $PHPDateObject->format('s');
  4062. }
  4063. private static function adjustDateByMonths ($dateValue = 0, $adjustmentMonths = 0) {
  4064. // Execute function
  4065. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  4066. $oMonth = (int) $PHPDateObject->format('m');
  4067. $oYear = (int) $PHPDateObject->format('Y');
  4068. $adjustmentMonthsString = (string) $adjustmentMonths;
  4069. if ($adjustmentMonths > 0) {
  4070. $adjustmentMonthsString = '+'.$adjustmentMonths;
  4071. }
  4072. if ($adjustmentMonths != 0) {
  4073. $PHPDateObject->modify($adjustmentMonthsString.' months');
  4074. }
  4075. $nMonth = (int) $PHPDateObject->format('m');
  4076. $nYear = (int) $PHPDateObject->format('Y');
  4077. $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
  4078. if ($monthDiff != $adjustmentMonths) {
  4079. $adjustDays = (int) $PHPDateObject->format('d');
  4080. $adjustDaysString = '-'.$adjustDays.' days';
  4081. $PHPDateObject->modify($adjustDaysString);
  4082. }
  4083. return $PHPDateObject;
  4084. }
  4085. /**
  4086. * EDATE
  4087. *
  4088. * @param long $dateValue Excel date serial value or a standard date string
  4089. * @param int $adjustmentMonths Number of months to adjust by
  4090. * @return long Excel date serial value
  4091. */
  4092. public static function EDATE($dateValue = 1, $adjustmentMonths = 0) {
  4093. $dateValue = self::flattenSingleValue($dateValue);
  4094. $adjustmentMonths = floor(self::flattenSingleValue($adjustmentMonths));
  4095. if (!is_numeric($adjustmentMonths)) {
  4096. return self::$_errorCodes['value'];
  4097. }
  4098. if (!is_numeric($dateValue)) {
  4099. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  4100. return self::$_errorCodes['value'];
  4101. }
  4102. $dateValue = self::DATEVALUE($dateValue);
  4103. if (is_string($dateValue)) {
  4104. return self::$_errorCodes['value'];
  4105. }
  4106. }
  4107. $PHPDateObject = self::adjustDateByMonths($dateValue,$adjustmentMonths);
  4108. return PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
  4109. }
  4110. /**
  4111. * EOMONTH
  4112. *
  4113. * @param long $dateValue Excel date serial value or a standard date string
  4114. * @param int $adjustmentMonths Number of months to adjust by
  4115. * @return long Excel date serial value
  4116. */
  4117. public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0) {
  4118. $dateValue = self::flattenSingleValue($dateValue);
  4119. $adjustmentMonths = floor(self::flattenSingleValue($adjustmentMonths));
  4120. if (!is_numeric($adjustmentMonths)) {
  4121. return self::$_errorCodes['value'];
  4122. }
  4123. if (!is_numeric($dateValue)) {
  4124. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  4125. return self::$_errorCodes['value'];
  4126. }
  4127. $dateValue = self::DATEVALUE($dateValue);
  4128. if (is_string($dateValue)) {
  4129. return self::$_errorCodes['value'];
  4130. }
  4131. }
  4132. // Execute function
  4133. $PHPDateObject = self::adjustDateByMonths($dateValue,$adjustmentMonths+1);
  4134. $adjustDays = (int) $PHPDateObject->format('d');
  4135. $adjustDaysString = '-'.$adjustDays.' days';
  4136. $PHPDateObject->modify($adjustDaysString);
  4137. return PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
  4138. }
  4139. /**
  4140. * TRUNC
  4141. *
  4142. * Truncates value to the number of fractional digits by number_digits.
  4143. *
  4144. * @param float $value
  4145. * @param int $number_digits
  4146. * @return float Truncated value
  4147. */
  4148. public static function TRUNC($value = 0, $number_digits = 0) {
  4149. $value = self::flattenSingleValue($value);
  4150. $number_digits = self::flattenSingleValue($number_digits);
  4151. // Validate parameters
  4152. if ($number_digits < 0) {
  4153. return self::$_errorCodes['value'];
  4154. }
  4155. // Truncate
  4156. if ($number_digits > 0) {
  4157. $value = $value * pow(10, $number_digits);
  4158. }
  4159. $value = intval($value);
  4160. if ($number_digits > 0) {
  4161. $value = $value / pow(10, $number_digits);
  4162. }
  4163. // Return
  4164. return $value;
  4165. }
  4166. /**
  4167. * POWER
  4168. *
  4169. * Computes x raised to the power y.
  4170. *
  4171. * @param float $x
  4172. * @param float $y
  4173. * @return float
  4174. */
  4175. public static function POWER($x = 0, $y = 2) {
  4176. $x = self::flattenSingleValue($x);
  4177. $y = self::flattenSingleValue($y);
  4178. // Validate parameters
  4179. if ($x < 0) {
  4180. return self::$_errorCodes['num'];
  4181. }
  4182. if ($x == 0 && $y <= 0) {
  4183. return self::$_errorCodes['divisionbyzero'];
  4184. }
  4185. // Return
  4186. return pow($x, $y);
  4187. }
  4188. /**
  4189. * BINTODEC
  4190. *
  4191. * Return a binary value as Decimal.
  4192. *
  4193. * @param string $x
  4194. * @return string
  4195. */
  4196. public static function BINTODEC($x) {
  4197. $x = self::flattenSingleValue($x);
  4198. if (is_bool($x)) {
  4199. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  4200. $x = (int) $x;
  4201. } else {
  4202. return self::$_errorCodes['value'];
  4203. }
  4204. }
  4205. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  4206. $x = floor($x);
  4207. }
  4208. $x = (string) $x;
  4209. if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
  4210. return self::$_errorCodes['num'];
  4211. }
  4212. if (strlen($x) > 10) {
  4213. return self::$_errorCodes['num'];
  4214. } elseif (strlen($x) == 10) {
  4215. // Two's Complement
  4216. $x = substr($x,-9);
  4217. return '-'.(512-bindec($x));
  4218. }
  4219. return bindec($x);
  4220. }
  4221. /**
  4222. * BINTOHEX
  4223. *
  4224. * Return a binary value as Hex.
  4225. *
  4226. * @param string $x
  4227. * @return string
  4228. */
  4229. public static function BINTOHEX($x) {
  4230. $x = floor(self::flattenSingleValue($x));
  4231. if (is_bool($x)) {
  4232. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  4233. $x = (int) $x;
  4234. } else {
  4235. return self::$_errorCodes['value'];
  4236. }
  4237. }
  4238. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  4239. $x = floor($x);
  4240. }
  4241. $x = (string) $x;
  4242. if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
  4243. return self::$_errorCodes['num'];
  4244. }
  4245. if (strlen($x) > 10) {
  4246. return self::$_errorCodes['num'];
  4247. } elseif (strlen($x) == 10) {
  4248. // Two's Complement
  4249. return str_repeat('F',8).substr(strtoupper(dechex(bindec(substr($x,-9)))),-2);
  4250. }
  4251. return strtoupper(dechex(bindec($x)));
  4252. }
  4253. /**
  4254. * BINTOOCT
  4255. *
  4256. * Return a binary value as Octal.
  4257. *
  4258. * @param string $x
  4259. * @return string
  4260. */
  4261. public static function BINTOOCT($x) {
  4262. $x = floor(self::flattenSingleValue($x));
  4263. if (is_bool($x)) {
  4264. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  4265. $x = (int) $x;
  4266. } else {
  4267. return self::$_errorCodes['value'];
  4268. }
  4269. }
  4270. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  4271. $x = floor($x);
  4272. }
  4273. $x = (string) $x;
  4274. if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
  4275. return self::$_errorCodes['num'];
  4276. }
  4277. if (strlen($x) > 10) {
  4278. return self::$_errorCodes['num'];
  4279. } elseif (strlen($x) == 10) {
  4280. // Two's Complement
  4281. return str_repeat('7',7).substr(strtoupper(dechex(bindec(substr($x,-9)))),-3);
  4282. }
  4283. return decoct(bindec($x));
  4284. }
  4285. /**
  4286. * DECTOBIN
  4287. *
  4288. * Return an octal value as binary.
  4289. *
  4290. * @param string $x
  4291. * @return string
  4292. */
  4293. public static function DECTOBIN($x) {
  4294. $x = self::flattenSingleValue($x);
  4295. if (is_bool($x)) {
  4296. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  4297. $x = (int) $x;
  4298. } else {
  4299. return self::$_errorCodes['value'];
  4300. }
  4301. }
  4302. $x = (string) $x;
  4303. if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
  4304. return self::$_errorCodes['value'];
  4305. }
  4306. $x = (string) floor($x);
  4307. $r = decbin($x);
  4308. if (strlen($r) == 32) {
  4309. // Two's Complement
  4310. $r = substr($r,-10);
  4311. } elseif (strlen($r) > 11) {
  4312. return self::$_errorCodes['num'];
  4313. }
  4314. return $r;
  4315. }
  4316. /**
  4317. * DECTOOCT
  4318. *
  4319. * Return an octal value as binary.
  4320. *
  4321. * @param string $x
  4322. * @return string
  4323. */
  4324. public static function DECTOOCT($x) {
  4325. $x = self::flattenSingleValue($x);
  4326. if (is_bool($x)) {
  4327. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  4328. $x = (int) $x;
  4329. } else {
  4330. return self::$_errorCodes['value'];
  4331. }
  4332. }
  4333. $x = (string) $x;
  4334. if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
  4335. return self::$_errorCodes['value'];
  4336. }
  4337. $x = (string) floor($x);
  4338. $r = decoct($x);
  4339. if (strlen($r) == 11) {
  4340. // Two's Complement
  4341. $r = substr($r,-10);
  4342. }
  4343. return ($r);
  4344. }
  4345. /**
  4346. * DECTOHEX
  4347. *
  4348. * Return an octal value as binary.
  4349. *
  4350. * @param string $x
  4351. * @return string
  4352. */
  4353. public static function DECTOHEX($x) {
  4354. $x = self::flattenSingleValue($x);
  4355. if (is_bool($x)) {
  4356. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  4357. $x = (int) $x;
  4358. } else {
  4359. return self::$_errorCodes['value'];
  4360. }
  4361. }
  4362. $x = (string) $x;
  4363. if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
  4364. return self::$_errorCodes['value'];
  4365. }
  4366. $x = (string) floor($x);
  4367. $r = strtoupper(dechex($x));
  4368. if (strlen($r) == 8) {
  4369. // Two's Complement
  4370. $r = 'FF'.$r;
  4371. }
  4372. return ($r);
  4373. }
  4374. /**
  4375. * HEXTOBIN
  4376. *
  4377. * Return a hex value as binary.
  4378. *
  4379. * @param string $x
  4380. * @return string
  4381. */
  4382. public static function HEXTOBIN($x) {
  4383. $x = self::flattenSingleValue($x);
  4384. if (is_bool($x)) {
  4385. return self::$_errorCodes['value'];
  4386. }
  4387. $x = (string) $x;
  4388. if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
  4389. return self::$_errorCodes['num'];
  4390. }
  4391. return decbin(hexdec($x));
  4392. }
  4393. /**
  4394. * HEXTOOCT
  4395. *
  4396. * Return a hex value as octal.
  4397. *
  4398. * @param string $x
  4399. * @return string
  4400. */
  4401. public static function HEXTOOCT($x) {
  4402. $x = self::flattenSingleValue($x);
  4403. if (is_bool($x)) {
  4404. return self::$_errorCodes['value'];
  4405. }
  4406. $x = (string) $x;
  4407. if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
  4408. return self::$_errorCodes['num'];
  4409. }
  4410. return decoct(hexdec($x));
  4411. }
  4412. /**
  4413. * HEXTODEC
  4414. *
  4415. * Return a hex value as octal.
  4416. *
  4417. * @param string $x
  4418. * @return string
  4419. */
  4420. public static function HEXTODEC($x) {
  4421. $x = self::flattenSingleValue($x);
  4422. if (is_bool($x)) {
  4423. return self::$_errorCodes['value'];
  4424. }
  4425. $x = (string) $x;
  4426. if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
  4427. return self::$_errorCodes['num'];
  4428. }
  4429. return hexdec($x);
  4430. }
  4431. /**
  4432. * OCTTOBIN
  4433. *
  4434. * Return an octal value as binary.
  4435. *
  4436. * @param string $x
  4437. * @return string
  4438. */
  4439. public static function OCTTOBIN($x) {
  4440. $x = self::flattenSingleValue($x);
  4441. if (is_bool($x)) {
  4442. return self::$_errorCodes['value'];
  4443. }
  4444. $x = (string) $x;
  4445. if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
  4446. return self::$_errorCodes['num'];
  4447. }
  4448. return decbin(octdec($x));
  4449. }
  4450. /**
  4451. * OCTTODEC
  4452. *
  4453. * Return an octal value as binary.
  4454. *
  4455. * @param string $x
  4456. * @return string
  4457. */
  4458. public static function OCTTODEC($x) {
  4459. $x = self::flattenSingleValue($x);
  4460. if (is_bool($x)) {
  4461. return self::$_errorCodes['value'];
  4462. }
  4463. $x = (string) $x;
  4464. if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
  4465. return self::$_errorCodes['num'];
  4466. }
  4467. return octdec($x);
  4468. }
  4469. /**
  4470. * OCTTOHEX
  4471. *
  4472. * Return an octal value as hex.
  4473. *
  4474. * @param string $x
  4475. * @return string
  4476. */
  4477. public static function OCTTOHEX($x) {
  4478. $x = self::flattenSingleValue($x);
  4479. if (is_bool($x)) {
  4480. return self::$_errorCodes['value'];
  4481. }
  4482. $x = (string) $x;
  4483. if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
  4484. return self::$_errorCodes['num'];
  4485. }
  4486. return strtoupper(dechex(octdec($x)));
  4487. }
  4488. /**
  4489. * BESSELI
  4490. *
  4491. * Returns the modified Bessel function, which is equivalent to the Bessel function evaluated for purely imaginary arguments
  4492. *
  4493. * @param float $x
  4494. * @param float $n
  4495. * @return int
  4496. */
  4497. public static function BESSELI($x, $n) {
  4498. $x = self::flattenSingleValue($x);
  4499. $n = floor(self::flattenSingleValue($n));
  4500. if ((is_numeric($x)) && (is_numeric($n))) {
  4501. if ($n < 0) {
  4502. return self::$_errorCodes['num'];
  4503. }
  4504. $f_2_PI = 2 * pi();
  4505. if (abs($x) <= 30) {
  4506. $fTerm = pow($x / 2, $n) / self::FACT($n);
  4507. $nK = 1;
  4508. $fResult = $fTerm;
  4509. $fSqrX = pow($x,2) / 4;
  4510. do {
  4511. $fTerm *= $fSqrX;
  4512. $fTerm /= ($nK * ($nK + $n));
  4513. $fResult += $fTerm;
  4514. } while ((abs($fTerm) > 1e-10) && (++$nK < 100));
  4515. } else {
  4516. $fXAbs = abs($x);
  4517. $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs);
  4518. if (($n && 1) && ($x < 0)) {
  4519. $fResult = -$fResult;
  4520. }
  4521. }
  4522. return $fResult;
  4523. }
  4524. return self::$_errorCodes['value'];
  4525. }
  4526. /**
  4527. * BESSELJ
  4528. *
  4529. * Returns the Bessel function
  4530. *
  4531. * @param float $x
  4532. * @param float $n
  4533. * @return int
  4534. */
  4535. public static function BESSELJ($x, $n) {
  4536. $x = self::flattenSingleValue($x);
  4537. $n = floor(self::flattenSingleValue($n));
  4538. if ((is_numeric($x)) && (is_numeric($n))) {
  4539. if ($n < 0) {
  4540. return self::$_errorCodes['num'];
  4541. }
  4542. $f_2_DIV_PI = 2 / pi();
  4543. $f_PI_DIV_2 = pi() / 2;
  4544. $f_PI_DIV_4 = pi() / 4;
  4545. $fResult = 0;
  4546. if (abs($x) <= 30) {
  4547. $fTerm = pow($x / 2, $n) / self::FACT($n);
  4548. $nK = 1;
  4549. $fResult = $fTerm;
  4550. $fSqrX = pow($x,2) / -4;
  4551. do {
  4552. $fTerm *= $fSqrX;
  4553. $fTerm /= ($nK * ($nK + $n));
  4554. $fResult += $fTerm;
  4555. } while ((abs($fTerm) > 1e-10) && (++$nK < 100));
  4556. } else {
  4557. $fXAbs = abs($x);
  4558. $fResult = sqrt($f_2_DIV_PI / $fXAbs) * cos($fXAbs - $n * $f_PI_DIV_2 - $f_PI_DIV_4);
  4559. if (($n && 1) && ($x < 0)) {
  4560. $fResult = -$fResult;
  4561. }
  4562. }
  4563. return $fResult;
  4564. }
  4565. return self::$_errorCodes['value'];
  4566. }
  4567. private static function Besselk0($fNum) {
  4568. if ($fNum <= 2) {
  4569. $fNum2 = $fNum * 0.5;
  4570. $y = pow($fNum2,2);
  4571. $fRet = -log($fNum2) * self::BESSELI($fNum, 0) +
  4572. (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
  4573. (0.10750e-3 + $y * 0.74e-5))))));
  4574. } else {
  4575. $y = 2 / $fNum;
  4576. $fRet = exp(-$fNum) / sqrt($fNum) *
  4577. (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
  4578. (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
  4579. }
  4580. return $fRet;
  4581. }
  4582. private static function Besselk1($fNum) {
  4583. if ($fNum <= 2) {
  4584. $fNum2 = $fNum * 0.5;
  4585. $y = pow($fNum2,2);
  4586. $fRet = log($fNum2) * self::BESSELI($fNum, 1) +
  4587. (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
  4588. (-0.110404e-2 + $y * (-0.4686e-4))))))) / $fNum;
  4589. } else {
  4590. $y = 2 / $fNum;
  4591. $fRet = exp(-$fNum) / sqrt($fNum) *
  4592. (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
  4593. (0.325614e-2 + $y * (-0.68245e-3)))))));
  4594. }
  4595. return $fRet;
  4596. }
  4597. /**
  4598. * BESSELK
  4599. *
  4600. * Returns the modified Bessel function, which is equivalent to the Bessel functions evaluated for purely imaginary arguments.
  4601. *
  4602. * @param float $x
  4603. * @param float $n
  4604. * @return int
  4605. */
  4606. public static function BESSELK($x, $ord) {
  4607. $x = self::flattenSingleValue($x);
  4608. $n = floor(self::flattenSingleValue($ord));
  4609. if ((is_numeric($x)) && (is_numeric($ord))) {
  4610. if ($ord < 0) {
  4611. return self::$_errorCodes['num'];
  4612. }
  4613. switch($ord) {
  4614. case 0 : return self::Besselk0($x);
  4615. break;
  4616. case 1 : return self::Besselk1($x);
  4617. break;
  4618. default : $fTox = 2 / $x;
  4619. $fBkm = self::Besselk0($x);
  4620. $fBk = self::Besselk1($x);
  4621. for ($n = 1; $n < $ord; $n++) {
  4622. $fBkp = $fBkm + $n * $fTox * $fBk;
  4623. $fBkm = $fBk;
  4624. $fBk = $fBkp;
  4625. }
  4626. }
  4627. return $fBk;
  4628. }
  4629. return self::$_errorCodes['value'];
  4630. }
  4631. private static function Bessely0($fNum) {
  4632. if ($fNum < 8) {
  4633. $y = pow($fNum,2);
  4634. $f1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y * (-86327.92757 + $y * 228.4622733))));
  4635. $f2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y * (47447.26470 + $y * (226.1030244 + $y))));
  4636. $fRet = $f1 / $f2 + 0.636619772 * self::BESSELJ($fNum, 0) * log($fNum);
  4637. } else {
  4638. $z = 8 / $fNum;
  4639. $y = pow($z,2);
  4640. $xx = $fNum - 0.785398164;
  4641. $f1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
  4642. $f2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y * (-0.934945152e-7))));
  4643. $fRet = sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
  4644. }
  4645. return $fRet;
  4646. }
  4647. private static function Bessely1($fNum) {
  4648. if ($fNum < 8) {
  4649. $y = pow($fNum,2);
  4650. $f1 = $fNum * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y * (0.7349264551e9 + $y *
  4651. (-0.4237922726e7 + $y * 0.8511937935e4)))));
  4652. $f2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
  4653. (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
  4654. $fRet = $f1 / $f2 + 0.636619772 * ( self::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum);
  4655. } else {
  4656. $z = 8 / $fNum;
  4657. $y = $z * $z;
  4658. $xx = $fNum - 2.356194491;
  4659. $f1 = 1 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e6))));
  4660. $f2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y * (-0.88228987e-6 + $y * 0.105787412e-6)));
  4661. $fRet = sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
  4662. #i12430# ...but this seems to work much better.
  4663. // $fRet = sqrt(0.636619772 / $fNum) * sin($fNum - 2.356194491);
  4664. }
  4665. return $fRet;
  4666. }
  4667. /**
  4668. * BESSELY
  4669. *
  4670. * Returns the Bessel function, which is also called the Weber function or the Neumann function.
  4671. *
  4672. * @param float $x
  4673. * @param float $n
  4674. * @return int
  4675. */
  4676. public static function BESSELY($x, $ord) {
  4677. $x = self::flattenSingleValue($x);
  4678. $n = floor(self::flattenSingleValue($ord));
  4679. if ((is_numeric($x)) && (is_numeric($ord))) {
  4680. if ($ord < 0) {
  4681. return self::$_errorCodes['num'];
  4682. }
  4683. switch($ord) {
  4684. case 0 : return self::Bessely0($x);
  4685. break;
  4686. case 1 : return self::Bessely1($x);
  4687. break;
  4688. default: $fTox = 2 / $x;
  4689. $fBym = self::Bessely0($x);
  4690. $fBy = self::Bessely1($x);
  4691. for ($n = 1; $n < $ord; $n++) {
  4692. $fByp = $n * $fTox * $fBy - $fBym;
  4693. $fBym = $fBy;
  4694. $fBy = $fByp;
  4695. }
  4696. }
  4697. return $fBy;
  4698. }
  4699. return self::$_errorCodes['value'];
  4700. }
  4701. /**
  4702. * DELTA
  4703. *
  4704. * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
  4705. *
  4706. * @param float $a
  4707. * @param float $b
  4708. * @return int
  4709. */
  4710. public static function DELTA($a, $b=0) {
  4711. $a = self::flattenSingleValue($a);
  4712. $b = self::flattenSingleValue($b);
  4713. return (int) ($a == $b);
  4714. }
  4715. /**
  4716. * GESTEP
  4717. *
  4718. * Returns 1 if number = step; returns 0 (zero) otherwise
  4719. *
  4720. * @param float $number
  4721. * @param float $step
  4722. * @return int
  4723. */
  4724. public static function GESTEP($number, $step=0) {
  4725. $number = self::flattenSingleValue($number);
  4726. $step = self::flattenSingleValue($step);
  4727. return (int) ($number >= $step);
  4728. }
  4729. //
  4730. // Private method to calculate the erf value
  4731. //
  4732. private static $two_sqrtpi = 1.128379167095512574;
  4733. private static $rel_error = 1E-15;
  4734. private static function erfVal($x) {
  4735. if (abs($x) > 2.2) {
  4736. return 1 - self::erfcVal($x);
  4737. }
  4738. $sum = $term = $x;
  4739. $xsqr = pow($x,2);
  4740. $j = 1;
  4741. do {
  4742. $term *= $xsqr / $j;
  4743. $sum -= $term / (2 * $j + 1);
  4744. ++$j;
  4745. $term *= $xsqr / $j;
  4746. $sum += $term / (2 * $j + 1);
  4747. ++$j;
  4748. if ($sum == 0) {
  4749. break;
  4750. }
  4751. } while (abs($term / $sum) > self::$rel_error);
  4752. return self::$two_sqrtpi * $sum;
  4753. }
  4754. /**
  4755. * ERF
  4756. *
  4757. * Returns the error function integrated between lower_limit and upper_limit
  4758. *
  4759. * @param float $lower lower bound for integrating ERF
  4760. * @param float $upper upper bound for integrating ERF.
  4761. * If omitted, ERF integrates between zero and lower_limit
  4762. * @return int
  4763. */
  4764. public static function ERF($lower, $upper = 0) {
  4765. $lower = self::flattenSingleValue($lower);
  4766. $upper = self::flattenSingleValue($upper);
  4767. if ((is_numeric($lower)) && (is_numeric($upper))) {
  4768. if (($lower < 0) || ($upper < 0)) {
  4769. return self::$_errorCodes['num'];
  4770. }
  4771. if ($upper > $lower) {
  4772. return self::erfVal($upper) - self::erfVal($lower);
  4773. } else {
  4774. return self::erfVal($lower) - self::erfVal($upper);
  4775. }
  4776. }
  4777. return self::$_errorCodes['value'];
  4778. }
  4779. //
  4780. // Private method to calculate the erfc value
  4781. //
  4782. private static $one_sqrtpi = 0.564189583547756287;
  4783. private static function erfcVal($x) {
  4784. if (abs($x) < 2.2) {
  4785. return 1 - self::erfVal($x);
  4786. }
  4787. if ($x < 0) {
  4788. return 2 - self::erfc(-$x);
  4789. }
  4790. $a = $n = 1;
  4791. $b = $c = $x;
  4792. $d = pow($x,2) + 0.5;
  4793. $q1 = $q2 = $b / $d;
  4794. $t = 0;
  4795. do {
  4796. $t = $a * $n + $b * $x;
  4797. $a = $b;
  4798. $b = $t;
  4799. $t = $c * $n + $d * $x;
  4800. $c = $d;
  4801. $d = $t;
  4802. $n += 0.5;
  4803. $q1 = $q2;
  4804. $q2 = $b / $d;
  4805. } while ((abs($q1 - $q2) / $q2) > self::$rel_error);
  4806. return self::$one_sqrtpi * exp(-$x * $x) * $q2;
  4807. }
  4808. /**
  4809. * ERFC
  4810. *
  4811. * Returns the complementary ERF function integrated between x and infinity
  4812. *
  4813. * @param float $x The lower bound for integrating ERF
  4814. * @return int
  4815. */
  4816. public static function ERFC($x) {
  4817. $x = self::flattenSingleValue($x);
  4818. if (is_numeric($x)) {
  4819. if ($x < 0) {
  4820. return self::$_errorCodes['num'];
  4821. }
  4822. return self::erfcVal($x);
  4823. }
  4824. return self::$_errorCodes['value'];
  4825. }
  4826. /**
  4827. * EFFECT
  4828. *
  4829. * Returns the effective interest rate given the nominal rate and the number of compounding payments per year.
  4830. *
  4831. * @param float $nominal_rate Nominal interest rate
  4832. * @param int $npery Number of compounding payments per year
  4833. * @return float
  4834. */
  4835. public static function EFFECT($nominal_rate = 0, $npery = 0) {
  4836. $nominal_rate = self::flattenSingleValue($$nominal_rate);
  4837. $npery = (int)self::flattenSingleValue($npery);
  4838. // Validate parameters
  4839. if ($$nominal_rate <= 0 || $npery < 1) {
  4840. return self::$_errorCodes['num'];
  4841. }
  4842. return pow((1 + $nominal_rate / $npery), $npery) - 1;
  4843. }
  4844. /**
  4845. * NOMINAL
  4846. *
  4847. * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
  4848. *
  4849. * @param float $effect_rate Effective interest rate
  4850. * @param int $npery Number of compounding payments per year
  4851. * @return float
  4852. */
  4853. public static function NOMINAL($effect_rate = 0, $npery = 0) {
  4854. $effect_rate = self::flattenSingleValue($effect_rate);
  4855. $npery = (int)self::flattenSingleValue($npery);
  4856. // Validate parameters
  4857. if ($effect_rate <= 0 || $npery < 1) {
  4858. return self::$_errorCodes['num'];
  4859. }
  4860. // Calculate
  4861. return $npery * (pow($effect_rate + 1, 1 / $npery) - 1);
  4862. }
  4863. /**
  4864. * PV
  4865. *
  4866. * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
  4867. *
  4868. * @param float $rate Interest rate per period
  4869. * @param int $nper Number of periods
  4870. * @param float $pmt Periodic payment (annuity)
  4871. * @param float $fv Future Value
  4872. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  4873. * @return float
  4874. */
  4875. public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0) {
  4876. $rate = self::flattenSingleValue($rate);
  4877. $nper = self::flattenSingleValue($nper);
  4878. $pmt = self::flattenSingleValue($pmt);
  4879. $fv = self::flattenSingleValue($fv);
  4880. $type = self::flattenSingleValue($type);
  4881. // Validate parameters
  4882. if ($type != 0 && $type != 1) {
  4883. return self::$_errorCodes['num'];
  4884. }
  4885. // Calculate
  4886. if (!is_null($rate) && $rate != 0) {
  4887. return (-$pmt * (1 + $rate * $type) * ((pow(1 + $rate, $nper) - 1) / $rate) - $fv) / pow(1 + $rate, $nper);
  4888. } else {
  4889. return -$fv - $pmt * $nper;
  4890. }
  4891. }
  4892. /**
  4893. * FV
  4894. *
  4895. * Returns the Future Value of a cash flow with constant payments and interest rate (annuities).
  4896. *
  4897. * @param float $rate Interest rate per period
  4898. * @param int $nper Number of periods
  4899. * @param float $pmt Periodic payment (annuity)
  4900. * @param float $pv Present Value
  4901. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  4902. * @return float
  4903. */
  4904. public static function FV($rate = 0, $nper = 0, $pmt = 0, $pv = 0, $type = 0) {
  4905. $rate = self::flattenSingleValue($rate);
  4906. $nper = self::flattenSingleValue($nper);
  4907. $pmt = self::flattenSingleValue($pmt);
  4908. $pv = self::flattenSingleValue($pv);
  4909. $type = self::flattenSingleValue($type);
  4910. // Validate parameters
  4911. if ($type != 0 && $type != 1) {
  4912. return self::$_errorCodes['num'];
  4913. }
  4914. // Calculate
  4915. if (!is_null($rate) && $rate != 0) {
  4916. return -$pv * pow(1 + $rate, $nper) - $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1) / $rate;
  4917. } else {
  4918. return -$pv - $pmt * $nper;
  4919. }
  4920. }
  4921. /**
  4922. * PMT
  4923. *
  4924. * Returns the constant payment (annuity) for a cash flow with a constant interest rate.
  4925. *
  4926. * @param float $rate Interest rate per period
  4927. * @param int $nper Number of periods
  4928. * @param float $pv Present Value
  4929. * @param float $fv Future Value
  4930. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  4931. * @return float
  4932. */
  4933. public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0) {
  4934. $rate = self::flattenSingleValue($rate);
  4935. $nper = self::flattenSingleValue($nper);
  4936. $pv = self::flattenSingleValue($pv);
  4937. $fv = self::flattenSingleValue($fv);
  4938. $type = self::flattenSingleValue($type);
  4939. // Validate parameters
  4940. if ($type != 0 && $type != 1) {
  4941. return self::$_errorCodes['num'];
  4942. }
  4943. // Calculate
  4944. if (!is_null($rate) && $rate != 0) {
  4945. return (-$fv - $pv * pow(1 + $rate, $nper)) / (1 + $rate * $type) / ((pow(1 + $rate, $nper) - 1) / $rate);
  4946. } else {
  4947. return (-$pv - $fv) / $nper;
  4948. }
  4949. }
  4950. /**
  4951. * NPER
  4952. *
  4953. * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
  4954. *
  4955. * @param float $rate Interest rate per period
  4956. * @param int $pmt Periodic payment (annuity)
  4957. * @param float $pv Present Value
  4958. * @param float $fv Future Value
  4959. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  4960. * @return float
  4961. */
  4962. public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0) {
  4963. $rate = self::flattenSingleValue($rate);
  4964. $pmt = self::flattenSingleValue($pmt);
  4965. $pv = self::flattenSingleValue($pv);
  4966. $fv = self::flattenSingleValue($fv);
  4967. $type = self::flattenSingleValue($type);
  4968. // Validate parameters
  4969. if ($type != 0 && $type != 1) {
  4970. return self::$_errorCodes['num'];
  4971. }
  4972. // Calculate
  4973. if (!is_null($rate) && $rate != 0) {
  4974. if ($pmt == 0 && $pv == 0) {
  4975. return self::$_errorCodes['num'];
  4976. }
  4977. return log(($pmt * (1 + $rate * $type) / $rate - $fv) / ($pv + $pmt * (1 + $rate * $type) / $rate)) / log(1 + $rate);
  4978. } else {
  4979. if ($pmt == 0) {
  4980. return self::$_errorCodes['num'];
  4981. }
  4982. return (-$pv -$fv) / $pmt;
  4983. }
  4984. }
  4985. /**
  4986. * NPV
  4987. *
  4988. * Returns the Net Present Value of a cash flow series given a discount rate.
  4989. *
  4990. * @param float Discount interest rate
  4991. * @param array Cash flow series
  4992. * @return float
  4993. */
  4994. public static function NPV() {
  4995. // Return value
  4996. $returnValue = 0;
  4997. // Loop trough arguments
  4998. $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  4999. // Calculate
  5000. $rate = array_shift($aArgs);
  5001. for ($i = 1; $i <= count($aArgs); ++$i) {
  5002. // Is it a numeric value?
  5003. if (is_numeric($aArgs[$i - 1])) {
  5004. $returnValue += $aArgs[$i - 1] / pow(1 + $rate, $i);
  5005. }
  5006. }
  5007. // Return
  5008. return $returnValue;
  5009. }
  5010. /**
  5011. * ACCRINT
  5012. *
  5013. * Computes the accrued interest for a security that pays periodic interest.
  5014. *
  5015. * @param int $issue
  5016. * @param int $firstInterest
  5017. * @param int $settlement
  5018. * @param int $rate
  5019. * @param int $par
  5020. * @param int $frequency
  5021. * @param int $basis
  5022. * @return int The accrued interest for a security that pays periodic interest.
  5023. */
  5024. /*
  5025. public static function ACCRINT($issue = 0, $firstInterest = 0, $settlement = 0, $rate = 0, $par = 1000, $frequency = 1, $basis = 0) {
  5026. $issue = self::flattenSingleValue($issue);
  5027. $firstInterest = self::flattenSingleValue($firstInterest);
  5028. $settlement = self::flattenSingleValue($settlement);
  5029. $rate = self::flattenSingleValue($rate);
  5030. $par = self::flattenSingleValue($par);
  5031. $frequency = self::flattenSingleValue($frequency);
  5032. $basis = self::flattenSingleValue($basis);
  5033. // Perform checks
  5034. if ($issue >= $settlement || $rate <= 0 || $par <= 0 || !($frequency == 1 || $frequency == 2 || $frequency == 4) || $basis < 0 || $basis > 4) return self::$_errorCodes['num'];
  5035. // Calculate value
  5036. return $par * ($rate / $frequency) *
  5037. }
  5038. */
  5039. /**
  5040. * SLN
  5041. *
  5042. * Returns the straight-line depreciation of an asset for one period
  5043. *
  5044. * @param cost Initial cost of the asset
  5045. * @param salvage Value at the end of the depreciation
  5046. * @param life Number of periods over which the asset is depreciated
  5047. * @return float
  5048. */
  5049. public static function SLN($cost, $salvage, $life) {
  5050. $cost = self::flattenSingleValue($cost);
  5051. $salvage = self::flattenSingleValue($salvage);
  5052. $life = self::flattenSingleValue($life);
  5053. // Calculate
  5054. if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life))) {
  5055. if ($life < 0) {
  5056. return self::$_errorCodes['num'];
  5057. }
  5058. return ($cost - $salvage) / $life;
  5059. }
  5060. return self::$_errorCodes['value'];
  5061. }
  5062. /**
  5063. * SYD
  5064. *
  5065. * Returns the sum-of-years' digits depreciation of an asset for a specified period.
  5066. *
  5067. * @param cost Initial cost of the asset
  5068. * @param salvage Value at the end of the depreciation
  5069. * @param life Number of periods over which the asset is depreciated
  5070. * @param period Period
  5071. * @return float
  5072. */
  5073. public static function SYD($cost, $salvage, $life, $period) {
  5074. $cost = self::flattenSingleValue($cost);
  5075. $salvage = self::flattenSingleValue($salvage);
  5076. $life = self::flattenSingleValue($life);
  5077. $period = self::flattenSingleValue($period);
  5078. // Calculate
  5079. if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period))) {
  5080. if (($life < 1) || ($salvage < $life)) {
  5081. return self::$_errorCodes['num'];
  5082. }
  5083. return (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
  5084. }
  5085. return self::$_errorCodes['value'];
  5086. }
  5087. /**
  5088. * Flatten multidemensional array
  5089. *
  5090. * @param array $array Array to be flattened
  5091. * @return array Flattened array
  5092. */
  5093. public static function flattenArray($array) {
  5094. $arrayValues = array();
  5095. foreach ($array as $value) {
  5096. if (is_scalar($value)) {
  5097. $arrayValues[] = self::flattenSingleValue($value);
  5098. } else if (is_array($value)) {
  5099. $arrayValues = array_merge($arrayValues, self::flattenArray($value));
  5100. } else {
  5101. $arrayValues[] = $value;
  5102. }
  5103. }
  5104. return $arrayValues;
  5105. }
  5106. /**
  5107. * Convert an array with one element to a flat value
  5108. *
  5109. * @param mixed $value Array or flat value
  5110. * @return mixed
  5111. */
  5112. public static function flattenSingleValue($value = '') {
  5113. if (is_array($value)) {
  5114. $value = self::flattenSingleValue(array_pop($value));
  5115. }
  5116. return $value;
  5117. }
  5118. }