PageRenderTime 98ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/libs/phpexcel/PHPExcel/Calculation/Functions.php

https://github.com/vykintasv/psiprogresas
PHP | 10505 lines | 6179 code | 1177 blank | 3149 comment | 1740 complexity | d7547c97c8a3cafe206bd8cfa285027c MD5 | raw file
  1. <?php
  2. /**
  3. * PHPExcel
  4. *
  5. * Copyright (c) 2006 - 2009 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 - 2009 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version 1.7.0, 2009-08-10
  26. */
  27. /** EPS */
  28. define('EPS', 2.22e-16);
  29. /** MAX_VALUE */
  30. define('MAX_VALUE', 1.2e308);
  31. /** LOG_GAMMA_X_MAX_VALUE */
  32. define('LOG_GAMMA_X_MAX_VALUE', 2.55e305);
  33. /** SQRT2PI */
  34. define('SQRT2PI', 2.5066282746310005024157652848110452530069867406099);
  35. /** XMININ */
  36. define('XMININ', 2.23e-308);
  37. /** MAX_ITERATIONS */
  38. define('MAX_ITERATIONS', 150);
  39. /** PRECISION */
  40. define('PRECISION', 8.88E-016);
  41. /** EULER */
  42. define('EULER', 2.71828182845904523536);
  43. $savedPrecision = ini_get('precision');
  44. if ($savedPrecision < 16) {
  45. ini_set('precision',16);
  46. }
  47. /** PHPExcel root directory */
  48. if (!defined('PHPEXCEL_ROOT')) {
  49. /**
  50. * @ignore
  51. */
  52. define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../');
  53. }
  54. /** PHPExcel_Cell */
  55. require_once PHPEXCEL_ROOT . 'PHPExcel/Cell.php';
  56. /** PHPExcel_Cell_DataType */
  57. require_once PHPEXCEL_ROOT . 'PHPExcel/Cell/DataType.php';
  58. /** PHPExcel_Style_NumberFormat */
  59. require_once PHPEXCEL_ROOT . 'PHPExcel/Style/NumberFormat.php';
  60. /** PHPExcel_Shared_Date */
  61. require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/Date.php';
  62. /** Matrix */
  63. require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/JAMA/Matrix.php';
  64. require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/trend/trendClass.php';
  65. /**
  66. * PHPExcel_Calculation_Functions
  67. *
  68. * @category PHPExcel
  69. * @package PHPExcel_Calculation
  70. * @copyright Copyright (c) 2006 - 2009 PHPExcel (http://www.codeplex.com/PHPExcel)
  71. */
  72. class PHPExcel_Calculation_Functions {
  73. /** constants */
  74. const COMPATIBILITY_EXCEL = 'Excel';
  75. const COMPATIBILITY_GNUMERIC = 'Gnumeric';
  76. const COMPATIBILITY_OPENOFFICE = 'OpenOfficeCalc';
  77. const RETURNDATE_PHP_NUMERIC = 'P';
  78. const RETURNDATE_PHP_OBJECT = 'O';
  79. const RETURNDATE_EXCEL = 'E';
  80. /**
  81. * Compatibility mode to use for error checking and responses
  82. *
  83. * @access private
  84. * @var string
  85. */
  86. private static $compatibilityMode = self::COMPATIBILITY_EXCEL;
  87. /**
  88. * Data Type to use when returning date values
  89. *
  90. * @access private
  91. * @var integer
  92. */
  93. private static $ReturnDateType = self::RETURNDATE_EXCEL;
  94. /**
  95. * List of error codes
  96. *
  97. * @access private
  98. * @var array
  99. */
  100. private static $_errorCodes = array( 'null' => '#NULL!',
  101. 'divisionbyzero' => '#DIV/0!',
  102. 'value' => '#VALUE!',
  103. 'reference' => '#REF!',
  104. 'name' => '#NAME?',
  105. 'num' => '#NUM!',
  106. 'na' => '#N/A',
  107. 'gettingdata' => '#GETTING_DATA'
  108. );
  109. /**
  110. * Set the Compatibility Mode
  111. *
  112. * @access public
  113. * @category Function Configuration
  114. * @param string $compatibilityMode Compatibility Mode
  115. * Permitted values are:
  116. * PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL 'Excel'
  117. * PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
  118. * PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
  119. * @return boolean (Success or Failure)
  120. */
  121. public static function setCompatibilityMode($compatibilityMode) {
  122. if (($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
  123. ($compatibilityMode == self::COMPATIBILITY_GNUMERIC) ||
  124. ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  125. self::$compatibilityMode = $compatibilityMode;
  126. return True;
  127. }
  128. return False;
  129. } // function setCompatibilityMode()
  130. /**
  131. * Return the current Compatibility Mode
  132. *
  133. * @access public
  134. * @category Function Configuration
  135. * @return string Compatibility Mode
  136. * Possible Return values are:
  137. * PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL 'Excel'
  138. * PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
  139. * PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
  140. */
  141. public static function getCompatibilityMode() {
  142. return self::$compatibilityMode;
  143. } // function getCompatibilityMode()
  144. /**
  145. * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object)
  146. *
  147. * @access public
  148. * @category Function Configuration
  149. * @param string $returnDateType Return Date Format
  150. * Permitted values are:
  151. * PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC 'P'
  152. * PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT 'O'
  153. * PHPExcel_Calculation_Functions::RETURNDATE_EXCEL 'E'
  154. * @return boolean Success or failure
  155. */
  156. public static function setReturnDateType($returnDateType) {
  157. if (($returnDateType == self::RETURNDATE_PHP_NUMERIC) ||
  158. ($returnDateType == self::RETURNDATE_PHP_OBJECT) ||
  159. ($returnDateType == self::RETURNDATE_EXCEL)) {
  160. self::$ReturnDateType = $returnDateType;
  161. return True;
  162. }
  163. return False;
  164. } // function setReturnDateType()
  165. /**
  166. * Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object)
  167. *
  168. * @access public
  169. * @category Function Configuration
  170. * @return string Return Date Format
  171. * Possible Return values are:
  172. * PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC 'P'
  173. * PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT 'O'
  174. * PHPExcel_Calculation_Functions::RETURNDATE_EXCEL 'E'
  175. */
  176. public static function getReturnDateType() {
  177. return self::$ReturnDateType;
  178. } // function getReturnDateType()
  179. /**
  180. * DUMMY
  181. *
  182. * @access public
  183. * @category Error Returns
  184. * @return string #Not Yet Implemented
  185. */
  186. public static function DUMMY() {
  187. return '#Not Yet Implemented';
  188. } // function DUMMY()
  189. /**
  190. * NA
  191. *
  192. * @access public
  193. * @category Error Returns
  194. * @return string #N/A!
  195. */
  196. public static function NA() {
  197. return self::$_errorCodes['na'];
  198. } // function NA()
  199. /**
  200. * NAN
  201. *
  202. * @access public
  203. * @category Error Returns
  204. * @return string #NUM!
  205. */
  206. public static function NaN() {
  207. return self::$_errorCodes['num'];
  208. } // function NAN()
  209. /**
  210. * NAME
  211. *
  212. * @access public
  213. * @category Error Returns
  214. * @return string #NAME!
  215. */
  216. public static function NAME() {
  217. return self::$_errorCodes['name'];
  218. } // function NAME()
  219. /**
  220. * REF
  221. *
  222. * @access public
  223. * @category Error Returns
  224. * @return string #REF!
  225. */
  226. public static function REF() {
  227. return self::$_errorCodes['reference'];
  228. } // function REF()
  229. /**
  230. * LOGICAL_AND
  231. *
  232. * Returns boolean TRUE if all its arguments are TRUE; returns FALSE if one or more argument is FALSE.
  233. *
  234. * Excel Function:
  235. * AND(logical1[,logical2[, ...]])
  236. *
  237. * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
  238. * or references that contain logical values.
  239. *
  240. * Booleans arguments are treated as True or False as appropriate
  241. * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
  242. * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds the value TRUE or FALSE,
  243. * holds the value TRUE or FALSE, in which case it is evaluated as a boolean
  244. *
  245. * @access public
  246. * @category Logical Functions
  247. * @param mixed $arg,... Data values
  248. * @return boolean The logical AND of the arguments.
  249. */
  250. public static function LOGICAL_AND() {
  251. // Return value
  252. $returnValue = True;
  253. // Loop through the arguments
  254. $aArgs = self::flattenArray(func_get_args());
  255. $argCount = 0;
  256. foreach ($aArgs as $arg) {
  257. // Is it a boolean value?
  258. if (is_bool($arg)) {
  259. $returnValue = $returnValue && $arg;
  260. ++$argCount;
  261. } elseif ((is_numeric($arg)) && (!is_string($arg))) {
  262. $returnValue = $returnValue && ($arg != 0);
  263. ++$argCount;
  264. } elseif (is_string($arg)) {
  265. $arg = strtoupper($arg);
  266. if ($arg == 'TRUE') {
  267. $arg = 1;
  268. } elseif ($arg == 'FALSE') {
  269. $arg = 0;
  270. } else {
  271. return self::$_errorCodes['value'];
  272. }
  273. $returnValue = $returnValue && ($arg != 0);
  274. ++$argCount;
  275. }
  276. }
  277. // Return
  278. if ($argCount == 0) {
  279. return self::$_errorCodes['value'];
  280. }
  281. return $returnValue;
  282. } // function LOGICAL_AND()
  283. /**
  284. * LOGICAL_OR
  285. *
  286. * Returns boolean TRUE if any argument is TRUE; returns FALSE if all arguments are FALSE.
  287. *
  288. * Excel Function:
  289. * OR(logical1[,logical2[, ...]])
  290. *
  291. * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
  292. * or references that contain logical values.
  293. *
  294. * Booleans arguments are treated as True or False as appropriate
  295. * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
  296. * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string
  297. * holds the value TRUE or FALSE, in which case it is evaluated as a boolean
  298. *
  299. * @access public
  300. * @category Logical Functions
  301. * @param mixed $arg,... Data values
  302. * @return boolean The logical OR of the arguments.
  303. */
  304. public static function LOGICAL_OR() {
  305. // Return value
  306. $returnValue = False;
  307. // Loop through the arguments
  308. $aArgs = self::flattenArray(func_get_args());
  309. $argCount = 0;
  310. foreach ($aArgs as $arg) {
  311. // Is it a boolean value?
  312. if (is_bool($arg)) {
  313. $returnValue = $returnValue || $arg;
  314. ++$argCount;
  315. } elseif ((is_numeric($arg)) && (!is_string($arg))) {
  316. $returnValue = $returnValue || ($arg != 0);
  317. ++$argCount;
  318. } elseif (is_string($arg)) {
  319. $arg = strtoupper($arg);
  320. if ($arg == 'TRUE') {
  321. $arg = 1;
  322. } elseif ($arg == 'FALSE') {
  323. $arg = 0;
  324. } else {
  325. return self::$_errorCodes['value'];
  326. }
  327. $returnValue = $returnValue || ($arg != 0);
  328. ++$argCount;
  329. }
  330. }
  331. // Return
  332. if ($argCount == 0) {
  333. return self::$_errorCodes['value'];
  334. }
  335. return $returnValue;
  336. } // function LOGICAL_OR()
  337. /**
  338. * LOGICAL_FALSE
  339. *
  340. * Returns the boolean FALSE.
  341. *
  342. * Excel Function:
  343. * FALSE()
  344. *
  345. * @access public
  346. * @category Logical Functions
  347. * @return boolean False
  348. */
  349. public static function LOGICAL_FALSE() {
  350. return False;
  351. } // function LOGICAL_FALSE()
  352. /**
  353. * LOGICAL_TRUE
  354. *
  355. * Returns the boolean TRUE.
  356. *
  357. * Excel Function:
  358. * TRUE()
  359. *
  360. * @access public
  361. * @category Logical Functions
  362. * @return boolean True
  363. */
  364. public static function LOGICAL_TRUE() {
  365. return True;
  366. } // function LOGICAL_TRUE()
  367. /**
  368. * LOGICAL_NOT
  369. *
  370. * Returns the boolean inverse of the argument.
  371. *
  372. * Excel Function:
  373. * NOT(logical)
  374. *
  375. * @access public
  376. * @category Logical Functions
  377. * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
  378. * @return boolean The boolean inverse of the argument.
  379. */
  380. public static function LOGICAL_NOT($logical) {
  381. return !$logical;
  382. } // function LOGICAL_NOT()
  383. /**
  384. * STATEMENT_IF
  385. *
  386. * Returns one value if a condition you specify evaluates to TRUE and another value if it evaluates to FALSE.
  387. *
  388. * Excel Function:
  389. * IF(condition[,returnIfTrue[,returnIfFalse]])
  390. *
  391. * Condition is any value or expression that can be evaluated to TRUE or FALSE.
  392. * For example, A10=100 is a logical expression; if the value in cell A10 is equal to 100,
  393. * the expression evaluates to TRUE. Otherwise, the expression evaluates to FALSE.
  394. * This argument can use any comparison calculation operator.
  395. * ReturnIfTrue is the value that is returned if condition evaluates to TRUE.
  396. * For example, if this argument is the text string "Within budget" and the condition argument evaluates to TRUE,
  397. * then the IF function returns the text "Within budget"
  398. * If condition is TRUE and ReturnIfTrue is blank, this argument returns 0 (zero). To display the word TRUE, use
  399. * the logical value TRUE for this argument.
  400. * ReturnIfTrue can be another formula.
  401. * ReturnIfFalse is the value that is returned if condition evaluates to FALSE.
  402. * For example, if this argument is the text string "Over budget" and the condition argument evaluates to FALSE,
  403. * then the IF function returns the text "Over budget".
  404. * If condition is FALSE and ReturnIfFalse is omitted, then the logical value FALSE is returned.
  405. * If condition is FALSE and ReturnIfFalse is blank, then the value 0 (zero) is returned.
  406. * ReturnIfFalse can be another formula.
  407. *
  408. * @param mixed $condition Condition to evaluate
  409. * @param mixed $returnIfTrue Value to return when condition is true
  410. * @param mixed $returnIfFalse Value to return when condition is false
  411. * @return mixed The value of returnIfTrue or returnIfFalse determined by condition
  412. */
  413. public static function STATEMENT_IF($condition = true, $returnIfTrue = 0, $returnIfFalse = False) {
  414. $condition = self::flattenSingleValue($condition);
  415. $returnIfTrue = self::flattenSingleValue($returnIfTrue);
  416. $returnIfFalse = self::flattenSingleValue($returnIfFalse);
  417. if (is_null($returnIfTrue)) { $returnIfTrue = 0; }
  418. if (is_null($returnIfFalse)) { $returnIfFalse = 0; }
  419. return ($condition ? $returnIfTrue : $returnIfFalse);
  420. } // function STATEMENT_IF()
  421. /**
  422. * STATEMENT_IFERROR
  423. *
  424. * @param mixed $value Value to check , is also value when no error
  425. * @param mixed $errorpart Value when error
  426. * @return mixed
  427. */
  428. public static function STATEMENT_IFERROR($value = '', $errorpart = '') {
  429. return self::STATEMENT_IF(self::IS_ERROR($value), $errorpart, $value);
  430. } // function STATEMENT_IFERROR()
  431. /**
  432. * ATAN2
  433. *
  434. * This function calculates the arc tangent of the two variables x and y. It is similar to
  435. * calculating the arc tangent of y ÷ x, except that the signs of both arguments are used
  436. * to determine the quadrant of the result.
  437. * The arctangent is the angle from the x-axis to a line containing the origin (0, 0) and a
  438. * point with coordinates (xCoordinate, yCoordinate). The angle is given in radians between
  439. * -pi and pi, excluding -pi.
  440. *
  441. * Note that the Excel ATAN2() function accepts its arguments in the reverse order to the standard
  442. * PHP atan2() function, so we need to reverse them here before calling the PHP atan() function.
  443. *
  444. * Excel Function:
  445. * ATAN2(xCoordinate,yCoordinate)
  446. *
  447. * @access public
  448. * @category Mathematical and Trigonometric Functions
  449. * @param float $xCoordinate The x-coordinate of the point.
  450. * @param float $yCoordinate The y-coordinate of the point.
  451. * @return float The inverse tangent of the specified x- and y-coordinates.
  452. */
  453. public static function REVERSE_ATAN2($xCoordinate, $yCoordinate) {
  454. $xCoordinate = (float) self::flattenSingleValue($xCoordinate);
  455. $yCoordinate = (float) self::flattenSingleValue($yCoordinate);
  456. if (($xCoordinate == 0) && ($yCoordinate == 0)) {
  457. return self::$_errorCodes['divisionbyzero'];
  458. }
  459. return atan2($yCoordinate, $xCoordinate);
  460. } // function REVERSE_ATAN2()
  461. /**
  462. * LOG_BASE
  463. *
  464. * Returns the logarithm of a number to a specified base. The default base is 10.
  465. *
  466. * Excel Function:
  467. * LOG(number[,base])
  468. *
  469. * @access public
  470. * @category Mathematical and Trigonometric Functions
  471. * @param float $value The positive real number for which you want the logarithm
  472. * @param float $base The base of the logarithm. If base is omitted, it is assumed to be 10.
  473. * @return float
  474. */
  475. public static function LOG_BASE($number, $base=10) {
  476. $number = self::flattenSingleValue($number);
  477. $base = self::flattenSingleValue($base);
  478. return log($number, $base);
  479. } // function LOG_BASE()
  480. /**
  481. * SUM
  482. *
  483. * SUM computes the sum of all the values and cells referenced in the argument list.
  484. *
  485. * Excel Function:
  486. * SUM(value1[,value2[, ...]])
  487. *
  488. * @access public
  489. * @category Mathematical and Trigonometric Functions
  490. * @param mixed $arg,... Data values
  491. * @return float
  492. */
  493. public static function SUM() {
  494. // Return value
  495. $returnValue = 0;
  496. // Loop through the arguments
  497. $aArgs = self::flattenArray(func_get_args());
  498. foreach ($aArgs as $arg) {
  499. // Is it a numeric value?
  500. if ((is_numeric($arg)) && (!is_string($arg))) {
  501. $returnValue += $arg;
  502. }
  503. }
  504. // Return
  505. return $returnValue;
  506. } // function SUM()
  507. /**
  508. * SUMSQ
  509. *
  510. * SUMSQ returns the sum of the squares of the arguments
  511. *
  512. * Excel Function:
  513. * SUMSQ(value1[,value2[, ...]])
  514. *
  515. * @access public
  516. * @category Mathematical and Trigonometric Functions
  517. * @param mixed $arg,... Data values
  518. * @return float
  519. */
  520. public static function SUMSQ() {
  521. // Return value
  522. $returnValue = 0;
  523. // Loop trough arguments
  524. $aArgs = self::flattenArray(func_get_args());
  525. foreach ($aArgs as $arg) {
  526. // Is it a numeric value?
  527. if ((is_numeric($arg)) && (!is_string($arg))) {
  528. $returnValue += pow($arg,2);
  529. }
  530. }
  531. // Return
  532. return $returnValue;
  533. } // function SUMSQ()
  534. /**
  535. * PRODUCT
  536. *
  537. * PRODUCT returns the product of all the values and cells referenced in the argument list.
  538. *
  539. * Excel Function:
  540. * PRODUCT(value1[,value2[, ...]])
  541. *
  542. * @access public
  543. * @category Mathematical and Trigonometric Functions
  544. * @param mixed $arg,... Data values
  545. * @return float
  546. */
  547. public static function PRODUCT() {
  548. // Return value
  549. $returnValue = null;
  550. // Loop trough arguments
  551. $aArgs = self::flattenArray(func_get_args());
  552. foreach ($aArgs as $arg) {
  553. // Is it a numeric value?
  554. if ((is_numeric($arg)) && (!is_string($arg))) {
  555. if (is_null($returnValue)) {
  556. $returnValue = $arg;
  557. } else {
  558. $returnValue *= $arg;
  559. }
  560. }
  561. }
  562. // Return
  563. if (is_null($returnValue)) {
  564. return 0;
  565. }
  566. return $returnValue;
  567. } // function PRODUCT()
  568. /**
  569. * QUOTIENT
  570. *
  571. * QUOTIENT function returns the integer portion of a division. Numerator is the divided number
  572. * and denominator is the divisor.
  573. *
  574. * Excel Function:
  575. * QUOTIENT(value1[,value2[, ...]])
  576. *
  577. * @access public
  578. * @category Mathematical and Trigonometric Functions
  579. * @param mixed $arg,... Data values
  580. * @return float
  581. */
  582. public static function QUOTIENT() {
  583. // Return value
  584. $returnValue = null;
  585. // Loop trough arguments
  586. $aArgs = self::flattenArray(func_get_args());
  587. foreach ($aArgs as $arg) {
  588. // Is it a numeric value?
  589. if ((is_numeric($arg)) && (!is_string($arg))) {
  590. if (is_null($returnValue)) {
  591. $returnValue = ($arg == 0) ? 0 : $arg;
  592. } else {
  593. if (($returnValue == 0) || ($arg == 0)) {
  594. $returnValue = 0;
  595. } else {
  596. $returnValue /= $arg;
  597. }
  598. }
  599. }
  600. }
  601. // Return
  602. return intval($returnValue);
  603. } // function QUOTIENT()
  604. /**
  605. * MIN
  606. *
  607. * MIN returns the value of the element of the values passed that has the smallest value,
  608. * with negative numbers considered smaller than positive numbers.
  609. *
  610. * Excel Function:
  611. * MIN(value1[,value2[, ...]])
  612. *
  613. * @access public
  614. * @category Statistical Functions
  615. * @param mixed $arg,... Data values
  616. * @return float
  617. */
  618. public static function MIN() {
  619. // Return value
  620. $returnValue = null;
  621. // Loop trough arguments
  622. $aArgs = self::flattenArray(func_get_args());
  623. foreach ($aArgs as $arg) {
  624. // Is it a numeric value?
  625. if ((is_numeric($arg)) && (!is_string($arg))) {
  626. if ((is_null($returnValue)) || ($arg < $returnValue)) {
  627. $returnValue = $arg;
  628. }
  629. }
  630. }
  631. // Return
  632. if(is_null($returnValue)) {
  633. return 0;
  634. }
  635. return $returnValue;
  636. } // function MIN()
  637. /**
  638. * MINA
  639. *
  640. * Returns the smallest value in a list of arguments, including numbers, text, and logical values
  641. *
  642. * Excel Function:
  643. * MINA(value1[,value2[, ...]])
  644. *
  645. * @access public
  646. * @category Statistical Functions
  647. * @param mixed $arg,... Data values
  648. * @return float
  649. */
  650. public static function MINA() {
  651. // Return value
  652. $returnValue = null;
  653. // Loop through arguments
  654. $aArgs = self::flattenArray(func_get_args());
  655. foreach ($aArgs as $arg) {
  656. // Is it a numeric value?
  657. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
  658. if (is_bool($arg)) {
  659. $arg = (integer) $arg;
  660. } elseif (is_string($arg)) {
  661. $arg = 0;
  662. }
  663. if ((is_null($returnValue)) || ($arg < $returnValue)) {
  664. $returnValue = $arg;
  665. }
  666. }
  667. }
  668. // Return
  669. if(is_null($returnValue)) {
  670. return 0;
  671. }
  672. return $returnValue;
  673. } // function MINA()
  674. /**
  675. * SMALL
  676. *
  677. * Returns the nth smallest value in a data set. You can use this function to
  678. * select a value based on its relative standing.
  679. *
  680. * Excel Function:
  681. * SMALL(value1[,value2[, ...]],entry)
  682. *
  683. * @access public
  684. * @category Statistical Functions
  685. * @param mixed $arg,... Data values
  686. * @param int $entry Position (ordered from the smallest) in the array or range of data to return
  687. * @return float
  688. */
  689. public static function SMALL() {
  690. $aArgs = self::flattenArray(func_get_args());
  691. // Calculate
  692. $n = array_pop($aArgs);
  693. if ((is_numeric($n)) && (!is_string($n))) {
  694. $mArgs = array();
  695. foreach ($aArgs as $arg) {
  696. // Is it a numeric value?
  697. if ((is_numeric($arg)) && (!is_string($arg))) {
  698. $mArgs[] = $arg;
  699. }
  700. }
  701. $count = self::COUNT($mArgs);
  702. $n = floor(--$n);
  703. if (($n < 0) || ($n >= $count) || ($count == 0)) {
  704. return self::$_errorCodes['num'];
  705. }
  706. sort($mArgs);
  707. return $mArgs[$n];
  708. }
  709. return self::$_errorCodes['value'];
  710. } // function SMALL()
  711. /**
  712. * MAX
  713. *
  714. * MAX returns the value of the element of the values passed that has the highest value,
  715. * with negative numbers considered smaller than positive numbers.
  716. *
  717. * Excel Function:
  718. * MAX(value1[,value2[, ...]])
  719. *
  720. * @access public
  721. * @category Statistical Functions
  722. * @param mixed $arg,... Data values
  723. * @return float
  724. */
  725. public static function MAX() {
  726. // Return value
  727. $returnValue = null;
  728. // Loop trough arguments
  729. $aArgs = self::flattenArray(func_get_args());
  730. foreach ($aArgs as $arg) {
  731. // Is it a numeric value?
  732. if ((is_numeric($arg)) && (!is_string($arg))) {
  733. if ((is_null($returnValue)) || ($arg > $returnValue)) {
  734. $returnValue = $arg;
  735. }
  736. }
  737. }
  738. // Return
  739. if(is_null($returnValue)) {
  740. return 0;
  741. }
  742. return $returnValue;
  743. } // function MAX()
  744. /**
  745. * MAXA
  746. *
  747. * Returns the greatest value in a list of arguments, including numbers, text, and logical values
  748. *
  749. * Excel Function:
  750. * MAXA(value1[,value2[, ...]])
  751. *
  752. * @access public
  753. * @category Statistical Functions
  754. * @param mixed $arg,... Data values
  755. * @return float
  756. */
  757. public static function MAXA() {
  758. // Return value
  759. $returnValue = null;
  760. // Loop through arguments
  761. $aArgs = self::flattenArray(func_get_args());
  762. foreach ($aArgs as $arg) {
  763. // Is it a numeric value?
  764. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
  765. if (is_bool($arg)) {
  766. $arg = (integer) $arg;
  767. } elseif (is_string($arg)) {
  768. $arg = 0;
  769. }
  770. if ((is_null($returnValue)) || ($arg > $returnValue)) {
  771. $returnValue = $arg;
  772. }
  773. }
  774. }
  775. // Return
  776. if(is_null($returnValue)) {
  777. return 0;
  778. }
  779. return $returnValue;
  780. } // function MAXA()
  781. /**
  782. * LARGE
  783. *
  784. * Returns the nth largest value in a data set. You can use this function to
  785. * select a value based on its relative standing.
  786. *
  787. * Excel Function:
  788. * LARGE(value1[,value2[, ...]],entry)
  789. *
  790. * @access public
  791. * @category Statistical Functions
  792. * @param mixed $arg,... Data values
  793. * @param int $entry Position (ordered from the largest) in the array or range of data to return
  794. * @return float
  795. *
  796. */
  797. public static function LARGE() {
  798. $aArgs = self::flattenArray(func_get_args());
  799. // Calculate
  800. $n = floor(array_pop($aArgs));
  801. if ((is_numeric($n)) && (!is_string($n))) {
  802. $mArgs = array();
  803. foreach ($aArgs as $arg) {
  804. // Is it a numeric value?
  805. if ((is_numeric($arg)) && (!is_string($arg))) {
  806. $mArgs[] = $arg;
  807. }
  808. }
  809. $count = self::COUNT($mArgs);
  810. $n = floor(--$n);
  811. if (($n < 0) || ($n >= $count) || ($count == 0)) {
  812. return self::$_errorCodes['num'];
  813. }
  814. rsort($mArgs);
  815. return $mArgs[$n];
  816. }
  817. return self::$_errorCodes['value'];
  818. } // function LARGE()
  819. /**
  820. * PERCENTILE
  821. *
  822. * Returns the nth percentile of values in a range..
  823. *
  824. * Excel Function:
  825. * PERCENTILE(value1[,value2[, ...]],entry)
  826. *
  827. * @access public
  828. * @category Statistical Functions
  829. * @param mixed $arg,... Data values
  830. * @param float $entry Percentile value in the range 0..1, inclusive.
  831. * @return float
  832. */
  833. public static function PERCENTILE() {
  834. $aArgs = self::flattenArray(func_get_args());
  835. // Calculate
  836. $entry = array_pop($aArgs);
  837. if ((is_numeric($entry)) && (!is_string($entry))) {
  838. if (($entry < 0) || ($entry > 1)) {
  839. return self::$_errorCodes['num'];
  840. }
  841. $mArgs = array();
  842. foreach ($aArgs as $arg) {
  843. // Is it a numeric value?
  844. if ((is_numeric($arg)) && (!is_string($arg))) {
  845. $mArgs[] = $arg;
  846. }
  847. }
  848. $mValueCount = count($mArgs);
  849. if ($mValueCount > 0) {
  850. sort($mArgs);
  851. $count = self::COUNT($mArgs);
  852. $index = $entry * ($count-1);
  853. $iBase = floor($index);
  854. if ($index == $iBase) {
  855. return $mArgs[$index];
  856. } else {
  857. $iNext = $iBase + 1;
  858. $iProportion = $index - $iBase;
  859. return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion) ;
  860. }
  861. }
  862. }
  863. return self::$_errorCodes['value'];
  864. } // function PERCENTILE()
  865. /**
  866. * QUARTILE
  867. *
  868. * Returns the quartile of a data set.
  869. *
  870. * Excel Function:
  871. * QUARTILE(value1[,value2[, ...]],entry)
  872. *
  873. * @access public
  874. * @category Statistical Functions
  875. * @param mixed $arg,... Data values
  876. * @param int $entry Quartile value in the range 1..3, inclusive.
  877. * @return float
  878. */
  879. public static function QUARTILE() {
  880. $aArgs = self::flattenArray(func_get_args());
  881. // Calculate
  882. $entry = floor(array_pop($aArgs));
  883. if ((is_numeric($entry)) && (!is_string($entry))) {
  884. $entry /= 4;
  885. if (($entry < 0) || ($entry > 1)) {
  886. return self::$_errorCodes['num'];
  887. }
  888. return self::PERCENTILE($aArgs,$entry);
  889. }
  890. return self::$_errorCodes['value'];
  891. } // function QUARTILE()
  892. /**
  893. * COUNT
  894. *
  895. * Counts the number of cells that contain numbers within the list of arguments
  896. *
  897. * Excel Function:
  898. * COUNT(value1[,value2[, ...]])
  899. *
  900. * @access public
  901. * @category Statistical Functions
  902. * @param mixed $arg,... Data values
  903. * @return int
  904. */
  905. public static function COUNT() {
  906. // Return value
  907. $returnValue = 0;
  908. // Loop trough arguments
  909. $aArgs = self::flattenArray(func_get_args());
  910. foreach ($aArgs as $arg) {
  911. if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  912. $arg = (int) $arg;
  913. }
  914. // Is it a numeric value?
  915. if ((is_numeric($arg)) && (!is_string($arg))) {
  916. ++$returnValue;
  917. }
  918. }
  919. // Return
  920. return $returnValue;
  921. } // function COUNT()
  922. /**
  923. * COUNTBLANK
  924. *
  925. * Counts the number of empty cells within the list of arguments
  926. *
  927. * Excel Function:
  928. * COUNTBLANK(value1[,value2[, ...]])
  929. *
  930. * @access public
  931. * @category Statistical Functions
  932. * @param mixed $arg,... Data values
  933. * @return int
  934. */
  935. public static function COUNTBLANK() {
  936. // Return value
  937. $returnValue = 0;
  938. // Loop trough arguments
  939. $aArgs = self::flattenArray(func_get_args());
  940. foreach ($aArgs as $arg) {
  941. // Is it a blank cell?
  942. if ((is_null($arg)) || ((is_string($arg)) && ($arg == ''))) {
  943. ++$returnValue;
  944. }
  945. }
  946. // Return
  947. return $returnValue;
  948. } // function COUNTBLANK()
  949. /**
  950. * COUNTA
  951. *
  952. * Counts the number of cells that are not empty within the list of arguments
  953. *
  954. * Excel Function:
  955. * COUNTA(value1[,value2[, ...]])
  956. *
  957. * @access public
  958. * @category Statistical Functions
  959. * @param mixed $arg,... Data values
  960. * @return int
  961. */
  962. public static function COUNTA() {
  963. // Return value
  964. $returnValue = 0;
  965. // Loop through arguments
  966. $aArgs = self::flattenArray(func_get_args());
  967. foreach ($aArgs as $arg) {
  968. // Is it a numeric, boolean or string value?
  969. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
  970. ++$returnValue;
  971. }
  972. }
  973. // Return
  974. return $returnValue;
  975. } // function COUNTA()
  976. /**
  977. * COUNTIF
  978. *
  979. * Counts the number of cells that contain numbers within the list of arguments
  980. *
  981. * Excel Function:
  982. * COUNTIF(value1[,value2[, ...]],condition)
  983. *
  984. * @access public
  985. * @category Statistical Functions
  986. * @param mixed $arg,... Data values
  987. * @param string $condition The criteria that defines which cells will be counted.
  988. * @return int
  989. */
  990. public static function COUNTIF($aArgs,$condition) {
  991. // Return value
  992. $returnValue = 0;
  993. $aArgs = self::flattenArray($aArgs);
  994. if (!in_array($condition{0},array('>', '<', '='))) {
  995. if (!is_numeric($condition)) { $condition = PHPExcel_Calculation::_wrapResult(strtoupper($condition)); }
  996. $condition = '='.$condition;
  997. }
  998. // Loop through arguments
  999. foreach ($aArgs as $arg) {
  1000. if (!is_numeric($arg)) { $arg = PHPExcel_Calculation::_wrapResult(strtoupper($arg)); }
  1001. $testCondition = '='.$arg.$condition;
  1002. if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
  1003. // Is it a value within our criteria
  1004. ++$returnValue;
  1005. }
  1006. }
  1007. // Return
  1008. return $returnValue;
  1009. } // function COUNTIF()
  1010. /**
  1011. * SUMIF
  1012. *
  1013. * Counts the number of cells that contain numbers within the list of arguments
  1014. *
  1015. * Excel Function:
  1016. * SUMIF(value1[,value2[, ...]],condition)
  1017. *
  1018. * @access public
  1019. * @category Mathematical and Trigonometric Functions
  1020. * @param mixed $arg,... Data values
  1021. * @param string $condition The criteria that defines which cells will be summed.
  1022. * @return float
  1023. */
  1024. public static function SUMIF($aArgs,$condition,$sumArgs = array()) {
  1025. // Return value
  1026. $returnValue = 0;
  1027. $aArgs = self::flattenArray($aArgs);
  1028. $sumArgs = self::flattenArray($sumArgs);
  1029. if (count($sumArgs) == 0) {
  1030. $sumArgs = $aArgs;
  1031. }
  1032. if (!in_array($condition{0},array('>', '<', '='))) {
  1033. if (!is_numeric($condition)) { $condition = PHPExcel_Calculation::_wrapResult(strtoupper($condition)); }
  1034. $condition = '='.$condition;
  1035. }
  1036. // Loop through arguments
  1037. foreach ($aArgs as $key => $arg) {
  1038. if (!is_numeric($arg)) { $arg = PHPExcel_Calculation::_wrapResult(strtoupper($arg)); }
  1039. $testCondition = '='.$arg.$condition;
  1040. if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
  1041. // Is it a value within our criteria
  1042. $returnValue += $sumArgs[$key];
  1043. }
  1044. }
  1045. // Return
  1046. return $returnValue;
  1047. } // function SUMIF()
  1048. /**
  1049. * AVERAGE
  1050. *
  1051. * Returns the average (arithmetic mean) of the arguments
  1052. *
  1053. * Excel Function:
  1054. * AVERAGE(value1[,value2[, ...]])
  1055. *
  1056. * @access public
  1057. * @category Statistical Functions
  1058. * @param mixed $arg,... Data values
  1059. * @return float
  1060. */
  1061. public static function AVERAGE() {
  1062. // Return value
  1063. $returnValue = 0;
  1064. // Loop through arguments
  1065. $aArgs = self::flattenArray(func_get_args());
  1066. $aCount = 0;
  1067. foreach ($aArgs as $arg) {
  1068. if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  1069. $arg = (integer) $arg;
  1070. }
  1071. // Is it a numeric value?
  1072. if ((is_numeric($arg)) && (!is_string($arg))) {
  1073. if (is_null($returnValue)) {
  1074. $returnValue = $arg;
  1075. } else {
  1076. $returnValue += $arg;
  1077. }
  1078. ++$aCount;
  1079. }
  1080. }
  1081. // Return
  1082. if ($aCount > 0) {
  1083. return $returnValue / $aCount;
  1084. } else {
  1085. return self::$_errorCodes['divisionbyzero'];
  1086. }
  1087. } // function AVERAGE()
  1088. /**
  1089. * AVERAGEA
  1090. *
  1091. * Returns the average of its arguments, including numbers, text, and logical values
  1092. *
  1093. * Excel Function:
  1094. * AVERAGEA(value1[,value2[, ...]])
  1095. *
  1096. * @access public
  1097. * @category Statistical Functions
  1098. * @param mixed $arg,... Data values
  1099. * @return float
  1100. */
  1101. public static function AVERAGEA() {
  1102. // Return value
  1103. $returnValue = null;
  1104. // Loop through arguments
  1105. $aArgs = self::flattenArray(func_get_args());
  1106. $aCount = 0;
  1107. foreach ($aArgs as $arg) {
  1108. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
  1109. if (is_bool($arg)) {
  1110. $arg = (integer) $arg;
  1111. } elseif (is_string($arg)) {
  1112. $arg = 0;
  1113. }
  1114. if (is_null($returnValue)) {
  1115. $returnValue = $arg;
  1116. } else {
  1117. $returnValue += $arg;
  1118. }
  1119. ++$aCount;
  1120. }
  1121. }
  1122. // Return
  1123. if ($aCount > 0) {
  1124. return $returnValue / $aCount;
  1125. } else {
  1126. return self::$_errorCodes['divisionbyzero'];
  1127. }
  1128. } // function AVERAGEA()
  1129. /**
  1130. * MEDIAN
  1131. *
  1132. * Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
  1133. *
  1134. * Excel Function:
  1135. * MEDIAN(value1[,value2[, ...]])
  1136. *
  1137. * @access public
  1138. * @category Statistical Functions
  1139. * @param mixed $arg,... Data values
  1140. * @return float
  1141. */
  1142. public static function MEDIAN() {
  1143. // Return value
  1144. $returnValue = self::$_errorCodes['num'];
  1145. $mArgs = array();
  1146. // Loop through arguments
  1147. $aArgs = self::flattenArray(func_get_args());
  1148. foreach ($aArgs as $arg) {
  1149. // Is it a numeric value?
  1150. if ((is_numeric($arg)) && (!is_string($arg))) {
  1151. $mArgs[] = $arg;
  1152. }
  1153. }
  1154. $mValueCount = count($mArgs);
  1155. if ($mValueCount > 0) {
  1156. sort($mArgs,SORT_NUMERIC);
  1157. $mValueCount = $mValueCount / 2;
  1158. if ($mValueCount == floor($mValueCount)) {
  1159. $returnValue = ($mArgs[$mValueCount--] + $mArgs[$mValueCount]) / 2;
  1160. } else {
  1161. $mValueCount == floor($mValueCount);
  1162. $returnValue = $mArgs[$mValueCount];
  1163. }
  1164. }
  1165. // Return
  1166. return $returnValue;
  1167. } // function MEDIAN()
  1168. //
  1169. // Special variant of array_count_values that isn't limited to strings and integers,
  1170. // but can work with floating point numbers as values
  1171. //
  1172. private static function _modeCalc($data) {
  1173. $frequencyArray = array();
  1174. foreach($data as $datum) {
  1175. $found = False;
  1176. foreach($frequencyArray as $key => $value) {
  1177. if ((string) $value['value'] == (string) $datum) {
  1178. ++$frequencyArray[$key]['frequency'];
  1179. $found = True;
  1180. break;
  1181. }
  1182. }
  1183. if (!$found) {
  1184. $frequencyArray[] = array('value' => $datum,
  1185. 'frequency' => 1 );
  1186. }
  1187. }
  1188. foreach($frequencyArray as $key => $value) {
  1189. $frequencyList[$key] = $value['frequency'];
  1190. $valueList[$key] = $value['value'];
  1191. }
  1192. array_multisort($frequencyList, SORT_DESC, $valueList, SORT_ASC, SORT_NUMERIC, $frequencyArray);
  1193. if ($frequencyArray[0]['frequency'] == 1) {
  1194. return self::NA();
  1195. }
  1196. return $frequencyArray[0]['value'];
  1197. } // function _modeCalc()
  1198. /**
  1199. * MODE
  1200. *
  1201. * Returns the most frequently occurring, or repetitive, value in an array or range of data
  1202. *
  1203. * Excel Function:
  1204. * MODE(value1[,value2[, ...]])
  1205. *
  1206. * @access public
  1207. * @category Statistical Functions
  1208. * @param mixed $arg,... Data values
  1209. * @return float
  1210. */
  1211. public static function MODE() {
  1212. // Return value
  1213. $returnValue = self::NA();
  1214. // Loop through arguments
  1215. $aArgs = self::flattenArray(func_get_args());
  1216. $mArgs = array();
  1217. foreach ($aArgs as $arg) {
  1218. // Is it a numeric value?
  1219. if ((is_numeric($arg)) && (!is_string($arg))) {
  1220. $mArgs[] = $arg;
  1221. }
  1222. }
  1223. if (count($mArgs) > 0) {
  1224. return self::_modeCalc($mArgs);
  1225. }
  1226. // Return
  1227. return $returnValue;
  1228. } // function MODE()
  1229. /**
  1230. * DEVSQ
  1231. *
  1232. * Returns the sum of squares of deviations of data points from their sample mean.
  1233. *
  1234. * Excel Function:
  1235. * DEVSQ(value1[,value2[, ...]])
  1236. *
  1237. * @access public
  1238. * @category Statistical Functions
  1239. * @param mixed $arg,... Data values
  1240. * @return float
  1241. */
  1242. public static function DEVSQ() {
  1243. // Return value
  1244. $returnValue = null;
  1245. $aMean = self::AVERAGE(func_get_args());
  1246. if (!is_null($aMean)) {
  1247. $aArgs = self::flattenArray(func_get_args());
  1248. $aCount = -1;
  1249. foreach ($aArgs as $arg) {
  1250. // Is it a numeric value?
  1251. if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  1252. $arg = (int) $arg;
  1253. }
  1254. if ((is_numeric($arg)) && (!is_string($arg))) {
  1255. if (is_null($returnValue)) {
  1256. $returnValue = pow(($arg - $aMean),2);
  1257. } else {
  1258. $returnValue += pow(($arg - $aMean),2);
  1259. }
  1260. ++$aCount;
  1261. }
  1262. }
  1263. // Return
  1264. if (is_null($returnValue)) {
  1265. return self::$_errorCodes['num'];
  1266. } else {
  1267. return $returnValue;
  1268. }
  1269. }
  1270. return self::NA();
  1271. } // function DEVSQ()
  1272. /**
  1273. * AVEDEV
  1274. *
  1275. * Returns the average of the absolute deviations of data points from their mean.
  1276. * AVEDEV is a measure of the variability in a data set.
  1277. *
  1278. * Excel Function:
  1279. * AVEDEV(value1[,value2[, ...]])
  1280. *
  1281. * @access public
  1282. * @category Statistical Functions
  1283. * @param mixed $arg,... Data values
  1284. * @return float
  1285. */
  1286. public static function AVEDEV() {
  1287. $aArgs = self::flattenArray(func_get_args());
  1288. // Return value
  1289. $returnValue = null;
  1290. $aMean = self::AVERAGE($aArgs);
  1291. if ($aMean != self::$_errorCodes['divisionbyzero']) {
  1292. $aCount = 0;
  1293. foreach ($aArgs as $arg) {
  1294. if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  1295. $arg = (integer) $arg;
  1296. }
  1297. // Is it a numeric value?
  1298. if ((is_numeric($arg)) && (!is_string($arg))) {
  1299. if (is_null($returnValue)) {
  1300. $returnValue = abs($arg - $aMean);
  1301. } else {
  1302. $returnValue += abs($arg - $aMean);
  1303. }
  1304. ++$aCount;
  1305. }
  1306. }
  1307. // Return
  1308. return $returnValue / $aCount ;
  1309. }
  1310. return self::$_errorCodes['num'];
  1311. } // function AVEDEV()
  1312. /**
  1313. * GEOMEAN
  1314. *
  1315. * Returns the geometric mean of an array or range of positive data. For example, you
  1316. * can use GEOMEAN to calculate average growth rate given compound interest with
  1317. * variable rates.
  1318. *
  1319. * Excel Function:
  1320. * GEOMEAN(value1[,value2[, ...]])
  1321. *
  1322. * @access public
  1323. * @category Statistical Functions
  1324. * @param mixed $arg,... Data values
  1325. * @return float
  1326. */
  1327. public static function GEOMEAN() {
  1328. $aMean = self::PRODUCT(func_get_args());
  1329. if (is_numeric($aMean) && ($aMean > 0)) {
  1330. $aArgs = self::flattenArray(func_get_args());
  1331. $aCount = self::COUNT($aArgs) ;
  1332. if (self::MIN($aArgs) > 0) {
  1333. return pow($aMean, (1 / $aCount));
  1334. }
  1335. }
  1336. return self::$_errorCodes['num'];
  1337. } // GEOMEAN()
  1338. /**
  1339. * HARMEAN
  1340. *
  1341. * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
  1342. * arithmetic mean of reciprocals.
  1343. *
  1344. * Excel Function:
  1345. * HARMEAN(value1[,value2[, ...]])
  1346. *
  1347. * @access public
  1348. * @category Statistical Functions
  1349. * @param mixed $arg,... Data values
  1350. * @return float
  1351. */
  1352. public static function HARMEAN() {
  1353. // Return value
  1354. $returnValue = self::NA();
  1355. // Loop through arguments
  1356. $aArgs = self::flattenArray(func_get_args());
  1357. if (self::MIN($aArgs) < 0) {
  1358. return self::$_errorCodes['num'];
  1359. }
  1360. $aCount = 0;
  1361. foreach ($aArgs as $arg) {
  1362. // Is it a numeric value?
  1363. if ((is_numeric($arg)) && (!is_string($arg))) {
  1364. if ($arg <= 0) {
  1365. return self::$_errorCodes['num'];
  1366. }
  1367. if (is_null($returnValue)) {
  1368. $returnValue = (1 / $arg);
  1369. } else {
  1370. $returnValue += (1 / $arg);
  1371. }
  1372. ++$aCount;
  1373. }
  1374. }
  1375. // Return
  1376. if ($aCount > 0) {
  1377. return 1 / ($returnValue / $aCount);
  1378. } else {
  1379. return $returnValue;
  1380. }
  1381. } // function HARMEAN()
  1382. /**
  1383. * TRIMMEAN
  1384. *
  1385. * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
  1386. * taken by excluding a percentage of data points from the top and bottom tails
  1387. * of a data set.
  1388. *
  1389. * Excel Function:
  1390. * TRIMEAN(value1[,value2[, ...]],$discard)
  1391. *
  1392. * @access public
  1393. * @category Statistical Functions
  1394. * @param mixed $arg,... Data values
  1395. * @param float $discard Percentage to discard
  1396. * @return float
  1397. */
  1398. public static function TRIMMEAN() {
  1399. $aArgs = self::flattenArray(func_get_args());
  1400. // Calculate
  1401. $percent = array_pop($aArgs);
  1402. if ((is_numeric($percent)) && (!is_string($percent))) {
  1403. if (($percent < 0) || ($percent > 1)) {
  1404. return self::$_errorCodes['num'];
  1405. }
  1406. $mArgs = array();
  1407. foreach ($aArgs as $arg) {
  1408. // Is it a numeric value?
  1409. if ((is_numeric($arg)) && (!is_string($arg))) {
  1410. $mArgs[] = $arg;
  1411. }
  1412. }
  1413. $discard = floor(self::COUNT($mArgs) * $percent / 2);
  1414. sort($mArgs);
  1415. for ($i=0; $i < $discard; ++$i) {
  1416. array_pop($mArgs);
  1417. array_shift($mArgs);
  1418. }
  1419. return self::AVERAGE($mArgs);
  1420. }
  1421. return self::$_errorCodes['value'];
  1422. } // function TRIMMEAN()
  1423. /**
  1424. * STDEV
  1425. *
  1426. * Estimates standard deviation based on a sample. The standard deviation is a measure of how
  1427. * widely values are dispersed from the average value (the mean).
  1428. *
  1429. * Excel Function:
  1430. * STDEV(value1[,value2[, ...]])
  1431. *
  1432. * @access public
  1433. * @category Statistical Functions
  1434. * @param mixed $arg,... Data values
  1435. * @return float
  1436. */
  1437. public static function STDEV() {
  1438. // Return value
  1439. $returnValue = null;
  1440. $aMean = self::AVERAGE(func_get_args());
  1441. if (!is_null($aMean)) {
  1442. $aArgs = self::flattenArray(func_get_args());
  1443. $aCount = -1;
  1444. foreach ($aArgs as $arg) {
  1445. // Is it a numeric value?
  1446. if ((is_numeric($arg)) && (!is_string($arg))) {
  1447. if (is_null($returnValue)) {
  1448. $returnValue = pow(($arg - $aMean),2);
  1449. } else {
  1450. $returnValue += pow(($arg - $aMean),2);
  1451. }
  1452. ++$aCount;
  1453. }
  1454. }
  1455. // Return
  1456. if (($aCount > 0) && ($returnValue > 0)) {
  1457. return sqrt($returnValue / $aCount);
  1458. }
  1459. }
  1460. return self::$_errorCodes['divisionbyzero'];
  1461. } // function STDEV()
  1462. /**
  1463. * STDEVA
  1464. *
  1465. * Estimates standard deviation based on a sample, including numbers, text, and logical values
  1466. *
  1467. * Excel Function:
  1468. * STDEVA(value1[,value2[, ...]])
  1469. *
  1470. * @access public
  1471. * @category Statistical Functions
  1472. * @param mixed $arg,... Data values
  1473. * @return float
  1474. */
  1475. public static function STDEVA() {
  1476. // Return value
  1477. $returnValue = null;
  1478. $aMean = self::AVERAGEA(func_get_args());
  1479. if (!is_null($aMean)) {
  1480. $aArgs = self::flattenArray(func_get_args());
  1481. $aCount = -1;
  1482. foreach ($aArgs as $arg) {
  1483. // Is it a numeric value?
  1484. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
  1485. if (is_bool($arg)) {
  1486. $arg = (integer) $arg;
  1487. } elseif (is_string($arg)) {
  1488. $arg = 0;
  1489. }
  1490. if (is_null($returnValue)) {
  1491. $returnValue = pow(($arg - $aMean),2);
  1492. } else {
  1493. $returnValue += pow(($arg - $aMean),2);
  1494. }
  1495. ++$aCount;
  1496. }
  1497. }
  1498. // Return
  1499. if (($aCount > 0) && ($returnValue > 0)) {
  1500. return sqrt($returnValue / $aCount);
  1501. }
  1502. }
  1503. return self::$_errorCodes['divisionbyzero'];
  1504. } // function STDEVA()
  1505. /**
  1506. * STDEVP
  1507. *
  1508. * Calculates standard deviation based on the entire population
  1509. *
  1510. * Excel Function:
  1511. * STDEVP(value1[,value2[, ...]])
  1512. *
  1513. * @access public
  1514. * @category Statistical Functions
  1515. * @param mixed $arg,... Data values
  1516. * @return float
  1517. */
  1518. public static function STDEVP() {
  1519. // Return value
  1520. $returnValue = null;
  1521. $aMean = self::AVERAGE(func_get_args());
  1522. if (!is_null($aMean)) {
  1523. $aArgs = self::flattenArray(func_get_args());
  1524. $aCount = 0;
  1525. foreach ($aArgs as $arg) {
  1526. // Is it a numeric value?
  1527. if ((is_numeric($arg)) && (!is_string($arg))) {
  1528. if (is_null($returnValue)) {
  1529. $returnValue = pow(($arg - $aMean),2);
  1530. } else {
  1531. $returnValue += pow(($arg - $aMean),2);
  1532. }
  1533. ++$aCount;
  1534. }
  1535. }
  1536. // Return
  1537. if (($aCount > 0) && ($returnValue > 0)) {
  1538. return sqrt($returnValue / $aCount);
  1539. }
  1540. }
  1541. return self::$_errorCodes['divisionbyzero'];
  1542. } // function STDEVP()
  1543. /**
  1544. * STDEVPA
  1545. *
  1546. * Calculates standard deviation based on the entire population, including numbers, text, and logical values
  1547. *
  1548. * Excel Function:
  1549. * STDEVPA(value1[,value2[, ...]])
  1550. *
  1551. * @access public
  1552. * @category Statistical Functions
  1553. * @param mixed $arg,... Data values
  1554. * @return float
  1555. */
  1556. public static function STDEVPA() {
  1557. // Return value
  1558. $returnValue = null;
  1559. $aMean = self::AVERAGEA(func_get_args());
  1560. if (!is_null($aMean)) {
  1561. $aArgs = self::flattenArray(func_get_args());
  1562. $aCount = 0;
  1563. foreach ($aArgs as $arg) {
  1564. // Is it a numeric value?
  1565. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
  1566. if (is_bool($arg)) {
  1567. $arg = (integer) $arg;
  1568. } elseif (is_string($arg)) {
  1569. $arg = 0;
  1570. }
  1571. if (is_null($returnValue)) {
  1572. $returnValue = pow(($arg - $aMean),2);
  1573. } else {
  1574. $returnValue += pow(($arg - $aMean),2);
  1575. }
  1576. ++$aCount;
  1577. }
  1578. }
  1579. // Return
  1580. if (($aCount > 0) && ($returnValue > 0)) {
  1581. return sqrt($returnValue / $aCount);
  1582. }
  1583. }
  1584. return self::$_errorCodes['divisionbyzero'];
  1585. } // function STDEVPA()
  1586. /**
  1587. * VARFunc
  1588. *
  1589. * Estimates variance based on a sample.
  1590. *
  1591. * Excel Function:
  1592. * VAR(value1[,value2[, ...]])
  1593. *
  1594. * @access public
  1595. * @category Statistical Functions
  1596. * @param mixed $arg,... Data values
  1597. * @return float
  1598. */
  1599. public static function VARFunc() {
  1600. // Return value
  1601. $returnValue = self::$_errorCodes['divisionbyzero'];
  1602. $summerA = $summerB = 0;
  1603. // Loop through arguments
  1604. $aArgs = self::flattenArray(func_get_args());
  1605. $aCount = 0;
  1606. foreach ($aArgs as $arg) {
  1607. // Is it a numeric value?
  1608. if ((is_numeric($arg)) && (!is_string($arg))) {
  1609. $summerA += ($arg * $arg);
  1610. $summerB += $arg;
  1611. ++$aCount;
  1612. }
  1613. }
  1614. // Return
  1615. if ($aCount > 1) {
  1616. $summerA = $summerA * $aCount;
  1617. $summerB = ($summerB * $summerB);
  1618. $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
  1619. }
  1620. return $returnValue;
  1621. } // function VARFunc()
  1622. /**
  1623. * VARA
  1624. *
  1625. * Estimates variance based on a sample, including numbers, text, and logical values
  1626. *
  1627. * Excel Function:
  1628. * VARA(value1[,value2[, ...]])
  1629. *
  1630. * @access public
  1631. * @category Statistical Functions
  1632. * @param mixed $arg,... Data values
  1633. * @return float
  1634. */
  1635. public static function VARA() {
  1636. // Return value
  1637. $returnValue = self::$_errorCodes['divisionbyzero'];
  1638. $summerA = $summerB = 0;
  1639. // Loop through arguments
  1640. $aArgs = self::flattenArray(func_get_args());
  1641. $aCount = 0;
  1642. foreach ($aArgs as $arg) {
  1643. // Is it a numeric value?
  1644. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
  1645. if (is_bool($arg)) {
  1646. $arg = (integer) $arg;
  1647. } elseif (is_string($arg)) {
  1648. $arg = 0;
  1649. }
  1650. $summerA += ($arg * $arg);
  1651. $summerB += $arg;
  1652. ++$aCount;
  1653. }
  1654. }
  1655. // Return
  1656. if ($aCount > 1) {
  1657. $summerA = $summerA * $aCount;
  1658. $summerB = ($summerB * $summerB);
  1659. $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
  1660. }
  1661. return $returnValue;
  1662. } // function VARA()
  1663. /**
  1664. * VARP
  1665. *
  1666. * Calculates variance based on the entire population
  1667. *
  1668. * Excel Function:
  1669. * VARP(value1[,value2[, ...]])
  1670. *
  1671. * @access public
  1672. * @category Statistical Functions
  1673. * @param mixed $arg,... Data values
  1674. * @return float
  1675. */
  1676. public static function VARP() {
  1677. // Return value
  1678. $returnValue = self::$_errorCodes['divisionbyzero'];
  1679. $summerA = $summerB = 0;
  1680. // Loop through arguments
  1681. $aArgs = self::flattenArray(func_get_args());
  1682. $aCount = 0;
  1683. foreach ($aArgs as $arg) {
  1684. // Is it a numeric value?
  1685. if ((is_numeric($arg)) && (!is_string($arg))) {
  1686. $summerA += ($arg * $arg);
  1687. $summerB += $arg;
  1688. ++$aCount;
  1689. }
  1690. }
  1691. // Return
  1692. if ($aCount > 0) {
  1693. $summerA = $summerA * $aCount;
  1694. $summerB = ($summerB * $summerB);
  1695. $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
  1696. }
  1697. return $returnValue;
  1698. } // function VARP()
  1699. /**
  1700. * VARPA
  1701. *
  1702. * Calculates variance based on the entire population, including numbers, text, and logical values
  1703. *
  1704. * Excel Function:
  1705. * VARPA(value1[,value2[, ...]])
  1706. *
  1707. * @access public
  1708. * @category Statistical Functions
  1709. * @param mixed $arg,... Data values
  1710. * @return float
  1711. */
  1712. public static function VARPA() {
  1713. // Return value
  1714. $returnValue = self::$_errorCodes['divisionbyzero'];
  1715. $summerA = $summerB = 0;
  1716. // Loop through arguments
  1717. $aArgs = self::flattenArray(func_get_args());
  1718. $aCount = 0;
  1719. foreach ($aArgs as $arg) {
  1720. // Is it a numeric value?
  1721. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
  1722. if (is_bool($arg)) {
  1723. $arg = (integer) $arg;
  1724. } elseif (is_string($arg)) {
  1725. $arg = 0;
  1726. }
  1727. $summerA += ($arg * $arg);
  1728. $summerB += $arg;
  1729. ++$aCount;
  1730. }
  1731. }
  1732. // Return
  1733. if ($aCount > 0) {
  1734. $summerA = $summerA * $aCount;
  1735. $summerB = ($summerB * $summerB);
  1736. $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
  1737. }
  1738. return $returnValue;
  1739. } // function VARPA()
  1740. /**
  1741. * RANK
  1742. *
  1743. * Returns the rank of a number in a list of numbers.
  1744. *
  1745. * @param number The number whose rank you want to find.
  1746. * @param array of number An array of, or a reference to, a list of numbers.
  1747. * @param mixed Order to sort the values in the value set
  1748. * @return float
  1749. */
  1750. public static function RANK($value,$valueSet,$order=0) {
  1751. $value = self::flattenSingleValue($value);
  1752. $valueSet = self::flattenArray($valueSet);
  1753. $order = self::flattenSingleValue($order);
  1754. foreach($valueSet as $key => $valueEntry) {
  1755. if (!is_numeric($valueEntry)) {
  1756. unset($valueSet[$key]);
  1757. }
  1758. }
  1759. if ($order == 0) {
  1760. rsort($valueSet,SORT_NUMERIC);
  1761. } else {
  1762. sort($valueSet,SORT_NUMERIC);
  1763. }
  1764. $pos = array_search($value,$valueSet);
  1765. if ($pos === False) {
  1766. return self::$_errorCodes['na'];
  1767. }
  1768. return ++$pos;
  1769. } // function RANK()
  1770. /**
  1771. * PERCENTRANK
  1772. *
  1773. * Returns the rank of a value in a data set as a percentage of the data set.
  1774. *
  1775. * @param array of number An array of, or a reference to, a list of numbers.
  1776. * @param number The number whose rank you want to find.
  1777. * @param number The number of significant digits for the returned percentage value.
  1778. * @return float
  1779. */
  1780. public static function PERCENTRANK($valueSet,$value,$significance=3) {
  1781. $valueSet = self::flattenArray($valueSet);
  1782. $value = self::flattenSingleValue($value);
  1783. $significance = self::flattenSingleValue($significance);
  1784. foreach($valueSet as $key => $valueEntry) {
  1785. if (!is_numeric($valueEntry)) {
  1786. unset($valueSet[$key]);
  1787. }
  1788. }
  1789. sort($valueSet,SORT_NUMERIC);
  1790. $valueCount = count($valueSet);
  1791. if ($valueCount == 0) {
  1792. return self::$_errorCodes['num'];
  1793. }
  1794. $valueAdjustor = $valueCount - 1;
  1795. if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
  1796. return self::$_errorCodes['na'];
  1797. }
  1798. $pos = array_search($value,$valueSet);
  1799. if ($pos === False) {
  1800. $pos = 0;
  1801. $testValue = $valueSet[0];
  1802. while ($testValue < $value) {
  1803. $testValue = $valueSet[++$pos];
  1804. }
  1805. --$pos;
  1806. $pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
  1807. }
  1808. return round($pos / $valueAdjustor,$significance);
  1809. } // function PERCENTRANK()
  1810. private static function _checkTrendArray($values) {
  1811. foreach($values as $key => $value) {
  1812. if ((is_bool($value)) || ((is_string($value)) && (trim($value) == ''))) {
  1813. unset($values[$key]);
  1814. } elseif (is_string($value)) {
  1815. if (is_numeric($value)) {
  1816. $values[$key] = (float) $value;
  1817. } else {
  1818. unset($values[$key]);
  1819. }
  1820. }
  1821. }
  1822. return $values;
  1823. } // function _checkTrendArray()
  1824. /**
  1825. * INTERCEPT
  1826. *
  1827. * Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
  1828. *
  1829. * @param array of mixed Data Series Y
  1830. * @param array of mixed Data Series X
  1831. * @return float
  1832. */
  1833. public static function INTERCEPT($yValues,$xValues) {
  1834. $yValues = self::flattenArray($yValues);
  1835. $xValues = self::flattenArray($xValues);
  1836. $yValues = self::_checkTrendArray($yValues);
  1837. $yValueCount = count($yValues);
  1838. $xValues = self::_checkTrendArray($xValues);
  1839. $xValueCount = count($xValues);
  1840. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  1841. return self::$_errorCodes['na'];
  1842. } elseif ($yValueCount == 1) {
  1843. return self::$_errorCodes['divisionbyzero'];
  1844. }
  1845. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
  1846. return $bestFitLinear->getIntersect();
  1847. } // function INTERCEPT()
  1848. /**
  1849. * RSQ
  1850. *
  1851. * Returns the square of the Pearson product moment correlation coefficient through data points in known_y's and known_x's.
  1852. *
  1853. * @param array of mixed Data Series Y
  1854. * @param array of mixed Data Series X
  1855. * @return float
  1856. */
  1857. public static function RSQ($yValues,$xValues) {
  1858. $yValues = self::flattenArray($yValues);
  1859. $xValues = self::flattenArray($xValues);
  1860. $yValues = self::_checkTrendArray($yValues);
  1861. $yValueCount = count($yValues);
  1862. $xValues = self::_checkTrendArray($xValues);
  1863. $xValueCount = count($xValues);
  1864. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  1865. return self::$_errorCodes['na'];
  1866. } elseif ($yValueCount == 1) {
  1867. return self::$_errorCodes['divisionbyzero'];
  1868. }
  1869. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
  1870. return $bestFitLinear->getGoodnessOfFit();
  1871. } // function RSQ()
  1872. /**
  1873. * SLOPE
  1874. *
  1875. * Returns the slope of the linear regression line through data points in known_y's and known_x's.
  1876. *
  1877. * @param array of mixed Data Series Y
  1878. * @param array of mixed Data Series X
  1879. * @return float
  1880. */
  1881. public static function SLOPE($yValues,$xValues) {
  1882. $yValues = self::flattenArray($yValues);
  1883. $xValues = self::flattenArray($xValues);
  1884. $yValues = self::_checkTrendArray($yValues);
  1885. $yValueCount = count($yValues);
  1886. $xValues = self::_checkTrendArray($xValues);
  1887. $xValueCount = count($xValues);
  1888. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  1889. return self::$_errorCodes['na'];
  1890. } elseif ($yValueCount == 1) {
  1891. return self::$_errorCodes['divisionbyzero'];
  1892. }
  1893. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
  1894. return $bestFitLinear->getSlope();
  1895. } // function SLOPE()
  1896. /**
  1897. * STEYX
  1898. *
  1899. * Returns the standard error of the predicted y-value for each x in the regression.
  1900. *
  1901. * @param array of mixed Data Series Y
  1902. * @param array of mixed Data Series X
  1903. * @return float
  1904. */
  1905. public static function STEYX($yValues,$xValues) {
  1906. $yValues = self::flattenArray($yValues);
  1907. $xValues = self::flattenArray($xValues);
  1908. $yValues = self::_checkTrendArray($yValues);
  1909. $yValueCount = count($yValues);
  1910. $xValues = self::_checkTrendArray($xValues);
  1911. $xValueCount = count($xValues);
  1912. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  1913. return self::$_errorCodes['na'];
  1914. } elseif ($yValueCount == 1) {
  1915. return self::$_errorCodes['divisionbyzero'];
  1916. }
  1917. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
  1918. return $bestFitLinear->getStdevOfResiduals();
  1919. } // function STEYX()
  1920. /**
  1921. * COVAR
  1922. *
  1923. * Returns covariance, the average of the products of deviations for each data point pair.
  1924. *
  1925. * @param array of mixed Data Series Y
  1926. * @param array of mixed Data Series X
  1927. * @return float
  1928. */
  1929. public static function COVAR($yValues,$xValues) {
  1930. $yValues = self::flattenArray($yValues);
  1931. $xValues = self::flattenArray($xValues);
  1932. $yValues = self::_checkTrendArray($yValues);
  1933. $yValueCount = count($yValues);
  1934. $xValues = self::_checkTrendArray($xValues);
  1935. $xValueCount = count($xValues);
  1936. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  1937. return self::$_errorCodes['na'];
  1938. } elseif ($yValueCount == 1) {
  1939. return self::$_errorCodes['divisionbyzero'];
  1940. }
  1941. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
  1942. return $bestFitLinear->getCovariance();
  1943. } // function COVAR()
  1944. /**
  1945. * CORREL
  1946. *
  1947. * Returns covariance, the average of the products of deviations for each data point pair.
  1948. *
  1949. * @param array of mixed Data Series Y
  1950. * @param array of mixed Data Series X
  1951. * @return float
  1952. */
  1953. public static function CORREL($yValues,$xValues) {
  1954. $yValues = self::flattenArray($yValues);
  1955. $xValues = self::flattenArray($xValues);
  1956. $yValues = self::_checkTrendArray($yValues);
  1957. $yValueCount = count($yValues);
  1958. $xValues = self::_checkTrendArray($xValues);
  1959. $xValueCount = count($xValues);
  1960. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  1961. return self::$_errorCodes['na'];
  1962. } elseif ($yValueCount == 1) {
  1963. return self::$_errorCodes['divisionbyzero'];
  1964. }
  1965. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
  1966. return $bestFitLinear->getCorrelation();
  1967. } // function CORREL()
  1968. /**
  1969. * LINEST
  1970. *
  1971. * Calculates the statistics for a line by using the "least squares" method to calculate a straight line that best fits your data,
  1972. * and then returns an array that describes the line.
  1973. *
  1974. * @param array of mixed Data Series Y
  1975. * @param array of mixed Data Series X
  1976. * @param boolean A logical value specifying whether to force the intersect to equal 0.
  1977. * @param boolean A logical value specifying whether to return additional regression statistics.
  1978. * @return array
  1979. */
  1980. public static function LINEST($yValues,$xValues,$const=True,$stats=False) {
  1981. $yValues = self::flattenArray($yValues);
  1982. $xValues = self::flattenArray($xValues);
  1983. $const = (boolean) self::flattenSingleValue($const);
  1984. $stats = (boolean) self::flattenSingleValue($stats);
  1985. $yValues = self::_checkTrendArray($yValues);
  1986. $yValueCount = count($yValues);
  1987. $xValues = self::_checkTrendArray($xValues);
  1988. $xValueCount = count($xValues);
  1989. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  1990. return self::$_errorCodes['na'];
  1991. } elseif ($yValueCount == 1) {
  1992. return self::$_errorCodes['divisionbyzero'];
  1993. }
  1994. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues,$const);
  1995. if ($stats) {
  1996. return array( array( $bestFitLinear->getSlope(),
  1997. $bestFitLinear->getSlopeSE(),
  1998. $bestFitLinear->getGoodnessOfFit(),
  1999. $bestFitLinear->getF(),
  2000. $bestFitLinear->getSSRegression(),
  2001. ),
  2002. array( $bestFitLinear->getIntersect(),
  2003. $bestFitLinear->getIntersectSE(),
  2004. $bestFitLinear->getStdevOfResiduals(),
  2005. $bestFitLinear->getDFResiduals(),
  2006. $bestFitLinear->getSSResiduals()
  2007. )
  2008. );
  2009. } else {
  2010. return array( $bestFitLinear->getSlope(),
  2011. $bestFitLinear->getIntersect()
  2012. );
  2013. }
  2014. } // function LINEST()
  2015. /**
  2016. * LOGEST
  2017. *
  2018. * Calculates an exponential curve that best fits the X and Y data series,
  2019. * and then returns an array that describes the line.
  2020. *
  2021. * @param array of mixed Data Series Y
  2022. * @param array of mixed Data Series X
  2023. * @param boolean A logical value specifying whether to force the intersect to equal 0.
  2024. * @param boolean A logical value specifying whether to return additional regression statistics.
  2025. * @return array
  2026. */
  2027. public static function LOGEST($yValues,$xValues,$const=True,$stats=False) {
  2028. $yValues = self::flattenArray($yValues);
  2029. $xValues = self::flattenArray($xValues);
  2030. $const = (boolean) self::flattenSingleValue($const);
  2031. $stats = (boolean) self::flattenSingleValue($stats);
  2032. $yValues = self::_checkTrendArray($yValues);
  2033. $yValueCount = count($yValues);
  2034. $xValues = self::_checkTrendArray($xValues);
  2035. $xValueCount = count($xValues);
  2036. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  2037. return self::$_errorCodes['na'];
  2038. } elseif ($yValueCount == 1) {
  2039. return self::$_errorCodes['divisionbyzero'];
  2040. }
  2041. $bestFitExponential = trendClass::calculate(trendClass::TREND_EXPONENTIAL,$yValues,$xValues,$const);
  2042. if ($stats) {
  2043. return array( array( $bestFitExponential->getSlope(),
  2044. $bestFitExponential->getSlopeSE(),
  2045. $bestFitExponential->getGoodnessOfFit(),
  2046. $bestFitExponential->getF(),
  2047. $bestFitExponential->getSSRegression(),
  2048. ),
  2049. array( $bestFitExponential->getIntersect(),
  2050. $bestFitExponential->getIntersectSE(),
  2051. $bestFitExponential->getStdevOfResiduals(),
  2052. $bestFitExponential->getDFResiduals(),
  2053. $bestFitExponential->getSSResiduals()
  2054. )
  2055. );
  2056. } else {
  2057. return array( $bestFitExponential->getSlope(),
  2058. $bestFitExponential->getIntersect()
  2059. );
  2060. }
  2061. } // function LOGEST()
  2062. /**
  2063. * FORECAST
  2064. *
  2065. * Calculates, or predicts, a future value by using existing values. The predicted value is a y-value for a given x-value.
  2066. *
  2067. * @param float Value of X for which we want to find Y
  2068. * @param array of mixed Data Series Y
  2069. * @param array of mixed Data Series X
  2070. * @return float
  2071. */
  2072. public static function FORECAST($xValue,$yValues,$xValues) {
  2073. $xValue = self::flattenSingleValue($xValue);
  2074. $yValues = self::flattenArray($yValues);
  2075. $xValues = self::flattenArray($xValues);
  2076. if (!is_numeric($xValue)) {
  2077. return self::$_errorCodes['value'];
  2078. }
  2079. $yValues = self::_checkTrendArray($yValues);
  2080. $yValueCount = count($yValues);
  2081. $xValues = self::_checkTrendArray($xValues);
  2082. $xValueCount = count($xValues);
  2083. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  2084. return self::$_errorCodes['na'];
  2085. } elseif ($yValueCount == 1) {
  2086. return self::$_errorCodes['divisionbyzero'];
  2087. }
  2088. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
  2089. return $bestFitLinear->getValueOfYForX($xValue);
  2090. } // function FORECAST()
  2091. /**
  2092. * TREND
  2093. *
  2094. * Returns values along a linear trend
  2095. *
  2096. * @param array of mixed Data Series Y
  2097. * @param array of mixed Data Series X
  2098. * @param array of mixed Values of X for which we want to find Y
  2099. * @param boolean A logical value specifying whether to force the intersect to equal 0.
  2100. * @return array of float
  2101. */
  2102. public static function TREND($yValues,$xValues=array(),$newValues=array(),$const=True) {
  2103. $yValues = self::flattenArray($yValues);
  2104. $xValues = self::flattenArray($xValues);
  2105. $newValues = self::flattenArray($newValues);
  2106. $const = (boolean) self::flattenSingleValue($const);
  2107. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues,$const);
  2108. if (count($newValues) == 0) {
  2109. $newValues = $bestFitLinear->getXValues();
  2110. }
  2111. $returnArray = array();
  2112. foreach($newValues as $xValue) {
  2113. $returnArray[0][] = $bestFitLinear->getValueOfYForX($xValue);
  2114. }
  2115. return $returnArray;
  2116. } // function TREND()
  2117. /**
  2118. * GROWTH
  2119. *
  2120. * Returns values along a predicted emponential trend
  2121. *
  2122. * @param array of mixed Data Series Y
  2123. * @param array of mixed Data Series X
  2124. * @param array of mixed Values of X for which we want to find Y
  2125. * @param boolean A logical value specifying whether to force the intersect to equal 0.
  2126. * @return array of float
  2127. */
  2128. public static function GROWTH($yValues,$xValues=array(),$newValues=array(),$const=True) {
  2129. $yValues = self::flattenArray($yValues);
  2130. $xValues = self::flattenArray($xValues);
  2131. $newValues = self::flattenArray($newValues);
  2132. $const = (boolean) self::flattenSingleValue($const);
  2133. $bestFitExponential = trendClass::calculate(trendClass::TREND_EXPONENTIAL,$yValues,$xValues,$const);
  2134. if (count($newValues) == 0) {
  2135. $newValues = $bestFitExponential->getXValues();
  2136. }
  2137. $returnArray = array();
  2138. foreach($newValues as $xValue) {
  2139. $returnArray[0][] = $bestFitExponential->getValueOfYForX($xValue);
  2140. }
  2141. return $returnArray;
  2142. } // function GROWTH()
  2143. private static function _romanCut($num, $n) {
  2144. return ($num - ($num % $n ) ) / $n;
  2145. } // function _romanCut()
  2146. public static function ROMAN($aValue, $style=0) {
  2147. $aValue = (integer) self::flattenSingleValue($aValue);
  2148. if ((!is_numeric($aValue)) || ($aValue < 0) || ($aValue >= 4000)) {
  2149. return self::$_errorCodes['value'];
  2150. }
  2151. if ($aValue == 0) {
  2152. return '';
  2153. }
  2154. $mill = Array('', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM');
  2155. $cent = Array('', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM');
  2156. $tens = Array('', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC');
  2157. $ones = Array('', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX');
  2158. $roman = '';
  2159. while ($aValue > 5999) {
  2160. $roman .= 'M';
  2161. $aValue -= 1000;
  2162. }
  2163. $m = self::_romanCut($aValue, 1000); $aValue %= 1000;
  2164. $c = self::_romanCut($aValue, 100); $aValue %= 100;
  2165. $t = self::_romanCut($aValue, 10); $aValue %= 10;
  2166. return $roman.$mill[$m].$cent[$c].$tens[$t].$ones[$aValue];
  2167. } // function ROMAN()
  2168. /**
  2169. * SUBTOTAL
  2170. *
  2171. * Returns a subtotal in a list or database.
  2172. *
  2173. * @param int the number 1 to 11 that specifies which function to
  2174. * use in calculating subtotals within a list.
  2175. * @param array of mixed Data Series
  2176. * @return float
  2177. */
  2178. public static function SUBTOTAL() {
  2179. $aArgs = self::flattenArray(func_get_args());
  2180. // Calculate
  2181. $subtotal = array_shift($aArgs);
  2182. if ((is_numeric($subtotal)) && (!is_string($subtotal))) {
  2183. switch($subtotal) {
  2184. case 1 :
  2185. return self::AVERAGE($aArgs);
  2186. break;
  2187. case 2 :
  2188. return self::COUNT($aArgs);
  2189. break;
  2190. case 3 :
  2191. return self::COUNTA($aArgs);
  2192. break;
  2193. case 4 :
  2194. return self::MAX($aArgs);
  2195. break;
  2196. case 5 :
  2197. return self::MIN($aArgs);
  2198. break;
  2199. case 6 :
  2200. return self::PRODUCT($aArgs);
  2201. break;
  2202. case 7 :
  2203. return self::STDEV($aArgs);
  2204. break;
  2205. case 8 :
  2206. return self::STDEVP($aArgs);
  2207. break;
  2208. case 9 :
  2209. return self::SUM($aArgs);
  2210. break;
  2211. case 10 :
  2212. return self::VARFunc($aArgs);
  2213. break;
  2214. case 11 :
  2215. return self::VARP($aArgs);
  2216. break;
  2217. }
  2218. }
  2219. return self::$_errorCodes['value'];
  2220. } // function SUBTOTAL()
  2221. /**
  2222. * SQRTPI
  2223. *
  2224. * Returns the square root of (number * pi).
  2225. *
  2226. * @param float $number Number
  2227. * @return float Square Root of Number * Pi
  2228. */
  2229. public static function SQRTPI($number) {
  2230. $number = self::flattenSingleValue($number);
  2231. if (is_numeric($number)) {
  2232. if ($number < 0) {
  2233. return self::$_errorCodes['num'];
  2234. }
  2235. return sqrt($number * pi()) ;
  2236. }
  2237. return self::$_errorCodes['value'];
  2238. } // function SQRTPI()
  2239. /**
  2240. * FACT
  2241. *
  2242. * Returns the factorial of a number.
  2243. *
  2244. * @param float $factVal Factorial Value
  2245. * @return int Factorial
  2246. */
  2247. public static function FACT($factVal) {
  2248. $factVal = self::flattenSingleValue($factVal);
  2249. if (is_numeric($factVal)) {
  2250. if ($factVal < 0) {
  2251. return self::$_errorCodes['num'];
  2252. }
  2253. $factLoop = floor($factVal);
  2254. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  2255. if ($factVal > $factLoop) {
  2256. return self::$_errorCodes['num'];
  2257. }
  2258. }
  2259. $factorial = 1;
  2260. while ($factLoop > 1) {
  2261. $factorial *= $factLoop--;
  2262. }
  2263. return $factorial ;
  2264. }
  2265. return self::$_errorCodes['value'];
  2266. } // function FACT()
  2267. /**
  2268. * FACTDOUBLE
  2269. *
  2270. * Returns the double factorial of a number.
  2271. *
  2272. * @param float $factVal Factorial Value
  2273. * @return int Double Factorial
  2274. */
  2275. public static function FACTDOUBLE($factVal) {
  2276. $factLoop = floor(self::flattenSingleValue($factVal));
  2277. if (is_numeric($factLoop)) {
  2278. if ($factVal < 0) {
  2279. return self::$_errorCodes['num'];
  2280. }
  2281. $factorial = 1;
  2282. while ($factLoop > 1) {
  2283. $factorial *= $factLoop--;
  2284. --$factLoop;
  2285. }
  2286. return $factorial ;
  2287. }
  2288. return self::$_errorCodes['value'];
  2289. } // function FACTDOUBLE()
  2290. /**
  2291. * MULTINOMIAL
  2292. *
  2293. * Returns the ratio of the factorial of a sum of values to the product of factorials.
  2294. *
  2295. * @param array of mixed Data Series
  2296. * @return float
  2297. */
  2298. public static function MULTINOMIAL() {
  2299. // Loop through arguments
  2300. $aArgs = self::flattenArray(func_get_args());
  2301. $summer = 0;
  2302. $divisor = 1;
  2303. foreach ($aArgs as $arg) {
  2304. // Is it a numeric value?
  2305. if (is_numeric($arg)) {
  2306. if ($arg < 1) {
  2307. return self::$_errorCodes['num'];
  2308. }
  2309. $summer += floor($arg);
  2310. $divisor *= self::FACT($arg);
  2311. } else {
  2312. return self::$_errorCodes['value'];
  2313. }
  2314. }
  2315. // Return
  2316. if ($summer > 0) {
  2317. $summer = self::FACT($summer);
  2318. return $summer / $divisor;
  2319. }
  2320. return 0;
  2321. } // function MULTINOMIAL()
  2322. /**
  2323. * CEILING
  2324. *
  2325. * Returns number rounded up, away from zero, to the nearest multiple of significance.
  2326. *
  2327. * @param float $number Number to round
  2328. * @param float $significance Significance
  2329. * @return float Rounded Number
  2330. */
  2331. public static function CEILING($number,$significance=null) {
  2332. $number = self::flattenSingleValue($number);
  2333. $significance = self::flattenSingleValue($significance);
  2334. if ((is_null($significance)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
  2335. $significance = $number/abs($number);
  2336. }
  2337. if ((is_numeric($number)) && (is_numeric($significance))) {
  2338. if (self::SIGN($number) == self::SIGN($significance)) {
  2339. if ($significance == 0.0) {
  2340. return 0;
  2341. }
  2342. return ceil($number / $significance) * $significance;
  2343. } else {
  2344. return self::$_errorCodes['num'];
  2345. }
  2346. }
  2347. return self::$_errorCodes['value'];
  2348. } // function CEILING()
  2349. /**
  2350. * EVEN
  2351. *
  2352. * Returns number rounded up to the nearest even integer.
  2353. *
  2354. * @param float $number Number to round
  2355. * @return int Rounded Number
  2356. */
  2357. public static function EVEN($number) {
  2358. $number = self::flattenSingleValue($number);
  2359. if (is_numeric($number)) {
  2360. $significance = 2 * self::SIGN($number);
  2361. return self::CEILING($number,$significance);
  2362. }
  2363. return self::$_errorCodes['value'];
  2364. } // function EVEN()
  2365. /**
  2366. * ODD
  2367. *
  2368. * Returns number rounded up to the nearest odd integer.
  2369. *
  2370. * @param float $number Number to round
  2371. * @return int Rounded Number
  2372. */
  2373. public static function ODD($number) {
  2374. $number = self::flattenSingleValue($number);
  2375. if (is_numeric($number)) {
  2376. $significance = self::SIGN($number);
  2377. if ($significance == 0) {
  2378. return 1;
  2379. }
  2380. $result = self::CEILING($number,$significance);
  2381. if (self::IS_EVEN($result)) {
  2382. $result += $significance;
  2383. }
  2384. return $result;
  2385. }
  2386. return self::$_errorCodes['value'];
  2387. } // function ODD()
  2388. /**
  2389. * INTVALUE
  2390. *
  2391. * Casts a floating point value to an integer
  2392. *
  2393. * @param float $number Number to cast to an integer
  2394. * @return integer Integer value
  2395. */
  2396. public static function INTVALUE($number) {
  2397. $number = self::flattenSingleValue($number);
  2398. if (is_numeric($number)) {
  2399. return (int) floor($number);
  2400. }
  2401. return self::$_errorCodes['value'];
  2402. } // function INTVALUE()
  2403. /**
  2404. * ROUNDUP
  2405. *
  2406. * Rounds a number up to a specified number of decimal places
  2407. *
  2408. * @param float $number Number to round
  2409. * @param int $digits Number of digits to which you want to round $number
  2410. * @return float Rounded Number
  2411. */
  2412. public static function ROUNDUP($number,$digits) {
  2413. $number = self::flattenSingleValue($number);
  2414. $digits = self::flattenSingleValue($digits);
  2415. if ((is_numeric($number)) && (is_numeric($digits))) {
  2416. $significance = pow(10,$digits);
  2417. if ($number < 0.0) {
  2418. return floor($number * $significance) / $significance;
  2419. } else {
  2420. return ceil($number * $significance) / $significance;
  2421. }
  2422. }
  2423. return self::$_errorCodes['value'];
  2424. } // function ROUNDUP()
  2425. /**
  2426. * ROUNDDOWN
  2427. *
  2428. * Rounds a number down to a specified number of decimal places
  2429. *
  2430. * @param float $number Number to round
  2431. * @param int $digits Number of digits to which you want to round $number
  2432. * @return float Rounded Number
  2433. */
  2434. public static function ROUNDDOWN($number,$digits) {
  2435. $number = self::flattenSingleValue($number);
  2436. $digits = self::flattenSingleValue($digits);
  2437. if ((is_numeric($number)) && (is_numeric($digits))) {
  2438. $significance = pow(10,$digits);
  2439. if ($number < 0.0) {
  2440. return ceil($number * $significance) / $significance;
  2441. } else {
  2442. return floor($number * $significance) / $significance;
  2443. }
  2444. }
  2445. return self::$_errorCodes['value'];
  2446. } // function ROUNDDOWN()
  2447. /**
  2448. * MROUND
  2449. *
  2450. * Rounds a number to the nearest multiple of a specified value
  2451. *
  2452. * @param float $number Number to round
  2453. * @param int $multiple Multiple to which you want to round $number
  2454. * @return float Rounded Number
  2455. */
  2456. public static function MROUND($number,$multiple) {
  2457. $number = self::flattenSingleValue($number);
  2458. $multiple = self::flattenSingleValue($multiple);
  2459. if ((is_numeric($number)) && (is_numeric($multiple))) {
  2460. if ($multiple == 0) {
  2461. return 0;
  2462. }
  2463. if ((self::SIGN($number)) == (self::SIGN($multiple))) {
  2464. $multiplier = 1 / $multiple;
  2465. return round($number * $multiplier) / $multiplier;
  2466. }
  2467. return self::$_errorCodes['num'];
  2468. }
  2469. return self::$_errorCodes['value'];
  2470. } // function MROUND()
  2471. /**
  2472. * SIGN
  2473. *
  2474. * Determines the sign of a number. Returns 1 if the number is positive, zero (0)
  2475. * if the number is 0, and -1 if the number is negative.
  2476. *
  2477. * @param float $number Number to round
  2478. * @return int sign value
  2479. */
  2480. public static function SIGN($number) {
  2481. $number = self::flattenSingleValue($number);
  2482. if (is_numeric($number)) {
  2483. if ($number == 0.0) {
  2484. return 0;
  2485. }
  2486. return $number / abs($number);
  2487. }
  2488. return self::$_errorCodes['value'];
  2489. } // function SIGN()
  2490. /**
  2491. * FLOOR
  2492. *
  2493. * Rounds number down, toward zero, to the nearest multiple of significance.
  2494. *
  2495. * @param float $number Number to round
  2496. * @param float $significance Significance
  2497. * @return float Rounded Number
  2498. */
  2499. public static function FLOOR($number,$significance=null) {
  2500. $number = self::flattenSingleValue($number);
  2501. $significance = self::flattenSingleValue($significance);
  2502. if ((is_null($significance)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
  2503. $significance = $number/abs($number);
  2504. }
  2505. if ((is_numeric($number)) && (is_numeric($significance))) {
  2506. if ((float) $significance == 0.0) {
  2507. return self::$_errorCodes['divisionbyzero'];
  2508. }
  2509. if (self::SIGN($number) == self::SIGN($significance)) {
  2510. return floor($number / $significance) * $significance;
  2511. } else {
  2512. return self::$_errorCodes['num'];
  2513. }
  2514. }
  2515. return self::$_errorCodes['value'];
  2516. } // function FLOOR()
  2517. /**
  2518. * PERMUT
  2519. *
  2520. * Returns the number of permutations for a given number of objects that can be
  2521. * selected from number objects. A permutation is any set or subset of objects or
  2522. * events where internal order is significant. Permutations are different from
  2523. * combinations, for which the internal order is not significant. Use this function
  2524. * for lottery-style probability calculations.
  2525. *
  2526. * @param int $numObjs Number of different objects
  2527. * @param int $numInSet Number of objects in each permutation
  2528. * @return int Number of permutations
  2529. */
  2530. public static function PERMUT($numObjs,$numInSet) {
  2531. $numObjs = self::flattenSingleValue($numObjs);
  2532. $numInSet = self::flattenSingleValue($numInSet);
  2533. if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
  2534. if ($numObjs < $numInSet) {
  2535. return self::$_errorCodes['num'];
  2536. }
  2537. return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet));
  2538. }
  2539. return self::$_errorCodes['value'];
  2540. } // function PERMUT()
  2541. /**
  2542. * COMBIN
  2543. *
  2544. * Returns the number of combinations for a given number of items. Use COMBIN to
  2545. * determine the total possible number of groups for a given number of items.
  2546. *
  2547. * @param int $numObjs Number of different objects
  2548. * @param int $numInSet Number of objects in each combination
  2549. * @return int Number of combinations
  2550. */
  2551. public static function COMBIN($numObjs,$numInSet) {
  2552. $numObjs = self::flattenSingleValue($numObjs);
  2553. $numInSet = self::flattenSingleValue($numInSet);
  2554. if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
  2555. if ($numObjs < $numInSet) {
  2556. return self::$_errorCodes['num'];
  2557. } elseif ($numInSet < 0) {
  2558. return self::$_errorCodes['num'];
  2559. }
  2560. return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet)) / self::FACT($numInSet);
  2561. }
  2562. return self::$_errorCodes['value'];
  2563. } // function COMBIN()
  2564. /**
  2565. * SERIESSUM
  2566. *
  2567. * Returns the sum of a power series
  2568. *
  2569. * @param float $x Input value to the power series
  2570. * @param float $n Initial power to which you want to raise $x
  2571. * @param float $m Step by which to increase $n for each term in the series
  2572. * @param array of mixed Data Series
  2573. * @return float
  2574. */
  2575. public static function SERIESSUM() {
  2576. // Return value
  2577. $returnValue = 0;
  2578. // Loop trough arguments
  2579. $aArgs = self::flattenArray(func_get_args());
  2580. $x = array_shift($aArgs);
  2581. $n = array_shift($aArgs);
  2582. $m = array_shift($aArgs);
  2583. if ((is_numeric($x)) && (is_numeric($n)) && (is_numeric($m))) {
  2584. // Calculate
  2585. $i = 0;
  2586. foreach($aArgs as $arg) {
  2587. // Is it a numeric value?
  2588. if ((is_numeric($arg)) && (!is_string($arg))) {
  2589. $returnValue += $arg * pow($x,$n + ($m * $i++));
  2590. } else {
  2591. return self::$_errorCodes['value'];
  2592. }
  2593. }
  2594. // Return
  2595. return $returnValue;
  2596. }
  2597. return self::$_errorCodes['value'];
  2598. } // function SERIESSUM()
  2599. /**
  2600. * STANDARDIZE
  2601. *
  2602. * Returns a normalized value from a distribution characterized by mean and standard_dev.
  2603. *
  2604. * @param float $value Value to normalize
  2605. * @param float $mean Mean Value
  2606. * @param float $stdDev Standard Deviation
  2607. * @return float Standardized value
  2608. */
  2609. public static function STANDARDIZE($value,$mean,$stdDev) {
  2610. $value = self::flattenSingleValue($value);
  2611. $mean = self::flattenSingleValue($mean);
  2612. $stdDev = self::flattenSingleValue($stdDev);
  2613. if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  2614. if ($stdDev <= 0) {
  2615. return self::$_errorCodes['num'];
  2616. }
  2617. return ($value - $mean) / $stdDev ;
  2618. }
  2619. return self::$_errorCodes['value'];
  2620. } // function STANDARDIZE()
  2621. //
  2622. // Private method to return an array of the factors of the input value
  2623. //
  2624. private static function _factors($value) {
  2625. $startVal = floor(sqrt($value));
  2626. $factorArray = array();
  2627. for ($i = $startVal; $i > 1; --$i) {
  2628. if (($value % $i) == 0) {
  2629. $factorArray = array_merge($factorArray,self::_factors($value / $i));
  2630. $factorArray = array_merge($factorArray,self::_factors($i));
  2631. if ($i <= sqrt($value)) {
  2632. break;
  2633. }
  2634. }
  2635. }
  2636. if (count($factorArray) > 0) {
  2637. rsort($factorArray);
  2638. return $factorArray;
  2639. } else {
  2640. return array((integer) $value);
  2641. }
  2642. } // function _factors()
  2643. /**
  2644. * LCM
  2645. *
  2646. * Returns the lowest common multiplier of a series of numbers
  2647. *
  2648. * @param $array Values to calculate the Lowest Common Multiplier
  2649. * @return int Lowest Common Multiplier
  2650. */
  2651. public static function LCM() {
  2652. $aArgs = self::flattenArray(func_get_args());
  2653. $returnValue = 1;
  2654. $allPoweredFactors = array();
  2655. foreach($aArgs as $value) {
  2656. if (!is_numeric($value)) {
  2657. return self::$_errorCodes['value'];
  2658. }
  2659. if ($value == 0) {
  2660. return 0;
  2661. } elseif ($value < 0) {
  2662. return self::$_errorCodes['num'];
  2663. }
  2664. $myFactors = self::_factors(floor($value));
  2665. $myCountedFactors = array_count_values($myFactors);
  2666. $myPoweredFactors = array();
  2667. foreach($myCountedFactors as $myCountedFactor => $myCountedPower) {
  2668. $myPoweredFactors[$myCountedFactor] = pow($myCountedFactor,$myCountedPower);
  2669. }
  2670. foreach($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
  2671. if (array_key_exists($myPoweredValue,$allPoweredFactors)) {
  2672. if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
  2673. $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
  2674. }
  2675. } else {
  2676. $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
  2677. }
  2678. }
  2679. }
  2680. foreach($allPoweredFactors as $allPoweredFactor) {
  2681. $returnValue *= (integer) $allPoweredFactor;
  2682. }
  2683. return $returnValue;
  2684. } // function LCM()
  2685. /**
  2686. * GCD
  2687. *
  2688. * Returns the greatest common divisor of a series of numbers
  2689. *
  2690. * @param $array Values to calculate the Greatest Common Divisor
  2691. * @return int Greatest Common Divisor
  2692. */
  2693. public static function GCD() {
  2694. $aArgs = self::flattenArray(func_get_args());
  2695. $returnValue = 1;
  2696. $allPoweredFactors = array();
  2697. foreach($aArgs as $value) {
  2698. if ($value == 0) {
  2699. break;
  2700. }
  2701. $myFactors = self::_factors($value);
  2702. $myCountedFactors = array_count_values($myFactors);
  2703. $allValuesFactors[] = $myCountedFactors;
  2704. }
  2705. $allValuesCount = count($allValuesFactors);
  2706. $mergedArray = $allValuesFactors[0];
  2707. for ($i=1;$i < $allValuesCount; ++$i) {
  2708. $mergedArray = array_intersect_key($mergedArray,$allValuesFactors[$i]);
  2709. }
  2710. $mergedArrayValues = count($mergedArray);
  2711. if ($mergedArrayValues == 0) {
  2712. return $returnValue;
  2713. } elseif ($mergedArrayValues > 1) {
  2714. foreach($mergedArray as $mergedKey => $mergedValue) {
  2715. foreach($allValuesFactors as $highestPowerTest) {
  2716. foreach($highestPowerTest as $testKey => $testValue) {
  2717. if (($testKey == $mergedKey) && ($testValue < $mergedValue)) {
  2718. $mergedArray[$mergedKey] = $testValue;
  2719. $mergedValue = $testValue;
  2720. }
  2721. }
  2722. }
  2723. }
  2724. $returnValue = 1;
  2725. foreach($mergedArray as $key => $value) {
  2726. $returnValue *= pow($key,$value);
  2727. }
  2728. return $returnValue;
  2729. } else {
  2730. $keys = array_keys($mergedArray);
  2731. $key = $keys[0];
  2732. $value = $mergedArray[$key];
  2733. foreach($allValuesFactors as $testValue) {
  2734. foreach($testValue as $mergedKey => $mergedValue) {
  2735. if (($mergedKey == $key) && ($mergedValue < $value)) {
  2736. $value = $mergedValue;
  2737. }
  2738. }
  2739. }
  2740. return pow($key,$value);
  2741. }
  2742. } // function GCD()
  2743. /**
  2744. * BINOMDIST
  2745. *
  2746. * Returns the individual term binomial distribution probability. Use BINOMDIST in problems with
  2747. * a fixed number of tests or trials, when the outcomes of any trial are only success or failure,
  2748. * when trials are independent, and when the probability of success is constant throughout the
  2749. * experiment. For example, BINOMDIST can calculate the probability that two of the next three
  2750. * babies born are male.
  2751. *
  2752. * @param float $value Number of successes in trials
  2753. * @param float $trials Number of trials
  2754. * @param float $probability Probability of success on each trial
  2755. * @param boolean $cumulative
  2756. * @return float
  2757. *
  2758. * @todo Cumulative distribution function
  2759. *
  2760. */
  2761. public static function BINOMDIST($value, $trials, $probability, $cumulative) {
  2762. $value = floor(self::flattenSingleValue($value));
  2763. $trials = floor(self::flattenSingleValue($trials));
  2764. $probability = self::flattenSingleValue($probability);
  2765. if ((is_numeric($value)) && (is_numeric($trials)) && (is_numeric($probability))) {
  2766. if (($value < 0) || ($value > $trials)) {
  2767. return self::$_errorCodes['num'];
  2768. }
  2769. if (($probability < 0) || ($probability > 1)) {
  2770. return self::$_errorCodes['num'];
  2771. }
  2772. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  2773. if ($cumulative) {
  2774. $summer = 0;
  2775. for ($i = 0; $i <= $value; ++$i) {
  2776. $summer += self::COMBIN($trials,$i) * pow($probability,$i) * pow(1 - $probability,$trials - $i);
  2777. }
  2778. return $summer;
  2779. } else {
  2780. return self::COMBIN($trials,$value) * pow($probability,$value) * pow(1 - $probability,$trials - $value) ;
  2781. }
  2782. }
  2783. }
  2784. return self::$_errorCodes['value'];
  2785. } // function BINOMDIST()
  2786. /**
  2787. * NEGBINOMDIST
  2788. *
  2789. * Returns the negative binomial distribution. NEGBINOMDIST returns the probability that
  2790. * there will be number_f failures before the number_s-th success, when the constant
  2791. * probability of a success is probability_s. This function is similar to the binomial
  2792. * distribution, except that the number of successes is fixed, and the number of trials is
  2793. * variable. Like the binomial, trials are assumed to be independent.
  2794. *
  2795. * @param float $failures Number of Failures
  2796. * @param float $successes Threshold number of Successes
  2797. * @param float $probability Probability of success on each trial
  2798. * @return float
  2799. *
  2800. */
  2801. public static function NEGBINOMDIST($failures, $successes, $probability) {
  2802. $failures = floor(self::flattenSingleValue($failures));
  2803. $successes = floor(self::flattenSingleValue($successes));
  2804. $probability = self::flattenSingleValue($probability);
  2805. if ((is_numeric($failures)) && (is_numeric($successes)) && (is_numeric($probability))) {
  2806. if (($failures < 0) || ($successes < 1)) {
  2807. return self::$_errorCodes['num'];
  2808. }
  2809. if (($probability < 0) || ($probability > 1)) {
  2810. return self::$_errorCodes['num'];
  2811. }
  2812. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  2813. if (($failures + $successes - 1) <= 0) {
  2814. return self::$_errorCodes['num'];
  2815. }
  2816. }
  2817. return (self::COMBIN($failures + $successes - 1,$successes - 1)) * (pow($probability,$successes)) * (pow(1 - $probability,$failures)) ;
  2818. }
  2819. return self::$_errorCodes['value'];
  2820. } // function NEGBINOMDIST()
  2821. /**
  2822. * CRITBINOM
  2823. *
  2824. * Returns the smallest value for which the cumulative binomial distribution is greater
  2825. * than or equal to a criterion value
  2826. *
  2827. * See http://support.microsoft.com/kb/828117/ for details of the algorithm used
  2828. *
  2829. * @param float $trials number of Bernoulli trials
  2830. * @param float $probability probability of a success on each trial
  2831. * @param float $alpha criterion value
  2832. * @return int
  2833. *
  2834. * @todo Warning. This implementation differs from the algorithm detailed on the MS
  2835. * web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess
  2836. * This eliminates a potential endless loop error, but may have an adverse affect on the
  2837. * accuracy of the function (although all my tests have so far returned correct results).
  2838. *
  2839. */
  2840. public static function CRITBINOM($trials, $probability, $alpha) {
  2841. $trials = floor(self::flattenSingleValue($trials));
  2842. $probability = self::flattenSingleValue($probability);
  2843. $alpha = self::flattenSingleValue($alpha);
  2844. if ((is_numeric($trials)) && (is_numeric($probability)) && (is_numeric($alpha))) {
  2845. if ($trials < 0) {
  2846. return self::$_errorCodes['num'];
  2847. }
  2848. if (($probability < 0) || ($probability > 1)) {
  2849. return self::$_errorCodes['num'];
  2850. }
  2851. if (($alpha < 0) || ($alpha > 1)) {
  2852. return self::$_errorCodes['num'];
  2853. }
  2854. if ($alpha <= 0.5) {
  2855. $t = sqrt(log(1 / pow($alpha,2)));
  2856. $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));
  2857. } else {
  2858. $t = sqrt(log(1 / pow(1 - $alpha,2)));
  2859. $trialsApprox = $t - (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t);
  2860. }
  2861. $Guess = floor($trials * $probability + $trialsApprox * sqrt($trials * $probability * (1 - $probability)));
  2862. if ($Guess < 0) {
  2863. $Guess = 0;
  2864. } elseif ($Guess > $trials) {
  2865. $Guess = $trials;
  2866. }
  2867. $TotalUnscaledProbability = $UnscaledPGuess = $UnscaledCumPGuess = 0.0;
  2868. $EssentiallyZero = 10e-12;
  2869. $m = floor($trials * $probability);
  2870. ++$TotalUnscaledProbability;
  2871. if ($m == $Guess) { ++$UnscaledPGuess; }
  2872. if ($m <= $Guess) { ++$UnscaledCumPGuess; }
  2873. $PreviousValue = 1;
  2874. $Done = False;
  2875. $k = $m + 1;
  2876. while ((!$Done) && ($k <= $trials)) {
  2877. $CurrentValue = $PreviousValue * ($trials - $k + 1) * $probability / ($k * (1 - $probability));
  2878. $TotalUnscaledProbability += $CurrentValue;
  2879. if ($k == $Guess) { $UnscaledPGuess += $CurrentValue; }
  2880. if ($k <= $Guess) { $UnscaledCumPGuess += $CurrentValue; }
  2881. if ($CurrentValue <= $EssentiallyZero) { $Done = True; }
  2882. $PreviousValue = $CurrentValue;
  2883. ++$k;
  2884. }
  2885. $PreviousValue = 1;
  2886. $Done = False;
  2887. $k = $m - 1;
  2888. while ((!$Done) && ($k >= 0)) {
  2889. $CurrentValue = $PreviousValue * $k + 1 * (1 - $probability) / (($trials - $k) * $probability);
  2890. $TotalUnscaledProbability += $CurrentValue;
  2891. if ($k == $Guess) { $UnscaledPGuess += $CurrentValue; }
  2892. if ($k <= $Guess) { $UnscaledCumPGuess += $CurrentValue; }
  2893. if ($CurrentValue <= $EssentiallyZero) { $Done = True; }
  2894. $PreviousValue = $CurrentValue;
  2895. --$k;
  2896. }
  2897. $PGuess = $UnscaledPGuess / $TotalUnscaledProbability;
  2898. $CumPGuess = $UnscaledCumPGuess / $TotalUnscaledProbability;
  2899. // $CumPGuessMinus1 = $CumPGuess - $PGuess;
  2900. $CumPGuessMinus1 = $CumPGuess - 1;
  2901. while (True) {
  2902. if (($CumPGuessMinus1 < $alpha) && ($CumPGuess >= $alpha)) {
  2903. return $Guess;
  2904. } elseif (($CumPGuessMinus1 < $alpha) && ($CumPGuess < $alpha)) {
  2905. $PGuessPlus1 = $PGuess * ($trials - $Guess) * $probability / $Guess / (1 - $probability);
  2906. $CumPGuessMinus1 = $CumPGuess;
  2907. $CumPGuess = $CumPGuess + $PGuessPlus1;
  2908. $PGuess = $PGuessPlus1;
  2909. ++$Guess;
  2910. } elseif (($CumPGuessMinus1 >= $alpha) && ($CumPGuess >= $alpha)) {
  2911. $PGuessMinus1 = $PGuess * $Guess * (1 - $probability) / ($trials - $Guess + 1) / $probability;
  2912. $CumPGuess = $CumPGuessMinus1;
  2913. $CumPGuessMinus1 = $CumPGuessMinus1 - $PGuess;
  2914. $PGuess = $PGuessMinus1;
  2915. --$Guess;
  2916. }
  2917. }
  2918. }
  2919. return self::$_errorCodes['value'];
  2920. } // function CRITBINOM()
  2921. /**
  2922. * CHIDIST
  2923. *
  2924. * Returns the one-tailed probability of the chi-squared distribution.
  2925. *
  2926. * @param float $value Value for the function
  2927. * @param float $degrees degrees of freedom
  2928. * @return float
  2929. */
  2930. public static function CHIDIST($value, $degrees) {
  2931. $value = self::flattenSingleValue($value);
  2932. $degrees = floor(self::flattenSingleValue($degrees));
  2933. if ((is_numeric($value)) && (is_numeric($degrees))) {
  2934. if ($degrees < 1) {
  2935. return self::$_errorCodes['num'];
  2936. }
  2937. if ($value < 0) {
  2938. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  2939. return 1;
  2940. }
  2941. return self::$_errorCodes['num'];
  2942. }
  2943. return 1 - (self::_incompleteGamma($degrees/2,$value/2) / self::_gamma($degrees/2));
  2944. }
  2945. return self::$_errorCodes['value'];
  2946. } // function CHIDIST()
  2947. /**
  2948. * CHIINV
  2949. *
  2950. * Returns the one-tailed probability of the chi-squared distribution.
  2951. *
  2952. * @param float $probability Probability for the function
  2953. * @param float $degrees degrees of freedom
  2954. * @return float
  2955. */
  2956. public static function CHIINV($probability, $degrees) {
  2957. $probability = self::flattenSingleValue($probability);
  2958. $degrees = floor(self::flattenSingleValue($degrees));
  2959. if ((is_numeric($probability)) && (is_numeric($degrees))) {
  2960. $xLo = 100;
  2961. $xHi = 0;
  2962. $maxIteration = 100;
  2963. $x = $xNew = 1;
  2964. $dx = 1;
  2965. $i = 0;
  2966. while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
  2967. // Apply Newton-Raphson step
  2968. $result = self::CHIDIST($x, $degrees);
  2969. $error = $result - $probability;
  2970. if ($error == 0.0) {
  2971. $dx = 0;
  2972. } elseif ($error < 0.0) {
  2973. $xLo = $x;
  2974. } else {
  2975. $xHi = $x;
  2976. }
  2977. // Avoid division by zero
  2978. if ($result != 0.0) {
  2979. $dx = $error / $result;
  2980. $xNew = $x - $dx;
  2981. }
  2982. // If the NR fails to converge (which for example may be the
  2983. // case if the initial guess is too rough) we apply a bisection
  2984. // step to determine a more narrow interval around the root.
  2985. if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
  2986. $xNew = ($xLo + $xHi) / 2;
  2987. $dx = $xNew - $x;
  2988. }
  2989. $x = $xNew;
  2990. }
  2991. if ($i == MAX_ITERATIONS) {
  2992. return self::$_errorCodes['na'];
  2993. }
  2994. return round($x,12);
  2995. }
  2996. return self::$_errorCodes['value'];
  2997. } // function CHIINV()
  2998. /**
  2999. * EXPONDIST
  3000. *
  3001. * Returns the exponential distribution. Use EXPONDIST to model the time between events,
  3002. * such as how long an automated bank teller takes to deliver cash. For example, you can
  3003. * use EXPONDIST to determine the probability that the process takes at most 1 minute.
  3004. *
  3005. * @param float $value Value of the function
  3006. * @param float $lambda The parameter value
  3007. * @param boolean $cumulative
  3008. * @return float
  3009. */
  3010. public static function EXPONDIST($value, $lambda, $cumulative) {
  3011. $value = self::flattenSingleValue($value);
  3012. $lambda = self::flattenSingleValue($lambda);
  3013. $cumulative = self::flattenSingleValue($cumulative);
  3014. if ((is_numeric($value)) && (is_numeric($lambda))) {
  3015. if (($value < 0) || ($lambda < 0)) {
  3016. return self::$_errorCodes['num'];
  3017. }
  3018. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  3019. if ($cumulative) {
  3020. return 1 - exp(0-$value*$lambda);
  3021. } else {
  3022. return $lambda * exp(0-$value*$lambda);
  3023. }
  3024. }
  3025. }
  3026. return self::$_errorCodes['value'];
  3027. } // function EXPONDIST()
  3028. /**
  3029. * FISHER
  3030. *
  3031. * Returns the Fisher transformation at x. This transformation produces a function that
  3032. * is normally distributed rather than skewed. Use this function to perform hypothesis
  3033. * testing on the correlation coefficient.
  3034. *
  3035. * @param float $value
  3036. * @return float
  3037. */
  3038. public static function FISHER($value) {
  3039. $value = self::flattenSingleValue($value);
  3040. if (is_numeric($value)) {
  3041. if (($value <= -1) || ($value >= 1)) {
  3042. return self::$_errorCodes['num'];
  3043. }
  3044. return 0.5 * log((1+$value)/(1-$value));
  3045. }
  3046. return self::$_errorCodes['value'];
  3047. } // function FISHER()
  3048. /**
  3049. * FISHERINV
  3050. *
  3051. * Returns the inverse of the Fisher transformation. Use this transformation when
  3052. * analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
  3053. * FISHERINV(y) = x.
  3054. *
  3055. * @param float $value
  3056. * @return float
  3057. */
  3058. public static function FISHERINV($value) {
  3059. $value = self::flattenSingleValue($value);
  3060. if (is_numeric($value)) {
  3061. return (exp(2 * $value) - 1) / (exp(2 * $value) + 1);
  3062. }
  3063. return self::$_errorCodes['value'];
  3064. } // function FISHERINV()
  3065. // Function cache for _logBeta function
  3066. private static $_logBetaCache_p = 0.0;
  3067. private static $_logBetaCache_q = 0.0;
  3068. private static $_logBetaCache_result = 0.0;
  3069. /**
  3070. * The natural logarithm of the beta function.
  3071. * @param p require p>0
  3072. * @param q require q>0
  3073. * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
  3074. * @author Jaco van Kooten
  3075. */
  3076. private static function _logBeta($p, $q) {
  3077. if ($p != self::$_logBetaCache_p || $q != self::$_logBetaCache_q) {
  3078. self::$_logBetaCache_p = $p;
  3079. self::$_logBetaCache_q = $q;
  3080. if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
  3081. self::$_logBetaCache_result = 0.0;
  3082. } else {
  3083. self::$_logBetaCache_result = self::_logGamma($p) + self::_logGamma($q) - self::_logGamma($p + $q);
  3084. }
  3085. }
  3086. return self::$_logBetaCache_result;
  3087. } // function _logBeta()
  3088. /**
  3089. * Evaluates of continued fraction part of incomplete beta function.
  3090. * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
  3091. * @author Jaco van Kooten
  3092. */
  3093. private static function _betaFraction($x, $p, $q) {
  3094. $c = 1.0;
  3095. $sum_pq = $p + $q;
  3096. $p_plus = $p + 1.0;
  3097. $p_minus = $p - 1.0;
  3098. $h = 1.0 - $sum_pq * $x / $p_plus;
  3099. if (abs($h) < XMININ) {
  3100. $h = XMININ;
  3101. }
  3102. $h = 1.0 / $h;
  3103. $frac = $h;
  3104. $m = 1;
  3105. $delta = 0.0;
  3106. while ($m <= MAX_ITERATIONS && abs($delta-1.0) > PRECISION ) {
  3107. $m2 = 2 * $m;
  3108. // even index for d
  3109. $d = $m * ($q - $m) * $x / ( ($p_minus + $m2) * ($p + $m2));
  3110. $h = 1.0 + $d * $h;
  3111. if (abs($h) < XMININ) {
  3112. $h = XMININ;
  3113. }
  3114. $h = 1.0 / $h;
  3115. $c = 1.0 + $d / $c;
  3116. if (abs($c) < XMININ) {
  3117. $c = XMININ;
  3118. }
  3119. $frac *= $h * $c;
  3120. // odd index for d
  3121. $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
  3122. $h = 1.0 + $d * $h;
  3123. if (abs($h) < XMININ) {
  3124. $h = XMININ;
  3125. }
  3126. $h = 1.0 / $h;
  3127. $c = 1.0 + $d / $c;
  3128. if (abs($c) < XMININ) {
  3129. $c = XMININ;
  3130. }
  3131. $delta = $h * $c;
  3132. $frac *= $delta;
  3133. ++$m;
  3134. }
  3135. return $frac;
  3136. } // function _betaFraction()
  3137. /**
  3138. * logGamma function
  3139. *
  3140. * @version 1.1
  3141. * @author Jaco van Kooten
  3142. *
  3143. * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
  3144. *
  3145. * The natural logarithm of the gamma function. <br />
  3146. * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz <br />
  3147. * Applied Mathematics Division <br />
  3148. * Argonne National Laboratory <br />
  3149. * Argonne, IL 60439 <br />
  3150. * <p>
  3151. * References:
  3152. * <ol>
  3153. * <li>W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural
  3154. * Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.</li>
  3155. * <li>K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.</li>
  3156. * <li>Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.</li>
  3157. * </ol>
  3158. * </p>
  3159. * <p>
  3160. * From the original documentation:
  3161. * </p>
  3162. * <p>
  3163. * This routine calculates the LOG(GAMMA) function for a positive real argument X.
  3164. * Computation is based on an algorithm outlined in references 1 and 2.
  3165. * The program uses rational functions that theoretically approximate LOG(GAMMA)
  3166. * to at least 18 significant decimal digits. The approximation for X > 12 is from
  3167. * reference 3, while approximations for X < 12.0 are similar to those in reference
  3168. * 1, but are unpublished. The accuracy achieved depends on the arithmetic system,
  3169. * the compiler, the intrinsic functions, and proper selection of the
  3170. * machine-dependent constants.
  3171. * </p>
  3172. * <p>
  3173. * Error returns: <br />
  3174. * The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
  3175. * The computation is believed to be free of underflow and overflow.
  3176. * </p>
  3177. * @return MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
  3178. */
  3179. // Function cache for logGamma
  3180. private static $_logGammaCache_result = 0.0;
  3181. private static $_logGammaCache_x = 0.0;
  3182. private static function _logGamma($x) {
  3183. // Log Gamma related constants
  3184. static $lg_d1 = -0.5772156649015328605195174;
  3185. static $lg_d2 = 0.4227843350984671393993777;
  3186. static $lg_d4 = 1.791759469228055000094023;
  3187. static $lg_p1 = array( 4.945235359296727046734888,
  3188. 201.8112620856775083915565,
  3189. 2290.838373831346393026739,
  3190. 11319.67205903380828685045,
  3191. 28557.24635671635335736389,
  3192. 38484.96228443793359990269,
  3193. 26377.48787624195437963534,
  3194. 7225.813979700288197698961 );
  3195. static $lg_p2 = array( 4.974607845568932035012064,
  3196. 542.4138599891070494101986,
  3197. 15506.93864978364947665077,
  3198. 184793.2904445632425417223,
  3199. 1088204.76946882876749847,
  3200. 3338152.967987029735917223,
  3201. 5106661.678927352456275255,
  3202. 3074109.054850539556250927 );
  3203. static $lg_p4 = array( 14745.02166059939948905062,
  3204. 2426813.369486704502836312,
  3205. 121475557.4045093227939592,
  3206. 2663432449.630976949898078,
  3207. 29403789566.34553899906876,
  3208. 170266573776.5398868392998,
  3209. 492612579337.743088758812,
  3210. 560625185622.3951465078242 );
  3211. static $lg_q1 = array( 67.48212550303777196073036,
  3212. 1113.332393857199323513008,
  3213. 7738.757056935398733233834,
  3214. 27639.87074403340708898585,
  3215. 54993.10206226157329794414,
  3216. 61611.22180066002127833352,
  3217. 36351.27591501940507276287,
  3218. 8785.536302431013170870835 );
  3219. static $lg_q2 = array( 183.0328399370592604055942,
  3220. 7765.049321445005871323047,
  3221. 133190.3827966074194402448,
  3222. 1136705.821321969608938755,
  3223. 5267964.117437946917577538,
  3224. 13467014.54311101692290052,
  3225. 17827365.30353274213975932,
  3226. 9533095.591844353613395747 );
  3227. static $lg_q4 = array( 2690.530175870899333379843,
  3228. 639388.5654300092398984238,
  3229. 41355999.30241388052042842,
  3230. 1120872109.61614794137657,
  3231. 14886137286.78813811542398,
  3232. 101680358627.2438228077304,
  3233. 341747634550.7377132798597,
  3234. 446315818741.9713286462081 );
  3235. static $lg_c = array( -0.001910444077728,
  3236. 8.4171387781295e-4,
  3237. -5.952379913043012e-4,
  3238. 7.93650793500350248e-4,
  3239. -0.002777777777777681622553,
  3240. 0.08333333333333333331554247,
  3241. 0.0057083835261 );
  3242. // Rough estimate of the fourth root of logGamma_xBig
  3243. static $lg_frtbig = 2.25e76;
  3244. static $pnt68 = 0.6796875;
  3245. if ($x == self::$_logGammaCache_x) {
  3246. return self::$_logGammaCache_result;
  3247. }
  3248. $y = $x;
  3249. if ($y > 0.0 && $y <= LOG_GAMMA_X_MAX_VALUE) {
  3250. if ($y <= EPS) {
  3251. $res = -log(y);
  3252. } elseif ($y <= 1.5) {
  3253. // ---------------------
  3254. // EPS .LT. X .LE. 1.5
  3255. // ---------------------
  3256. if ($y < $pnt68) {
  3257. $corr = -log($y);
  3258. $xm1 = $y;
  3259. } else {
  3260. $corr = 0.0;
  3261. $xm1 = $y - 1.0;
  3262. }
  3263. if ($y <= 0.5 || $y >= $pnt68) {
  3264. $xden = 1.0;
  3265. $xnum = 0.0;
  3266. for ($i = 0; $i < 8; ++$i) {
  3267. $xnum = $xnum * $xm1 + $lg_p1[$i];
  3268. $xden = $xden * $xm1 + $lg_q1[$i];
  3269. }
  3270. $res = $corr + $xm1 * ($lg_d1 + $xm1 * ($xnum / $xden));
  3271. } else {
  3272. $xm2 = $y - 1.0;
  3273. $xden = 1.0;
  3274. $xnum = 0.0;
  3275. for ($i = 0; $i < 8; ++$i) {
  3276. $xnum = $xnum * $xm2 + $lg_p2[$i];
  3277. $xden = $xden * $xm2 + $lg_q2[$i];
  3278. }
  3279. $res = $corr + $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
  3280. }
  3281. } elseif ($y <= 4.0) {
  3282. // ---------------------
  3283. // 1.5 .LT. X .LE. 4.0
  3284. // ---------------------
  3285. $xm2 = $y - 2.0;
  3286. $xden = 1.0;
  3287. $xnum = 0.0;
  3288. for ($i = 0; $i < 8; ++$i) {
  3289. $xnum = $xnum * $xm2 + $lg_p2[$i];
  3290. $xden = $xden * $xm2 + $lg_q2[$i];
  3291. }
  3292. $res = $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
  3293. } elseif ($y <= 12.0) {
  3294. // ----------------------
  3295. // 4.0 .LT. X .LE. 12.0
  3296. // ----------------------
  3297. $xm4 = $y - 4.0;
  3298. $xden = -1.0;
  3299. $xnum = 0.0;
  3300. for ($i = 0; $i < 8; ++$i) {
  3301. $xnum = $xnum * $xm4 + $lg_p4[$i];
  3302. $xden = $xden * $xm4 + $lg_q4[$i];
  3303. }
  3304. $res = $lg_d4 + $xm4 * ($xnum / $xden);
  3305. } else {
  3306. // ---------------------------------
  3307. // Evaluate for argument .GE. 12.0
  3308. // ---------------------------------
  3309. $res = 0.0;
  3310. if ($y <= $lg_frtbig) {
  3311. $res = $lg_c[6];
  3312. $ysq = $y * $y;
  3313. for ($i = 0; $i < 6; ++$i)
  3314. $res = $res / $ysq + $lg_c[$i];
  3315. }
  3316. $res /= $y;
  3317. $corr = log($y);
  3318. $res = $res + log(SQRT2PI) - 0.5 * $corr;
  3319. $res += $y * ($corr - 1.0);
  3320. }
  3321. } else {
  3322. // --------------------------
  3323. // Return for bad arguments
  3324. // --------------------------
  3325. $res = MAX_VALUE;
  3326. }
  3327. // ------------------------------
  3328. // Final adjustments and return
  3329. // ------------------------------
  3330. self::$_logGammaCache_x = $x;
  3331. self::$_logGammaCache_result = $res;
  3332. return $res;
  3333. } // function _logGamma()
  3334. /**
  3335. * Beta function.
  3336. *
  3337. * @author Jaco van Kooten
  3338. *
  3339. * @param p require p>0
  3340. * @param q require q>0
  3341. * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
  3342. */
  3343. private static function _beta($p, $q) {
  3344. if ($p <= 0.0 || $q <= 0.0 || ($p + $q) > LOG_GAMMA_X_MAX_VALUE) {
  3345. return 0.0;
  3346. } else {
  3347. return exp(self::_logBeta($p, $q));
  3348. }
  3349. } // function _beta()
  3350. /**
  3351. * Incomplete beta function
  3352. *
  3353. * @author Jaco van Kooten
  3354. * @author Paul Meagher
  3355. *
  3356. * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
  3357. * @param x require 0<=x<=1
  3358. * @param p require p>0
  3359. * @param q require q>0
  3360. * @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
  3361. */
  3362. private static function _incompleteBeta($x, $p, $q) {
  3363. if ($x <= 0.0) {
  3364. return 0.0;
  3365. } elseif ($x >= 1.0) {
  3366. return 1.0;
  3367. } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
  3368. return 0.0;
  3369. }
  3370. $beta_gam = exp((0 - self::_logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
  3371. if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
  3372. return $beta_gam * self::_betaFraction($x, $p, $q) / $p;
  3373. } else {
  3374. return 1.0 - ($beta_gam * self::_betaFraction(1 - $x, $q, $p) / $q);
  3375. }
  3376. } // function _incompleteBeta()
  3377. /**
  3378. * BETADIST
  3379. *
  3380. * Returns the beta distribution.
  3381. *
  3382. * @param float $value Value at which you want to evaluate the distribution
  3383. * @param float $alpha Parameter to the distribution
  3384. * @param float $beta Parameter to the distribution
  3385. * @param boolean $cumulative
  3386. * @return float
  3387. *
  3388. */
  3389. public static function BETADIST($value,$alpha,$beta,$rMin=0,$rMax=1) {
  3390. $value = self::flattenSingleValue($value);
  3391. $alpha = self::flattenSingleValue($alpha);
  3392. $beta = self::flattenSingleValue($beta);
  3393. $rMin = self::flattenSingleValue($rMin);
  3394. $rMax = self::flattenSingleValue($rMax);
  3395. if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
  3396. if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
  3397. return self::$_errorCodes['num'];
  3398. }
  3399. if ($rMin > $rMax) {
  3400. $tmp = $rMin;
  3401. $rMin = $rMax;
  3402. $rMax = $tmp;
  3403. }
  3404. $value -= $rMin;
  3405. $value /= ($rMax - $rMin);
  3406. return self::_incompleteBeta($value,$alpha,$beta);
  3407. }
  3408. return self::$_errorCodes['value'];
  3409. } // function BETADIST()
  3410. /**
  3411. * BETAINV
  3412. *
  3413. * Returns the inverse of the beta distribution.
  3414. *
  3415. * @param float $probability Probability at which you want to evaluate the distribution
  3416. * @param float $alpha Parameter to the distribution
  3417. * @param float $beta Parameter to the distribution
  3418. * @param boolean $cumulative
  3419. * @return float
  3420. *
  3421. */
  3422. public static function BETAINV($probability,$alpha,$beta,$rMin=0,$rMax=1) {
  3423. $probability = self::flattenSingleValue($probability);
  3424. $alpha = self::flattenSingleValue($alpha);
  3425. $beta = self::flattenSingleValue($beta);
  3426. $rMin = self::flattenSingleValue($rMin);
  3427. $rMax = self::flattenSingleValue($rMax);
  3428. if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
  3429. if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0) || ($probability > 1)) {
  3430. return self::$_errorCodes['num'];
  3431. }
  3432. if ($rMin > $rMax) {
  3433. $tmp = $rMin;
  3434. $rMin = $rMax;
  3435. $rMax = $tmp;
  3436. }
  3437. $a = 0;
  3438. $b = 2;
  3439. $maxIteration = 100;
  3440. $i = 0;
  3441. while ((($b - $a) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
  3442. $guess = ($a + $b) / 2;
  3443. $result = self::BETADIST($guess, $alpha, $beta);
  3444. if (($result == $probability) || ($result == 0)) {
  3445. $b = $a;
  3446. } elseif ($result > $probability) {
  3447. $b = $guess;
  3448. } else {
  3449. $a = $guess;
  3450. }
  3451. }
  3452. if ($i == MAX_ITERATIONS) {
  3453. return self::$_errorCodes['na'];
  3454. }
  3455. return round($rMin + $guess * ($rMax - $rMin),12);
  3456. }
  3457. return self::$_errorCodes['value'];
  3458. } // function BETAINV()
  3459. //
  3460. // Private implementation of the incomplete Gamma function
  3461. //
  3462. private static function _incompleteGamma($a,$x) {
  3463. static $max = 32;
  3464. $summer = 0;
  3465. for ($n=0; $n<=$max; ++$n) {
  3466. $divisor = $a;
  3467. for ($i=1; $i<=$n; ++$i) {
  3468. $divisor *= ($a + $i);
  3469. }
  3470. $summer += (pow($x,$n) / $divisor);
  3471. }
  3472. return pow($x,$a) * exp(0-$x) * $summer;
  3473. } // function _incompleteGamma()
  3474. //
  3475. // Private implementation of the Gamma function
  3476. //
  3477. private static function _gamma($data) {
  3478. if ($data == 0.0) return 0;
  3479. static $p0 = 1.000000000190015;
  3480. static $p = array ( 1 => 76.18009172947146,
  3481. 2 => -86.50532032941677,
  3482. 3 => 24.01409824083091,
  3483. 4 => -1.231739572450155,
  3484. 5 => 1.208650973866179e-3,
  3485. 6 => -5.395239384953e-6
  3486. );
  3487. $y = $x = $data;
  3488. $tmp = $x + 5.5;
  3489. $tmp -= ($x + 0.5) * log($tmp);
  3490. $summer = $p0;
  3491. for ($j=1;$j<=6;++$j) {
  3492. $summer += ($p[$j] / ++$y);
  3493. }
  3494. return exp(0 - $tmp + log(2.5066282746310005 * $summer / $x));
  3495. } // function _gamma()
  3496. /**
  3497. * GAMMADIST
  3498. *
  3499. * Returns the gamma distribution.
  3500. *
  3501. * @param float $value Value at which you want to evaluate the distribution
  3502. * @param float $a Parameter to the distribution
  3503. * @param float $b Parameter to the distribution
  3504. * @param boolean $cumulative
  3505. * @return float
  3506. *
  3507. */
  3508. public static function GAMMADIST($value,$a,$b,$cumulative) {
  3509. $value = self::flattenSingleValue($value);
  3510. $a = self::flattenSingleValue($a);
  3511. $b = self::flattenSingleValue($b);
  3512. if ((is_numeric($value)) && (is_numeric($a)) && (is_numeric($b))) {
  3513. if (($value < 0) || ($a <= 0) || ($b <= 0)) {
  3514. return self::$_errorCodes['num'];
  3515. }
  3516. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  3517. if ($cumulative) {
  3518. return self::_incompleteGamma($a,$value / $b) / self::_gamma($a);
  3519. } else {
  3520. return (1 / (pow($b,$a) * self::_gamma($a))) * pow($value,$a-1) * exp(0-($value / $b));
  3521. }
  3522. }
  3523. }
  3524. return self::$_errorCodes['value'];
  3525. } // function GAMMADIST()
  3526. /**
  3527. * GAMMAINV
  3528. *
  3529. * Returns the inverse of the beta distribution.
  3530. *
  3531. * @param float $probability Probability at which you want to evaluate the distribution
  3532. * @param float $alpha Parameter to the distribution
  3533. * @param float $beta Parameter to the distribution
  3534. * @param boolean $cumulative
  3535. * @return float
  3536. *
  3537. */
  3538. public static function GAMMAINV($probability,$alpha,$beta) {
  3539. $probability = self::flattenSingleValue($probability);
  3540. $alpha = self::flattenSingleValue($alpha);
  3541. $beta = self::flattenSingleValue($beta);
  3542. // $rMin = self::flattenSingleValue($rMin);
  3543. // $rMax = self::flattenSingleValue($rMax);
  3544. if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta))) {
  3545. if (($alpha <= 0) || ($beta <= 0) || ($probability <= 0) || ($probability > 1)) {
  3546. return self::$_errorCodes['num'];
  3547. }
  3548. $xLo = 0;
  3549. $xHi = 100;
  3550. $maxIteration = 100;
  3551. $x = $xNew = 1;
  3552. $dx = 1;
  3553. $i = 0;
  3554. while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
  3555. // Apply Newton-Raphson step
  3556. $result = self::GAMMADIST($x, $alpha, $beta, True);
  3557. $error = $result - $probability;
  3558. if ($error == 0.0) {
  3559. $dx = 0;
  3560. } elseif ($error < 0.0) {
  3561. $xLo = $x;
  3562. } else {
  3563. $xHi = $x;
  3564. }
  3565. // Avoid division by zero
  3566. if ($result != 0.0) {
  3567. $dx = $error / $result;
  3568. $xNew = $x - $dx;
  3569. }
  3570. // If the NR fails to converge (which for example may be the
  3571. // case if the initial guess is too rough) we apply a bisection
  3572. // step to determine a more narrow interval around the root.
  3573. if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
  3574. $xNew = ($xLo + $xHi) / 2;
  3575. $dx = $xNew - $x;
  3576. }
  3577. $x = $xNew;
  3578. }
  3579. if ($i == MAX_ITERATIONS) {
  3580. return self::$_errorCodes['na'];
  3581. }
  3582. return round($x,12);
  3583. }
  3584. return self::$_errorCodes['value'];
  3585. } // function GAMMAINV()
  3586. /**
  3587. * GAMMALN
  3588. *
  3589. * Returns the natural logarithm of the gamma function.
  3590. *
  3591. * @param float $value
  3592. * @return float
  3593. */
  3594. public static function GAMMALN($value) {
  3595. $value = self::flattenSingleValue($value);
  3596. if (is_numeric($value)) {
  3597. if ($value <= 0) {
  3598. return self::$_errorCodes['num'];
  3599. }
  3600. return log(self::_gamma($value));
  3601. }
  3602. return self::$_errorCodes['value'];
  3603. } // function GAMMALN()
  3604. /**
  3605. * NORMDIST
  3606. *
  3607. * Returns the normal distribution for the specified mean and standard deviation. This
  3608. * function has a very wide range of applications in statistics, including hypothesis
  3609. * testing.
  3610. *
  3611. * @param float $value
  3612. * @param float $mean Mean Value
  3613. * @param float $stdDev Standard Deviation
  3614. * @param boolean $cumulative
  3615. * @return float
  3616. *
  3617. */
  3618. public static function NORMDIST($value, $mean, $stdDev, $cumulative) {
  3619. $value = self::flattenSingleValue($value);
  3620. $mean = self::flattenSingleValue($mean);
  3621. $stdDev = self::flattenSingleValue($stdDev);
  3622. if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  3623. if ($stdDev < 0) {
  3624. return self::$_errorCodes['num'];
  3625. }
  3626. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  3627. if ($cumulative) {
  3628. return 0.5 * (1 + self::_erfVal(($value - $mean) / ($stdDev * sqrt(2))));
  3629. } else {
  3630. return (1 / (SQRT2PI * $stdDev)) * exp(0 - (pow($value - $mean,2) / (2 * pow($stdDev,2))));
  3631. }
  3632. }
  3633. }
  3634. return self::$_errorCodes['value'];
  3635. } // function NORMDIST()
  3636. /**
  3637. * NORMSDIST
  3638. *
  3639. * Returns the standard normal cumulative distribution function. The distribution has
  3640. * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
  3641. * table of standard normal curve areas.
  3642. *
  3643. * @param float $value
  3644. * @return float
  3645. */
  3646. public static function NORMSDIST($value) {
  3647. $value = self::flattenSingleValue($value);
  3648. return self::NORMDIST($value, 0, 1, True);
  3649. } // function NORMSDIST()
  3650. /**
  3651. * LOGNORMDIST
  3652. *
  3653. * Returns the cumulative lognormal distribution of x, where ln(x) is normally distributed
  3654. * with parameters mean and standard_dev.
  3655. *
  3656. * @param float $value
  3657. * @return float
  3658. */
  3659. public static function LOGNORMDIST($value, $mean, $stdDev) {
  3660. $value = self::flattenSingleValue($value);
  3661. $mean = self::flattenSingleValue($mean);
  3662. $stdDev = self::flattenSingleValue($stdDev);
  3663. if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  3664. if (($value <= 0) || ($stdDev <= 0)) {
  3665. return self::$_errorCodes['num'];
  3666. }
  3667. return self::NORMSDIST((log($value) - $mean) / $stdDev);
  3668. }
  3669. return self::$_errorCodes['value'];
  3670. } // function LOGNORMDIST()
  3671. /***************************************************************************
  3672. * inverse_ncdf.php
  3673. * -------------------
  3674. * begin : Friday, January 16, 2004
  3675. * copyright : (C) 2004 Michael Nickerson
  3676. * email : nickersonm@yahoo.com
  3677. *
  3678. ***************************************************************************/
  3679. private static function _inverse_ncdf($p) {
  3680. // Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
  3681. // PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
  3682. // a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
  3683. // I have not checked the accuracy of this implementation. Be aware that PHP
  3684. // will truncate the coeficcients to 14 digits.
  3685. // You have permission to use and distribute this function freely for
  3686. // whatever purpose you want, but please show common courtesy and give credit
  3687. // where credit is due.
  3688. // Input paramater is $p - probability - where 0 < p < 1.
  3689. // Coefficients in rational approximations
  3690. static $a = array( 1 => -3.969683028665376e+01,
  3691. 2 => 2.209460984245205e+02,
  3692. 3 => -2.759285104469687e+02,
  3693. 4 => 1.383577518672690e+02,
  3694. 5 => -3.066479806614716e+01,
  3695. 6 => 2.506628277459239e+00
  3696. );
  3697. static $b = array( 1 => -5.447609879822406e+01,
  3698. 2 => 1.615858368580409e+02,
  3699. 3 => -1.556989798598866e+02,
  3700. 4 => 6.680131188771972e+01,
  3701. 5 => -1.328068155288572e+01
  3702. );
  3703. static $c = array( 1 => -7.784894002430293e-03,
  3704. 2 => -3.223964580411365e-01,
  3705. 3 => -2.400758277161838e+00,
  3706. 4 => -2.549732539343734e+00,
  3707. 5 => 4.374664141464968e+00,
  3708. 6 => 2.938163982698783e+00
  3709. );
  3710. static $d = array( 1 => 7.784695709041462e-03,
  3711. 2 => 3.224671290700398e-01,
  3712. 3 => 2.445134137142996e+00,
  3713. 4 => 3.754408661907416e+00
  3714. );
  3715. // Define lower and upper region break-points.
  3716. $p_low = 0.02425; //Use lower region approx. below this
  3717. $p_high = 1 - $p_low; //Use upper region approx. above this
  3718. if (0 < $p && $p < $p_low) {
  3719. // Rational approximation for lower region.
  3720. $q = sqrt(-2 * log($p));
  3721. return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
  3722. (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
  3723. } elseif ($p_low <= $p && $p <= $p_high) {
  3724. // Rational approximation for central region.
  3725. $q = $p - 0.5;
  3726. $r = $q * $q;
  3727. return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
  3728. ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
  3729. } elseif ($p_high < $p && $p < 1) {
  3730. // Rational approximation for upper region.
  3731. $q = sqrt(-2 * log(1 - $p));
  3732. return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
  3733. (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
  3734. }
  3735. // If 0 < p < 1, return a null value
  3736. return self::$_errorCodes['null'];
  3737. } // function _inverse_ncdf()
  3738. private static function _inverse_ncdf2($prob) {
  3739. // Approximation of inverse standard normal CDF developed by
  3740. // B. Moro, "The Full Monte," Risk 8(2), Feb 1995, 57-58.
  3741. $a1 = 2.50662823884;
  3742. $a2 = -18.61500062529;
  3743. $a3 = 41.39119773534;
  3744. $a4 = -25.44106049637;
  3745. $b1 = -8.4735109309;
  3746. $b2 = 23.08336743743;
  3747. $b3 = -21.06224101826;
  3748. $b4 = 3.13082909833;
  3749. $c1 = 0.337475482272615;
  3750. $c2 = 0.976169019091719;
  3751. $c3 = 0.160797971491821;
  3752. $c4 = 2.76438810333863E-02;
  3753. $c5 = 3.8405729373609E-03;
  3754. $c6 = 3.951896511919E-04;
  3755. $c7 = 3.21767881768E-05;
  3756. $c8 = 2.888167364E-07;
  3757. $c9 = 3.960315187E-07;
  3758. $y = $prob - 0.5;
  3759. if (abs($y) < 0.42) {
  3760. $z = pow($y,2);
  3761. $z = $y * ((($a4 * $z + $a3) * $z + $a2) * $z + $a1) / (((($b4 * $z + $b3) * $z + $b2) * $z + $b1) * $z + 1);
  3762. } else {
  3763. if ($y > 0) {
  3764. $z = log(-log(1 - $prob));
  3765. } else {
  3766. $z = log(-log($prob));
  3767. }
  3768. $z = $c1 + $z * ($c2 + $z * ($c3 + $z * ($c4 + $z * ($c5 + $z * ($c6 + $z * ($c7 + $z * ($c8 + $z * $c9)))))));
  3769. if ($y < 0) {
  3770. $z = -$z;
  3771. }
  3772. }
  3773. return $z;
  3774. } // function _inverse_ncdf2()
  3775. private static function _inverse_ncdf3($p) {
  3776. // ALGORITHM AS241 APPL. STATIST. (1988) VOL. 37, NO. 3.
  3777. // Produces the normal deviate Z corresponding to a given lower
  3778. // tail area of P; Z is accurate to about 1 part in 10**16.
  3779. //
  3780. // This is a PHP version of the original FORTRAN code that can
  3781. // be found at http://lib.stat.cmu.edu/apstat/
  3782. $split1 = 0.425;
  3783. $split2 = 5;
  3784. $const1 = 0.180625;
  3785. $const2 = 1.6;
  3786. // coefficients for p close to 0.5
  3787. $a0 = 3.3871328727963666080;
  3788. $a1 = 1.3314166789178437745E+2;
  3789. $a2 = 1.9715909503065514427E+3;
  3790. $a3 = 1.3731693765509461125E+4;
  3791. $a4 = 4.5921953931549871457E+4;
  3792. $a5 = 6.7265770927008700853E+4;
  3793. $a6 = 3.3430575583588128105E+4;
  3794. $a7 = 2.5090809287301226727E+3;
  3795. $b1 = 4.2313330701600911252E+1;
  3796. $b2 = 6.8718700749205790830E+2;
  3797. $b3 = 5.3941960214247511077E+3;
  3798. $b4 = 2.1213794301586595867E+4;
  3799. $b5 = 3.9307895800092710610E+4;
  3800. $b6 = 2.8729085735721942674E+4;
  3801. $b7 = 5.2264952788528545610E+3;
  3802. // coefficients for p not close to 0, 0.5 or 1.
  3803. $c0 = 1.42343711074968357734;
  3804. $c1 = 4.63033784615654529590;
  3805. $c2 = 5.76949722146069140550;
  3806. $c3 = 3.64784832476320460504;
  3807. $c4 = 1.27045825245236838258;
  3808. $c5 = 2.41780725177450611770E-1;
  3809. $c6 = 2.27238449892691845833E-2;
  3810. $c7 = 7.74545014278341407640E-4;
  3811. $d1 = 2.05319162663775882187;
  3812. $d2 = 1.67638483018380384940;
  3813. $d3 = 6.89767334985100004550E-1;
  3814. $d4 = 1.48103976427480074590E-1;
  3815. $d5 = 1.51986665636164571966E-2;
  3816. $d6 = 5.47593808499534494600E-4;
  3817. $d7 = 1.05075007164441684324E-9;
  3818. // coefficients for p near 0 or 1.
  3819. $e0 = 6.65790464350110377720;
  3820. $e1 = 5.46378491116411436990;
  3821. $e2 = 1.78482653991729133580;
  3822. $e3 = 2.96560571828504891230E-1;
  3823. $e4 = 2.65321895265761230930E-2;
  3824. $e5 = 1.24266094738807843860E-3;
  3825. $e6 = 2.71155556874348757815E-5;
  3826. $e7 = 2.01033439929228813265E-7;
  3827. $f1 = 5.99832206555887937690E-1;
  3828. $f2 = 1.36929880922735805310E-1;
  3829. $f3 = 1.48753612908506148525E-2;
  3830. $f4 = 7.86869131145613259100E-4;
  3831. $f5 = 1.84631831751005468180E-5;
  3832. $f6 = 1.42151175831644588870E-7;
  3833. $f7 = 2.04426310338993978564E-15;
  3834. $q = $p - 0.5;
  3835. // computation for p close to 0.5
  3836. if (abs($q) <= split1) {
  3837. $R = $const1 - $q * $q;
  3838. $z = $q * ((((((($a7 * $R + $a6) * $R + $a5) * $R + $a4) * $R + $a3) * $R + $a2) * $R + $a1) * $R + $a0) /
  3839. ((((((($b7 * $R + $b6) * $R + $b5) * $R + $b4) * $R + $b3) * $R + $b2) * $R + $b1) * $R + 1);
  3840. } else {
  3841. if ($q < 0) {
  3842. $R = $p;
  3843. } else {
  3844. $R = 1 - $p;
  3845. }
  3846. $R = pow(-log($R),2);
  3847. // computation for p not close to 0, 0.5 or 1.
  3848. If ($R <= $split2) {
  3849. $R = $R - $const2;
  3850. $z = ((((((($c7 * $R + $c6) * $R + $c5) * $R + $c4) * $R + $c3) * $R + $c2) * $R + $c1) * $R + $c0) /
  3851. ((((((($d7 * $R + $d6) * $R + $d5) * $R + $d4) * $R + $d3) * $R + $d2) * $R + $d1) * $R + 1);
  3852. } else {
  3853. // computation for p near 0 or 1.
  3854. $R = $R - $split2;
  3855. $z = ((((((($e7 * $R + $e6) * $R + $e5) * $R + $e4) * $R + $e3) * $R + $e2) * $R + $e1) * $R + $e0) /
  3856. ((((((($f7 * $R + $f6) * $R + $f5) * $R + $f4) * $R + $f3) * $R + $f2) * $R + $f1) * $R + 1);
  3857. }
  3858. if ($q < 0) {
  3859. $z = -$z;
  3860. }
  3861. }
  3862. return $z;
  3863. } // function _inverse_ncdf3()
  3864. /**
  3865. * NORMINV
  3866. *
  3867. * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
  3868. *
  3869. * @param float $value
  3870. * @param float $mean Mean Value
  3871. * @param float $stdDev Standard Deviation
  3872. * @return float
  3873. *
  3874. */
  3875. public static function NORMINV($probability,$mean,$stdDev) {
  3876. $probability = self::flattenSingleValue($probability);
  3877. $mean = self::flattenSingleValue($mean);
  3878. $stdDev = self::flattenSingleValue($stdDev);
  3879. if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  3880. if (($probability < 0) || ($probability > 1)) {
  3881. return self::$_errorCodes['num'];
  3882. }
  3883. if ($stdDev < 0) {
  3884. return self::$_errorCodes['num'];
  3885. }
  3886. return (self::_inverse_ncdf($probability) * $stdDev) + $mean;
  3887. }
  3888. return self::$_errorCodes['value'];
  3889. } // function NORMINV()
  3890. /**
  3891. * NORMSINV
  3892. *
  3893. * Returns the inverse of the standard normal cumulative distribution
  3894. *
  3895. * @param float $value
  3896. * @return float
  3897. */
  3898. public static function NORMSINV($value) {
  3899. return self::NORMINV($value, 0, 1);
  3900. } // function NORMSINV()
  3901. /**
  3902. * LOGINV
  3903. *
  3904. * Returns the inverse of the normal cumulative distribution
  3905. *
  3906. * @param float $value
  3907. * @return float
  3908. *
  3909. * @todo Try implementing P J Acklam's refinement algorithm for greater
  3910. * accuracy if I can get my head round the mathematics
  3911. * (as described at) http://home.online.no/~pjacklam/notes/invnorm/
  3912. */
  3913. public static function LOGINV($probability, $mean, $stdDev) {
  3914. $probability = self::flattenSingleValue($probability);
  3915. $mean = self::flattenSingleValue($mean);
  3916. $stdDev = self::flattenSingleValue($stdDev);
  3917. if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  3918. if (($probability < 0) || ($probability > 1) || ($stdDev <= 0)) {
  3919. return self::$_errorCodes['num'];
  3920. }
  3921. return exp($mean + $stdDev * self::NORMSINV($probability));
  3922. }
  3923. return self::$_errorCodes['value'];
  3924. } // function LOGINV()
  3925. /**
  3926. * HYPGEOMDIST
  3927. *
  3928. * Returns the hypergeometric distribution. HYPGEOMDIST returns the probability of a given number of
  3929. * sample successes, given the sample size, population successes, and population size.
  3930. *
  3931. * @param float $sampleSuccesses Number of successes in the sample
  3932. * @param float $sampleNumber Size of the sample
  3933. * @param float $populationSuccesses Number of successes in the population
  3934. * @param float $populationNumber Population size
  3935. * @return float
  3936. *
  3937. */
  3938. public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber) {
  3939. $sampleSuccesses = floor(self::flattenSingleValue($sampleSuccesses));
  3940. $sampleNumber = floor(self::flattenSingleValue($sampleNumber));
  3941. $populationSuccesses = floor(self::flattenSingleValue($populationSuccesses));
  3942. $populationNumber = floor(self::flattenSingleValue($populationNumber));
  3943. if ((is_numeric($sampleSuccesses)) && (is_numeric($sampleNumber)) && (is_numeric($populationSuccesses)) && (is_numeric($populationNumber))) {
  3944. if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
  3945. return self::$_errorCodes['num'];
  3946. }
  3947. if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
  3948. return self::$_errorCodes['num'];
  3949. }
  3950. if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
  3951. return self::$_errorCodes['num'];
  3952. }
  3953. return self::COMBIN($populationSuccesses,$sampleSuccesses) *
  3954. self::COMBIN($populationNumber - $populationSuccesses,$sampleNumber - $sampleSuccesses) /
  3955. self::COMBIN($populationNumber,$sampleNumber);
  3956. }
  3957. return self::$_errorCodes['value'];
  3958. } // function HYPGEOMDIST()
  3959. /**
  3960. * TDIST
  3961. *
  3962. * Returns the probability of Student's T distribution.
  3963. *
  3964. * @param float $value Value for the function
  3965. * @param float $degrees degrees of freedom
  3966. * @param float $tails number of tails (1 or 2)
  3967. * @return float
  3968. */
  3969. public static function TDIST($value, $degrees, $tails) {
  3970. $value = self::flattenSingleValue($value);
  3971. $degrees = floor(self::flattenSingleValue($degrees));
  3972. $tails = floor(self::flattenSingleValue($tails));
  3973. if ((is_numeric($value)) && (is_numeric($degrees)) && (is_numeric($tails))) {
  3974. if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
  3975. return self::$_errorCodes['num'];
  3976. }
  3977. // tdist, which finds the probability that corresponds to a given value
  3978. // of t with k degrees of freedom. This algorithm is translated from a
  3979. // pascal function on p81 of "Statistical Computing in Pascal" by D
  3980. // Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
  3981. // London). The above Pascal algorithm is itself a translation of the
  3982. // fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
  3983. // Laboratory as reported in (among other places) "Applied Statistics
  3984. // Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
  3985. // Horwood Ltd.; W. Sussex, England).
  3986. // $ta = 2 / pi();
  3987. $ta = 0.636619772367581;
  3988. $tterm = $degrees;
  3989. $ttheta = atan2($value,sqrt($tterm));
  3990. $tc = cos($ttheta);
  3991. $ts = sin($ttheta);
  3992. $tsum = 0;
  3993. if (($degrees % 2) == 1) {
  3994. $ti = 3;
  3995. $tterm = $tc;
  3996. } else {
  3997. $ti = 2;
  3998. $tterm = 1;
  3999. }
  4000. $tsum = $tterm;
  4001. while ($ti < $degrees) {
  4002. $tterm *= $tc * $tc * ($ti - 1) / $ti;
  4003. $tsum += $tterm;
  4004. $ti += 2;
  4005. }
  4006. $tsum *= $ts;
  4007. if (($degrees % 2) == 1) { $tsum = $ta * ($tsum + $ttheta); }
  4008. $tValue = 0.5 * (1 + $tsum);
  4009. if ($tails == 1) {
  4010. return 1 - abs($tValue);
  4011. } else {
  4012. return 1 - abs((1 - $tValue) - $tValue);
  4013. }
  4014. }
  4015. return self::$_errorCodes['value'];
  4016. } // function TDIST()
  4017. /**
  4018. * TINV
  4019. *
  4020. * Returns the one-tailed probability of the chi-squared distribution.
  4021. *
  4022. * @param float $probability Probability for the function
  4023. * @param float $degrees degrees of freedom
  4024. * @return float
  4025. */
  4026. public static function TINV($probability, $degrees) {
  4027. $probability = self::flattenSingleValue($probability);
  4028. $degrees = floor(self::flattenSingleValue($degrees));
  4029. if ((is_numeric($probability)) && (is_numeric($degrees))) {
  4030. $xLo = 100;
  4031. $xHi = 0;
  4032. $maxIteration = 100;
  4033. $x = $xNew = 1;
  4034. $dx = 1;
  4035. $i = 0;
  4036. while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
  4037. // Apply Newton-Raphson step
  4038. $result = self::TDIST($x, $degrees, 2);
  4039. $error = $result - $probability;
  4040. if ($error == 0.0) {
  4041. $dx = 0;
  4042. } elseif ($error < 0.0) {
  4043. $xLo = $x;
  4044. } else {
  4045. $xHi = $x;
  4046. }
  4047. // Avoid division by zero
  4048. if ($result != 0.0) {
  4049. $dx = $error / $result;
  4050. $xNew = $x - $dx;
  4051. }
  4052. // If the NR fails to converge (which for example may be the
  4053. // case if the initial guess is too rough) we apply a bisection
  4054. // step to determine a more narrow interval around the root.
  4055. if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
  4056. $xNew = ($xLo + $xHi) / 2;
  4057. $dx = $xNew - $x;
  4058. }
  4059. $x = $xNew;
  4060. }
  4061. if ($i == MAX_ITERATIONS) {
  4062. return self::$_errorCodes['na'];
  4063. }
  4064. return round($x,12);
  4065. }
  4066. return self::$_errorCodes['value'];
  4067. } // function TINV()
  4068. /**
  4069. * CONFIDENCE
  4070. *
  4071. * Returns the confidence interval for a population mean
  4072. *
  4073. * @param float $alpha
  4074. * @param float $stdDev Standard Deviation
  4075. * @param float $size
  4076. * @return float
  4077. *
  4078. */
  4079. public static function CONFIDENCE($alpha,$stdDev,$size) {
  4080. $alpha = self::flattenSingleValue($alpha);
  4081. $stdDev = self::flattenSingleValue($stdDev);
  4082. $size = floor(self::flattenSingleValue($size));
  4083. if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
  4084. if (($alpha <= 0) || ($alpha >= 1)) {
  4085. return self::$_errorCodes['num'];
  4086. }
  4087. if (($stdDev <= 0) || ($size < 1)) {
  4088. return self::$_errorCodes['num'];
  4089. }
  4090. return self::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size);
  4091. }
  4092. return self::$_errorCodes['value'];
  4093. } // function CONFIDENCE()
  4094. /**
  4095. * POISSON
  4096. *
  4097. * Returns the Poisson distribution. A common application of the Poisson distribution
  4098. * is predicting the number of events over a specific time, such as the number of
  4099. * cars arriving at a toll plaza in 1 minute.
  4100. *
  4101. * @param float $value
  4102. * @param float $mean Mean Value
  4103. * @param boolean $cumulative
  4104. * @return float
  4105. *
  4106. */
  4107. public static function POISSON($value, $mean, $cumulative) {
  4108. $value = self::flattenSingleValue($value);
  4109. $mean = self::flattenSingleValue($mean);
  4110. if ((is_numeric($value)) && (is_numeric($mean))) {
  4111. if (($value <= 0) || ($mean <= 0)) {
  4112. return self::$_errorCodes['num'];
  4113. }
  4114. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  4115. if ($cumulative) {
  4116. $summer = 0;
  4117. for ($i = 0; $i <= floor($value); ++$i) {
  4118. $summer += pow($mean,$i) / self::FACT($i);
  4119. }
  4120. return exp(0-$mean) * $summer;
  4121. } else {
  4122. return (exp(0-$mean) * pow($mean,$value)) / self::FACT($value);
  4123. }
  4124. }
  4125. }
  4126. return self::$_errorCodes['value'];
  4127. } // function POISSON()
  4128. /**
  4129. * WEIBULL
  4130. *
  4131. * Returns the Weibull distribution. Use this distribution in reliability
  4132. * analysis, such as calculating a device's mean time to failure.
  4133. *
  4134. * @param float $value
  4135. * @param float $alpha Alpha Parameter
  4136. * @param float $beta Beta Parameter
  4137. * @param boolean $cumulative
  4138. * @return float
  4139. *
  4140. */
  4141. public static function WEIBULL($value, $alpha, $beta, $cumulative) {
  4142. $value = self::flattenSingleValue($value);
  4143. $alpha = self::flattenSingleValue($alpha);
  4144. $beta = self::flattenSingleValue($beta);
  4145. if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta))) {
  4146. if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
  4147. return self::$_errorCodes['num'];
  4148. }
  4149. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  4150. if ($cumulative) {
  4151. return 1 - exp(0 - pow($value / $beta,$alpha));
  4152. } else {
  4153. return ($alpha / pow($beta,$alpha)) * pow($value,$alpha - 1) * exp(0 - pow($value / $beta,$alpha));
  4154. }
  4155. }
  4156. }
  4157. return self::$_errorCodes['value'];
  4158. } // function WEIBULL()
  4159. /**
  4160. * SKEW
  4161. *
  4162. * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
  4163. * of a distribution around its mean. Positive skewness indicates a distribution with an
  4164. * asymmetric tail extending toward more positive values. Negative skewness indicates a
  4165. * distribution with an asymmetric tail extending toward more negative values.
  4166. *
  4167. * @param array Data Series
  4168. * @return float
  4169. */
  4170. public static function SKEW() {
  4171. $aArgs = self::flattenArray(func_get_args());
  4172. $mean = self::AVERAGE($aArgs);
  4173. $stdDev = self::STDEV($aArgs);
  4174. $count = $summer = 0;
  4175. // Loop through arguments
  4176. foreach ($aArgs as $arg) {
  4177. // Is it a numeric value?
  4178. if ((is_numeric($arg)) && (!is_string($arg))) {
  4179. $summer += pow((($arg - $mean) / $stdDev),3) ;
  4180. ++$count;
  4181. }
  4182. }
  4183. // Return
  4184. if ($count > 2) {
  4185. return $summer * ($count / (($count-1) * ($count-2)));
  4186. }
  4187. return self::$_errorCodes['divisionbyzero'];
  4188. } // function SKEW()
  4189. /**
  4190. * KURT
  4191. *
  4192. * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
  4193. * or flatness of a distribution compared with the normal distribution. Positive
  4194. * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
  4195. * relatively flat distribution.
  4196. *
  4197. * @param array Data Series
  4198. * @return float
  4199. */
  4200. public static function KURT() {
  4201. $aArgs = self::flattenArray(func_get_args());
  4202. $mean = self::AVERAGE($aArgs);
  4203. $stdDev = self::STDEV($aArgs);
  4204. if ($stdDev > 0) {
  4205. $count = $summer = 0;
  4206. // Loop through arguments
  4207. foreach ($aArgs as $arg) {
  4208. // Is it a numeric value?
  4209. if ((is_numeric($arg)) && (!is_string($arg))) {
  4210. $summer += pow((($arg - $mean) / $stdDev),4) ;
  4211. ++$count;
  4212. }
  4213. }
  4214. // Return
  4215. if ($count > 3) {
  4216. return $summer * ($count * ($count+1) / (($count-1) * ($count-2) * ($count-3))) - (3 * pow($count-1,2) / (($count-2) * ($count-3)));
  4217. }
  4218. }
  4219. return self::$_errorCodes['divisionbyzero'];
  4220. } // function KURT()
  4221. /**
  4222. * RAND
  4223. *
  4224. * @param int $min Minimal value
  4225. * @param int $max Maximal value
  4226. * @return int Random number
  4227. */
  4228. public static function RAND($min = 0, $max = 0) {
  4229. $min = self::flattenSingleValue($min);
  4230. $max = self::flattenSingleValue($max);
  4231. if ($min == 0 && $max == 0) {
  4232. return (rand(0,10000000)) / 10000000;
  4233. } else {
  4234. return rand($min, $max);
  4235. }
  4236. } // function RAND()
  4237. /**
  4238. * MOD
  4239. *
  4240. * @param int $a Dividend
  4241. * @param int $b Divisor
  4242. * @return int Remainder
  4243. */
  4244. public static function MOD($a = 1, $b = 1) {
  4245. $a = self::flattenSingleValue($a);
  4246. $b = self::flattenSingleValue($b);
  4247. return fmod($a,$b);
  4248. } // function MOD()
  4249. /**
  4250. * CHARACTER
  4251. *
  4252. * @param string $character Value
  4253. * @return int
  4254. */
  4255. public static function CHARACTER($character) {
  4256. $character = self::flattenSingleValue($character);
  4257. if (function_exists('mb_convert_encoding')) {
  4258. return mb_convert_encoding('&#'.intval($character).';', 'UTF-8', 'HTML-ENTITIES');
  4259. } else {
  4260. return chr(intval($character));
  4261. }
  4262. }
  4263. /**
  4264. * ASCIICODE
  4265. *
  4266. * @param string $character Value
  4267. * @return int
  4268. */
  4269. public static function ASCIICODE($characters) {
  4270. $characters = self::flattenSingleValue($characters);
  4271. if (is_bool($characters)) {
  4272. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  4273. $characters = (int) $characters;
  4274. } else {
  4275. if ($characters) {
  4276. $characters = 'True';
  4277. } else {
  4278. $characters = 'False';
  4279. }
  4280. }
  4281. }
  4282. if ((function_exists('mb_strlen')) && (function_exists('mb_substr'))) {
  4283. if (mb_strlen($characters, 'UTF-8') > 0) {
  4284. $character = mb_substr($characters, 0, 1, 'UTF-8');
  4285. $byteLength = strlen($character);
  4286. $xValue = 0;
  4287. for ($i = 0; $i < $byteLength; ++$i) {
  4288. $xValue = ($xValue * 256) + ord($character{$i});
  4289. }
  4290. return $xValue;
  4291. }
  4292. } else {
  4293. if (strlen($characters) > 0) {
  4294. return ord(substr($characters, 0, 1));
  4295. }
  4296. }
  4297. return self::$_errorCodes['value'];
  4298. } // function ASCIICODE()
  4299. /**
  4300. * CONCATENATE
  4301. *
  4302. * @return string
  4303. */
  4304. public static function CONCATENATE() {
  4305. // Return value
  4306. $returnValue = '';
  4307. // Loop trough arguments
  4308. $aArgs = self::flattenArray(func_get_args());
  4309. foreach ($aArgs as $arg) {
  4310. if (is_bool($arg)) {
  4311. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  4312. $arg = (int) $arg;
  4313. } else {
  4314. if ($arg) {
  4315. $arg = 'TRUE';
  4316. } else {
  4317. $arg = 'FALSE';
  4318. }
  4319. }
  4320. }
  4321. $returnValue .= $arg;
  4322. }
  4323. // Return
  4324. return $returnValue;
  4325. } // function CONCATENATE()
  4326. /**
  4327. * STRINGLENGTH
  4328. *
  4329. * @param string $value Value
  4330. * @param int $chars Number of characters
  4331. * @return string
  4332. */
  4333. public static function STRINGLENGTH($value = '') {
  4334. $value = self::flattenSingleValue($value);
  4335. if (function_exists('mb_strlen')) {
  4336. return mb_strlen($value, 'UTF-8');
  4337. } else {
  4338. return strlen($value);
  4339. }
  4340. } // function STRINGLENGTH()
  4341. /**
  4342. * SEARCHSENSITIVE
  4343. *
  4344. * @param string $needle The string to look for
  4345. * @param string $haystack The string in which to look
  4346. * @param int $offset Offset within $haystack
  4347. * @return string
  4348. */
  4349. public static function SEARCHSENSITIVE($needle,$haystack,$offset=1) {
  4350. $needle = (string) self::flattenSingleValue($needle);
  4351. $haystack = (string) self::flattenSingleValue($haystack);
  4352. $offset = self::flattenSingleValue($offset);
  4353. if (($offset > 0) && (strlen($haystack) > $offset)) {
  4354. if (function_exists('mb_strpos')) {
  4355. $pos = mb_strpos($haystack, $needle, --$offset,'UTF-8');
  4356. } else {
  4357. $pos = strpos($haystack, $needle, --$offset);
  4358. }
  4359. if ($pos !== false) {
  4360. return ++$pos;
  4361. }
  4362. }
  4363. return self::$_errorCodes['value'];
  4364. } // function SEARCHSENSITIVE()
  4365. /**
  4366. * SEARCHINSENSITIVE
  4367. *
  4368. * @param string $needle The string to look for
  4369. * @param string $haystack The string in which to look
  4370. * @param int $offset Offset within $haystack
  4371. * @return string
  4372. */
  4373. public static function SEARCHINSENSITIVE($needle,$haystack,$offset=1) {
  4374. $needle = (string) self::flattenSingleValue($needle);
  4375. $haystack = (string) self::flattenSingleValue($haystack);
  4376. $offset = self::flattenSingleValue($offset);
  4377. if (($offset > 0) && (strlen($haystack) > $offset)) {
  4378. if (function_exists('mb_stripos')) {
  4379. $pos = mb_stripos($haystack, $needle, --$offset,'UTF-8');
  4380. } else {
  4381. $pos = stripos($haystack, $needle, --$offset);
  4382. }
  4383. if ($pos !== false) {
  4384. return ++$pos;
  4385. }
  4386. }
  4387. return self::$_errorCodes['value'];
  4388. } // function SEARCHINSENSITIVE()
  4389. /**
  4390. * LEFT
  4391. *
  4392. * @param string $value Value
  4393. * @param int $chars Number of characters
  4394. * @return string
  4395. */
  4396. public static function LEFT($value = '', $chars = 1) {
  4397. $value = self::flattenSingleValue($value);
  4398. $chars = self::flattenSingleValue($chars);
  4399. if (function_exists('mb_substr')) {
  4400. return mb_substr($value, 0, $chars, 'UTF-8');
  4401. } else {
  4402. return substr($value, 0, $chars);
  4403. }
  4404. } // function LEFT()
  4405. /**
  4406. * RIGHT
  4407. *
  4408. * @param string $value Value
  4409. * @param int $chars Number of characters
  4410. * @return string
  4411. */
  4412. public static function RIGHT($value = '', $chars = 1) {
  4413. $value = self::flattenSingleValue($value);
  4414. $chars = self::flattenSingleValue($chars);
  4415. if ((function_exists('mb_substr')) && (function_exists('mb_strlen'))) {
  4416. return mb_substr($value, mb_strlen($value, 'UTF-8') - $chars, $chars, 'UTF-8');
  4417. } else {
  4418. return substr($value, strlen($value) - $chars);
  4419. }
  4420. } // function RIGHT()
  4421. /**
  4422. * MID
  4423. *
  4424. * @param string $value Value
  4425. * @param int $start Start character
  4426. * @param int $chars Number of characters
  4427. * @return string
  4428. */
  4429. public static function MID($value = '', $start = 1, $chars = null) {
  4430. $value = self::flattenSingleValue($value);
  4431. $start = self::flattenSingleValue($start);
  4432. $chars = self::flattenSingleValue($chars);
  4433. if (function_exists('mb_substr')) {
  4434. return mb_substr($value, --$start, $chars, 'UTF-8');
  4435. } else {
  4436. return substr($value, --$start, $chars);
  4437. }
  4438. } // function MID()
  4439. /**
  4440. * REPLACE
  4441. *
  4442. * @param string $value Value
  4443. * @param int $start Start character
  4444. * @param int $chars Number of characters
  4445. * @return string
  4446. */
  4447. public static function REPLACE($oldText = '', $start = 1, $chars = null, $newText) {
  4448. $oldText = self::flattenSingleValue($oldText);
  4449. $start = self::flattenSingleValue($start);
  4450. $chars = self::flattenSingleValue($chars);
  4451. $newText = self::flattenSingleValue($newText);
  4452. $left = self::LEFT($oldText,$start-1);
  4453. $right = self::RIGHT($oldText,self::STRINGLENGTH($oldText)-($start+$chars)+1);
  4454. return $left.$newText.$right;
  4455. } // function REPLACE()
  4456. /**
  4457. * RETURNSTRING
  4458. *
  4459. * @param mixed $value Value to check
  4460. * @return boolean
  4461. */
  4462. public static function RETURNSTRING($testValue = '') {
  4463. $testValue = self::flattenSingleValue($testValue);
  4464. if (is_string($testValue)) {
  4465. return $testValue;
  4466. }
  4467. return Null;
  4468. } // function RETURNSTRING()
  4469. /**
  4470. * FIXEDFORMAT
  4471. *
  4472. * @param mixed $value Value to check
  4473. * @return boolean
  4474. */
  4475. public static function FIXEDFORMAT($value,$decimals=2,$no_commas=false) {
  4476. $value = self::flattenSingleValue($value);
  4477. $decimals = self::flattenSingleValue($decimals);
  4478. $no_commas = self::flattenSingleValue($no_commas);
  4479. $valueResult = round($value,$decimals);
  4480. if ($decimals < 0) { $decimals = 0; }
  4481. if (!$no_commas) {
  4482. $valueResult = number_format($valueResult,$decimals);
  4483. }
  4484. return (string) $valueResult;
  4485. } // function FIXEDFORMAT()
  4486. /**
  4487. * TEXTFORMAT
  4488. *
  4489. * @param mixed $value Value to check
  4490. * @return boolean
  4491. */
  4492. public static function TEXTFORMAT($value,$format) {
  4493. $value = self::flattenSingleValue($value);
  4494. $format = self::flattenSingleValue($format);
  4495. return (string) PHPExcel_Style_NumberFormat::toFormattedString($value,$format);
  4496. } // function TEXTFORMAT()
  4497. /**
  4498. * TRIMSPACES
  4499. *
  4500. * @param mixed $value Value to check
  4501. * @return string
  4502. */
  4503. public static function TRIMSPACES($stringValue = '') {
  4504. $stringValue = self::flattenSingleValue($stringValue);
  4505. if (is_string($stringValue) || is_numeric($stringValue)) {
  4506. return trim(preg_replace('/ +/',' ',$stringValue));
  4507. }
  4508. return Null;
  4509. } // function TRIMSPACES()
  4510. private static $_invalidChars = Null;
  4511. /**
  4512. * TRIMNONPRINTABLE
  4513. *
  4514. * @param mixed $value Value to check
  4515. * @return string
  4516. */
  4517. public static function TRIMNONPRINTABLE($stringValue = '') {
  4518. $stringValue = self::flattenSingleValue($stringValue);
  4519. if (self::$_invalidChars == Null) {
  4520. self::$_invalidChars = range(chr(0),chr(31));
  4521. }
  4522. if (is_string($stringValue) || is_numeric($stringValue)) {
  4523. return str_replace(self::$_invalidChars,'',trim($stringValue,"\x00..\x1F"));
  4524. }
  4525. return Null;
  4526. } // function TRIMNONPRINTABLE()
  4527. /**
  4528. * ERROR_TYPE
  4529. *
  4530. * @param mixed $value Value to check
  4531. * @return boolean
  4532. */
  4533. public static function ERROR_TYPE($value = '') {
  4534. $value = self::flattenSingleValue($value);
  4535. $i = 1;
  4536. foreach(self::$_errorCodes as $errorCode) {
  4537. if ($value == $errorCode) {
  4538. return $i;
  4539. }
  4540. ++$i;
  4541. }
  4542. return self::$_errorCodes['na'];
  4543. } // function ERROR_TYPE()
  4544. /**
  4545. * IS_BLANK
  4546. *
  4547. * @param mixed $value Value to check
  4548. * @return boolean
  4549. */
  4550. public static function IS_BLANK($value = null) {
  4551. if (!is_null($value)) {
  4552. $value = self::flattenSingleValue($value);
  4553. }
  4554. return is_null($value);
  4555. } // function IS_BLANK()
  4556. /**
  4557. * IS_ERR
  4558. *
  4559. * @param mixed $value Value to check
  4560. * @return boolean
  4561. */
  4562. public static function IS_ERR($value = '') {
  4563. $value = self::flattenSingleValue($value);
  4564. return self::IS_ERROR($value) && (!self::IS_NA($value));
  4565. } // function IS_ERR()
  4566. /**
  4567. * IS_ERROR
  4568. *
  4569. * @param mixed $value Value to check
  4570. * @return boolean
  4571. */
  4572. public static function IS_ERROR($value = '') {
  4573. $value = self::flattenSingleValue($value);
  4574. return in_array($value, array_values(self::$_errorCodes));
  4575. } // function IS_ERROR()
  4576. /**
  4577. * IS_NA
  4578. *
  4579. * @param mixed $value Value to check
  4580. * @return boolean
  4581. */
  4582. public static function IS_NA($value = '') {
  4583. $value = self::flattenSingleValue($value);
  4584. return ($value === self::$_errorCodes['na']);
  4585. } // function IS_NA()
  4586. /**
  4587. * IS_EVEN
  4588. *
  4589. * @param mixed $value Value to check
  4590. * @return boolean
  4591. */
  4592. public static function IS_EVEN($value = 0) {
  4593. $value = self::flattenSingleValue($value);
  4594. if ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
  4595. return self::$_errorCodes['value'];
  4596. }
  4597. return ($value % 2 == 0);
  4598. } // function IS_EVEN()
  4599. /**
  4600. * IS_ODD
  4601. *
  4602. * @param mixed $value Value to check
  4603. * @return boolean
  4604. */
  4605. public static function IS_ODD($value = null) {
  4606. $value = self::flattenSingleValue($value);
  4607. if ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
  4608. return self::$_errorCodes['value'];
  4609. }
  4610. return (abs($value) % 2 == 1);
  4611. } // function IS_ODD()
  4612. /**
  4613. * IS_NUMBER
  4614. *
  4615. * @param mixed $value Value to check
  4616. * @return boolean
  4617. */
  4618. public static function IS_NUMBER($value = 0) {
  4619. $value = self::flattenSingleValue($value);
  4620. if (is_string($value)) {
  4621. return False;
  4622. }
  4623. return is_numeric($value);
  4624. } // function IS_NUMBER()
  4625. /**
  4626. * IS_LOGICAL
  4627. *
  4628. * @param mixed $value Value to check
  4629. * @return boolean
  4630. */
  4631. public static function IS_LOGICAL($value = true) {
  4632. $value = self::flattenSingleValue($value);
  4633. return is_bool($value);
  4634. } // function IS_LOGICAL()
  4635. /**
  4636. * IS_TEXT
  4637. *
  4638. * @param mixed $value Value to check
  4639. * @return boolean
  4640. */
  4641. public static function IS_TEXT($value = '') {
  4642. $value = self::flattenSingleValue($value);
  4643. return is_string($value);
  4644. } // function IS_TEXT()
  4645. /**
  4646. * IS_NONTEXT
  4647. *
  4648. * @param mixed $value Value to check
  4649. * @return boolean
  4650. */
  4651. public static function IS_NONTEXT($value = '') {
  4652. return !self::IS_TEXT($value);
  4653. } // function IS_NONTEXT()
  4654. /**
  4655. * VERSION
  4656. *
  4657. * @return string Version information
  4658. */
  4659. public static function VERSION() {
  4660. return 'PHPExcel 1.7.0, 2009-08-10';
  4661. } // function VERSION()
  4662. /**
  4663. * DATE
  4664. *
  4665. * @param long $year
  4666. * @param long $month
  4667. * @param long $day
  4668. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4669. * depending on the value of the ReturnDateType flag
  4670. */
  4671. public static function DATE($year = 0, $month = 1, $day = 1) {
  4672. $year = (integer) self::flattenSingleValue($year);
  4673. $month = (integer) self::flattenSingleValue($month);
  4674. $day = (integer) self::flattenSingleValue($day);
  4675. $baseYear = PHPExcel_Shared_Date::getExcelCalendar();
  4676. // Validate parameters
  4677. if ($year < ($baseYear-1900)) {
  4678. return self::$_errorCodes['num'];
  4679. }
  4680. if ((($baseYear-1900) != 0) && ($year < $baseYear) && ($year >= 1900)) {
  4681. return self::$_errorCodes['num'];
  4682. }
  4683. if (($year < $baseYear) && ($year >= ($baseYear-1900))) {
  4684. $year += 1900;
  4685. }
  4686. if ($month < 1) {
  4687. // Handle year/month adjustment if month < 1
  4688. --$month;
  4689. $year += ceil($month / 12) - 1;
  4690. $month = 13 - abs($month % 12);
  4691. } elseif ($month > 12) {
  4692. // Handle year/month adjustment if month > 12
  4693. $year += floor($month / 12);
  4694. $month = ($month % 12);
  4695. }
  4696. // Re-validate the year parameter after adjustments
  4697. if (($year < $baseYear) || ($year >= 10000)) {
  4698. return self::$_errorCodes['num'];
  4699. }
  4700. // Execute function
  4701. $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel($year, $month, $day);
  4702. switch (self::getReturnDateType()) {
  4703. case self::RETURNDATE_EXCEL : return (float) $excelDateValue;
  4704. break;
  4705. case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
  4706. break;
  4707. case self::RETURNDATE_PHP_OBJECT : return PHPExcel_Shared_Date::ExcelToPHPObject($excelDateValue);
  4708. break;
  4709. }
  4710. } // function DATE()
  4711. /**
  4712. * TIME
  4713. *
  4714. * @param long $hour
  4715. * @param long $minute
  4716. * @param long $second
  4717. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4718. * depending on the value of the ReturnDateType flag
  4719. */
  4720. public static function TIME($hour = 0, $minute = 0, $second = 0) {
  4721. $hour = self::flattenSingleValue($hour);
  4722. $minute = self::flattenSingleValue($minute);
  4723. $second = self::flattenSingleValue($second);
  4724. if ($hour == '') { $hour = 0; }
  4725. if ($minute == '') { $minute = 0; }
  4726. if ($second == '') { $second = 0; }
  4727. if ((!is_numeric($hour)) || (!is_numeric($minute)) || (!is_numeric($second))) {
  4728. return self::$_errorCodes['value'];
  4729. }
  4730. $hour = (integer) $hour;
  4731. $minute = (integer) $minute;
  4732. $second = (integer) $second;
  4733. if ($second < 0) {
  4734. $minute += floor($second / 60);
  4735. $second = 60 - abs($second % 60);
  4736. if ($second == 60) { $second = 0; }
  4737. } elseif ($second >= 60) {
  4738. $minute += floor($second / 60);
  4739. $second = $second % 60;
  4740. }
  4741. if ($minute < 0) {
  4742. $hour += floor($minute / 60);
  4743. $minute = 60 - abs($minute % 60);
  4744. if ($minute == 60) { $minute = 0; }
  4745. } elseif ($minute >= 60) {
  4746. $hour += floor($minute / 60);
  4747. $minute = $minute % 60;
  4748. }
  4749. if ($hour > 23) {
  4750. $hour = $hour % 24;
  4751. } elseif ($hour < 0) {
  4752. return self::$_errorCodes['num'];
  4753. }
  4754. // Execute function
  4755. switch (self::getReturnDateType()) {
  4756. case self::RETURNDATE_EXCEL : $date = 0;
  4757. $calendar = PHPExcel_Shared_Date::getExcelCalendar();
  4758. if ($calendar != PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900) {
  4759. $date = 1;
  4760. }
  4761. return (float) PHPExcel_Shared_Date::FormattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
  4762. break;
  4763. case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::FormattedPHPToExcel(1970, 1, 1, $hour-1, $minute, $second)); // -2147468400; // -2147472000 + 3600
  4764. break;
  4765. case self::RETURNDATE_PHP_OBJECT : $dayAdjust = 0;
  4766. if ($hour < 0) {
  4767. $dayAdjust = floor($hour / 24);
  4768. $hour = 24 - abs($hour % 24);
  4769. if ($hour == 24) { $hour = 0; }
  4770. } elseif ($hour >= 24) {
  4771. $dayAdjust = floor($hour / 24);
  4772. $hour = $hour % 24;
  4773. }
  4774. $phpDateObject = new DateTime('1900-01-01 '.$hour.':'.$minute.':'.$second);
  4775. if ($dayAdjust != 0) {
  4776. $phpDateObject->modify($dayAdjust.' days');
  4777. }
  4778. return $phpDateObject;
  4779. break;
  4780. }
  4781. } // function TIME()
  4782. /**
  4783. * DATEVALUE
  4784. *
  4785. * @param string $dateValue
  4786. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4787. * depending on the value of the ReturnDateType flag
  4788. */
  4789. public static function DATEVALUE($dateValue = 1) {
  4790. $dateValue = str_replace(array('/','.',' '),array('-','-','-'),self::flattenSingleValue(trim($dateValue,'"')));
  4791. $yearFound = false;
  4792. $t1 = explode('-',$dateValue);
  4793. foreach($t1 as &$t) {
  4794. if ((is_numeric($t)) && (($t > 31) && ($t < 100))) {
  4795. if ($yearFound) {
  4796. return self::$_errorCodes['value'];
  4797. } else {
  4798. $t += 1900;
  4799. $yearFound = true;
  4800. }
  4801. }
  4802. }
  4803. unset($t);
  4804. $dateValue = implode('-',$t1);
  4805. $PHPDateArray = date_parse($dateValue);
  4806. if (($PHPDateArray === False) || ($PHPDateArray['error_count'] > 0)) {
  4807. $testVal1 = strtok($dateValue,'- ');
  4808. if ($testVal1 !== False) {
  4809. $testVal2 = strtok('- ');
  4810. if ($testVal2 !== False) {
  4811. $testVal3 = strtok('- ');
  4812. if ($testVal3 === False) {
  4813. $testVal3 = strftime('%Y');
  4814. }
  4815. } else {
  4816. return self::$_errorCodes['value'];
  4817. }
  4818. } else {
  4819. return self::$_errorCodes['value'];
  4820. }
  4821. $PHPDateArray = date_parse($testVal1.'-'.$testVal2.'-'.$testVal3);
  4822. if (($PHPDateArray === False) || ($PHPDateArray['error_count'] > 0)) {
  4823. $PHPDateArray = date_parse($testVal2.'-'.$testVal1.'-'.$testVal3);
  4824. if (($PHPDateArray === False) || ($PHPDateArray['error_count'] > 0)) {
  4825. return self::$_errorCodes['value'];
  4826. }
  4827. }
  4828. }
  4829. if (($PHPDateArray !== False) && ($PHPDateArray['error_count'] == 0)) {
  4830. // Execute function
  4831. if ($PHPDateArray['year'] == '') { $PHPDateArray['year'] = strftime('%Y'); }
  4832. if ($PHPDateArray['month'] == '') { $PHPDateArray['month'] = strftime('%m'); }
  4833. if ($PHPDateArray['day'] == '') { $PHPDateArray['day'] = strftime('%d'); }
  4834. $excelDateValue = floor(PHPExcel_Shared_Date::FormattedPHPToExcel($PHPDateArray['year'],$PHPDateArray['month'],$PHPDateArray['day'],$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']));
  4835. switch (self::getReturnDateType()) {
  4836. case self::RETURNDATE_EXCEL : return (float) $excelDateValue;
  4837. break;
  4838. case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
  4839. break;
  4840. case self::RETURNDATE_PHP_OBJECT : return new DateTime($PHPDateArray['year'].'-'.$PHPDateArray['month'].'-'.$PHPDateArray['day'].' 00:00:00');
  4841. break;
  4842. }
  4843. }
  4844. return self::$_errorCodes['value'];
  4845. } // function DATEVALUE()
  4846. /**
  4847. * _getDateValue
  4848. *
  4849. * @param string $dateValue
  4850. * @return mixed Excel date/time serial value, or string if error
  4851. */
  4852. private static function _getDateValue($dateValue) {
  4853. if (!is_numeric($dateValue)) {
  4854. if ((is_string($dateValue)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
  4855. return self::$_errorCodes['value'];
  4856. }
  4857. if ((is_object($dateValue)) && ($dateValue instanceof PHPExcel_Shared_Date::$dateTimeObjectType)) {
  4858. $dateValue = PHPExcel_Shared_Date::PHPToExcel($dateValue);
  4859. } else {
  4860. $saveReturnDateType = self::getReturnDateType();
  4861. self::setReturnDateType(self::RETURNDATE_EXCEL);
  4862. $dateValue = self::DATEVALUE($dateValue);
  4863. self::setReturnDateType($saveReturnDateType);
  4864. }
  4865. }
  4866. return $dateValue;
  4867. } // function _getDateValue()
  4868. /**
  4869. * TIMEVALUE
  4870. *
  4871. * @param string $timeValue
  4872. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4873. * depending on the value of the ReturnDateType flag
  4874. */
  4875. public static function TIMEVALUE($timeValue) {
  4876. $timeValue = self::flattenSingleValue($timeValue);
  4877. if ((($PHPDateArray = date_parse($timeValue)) !== False) && ($PHPDateArray['error_count'] == 0)) {
  4878. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  4879. $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel($PHPDateArray['year'],$PHPDateArray['month'],$PHPDateArray['day'],$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']);
  4880. } else {
  4881. $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel(1900,1,1,$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']) - 1;
  4882. }
  4883. switch (self::getReturnDateType()) {
  4884. case self::RETURNDATE_EXCEL : return (float) $excelDateValue;
  4885. break;
  4886. case self::RETURNDATE_PHP_NUMERIC : return (integer) $phpDateValue = PHPExcel_Shared_Date::ExcelToPHP($excelDateValue+25569) - 3600;;
  4887. break;
  4888. case self::RETURNDATE_PHP_OBJECT : return new DateTime('1900-01-01 '.$PHPDateArray['hour'].':'.$PHPDateArray['minute'].':'.$PHPDateArray['second']);
  4889. break;
  4890. }
  4891. }
  4892. return self::$_errorCodes['value'];
  4893. } // function TIMEVALUE()
  4894. /**
  4895. * _getTimeValue
  4896. *
  4897. * @param string $timeValue
  4898. * @return mixed Excel date/time serial value, or string if error
  4899. */
  4900. private static function _getTimeValue($timeValue) {
  4901. $saveReturnDateType = self::getReturnDateType();
  4902. self::setReturnDateType(self::RETURNDATE_EXCEL);
  4903. $timeValue = self::TIMEVALUE($timeValue);
  4904. self::setReturnDateType($saveReturnDateType);
  4905. return $timeValue;
  4906. } // function _getTimeValue()
  4907. /**
  4908. * DATETIMENOW
  4909. *
  4910. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4911. * depending on the value of the ReturnDateType flag
  4912. */
  4913. public static function DATETIMENOW() {
  4914. $saveTimeZone = date_default_timezone_get();
  4915. date_default_timezone_set('UTC');
  4916. $retValue = False;
  4917. switch (self::getReturnDateType()) {
  4918. case self::RETURNDATE_EXCEL : $retValue = (float) PHPExcel_Shared_Date::PHPToExcel(time());
  4919. break;
  4920. case self::RETURNDATE_PHP_NUMERIC : $retValue = (integer) time();
  4921. break;
  4922. case self::RETURNDATE_PHP_OBJECT : $retValue = new DateTime();
  4923. break;
  4924. }
  4925. date_default_timezone_set($saveTimeZone);
  4926. return $retValue;
  4927. } // function DATETIMENOW()
  4928. /**
  4929. * DATENOW
  4930. *
  4931. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4932. * depending on the value of the ReturnDateType flag
  4933. */
  4934. public static function DATENOW() {
  4935. $saveTimeZone = date_default_timezone_get();
  4936. date_default_timezone_set('UTC');
  4937. $retValue = False;
  4938. $excelDateTime = floor(PHPExcel_Shared_Date::PHPToExcel(time()));
  4939. switch (self::getReturnDateType()) {
  4940. case self::RETURNDATE_EXCEL : $retValue = (float) $excelDateTime;
  4941. break;
  4942. case self::RETURNDATE_PHP_NUMERIC : $retValue = (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateTime) - 3600;
  4943. break;
  4944. case self::RETURNDATE_PHP_OBJECT : $retValue = PHPExcel_Shared_Date::ExcelToPHPObject($excelDateTime);
  4945. break;
  4946. }
  4947. date_default_timezone_set($saveTimeZone);
  4948. return $retValue;
  4949. } // function DATENOW()
  4950. private static function _isLeapYear($year) {
  4951. return ((($year % 4) == 0) && (($year % 100) != 0) || (($year % 400) == 0));
  4952. } // function _isLeapYear()
  4953. private static function _dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, $methodUS) {
  4954. if ($startDay == 31) {
  4955. --$startDay;
  4956. } elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !self::_isLeapYear($startYear))))) {
  4957. $startDay = 30;
  4958. }
  4959. if ($endDay == 31) {
  4960. if ($methodUS && $startDay != 30) {
  4961. $endDay = 1;
  4962. if ($endMonth == 12) {
  4963. ++$endYear;
  4964. $endMonth = 1;
  4965. } else {
  4966. ++$endMonth;
  4967. }
  4968. } else {
  4969. $endDay = 30;
  4970. }
  4971. }
  4972. return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
  4973. } // function _dateDiff360()
  4974. /**
  4975. * DAYS360
  4976. *
  4977. * @param long $startDate Excel date serial value or a standard date string
  4978. * @param long $endDate Excel date serial value or a standard date string
  4979. * @param boolean $method US or European Method
  4980. * @return long PHP date/time serial
  4981. */
  4982. public static function DAYS360($startDate = 0, $endDate = 0, $method = false) {
  4983. $startDate = self::flattenSingleValue($startDate);
  4984. $endDate = self::flattenSingleValue($endDate);
  4985. if (is_string($startDate = self::_getDateValue($startDate))) {
  4986. return self::$_errorCodes['value'];
  4987. }
  4988. if (is_string($endDate = self::_getDateValue($endDate))) {
  4989. return self::$_errorCodes['value'];
  4990. }
  4991. // Execute function
  4992. $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
  4993. $startDay = $PHPStartDateObject->format('j');
  4994. $startMonth = $PHPStartDateObject->format('n');
  4995. $startYear = $PHPStartDateObject->format('Y');
  4996. $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  4997. $endDay = $PHPEndDateObject->format('j');
  4998. $endMonth = $PHPEndDateObject->format('n');
  4999. $endYear = $PHPEndDateObject->format('Y');
  5000. return self::_dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
  5001. } // function DAYS360()
  5002. /**
  5003. * DATEDIF
  5004. *
  5005. * @param long $startDate Excel date serial value or a standard date string
  5006. * @param long $endDate Excel date serial value or a standard date string
  5007. * @param string $unit
  5008. * @return long Interval between the dates
  5009. */
  5010. public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D') {
  5011. $startDate = self::flattenSingleValue($startDate);
  5012. $endDate = self::flattenSingleValue($endDate);
  5013. $unit = strtoupper(self::flattenSingleValue($unit));
  5014. if (is_string($startDate = self::_getDateValue($startDate))) {
  5015. return self::$_errorCodes['value'];
  5016. }
  5017. if (is_string($endDate = self::_getDateValue($endDate))) {
  5018. return self::$_errorCodes['value'];
  5019. }
  5020. // Validate parameters
  5021. if ($startDate >= $endDate) {
  5022. return self::$_errorCodes['num'];
  5023. }
  5024. // Execute function
  5025. $difference = $endDate - $startDate;
  5026. $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
  5027. $startDays = $PHPStartDateObject->format('j');
  5028. $startMonths = $PHPStartDateObject->format('n');
  5029. $startYears = $PHPStartDateObject->format('Y');
  5030. $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  5031. $endDays = $PHPEndDateObject->format('j');
  5032. $endMonths = $PHPEndDateObject->format('n');
  5033. $endYears = $PHPEndDateObject->format('Y');
  5034. $retVal = self::$_errorCodes['num'];
  5035. switch ($unit) {
  5036. case 'D':
  5037. $retVal = intval($difference);
  5038. break;
  5039. case 'M':
  5040. $retVal = intval($endMonths - $startMonths) + (intval($endYears - $startYears) * 12);
  5041. // We're only interested in full months
  5042. if ($endDays < $startDays) {
  5043. --$retVal;
  5044. }
  5045. break;
  5046. case 'Y':
  5047. $retVal = intval($endYears - $startYears);
  5048. // We're only interested in full months
  5049. if ($endMonths < $startMonths) {
  5050. --$retVal;
  5051. } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) {
  5052. --$retVal;
  5053. }
  5054. break;
  5055. case 'MD':
  5056. if ($endDays < $startDays) {
  5057. $retVal = $endDays;
  5058. $PHPEndDateObject->modify('-'.$endDays.' days');
  5059. $adjustDays = $PHPEndDateObject->format('j');
  5060. if ($adjustDays > $startDays) {
  5061. $retVal += ($adjustDays - $startDays);
  5062. }
  5063. } else {
  5064. $retVal = $endDays - $startDays;
  5065. }
  5066. break;
  5067. case 'YM':
  5068. $retVal = abs(intval($endMonths - $startMonths));
  5069. // We're only interested in full months
  5070. if ($endDays < $startDays) {
  5071. --$retVal;
  5072. }
  5073. break;
  5074. case 'YD':
  5075. $retVal = intval($difference);
  5076. if ($endYears > $startYears) {
  5077. while ($endYears > $startYears) {
  5078. $PHPEndDateObject->modify('-1 year');
  5079. $endYears = $PHPEndDateObject->format('Y');
  5080. }
  5081. $retVal = abs($PHPEndDateObject->format('z') - $PHPStartDateObject->format('z'));
  5082. }
  5083. break;
  5084. }
  5085. return $retVal;
  5086. } // function DATEDIF()
  5087. /**
  5088. * YEARFRAC
  5089. *
  5090. * Calculates the fraction of the year represented by the number of whole days between two dates (the start_date and the
  5091. * end_date). Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or obligations
  5092. * to assign to a specific term.
  5093. *
  5094. * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer) or date object, or a standard date string
  5095. * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer) or date object, or a standard date string
  5096. * @param integer $method Method used for the calculation
  5097. * 0 or omitted US (NASD) 30/360
  5098. * 1 Actual/actual
  5099. * 2 Actual/360
  5100. * 3 Actual/365
  5101. * 4 European 30/360
  5102. * @return float fraction of the year
  5103. */
  5104. public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0) {
  5105. $startDate = self::flattenSingleValue($startDate);
  5106. $endDate = self::flattenSingleValue($endDate);
  5107. $method = self::flattenSingleValue($method);
  5108. if (is_string($startDate = self::_getDateValue($startDate))) {
  5109. return self::$_errorCodes['value'];
  5110. }
  5111. if (is_string($endDate = self::_getDateValue($endDate))) {
  5112. return self::$_errorCodes['value'];
  5113. }
  5114. if ((is_numeric($method)) && (!is_string($method))) {
  5115. switch($method) {
  5116. case 0 :
  5117. return self::DAYS360($startDate,$endDate) / 360;
  5118. break;
  5119. case 1 :
  5120. $startYear = self::YEAR($startDate);
  5121. $endYear = self::YEAR($endDate);
  5122. $leapDay = 0;
  5123. if (self::_isLeapYear($startYear) || self::_isLeapYear($endYear)) {
  5124. $leapDay = 1;
  5125. }
  5126. return self::DATEDIF($startDate,$endDate) / (365 + $leapDay);
  5127. break;
  5128. case 2 :
  5129. return self::DATEDIF($startDate,$endDate) / 360;
  5130. break;
  5131. case 3 :
  5132. return self::DATEDIF($startDate,$endDate) / 365;
  5133. break;
  5134. case 4 :
  5135. return self::DAYS360($startDate,$endDate,True) / 360;
  5136. break;
  5137. }
  5138. }
  5139. return self::$_errorCodes['value'];
  5140. } // function YEARFRAC()
  5141. /**
  5142. * NETWORKDAYS
  5143. *
  5144. * @param mixed Start date
  5145. * @param mixed End date
  5146. * @param array of mixed Optional Date Series
  5147. * @return long Interval between the dates
  5148. */
  5149. public static function NETWORKDAYS($startDate,$endDate) {
  5150. // Flush the mandatory start and end date that are referenced in the function definition
  5151. $dateArgs = self::flattenArray(func_get_args());
  5152. array_shift($dateArgs);
  5153. array_shift($dateArgs);
  5154. // Validate the start and end dates
  5155. if (is_string($startDate = $sDate = self::_getDateValue($startDate))) {
  5156. return self::$_errorCodes['value'];
  5157. }
  5158. if (is_string($endDate = $eDate = self::_getDateValue($endDate))) {
  5159. return self::$_errorCodes['value'];
  5160. }
  5161. if ($sDate > $eDate) {
  5162. $startDate = $eDate;
  5163. $endDate = $sDate;
  5164. }
  5165. // Execute function
  5166. $startDoW = 6 - self::DAYOFWEEK($startDate,2);
  5167. if ($startDoW < 0) { $startDoW = 0; }
  5168. $endDoW = self::DAYOFWEEK($endDate,2);
  5169. if ($endDoW >= 6) { $endDoW = 0; }
  5170. $wholeWeekDays = floor(($endDate - $startDate) / 7) * 5;
  5171. $partWeekDays = $endDoW + $startDoW;
  5172. if ($partWeekDays > 5) {
  5173. $partWeekDays -= 5;
  5174. }
  5175. // Test any extra holiday parameters
  5176. $holidayCountedArray = array();
  5177. foreach ($dateArgs as $holidayDate) {
  5178. if (is_string($holidayDate = self::_getDateValue($holidayDate))) {
  5179. return self::$_errorCodes['value'];
  5180. }
  5181. if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
  5182. if ((self::DAYOFWEEK($holidayDate,2) < 6) && (!in_array($holidayDate,$holidayCountedArray))) {
  5183. --$partWeekDays;
  5184. $holidayCountedArray[] = $holidayDate;
  5185. }
  5186. }
  5187. }
  5188. if ($sDate > $eDate) {
  5189. return 0 - ($wholeWeekDays + $partWeekDays);
  5190. }
  5191. return $wholeWeekDays + $partWeekDays;
  5192. } // function NETWORKDAYS()
  5193. /**
  5194. * WORKDAY
  5195. *
  5196. * @param mixed Start date
  5197. * @param mixed number of days for adjustment
  5198. * @param array of mixed Optional Date Series
  5199. * @return long Interval between the dates
  5200. */
  5201. public static function WORKDAY($startDate,$endDays) {
  5202. $dateArgs = self::flattenArray(func_get_args());
  5203. array_shift($dateArgs);
  5204. array_shift($dateArgs);
  5205. if (is_string($startDate = self::_getDateValue($startDate))) {
  5206. return self::$_errorCodes['value'];
  5207. }
  5208. if (!is_numeric($endDays)) {
  5209. return self::$_errorCodes['value'];
  5210. }
  5211. $endDate = (float) $startDate + (floor($endDays / 5) * 7) + ($endDays % 5);
  5212. if ($endDays < 0) {
  5213. $endDate += 7;
  5214. }
  5215. $endDoW = self::DAYOFWEEK($endDate,3);
  5216. if ($endDoW >= 5) {
  5217. if ($endDays >= 0) {
  5218. $endDate += (7 - $endDoW);
  5219. } else {
  5220. $endDate -= ($endDoW - 5);
  5221. }
  5222. }
  5223. // Test any extra holiday parameters
  5224. if (count($dateArgs) > 0) {
  5225. $holidayCountedArray = $holidayDates = array();
  5226. foreach ($dateArgs as $holidayDate) {
  5227. if (is_string($holidayDate = self::_getDateValue($holidayDate))) {
  5228. return self::$_errorCodes['value'];
  5229. }
  5230. $holidayDates[] = $holidayDate;
  5231. }
  5232. if ($endDays >= 0) {
  5233. sort($holidayDates, SORT_NUMERIC);
  5234. } else {
  5235. rsort($holidayDates, SORT_NUMERIC);
  5236. }
  5237. foreach ($holidayDates as $holidayDate) {
  5238. if ($endDays >= 0) {
  5239. if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
  5240. if ((self::DAYOFWEEK($holidayDate,2) < 6) && (!in_array($holidayDate,$holidayCountedArray))) {
  5241. ++$endDate;
  5242. $holidayCountedArray[] = $holidayDate;
  5243. }
  5244. }
  5245. } else {
  5246. if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
  5247. if ((self::DAYOFWEEK($holidayDate,2) < 6) && (!in_array($holidayDate,$holidayCountedArray))) {
  5248. --$endDate;
  5249. $holidayCountedArray[] = $holidayDate;
  5250. }
  5251. }
  5252. }
  5253. $endDoW = self::DAYOFWEEK($endDate,3);
  5254. if ($endDoW >= 5) {
  5255. if ($endDays >= 0) {
  5256. $endDate += (7 - $endDoW);
  5257. } else {
  5258. $endDate -= ($endDoW - 5);
  5259. }
  5260. }
  5261. }
  5262. }
  5263. switch (self::getReturnDateType()) {
  5264. case self::RETURNDATE_EXCEL : return (float) $endDate;
  5265. break;
  5266. case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP($endDate);
  5267. break;
  5268. case self::RETURNDATE_PHP_OBJECT : return PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  5269. break;
  5270. }
  5271. } // function WORKDAY()
  5272. /**
  5273. * DAYOFMONTH
  5274. *
  5275. * @param long $dateValue Excel date serial value or a standard date string
  5276. * @return int Day
  5277. */
  5278. public static function DAYOFMONTH($dateValue = 1) {
  5279. $dateValue = self::flattenSingleValue($dateValue);
  5280. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5281. return self::$_errorCodes['value'];
  5282. }
  5283. // Execute function
  5284. $PHPDateObject = &PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  5285. return (int) $PHPDateObject->format('j');
  5286. } // function DAYOFMONTH()
  5287. /**
  5288. * DAYOFWEEK
  5289. *
  5290. * @param long $dateValue Excel date serial value or a standard date string
  5291. * @return int Day
  5292. */
  5293. public static function DAYOFWEEK($dateValue = 1, $style = 1) {
  5294. $dateValue = self::flattenSingleValue($dateValue);
  5295. $style = floor(self::flattenSingleValue($style));
  5296. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5297. return self::$_errorCodes['value'];
  5298. }
  5299. // Execute function
  5300. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  5301. $DoW = $PHPDateObject->format('w');
  5302. $firstDay = 1;
  5303. switch ($style) {
  5304. case 1: ++$DoW;
  5305. break;
  5306. case 2: if ($DoW == 0) { $DoW = 7; }
  5307. break;
  5308. case 3: if ($DoW == 0) { $DoW = 7; }
  5309. $firstDay = 0;
  5310. --$DoW;
  5311. break;
  5312. default:
  5313. }
  5314. if (self::$compatibilityMode == self::COMPATIBILITY_EXCEL) {
  5315. // Test for Excel's 1900 leap year, and introduce the error as required
  5316. if (($PHPDateObject->format('Y') == 1900) && ($PHPDateObject->format('n') <= 2)) {
  5317. --$DoW;
  5318. if ($DoW < $firstDay) {
  5319. $DoW += 7;
  5320. }
  5321. }
  5322. }
  5323. return (int) $DoW;
  5324. } // function DAYOFWEEK()
  5325. /**
  5326. * WEEKOFYEAR
  5327. *
  5328. * @param long $dateValue Excel date serial value or a standard date string
  5329. * @param boolean $method Week begins on Sunday or Monday
  5330. * @return int Week Number
  5331. */
  5332. public static function WEEKOFYEAR($dateValue = 1, $method = 1) {
  5333. $dateValue = self::flattenSingleValue($dateValue);
  5334. $method = floor(self::flattenSingleValue($method));
  5335. if (!is_numeric($method)) {
  5336. return self::$_errorCodes['value'];
  5337. } elseif (($method < 1) || ($method > 2)) {
  5338. return self::$_errorCodes['num'];
  5339. }
  5340. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5341. return self::$_errorCodes['value'];
  5342. }
  5343. // Execute function
  5344. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  5345. $dayOfYear = $PHPDateObject->format('z');
  5346. $dow = $PHPDateObject->format('w');
  5347. $PHPDateObject->modify('-'.$dayOfYear.' days');
  5348. $dow = $PHPDateObject->format('w');
  5349. $daysInFirstWeek = 7 - (($dow + (2 - $method)) % 7);
  5350. $dayOfYear -= $daysInFirstWeek;
  5351. $weekOfYear = ceil($dayOfYear / 7) + 1;
  5352. return (int) $weekOfYear;
  5353. } // function WEEKOFYEAR()
  5354. /**
  5355. * MONTHOFYEAR
  5356. *
  5357. * @param long $dateValue Excel date serial value or a standard date string
  5358. * @return int Month
  5359. */
  5360. public static function MONTHOFYEAR($dateValue = 1) {
  5361. $dateValue = self::flattenSingleValue($dateValue);
  5362. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5363. return self::$_errorCodes['value'];
  5364. }
  5365. // Execute function
  5366. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  5367. return (int) $PHPDateObject->format('n');
  5368. } // function MONTHOFYEAR()
  5369. /**
  5370. * YEAR
  5371. *
  5372. * @param long $dateValue Excel date serial value or a standard date string
  5373. * @return int Year
  5374. */
  5375. public static function YEAR($dateValue = 1) {
  5376. $dateValue = self::flattenSingleValue($dateValue);
  5377. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5378. return self::$_errorCodes['value'];
  5379. }
  5380. // Execute function
  5381. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  5382. return (int) $PHPDateObject->format('Y');
  5383. } // function YEAR()
  5384. /**
  5385. * HOUROFDAY
  5386. *
  5387. * @param mixed $timeValue Excel time serial value or a standard time string
  5388. * @return int Hour
  5389. */
  5390. public static function HOUROFDAY($timeValue = 0) {
  5391. $timeValue = self::flattenSingleValue($timeValue);
  5392. if (!is_numeric($timeValue)) {
  5393. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  5394. $testVal = strtok($timeValue,'/-: ');
  5395. if (strlen($testVal) < strlen($timeValue)) {
  5396. return self::$_errorCodes['value'];
  5397. }
  5398. }
  5399. $timeValue = self::_getTimeValue($timeValue);
  5400. if (is_string($timeValue)) {
  5401. return self::$_errorCodes['value'];
  5402. }
  5403. }
  5404. // Execute function
  5405. if (is_real($timeValue)) {
  5406. if ($timeValue >= 1) {
  5407. $timeValue = fmod($timeValue,1);
  5408. } elseif ($timeValue < 0.0) {
  5409. return self::$_errorCodes['num'];
  5410. }
  5411. $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
  5412. }
  5413. return (int) date('G',$timeValue);
  5414. } // function HOUROFDAY()
  5415. /**
  5416. * MINUTEOFHOUR
  5417. *
  5418. * @param long $timeValue Excel time serial value or a standard time string
  5419. * @return int Minute
  5420. */
  5421. public static function MINUTEOFHOUR($timeValue = 0) {
  5422. $timeValue = $timeTester = self::flattenSingleValue($timeValue);
  5423. if (!is_numeric($timeValue)) {
  5424. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  5425. $testVal = strtok($timeValue,'/-: ');
  5426. if (strlen($testVal) < strlen($timeValue)) {
  5427. return self::$_errorCodes['value'];
  5428. }
  5429. }
  5430. $timeValue = self::_getTimeValue($timeValue);
  5431. if (is_string($timeValue)) {
  5432. return self::$_errorCodes['value'];
  5433. }
  5434. }
  5435. // Execute function
  5436. if (is_real($timeValue)) {
  5437. if ($timeValue >= 1) {
  5438. $timeValue = fmod($timeValue,1);
  5439. } elseif ($timeValue < 0.0) {
  5440. return self::$_errorCodes['num'];
  5441. }
  5442. $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
  5443. }
  5444. return (int) date('i',$timeValue);
  5445. } // function MINUTEOFHOUR()
  5446. /**
  5447. * SECONDOFMINUTE
  5448. *
  5449. * @param long $timeValue Excel time serial value or a standard time string
  5450. * @return int Second
  5451. */
  5452. public static function SECONDOFMINUTE($timeValue = 0) {
  5453. $timeValue = self::flattenSingleValue($timeValue);
  5454. if (!is_numeric($timeValue)) {
  5455. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  5456. $testVal = strtok($timeValue,'/-: ');
  5457. if (strlen($testVal) < strlen($timeValue)) {
  5458. return self::$_errorCodes['value'];
  5459. }
  5460. }
  5461. $timeValue = self::_getTimeValue($timeValue);
  5462. if (is_string($timeValue)) {
  5463. return self::$_errorCodes['value'];
  5464. }
  5465. }
  5466. // Execute function
  5467. if (is_real($timeValue)) {
  5468. if ($timeValue >= 1) {
  5469. $timeValue = fmod($timeValue,1);
  5470. } elseif ($timeValue < 0.0) {
  5471. return self::$_errorCodes['num'];
  5472. }
  5473. $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
  5474. }
  5475. return (int) date('s',$timeValue);
  5476. } // function SECONDOFMINUTE()
  5477. private static function _adjustDateByMonths($dateValue = 0, $adjustmentMonths = 0) {
  5478. // Execute function
  5479. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  5480. $oMonth = (int) $PHPDateObject->format('m');
  5481. $oYear = (int) $PHPDateObject->format('Y');
  5482. $adjustmentMonthsString = (string) $adjustmentMonths;
  5483. if ($adjustmentMonths > 0) {
  5484. $adjustmentMonthsString = '+'.$adjustmentMonths;
  5485. }
  5486. if ($adjustmentMonths != 0) {
  5487. $PHPDateObject->modify($adjustmentMonthsString.' months');
  5488. }
  5489. $nMonth = (int) $PHPDateObject->format('m');
  5490. $nYear = (int) $PHPDateObject->format('Y');
  5491. $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
  5492. if ($monthDiff != $adjustmentMonths) {
  5493. $adjustDays = (int) $PHPDateObject->format('d');
  5494. $adjustDaysString = '-'.$adjustDays.' days';
  5495. $PHPDateObject->modify($adjustDaysString);
  5496. }
  5497. return $PHPDateObject;
  5498. } // function _adjustDateByMonths()
  5499. /**
  5500. * EDATE
  5501. *
  5502. * Returns the serial number that represents the date that is the indicated number of months before or after a specified date
  5503. * (the start_date). Use EDATE to calculate maturity dates or due dates that fall on the same day of the month as the date of issue.
  5504. *
  5505. * @param long $dateValue Excel date serial value or a standard date string
  5506. * @param int $adjustmentMonths Number of months to adjust by
  5507. * @return long Excel date serial value
  5508. */
  5509. public static function EDATE($dateValue = 1, $adjustmentMonths = 0) {
  5510. $dateValue = self::flattenSingleValue($dateValue);
  5511. $adjustmentMonths = floor(self::flattenSingleValue($adjustmentMonths));
  5512. if (!is_numeric($adjustmentMonths)) {
  5513. return self::$_errorCodes['value'];
  5514. }
  5515. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5516. return self::$_errorCodes['value'];
  5517. }
  5518. // Execute function
  5519. $PHPDateObject = self::_adjustDateByMonths($dateValue,$adjustmentMonths);
  5520. switch (self::getReturnDateType()) {
  5521. case self::RETURNDATE_EXCEL : return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
  5522. break;
  5523. case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
  5524. break;
  5525. case self::RETURNDATE_PHP_OBJECT : return $PHPDateObject;
  5526. break;
  5527. }
  5528. } // function EDATE()
  5529. /**
  5530. * EOMONTH
  5531. *
  5532. * Returns the serial number for the last day of the month that is the indicated number of months before or after start_date.
  5533. * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
  5534. *
  5535. * @param long $dateValue Excel date serial value or a standard date string
  5536. * @param int $adjustmentMonths Number of months to adjust by
  5537. * @return long Excel date serial value
  5538. */
  5539. public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0) {
  5540. $dateValue = self::flattenSingleValue($dateValue);
  5541. $adjustmentMonths = floor(self::flattenSingleValue($adjustmentMonths));
  5542. if (!is_numeric($adjustmentMonths)) {
  5543. return self::$_errorCodes['value'];
  5544. }
  5545. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5546. return self::$_errorCodes['value'];
  5547. }
  5548. // Execute function
  5549. $PHPDateObject = self::_adjustDateByMonths($dateValue,$adjustmentMonths+1);
  5550. $adjustDays = (int) $PHPDateObject->format('d');
  5551. $adjustDaysString = '-'.$adjustDays.' days';
  5552. $PHPDateObject->modify($adjustDaysString);
  5553. switch (self::getReturnDateType()) {
  5554. case self::RETURNDATE_EXCEL : return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
  5555. break;
  5556. case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
  5557. break;
  5558. case self::RETURNDATE_PHP_OBJECT : return $PHPDateObject;
  5559. break;
  5560. }
  5561. } // function EOMONTH()
  5562. /**
  5563. * TRUNC
  5564. *
  5565. * Truncates value to the number of fractional digits by number_digits.
  5566. *
  5567. * @param float $value
  5568. * @param int $number_digits
  5569. * @return float Truncated value
  5570. */
  5571. public static function TRUNC($value = 0, $number_digits = 0) {
  5572. $value = self::flattenSingleValue($value);
  5573. $number_digits = self::flattenSingleValue($number_digits);
  5574. // Validate parameters
  5575. if ($number_digits < 0) {
  5576. return self::$_errorCodes['value'];
  5577. }
  5578. // Truncate
  5579. if ($number_digits > 0) {
  5580. $value = $value * pow(10, $number_digits);
  5581. }
  5582. $value = intval($value);
  5583. if ($number_digits > 0) {
  5584. $value = $value / pow(10, $number_digits);
  5585. }
  5586. // Return
  5587. return $value;
  5588. } // function TRUNC()
  5589. /**
  5590. * POWER
  5591. *
  5592. * Computes x raised to the power y.
  5593. *
  5594. * @param float $x
  5595. * @param float $y
  5596. * @return float
  5597. */
  5598. public static function POWER($x = 0, $y = 2) {
  5599. $x = self::flattenSingleValue($x);
  5600. $y = self::flattenSingleValue($y);
  5601. // Validate parameters
  5602. if ($x == 0 && $y <= 0) {
  5603. return self::$_errorCodes['divisionbyzero'];
  5604. }
  5605. // Return
  5606. return pow($x, $y);
  5607. } // function POWER()
  5608. private static function _nbrConversionFormat($xVal,$places) {
  5609. if (!is_null($places)) {
  5610. if (strlen($xVal) <= $places) {
  5611. return substr(str_pad($xVal,$places,'0',STR_PAD_LEFT),-10);
  5612. } else {
  5613. return self::$_errorCodes['num'];
  5614. }
  5615. }
  5616. return substr($xVal,-10);
  5617. } // function _nbrConversionFormat()
  5618. /**
  5619. * BINTODEC
  5620. *
  5621. * Return a binary value as Decimal.
  5622. *
  5623. * @param string $x
  5624. * @return string
  5625. */
  5626. public static function BINTODEC($x) {
  5627. $x = self::flattenSingleValue($x);
  5628. if (is_bool($x)) {
  5629. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  5630. $x = (int) $x;
  5631. } else {
  5632. return self::$_errorCodes['value'];
  5633. }
  5634. }
  5635. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  5636. $x = floor($x);
  5637. }
  5638. $x = (string) $x;
  5639. if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
  5640. return self::$_errorCodes['num'];
  5641. }
  5642. if (strlen($x) > 10) {
  5643. return self::$_errorCodes['num'];
  5644. } elseif (strlen($x) == 10) {
  5645. // Two's Complement
  5646. $x = substr($x,-9);
  5647. return '-'.(512-bindec($x));
  5648. }
  5649. return bindec($x);
  5650. } // function BINTODEC()
  5651. /**
  5652. * BINTOHEX
  5653. *
  5654. * Return a binary value as Hex.
  5655. *
  5656. * @param string $x
  5657. * @return string
  5658. */
  5659. public static function BINTOHEX($x, $places=null) {
  5660. $x = floor(self::flattenSingleValue($x));
  5661. $places = self::flattenSingleValue($places);
  5662. if (is_bool($x)) {
  5663. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  5664. $x = (int) $x;
  5665. } else {
  5666. return self::$_errorCodes['value'];
  5667. }
  5668. }
  5669. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  5670. $x = floor($x);
  5671. }
  5672. $x = (string) $x;
  5673. if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
  5674. return self::$_errorCodes['num'];
  5675. }
  5676. if (strlen($x) > 10) {
  5677. return self::$_errorCodes['num'];
  5678. } elseif (strlen($x) == 10) {
  5679. // Two's Complement
  5680. return str_repeat('F',8).substr(strtoupper(dechex(bindec(substr($x,-9)))),-2);
  5681. }
  5682. $hexVal = (string) strtoupper(dechex(bindec($x)));
  5683. return self::_nbrConversionFormat($hexVal,$places);
  5684. } // function BINTOHEX()
  5685. /**
  5686. * BINTOOCT
  5687. *
  5688. * Return a binary value as Octal.
  5689. *
  5690. * @param string $x
  5691. * @return string
  5692. */
  5693. public static function BINTOOCT($x, $places=null) {
  5694. $x = floor(self::flattenSingleValue($x));
  5695. $places = self::flattenSingleValue($places);
  5696. if (is_bool($x)) {
  5697. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  5698. $x = (int) $x;
  5699. } else {
  5700. return self::$_errorCodes['value'];
  5701. }
  5702. }
  5703. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  5704. $x = floor($x);
  5705. }
  5706. $x = (string) $x;
  5707. if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
  5708. return self::$_errorCodes['num'];
  5709. }
  5710. if (strlen($x) > 10) {
  5711. return self::$_errorCodes['num'];
  5712. } elseif (strlen($x) == 10) {
  5713. // Two's Complement
  5714. return str_repeat('7',7).substr(strtoupper(decoct(bindec(substr($x,-9)))),-3);
  5715. }
  5716. $octVal = (string) decoct(bindec($x));
  5717. return self::_nbrConversionFormat($octVal,$places);
  5718. } // function BINTOOCT()
  5719. /**
  5720. * DECTOBIN
  5721. *
  5722. * Return an octal value as binary.
  5723. *
  5724. * @param string $x
  5725. * @return string
  5726. */
  5727. public static function DECTOBIN($x, $places=null) {
  5728. $x = self::flattenSingleValue($x);
  5729. $places = self::flattenSingleValue($places);
  5730. if (is_bool($x)) {
  5731. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  5732. $x = (int) $x;
  5733. } else {
  5734. return self::$_errorCodes['value'];
  5735. }
  5736. }
  5737. $x = (string) $x;
  5738. if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
  5739. return self::$_errorCodes['value'];
  5740. }
  5741. $x = (string) floor($x);
  5742. $r = decbin($x);
  5743. if (strlen($r) == 32) {
  5744. // Two's Complement
  5745. $r = substr($r,-10);
  5746. } elseif (strlen($r) > 11) {
  5747. return self::$_errorCodes['num'];
  5748. }
  5749. return self::_nbrConversionFormat($r,$places);
  5750. } // function DECTOBIN()
  5751. /**
  5752. * DECTOOCT
  5753. *
  5754. * Return an octal value as binary.
  5755. *
  5756. * @param string $x
  5757. * @return string
  5758. */
  5759. public static function DECTOOCT($x, $places=null) {
  5760. $x = self::flattenSingleValue($x);
  5761. $places = self::flattenSingleValue($places);
  5762. if (is_bool($x)) {
  5763. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  5764. $x = (int) $x;
  5765. } else {
  5766. return self::$_errorCodes['value'];
  5767. }
  5768. }
  5769. $x = (string) $x;
  5770. if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
  5771. return self::$_errorCodes['value'];
  5772. }
  5773. $x = (string) floor($x);
  5774. $r = decoct($x);
  5775. if (strlen($r) == 11) {
  5776. // Two's Complement
  5777. $r = substr($r,-10);
  5778. }
  5779. return self::_nbrConversionFormat($r,$places);
  5780. } // function DECTOOCT()
  5781. /**
  5782. * DECTOHEX
  5783. *
  5784. * Return an octal value as binary.
  5785. *
  5786. * @param string $x
  5787. * @return string
  5788. */
  5789. public static function DECTOHEX($x, $places=null) {
  5790. $x = self::flattenSingleValue($x);
  5791. $places = self::flattenSingleValue($places);
  5792. if (is_bool($x)) {
  5793. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  5794. $x = (int) $x;
  5795. } else {
  5796. return self::$_errorCodes['value'];
  5797. }
  5798. }
  5799. $x = (string) $x;
  5800. if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
  5801. return self::$_errorCodes['value'];
  5802. }
  5803. $x = (string) floor($x);
  5804. $r = strtoupper(dechex($x));
  5805. if (strlen($r) == 8) {
  5806. // Two's Complement
  5807. $r = 'FF'.$r;
  5808. }
  5809. return self::_nbrConversionFormat($r,$places);
  5810. } // function DECTOHEX()
  5811. /**
  5812. * HEXTOBIN
  5813. *
  5814. * Return a hex value as binary.
  5815. *
  5816. * @param string $x
  5817. * @return string
  5818. */
  5819. public static function HEXTOBIN($x, $places=null) {
  5820. $x = self::flattenSingleValue($x);
  5821. $places = self::flattenSingleValue($places);
  5822. if (is_bool($x)) {
  5823. return self::$_errorCodes['value'];
  5824. }
  5825. $x = (string) $x;
  5826. if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
  5827. return self::$_errorCodes['num'];
  5828. }
  5829. $binVal = decbin(hexdec($x));
  5830. return substr(self::_nbrConversionFormat($binVal,$places),-10);
  5831. } // function HEXTOBIN()
  5832. /**
  5833. * HEXTOOCT
  5834. *
  5835. * Return a hex value as octal.
  5836. *
  5837. * @param string $x
  5838. * @return string
  5839. */
  5840. public static function HEXTOOCT($x, $places=null) {
  5841. $x = self::flattenSingleValue($x);
  5842. $places = self::flattenSingleValue($places);
  5843. if (is_bool($x)) {
  5844. return self::$_errorCodes['value'];
  5845. }
  5846. $x = (string) $x;
  5847. if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
  5848. return self::$_errorCodes['num'];
  5849. }
  5850. $octVal = decoct(hexdec($x));
  5851. return self::_nbrConversionFormat($octVal,$places);
  5852. } // function HEXTOOCT()
  5853. /**
  5854. * HEXTODEC
  5855. *
  5856. * Return a hex value as octal.
  5857. *
  5858. * @param string $x
  5859. * @return string
  5860. */
  5861. public static function HEXTODEC($x) {
  5862. $x = self::flattenSingleValue($x);
  5863. if (is_bool($x)) {
  5864. return self::$_errorCodes['value'];
  5865. }
  5866. $x = (string) $x;
  5867. if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
  5868. return self::$_errorCodes['num'];
  5869. }
  5870. return hexdec($x);
  5871. } // function HEXTODEC()
  5872. /**
  5873. * OCTTOBIN
  5874. *
  5875. * Return an octal value as binary.
  5876. *
  5877. * @param string $x
  5878. * @return string
  5879. */
  5880. public static function OCTTOBIN($x, $places=null) {
  5881. $x = self::flattenSingleValue($x);
  5882. $places = self::flattenSingleValue($places);
  5883. if (is_bool($x)) {
  5884. return self::$_errorCodes['value'];
  5885. }
  5886. $x = (string) $x;
  5887. if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
  5888. return self::$_errorCodes['num'];
  5889. }
  5890. $binVal = decbin(octdec($x));
  5891. return self::_nbrConversionFormat($binVal,$places);
  5892. } // function OCTTOBIN()
  5893. /**
  5894. * OCTTODEC
  5895. *
  5896. * Return an octal value as binary.
  5897. *
  5898. * @param string $x
  5899. * @return string
  5900. */
  5901. public static function OCTTODEC($x) {
  5902. $x = self::flattenSingleValue($x);
  5903. if (is_bool($x)) {
  5904. return self::$_errorCodes['value'];
  5905. }
  5906. $x = (string) $x;
  5907. if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
  5908. return self::$_errorCodes['num'];
  5909. }
  5910. return octdec($x);
  5911. } // function OCTTODEC()
  5912. /**
  5913. * OCTTOHEX
  5914. *
  5915. * Return an octal value as hex.
  5916. *
  5917. * @param string $x
  5918. * @return string
  5919. */
  5920. public static function OCTTOHEX($x, $places=null) {
  5921. $x = self::flattenSingleValue($x);
  5922. $places = self::flattenSingleValue($places);
  5923. if (is_bool($x)) {
  5924. return self::$_errorCodes['value'];
  5925. }
  5926. $x = (string) $x;
  5927. if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
  5928. return self::$_errorCodes['num'];
  5929. }
  5930. $hexVal = strtoupper(dechex(octdec($x)));
  5931. return self::_nbrConversionFormat($hexVal,$places);
  5932. } // function OCTTOHEX()
  5933. public static function _parseComplex($complexNumber) {
  5934. $workString = (string) $complexNumber;
  5935. $realNumber = $imaginary = 0;
  5936. // Extract the suffix, if there is one
  5937. $suffix = substr($workString,-1);
  5938. if (!is_numeric($suffix)) {
  5939. $workString = substr($workString,0,-1);
  5940. } else {
  5941. $suffix = '';
  5942. }
  5943. // Split the input into its Real and Imaginary components
  5944. $leadingSign = 0;
  5945. if (strlen($workString) > 0) {
  5946. $leadingSign = (($workString{0} == '+') || ($workString{0} == '-')) ? 1 : 0;
  5947. }
  5948. $power = '';
  5949. $realNumber = strtok($workString, '+-');
  5950. if (strtoupper(substr($realNumber,-1)) == 'E') {
  5951. $power = strtok('+-');
  5952. ++$leadingSign;
  5953. }
  5954. $realNumber = substr($workString,0,strlen($realNumber)+strlen($power)+$leadingSign);
  5955. if ($suffix != '') {
  5956. $imaginary = substr($workString,strlen($realNumber));
  5957. if (($imaginary == '') && (($realNumber == '') || ($realNumber == '+') || ($realNumber == '-'))) {
  5958. $imaginary = $realNumber.'1';
  5959. $realNumber = '0';
  5960. } else if ($imaginary == '') {
  5961. $imaginary = $realNumber;
  5962. $realNumber = '0';
  5963. } elseif (($imaginary == '+') || ($imaginary == '-')) {
  5964. $imaginary .= '1';
  5965. }
  5966. }
  5967. $complexArray = array( 'real' => $realNumber,
  5968. 'imaginary' => $imaginary,
  5969. 'suffix' => $suffix
  5970. );
  5971. return $complexArray;
  5972. } // function _parseComplex()
  5973. private static function _cleanComplex($complexNumber) {
  5974. if ($complexNumber{0} == '+') $complexNumber = substr($complexNumber,1);
  5975. if ($complexNumber{0} == '0') $complexNumber = substr($complexNumber,1);
  5976. if ($complexNumber{0} == '.') $complexNumber = '0'.$complexNumber;
  5977. if ($complexNumber{0} == '+') $complexNumber = substr($complexNumber,1);
  5978. return $complexNumber;
  5979. }
  5980. /**
  5981. * COMPLEX
  5982. *
  5983. * returns a complex number of the form x + yi or x + yj.
  5984. *
  5985. * @param float $realNumber
  5986. * @param float $imaginary
  5987. * @param string $suffix
  5988. * @return string
  5989. */
  5990. public static function COMPLEX($realNumber=0.0, $imaginary=0.0, $suffix='i') {
  5991. $realNumber = self::flattenSingleValue($realNumber);
  5992. $imaginary = self::flattenSingleValue($imaginary);
  5993. $suffix = self::flattenSingleValue($suffix);
  5994. if (((is_numeric($realNumber)) && (is_numeric($imaginary))) &&
  5995. (($suffix == 'i') || ($suffix == 'j') || ($suffix == ''))) {
  5996. if ($realNumber == 0.0) {
  5997. if ($imaginary == 0.0) {
  5998. return (string) '0';
  5999. } elseif ($imaginary == 1.0) {
  6000. return (string) $suffix;
  6001. } elseif ($imaginary == -1.0) {
  6002. return (string) '-'.$suffix;
  6003. }
  6004. return (string) $imaginary.$suffix;
  6005. } elseif ($imaginary == 0.0) {
  6006. return (string) $realNumber;
  6007. } elseif ($imaginary == 1.0) {
  6008. return (string) $realNumber.'+'.$suffix;
  6009. } elseif ($imaginary == -1.0) {
  6010. return (string) $realNumber.'-'.$suffix;
  6011. }
  6012. if ($imaginary > 0) { $imaginary = (string) '+'.$imaginary; }
  6013. return (string) $realNumber.$imaginary.$suffix;
  6014. }
  6015. return self::$_errorCodes['value'];
  6016. } // function COMPLEX()
  6017. /**
  6018. * IMAGINARY
  6019. *
  6020. * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
  6021. *
  6022. * @param string $complexNumber
  6023. * @return real
  6024. */
  6025. public static function IMAGINARY($complexNumber) {
  6026. $complexNumber = self::flattenSingleValue($complexNumber);
  6027. $parsedComplex = self::_parseComplex($complexNumber);
  6028. if (!is_array($parsedComplex)) {
  6029. return $parsedComplex;
  6030. }
  6031. return $parsedComplex['imaginary'];
  6032. } // function IMAGINARY()
  6033. /**
  6034. * IMREAL
  6035. *
  6036. * Returns the real coefficient of a complex number in x + yi or x + yj text format.
  6037. *
  6038. * @param string $complexNumber
  6039. * @return real
  6040. */
  6041. public static function IMREAL($complexNumber) {
  6042. $complexNumber = self::flattenSingleValue($complexNumber);
  6043. $parsedComplex = self::_parseComplex($complexNumber);
  6044. if (!is_array($parsedComplex)) {
  6045. return $parsedComplex;
  6046. }
  6047. return $parsedComplex['real'];
  6048. } // function IMREAL()
  6049. /**
  6050. * IMABS
  6051. *
  6052. * Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.
  6053. *
  6054. * @param string $complexNumber
  6055. * @return real
  6056. */
  6057. public static function IMABS($complexNumber) {
  6058. $complexNumber = self::flattenSingleValue($complexNumber);
  6059. $parsedComplex = self::_parseComplex($complexNumber);
  6060. if (!is_array($parsedComplex)) {
  6061. return $parsedComplex;
  6062. }
  6063. return sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary']));
  6064. } // function IMABS()
  6065. /**
  6066. * IMARGUMENT
  6067. *
  6068. * Returns the argument theta of a complex number, i.e. the angle in radians from the real axis to the representation of the number in polar coordinates.
  6069. *
  6070. * @param string $complexNumber
  6071. * @return string
  6072. */
  6073. public static function IMARGUMENT($complexNumber) {
  6074. $complexNumber = self::flattenSingleValue($complexNumber);
  6075. $parsedComplex = self::_parseComplex($complexNumber);
  6076. if (!is_array($parsedComplex)) {
  6077. return $parsedComplex;
  6078. }
  6079. if ($parsedComplex['real'] == 0.0) {
  6080. if ($parsedComplex['imaginary'] == 0.0) {
  6081. return 0.0;
  6082. } elseif($parsedComplex['imaginary'] < 0.0) {
  6083. return pi() / -2;
  6084. } else {
  6085. return pi() / 2;
  6086. }
  6087. } elseif ($parsedComplex['real'] > 0.0) {
  6088. return atan($parsedComplex['imaginary'] / $parsedComplex['real']);
  6089. } elseif ($parsedComplex['imaginary'] < 0.0) {
  6090. return 0 - (pi() - atan(abs($parsedComplex['imaginary']) / abs($parsedComplex['real'])));
  6091. } else {
  6092. return pi() - atan($parsedComplex['imaginary'] / abs($parsedComplex['real']));
  6093. }
  6094. } // function IMARGUMENT()
  6095. /**
  6096. * IMCONJUGATE
  6097. *
  6098. * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
  6099. *
  6100. * @param string $complexNumber
  6101. * @return string
  6102. */
  6103. public static function IMCONJUGATE($complexNumber) {
  6104. $complexNumber = self::flattenSingleValue($complexNumber);
  6105. $parsedComplex = self::_parseComplex($complexNumber);
  6106. if (!is_array($parsedComplex)) {
  6107. return $parsedComplex;
  6108. }
  6109. if ($parsedComplex['imaginary'] == 0.0) {
  6110. return $parsedComplex['real'];
  6111. } else {
  6112. return self::_cleanComplex(self::COMPLEX($parsedComplex['real'], 0 - $parsedComplex['imaginary'], $parsedComplex['suffix']));
  6113. }
  6114. } // function IMCONJUGATE()
  6115. /**
  6116. * IMCOS
  6117. *
  6118. * Returns the cosine of a complex number in x + yi or x + yj text format.
  6119. *
  6120. * @param string $complexNumber
  6121. * @return string
  6122. */
  6123. public static function IMCOS($complexNumber) {
  6124. $complexNumber = self::flattenSingleValue($complexNumber);
  6125. $parsedComplex = self::_parseComplex($complexNumber);
  6126. if (!is_array($parsedComplex)) {
  6127. return $parsedComplex;
  6128. }
  6129. if ($parsedComplex['imaginary'] == 0.0) {
  6130. return cos($parsedComplex['real']);
  6131. } else {
  6132. return self::IMCONJUGATE(self::COMPLEX(cos($parsedComplex['real']) * cosh($parsedComplex['imaginary']),sin($parsedComplex['real']) * sinh($parsedComplex['imaginary']),$parsedComplex['suffix']));
  6133. }
  6134. } // function IMCOS()
  6135. /**
  6136. * IMSIN
  6137. *
  6138. * Returns the sine of a complex number in x + yi or x + yj text format.
  6139. *
  6140. * @param string $complexNumber
  6141. * @return string
  6142. */
  6143. public static function IMSIN($complexNumber) {
  6144. $complexNumber = self::flattenSingleValue($complexNumber);
  6145. $parsedComplex = self::_parseComplex($complexNumber);
  6146. if (!is_array($parsedComplex)) {
  6147. return $parsedComplex;
  6148. }
  6149. if ($parsedComplex['imaginary'] == 0.0) {
  6150. return sin($parsedComplex['real']);
  6151. } else {
  6152. return self::COMPLEX(sin($parsedComplex['real']) * cosh($parsedComplex['imaginary']),cos($parsedComplex['real']) * sinh($parsedComplex['imaginary']),$parsedComplex['suffix']);
  6153. }
  6154. } // function IMSIN()
  6155. /**
  6156. * IMSQRT
  6157. *
  6158. * Returns the square root of a complex number in x + yi or x + yj text format.
  6159. *
  6160. * @param string $complexNumber
  6161. * @return string
  6162. */
  6163. public static function IMSQRT($complexNumber) {
  6164. $complexNumber = self::flattenSingleValue($complexNumber);
  6165. $parsedComplex = self::_parseComplex($complexNumber);
  6166. if (!is_array($parsedComplex)) {
  6167. return $parsedComplex;
  6168. }
  6169. $theta = self::IMARGUMENT($complexNumber);
  6170. $d1 = cos($theta / 2);
  6171. $d2 = sin($theta / 2);
  6172. $r = sqrt(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
  6173. if ($parsedComplex['suffix'] == '') {
  6174. return self::COMPLEX($d1 * $r,$d2 * $r);
  6175. } else {
  6176. return self::COMPLEX($d1 * $r,$d2 * $r,$parsedComplex['suffix']);
  6177. }
  6178. } // function IMSQRT()
  6179. /**
  6180. * IMLN
  6181. *
  6182. * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
  6183. *
  6184. * @param string $complexNumber
  6185. * @return string
  6186. */
  6187. public static function IMLN($complexNumber) {
  6188. $complexNumber = self::flattenSingleValue($complexNumber);
  6189. $parsedComplex = self::_parseComplex($complexNumber);
  6190. if (!is_array($parsedComplex)) {
  6191. return $parsedComplex;
  6192. }
  6193. if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
  6194. return self::$_errorCodes['num'];
  6195. }
  6196. $logR = log(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
  6197. $t = self::IMARGUMENT($complexNumber);
  6198. if ($parsedComplex['suffix'] == '') {
  6199. return self::COMPLEX($logR,$t);
  6200. } else {
  6201. return self::COMPLEX($logR,$t,$parsedComplex['suffix']);
  6202. }
  6203. } // function IMLN()
  6204. /**
  6205. * IMLOG10
  6206. *
  6207. * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
  6208. *
  6209. * @param string $complexNumber
  6210. * @return string
  6211. */
  6212. public static function IMLOG10($complexNumber) {
  6213. $complexNumber = self::flattenSingleValue($complexNumber);
  6214. $parsedComplex = self::_parseComplex($complexNumber);
  6215. if (!is_array($parsedComplex)) {
  6216. return $parsedComplex;
  6217. }
  6218. if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
  6219. return self::$_errorCodes['num'];
  6220. } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
  6221. return log10($parsedComplex['real']);
  6222. }
  6223. return self::IMPRODUCT(log10(EULER),self::IMLN($complexNumber));
  6224. } // function IMLOG10()
  6225. /**
  6226. * IMLOG2
  6227. *
  6228. * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
  6229. *
  6230. * @param string $complexNumber
  6231. * @return string
  6232. */
  6233. public static function IMLOG2($complexNumber) {
  6234. $complexNumber = self::flattenSingleValue($complexNumber);
  6235. $parsedComplex = self::_parseComplex($complexNumber);
  6236. if (!is_array($parsedComplex)) {
  6237. return $parsedComplex;
  6238. }
  6239. if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
  6240. return self::$_errorCodes['num'];
  6241. } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
  6242. return log($parsedComplex['real'],2);
  6243. }
  6244. return self::IMPRODUCT(log(EULER,2),self::IMLN($complexNumber));
  6245. } // function IMLOG2()
  6246. /**
  6247. * IMEXP
  6248. *
  6249. * Returns the exponential of a complex number in x + yi or x + yj text format.
  6250. *
  6251. * @param string $complexNumber
  6252. * @return string
  6253. */
  6254. public static function IMEXP($complexNumber) {
  6255. $complexNumber = self::flattenSingleValue($complexNumber);
  6256. $parsedComplex = self::_parseComplex($complexNumber);
  6257. if (!is_array($parsedComplex)) {
  6258. return $parsedComplex;
  6259. }
  6260. if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
  6261. return '1';
  6262. }
  6263. $e = exp($parsedComplex['real']);
  6264. $eX = $e * cos($parsedComplex['imaginary']);
  6265. $eY = $e * sin($parsedComplex['imaginary']);
  6266. if ($parsedComplex['suffix'] == '') {
  6267. return self::COMPLEX($eX,$eY);
  6268. } else {
  6269. return self::COMPLEX($eX,$eY,$parsedComplex['suffix']);
  6270. }
  6271. } // function IMEXP()
  6272. /**
  6273. * IMPOWER
  6274. *
  6275. * Returns a complex number in x + yi or x + yj text format raised to a power.
  6276. *
  6277. * @param string $complexNumber
  6278. * @return string
  6279. */
  6280. public static function IMPOWER($complexNumber,$realNumber) {
  6281. $complexNumber = self::flattenSingleValue($complexNumber);
  6282. $realNumber = self::flattenSingleValue($realNumber);
  6283. if (!is_numeric($realNumber)) {
  6284. return self::$_errorCodes['value'];
  6285. }
  6286. $parsedComplex = self::_parseComplex($complexNumber);
  6287. if (!is_array($parsedComplex)) {
  6288. return $parsedComplex;
  6289. }
  6290. $r = sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary']));
  6291. $rPower = pow($r,$realNumber);
  6292. $theta = self::IMARGUMENT($complexNumber) * $realNumber;
  6293. if ($theta == 0) {
  6294. return 1;
  6295. } elseif ($parsedComplex['imaginary'] == 0.0) {
  6296. return self::COMPLEX($rPower * cos($theta),$rPower * sin($theta),$parsedComplex['suffix']);
  6297. } else {
  6298. return self::COMPLEX($rPower * cos($theta),$rPower * sin($theta),$parsedComplex['suffix']);
  6299. }
  6300. } // function IMPOWER()
  6301. /**
  6302. * IMDIV
  6303. *
  6304. * Returns the quotient of two complex numbers in x + yi or x + yj text format.
  6305. *
  6306. * @param string $complexDividend
  6307. * @param string $complexDivisor
  6308. * @return real
  6309. */
  6310. public static function IMDIV($complexDividend,$complexDivisor) {
  6311. $complexDividend = self::flattenSingleValue($complexDividend);
  6312. $complexDivisor = self::flattenSingleValue($complexDivisor);
  6313. $parsedComplexDividend = self::_parseComplex($complexDividend);
  6314. if (!is_array($parsedComplexDividend)) {
  6315. return $parsedComplexDividend;
  6316. }
  6317. $parsedComplexDivisor = self::_parseComplex($complexDivisor);
  6318. if (!is_array($parsedComplexDivisor)) {
  6319. return $parsedComplexDividend;
  6320. }
  6321. if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] != '') &&
  6322. ($parsedComplexDividend['suffix'] != $parsedComplexDivisor['suffix'])) {
  6323. return self::$_errorCodes['num'];
  6324. }
  6325. if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] == '')) {
  6326. $parsedComplexDivisor['suffix'] = $parsedComplexDividend['suffix'];
  6327. }
  6328. $d1 = ($parsedComplexDividend['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['imaginary']);
  6329. $d2 = ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['real']) - ($parsedComplexDividend['real'] * $parsedComplexDivisor['imaginary']);
  6330. $d3 = ($parsedComplexDivisor['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDivisor['imaginary'] * $parsedComplexDivisor['imaginary']);
  6331. $r = $d1/$d3;
  6332. $i = $d2/$d3;
  6333. if ($i > 0.0) {
  6334. return self::_cleanComplex($r.'+'.$i.$parsedComplexDivisor['suffix']);
  6335. } elseif ($i < 0.0) {
  6336. return self::_cleanComplex($r.$i.$parsedComplexDivisor['suffix']);
  6337. } else {
  6338. return $r;
  6339. }
  6340. } // function IMDIV()
  6341. /**
  6342. * IMSUB
  6343. *
  6344. * Returns the difference of two complex numbers in x + yi or x + yj text format.
  6345. *
  6346. * @param string $complexNumber1
  6347. * @param string $complexNumber2
  6348. * @return real
  6349. */
  6350. public static function IMSUB($complexNumber1,$complexNumber2) {
  6351. $complexNumber1 = self::flattenSingleValue($complexNumber1);
  6352. $complexNumber2 = self::flattenSingleValue($complexNumber2);
  6353. $parsedComplex1 = self::_parseComplex($complexNumber1);
  6354. if (!is_array($parsedComplex1)) {
  6355. return $parsedComplex1;
  6356. }
  6357. $parsedComplex2 = self::_parseComplex($complexNumber2);
  6358. if (!is_array($parsedComplex2)) {
  6359. return $parsedComplex2;
  6360. }
  6361. if ((($parsedComplex1['suffix'] != '') && ($parsedComplex2['suffix'] != '')) &&
  6362. ($parsedComplex1['suffix'] != $parsedComplex2['suffix'])) {
  6363. return self::$_errorCodes['num'];
  6364. } elseif (($parsedComplex1['suffix'] == '') && ($parsedComplex2['suffix'] != '')) {
  6365. $parsedComplex1['suffix'] = $parsedComplex2['suffix'];
  6366. }
  6367. $d1 = $parsedComplex1['real'] - $parsedComplex2['real'];
  6368. $d2 = $parsedComplex1['imaginary'] - $parsedComplex2['imaginary'];
  6369. return self::COMPLEX($d1,$d2,$parsedComplex1['suffix']);
  6370. } // function IMSUB()
  6371. /**
  6372. * IMSUM
  6373. *
  6374. * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
  6375. *
  6376. * @param array of mixed Data Series
  6377. * @return real
  6378. */
  6379. public static function IMSUM() {
  6380. // Return value
  6381. $returnValue = self::_parseComplex('0');
  6382. $activeSuffix = '';
  6383. // Loop through the arguments
  6384. $aArgs = self::flattenArray(func_get_args());
  6385. foreach ($aArgs as $arg) {
  6386. $parsedComplex = self::_parseComplex($arg);
  6387. if (!is_array($parsedComplex)) {
  6388. return $parsedComplex;
  6389. }
  6390. if ($activeSuffix == '') {
  6391. $activeSuffix = $parsedComplex['suffix'];
  6392. } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
  6393. return self::$_errorCodes['value'];
  6394. }
  6395. $returnValue['real'] += $parsedComplex['real'];
  6396. $returnValue['imaginary'] += $parsedComplex['imaginary'];
  6397. }
  6398. if ($returnValue['imaginary'] == 0.0) { $activeSuffix = ''; }
  6399. return self::COMPLEX($returnValue['real'],$returnValue['imaginary'],$activeSuffix);
  6400. } // function IMSUM()
  6401. /**
  6402. * IMPRODUCT
  6403. *
  6404. * Returns the product of two or more complex numbers in x + yi or x + yj text format.
  6405. *
  6406. * @param array of mixed Data Series
  6407. * @return real
  6408. */
  6409. public static function IMPRODUCT() {
  6410. // Return value
  6411. $returnValue = self::_parseComplex('1');
  6412. $activeSuffix = '';
  6413. // Loop through the arguments
  6414. $aArgs = self::flattenArray(func_get_args());
  6415. foreach ($aArgs as $arg) {
  6416. $parsedComplex = self::_parseComplex($arg);
  6417. if (!is_array($parsedComplex)) {
  6418. return $parsedComplex;
  6419. }
  6420. $workValue = $returnValue;
  6421. if (($parsedComplex['suffix'] != '') && ($activeSuffix == '')) {
  6422. $activeSuffix = $parsedComplex['suffix'];
  6423. } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
  6424. return self::$_errorCodes['num'];
  6425. }
  6426. $returnValue['real'] = ($workValue['real'] * $parsedComplex['real']) - ($workValue['imaginary'] * $parsedComplex['imaginary']);
  6427. $returnValue['imaginary'] = ($workValue['real'] * $parsedComplex['imaginary']) + ($workValue['imaginary'] * $parsedComplex['real']);
  6428. }
  6429. if ($returnValue['imaginary'] == 0.0) { $activeSuffix = ''; }
  6430. return self::COMPLEX($returnValue['real'],$returnValue['imaginary'],$activeSuffix);
  6431. } // function IMPRODUCT()
  6432. private static $_conversionUnits = array( 'g' => array( 'Group' => 'Mass', 'Unit Name' => 'Gram', 'AllowPrefix' => True ),
  6433. 'sg' => array( 'Group' => 'Mass', 'Unit Name' => 'Slug', 'AllowPrefix' => False ),
  6434. 'lbm' => array( 'Group' => 'Mass', 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => False ),
  6435. 'u' => array( 'Group' => 'Mass', 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => True ),
  6436. 'ozm' => array( 'Group' => 'Mass', 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => False ),
  6437. 'm' => array( 'Group' => 'Distance', 'Unit Name' => 'Meter', 'AllowPrefix' => True ),
  6438. 'mi' => array( 'Group' => 'Distance', 'Unit Name' => 'Statute mile', 'AllowPrefix' => False ),
  6439. 'Nmi' => array( 'Group' => 'Distance', 'Unit Name' => 'Nautical mile', 'AllowPrefix' => False ),
  6440. 'in' => array( 'Group' => 'Distance', 'Unit Name' => 'Inch', 'AllowPrefix' => False ),
  6441. 'ft' => array( 'Group' => 'Distance', 'Unit Name' => 'Foot', 'AllowPrefix' => False ),
  6442. 'yd' => array( 'Group' => 'Distance', 'Unit Name' => 'Yard', 'AllowPrefix' => False ),
  6443. 'ang' => array( 'Group' => 'Distance', 'Unit Name' => 'Angstrom', 'AllowPrefix' => True ),
  6444. 'Pica' => array( 'Group' => 'Distance', 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => False ),
  6445. 'yr' => array( 'Group' => 'Time', 'Unit Name' => 'Year', 'AllowPrefix' => False ),
  6446. 'day' => array( 'Group' => 'Time', 'Unit Name' => 'Day', 'AllowPrefix' => False ),
  6447. 'hr' => array( 'Group' => 'Time', 'Unit Name' => 'Hour', 'AllowPrefix' => False ),
  6448. 'mn' => array( 'Group' => 'Time', 'Unit Name' => 'Minute', 'AllowPrefix' => False ),
  6449. 'sec' => array( 'Group' => 'Time', 'Unit Name' => 'Second', 'AllowPrefix' => True ),
  6450. 'Pa' => array( 'Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => True ),
  6451. 'p' => array( 'Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => True ),
  6452. 'atm' => array( 'Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => True ),
  6453. 'at' => array( 'Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => True ),
  6454. 'mmHg' => array( 'Group' => 'Pressure', 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => True ),
  6455. 'N' => array( 'Group' => 'Force', 'Unit Name' => 'Newton', 'AllowPrefix' => True ),
  6456. 'dyn' => array( 'Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => True ),
  6457. 'dy' => array( 'Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => True ),
  6458. 'lbf' => array( 'Group' => 'Force', 'Unit Name' => 'Pound force', 'AllowPrefix' => False ),
  6459. 'J' => array( 'Group' => 'Energy', 'Unit Name' => 'Joule', 'AllowPrefix' => True ),
  6460. 'e' => array( 'Group' => 'Energy', 'Unit Name' => 'Erg', 'AllowPrefix' => True ),
  6461. 'c' => array( 'Group' => 'Energy', 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => True ),
  6462. 'cal' => array( 'Group' => 'Energy', 'Unit Name' => 'IT calorie', 'AllowPrefix' => True ),
  6463. 'eV' => array( 'Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => True ),
  6464. 'ev' => array( 'Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => True ),
  6465. 'HPh' => array( 'Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => False ),
  6466. 'hh' => array( 'Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => False ),
  6467. 'Wh' => array( 'Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => True ),
  6468. 'wh' => array( 'Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => True ),
  6469. 'flb' => array( 'Group' => 'Energy', 'Unit Name' => 'Foot-pound', 'AllowPrefix' => False ),
  6470. 'BTU' => array( 'Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => False ),
  6471. 'btu' => array( 'Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => False ),
  6472. 'HP' => array( 'Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => False ),
  6473. 'h' => array( 'Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => False ),
  6474. 'W' => array( 'Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => True ),
  6475. 'w' => array( 'Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => True ),
  6476. 'T' => array( 'Group' => 'Magnetism', 'Unit Name' => 'Tesla', 'AllowPrefix' => True ),
  6477. 'ga' => array( 'Group' => 'Magnetism', 'Unit Name' => 'Gauss', 'AllowPrefix' => True ),
  6478. 'C' => array( 'Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => False ),
  6479. 'cel' => array( 'Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => False ),
  6480. 'F' => array( 'Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => False ),
  6481. 'fah' => array( 'Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => False ),
  6482. 'K' => array( 'Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => False ),
  6483. 'kel' => array( 'Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => False ),
  6484. 'tsp' => array( 'Group' => 'Liquid', 'Unit Name' => 'Teaspoon', 'AllowPrefix' => False ),
  6485. 'tbs' => array( 'Group' => 'Liquid', 'Unit Name' => 'Tablespoon', 'AllowPrefix' => False ),
  6486. 'oz' => array( 'Group' => 'Liquid', 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => False ),
  6487. 'cup' => array( 'Group' => 'Liquid', 'Unit Name' => 'Cup', 'AllowPrefix' => False ),
  6488. 'pt' => array( 'Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => False ),
  6489. 'us_pt' => array( 'Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => False ),
  6490. 'uk_pt' => array( 'Group' => 'Liquid', 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => False ),
  6491. 'qt' => array( 'Group' => 'Liquid', 'Unit Name' => 'Quart', 'AllowPrefix' => False ),
  6492. 'gal' => array( 'Group' => 'Liquid', 'Unit Name' => 'Gallon', 'AllowPrefix' => False ),
  6493. 'l' => array( 'Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => True ),
  6494. 'lt' => array( 'Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => True )
  6495. );
  6496. private static $_conversionMultipliers = array( 'Y' => array( 'multiplier' => 1E24, 'name' => 'yotta' ),
  6497. 'Z' => array( 'multiplier' => 1E21, 'name' => 'zetta' ),
  6498. 'E' => array( 'multiplier' => 1E18, 'name' => 'exa' ),
  6499. 'P' => array( 'multiplier' => 1E15, 'name' => 'peta' ),
  6500. 'T' => array( 'multiplier' => 1E12, 'name' => 'tera' ),
  6501. 'G' => array( 'multiplier' => 1E9, 'name' => 'giga' ),
  6502. 'M' => array( 'multiplier' => 1E6, 'name' => 'mega' ),
  6503. 'k' => array( 'multiplier' => 1E3, 'name' => 'kilo' ),
  6504. 'h' => array( 'multiplier' => 1E2, 'name' => 'hecto' ),
  6505. 'e' => array( 'multiplier' => 1E1, 'name' => 'deka' ),
  6506. 'd' => array( 'multiplier' => 1E-1, 'name' => 'deci' ),
  6507. 'c' => array( 'multiplier' => 1E-2, 'name' => 'centi' ),
  6508. 'm' => array( 'multiplier' => 1E-3, 'name' => 'milli' ),
  6509. 'u' => array( 'multiplier' => 1E-6, 'name' => 'micro' ),
  6510. 'n' => array( 'multiplier' => 1E-9, 'name' => 'nano' ),
  6511. 'p' => array( 'multiplier' => 1E-12, 'name' => 'pico' ),
  6512. 'f' => array( 'multiplier' => 1E-15, 'name' => 'femto' ),
  6513. 'a' => array( 'multiplier' => 1E-18, 'name' => 'atto' ),
  6514. 'z' => array( 'multiplier' => 1E-21, 'name' => 'zepto' ),
  6515. 'y' => array( 'multiplier' => 1E-24, 'name' => 'yocto' )
  6516. );
  6517. private static $_unitConversions = array( 'Mass' => array( 'g' => array( 'g' => 1.0,
  6518. 'sg' => 6.85220500053478E-05,
  6519. 'lbm' => 2.20462291469134E-03,
  6520. 'u' => 6.02217000000000E+23,
  6521. 'ozm' => 3.52739718003627E-02
  6522. ),
  6523. 'sg' => array( 'g' => 1.45938424189287E+04,
  6524. 'sg' => 1.0,
  6525. 'lbm' => 3.21739194101647E+01,
  6526. 'u' => 8.78866000000000E+27,
  6527. 'ozm' => 5.14782785944229E+02
  6528. ),
  6529. 'lbm' => array( 'g' => 4.5359230974881148E+02,
  6530. 'sg' => 3.10810749306493E-02,
  6531. 'lbm' => 1.0,
  6532. 'u' => 2.73161000000000E+26,
  6533. 'ozm' => 1.60000023429410E+01
  6534. ),
  6535. 'u' => array( 'g' => 1.66053100460465E-24,
  6536. 'sg' => 1.13782988532950E-28,
  6537. 'lbm' => 3.66084470330684E-27,
  6538. 'u' => 1.0,
  6539. 'ozm' => 5.85735238300524E-26
  6540. ),
  6541. 'ozm' => array( 'g' => 2.83495152079732E+01,
  6542. 'sg' => 1.94256689870811E-03,
  6543. 'lbm' => 6.24999908478882E-02,
  6544. 'u' => 1.70725600000000E+25,
  6545. 'ozm' => 1.0
  6546. )
  6547. ),
  6548. 'Distance' => array( 'm' => array( 'm' => 1.0,
  6549. 'mi' => 6.21371192237334E-04,
  6550. 'Nmi' => 5.39956803455724E-04,
  6551. 'in' => 3.93700787401575E+01,
  6552. 'ft' => 3.28083989501312E+00,
  6553. 'yd' => 1.09361329797891E+00,
  6554. 'ang' => 1.00000000000000E+10,
  6555. 'Pica' => 2.83464566929116E+03
  6556. ),
  6557. 'mi' => array( 'm' => 1.60934400000000E+03,
  6558. 'mi' => 1.0,
  6559. 'Nmi' => 8.68976241900648E-01,
  6560. 'in' => 6.33600000000000E+04,
  6561. 'ft' => 5.28000000000000E+03,
  6562. 'yd' => 1.76000000000000E+03,
  6563. 'ang' => 1.60934400000000E+13,
  6564. 'Pica' => 4.56191999999971E+06
  6565. ),
  6566. 'Nmi' => array( 'm' => 1.85200000000000E+03,
  6567. 'mi' => 1.15077944802354E+00,
  6568. 'Nmi' => 1.0,
  6569. 'in' => 7.29133858267717E+04,
  6570. 'ft' => 6.07611548556430E+03,
  6571. 'yd' => 2.02537182785694E+03,
  6572. 'ang' => 1.85200000000000E+13,
  6573. 'Pica' => 5.24976377952723E+06
  6574. ),
  6575. 'in' => array( 'm' => 2.54000000000000E-02,
  6576. 'mi' => 1.57828282828283E-05,
  6577. 'Nmi' => 1.37149028077754E-05,
  6578. 'in' => 1.0,
  6579. 'ft' => 8.33333333333333E-02,
  6580. 'yd' => 2.77777777686643E-02,
  6581. 'ang' => 2.54000000000000E+08,
  6582. 'Pica' => 7.19999999999955E+01
  6583. ),
  6584. 'ft' => array( 'm' => 3.04800000000000E-01,
  6585. 'mi' => 1.89393939393939E-04,
  6586. 'Nmi' => 1.64578833693305E-04,
  6587. 'in' => 1.20000000000000E+01,
  6588. 'ft' => 1.0,
  6589. 'yd' => 3.33333333223972E-01,
  6590. 'ang' => 3.04800000000000E+09,
  6591. 'Pica' => 8.63999999999946E+02
  6592. ),
  6593. 'yd' => array( 'm' => 9.14400000300000E-01,
  6594. 'mi' => 5.68181818368230E-04,
  6595. 'Nmi' => 4.93736501241901E-04,
  6596. 'in' => 3.60000000118110E+01,
  6597. 'ft' => 3.00000000000000E+00,
  6598. 'yd' => 1.0,
  6599. 'ang' => 9.14400000300000E+09,
  6600. 'Pica' => 2.59200000085023E+03
  6601. ),
  6602. 'ang' => array( 'm' => 1.00000000000000E-10,
  6603. 'mi' => 6.21371192237334E-14,
  6604. 'Nmi' => 5.39956803455724E-14,
  6605. 'in' => 3.93700787401575E-09,
  6606. 'ft' => 3.28083989501312E-10,
  6607. 'yd' => 1.09361329797891E-10,
  6608. 'ang' => 1.0,
  6609. 'Pica' => 2.83464566929116E-07
  6610. ),
  6611. 'Pica' => array( 'm' => 3.52777777777800E-04,
  6612. 'mi' => 2.19205948372629E-07,
  6613. 'Nmi' => 1.90484761219114E-07,
  6614. 'in' => 1.38888888888898E-02,
  6615. 'ft' => 1.15740740740748E-03,
  6616. 'yd' => 3.85802469009251E-04,
  6617. 'ang' => 3.52777777777800E+06,
  6618. 'Pica' => 1.0
  6619. )
  6620. ),
  6621. 'Time' => array( 'yr' => array( 'yr' => 1.0,
  6622. 'day' => 365.25,
  6623. 'hr' => 8766.0,
  6624. 'mn' => 525960.0,
  6625. 'sec' => 31557600.0
  6626. ),
  6627. 'day' => array( 'yr' => 2.73785078713210E-03,
  6628. 'day' => 1.0,
  6629. 'hr' => 24.0,
  6630. 'mn' => 1440.0,
  6631. 'sec' => 86400.0
  6632. ),
  6633. 'hr' => array( 'yr' => 1.14077116130504E-04,
  6634. 'day' => 4.16666666666667E-02,
  6635. 'hr' => 1.0,
  6636. 'mn' => 60.0,
  6637. 'sec' => 3600.0
  6638. ),
  6639. 'mn' => array( 'yr' => 1.90128526884174E-06,
  6640. 'day' => 6.94444444444444E-04,
  6641. 'hr' => 1.66666666666667E-02,
  6642. 'mn' => 1.0,
  6643. 'sec' => 60.0
  6644. ),
  6645. 'sec' => array( 'yr' => 3.16880878140289E-08,
  6646. 'day' => 1.15740740740741E-05,
  6647. 'hr' => 2.77777777777778E-04,
  6648. 'mn' => 1.66666666666667E-02,
  6649. 'sec' => 1.0
  6650. )
  6651. ),
  6652. 'Pressure' => array( 'Pa' => array( 'Pa' => 1.0,
  6653. 'p' => 1.0,
  6654. 'atm' => 9.86923299998193E-06,
  6655. 'at' => 9.86923299998193E-06,
  6656. 'mmHg' => 7.50061707998627E-03
  6657. ),
  6658. 'p' => array( 'Pa' => 1.0,
  6659. 'p' => 1.0,
  6660. 'atm' => 9.86923299998193E-06,
  6661. 'at' => 9.86923299998193E-06,
  6662. 'mmHg' => 7.50061707998627E-03
  6663. ),
  6664. 'atm' => array( 'Pa' => 1.01324996583000E+05,
  6665. 'p' => 1.01324996583000E+05,
  6666. 'atm' => 1.0,
  6667. 'at' => 1.0,
  6668. 'mmHg' => 760.0
  6669. ),
  6670. 'at' => array( 'Pa' => 1.01324996583000E+05,
  6671. 'p' => 1.01324996583000E+05,
  6672. 'atm' => 1.0,
  6673. 'at' => 1.0,
  6674. 'mmHg' => 760.0
  6675. ),
  6676. 'mmHg' => array( 'Pa' => 1.33322363925000E+02,
  6677. 'p' => 1.33322363925000E+02,
  6678. 'atm' => 1.31578947368421E-03,
  6679. 'at' => 1.31578947368421E-03,
  6680. 'mmHg' => 1.0
  6681. )
  6682. ),
  6683. 'Force' => array( 'N' => array( 'N' => 1.0,
  6684. 'dyn' => 1.0E+5,
  6685. 'dy' => 1.0E+5,
  6686. 'lbf' => 2.24808923655339E-01
  6687. ),
  6688. 'dyn' => array( 'N' => 1.0E-5,
  6689. 'dyn' => 1.0,
  6690. 'dy' => 1.0,
  6691. 'lbf' => 2.24808923655339E-06
  6692. ),
  6693. 'dy' => array( 'N' => 1.0E-5,
  6694. 'dyn' => 1.0,
  6695. 'dy' => 1.0,
  6696. 'lbf' => 2.24808923655339E-06
  6697. ),
  6698. 'lbf' => array( 'N' => 4.448222,
  6699. 'dyn' => 4.448222E+5,
  6700. 'dy' => 4.448222E+5,
  6701. 'lbf' => 1.0
  6702. )
  6703. ),
  6704. 'Energy' => array( 'J' => array( 'J' => 1.0,
  6705. 'e' => 9.99999519343231E+06,
  6706. 'c' => 2.39006249473467E-01,
  6707. 'cal' => 2.38846190642017E-01,
  6708. 'eV' => 6.24145700000000E+18,
  6709. 'ev' => 6.24145700000000E+18,
  6710. 'HPh' => 3.72506430801000E-07,
  6711. 'hh' => 3.72506430801000E-07,
  6712. 'Wh' => 2.77777916238711E-04,
  6713. 'wh' => 2.77777916238711E-04,
  6714. 'flb' => 2.37304222192651E+01,
  6715. 'BTU' => 9.47815067349015E-04,
  6716. 'btu' => 9.47815067349015E-04
  6717. ),
  6718. 'e' => array( 'J' => 1.00000048065700E-07,
  6719. 'e' => 1.0,
  6720. 'c' => 2.39006364353494E-08,
  6721. 'cal' => 2.38846305445111E-08,
  6722. 'eV' => 6.24146000000000E+11,
  6723. 'ev' => 6.24146000000000E+11,
  6724. 'HPh' => 3.72506609848824E-14,
  6725. 'hh' => 3.72506609848824E-14,
  6726. 'Wh' => 2.77778049754611E-11,
  6727. 'wh' => 2.77778049754611E-11,
  6728. 'flb' => 2.37304336254586E-06,
  6729. 'BTU' => 9.47815522922962E-11,
  6730. 'btu' => 9.47815522922962E-11
  6731. ),
  6732. 'c' => array( 'J' => 4.18399101363672E+00,
  6733. 'e' => 4.18398900257312E+07,
  6734. 'c' => 1.0,
  6735. 'cal' => 9.99330315287563E-01,
  6736. 'eV' => 2.61142000000000E+19,
  6737. 'ev' => 2.61142000000000E+19,
  6738. 'HPh' => 1.55856355899327E-06,
  6739. 'hh' => 1.55856355899327E-06,
  6740. 'Wh' => 1.16222030532950E-03,
  6741. 'wh' => 1.16222030532950E-03,
  6742. 'flb' => 9.92878733152102E+01,
  6743. 'BTU' => 3.96564972437776E-03,
  6744. 'btu' => 3.96564972437776E-03
  6745. ),
  6746. 'cal' => array( 'J' => 4.18679484613929E+00,
  6747. 'e' => 4.18679283372801E+07,
  6748. 'c' => 1.00067013349059E+00,
  6749. 'cal' => 1.0,
  6750. 'eV' => 2.61317000000000E+19,
  6751. 'ev' => 2.61317000000000E+19,
  6752. 'HPh' => 1.55960800463137E-06,
  6753. 'hh' => 1.55960800463137E-06,
  6754. 'Wh' => 1.16299914807955E-03,
  6755. 'wh' => 1.16299914807955E-03,
  6756. 'flb' => 9.93544094443283E+01,
  6757. 'BTU' => 3.96830723907002E-03,
  6758. 'btu' => 3.96830723907002E-03
  6759. ),
  6760. 'eV' => array( 'J' => 1.60219000146921E-19,
  6761. 'e' => 1.60218923136574E-12,
  6762. 'c' => 3.82933423195043E-20,
  6763. 'cal' => 3.82676978535648E-20,
  6764. 'eV' => 1.0,
  6765. 'ev' => 1.0,
  6766. 'HPh' => 5.96826078912344E-26,
  6767. 'hh' => 5.96826078912344E-26,
  6768. 'Wh' => 4.45053000026614E-23,
  6769. 'wh' => 4.45053000026614E-23,
  6770. 'flb' => 3.80206452103492E-18,
  6771. 'BTU' => 1.51857982414846E-22,
  6772. 'btu' => 1.51857982414846E-22
  6773. ),
  6774. 'ev' => array( 'J' => 1.60219000146921E-19,
  6775. 'e' => 1.60218923136574E-12,
  6776. 'c' => 3.82933423195043E-20,
  6777. 'cal' => 3.82676978535648E-20,
  6778. 'eV' => 1.0,
  6779. 'ev' => 1.0,
  6780. 'HPh' => 5.96826078912344E-26,
  6781. 'hh' => 5.96826078912344E-26,
  6782. 'Wh' => 4.45053000026614E-23,
  6783. 'wh' => 4.45053000026614E-23,
  6784. 'flb' => 3.80206452103492E-18,
  6785. 'BTU' => 1.51857982414846E-22,
  6786. 'btu' => 1.51857982414846E-22
  6787. ),
  6788. 'HPh' => array( 'J' => 2.68451741316170E+06,
  6789. 'e' => 2.68451612283024E+13,
  6790. 'c' => 6.41616438565991E+05,
  6791. 'cal' => 6.41186757845835E+05,
  6792. 'eV' => 1.67553000000000E+25,
  6793. 'ev' => 1.67553000000000E+25,
  6794. 'HPh' => 1.0,
  6795. 'hh' => 1.0,
  6796. 'Wh' => 7.45699653134593E+02,
  6797. 'wh' => 7.45699653134593E+02,
  6798. 'flb' => 6.37047316692964E+07,
  6799. 'BTU' => 2.54442605275546E+03,
  6800. 'btu' => 2.54442605275546E+03
  6801. ),
  6802. 'hh' => array( 'J' => 2.68451741316170E+06,
  6803. 'e' => 2.68451612283024E+13,
  6804. 'c' => 6.41616438565991E+05,
  6805. 'cal' => 6.41186757845835E+05,
  6806. 'eV' => 1.67553000000000E+25,
  6807. 'ev' => 1.67553000000000E+25,
  6808. 'HPh' => 1.0,
  6809. 'hh' => 1.0,
  6810. 'Wh' => 7.45699653134593E+02,
  6811. 'wh' => 7.45699653134593E+02,
  6812. 'flb' => 6.37047316692964E+07,
  6813. 'BTU' => 2.54442605275546E+03,
  6814. 'btu' => 2.54442605275546E+03
  6815. ),
  6816. 'Wh' => array( 'J' => 3.59999820554720E+03,
  6817. 'e' => 3.59999647518369E+10,
  6818. 'c' => 8.60422069219046E+02,
  6819. 'cal' => 8.59845857713046E+02,
  6820. 'eV' => 2.24692340000000E+22,
  6821. 'ev' => 2.24692340000000E+22,
  6822. 'HPh' => 1.34102248243839E-03,
  6823. 'hh' => 1.34102248243839E-03,
  6824. 'Wh' => 1.0,
  6825. 'wh' => 1.0,
  6826. 'flb' => 8.54294774062316E+04,
  6827. 'BTU' => 3.41213254164705E+00,
  6828. 'btu' => 3.41213254164705E+00
  6829. ),
  6830. 'wh' => array( 'J' => 3.59999820554720E+03,
  6831. 'e' => 3.59999647518369E+10,
  6832. 'c' => 8.60422069219046E+02,
  6833. 'cal' => 8.59845857713046E+02,
  6834. 'eV' => 2.24692340000000E+22,
  6835. 'ev' => 2.24692340000000E+22,
  6836. 'HPh' => 1.34102248243839E-03,
  6837. 'hh' => 1.34102248243839E-03,
  6838. 'Wh' => 1.0,
  6839. 'wh' => 1.0,
  6840. 'flb' => 8.54294774062316E+04,
  6841. 'BTU' => 3.41213254164705E+00,
  6842. 'btu' => 3.41213254164705E+00
  6843. ),
  6844. 'flb' => array( 'J' => 4.21400003236424E-02,
  6845. 'e' => 4.21399800687660E+05,
  6846. 'c' => 1.00717234301644E-02,
  6847. 'cal' => 1.00649785509554E-02,
  6848. 'eV' => 2.63015000000000E+17,
  6849. 'ev' => 2.63015000000000E+17,
  6850. 'HPh' => 1.56974211145130E-08,
  6851. 'hh' => 1.56974211145130E-08,
  6852. 'Wh' => 1.17055614802000E-05,
  6853. 'wh' => 1.17055614802000E-05,
  6854. 'flb' => 1.0,
  6855. 'BTU' => 3.99409272448406E-05,
  6856. 'btu' => 3.99409272448406E-05
  6857. ),
  6858. 'BTU' => array( 'J' => 1.05505813786749E+03,
  6859. 'e' => 1.05505763074665E+10,
  6860. 'c' => 2.52165488508168E+02,
  6861. 'cal' => 2.51996617135510E+02,
  6862. 'eV' => 6.58510000000000E+21,
  6863. 'ev' => 6.58510000000000E+21,
  6864. 'HPh' => 3.93015941224568E-04,
  6865. 'hh' => 3.93015941224568E-04,
  6866. 'Wh' => 2.93071851047526E-01,
  6867. 'wh' => 2.93071851047526E-01,
  6868. 'flb' => 2.50369750774671E+04,
  6869. 'BTU' => 1.0,
  6870. 'btu' => 1.0,
  6871. ),
  6872. 'btu' => array( 'J' => 1.05505813786749E+03,
  6873. 'e' => 1.05505763074665E+10,
  6874. 'c' => 2.52165488508168E+02,
  6875. 'cal' => 2.51996617135510E+02,
  6876. 'eV' => 6.58510000000000E+21,
  6877. 'ev' => 6.58510000000000E+21,
  6878. 'HPh' => 3.93015941224568E-04,
  6879. 'hh' => 3.93015941224568E-04,
  6880. 'Wh' => 2.93071851047526E-01,
  6881. 'wh' => 2.93071851047526E-01,
  6882. 'flb' => 2.50369750774671E+04,
  6883. 'BTU' => 1.0,
  6884. 'btu' => 1.0,
  6885. )
  6886. ),
  6887. 'Power' => array( 'HP' => array( 'HP' => 1.0,
  6888. 'h' => 1.0,
  6889. 'W' => 7.45701000000000E+02,
  6890. 'w' => 7.45701000000000E+02
  6891. ),
  6892. 'h' => array( 'HP' => 1.0,
  6893. 'h' => 1.0,
  6894. 'W' => 7.45701000000000E+02,
  6895. 'w' => 7.45701000000000E+02
  6896. ),
  6897. 'W' => array( 'HP' => 1.34102006031908E-03,
  6898. 'h' => 1.34102006031908E-03,
  6899. 'W' => 1.0,
  6900. 'w' => 1.0
  6901. ),
  6902. 'w' => array( 'HP' => 1.34102006031908E-03,
  6903. 'h' => 1.34102006031908E-03,
  6904. 'W' => 1.0,
  6905. 'w' => 1.0
  6906. )
  6907. ),
  6908. 'Magnetism' => array( 'T' => array( 'T' => 1.0,
  6909. 'ga' => 10000.0
  6910. ),
  6911. 'ga' => array( 'T' => 0.0001,
  6912. 'ga' => 1.0
  6913. )
  6914. ),
  6915. 'Liquid' => array( 'tsp' => array( 'tsp' => 1.0,
  6916. 'tbs' => 3.33333333333333E-01,
  6917. 'oz' => 1.66666666666667E-01,
  6918. 'cup' => 2.08333333333333E-02,
  6919. 'pt' => 1.04166666666667E-02,
  6920. 'us_pt' => 1.04166666666667E-02,
  6921. 'uk_pt' => 8.67558516821960E-03,
  6922. 'qt' => 5.20833333333333E-03,
  6923. 'gal' => 1.30208333333333E-03,
  6924. 'l' => 4.92999408400710E-03,
  6925. 'lt' => 4.92999408400710E-03
  6926. ),
  6927. 'tbs' => array( 'tsp' => 3.00000000000000E+00,
  6928. 'tbs' => 1.0,
  6929. 'oz' => 5.00000000000000E-01,
  6930. 'cup' => 6.25000000000000E-02,
  6931. 'pt' => 3.12500000000000E-02,
  6932. 'us_pt' => 3.12500000000000E-02,
  6933. 'uk_pt' => 2.60267555046588E-02,
  6934. 'qt' => 1.56250000000000E-02,
  6935. 'gal' => 3.90625000000000E-03,
  6936. 'l' => 1.47899822520213E-02,
  6937. 'lt' => 1.47899822520213E-02
  6938. ),
  6939. 'oz' => array( 'tsp' => 6.00000000000000E+00,
  6940. 'tbs' => 2.00000000000000E+00,
  6941. 'oz' => 1.0,
  6942. 'cup' => 1.25000000000000E-01,
  6943. 'pt' => 6.25000000000000E-02,
  6944. 'us_pt' => 6.25000000000000E-02,
  6945. 'uk_pt' => 5.20535110093176E-02,
  6946. 'qt' => 3.12500000000000E-02,
  6947. 'gal' => 7.81250000000000E-03,
  6948. 'l' => 2.95799645040426E-02,
  6949. 'lt' => 2.95799645040426E-02
  6950. ),
  6951. 'cup' => array( 'tsp' => 4.80000000000000E+01,
  6952. 'tbs' => 1.60000000000000E+01,
  6953. 'oz' => 8.00000000000000E+00,
  6954. 'cup' => 1.0,
  6955. 'pt' => 5.00000000000000E-01,
  6956. 'us_pt' => 5.00000000000000E-01,
  6957. 'uk_pt' => 4.16428088074541E-01,
  6958. 'qt' => 2.50000000000000E-01,
  6959. 'gal' => 6.25000000000000E-02,
  6960. 'l' => 2.36639716032341E-01,
  6961. 'lt' => 2.36639716032341E-01
  6962. ),
  6963. 'pt' => array( 'tsp' => 9.60000000000000E+01,
  6964. 'tbs' => 3.20000000000000E+01,
  6965. 'oz' => 1.60000000000000E+01,
  6966. 'cup' => 2.00000000000000E+00,
  6967. 'pt' => 1.0,
  6968. 'us_pt' => 1.0,
  6969. 'uk_pt' => 8.32856176149081E-01,
  6970. 'qt' => 5.00000000000000E-01,
  6971. 'gal' => 1.25000000000000E-01,
  6972. 'l' => 4.73279432064682E-01,
  6973. 'lt' => 4.73279432064682E-01
  6974. ),
  6975. 'us_pt' => array( 'tsp' => 9.60000000000000E+01,
  6976. 'tbs' => 3.20000000000000E+01,
  6977. 'oz' => 1.60000000000000E+01,
  6978. 'cup' => 2.00000000000000E+00,
  6979. 'pt' => 1.0,
  6980. 'us_pt' => 1.0,
  6981. 'uk_pt' => 8.32856176149081E-01,
  6982. 'qt' => 5.00000000000000E-01,
  6983. 'gal' => 1.25000000000000E-01,
  6984. 'l' => 4.73279432064682E-01,
  6985. 'lt' => 4.73279432064682E-01
  6986. ),
  6987. 'uk_pt' => array( 'tsp' => 1.15266000000000E+02,
  6988. 'tbs' => 3.84220000000000E+01,
  6989. 'oz' => 1.92110000000000E+01,
  6990. 'cup' => 2.40137500000000E+00,
  6991. 'pt' => 1.20068750000000E+00,
  6992. 'us_pt' => 1.20068750000000E+00,
  6993. 'uk_pt' => 1.0,
  6994. 'qt' => 6.00343750000000E-01,
  6995. 'gal' => 1.50085937500000E-01,
  6996. 'l' => 5.68260698087162E-01,
  6997. 'lt' => 5.68260698087162E-01
  6998. ),
  6999. 'qt' => array( 'tsp' => 1.92000000000000E+02,
  7000. 'tbs' => 6.40000000000000E+01,
  7001. 'oz' => 3.20000000000000E+01,
  7002. 'cup' => 4.00000000000000E+00,
  7003. 'pt' => 2.00000000000000E+00,
  7004. 'us_pt' => 2.00000000000000E+00,
  7005. 'uk_pt' => 1.66571235229816E+00,
  7006. 'qt' => 1.0,
  7007. 'gal' => 2.50000000000000E-01,
  7008. 'l' => 9.46558864129363E-01,
  7009. 'lt' => 9.46558864129363E-01
  7010. ),
  7011. 'gal' => array( 'tsp' => 7.68000000000000E+02,
  7012. 'tbs' => 2.56000000000000E+02,
  7013. 'oz' => 1.28000000000000E+02,
  7014. 'cup' => 1.60000000000000E+01,
  7015. 'pt' => 8.00000000000000E+00,
  7016. 'us_pt' => 8.00000000000000E+00,
  7017. 'uk_pt' => 6.66284940919265E+00,
  7018. 'qt' => 4.00000000000000E+00,
  7019. 'gal' => 1.0,
  7020. 'l' => 3.78623545651745E+00,
  7021. 'lt' => 3.78623545651745E+00
  7022. ),
  7023. 'l' => array( 'tsp' => 2.02840000000000E+02,
  7024. 'tbs' => 6.76133333333333E+01,
  7025. 'oz' => 3.38066666666667E+01,
  7026. 'cup' => 4.22583333333333E+00,
  7027. 'pt' => 2.11291666666667E+00,
  7028. 'us_pt' => 2.11291666666667E+00,
  7029. 'uk_pt' => 1.75975569552166E+00,
  7030. 'qt' => 1.05645833333333E+00,
  7031. 'gal' => 2.64114583333333E-01,
  7032. 'l' => 1.0,
  7033. 'lt' => 1.0
  7034. ),
  7035. 'lt' => array( 'tsp' => 2.02840000000000E+02,
  7036. 'tbs' => 6.76133333333333E+01,
  7037. 'oz' => 3.38066666666667E+01,
  7038. 'cup' => 4.22583333333333E+00,
  7039. 'pt' => 2.11291666666667E+00,
  7040. 'us_pt' => 2.11291666666667E+00,
  7041. 'uk_pt' => 1.75975569552166E+00,
  7042. 'qt' => 1.05645833333333E+00,
  7043. 'gal' => 2.64114583333333E-01,
  7044. 'l' => 1.0,
  7045. 'lt' => 1.0
  7046. )
  7047. )
  7048. );
  7049. /**
  7050. * getConversionGroups
  7051. *
  7052. * @return array
  7053. */
  7054. public static function getConversionGroups() {
  7055. $conversionGroups = array();
  7056. foreach(self::$_conversionUnits as $conversionUnit) {
  7057. $conversionGroups[] = $conversionUnit['Group'];
  7058. }
  7059. return array_merge(array_unique($conversionGroups));
  7060. } // function getConversionGroups()
  7061. /**
  7062. * getConversionGroupUnits
  7063. *
  7064. * @return array
  7065. */
  7066. public static function getConversionGroupUnits($group = NULL) {
  7067. $conversionGroups = array();
  7068. foreach(self::$_conversionUnits as $conversionUnit => $conversionGroup) {
  7069. if ((is_null($group)) || ($conversionGroup['Group'] == $group)) {
  7070. $conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
  7071. }
  7072. }
  7073. return $conversionGroups;
  7074. } // function getConversionGroupUnits()
  7075. /**
  7076. * getConversionGroupUnitDetails
  7077. *
  7078. * @return array
  7079. */
  7080. public static function getConversionGroupUnitDetails($group = NULL) {
  7081. $conversionGroups = array();
  7082. foreach(self::$_conversionUnits as $conversionUnit => $conversionGroup) {
  7083. if ((is_null($group)) || ($conversionGroup['Group'] == $group)) {
  7084. $conversionGroups[$conversionGroup['Group']][] = array( 'unit' => $conversionUnit,
  7085. 'description' => $conversionGroup['Unit Name']
  7086. );
  7087. }
  7088. }
  7089. return $conversionGroups;
  7090. } // function getConversionGroupUnitDetails()
  7091. /**
  7092. * getConversionGroups
  7093. *
  7094. * @return array
  7095. */
  7096. public static function getConversionMultipliers() {
  7097. return self::$_conversionMultipliers;
  7098. } // function getConversionGroups()
  7099. /**
  7100. * CONVERTUOM
  7101. *
  7102. * @param float $value
  7103. * @param string $fromUOM
  7104. * @param string $toUOM
  7105. * @return float
  7106. */
  7107. public static function CONVERTUOM($value, $fromUOM, $toUOM) {
  7108. $value = self::flattenSingleValue($value);
  7109. $fromUOM = self::flattenSingleValue($fromUOM);
  7110. $toUOM = self::flattenSingleValue($toUOM);
  7111. if (!is_numeric($value)) {
  7112. return self::$_errorCodes['value'];
  7113. }
  7114. $fromMultiplier = 1;
  7115. if (isset(self::$_conversionUnits[$fromUOM])) {
  7116. $unitGroup1 = self::$_conversionUnits[$fromUOM]['Group'];
  7117. } else {
  7118. $fromMultiplier = substr($fromUOM,0,1);
  7119. $fromUOM = substr($fromUOM,1);
  7120. if (isset(self::$_conversionMultipliers[$fromMultiplier])) {
  7121. $fromMultiplier = self::$_conversionMultipliers[$fromMultiplier]['multiplier'];
  7122. } else {
  7123. return self::$_errorCodes['na'];
  7124. }
  7125. if ((isset(self::$_conversionUnits[$fromUOM])) && (self::$_conversionUnits[$fromUOM]['AllowPrefix'])) {
  7126. $unitGroup1 = self::$_conversionUnits[$fromUOM]['Group'];
  7127. } else {
  7128. return self::$_errorCodes['na'];
  7129. }
  7130. }
  7131. $value *= $fromMultiplier;
  7132. $toMultiplier = 1;
  7133. if (isset(self::$_conversionUnits[$toUOM])) {
  7134. $unitGroup2 = self::$_conversionUnits[$toUOM]['Group'];
  7135. } else {
  7136. $toMultiplier = substr($toUOM,0,1);
  7137. $toUOM = substr($toUOM,1);
  7138. if (isset(self::$_conversionMultipliers[$toMultiplier])) {
  7139. $toMultiplier = self::$_conversionMultipliers[$toMultiplier]['multiplier'];
  7140. } else {
  7141. return self::$_errorCodes['na'];
  7142. }
  7143. if ((isset(self::$_conversionUnits[$toUOM])) && (self::$_conversionUnits[$toUOM]['AllowPrefix'])) {
  7144. $unitGroup2 = self::$_conversionUnits[$toUOM]['Group'];
  7145. } else {
  7146. return self::$_errorCodes['na'];
  7147. }
  7148. }
  7149. if ($unitGroup1 != $unitGroup2) {
  7150. return self::$_errorCodes['na'];
  7151. }
  7152. if ($fromUOM == $toUOM) {
  7153. return 1.0;
  7154. } elseif ($unitGroup1 == 'Temperature') {
  7155. if (($fromUOM == 'F') || ($fromUOM == 'fah')) {
  7156. if (($toUOM == 'F') || ($toUOM == 'fah')) {
  7157. return 1.0;
  7158. } else {
  7159. $value = (($value - 32) / 1.8);
  7160. if (($toUOM == 'K') || ($toUOM == 'kel')) {
  7161. $value += 273.15;
  7162. }
  7163. return $value;
  7164. }
  7165. } elseif ((($fromUOM == 'K') || ($fromUOM == 'kel')) &&
  7166. (($toUOM == 'K') || ($toUOM == 'kel'))) {
  7167. return 1.0;
  7168. } elseif ((($fromUOM == 'C') || ($fromUOM == 'cel')) &&
  7169. (($toUOM == 'C') || ($toUOM == 'cel'))) {
  7170. return 1.0;
  7171. }
  7172. if (($toUOM == 'F') || ($toUOM == 'fah')) {
  7173. if (($fromUOM == 'K') || ($fromUOM == 'kel')) {
  7174. $value -= 273.15;
  7175. }
  7176. return ($value * 1.8) + 32;
  7177. }
  7178. if (($toUOM == 'C') || ($toUOM == 'cel')) {
  7179. return $value - 273.15;
  7180. }
  7181. return $value + 273.15;
  7182. }
  7183. return ($value * self::$_unitConversions[$unitGroup1][$fromUOM][$toUOM]) / $toMultiplier;
  7184. } // function CONVERTUOM()
  7185. /**
  7186. * BESSELI
  7187. *
  7188. * Returns the modified Bessel function, which is equivalent to the Bessel function evaluated for purely imaginary arguments
  7189. *
  7190. * @param float $x
  7191. * @param float $n
  7192. * @return int
  7193. */
  7194. public static function BESSELI($x, $n) {
  7195. $x = self::flattenSingleValue($x);
  7196. $n = floor(self::flattenSingleValue($n));
  7197. if ((is_numeric($x)) && (is_numeric($n))) {
  7198. if ($n < 0) {
  7199. return self::$_errorCodes['num'];
  7200. }
  7201. $f_2_PI = 2 * pi();
  7202. if (abs($x) <= 30) {
  7203. $fTerm = pow($x / 2, $n) / self::FACT($n);
  7204. $nK = 1;
  7205. $fResult = $fTerm;
  7206. $fSqrX = pow($x,2) / 4;
  7207. do {
  7208. $fTerm *= $fSqrX;
  7209. $fTerm /= ($nK * ($nK + $n));
  7210. $fResult += $fTerm;
  7211. } while ((abs($fTerm) > 1e-10) && (++$nK < 100));
  7212. } else {
  7213. $fXAbs = abs($x);
  7214. $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs);
  7215. if (($n && 1) && ($x < 0)) {
  7216. $fResult = -$fResult;
  7217. }
  7218. }
  7219. return $fResult;
  7220. }
  7221. return self::$_errorCodes['value'];
  7222. } // function BESSELI()
  7223. /**
  7224. * BESSELJ
  7225. *
  7226. * Returns the Bessel function
  7227. *
  7228. * @param float $x
  7229. * @param float $n
  7230. * @return int
  7231. */
  7232. public static function BESSELJ($x, $n) {
  7233. $x = self::flattenSingleValue($x);
  7234. $n = floor(self::flattenSingleValue($n));
  7235. if ((is_numeric($x)) && (is_numeric($n))) {
  7236. if ($n < 0) {
  7237. return self::$_errorCodes['num'];
  7238. }
  7239. $f_2_DIV_PI = 2 / pi();
  7240. $f_PI_DIV_2 = pi() / 2;
  7241. $f_PI_DIV_4 = pi() / 4;
  7242. $fResult = 0;
  7243. if (abs($x) <= 30) {
  7244. $fTerm = pow($x / 2, $n) / self::FACT($n);
  7245. $nK = 1;
  7246. $fResult = $fTerm;
  7247. $fSqrX = pow($x,2) / -4;
  7248. do {
  7249. $fTerm *= $fSqrX;
  7250. $fTerm /= ($nK * ($nK + $n));
  7251. $fResult += $fTerm;
  7252. } while ((abs($fTerm) > 1e-10) && (++$nK < 100));
  7253. } else {
  7254. $fXAbs = abs($x);
  7255. $fResult = sqrt($f_2_DIV_PI / $fXAbs) * cos($fXAbs - $n * $f_PI_DIV_2 - $f_PI_DIV_4);
  7256. if (($n && 1) && ($x < 0)) {
  7257. $fResult = -$fResult;
  7258. }
  7259. }
  7260. return $fResult;
  7261. }
  7262. return self::$_errorCodes['value'];
  7263. } // function BESSELJ()
  7264. private static function _Besselk0($fNum) {
  7265. if ($fNum <= 2) {
  7266. $fNum2 = $fNum * 0.5;
  7267. $y = pow($fNum2,2);
  7268. $fRet = -log($fNum2) * self::BESSELI($fNum, 0) +
  7269. (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
  7270. (0.10750e-3 + $y * 0.74e-5))))));
  7271. } else {
  7272. $y = 2 / $fNum;
  7273. $fRet = exp(-$fNum) / sqrt($fNum) *
  7274. (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
  7275. (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
  7276. }
  7277. return $fRet;
  7278. } // function _Besselk0()
  7279. private static function _Besselk1($fNum) {
  7280. if ($fNum <= 2) {
  7281. $fNum2 = $fNum * 0.5;
  7282. $y = pow($fNum2,2);
  7283. $fRet = log($fNum2) * self::BESSELI($fNum, 1) +
  7284. (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
  7285. (-0.110404e-2 + $y * (-0.4686e-4))))))) / $fNum;
  7286. } else {
  7287. $y = 2 / $fNum;
  7288. $fRet = exp(-$fNum) / sqrt($fNum) *
  7289. (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
  7290. (0.325614e-2 + $y * (-0.68245e-3)))))));
  7291. }
  7292. return $fRet;
  7293. } // function _Besselk1()
  7294. /**
  7295. * BESSELK
  7296. *
  7297. * Returns the modified Bessel function, which is equivalent to the Bessel functions evaluated for purely imaginary arguments.
  7298. *
  7299. * @param float $x
  7300. * @param float $ord
  7301. * @return float
  7302. */
  7303. public static function BESSELK($x, $ord) {
  7304. $x = self::flattenSingleValue($x);
  7305. $ord = floor(self::flattenSingleValue($ord));
  7306. if ((is_numeric($x)) && (is_numeric($ord))) {
  7307. if ($ord < 0) {
  7308. return self::$_errorCodes['num'];
  7309. }
  7310. switch($ord) {
  7311. case 0 : return self::_Besselk0($x);
  7312. break;
  7313. case 1 : return self::_Besselk1($x);
  7314. break;
  7315. default : $fTox = 2 / $x;
  7316. $fBkm = self::_Besselk0($x);
  7317. $fBk = self::_Besselk1($x);
  7318. for ($n = 1; $n < $ord; ++$n) {
  7319. $fBkp = $fBkm + $n * $fTox * $fBk;
  7320. $fBkm = $fBk;
  7321. $fBk = $fBkp;
  7322. }
  7323. }
  7324. return $fBk;
  7325. }
  7326. return self::$_errorCodes['value'];
  7327. } // function BESSELK()
  7328. private static function _Bessely0($fNum) {
  7329. if ($fNum < 8.0) {
  7330. $y = pow($fNum,2);
  7331. $f1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y * (-86327.92757 + $y * 228.4622733))));
  7332. $f2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y * (47447.26470 + $y * (226.1030244 + $y))));
  7333. $fRet = $f1 / $f2 + 0.636619772 * self::BESSELJ($fNum, 0) * log($fNum);
  7334. } else {
  7335. $z = 8.0 / $fNum;
  7336. $y = pow($z,2);
  7337. $xx = $fNum - 0.785398164;
  7338. $f1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
  7339. $f2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y * (-0.934945152e-7))));
  7340. $fRet = sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
  7341. }
  7342. return $fRet;
  7343. } // function _Bessely0()
  7344. private static function _Bessely1($fNum) {
  7345. if ($fNum < 8.0) {
  7346. $y = pow($fNum,2);
  7347. $f1 = $fNum * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y * (0.7349264551e9 + $y *
  7348. (-0.4237922726e7 + $y * 0.8511937935e4)))));
  7349. $f2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
  7350. (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
  7351. $fRet = $f1 / $f2 + 0.636619772 * ( self::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum);
  7352. } else {
  7353. $z = 8.0 / $fNum;
  7354. $y = pow($z,2);
  7355. $xx = $fNum - 2.356194491;
  7356. $f1 = 1 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e6))));
  7357. $f2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y * (-0.88228987e-6 + $y * 0.105787412e-6)));
  7358. $fRet = sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
  7359. #i12430# ...but this seems to work much better.
  7360. // $fRet = sqrt(0.636619772 / $fNum) * sin($fNum - 2.356194491);
  7361. }
  7362. return $fRet;
  7363. } // function _Bessely1()
  7364. /**
  7365. * BESSELY
  7366. *
  7367. * Returns the Bessel function, which is also called the Weber function or the Neumann function.
  7368. *
  7369. * @param float $x
  7370. * @param float $n
  7371. * @return int
  7372. */
  7373. public static function BESSELY($x, $ord) {
  7374. $x = self::flattenSingleValue($x);
  7375. $ord = floor(self::flattenSingleValue($ord));
  7376. if ((is_numeric($x)) && (is_numeric($ord))) {
  7377. if ($ord < 0) {
  7378. return self::$_errorCodes['num'];
  7379. }
  7380. switch($ord) {
  7381. case 0 : return self::_Bessely0($x);
  7382. break;
  7383. case 1 : return self::_Bessely1($x);
  7384. break;
  7385. default: $fTox = 2 / $x;
  7386. $fBym = self::_Bessely0($x);
  7387. $fBy = self::_Bessely1($x);
  7388. for ($n = 1; $n < $ord; ++$n) {
  7389. $fByp = $n * $fTox * $fBy - $fBym;
  7390. $fBym = $fBy;
  7391. $fBy = $fByp;
  7392. }
  7393. }
  7394. return $fBy;
  7395. }
  7396. return self::$_errorCodes['value'];
  7397. } // function BESSELY()
  7398. /**
  7399. * DELTA
  7400. *
  7401. * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
  7402. *
  7403. * @param float $a
  7404. * @param float $b
  7405. * @return int
  7406. */
  7407. public static function DELTA($a, $b=0) {
  7408. $a = self::flattenSingleValue($a);
  7409. $b = self::flattenSingleValue($b);
  7410. return (int) ($a == $b);
  7411. } // function DELTA()
  7412. /**
  7413. * GESTEP
  7414. *
  7415. * Returns 1 if number = step; returns 0 (zero) otherwise
  7416. *
  7417. * @param float $number
  7418. * @param float $step
  7419. * @return int
  7420. */
  7421. public static function GESTEP($number, $step=0) {
  7422. $number = self::flattenSingleValue($number);
  7423. $step = self::flattenSingleValue($step);
  7424. return (int) ($number >= $step);
  7425. } // function GESTEP()
  7426. //
  7427. // Private method to calculate the erf value
  7428. //
  7429. private static $_two_sqrtpi = 1.128379167095512574;
  7430. private static $_rel_error = 1E-15;
  7431. private static function _erfVal($x) {
  7432. if (abs($x) > 2.2) {
  7433. return 1 - self::_erfcVal($x);
  7434. }
  7435. $sum = $term = $x;
  7436. $xsqr = pow($x,2);
  7437. $j = 1;
  7438. do {
  7439. $term *= $xsqr / $j;
  7440. $sum -= $term / (2 * $j + 1);
  7441. ++$j;
  7442. $term *= $xsqr / $j;
  7443. $sum += $term / (2 * $j + 1);
  7444. ++$j;
  7445. if ($sum == 0) {
  7446. break;
  7447. }
  7448. } while (abs($term / $sum) > self::$_rel_error);
  7449. return self::$_two_sqrtpi * $sum;
  7450. } // function _erfVal()
  7451. /**
  7452. * ERF
  7453. *
  7454. * Returns the error function integrated between lower_limit and upper_limit
  7455. *
  7456. * @param float $lower lower bound for integrating ERF
  7457. * @param float $upper upper bound for integrating ERF.
  7458. * If omitted, ERF integrates between zero and lower_limit
  7459. * @return int
  7460. */
  7461. public static function ERF($lower, $upper = null) {
  7462. $lower = self::flattenSingleValue($lower);
  7463. $upper = self::flattenSingleValue($upper);
  7464. if (is_numeric($lower)) {
  7465. if ($lower < 0) {
  7466. return self::$_errorCodes['num'];
  7467. }
  7468. if (is_null($upper)) {
  7469. return self::_erfVal($lower);
  7470. }
  7471. if (is_numeric($upper)) {
  7472. if ($upper < 0) {
  7473. return self::$_errorCodes['num'];
  7474. }
  7475. return self::_erfVal($upper) - self::_erfVal($lower);
  7476. }
  7477. }
  7478. return self::$_errorCodes['value'];
  7479. } // function ERF()
  7480. //
  7481. // Private method to calculate the erfc value
  7482. //
  7483. private static $_one_sqrtpi = 0.564189583547756287;
  7484. private static function _erfcVal($x) {
  7485. if (abs($x) < 2.2) {
  7486. return 1 - self::_erfVal($x);
  7487. }
  7488. if ($x < 0) {
  7489. return 2 - self::erfc(-$x);
  7490. }
  7491. $a = $n = 1;
  7492. $b = $c = $x;
  7493. $d = pow($x,2) + 0.5;
  7494. $q1 = $q2 = $b / $d;
  7495. $t = 0;
  7496. do {
  7497. $t = $a * $n + $b * $x;
  7498. $a = $b;
  7499. $b = $t;
  7500. $t = $c * $n + $d * $x;
  7501. $c = $d;
  7502. $d = $t;
  7503. $n += 0.5;
  7504. $q1 = $q2;
  7505. $q2 = $b / $d;
  7506. } while ((abs($q1 - $q2) / $q2) > self::$_rel_error);
  7507. return self::$_one_sqrtpi * exp(-$x * $x) * $q2;
  7508. } // function _erfcVal()
  7509. /**
  7510. * ERFC
  7511. *
  7512. * Returns the complementary ERF function integrated between x and infinity
  7513. *
  7514. * @param float $x The lower bound for integrating ERF
  7515. * @return int
  7516. */
  7517. public static function ERFC($x) {
  7518. $x = self::flattenSingleValue($x);
  7519. if (is_numeric($x)) {
  7520. if ($x < 0) {
  7521. return self::$_errorCodes['num'];
  7522. }
  7523. return self::_erfcVal($x);
  7524. }
  7525. return self::$_errorCodes['value'];
  7526. } // function ERFC()
  7527. /**
  7528. * LOWERCASE
  7529. *
  7530. * Converts a string value to upper case.
  7531. *
  7532. * @param string $mixedCaseString
  7533. * @return string
  7534. */
  7535. public static function LOWERCASE($mixedCaseString) {
  7536. $mixedCaseString = self::flattenSingleValue($mixedCaseString);
  7537. if (function_exists('mb_convert_case')) {
  7538. return mb_convert_case($mixedCaseString, MB_CASE_LOWER, 'UTF-8');
  7539. } else {
  7540. return strtoupper($mixedCaseString);
  7541. }
  7542. } // function LOWERCASE()
  7543. /**
  7544. * UPPERCASE
  7545. *
  7546. * Converts a string value to upper case.
  7547. *
  7548. * @param string $mixedCaseString
  7549. * @return string
  7550. */
  7551. public static function UPPERCASE($mixedCaseString) {
  7552. $mixedCaseString = self::flattenSingleValue($mixedCaseString);
  7553. if (function_exists('mb_convert_case')) {
  7554. return mb_convert_case($mixedCaseString, MB_CASE_UPPER, 'UTF-8');
  7555. } else {
  7556. return strtoupper($mixedCaseString);
  7557. }
  7558. } // function UPPERCASE()
  7559. /**
  7560. * PROPERCASE
  7561. *
  7562. * Converts a string value to upper case.
  7563. *
  7564. * @param string $mixedCaseString
  7565. * @return string
  7566. */
  7567. public static function PROPERCASE($mixedCaseString) {
  7568. $mixedCaseString = self::flattenSingleValue($mixedCaseString);
  7569. if (function_exists('mb_convert_case')) {
  7570. return mb_convert_case($mixedCaseString, MB_CASE_TITLE, 'UTF-8');
  7571. } else {
  7572. return ucwords($mixedCaseString);
  7573. }
  7574. } // function PROPERCASE()
  7575. /**
  7576. * DOLLAR
  7577. *
  7578. * This function converts a number to text using currency format, with the decimals rounded to the specified place.
  7579. * The format used is $#,##0.00_);($#,##0.00)..
  7580. *
  7581. * @param float $value The value to format
  7582. * @param int $decimals The number of digits to display to the right of the decimal point.
  7583. * If decimals is negative, number is rounded to the left of the decimal point.
  7584. * If you omit decimals, it is assumed to be 2
  7585. * @return string
  7586. */
  7587. public static function DOLLAR($value = 0, $decimals = 2) {
  7588. $value = self::flattenSingleValue($value);
  7589. $decimals = self::flattenSingleValue($decimals);
  7590. // Validate parameters
  7591. if (!is_numeric($value) || !is_numeric($decimals)) {
  7592. return self::$_errorCodes['num'];
  7593. }
  7594. $decimals = floor($decimals);
  7595. if ($decimals > 0) {
  7596. return money_format('%.'.$decimals.'n',$value);
  7597. } else {
  7598. $round = pow(10,abs($decimals));
  7599. if ($value < 0) { $round = 0-$round; }
  7600. $value = self::MROUND($value,$round);
  7601. // The implementation of money_format used if the standard PHP function is not available can't handle decimal places of 0,
  7602. // so we display to 1 dp and chop off that character and the decimal separator using substr
  7603. return substr(money_format('%.1n',$value),0,-2);
  7604. }
  7605. } // function DOLLAR()
  7606. /**
  7607. * DOLLARDE
  7608. *
  7609. * Converts a dollar price expressed as an integer part and a fraction part into a dollar price expressed as a decimal number.
  7610. * Fractional dollar numbers are sometimes used for security prices.
  7611. *
  7612. * @param float $fractional_dollar Fractional Dollar
  7613. * @param int $fraction Fraction
  7614. * @return float
  7615. */
  7616. public static function DOLLARDE($fractional_dollar = Null, $fraction = 0) {
  7617. $fractional_dollar = self::flattenSingleValue($fractional_dollar);
  7618. $fraction = (int)self::flattenSingleValue($fraction);
  7619. // Validate parameters
  7620. if (is_null($fractional_dollar) || $fraction < 0) {
  7621. return self::$_errorCodes['num'];
  7622. }
  7623. if ($fraction == 0) {
  7624. return self::$_errorCodes['divisionbyzero'];
  7625. }
  7626. $dollars = floor($fractional_dollar);
  7627. $cents = fmod($fractional_dollar,1);
  7628. $cents /= $fraction;
  7629. $cents *= pow(10,ceil(log10($fraction)));
  7630. return $dollars + $cents;
  7631. } // function DOLLARDE()
  7632. /**
  7633. * DOLLARFR
  7634. *
  7635. * Converts a dollar price expressed as a decimal number into a dollar price expressed as a fraction.
  7636. * Fractional dollar numbers are sometimes used for security prices.
  7637. *
  7638. * @param float $decimal_dollar Decimal Dollar
  7639. * @param int $fraction Fraction
  7640. * @return float
  7641. */
  7642. public static function DOLLARFR($decimal_dollar = Null, $fraction = 0) {
  7643. $decimal_dollar = self::flattenSingleValue($decimal_dollar);
  7644. $fraction = (int)self::flattenSingleValue($fraction);
  7645. // Validate parameters
  7646. if (is_null($decimal_dollar) || $fraction < 0) {
  7647. return self::$_errorCodes['num'];
  7648. }
  7649. if ($fraction == 0) {
  7650. return self::$_errorCodes['divisionbyzero'];
  7651. }
  7652. $dollars = floor($decimal_dollar);
  7653. $cents = fmod($decimal_dollar,1);
  7654. $cents *= $fraction;
  7655. $cents *= pow(10,-ceil(log10($fraction)));
  7656. return $dollars + $cents;
  7657. } // function DOLLARFR()
  7658. /**
  7659. * EFFECT
  7660. *
  7661. * Returns the effective interest rate given the nominal rate and the number of compounding payments per year.
  7662. *
  7663. * @param float $nominal_rate Nominal interest rate
  7664. * @param int $npery Number of compounding payments per year
  7665. * @return float
  7666. */
  7667. public static function EFFECT($nominal_rate = 0, $npery = 0) {
  7668. $nominal_rate = self::flattenSingleValue($nominal_rate);
  7669. $npery = (int)self::flattenSingleValue($npery);
  7670. // Validate parameters
  7671. if ($nominal_rate <= 0 || $npery < 1) {
  7672. return self::$_errorCodes['num'];
  7673. }
  7674. return pow((1 + $nominal_rate / $npery), $npery) - 1;
  7675. } // function EFFECT()
  7676. /**
  7677. * NOMINAL
  7678. *
  7679. * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
  7680. *
  7681. * @param float $effect_rate Effective interest rate
  7682. * @param int $npery Number of compounding payments per year
  7683. * @return float
  7684. */
  7685. public static function NOMINAL($effect_rate = 0, $npery = 0) {
  7686. $effect_rate = self::flattenSingleValue($effect_rate);
  7687. $npery = (int)self::flattenSingleValue($npery);
  7688. // Validate parameters
  7689. if ($effect_rate <= 0 || $npery < 1) {
  7690. return self::$_errorCodes['num'];
  7691. }
  7692. // Calculate
  7693. return $npery * (pow($effect_rate + 1, 1 / $npery) - 1);
  7694. } // function NOMINAL()
  7695. /**
  7696. * PV
  7697. *
  7698. * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
  7699. *
  7700. * @param float $rate Interest rate per period
  7701. * @param int $nper Number of periods
  7702. * @param float $pmt Periodic payment (annuity)
  7703. * @param float $fv Future Value
  7704. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7705. * @return float
  7706. */
  7707. public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0) {
  7708. $rate = self::flattenSingleValue($rate);
  7709. $nper = self::flattenSingleValue($nper);
  7710. $pmt = self::flattenSingleValue($pmt);
  7711. $fv = self::flattenSingleValue($fv);
  7712. $type = self::flattenSingleValue($type);
  7713. // Validate parameters
  7714. if ($type != 0 && $type != 1) {
  7715. return self::$_errorCodes['num'];
  7716. }
  7717. // Calculate
  7718. if (!is_null($rate) && $rate != 0) {
  7719. return (-$pmt * (1 + $rate * $type) * ((pow(1 + $rate, $nper) - 1) / $rate) - $fv) / pow(1 + $rate, $nper);
  7720. } else {
  7721. return -$fv - $pmt * $nper;
  7722. }
  7723. } // function PV()
  7724. /**
  7725. * FV
  7726. *
  7727. * Returns the Future Value of a cash flow with constant payments and interest rate (annuities).
  7728. *
  7729. * @param float $rate Interest rate per period
  7730. * @param int $nper Number of periods
  7731. * @param float $pmt Periodic payment (annuity)
  7732. * @param float $pv Present Value
  7733. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7734. * @return float
  7735. */
  7736. public static function FV($rate = 0, $nper = 0, $pmt = 0, $pv = 0, $type = 0) {
  7737. $rate = self::flattenSingleValue($rate);
  7738. $nper = self::flattenSingleValue($nper);
  7739. $pmt = self::flattenSingleValue($pmt);
  7740. $pv = self::flattenSingleValue($pv);
  7741. $type = self::flattenSingleValue($type);
  7742. // Validate parameters
  7743. if ($type != 0 && $type != 1) {
  7744. return self::$_errorCodes['num'];
  7745. }
  7746. // Calculate
  7747. if (!is_null($rate) && $rate != 0) {
  7748. return -$pv * pow(1 + $rate, $nper) - $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1) / $rate;
  7749. } else {
  7750. return -$pv - $pmt * $nper;
  7751. }
  7752. } // function FV()
  7753. /**
  7754. * PMT
  7755. *
  7756. * Returns the constant payment (annuity) for a cash flow with a constant interest rate.
  7757. *
  7758. * @param float $rate Interest rate per period
  7759. * @param int $nper Number of periods
  7760. * @param float $pv Present Value
  7761. * @param float $fv Future Value
  7762. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7763. * @return float
  7764. */
  7765. public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0) {
  7766. $rate = self::flattenSingleValue($rate);
  7767. $nper = self::flattenSingleValue($nper);
  7768. $pv = self::flattenSingleValue($pv);
  7769. $fv = self::flattenSingleValue($fv);
  7770. $type = self::flattenSingleValue($type);
  7771. // Validate parameters
  7772. if ($type != 0 && $type != 1) {
  7773. return self::$_errorCodes['num'];
  7774. }
  7775. // Calculate
  7776. if (!is_null($rate) && $rate != 0) {
  7777. return (-$fv - $pv * pow(1 + $rate, $nper)) / (1 + $rate * $type) / ((pow(1 + $rate, $nper) - 1) / $rate);
  7778. } else {
  7779. return (-$pv - $fv) / $nper;
  7780. }
  7781. } // function PMT()
  7782. /**
  7783. * NPER
  7784. *
  7785. * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
  7786. *
  7787. * @param float $rate Interest rate per period
  7788. * @param int $pmt Periodic payment (annuity)
  7789. * @param float $pv Present Value
  7790. * @param float $fv Future Value
  7791. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7792. * @return float
  7793. */
  7794. public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0) {
  7795. $rate = self::flattenSingleValue($rate);
  7796. $pmt = self::flattenSingleValue($pmt);
  7797. $pv = self::flattenSingleValue($pv);
  7798. $fv = self::flattenSingleValue($fv);
  7799. $type = self::flattenSingleValue($type);
  7800. // Validate parameters
  7801. if ($type != 0 && $type != 1) {
  7802. return self::$_errorCodes['num'];
  7803. }
  7804. // Calculate
  7805. if (!is_null($rate) && $rate != 0) {
  7806. if ($pmt == 0 && $pv == 0) {
  7807. return self::$_errorCodes['num'];
  7808. }
  7809. return log(($pmt * (1 + $rate * $type) / $rate - $fv) / ($pv + $pmt * (1 + $rate * $type) / $rate)) / log(1 + $rate);
  7810. } else {
  7811. if ($pmt == 0) {
  7812. return self::$_errorCodes['num'];
  7813. }
  7814. return (-$pv -$fv) / $pmt;
  7815. }
  7816. } // function NPER()
  7817. private static function _interestAndPrincipal($rate=0, $per=0, $nper=0, $pv=0, $fv=0, $type=0) {
  7818. $pmt = self::PMT($rate, $nper, $pv, $fv, $type);
  7819. $capital = $pv;
  7820. for ($i = 1; $i<= $per; ++$i) {
  7821. $interest = ($type && $i == 1)? 0 : -$capital * $rate;
  7822. $principal = $pmt - $interest;
  7823. $capital += $principal;
  7824. }
  7825. return array($interest, $principal);
  7826. } // function _interestAndPrincipal()
  7827. /**
  7828. * IPMT
  7829. *
  7830. * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
  7831. *
  7832. * @param float $rate Interest rate per period
  7833. * @param int $per Period for which we want to find the interest
  7834. * @param int $nper Number of periods
  7835. * @param float $pv Present Value
  7836. * @param float $fv Future Value
  7837. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7838. * @return float
  7839. */
  7840. public static function IPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0) {
  7841. $rate = self::flattenSingleValue($rate);
  7842. $per = (int) self::flattenSingleValue($per);
  7843. $nper = (int) self::flattenSingleValue($nper);
  7844. $pv = self::flattenSingleValue($pv);
  7845. $fv = self::flattenSingleValue($fv);
  7846. $type = (int) self::flattenSingleValue($type);
  7847. // Validate parameters
  7848. if ($type != 0 && $type != 1) {
  7849. return self::$_errorCodes['num'];
  7850. }
  7851. if ($per <= 0 || $per > $nper) {
  7852. return self::$_errorCodes['value'];
  7853. }
  7854. // Calculate
  7855. $interestAndPrincipal = self::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
  7856. return $interestAndPrincipal[0];
  7857. } // function IPMT()
  7858. /**
  7859. * CUMIPMT
  7860. *
  7861. * Returns the cumulative interest paid on a loan between start_period and end_period.
  7862. *
  7863. * @param float $rate Interest rate per period
  7864. * @param int $nper Number of periods
  7865. * @param float $pv Present Value
  7866. * @param int start The first period in the calculation.
  7867. * Payment periods are numbered beginning with 1.
  7868. * @param int end The last period in the calculation.
  7869. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7870. * @return float
  7871. */
  7872. public static function CUMIPMT($rate, $nper, $pv, $start, $end, $type = 0) {
  7873. $rate = self::flattenSingleValue($rate);
  7874. $nper = (int) self::flattenSingleValue($nper);
  7875. $pv = self::flattenSingleValue($pv);
  7876. $start = (int) self::flattenSingleValue($start);
  7877. $end = (int) self::flattenSingleValue($end);
  7878. $type = (int) self::flattenSingleValue($type);
  7879. // Validate parameters
  7880. if ($type != 0 && $type != 1) {
  7881. return self::$_errorCodes['num'];
  7882. }
  7883. if ($start < 1 || $start > $end) {
  7884. return self::$_errorCodes['value'];
  7885. }
  7886. // Calculate
  7887. $interest = 0;
  7888. for ($per = $start; $per <= $end; ++$per) {
  7889. $interest += self::IPMT($rate, $per, $nper, $pv, 0, $type);
  7890. }
  7891. return $interest;
  7892. } // function CUMIPMT()
  7893. /**
  7894. * PPMT
  7895. *
  7896. * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
  7897. *
  7898. * @param float $rate Interest rate per period
  7899. * @param int $per Period for which we want to find the interest
  7900. * @param int $nper Number of periods
  7901. * @param float $pv Present Value
  7902. * @param float $fv Future Value
  7903. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7904. * @return float
  7905. */
  7906. public static function PPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0) {
  7907. $rate = self::flattenSingleValue($rate);
  7908. $per = (int) self::flattenSingleValue($per);
  7909. $nper = (int) self::flattenSingleValue($nper);
  7910. $pv = self::flattenSingleValue($pv);
  7911. $fv = self::flattenSingleValue($fv);
  7912. $type = (int) self::flattenSingleValue($type);
  7913. // Validate parameters
  7914. if ($type != 0 && $type != 1) {
  7915. return self::$_errorCodes['num'];
  7916. }
  7917. if ($per <= 0 || $per > $nper) {
  7918. return self::$_errorCodes['value'];
  7919. }
  7920. // Calculate
  7921. $interestAndPrincipal = self::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
  7922. return $interestAndPrincipal[1];
  7923. } // function PPMT()
  7924. /**
  7925. * CUMPRINC
  7926. *
  7927. * Returns the cumulative principal paid on a loan between start_period and end_period.
  7928. *
  7929. * @param float $rate Interest rate per period
  7930. * @param int $nper Number of periods
  7931. * @param float $pv Present Value
  7932. * @param int start The first period in the calculation.
  7933. * Payment periods are numbered beginning with 1.
  7934. * @param int end The last period in the calculation.
  7935. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7936. * @return float
  7937. */
  7938. public static function CUMPRINC($rate, $nper, $pv, $start, $end, $type = 0) {
  7939. $rate = self::flattenSingleValue($rate);
  7940. $nper = (int) self::flattenSingleValue($nper);
  7941. $pv = self::flattenSingleValue($pv);
  7942. $start = (int) self::flattenSingleValue($start);
  7943. $end = (int) self::flattenSingleValue($end);
  7944. $type = (int) self::flattenSingleValue($type);
  7945. // Validate parameters
  7946. if ($type != 0 && $type != 1) {
  7947. return self::$_errorCodes['num'];
  7948. }
  7949. if ($start < 1 || $start > $end) {
  7950. return self::$_errorCodes['value'];
  7951. }
  7952. // Calculate
  7953. $principal = 0;
  7954. for ($per = $start; $per <= $end; ++$per) {
  7955. $principal += self::PPMT($rate, $per, $nper, $pv, 0, $type);
  7956. }
  7957. return $principal;
  7958. } // function CUMPRINC()
  7959. /**
  7960. * NPV
  7961. *
  7962. * Returns the Net Present Value of a cash flow series given a discount rate.
  7963. *
  7964. * @param float Discount interest rate
  7965. * @param array Cash flow series
  7966. * @return float
  7967. */
  7968. public static function NPV() {
  7969. // Return value
  7970. $returnValue = 0;
  7971. // Loop trough arguments
  7972. $aArgs = self::flattenArray(func_get_args());
  7973. // Calculate
  7974. $rate = array_shift($aArgs);
  7975. for ($i = 1; $i <= count($aArgs); ++$i) {
  7976. // Is it a numeric value?
  7977. if (is_numeric($aArgs[$i - 1])) {
  7978. $returnValue += $aArgs[$i - 1] / pow(1 + $rate, $i);
  7979. }
  7980. }
  7981. // Return
  7982. return $returnValue;
  7983. } // function NPV()
  7984. /**
  7985. * DB
  7986. *
  7987. * Returns the depreciation of an asset for a specified period using the fixed-declining balance method.
  7988. * This form of depreciation is used if you want to get a higher depreciation value at the beginning of the depreciation
  7989. * (as opposed to linear depreciation). The depreciation value is reduced with every depreciation period by the
  7990. * depreciation already deducted from the initial cost.
  7991. *
  7992. * @param float cost Initial cost of the asset.
  7993. * @param float salvage Value at the end of the depreciation. (Sometimes called the salvage value of the asset)
  7994. * @param int life Number of periods over which the asset is depreciated. (Sometimes called the useful life of the asset)
  7995. * @param int period The period for which you want to calculate the depreciation. Period must use the same units as life.
  7996. * @param float month Number of months in the first year. If month is omitted, it defaults to 12.
  7997. * @return float
  7998. */
  7999. public static function DB($cost, $salvage, $life, $period, $month=12) {
  8000. $cost = (float) self::flattenSingleValue($cost);
  8001. $salvage = (float) self::flattenSingleValue($salvage);
  8002. $life = (int) self::flattenSingleValue($life);
  8003. $period = (int) self::flattenSingleValue($period);
  8004. $month = (int) self::flattenSingleValue($month);
  8005. // Validate
  8006. if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($month))) {
  8007. if ($cost == 0) {
  8008. return 0.0;
  8009. } elseif (($cost < 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($month < 1)) {
  8010. return self::$_errorCodes['num'];
  8011. }
  8012. // Set Fixed Depreciation Rate
  8013. $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life));
  8014. $fixedDepreciationRate = round($fixedDepreciationRate, 3);
  8015. // Loop through each period calculating the depreciation
  8016. $previousDepreciation = 0;
  8017. for ($per = 1; $per <= $period; ++$per) {
  8018. if ($per == 1) {
  8019. $depreciation = $cost * $fixedDepreciationRate * $month / 12;
  8020. } elseif ($per == ($life + 1)) {
  8021. $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate * (12 - $month) / 12;
  8022. } else {
  8023. $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate;
  8024. }
  8025. $previousDepreciation += $depreciation;
  8026. }
  8027. return $depreciation;
  8028. }
  8029. return self::$_errorCodes['value'];
  8030. } // function DB()
  8031. /**
  8032. * DDB
  8033. *
  8034. * Returns the depreciation of an asset for a specified period using the double-declining balance method or some other method you specify.
  8035. *
  8036. * @param float cost Initial cost of the asset.
  8037. * @param float salvage Value at the end of the depreciation. (Sometimes called the salvage value of the asset)
  8038. * @param int life Number of periods over which the asset is depreciated. (Sometimes called the useful life of the asset)
  8039. * @param int period The period for which you want to calculate the depreciation. Period must use the same units as life.
  8040. * @param float factor The rate at which the balance declines.
  8041. * If factor is omitted, it is assumed to be 2 (the double-declining balance method).
  8042. * @return float
  8043. */
  8044. public static function DDB($cost, $salvage, $life, $period, $factor=2.0) {
  8045. $cost = (float) self::flattenSingleValue($cost);
  8046. $salvage = (float) self::flattenSingleValue($salvage);
  8047. $life = (int) self::flattenSingleValue($life);
  8048. $period = (int) self::flattenSingleValue($period);
  8049. $factor = (float) self::flattenSingleValue($factor);
  8050. // Validate
  8051. if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($factor))) {
  8052. if (($cost <= 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($factor <= 0.0) || ($period > $life)) {
  8053. return self::$_errorCodes['num'];
  8054. }
  8055. // Set Fixed Depreciation Rate
  8056. $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life));
  8057. $fixedDepreciationRate = round($fixedDepreciationRate, 3);
  8058. // Loop through each period calculating the depreciation
  8059. $previousDepreciation = 0;
  8060. for ($per = 1; $per <= $period; ++$per) {
  8061. $depreciation = min( ($cost - $previousDepreciation) * ($factor / $life), ($cost - $salvage - $previousDepreciation) );
  8062. $previousDepreciation += $depreciation;
  8063. }
  8064. return $depreciation;
  8065. }
  8066. return self::$_errorCodes['value'];
  8067. } // function DDB()
  8068. private static function _daysPerYear($year,$basis) {
  8069. switch ($basis) {
  8070. case 0 :
  8071. case 2 :
  8072. case 4 :
  8073. $daysPerYear = 360;
  8074. break;
  8075. case 3 :
  8076. $daysPerYear = 365;
  8077. break;
  8078. case 1 :
  8079. if (self::_isLeapYear(self::YEAR($year))) {
  8080. $daysPerYear = 366;
  8081. } else {
  8082. $daysPerYear = 365;
  8083. }
  8084. break;
  8085. default :
  8086. return self::$_errorCodes['num'];
  8087. }
  8088. return $daysPerYear;
  8089. } // function _daysPerYear()
  8090. /**
  8091. * ACCRINT
  8092. *
  8093. * Returns the discount rate for a security.
  8094. *
  8095. * @param mixed issue The security's issue date.
  8096. * @param mixed firstinter The security's first interest date.
  8097. * @param mixed settlement The security's settlement date.
  8098. * @param float rate The security's annual coupon rate.
  8099. * @param float par The security's par value.
  8100. * @param int basis The type of day count to use.
  8101. * 0 or omitted US (NASD) 30/360
  8102. * 1 Actual/actual
  8103. * 2 Actual/360
  8104. * 3 Actual/365
  8105. * 4 European 30/360
  8106. * @return float
  8107. */
  8108. public static function ACCRINT($issue, $firstinter, $settlement, $rate, $par=1000, $frequency=1, $basis=0) {
  8109. $issue = self::flattenSingleValue($issue);
  8110. $firstinter = self::flattenSingleValue($firstinter);
  8111. $settlement = self::flattenSingleValue($settlement);
  8112. $rate = (float) self::flattenSingleValue($rate);
  8113. $par = (is_null($par)) ? 1000 : (float) self::flattenSingleValue($par);
  8114. $frequency = (is_null($frequency)) ? 1 : (int) self::flattenSingleValue($frequency);
  8115. $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
  8116. // Validate
  8117. if ((is_numeric($rate)) && (is_numeric($par))) {
  8118. if (($rate <= 0) || ($par <= 0)) {
  8119. return self::$_errorCodes['num'];
  8120. }
  8121. $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
  8122. if (!is_numeric($daysBetweenIssueAndSettlement)) {
  8123. return $daysBetweenIssueAndSettlement;
  8124. }
  8125. $daysPerYear = self::_daysPerYear(self::YEAR($issue),$basis);
  8126. if (!is_numeric($daysPerYear)) {
  8127. return $daysPerYear;
  8128. }
  8129. $daysBetweenIssueAndSettlement *= $daysPerYear;
  8130. return $par * $rate * ($daysBetweenIssueAndSettlement / $daysPerYear);
  8131. }
  8132. return self::$_errorCodes['value'];
  8133. } // function ACCRINT()
  8134. /**
  8135. * ACCRINTM
  8136. *
  8137. * Returns the discount rate for a security.
  8138. *
  8139. * @param mixed issue The security's issue date.
  8140. * @param mixed settlement The security's settlement date.
  8141. * @param float rate The security's annual coupon rate.
  8142. * @param float par The security's par value.
  8143. * @param int basis The type of day count to use.
  8144. * 0 or omitted US (NASD) 30/360
  8145. * 1 Actual/actual
  8146. * 2 Actual/360
  8147. * 3 Actual/365
  8148. * 4 European 30/360
  8149. * @return float
  8150. */
  8151. public static function ACCRINTM($issue, $settlement, $rate, $par=1000, $basis=0) {
  8152. $issue = self::flattenSingleValue($issue);
  8153. $settlement = self::flattenSingleValue($settlement);
  8154. $rate = (float) self::flattenSingleValue($rate);
  8155. $par = (is_null($par)) ? 1000 : (float) self::flattenSingleValue($par);
  8156. $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
  8157. // Validate
  8158. if ((is_numeric($rate)) && (is_numeric($par))) {
  8159. if (($rate <= 0) || ($par <= 0)) {
  8160. return self::$_errorCodes['num'];
  8161. }
  8162. $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
  8163. if (!is_numeric($daysBetweenIssueAndSettlement)) {
  8164. return $daysBetweenIssueAndSettlement;
  8165. }
  8166. $daysPerYear = self::_daysPerYear(self::YEAR($issue),$basis);
  8167. if (!is_numeric($daysPerYear)) {
  8168. return $daysPerYear;
  8169. }
  8170. $daysBetweenIssueAndSettlement *= $daysPerYear;
  8171. return $par * $rate * ($daysBetweenIssueAndSettlement / $daysPerYear);
  8172. }
  8173. return self::$_errorCodes['value'];
  8174. } // function ACCRINTM()
  8175. public static function AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis=0) {
  8176. } // function AMORDEGRC()
  8177. public static function AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis=0) {
  8178. } // function AMORLINC()
  8179. /**
  8180. * DISC
  8181. *
  8182. * Returns the discount rate for a security.
  8183. *
  8184. * @param mixed settlement The security's settlement date.
  8185. * The security settlement date is the date after the issue date when the security is traded to the buyer.
  8186. * @param mixed maturity The security's maturity date.
  8187. * The maturity date is the date when the security expires.
  8188. * @param int price The security's price per $100 face value.
  8189. * @param int redemption the security's redemption value per $100 face value.
  8190. * @param int basis The type of day count to use.
  8191. * 0 or omitted US (NASD) 30/360
  8192. * 1 Actual/actual
  8193. * 2 Actual/360
  8194. * 3 Actual/365
  8195. * 4 European 30/360
  8196. * @return float
  8197. */
  8198. public static function DISC($settlement, $maturity, $price, $redemption, $basis=0) {
  8199. $settlement = self::flattenSingleValue($settlement);
  8200. $maturity = self::flattenSingleValue($maturity);
  8201. $price = (float) self::flattenSingleValue($price);
  8202. $redemption = (float) self::flattenSingleValue($redemption);
  8203. $basis = (int) self::flattenSingleValue($basis);
  8204. // Validate
  8205. if ((is_numeric($price)) && (is_numeric($redemption)) && (is_numeric($basis))) {
  8206. if (($price <= 0) || ($redemption <= 0)) {
  8207. return self::$_errorCodes['num'];
  8208. }
  8209. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
  8210. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8211. return $daysBetweenSettlementAndMaturity;
  8212. }
  8213. return ((1 - $price / $redemption) / $daysBetweenSettlementAndMaturity);
  8214. }
  8215. return self::$_errorCodes['value'];
  8216. } // function DISC()
  8217. /**
  8218. * PRICEDISC
  8219. *
  8220. * Returns the price per $100 face value of a discounted security.
  8221. *
  8222. * @param mixed settlement The security's settlement date.
  8223. * The security settlement date is the date after the issue date when the security is traded to the buyer.
  8224. * @param mixed maturity The security's maturity date.
  8225. * The maturity date is the date when the security expires.
  8226. * @param int discount The security's discount rate.
  8227. * @param int redemption The security's redemption value per $100 face value.
  8228. * @param int basis The type of day count to use.
  8229. * 0 or omitted US (NASD) 30/360
  8230. * 1 Actual/actual
  8231. * 2 Actual/360
  8232. * 3 Actual/365
  8233. * 4 European 30/360
  8234. * @return float
  8235. */
  8236. public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis=0) {
  8237. $settlement = self::flattenSingleValue($settlement);
  8238. $maturity = self::flattenSingleValue($maturity);
  8239. $discount = (float) self::flattenSingleValue($discount);
  8240. $redemption = (float) self::flattenSingleValue($redemption);
  8241. $basis = (int) self::flattenSingleValue($basis);
  8242. // Validate
  8243. if ((is_numeric($discount)) && (is_numeric($redemption)) && (is_numeric($basis))) {
  8244. if (($discount <= 0) || ($redemption <= 0)) {
  8245. return self::$_errorCodes['num'];
  8246. }
  8247. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
  8248. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8249. return $daysBetweenSettlementAndMaturity;
  8250. }
  8251. return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
  8252. }
  8253. return self::$_errorCodes['value'];
  8254. } // function PRICEDISC()
  8255. /**
  8256. * PRICEMAT
  8257. *
  8258. * Returns the price per $100 face value of a security that pays interest at maturity.
  8259. *
  8260. * @param mixed settlement The security's settlement date.
  8261. * The security's settlement date is the date after the issue date when the security is traded to the buyer.
  8262. * @param mixed maturity The security's maturity date.
  8263. * The maturity date is the date when the security expires.
  8264. * @param mixed issue The security's issue date.
  8265. * @param int rate The security's interest rate at date of issue.
  8266. * @param int yield The security's annual yield.
  8267. * @param int basis The type of day count to use.
  8268. * 0 or omitted US (NASD) 30/360
  8269. * 1 Actual/actual
  8270. * 2 Actual/360
  8271. * 3 Actual/365
  8272. * 4 European 30/360
  8273. * @return float
  8274. */
  8275. public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis=0) {
  8276. $settlement = self::flattenSingleValue($settlement);
  8277. $maturity = self::flattenSingleValue($maturity);
  8278. $issue = self::flattenSingleValue($issue);
  8279. $rate = self::flattenSingleValue($rate);
  8280. $yield = self::flattenSingleValue($yield);
  8281. $basis = (int) self::flattenSingleValue($basis);
  8282. // Validate
  8283. if (is_numeric($rate) && is_numeric($yield)) {
  8284. if (($rate <= 0) || ($yield <= 0)) {
  8285. return self::$_errorCodes['num'];
  8286. }
  8287. $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
  8288. if (!is_numeric($daysPerYear)) {
  8289. return $daysPerYear;
  8290. }
  8291. $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
  8292. if (!is_numeric($daysBetweenIssueAndSettlement)) {
  8293. return $daysBetweenIssueAndSettlement;
  8294. }
  8295. $daysBetweenIssueAndSettlement *= $daysPerYear;
  8296. $daysBetweenIssueAndMaturity = self::YEARFRAC($issue, $maturity, $basis);
  8297. if (!is_numeric($daysBetweenIssueAndMaturity)) {
  8298. return $daysBetweenIssueAndMaturity;
  8299. }
  8300. $daysBetweenIssueAndMaturity *= $daysPerYear;
  8301. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
  8302. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8303. return $daysBetweenSettlementAndMaturity;
  8304. }
  8305. $daysBetweenSettlementAndMaturity *= $daysPerYear;
  8306. return ((100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100)) /
  8307. (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
  8308. (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100));
  8309. }
  8310. return self::$_errorCodes['value'];
  8311. } // function PRICEMAT()
  8312. /**
  8313. * RECEIVED
  8314. *
  8315. * Returns the price per $100 face value of a discounted security.
  8316. *
  8317. * @param mixed settlement The security's settlement date.
  8318. * The security settlement date is the date after the issue date when the security is traded to the buyer.
  8319. * @param mixed maturity The security's maturity date.
  8320. * The maturity date is the date when the security expires.
  8321. * @param int investment The amount invested in the security.
  8322. * @param int discount The security's discount rate.
  8323. * @param int basis The type of day count to use.
  8324. * 0 or omitted US (NASD) 30/360
  8325. * 1 Actual/actual
  8326. * 2 Actual/360
  8327. * 3 Actual/365
  8328. * 4 European 30/360
  8329. * @return float
  8330. */
  8331. public static function RECEIVED($settlement, $maturity, $investment, $discount, $basis=0) {
  8332. $settlement = self::flattenSingleValue($settlement);
  8333. $maturity = self::flattenSingleValue($maturity);
  8334. $investment = (float) self::flattenSingleValue($investment);
  8335. $discount = (float) self::flattenSingleValue($discount);
  8336. $basis = (int) self::flattenSingleValue($basis);
  8337. // Validate
  8338. if ((is_numeric($investment)) && (is_numeric($discount)) && (is_numeric($basis))) {
  8339. if (($investment <= 0) || ($discount <= 0)) {
  8340. return self::$_errorCodes['num'];
  8341. }
  8342. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
  8343. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8344. return $daysBetweenSettlementAndMaturity;
  8345. }
  8346. return $investment / ( 1 - ($discount * $daysBetweenSettlementAndMaturity));
  8347. }
  8348. return self::$_errorCodes['value'];
  8349. } // function RECEIVED()
  8350. /**
  8351. * INTRATE
  8352. *
  8353. * Returns the interest rate for a fully invested security.
  8354. *
  8355. * @param mixed settlement The security's settlement date.
  8356. * The security settlement date is the date after the issue date when the security is traded to the buyer.
  8357. * @param mixed maturity The security's maturity date.
  8358. * The maturity date is the date when the security expires.
  8359. * @param int investment The amount invested in the security.
  8360. * @param int redemption The amount to be received at maturity.
  8361. * @param int basis The type of day count to use.
  8362. * 0 or omitted US (NASD) 30/360
  8363. * 1 Actual/actual
  8364. * 2 Actual/360
  8365. * 3 Actual/365
  8366. * 4 European 30/360
  8367. * @return float
  8368. */
  8369. public static function INTRATE($settlement, $maturity, $investment, $redemption, $basis=0) {
  8370. $settlement = self::flattenSingleValue($settlement);
  8371. $maturity = self::flattenSingleValue($maturity);
  8372. $investment = (float) self::flattenSingleValue($investment);
  8373. $redemption = (float) self::flattenSingleValue($redemption);
  8374. $basis = (int) self::flattenSingleValue($basis);
  8375. // Validate
  8376. if ((is_numeric($investment)) && (is_numeric($redemption)) && (is_numeric($basis))) {
  8377. if (($investment <= 0) || ($redemption <= 0)) {
  8378. return self::$_errorCodes['num'];
  8379. }
  8380. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
  8381. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8382. return $daysBetweenSettlementAndMaturity;
  8383. }
  8384. return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
  8385. }
  8386. return self::$_errorCodes['value'];
  8387. } // function INTRATE()
  8388. /**
  8389. * TBILLEQ
  8390. *
  8391. * Returns the bond-equivalent yield for a Treasury bill.
  8392. *
  8393. * @param mixed settlement The Treasury bill's settlement date.
  8394. * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
  8395. * @param mixed maturity The Treasury bill's maturity date.
  8396. * The maturity date is the date when the Treasury bill expires.
  8397. * @param int discount The Treasury bill's discount rate.
  8398. * @return float
  8399. */
  8400. public static function TBILLEQ($settlement, $maturity, $discount) {
  8401. $settlement = self::flattenSingleValue($settlement);
  8402. $maturity = self::flattenSingleValue($maturity);
  8403. $discount = self::flattenSingleValue($discount);
  8404. // Use TBILLPRICE for validation
  8405. $testValue = self::TBILLPRICE($settlement, $maturity, $discount);
  8406. if (is_string($testValue)) {
  8407. return $testValue;
  8408. }
  8409. if (is_string($maturity = self::_getDateValue($maturity))) {
  8410. return self::$_errorCodes['value'];
  8411. }
  8412. ++$maturity;
  8413. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity);
  8414. $daysBetweenSettlementAndMaturity *= 360;
  8415. return (365 * $discount) / (360 - ($discount * ($daysBetweenSettlementAndMaturity)));
  8416. } // function TBILLEQ()
  8417. /**
  8418. * TBILLPRICE
  8419. *
  8420. * Returns the yield for a Treasury bill.
  8421. *
  8422. * @param mixed settlement The Treasury bill's settlement date.
  8423. * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
  8424. * @param mixed maturity The Treasury bill's maturity date.
  8425. * The maturity date is the date when the Treasury bill expires.
  8426. * @param int discount The Treasury bill's discount rate.
  8427. * @return float
  8428. */
  8429. public static function TBILLPRICE($settlement, $maturity, $discount) {
  8430. $settlement = self::flattenSingleValue($settlement);
  8431. $maturity = self::flattenSingleValue($maturity);
  8432. $discount = self::flattenSingleValue($discount);
  8433. if (is_string($maturity = self::_getDateValue($maturity))) {
  8434. return self::$_errorCodes['value'];
  8435. }
  8436. ++$maturity;
  8437. // Validate
  8438. if (is_numeric($discount)) {
  8439. if ($discount <= 0) {
  8440. return self::$_errorCodes['num'];
  8441. }
  8442. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity);
  8443. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8444. return $daysBetweenSettlementAndMaturity;
  8445. }
  8446. $daysBetweenSettlementAndMaturity *= 360;
  8447. if ($daysBetweenSettlementAndMaturity > 360) {
  8448. return self::$_errorCodes['num'];
  8449. }
  8450. $price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
  8451. if ($price <= 0) {
  8452. return self::$_errorCodes['num'];
  8453. }
  8454. return $price;
  8455. }
  8456. return self::$_errorCodes['value'];
  8457. } // function TBILLPRICE()
  8458. /**
  8459. * TBILLYIELD
  8460. *
  8461. * Returns the yield for a Treasury bill.
  8462. *
  8463. * @param mixed settlement The Treasury bill's settlement date.
  8464. * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
  8465. * @param mixed maturity The Treasury bill's maturity date.
  8466. * The maturity date is the date when the Treasury bill expires.
  8467. * @param int price The Treasury bill's price per $100 face value.
  8468. * @return float
  8469. */
  8470. public static function TBILLYIELD($settlement, $maturity, $price) {
  8471. $settlement = self::flattenSingleValue($settlement);
  8472. $maturity = self::flattenSingleValue($maturity);
  8473. $price = self::flattenSingleValue($price);
  8474. // Validate
  8475. if (is_numeric($price)) {
  8476. if ($price <= 0) {
  8477. return self::$_errorCodes['num'];
  8478. }
  8479. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity);
  8480. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8481. return $daysBetweenSettlementAndMaturity;
  8482. }
  8483. $daysBetweenSettlementAndMaturity *= 360;
  8484. // Sometimes we need to add 1, sometimes not. I haven't yet worked out the rule which determines this.
  8485. ++$daysBetweenSettlementAndMaturity;
  8486. if ($daysBetweenSettlementAndMaturity > 360) {
  8487. return self::$_errorCodes['num'];
  8488. }
  8489. return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
  8490. }
  8491. return self::$_errorCodes['value'];
  8492. } // function TBILLYIELD()
  8493. /**
  8494. * SLN
  8495. *
  8496. * Returns the straight-line depreciation of an asset for one period
  8497. *
  8498. * @param cost Initial cost of the asset
  8499. * @param salvage Value at the end of the depreciation
  8500. * @param life Number of periods over which the asset is depreciated
  8501. * @return float
  8502. */
  8503. public static function SLN($cost, $salvage, $life) {
  8504. $cost = self::flattenSingleValue($cost);
  8505. $salvage = self::flattenSingleValue($salvage);
  8506. $life = self::flattenSingleValue($life);
  8507. // Calculate
  8508. if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life))) {
  8509. if ($life < 0) {
  8510. return self::$_errorCodes['num'];
  8511. }
  8512. return ($cost - $salvage) / $life;
  8513. }
  8514. return self::$_errorCodes['value'];
  8515. } // function SLN()
  8516. /**
  8517. * YIELDMAT
  8518. *
  8519. * Returns the annual yield of a security that pays interest at maturity.
  8520. *
  8521. * @param mixed settlement The security's settlement date.
  8522. * The security's settlement date is the date after the issue date when the security is traded to the buyer.
  8523. * @param mixed maturity The security's maturity date.
  8524. * The maturity date is the date when the security expires.
  8525. * @param mixed issue The security's issue date.
  8526. * @param int rate The security's interest rate at date of issue.
  8527. * @param int price The security's price per $100 face value.
  8528. * @param int basis The type of day count to use.
  8529. * 0 or omitted US (NASD) 30/360
  8530. * 1 Actual/actual
  8531. * 2 Actual/360
  8532. * 3 Actual/365
  8533. * 4 European 30/360
  8534. * @return float
  8535. */
  8536. public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis=0) {
  8537. $settlement = self::flattenSingleValue($settlement);
  8538. $maturity = self::flattenSingleValue($maturity);
  8539. $issue = self::flattenSingleValue($issue);
  8540. $rate = self::flattenSingleValue($rate);
  8541. $price = self::flattenSingleValue($price);
  8542. $basis = (int) self::flattenSingleValue($basis);
  8543. // Validate
  8544. if (is_numeric($rate) && is_numeric($price)) {
  8545. if (($rate <= 0) || ($price <= 0)) {
  8546. return self::$_errorCodes['num'];
  8547. }
  8548. $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
  8549. if (!is_numeric($daysPerYear)) {
  8550. return $daysPerYear;
  8551. }
  8552. $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
  8553. if (!is_numeric($daysBetweenIssueAndSettlement)) {
  8554. return $daysBetweenIssueAndSettlement;
  8555. }
  8556. $daysBetweenIssueAndSettlement *= $daysPerYear;
  8557. $daysBetweenIssueAndMaturity = self::YEARFRAC($issue, $maturity, $basis);
  8558. if (!is_numeric($daysBetweenIssueAndMaturity)) {
  8559. return $daysBetweenIssueAndMaturity;
  8560. }
  8561. $daysBetweenIssueAndMaturity *= $daysPerYear;
  8562. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
  8563. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8564. return $daysBetweenSettlementAndMaturity;
  8565. }
  8566. $daysBetweenSettlementAndMaturity *= $daysPerYear;
  8567. return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) - (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
  8568. (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
  8569. ($daysPerYear / $daysBetweenSettlementAndMaturity);
  8570. }
  8571. return self::$_errorCodes['value'];
  8572. } // function YIELDMAT()
  8573. /**
  8574. * YIELDDISC
  8575. *
  8576. * Returns the annual yield of a security that pays interest at maturity.
  8577. *
  8578. * @param mixed settlement The security's settlement date.
  8579. * The security's settlement date is the date after the issue date when the security is traded to the buyer.
  8580. * @param mixed maturity The security's maturity date.
  8581. * The maturity date is the date when the security expires.
  8582. * @param int price The security's price per $100 face value.
  8583. * @param int redemption The security's redemption value per $100 face value.
  8584. * @param int basis The type of day count to use.
  8585. * 0 or omitted US (NASD) 30/360
  8586. * 1 Actual/actual
  8587. * 2 Actual/360
  8588. * 3 Actual/365
  8589. * 4 European 30/360
  8590. * @return float
  8591. */
  8592. public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis=0) {
  8593. $settlement = self::flattenSingleValue($settlement);
  8594. $maturity = self::flattenSingleValue($maturity);
  8595. $price = self::flattenSingleValue($price);
  8596. $redemption = self::flattenSingleValue($redemption);
  8597. $basis = (int) self::flattenSingleValue($basis);
  8598. // Validate
  8599. if (is_numeric($price) && is_numeric($redemption)) {
  8600. if (($price <= 0) || ($redemption <= 0)) {
  8601. return self::$_errorCodes['num'];
  8602. }
  8603. $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
  8604. if (!is_numeric($daysPerYear)) {
  8605. return $daysPerYear;
  8606. }
  8607. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity,$basis);
  8608. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8609. return $daysBetweenSettlementAndMaturity;
  8610. }
  8611. $daysBetweenSettlementAndMaturity *= $daysPerYear;
  8612. return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
  8613. }
  8614. return self::$_errorCodes['value'];
  8615. } // function YIELDDISC()
  8616. /**
  8617. * CELL_ADDRESS
  8618. *
  8619. * Returns the straight-line depreciation of an asset for one period
  8620. *
  8621. * @param row Row number to use in the cell reference
  8622. * @param column Column number to use in the cell reference
  8623. * @param relativity Flag indicating the type of reference to return
  8624. * @param sheetText Name of worksheet to use
  8625. * @return string
  8626. */
  8627. public static function CELL_ADDRESS($row, $column, $relativity=1, $referenceStyle=True, $sheetText='') {
  8628. $row = self::flattenSingleValue($row);
  8629. $column = self::flattenSingleValue($column);
  8630. $relativity = self::flattenSingleValue($relativity);
  8631. $sheetText = self::flattenSingleValue($sheetText);
  8632. if ($sheetText > '') {
  8633. if (strpos($sheetText,' ') !== False) { $sheetText = "'".$sheetText."'"; }
  8634. $sheetText .='!';
  8635. }
  8636. if ((!is_bool($referenceStyle)) || $referenceStyle) {
  8637. $rowRelative = $columnRelative = '$';
  8638. $column = PHPExcel_Cell::stringFromColumnIndex($column-1);
  8639. if (($relativity == 2) || ($relativity == 4)) { $columnRelative = ''; }
  8640. if (($relativity == 3) || ($relativity == 4)) { $rowRelative = ''; }
  8641. return $sheetText.$columnRelative.$column.$rowRelative.$row;
  8642. } else {
  8643. if (($relativity == 2) || ($relativity == 4)) { $column = '['.$column.']'; }
  8644. if (($relativity == 3) || ($relativity == 4)) { $row = '['.$row.']'; }
  8645. return $sheetText.'R'.$row.'C'.$column;
  8646. }
  8647. } // function CELL_ADDRESS()
  8648. public static function COLUMN($cellAddress=Null) {
  8649. if (is_null($cellAddress) || $cellAddress === '') {
  8650. return 0;
  8651. }
  8652. foreach($cellAddress as $columnKey => $value) {
  8653. return PHPExcel_Cell::columnIndexFromString($columnKey);
  8654. }
  8655. } // function COLUMN()
  8656. public static function ROW($cellAddress=Null) {
  8657. if ($cellAddress === Null) {
  8658. return 0;
  8659. }
  8660. foreach($cellAddress as $columnKey => $rowValue) {
  8661. foreach($rowValue as $rowKey => $cellValue) {
  8662. return $rowKey;
  8663. }
  8664. }
  8665. } // function ROW()
  8666. public static function OFFSET($cellAddress=Null,$rows=0,$columns=0,$height=null,$width=null) {
  8667. if ($cellAddress == Null) {
  8668. return 0;
  8669. }
  8670. foreach($cellAddress as $startColumnKey => $rowValue) {
  8671. $startColumnIndex = PHPExcel_Cell::columnIndexFromString($startColumnKey);
  8672. foreach($rowValue as $startRowKey => $cellValue) {
  8673. break 2;
  8674. }
  8675. }
  8676. foreach($cellAddress as $endColumnKey => $rowValue) {
  8677. foreach($rowValue as $endRowKey => $cellValue) {
  8678. }
  8679. }
  8680. $endColumnIndex = PHPExcel_Cell::columnIndexFromString($endColumnKey);
  8681. $startColumnIndex += --$columns;
  8682. $startRowKey += $rows;
  8683. if ($width == null) {
  8684. $endColumnIndex += $columns -1;
  8685. } else {
  8686. $endColumnIndex = $startColumnIndex + $width;
  8687. }
  8688. if ($height == null) {
  8689. $endRowKey += $rows;
  8690. } else {
  8691. $endRowKey = $startRowKey + $height -1;
  8692. }
  8693. if (($startColumnIndex < 0) || ($startRowKey <= 0)) {
  8694. return self::$_errorCodes['reference'];
  8695. }
  8696. $startColumnKey = PHPExcel_Cell::stringFromColumnIndex($startColumnIndex);
  8697. $endColumnKey = PHPExcel_Cell::stringFromColumnIndex($endColumnIndex);
  8698. $startCell = $startColumnKey.$startRowKey;
  8699. $endCell = $endColumnKey.$endRowKey;
  8700. if ($startCell == $endCell) {
  8701. return $startColumnKey.$startRowKey;
  8702. } else {
  8703. return $startColumnKey.$startRowKey.':'.$endColumnKey.$endRowKey;
  8704. }
  8705. } // function OFFSET()
  8706. public static function CHOOSE() {
  8707. $chooseArgs = func_get_args();
  8708. $chosenEntry = self::flattenSingleValue(array_shift($chooseArgs));
  8709. $entryCount = count($chooseArgs) - 1;
  8710. if ((is_numeric($chosenEntry)) && (!is_bool($chosenEntry))) {
  8711. --$chosenEntry;
  8712. } else {
  8713. return self::$_errorCodes['value'];
  8714. }
  8715. $chosenEntry = floor($chosenEntry);
  8716. if (($chosenEntry <= 0) || ($chosenEntry > $entryCount)) {
  8717. return self::$_errorCodes['value'];
  8718. }
  8719. if (is_array($chooseArgs[$chosenEntry])) {
  8720. return self::flattenArray($chooseArgs[$chosenEntry]);
  8721. } else {
  8722. return $chooseArgs[$chosenEntry];
  8723. }
  8724. } // function CHOOSE()
  8725. /**
  8726. * MATCH
  8727. * The MATCH function searches for a specified item in a range of cells
  8728. * @param lookup_value The value that you want to match in lookup_array
  8729. * @param lookup_array The range of cells being searched
  8730. * @param match_type The number -1, 0, or 1. -1 means above, 0 means exact match, 1 means below. If match_type is 1 or -1, the list has to be ordered.
  8731. * @return integer the relative position of the found item
  8732. */
  8733. public static function MATCH($lookup_value, $lookup_array, $match_type=1) {
  8734. // flatten the lookup_array
  8735. $lookup_array = self::flattenArray($lookup_array);
  8736. // flatten lookup_value since it may be a cell reference to a value or the value itself
  8737. $lookup_value = self::flattenSingleValue($lookup_value);
  8738. // MATCH is not case sensitive
  8739. $lookup_value = strtolower($lookup_value);
  8740. /*
  8741. echo "--------------------<br>looking for $lookup_value in <br>";
  8742. print_r($lookup_array);
  8743. echo "<br>";
  8744. //return 1;
  8745. /**/
  8746. // **
  8747. // check inputs
  8748. // **
  8749. // lookup_value type has to be number, text, or logical values
  8750. if (!is_numeric($lookup_value) && !is_string($lookup_value) && !is_bool($lookup_value)){
  8751. // error: lookup_array should contain only number, text, or logical values
  8752. //echo "error: lookup_array should contain only number, text, or logical values<br>";
  8753. return self::$_errorCodes['na'];
  8754. }
  8755. // match_type is 0, 1 or -1
  8756. if ($match_type!==0 && $match_type!==-1 && $match_type!==1){
  8757. // error: wrong value for match_type
  8758. //echo "error: wrong value for match_type<br>";
  8759. return self::$_errorCodes['na'];
  8760. }
  8761. // lookup_array should not be empty
  8762. if (sizeof($lookup_array)<=0){
  8763. // error: empty range
  8764. //echo "error: empty range ".sizeof($lookup_array)."<br>";
  8765. return self::$_errorCodes['na'];
  8766. }
  8767. // lookup_array should contain only number, text, or logical values
  8768. for ($i=0;$i<sizeof($lookup_array);++$i){
  8769. // check the type of the value
  8770. if (!is_numeric($lookup_array[$i]) && !is_string($lookup_array[$i]) && !is_bool($lookup_array[$i])){
  8771. // error: lookup_array should contain only number, text, or logical values
  8772. //echo "error: lookup_array should contain only number, text, or logical values<br>";
  8773. return self::$_errorCodes['na'];
  8774. }
  8775. // convert tpo lowercase
  8776. if (is_string($lookup_array[$i]))
  8777. $lookup_array[$i] = strtolower($lookup_array[$i]);
  8778. }
  8779. // if match_type is 1 or -1, the list has to be ordered
  8780. if($match_type==1 || $match_type==-1){
  8781. // **
  8782. // iniitialization
  8783. // store the last value
  8784. $iLastValue=$lookup_array[0];
  8785. // **
  8786. // loop on the cells
  8787. for ($i=0;$i<sizeof($lookup_array);++$i){
  8788. // check ascending order
  8789. if(($match_type==1 && $lookup_array[$i]<$iLastValue)
  8790. // OR check descending order
  8791. || ($match_type==-1 && $lookup_array[$i]>$iLastValue)){
  8792. // error: list is not ordered correctly
  8793. //echo "error: list is not ordered correctly<br>";
  8794. return self::$_errorCodes['na'];
  8795. }
  8796. }
  8797. }
  8798. // **
  8799. // find the match
  8800. // **
  8801. // loop on the cells
  8802. for ($i=0; $i < sizeof($lookup_array); ++$i){
  8803. // if match_type is 0 <=> find the first value that is exactly equal to lookup_value
  8804. if ($match_type==0 && $lookup_array[$i]==$lookup_value){
  8805. // this is the exact match
  8806. return $i+1;
  8807. }
  8808. // if match_type is -1 <=> find the smallest value that is greater than or equal to lookup_value
  8809. if ($match_type==-1 && $lookup_array[$i] < $lookup_value){
  8810. if ($i<1){
  8811. // 1st cell was allready smaller than the lookup_value
  8812. break;
  8813. }
  8814. else
  8815. // the previous cell was the match
  8816. return $i;
  8817. }
  8818. // if match_type is 1 <=> find the largest value that is less than or equal to lookup_value
  8819. if ($match_type==1 && $lookup_array[$i] > $lookup_value){
  8820. if ($i<1){
  8821. // 1st cell was allready bigger than the lookup_value
  8822. break;
  8823. }
  8824. else
  8825. // the previous cell was the match
  8826. return $i;
  8827. }
  8828. }
  8829. // unsuccessful in finding a match, return #N/A error value
  8830. //echo "unsuccessful in finding a match<br>";
  8831. return self::$_errorCodes['na'];
  8832. } // function MATCH()
  8833. /**
  8834. * Uses an index to choose a value from a reference or array
  8835. * implemented: Return the value of a specified cell or array of cells Array form
  8836. * not implemented: Return a reference to specified cells Reference form
  8837. *
  8838. * @param range_array a range of cells or an array constant
  8839. * @param row_num selects the row in array from which to return a value. If row_num is omitted, column_num is required.
  8840. * @param column_num selects the column in array from which to return a value. If column_num is omitted, row_num is required.
  8841. */
  8842. public static function INDEX($arrayValues,$rowNum = 0,$columnNum = 0) {
  8843. if (($rowNum < 0) || ($columnNum < 0)) {
  8844. return self::$_errorCodes['value'];
  8845. }
  8846. $rowKeys = array_keys($arrayValues);
  8847. $columnKeys = array_keys($arrayValues[$rowKeys[0]]);
  8848. if ($columnNum > count($columnKeys)) {
  8849. return self::$_errorCodes['value'];
  8850. } elseif ($columnNum == 0) {
  8851. if ($rowNum == 0) {
  8852. return $arrayValues;
  8853. }
  8854. $rowNum = $rowKeys[--$rowNum];
  8855. $returnArray = array();
  8856. foreach($arrayValues as $arrayColumn) {
  8857. $returnArray[] = $arrayColumn[$rowNum];
  8858. }
  8859. return $returnArray;
  8860. }
  8861. $columnNum = $columnKeys[--$columnNum];
  8862. if ($rowNum > count($rowKeys)) {
  8863. return self::$_errorCodes['value'];
  8864. } elseif ($rowNum == 0) {
  8865. return $arrayValues[$columnNum];
  8866. }
  8867. $rowNum = $rowKeys[--$rowNum];
  8868. return $arrayValues[$rowNum][$columnNum];
  8869. } // function INDEX()
  8870. /**
  8871. * SYD
  8872. *
  8873. * Returns the sum-of-years' digits depreciation of an asset for a specified period.
  8874. *
  8875. * @param cost Initial cost of the asset
  8876. * @param salvage Value at the end of the depreciation
  8877. * @param life Number of periods over which the asset is depreciated
  8878. * @param period Period
  8879. * @return float
  8880. */
  8881. public static function SYD($cost, $salvage, $life, $period) {
  8882. $cost = self::flattenSingleValue($cost);
  8883. $salvage = self::flattenSingleValue($salvage);
  8884. $life = self::flattenSingleValue($life);
  8885. $period = self::flattenSingleValue($period);
  8886. // Calculate
  8887. if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period))) {
  8888. if (($life < 1) || ($salvage < $life) || ($period > $life)) {
  8889. return self::$_errorCodes['num'];
  8890. }
  8891. return (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
  8892. }
  8893. return self::$_errorCodes['value'];
  8894. } // function SYD()
  8895. /**
  8896. * TRANSPOSE
  8897. *
  8898. * @param array $matrixData A matrix of values
  8899. * @return array
  8900. *
  8901. * Unlike the Excel TRANSPOSE function, which will only work on a single row or column, this function will transpose a full matrix.
  8902. */
  8903. public static function TRANSPOSE($matrixData) {
  8904. $returnMatrix = array();
  8905. if (!is_array($matrixData)) { $matrixData = array(array($matrixData)); }
  8906. $column = 0;
  8907. foreach($matrixData as $matrixRow) {
  8908. $row = 0;
  8909. foreach($matrixRow as $matrixCell) {
  8910. $returnMatrix[$column][$row] = $matrixCell;
  8911. ++$row;
  8912. }
  8913. ++$column;
  8914. }
  8915. return $returnMatrix;
  8916. } // function TRANSPOSE()
  8917. /**
  8918. * MMULT
  8919. *
  8920. * @param array $matrixData1 A matrix of values
  8921. * @param array $matrixData2 A matrix of values
  8922. * @return array
  8923. */
  8924. public static function MMULT($matrixData1,$matrixData2) {
  8925. $matrixAData = $matrixBData = array();
  8926. if (!is_array($matrixData1)) { $matrixData1 = array(array($matrixData1)); }
  8927. if (!is_array($matrixData2)) { $matrixData2 = array(array($matrixData2)); }
  8928. $rowA = 0;
  8929. foreach($matrixData1 as $matrixRow) {
  8930. $columnA = 0;
  8931. foreach($matrixRow as $matrixCell) {
  8932. if ((is_string($matrixCell)) || ($matrixCell === null)) {
  8933. return self::$_errorCodes['value'];
  8934. }
  8935. $matrixAData[$rowA][$columnA] = $matrixCell;
  8936. ++$columnA;
  8937. }
  8938. ++$rowA;
  8939. }
  8940. try {
  8941. $matrixA = new Matrix($matrixAData);
  8942. $rowB = 0;
  8943. foreach($matrixData2 as $matrixRow) {
  8944. $columnB = 0;
  8945. foreach($matrixRow as $matrixCell) {
  8946. if ((is_string($matrixCell)) || ($matrixCell === null)) {
  8947. return self::$_errorCodes['value'];
  8948. }
  8949. $matrixBData[$rowB][$columnB] = $matrixCell;
  8950. ++$columnB;
  8951. }
  8952. ++$rowB;
  8953. }
  8954. $matrixB = new Matrix($matrixBData);
  8955. if (($rowA != $columnB) || ($rowB != $columnA)) {
  8956. return self::$_errorCodes['value'];
  8957. }
  8958. return $matrixA->times($matrixB)->getArray();
  8959. } catch (Exception $ex) {
  8960. return self::$_errorCodes['value'];
  8961. }
  8962. } // function MMULT()
  8963. /**
  8964. * MINVERSE
  8965. *
  8966. * @param array $matrixValues A matrix of values
  8967. * @return array
  8968. */
  8969. public static function MINVERSE($matrixValues) {
  8970. $matrixData = array();
  8971. if (!is_array($matrixValues)) { $matrixValues = array(array($matrixValues)); }
  8972. $row = $maxColumn = 0;
  8973. foreach($matrixValues as $matrixRow) {
  8974. $column = 0;
  8975. foreach($matrixRow as $matrixCell) {
  8976. if ((is_string($matrixCell)) || ($matrixCell === null)) {
  8977. return self::$_errorCodes['value'];
  8978. }
  8979. $matrixData[$column][$row] = $matrixCell;
  8980. ++$column;
  8981. }
  8982. if ($column > $maxColumn) { $maxColumn = $column; }
  8983. ++$row;
  8984. }
  8985. if ($row != $maxColumn) { return self::$_errorCodes['value']; }
  8986. try {
  8987. $matrix = new Matrix($matrixData);
  8988. return $matrix->inverse()->getArray();
  8989. } catch (Exception $ex) {
  8990. return self::$_errorCodes['value'];
  8991. }
  8992. } // function MINVERSE()
  8993. /**
  8994. * MDETERM
  8995. *
  8996. * @param array $matrixValues A matrix of values
  8997. * @return float
  8998. */
  8999. public static function MDETERM($matrixValues) {
  9000. $matrixData = array();
  9001. if (!is_array($matrixValues)) { $matrixValues = array(array($matrixValues)); }
  9002. $row = $maxColumn = 0;
  9003. foreach($matrixValues as $matrixRow) {
  9004. $column = 0;
  9005. foreach($matrixRow as $matrixCell) {
  9006. if ((is_string($matrixCell)) || ($matrixCell === null)) {
  9007. return self::$_errorCodes['value'];
  9008. }
  9009. $matrixData[$column][$row] = $matrixCell;
  9010. ++$column;
  9011. }
  9012. if ($column > $maxColumn) { $maxColumn = $column; }
  9013. ++$row;
  9014. }
  9015. if ($row != $maxColumn) { return self::$_errorCodes['value']; }
  9016. try {
  9017. $matrix = new Matrix($matrixData);
  9018. return $matrix->det();
  9019. } catch (Exception $ex) {
  9020. return self::$_errorCodes['value'];
  9021. }
  9022. } // function MDETERM()
  9023. /**
  9024. * SUMX2MY2
  9025. *
  9026. * @param mixed $value Value to check
  9027. * @return float
  9028. */
  9029. public static function SUMX2MY2($matrixData1,$matrixData2) {
  9030. $array1 = self::flattenArray($matrixData1);
  9031. $array2 = self::flattenArray($matrixData2);
  9032. $count1 = count($array1);
  9033. $count2 = count($array2);
  9034. if ($count1 < $count2) {
  9035. $count = $count1;
  9036. } else {
  9037. $count = $count2;
  9038. }
  9039. $result = 0;
  9040. for ($i = 0; $i < $count; ++$i) {
  9041. if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
  9042. ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
  9043. $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
  9044. }
  9045. }
  9046. return $result;
  9047. } // function SUMX2MY2()
  9048. /**
  9049. * SUMX2PY2
  9050. *
  9051. * @param mixed $value Value to check
  9052. * @return float
  9053. */
  9054. public static function SUMX2PY2($matrixData1,$matrixData2) {
  9055. $array1 = self::flattenArray($matrixData1);
  9056. $array2 = self::flattenArray($matrixData2);
  9057. $count1 = count($array1);
  9058. $count2 = count($array2);
  9059. if ($count1 < $count2) {
  9060. $count = $count1;
  9061. } else {
  9062. $count = $count2;
  9063. }
  9064. $result = 0;
  9065. for ($i = 0; $i < $count; ++$i) {
  9066. if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
  9067. ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
  9068. $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
  9069. }
  9070. }
  9071. return $result;
  9072. } // function SUMX2PY2()
  9073. /**
  9074. * SUMXMY2
  9075. *
  9076. * @param mixed $value Value to check
  9077. * @return float
  9078. */
  9079. public static function SUMXMY2($matrixData1,$matrixData2) {
  9080. $array1 = self::flattenArray($matrixData1);
  9081. $array2 = self::flattenArray($matrixData2);
  9082. $count1 = count($array1);
  9083. $count2 = count($array2);
  9084. if ($count1 < $count2) {
  9085. $count = $count1;
  9086. } else {
  9087. $count = $count2;
  9088. }
  9089. $result = 0;
  9090. for ($i = 0; $i < $count; ++$i) {
  9091. if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
  9092. ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
  9093. $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
  9094. }
  9095. }
  9096. return $result;
  9097. } // function SUMXMY2()
  9098. /**
  9099. * VLOOKUP
  9100. * The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value in the same row based on the index_number.
  9101. * @param lookup_value The value that you want to match in lookup_array
  9102. * @param lookup_array The range of cells being searched
  9103. * @param index_number The column number in table_array from which the matching value must be returned. The first column is 1.
  9104. * @param not_exact_match Determines if you are looking for an exact match based on lookup_value.
  9105. * @return mixed The value of the found cell
  9106. */
  9107. public static function VLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match=true) {
  9108. // index_number must be greater than or equal to 1
  9109. if ($index_number < 1) {
  9110. return self::$_errorCodes['value'];
  9111. }
  9112. // index_number must be less than or equal to the number of columns in lookup_array
  9113. if ($index_number > count($lookup_array)) {
  9114. return self::$_errorCodes['reference'];
  9115. }
  9116. // re-index lookup_array with numeric keys starting at 1
  9117. array_unshift($lookup_array, array());
  9118. $lookup_array = array_slice(array_values($lookup_array), 1, count($lookup_array), true);
  9119. // look for an exact match
  9120. $row_number = array_search($lookup_value, $lookup_array[1]);
  9121. // if an exact match is required, we have what we need to return an appropriate response
  9122. if ($not_exact_match == false) {
  9123. if ($row_number === false) {
  9124. return self::$_errorCodes['na'];
  9125. } else {
  9126. return $lookup_array[$index_number][$row_number];
  9127. }
  9128. }
  9129. // TODO: The VLOOKUP spec in Excel states that, at this point, we should search for
  9130. // the highest value that is less than lookup_value. However, documentation on how string
  9131. // values should be treated here is sparse.
  9132. return self::$_errorCodes['na'];
  9133. } // function VLOOKUP()
  9134. /**
  9135. * LOOKUP
  9136. * The LOOKUP function searches for value either from a one-row or one-column range or from an array.
  9137. * @param lookup_value The value that you want to match in lookup_array
  9138. * @param lookup_vector The range of cells being searched
  9139. * @param result_vector The column from which the matching value must be returned
  9140. * @return mixed The value of the found cell
  9141. */
  9142. public static function LOOKUP($lookup_value, $lookup_vector, $result_vector=null) {
  9143. // check for LOOKUP Syntax (view Excel documentation)
  9144. if( is_null($result_vector) )
  9145. {
  9146. // TODO: Syntax 2 (array)
  9147. } else {
  9148. // Syntax 1 (vector)
  9149. // get key (column or row) of lookup_vector
  9150. $kl = key($lookup_vector);
  9151. // check if lookup_value exists in lookup_vector
  9152. if( in_array($lookup_value, $lookup_vector[$kl]) )
  9153. {
  9154. // FOUND IT! Get key of lookup_vector
  9155. $k_res = array_search($lookup_value, $lookup_vector[$kl]);
  9156. } else {
  9157. // value NOT FOUND
  9158. // Get the smallest value in lookup_vector
  9159. // The LOOKUP spec in Excel states --> IMPORTANT - The values in lookup_vector must be placed in ascending order!
  9160. $ksv = key($lookup_vector[$kl]);
  9161. $smallest_value = $lookup_vector[$kl][$ksv];
  9162. // If lookup_value is smaller than the smallest value in lookup_vector, LOOKUP gives the #N/A error value.
  9163. if( $lookup_value < $smallest_value )
  9164. {
  9165. return self::$_errorCodes['na'];
  9166. } else {
  9167. // If LOOKUP can't find the lookup_value, it matches the largest value in lookup_vector that is less than or equal to lookup_value.
  9168. // IMPORTANT : In Excel Documentation is not clear what happen if lookup_value is text!
  9169. foreach( $lookup_vector[$kl] AS $kk => $value )
  9170. {
  9171. if( $lookup_value >= $value )
  9172. {
  9173. $k_res = $kk;
  9174. }
  9175. }
  9176. }
  9177. }
  9178. // Returns a value from the same position in result_vector
  9179. // get key (column or row) of result_vector
  9180. $kr = key($result_vector);
  9181. if( isset($result_vector[$kr][$k_res]) )
  9182. {
  9183. return $result_vector[$kr][$k_res];
  9184. } else {
  9185. // TODO: In Excel Documentation is not clear what happen here...
  9186. }
  9187. }
  9188. } // function LOOKUP()
  9189. /**
  9190. * Flatten multidemensional array
  9191. *
  9192. * @param array $array Array to be flattened
  9193. * @return array Flattened array
  9194. */
  9195. public static function flattenArray($array) {
  9196. if(!is_array ( $array ) ){
  9197. $array = array ( $array );
  9198. }
  9199. $arrayValues = array();
  9200. foreach ($array as $value) {
  9201. if (is_scalar($value)) {
  9202. $arrayValues[] = self::flattenSingleValue($value);
  9203. } elseif (is_array($value)) {
  9204. $arrayValues = array_merge($arrayValues, self::flattenArray($value));
  9205. } else {
  9206. $arrayValues[] = $value;
  9207. }
  9208. }
  9209. return $arrayValues;
  9210. } // function flattenArray()
  9211. /**
  9212. * Convert an array with one element to a flat value
  9213. *
  9214. * @param mixed $value Array or flat value
  9215. * @return mixed
  9216. */
  9217. public static function flattenSingleValue($value = '') {
  9218. if (is_array($value)) {
  9219. $value = self::flattenSingleValue(array_pop($value));
  9220. }
  9221. return $value;
  9222. } // function flattenSingleValue()
  9223. } // class PHPExcel_Calculation_Functions
  9224. //
  9225. // There are a few mathematical functions that aren't available on all versions of PHP for all platforms
  9226. // These functions aren't available in Windows implementations of PHP prior to version 5.3.0
  9227. // So we test if they do exist for this version of PHP/operating platform; and if not we create them
  9228. //
  9229. if (!function_exists('acosh')) {
  9230. function acosh($x) {
  9231. return 2 * log(sqrt(($x + 1) / 2) + sqrt(($x - 1) / 2));
  9232. } // function acosh()
  9233. }
  9234. if (!function_exists('asinh')) {
  9235. function asinh($x) {
  9236. return log($x + sqrt(1 + $x * $x));
  9237. } // function asinh()
  9238. }
  9239. if (!function_exists('atanh')) {
  9240. function atanh($x) {
  9241. return (log(1 + $x) - log(1 - $x)) / 2;
  9242. } // function atanh()
  9243. }
  9244. if (!function_exists('money_format')) {
  9245. function money_format($format, $number) {
  9246. $regex = array( '/%((?:[\^!\-]|\+|\(|\=.)*)([0-9]+)?(?:#([0-9]+))?',
  9247. '(?:\.([0-9]+))?([in%])/'
  9248. );
  9249. $regex = implode('', $regex);
  9250. if (setlocale(LC_MONETARY, null) == '') {
  9251. setlocale(LC_MONETARY, '');
  9252. }
  9253. $locale = localeconv();
  9254. $number = floatval($number);
  9255. if (!preg_match($regex, $format, $fmatch)) {
  9256. trigger_error("No format specified or invalid format", E_USER_WARNING);
  9257. return $number;
  9258. }
  9259. $flags = array( 'fillchar' => preg_match('/\=(.)/', $fmatch[1], $match) ? $match[1] : ' ',
  9260. 'nogroup' => preg_match('/\^/', $fmatch[1]) > 0,
  9261. 'usesignal' => preg_match('/\+|\(/', $fmatch[1], $match) ? $match[0] : '+',
  9262. 'nosimbol' => preg_match('/\!/', $fmatch[1]) > 0,
  9263. 'isleft' => preg_match('/\-/', $fmatch[1]) > 0
  9264. );
  9265. $width = trim($fmatch[2]) ? (int)$fmatch[2] : 0;
  9266. $left = trim($fmatch[3]) ? (int)$fmatch[3] : 0;
  9267. $right = trim($fmatch[4]) ? (int)$fmatch[4] : $locale['int_frac_digits'];
  9268. $conversion = $fmatch[5];
  9269. $positive = true;
  9270. if ($number < 0) {
  9271. $positive = false;
  9272. $number *= -1;
  9273. }
  9274. $letter = $positive ? 'p' : 'n';
  9275. $prefix = $suffix = $cprefix = $csuffix = $signal = '';
  9276. if (!$positive) {
  9277. $signal = $locale['negative_sign'];
  9278. switch (true) {
  9279. case $locale['n_sign_posn'] == 0 || $flags['usesignal'] == '(':
  9280. $prefix = '(';
  9281. $suffix = ')';
  9282. break;
  9283. case $locale['n_sign_posn'] == 1:
  9284. $prefix = $signal;
  9285. break;
  9286. case $locale['n_sign_posn'] == 2:
  9287. $suffix = $signal;
  9288. break;
  9289. case $locale['n_sign_posn'] == 3:
  9290. $cprefix = $signal;
  9291. break;
  9292. case $locale['n_sign_posn'] == 4:
  9293. $csuffix = $signal;
  9294. break;
  9295. }
  9296. }
  9297. if (!$flags['nosimbol']) {
  9298. $currency = $cprefix;
  9299. $currency .= ($conversion == 'i' ? $locale['int_curr_symbol'] : $locale['currency_symbol']);
  9300. $currency .= $csuffix;
  9301. $currency = iconv('ISO-8859-1','UTF-8',$currency);
  9302. } else {
  9303. $currency = '';
  9304. }
  9305. $space = $locale["{$letter}_sep_by_space"] ? ' ' : '';
  9306. $number = number_format($number, $right, $locale['mon_decimal_point'], $flags['nogroup'] ? '' : $locale['mon_thousands_sep'] );
  9307. $number = explode($locale['mon_decimal_point'], $number);
  9308. $n = strlen($prefix) + strlen($currency);
  9309. if ($left > 0 && $left > $n) {
  9310. if ($flags['isleft']) {
  9311. $number[0] .= str_repeat($flags['fillchar'], $left - $n);
  9312. } else {
  9313. $number[0] = str_repeat($flags['fillchar'], $left - $n) . $number[0];
  9314. }
  9315. }
  9316. $number = implode($locale['mon_decimal_point'], $number);
  9317. if ($locale["{$letter}_cs_precedes"]) {
  9318. $number = $prefix . $currency . $space . $number . $suffix;
  9319. } else {
  9320. $number = $prefix . $number . $space . $currency . $suffix;
  9321. }
  9322. if ($width > 0) {
  9323. $number = str_pad($number, $width, $flags['fillchar'], $flags['isleft'] ? STR_PAD_RIGHT : STR_PAD_LEFT);
  9324. }
  9325. $format = str_replace($fmatch[0], $number, $format);
  9326. return $format;
  9327. } // function money_format()
  9328. }