PageRenderTime 105ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/add-ons/PHPExcel/PHPExcel/Calculation/Functions.php

https://github.com/jcplat/console-seolan
PHP | 11008 lines | 6544 code | 1235 blank | 3229 comment | 1907 complexity | 860ce4e10d9c9aeb497e9b03e6270be5 MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1, GPL-3.0, Apache-2.0, BSD-3-Clause
  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.1, 2009-11-02
  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_Calculation */
  57. require_once PHPEXCEL_ROOT . 'PHPExcel/Calculation.php';
  58. /** PHPExcel_Cell_DataType */
  59. require_once PHPEXCEL_ROOT . 'PHPExcel/Cell/DataType.php';
  60. /** PHPExcel_Style_NumberFormat */
  61. require_once PHPEXCEL_ROOT . 'PHPExcel/Style/NumberFormat.php';
  62. /** PHPExcel_Shared_Date */
  63. require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/Date.php';
  64. /** Matrix */
  65. require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/JAMA/Matrix.php';
  66. require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/trend/trendClass.php';
  67. /**
  68. * PHPExcel_Calculation_Functions
  69. *
  70. * @category PHPExcel
  71. * @package PHPExcel_Calculation
  72. * @copyright Copyright (c) 2006 - 2009 PHPExcel (http://www.codeplex.com/PHPExcel)
  73. */
  74. class PHPExcel_Calculation_Functions {
  75. /** constants */
  76. const COMPATIBILITY_EXCEL = 'Excel';
  77. const COMPATIBILITY_GNUMERIC = 'Gnumeric';
  78. const COMPATIBILITY_OPENOFFICE = 'OpenOfficeCalc';
  79. const RETURNDATE_PHP_NUMERIC = 'P';
  80. const RETURNDATE_PHP_OBJECT = 'O';
  81. const RETURNDATE_EXCEL = 'E';
  82. /**
  83. * Compatibility mode to use for error checking and responses
  84. *
  85. * @access private
  86. * @var string
  87. */
  88. private static $compatibilityMode = self::COMPATIBILITY_EXCEL;
  89. /**
  90. * Data Type to use when returning date values
  91. *
  92. * @access private
  93. * @var integer
  94. */
  95. private static $ReturnDateType = self::RETURNDATE_EXCEL;
  96. /**
  97. * List of error codes
  98. *
  99. * @access private
  100. * @var array
  101. */
  102. private static $_errorCodes = array( 'null' => '#NULL!',
  103. 'divisionbyzero' => '#DIV/0!',
  104. 'value' => '#VALUE!',
  105. 'reference' => '#REF!',
  106. 'name' => '#NAME?',
  107. 'num' => '#NUM!',
  108. 'na' => '#N/A',
  109. 'gettingdata' => '#GETTING_DATA'
  110. );
  111. /**
  112. * Set the Compatibility Mode
  113. *
  114. * @access public
  115. * @category Function Configuration
  116. * @param string $compatibilityMode Compatibility Mode
  117. * Permitted values are:
  118. * PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL 'Excel'
  119. * PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
  120. * PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
  121. * @return boolean (Success or Failure)
  122. */
  123. public static function setCompatibilityMode($compatibilityMode) {
  124. if (($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
  125. ($compatibilityMode == self::COMPATIBILITY_GNUMERIC) ||
  126. ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  127. self::$compatibilityMode = $compatibilityMode;
  128. return True;
  129. }
  130. return False;
  131. } // function setCompatibilityMode()
  132. /**
  133. * Return the current Compatibility Mode
  134. *
  135. * @access public
  136. * @category Function Configuration
  137. * @return string Compatibility Mode
  138. * Possible Return values are:
  139. * PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL 'Excel'
  140. * PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC 'Gnumeric'
  141. * PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE 'OpenOfficeCalc'
  142. */
  143. public static function getCompatibilityMode() {
  144. return self::$compatibilityMode;
  145. } // function getCompatibilityMode()
  146. /**
  147. * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object)
  148. *
  149. * @access public
  150. * @category Function Configuration
  151. * @param string $returnDateType Return Date Format
  152. * Permitted values are:
  153. * PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC 'P'
  154. * PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT 'O'
  155. * PHPExcel_Calculation_Functions::RETURNDATE_EXCEL 'E'
  156. * @return boolean Success or failure
  157. */
  158. public static function setReturnDateType($returnDateType) {
  159. if (($returnDateType == self::RETURNDATE_PHP_NUMERIC) ||
  160. ($returnDateType == self::RETURNDATE_PHP_OBJECT) ||
  161. ($returnDateType == self::RETURNDATE_EXCEL)) {
  162. self::$ReturnDateType = $returnDateType;
  163. return True;
  164. }
  165. return False;
  166. } // function setReturnDateType()
  167. /**
  168. * Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object)
  169. *
  170. * @access public
  171. * @category Function Configuration
  172. * @return string Return Date Format
  173. * Possible Return values are:
  174. * PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC 'P'
  175. * PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT 'O'
  176. * PHPExcel_Calculation_Functions::RETURNDATE_EXCEL 'E'
  177. */
  178. public static function getReturnDateType() {
  179. return self::$ReturnDateType;
  180. } // function getReturnDateType()
  181. /**
  182. * DUMMY
  183. *
  184. * @access public
  185. * @category Error Returns
  186. * @return string #Not Yet Implemented
  187. */
  188. public static function DUMMY() {
  189. return '#Not Yet Implemented';
  190. } // function DUMMY()
  191. /**
  192. * NA
  193. *
  194. * @access public
  195. * @category Error Returns
  196. * @return string #N/A!
  197. */
  198. public static function NA() {
  199. return self::$_errorCodes['na'];
  200. } // function NA()
  201. /**
  202. * NAN
  203. *
  204. * @access public
  205. * @category Error Returns
  206. * @return string #NUM!
  207. */
  208. public static function NaN() {
  209. return self::$_errorCodes['num'];
  210. } // function NAN()
  211. /**
  212. * NAME
  213. *
  214. * @access public
  215. * @category Error Returns
  216. * @return string #NAME!
  217. */
  218. public static function NAME() {
  219. return self::$_errorCodes['name'];
  220. } // function NAME()
  221. /**
  222. * REF
  223. *
  224. * @access public
  225. * @category Error Returns
  226. * @return string #REF!
  227. */
  228. public static function REF() {
  229. return self::$_errorCodes['reference'];
  230. } // function REF()
  231. /**
  232. * VALUE
  233. *
  234. * @access public
  235. * @category Error Returns
  236. * @return string #VALUE!
  237. */
  238. public static function VALUE() {
  239. return self::$_errorCodes['value'];
  240. } // function VALUE()
  241. /**
  242. * LOGICAL_AND
  243. *
  244. * Returns boolean TRUE if all its arguments are TRUE; returns FALSE if one or more argument is FALSE.
  245. *
  246. * Excel Function:
  247. * =AND(logical1[,logical2[, ...]])
  248. *
  249. * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
  250. * or references that contain logical values.
  251. *
  252. * Booleans arguments are treated as True or False as appropriate
  253. * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
  254. * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
  255. * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
  256. *
  257. * @access public
  258. * @category Logical Functions
  259. * @param mixed $arg,... Data values
  260. * @return boolean The logical AND of the arguments.
  261. */
  262. public static function LOGICAL_AND() {
  263. // Return value
  264. $returnValue = True;
  265. // Loop through the arguments
  266. $aArgs = self::flattenArray(func_get_args());
  267. $argCount = 0;
  268. foreach ($aArgs as $arg) {
  269. // Is it a boolean value?
  270. if (is_bool($arg)) {
  271. $returnValue = $returnValue && $arg;
  272. ++$argCount;
  273. } elseif ((is_numeric($arg)) && (!is_string($arg))) {
  274. $returnValue = $returnValue && ($arg != 0);
  275. ++$argCount;
  276. } elseif (is_string($arg)) {
  277. $arg = strtoupper($arg);
  278. if ($arg == 'TRUE') {
  279. $arg = 1;
  280. } elseif ($arg == 'FALSE') {
  281. $arg = 0;
  282. } else {
  283. return self::$_errorCodes['value'];
  284. }
  285. $returnValue = $returnValue && ($arg != 0);
  286. ++$argCount;
  287. }
  288. }
  289. // Return
  290. if ($argCount == 0) {
  291. return self::$_errorCodes['value'];
  292. }
  293. return $returnValue;
  294. } // function LOGICAL_AND()
  295. /**
  296. * LOGICAL_OR
  297. *
  298. * Returns boolean TRUE if any argument is TRUE; returns FALSE if all arguments are FALSE.
  299. *
  300. * Excel Function:
  301. * =OR(logical1[,logical2[, ...]])
  302. *
  303. * The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays
  304. * or references that contain logical values.
  305. *
  306. * Booleans arguments are treated as True or False as appropriate
  307. * Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
  308. * If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds
  309. * the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value
  310. *
  311. * @access public
  312. * @category Logical Functions
  313. * @param mixed $arg,... Data values
  314. * @return boolean The logical OR of the arguments.
  315. */
  316. public static function LOGICAL_OR() {
  317. // Return value
  318. $returnValue = False;
  319. // Loop through the arguments
  320. $aArgs = self::flattenArray(func_get_args());
  321. $argCount = 0;
  322. foreach ($aArgs as $arg) {
  323. // Is it a boolean value?
  324. if (is_bool($arg)) {
  325. $returnValue = $returnValue || $arg;
  326. ++$argCount;
  327. } elseif ((is_numeric($arg)) && (!is_string($arg))) {
  328. $returnValue = $returnValue || ($arg != 0);
  329. ++$argCount;
  330. } elseif (is_string($arg)) {
  331. $arg = strtoupper($arg);
  332. if ($arg == 'TRUE') {
  333. $arg = 1;
  334. } elseif ($arg == 'FALSE') {
  335. $arg = 0;
  336. } else {
  337. return self::$_errorCodes['value'];
  338. }
  339. $returnValue = $returnValue || ($arg != 0);
  340. ++$argCount;
  341. }
  342. }
  343. // Return
  344. if ($argCount == 0) {
  345. return self::$_errorCodes['value'];
  346. }
  347. return $returnValue;
  348. } // function LOGICAL_OR()
  349. /**
  350. * LOGICAL_FALSE
  351. *
  352. * Returns the boolean FALSE.
  353. *
  354. * Excel Function:
  355. * =FALSE()
  356. *
  357. * @access public
  358. * @category Logical Functions
  359. * @return boolean False
  360. */
  361. public static function LOGICAL_FALSE() {
  362. return False;
  363. } // function LOGICAL_FALSE()
  364. /**
  365. * LOGICAL_TRUE
  366. *
  367. * Returns the boolean TRUE.
  368. *
  369. * Excel Function:
  370. * =TRUE()
  371. *
  372. * @access public
  373. * @category Logical Functions
  374. * @return boolean True
  375. */
  376. public static function LOGICAL_TRUE() {
  377. return True;
  378. } // function LOGICAL_TRUE()
  379. /**
  380. * LOGICAL_NOT
  381. *
  382. * Returns the boolean inverse of the argument.
  383. *
  384. * Excel Function:
  385. * =NOT(logical)
  386. *
  387. * @access public
  388. * @category Logical Functions
  389. * @param mixed $logical A value or expression that can be evaluated to TRUE or FALSE
  390. * @return boolean The boolean inverse of the argument.
  391. */
  392. public static function LOGICAL_NOT($logical) {
  393. return !$logical;
  394. } // function LOGICAL_NOT()
  395. /**
  396. * STATEMENT_IF
  397. *
  398. * Returns one value if a condition you specify evaluates to TRUE and another value if it evaluates to FALSE.
  399. *
  400. * Excel Function:
  401. * =IF(condition[,returnIfTrue[,returnIfFalse]])
  402. *
  403. * Condition is any value or expression that can be evaluated to TRUE or FALSE.
  404. * For example, A10=100 is a logical expression; if the value in cell A10 is equal to 100,
  405. * the expression evaluates to TRUE. Otherwise, the expression evaluates to FALSE.
  406. * This argument can use any comparison calculation operator.
  407. * ReturnIfTrue is the value that is returned if condition evaluates to TRUE.
  408. * For example, if this argument is the text string "Within budget" and the condition argument evaluates to TRUE,
  409. * then the IF function returns the text "Within budget"
  410. * If condition is TRUE and ReturnIfTrue is blank, this argument returns 0 (zero). To display the word TRUE, use
  411. * the logical value TRUE for this argument.
  412. * ReturnIfTrue can be another formula.
  413. * ReturnIfFalse is the value that is returned if condition evaluates to FALSE.
  414. * For example, if this argument is the text string "Over budget" and the condition argument evaluates to FALSE,
  415. * then the IF function returns the text "Over budget".
  416. * If condition is FALSE and ReturnIfFalse is omitted, then the logical value FALSE is returned.
  417. * If condition is FALSE and ReturnIfFalse is blank, then the value 0 (zero) is returned.
  418. * ReturnIfFalse can be another formula.
  419. *
  420. * @access public
  421. * @category Logical Functions
  422. * @param mixed $condition Condition to evaluate
  423. * @param mixed $returnIfTrue Value to return when condition is true
  424. * @param mixed $returnIfFalse Optional value to return when condition is false
  425. * @return mixed The value of returnIfTrue or returnIfFalse determined by condition
  426. */
  427. public static function STATEMENT_IF($condition = true, $returnIfTrue = 0, $returnIfFalse = False) {
  428. $condition = self::flattenSingleValue($condition);
  429. $returnIfTrue = self::flattenSingleValue($returnIfTrue);
  430. $returnIfFalse = self::flattenSingleValue($returnIfFalse);
  431. if (is_null($returnIfTrue)) { $returnIfTrue = 0; }
  432. if (is_null($returnIfFalse)) { $returnIfFalse = 0; }
  433. return ($condition ? $returnIfTrue : $returnIfFalse);
  434. } // function STATEMENT_IF()
  435. /**
  436. * STATEMENT_IFERROR
  437. *
  438. * @param mixed $value Value to check , is also value when no error
  439. * @param mixed $errorpart Value when error
  440. * @return mixed
  441. */
  442. public static function STATEMENT_IFERROR($value = '', $errorpart = '') {
  443. return self::STATEMENT_IF(self::IS_ERROR($value), $errorpart, $value);
  444. } // function STATEMENT_IFERROR()
  445. /**
  446. * ATAN2
  447. *
  448. * This function calculates the arc tangent of the two variables x and y. It is similar to
  449. * calculating the arc tangent of y รท x, except that the signs of both arguments are used
  450. * to determine the quadrant of the result.
  451. * The arctangent is the angle from the x-axis to a line containing the origin (0, 0) and a
  452. * point with coordinates (xCoordinate, yCoordinate). The angle is given in radians between
  453. * -pi and pi, excluding -pi.
  454. *
  455. * Note that the Excel ATAN2() function accepts its arguments in the reverse order to the standard
  456. * PHP atan2() function, so we need to reverse them here before calling the PHP atan() function.
  457. *
  458. * Excel Function:
  459. * ATAN2(xCoordinate,yCoordinate)
  460. *
  461. * @access public
  462. * @category Mathematical and Trigonometric Functions
  463. * @param float $xCoordinate The x-coordinate of the point.
  464. * @param float $yCoordinate The y-coordinate of the point.
  465. * @return float The inverse tangent of the specified x- and y-coordinates.
  466. */
  467. public static function REVERSE_ATAN2($xCoordinate, $yCoordinate) {
  468. $xCoordinate = (float) self::flattenSingleValue($xCoordinate);
  469. $yCoordinate = (float) self::flattenSingleValue($yCoordinate);
  470. if (($xCoordinate == 0) && ($yCoordinate == 0)) {
  471. return self::$_errorCodes['divisionbyzero'];
  472. }
  473. return atan2($yCoordinate, $xCoordinate);
  474. } // function REVERSE_ATAN2()
  475. /**
  476. * LOG_BASE
  477. *
  478. * Returns the logarithm of a number to a specified base. The default base is 10.
  479. *
  480. * Excel Function:
  481. * LOG(number[,base])
  482. *
  483. * @access public
  484. * @category Mathematical and Trigonometric Functions
  485. * @param float $value The positive real number for which you want the logarithm
  486. * @param float $base The base of the logarithm. If base is omitted, it is assumed to be 10.
  487. * @return float
  488. */
  489. public static function LOG_BASE($number, $base=10) {
  490. $number = self::flattenSingleValue($number);
  491. $base = self::flattenSingleValue($base);
  492. return log($number, $base);
  493. } // function LOG_BASE()
  494. /**
  495. * SUM
  496. *
  497. * SUM computes the sum of all the values and cells referenced in the argument list.
  498. *
  499. * Excel Function:
  500. * SUM(value1[,value2[, ...]])
  501. *
  502. * @access public
  503. * @category Mathematical and Trigonometric Functions
  504. * @param mixed $arg,... Data values
  505. * @return float
  506. */
  507. public static function SUM() {
  508. // Return value
  509. $returnValue = 0;
  510. // Loop through the arguments
  511. $aArgs = self::flattenArray(func_get_args());
  512. foreach ($aArgs as $arg) {
  513. // Is it a numeric value?
  514. if ((is_numeric($arg)) && (!is_string($arg))) {
  515. $returnValue += $arg;
  516. }
  517. }
  518. // Return
  519. return $returnValue;
  520. } // function SUM()
  521. /**
  522. * SUMSQ
  523. *
  524. * SUMSQ returns the sum of the squares of the arguments
  525. *
  526. * Excel Function:
  527. * SUMSQ(value1[,value2[, ...]])
  528. *
  529. * @access public
  530. * @category Mathematical and Trigonometric Functions
  531. * @param mixed $arg,... Data values
  532. * @return float
  533. */
  534. public static function SUMSQ() {
  535. // Return value
  536. $returnValue = 0;
  537. // Loop through arguments
  538. $aArgs = self::flattenArray(func_get_args());
  539. foreach ($aArgs as $arg) {
  540. // Is it a numeric value?
  541. if ((is_numeric($arg)) && (!is_string($arg))) {
  542. $returnValue += ($arg * $arg);
  543. }
  544. }
  545. // Return
  546. return $returnValue;
  547. } // function SUMSQ()
  548. /**
  549. * PRODUCT
  550. *
  551. * PRODUCT returns the product of all the values and cells referenced in the argument list.
  552. *
  553. * Excel Function:
  554. * PRODUCT(value1[,value2[, ...]])
  555. *
  556. * @access public
  557. * @category Mathematical and Trigonometric Functions
  558. * @param mixed $arg,... Data values
  559. * @return float
  560. */
  561. public static function PRODUCT() {
  562. // Return value
  563. $returnValue = null;
  564. // Loop through arguments
  565. $aArgs = self::flattenArray(func_get_args());
  566. foreach ($aArgs as $arg) {
  567. // Is it a numeric value?
  568. if ((is_numeric($arg)) && (!is_string($arg))) {
  569. if (is_null($returnValue)) {
  570. $returnValue = $arg;
  571. } else {
  572. $returnValue *= $arg;
  573. }
  574. }
  575. }
  576. // Return
  577. if (is_null($returnValue)) {
  578. return 0;
  579. }
  580. return $returnValue;
  581. } // function PRODUCT()
  582. /**
  583. * QUOTIENT
  584. *
  585. * QUOTIENT function returns the integer portion of a division. Numerator is the divided number
  586. * and denominator is the divisor.
  587. *
  588. * Excel Function:
  589. * QUOTIENT(value1[,value2[, ...]])
  590. *
  591. * @access public
  592. * @category Mathematical and Trigonometric Functions
  593. * @param mixed $arg,... Data values
  594. * @return float
  595. */
  596. public static function QUOTIENT() {
  597. // Return value
  598. $returnValue = null;
  599. // Loop through arguments
  600. $aArgs = self::flattenArray(func_get_args());
  601. foreach ($aArgs as $arg) {
  602. // Is it a numeric value?
  603. if ((is_numeric($arg)) && (!is_string($arg))) {
  604. if (is_null($returnValue)) {
  605. $returnValue = ($arg == 0) ? 0 : $arg;
  606. } else {
  607. if (($returnValue == 0) || ($arg == 0)) {
  608. $returnValue = 0;
  609. } else {
  610. $returnValue /= $arg;
  611. }
  612. }
  613. }
  614. }
  615. // Return
  616. return intval($returnValue);
  617. } // function QUOTIENT()
  618. /**
  619. * MIN
  620. *
  621. * MIN returns the value of the element of the values passed that has the smallest value,
  622. * with negative numbers considered smaller than positive numbers.
  623. *
  624. * Excel Function:
  625. * MIN(value1[,value2[, ...]])
  626. *
  627. * @access public
  628. * @category Statistical Functions
  629. * @param mixed $arg,... Data values
  630. * @return float
  631. */
  632. public static function MIN() {
  633. // Return value
  634. $returnValue = null;
  635. // Loop through arguments
  636. $aArgs = self::flattenArray(func_get_args());
  637. foreach ($aArgs as $arg) {
  638. // Is it a numeric value?
  639. if ((is_numeric($arg)) && (!is_string($arg))) {
  640. if ((is_null($returnValue)) || ($arg < $returnValue)) {
  641. $returnValue = $arg;
  642. }
  643. }
  644. }
  645. // Return
  646. if(is_null($returnValue)) {
  647. return 0;
  648. }
  649. return $returnValue;
  650. } // function MIN()
  651. /**
  652. * MINA
  653. *
  654. * Returns the smallest value in a list of arguments, including numbers, text, and logical values
  655. *
  656. * Excel Function:
  657. * MINA(value1[,value2[, ...]])
  658. *
  659. * @access public
  660. * @category Statistical Functions
  661. * @param mixed $arg,... Data values
  662. * @return float
  663. */
  664. public static function MINA() {
  665. // Return value
  666. $returnValue = null;
  667. // Loop through arguments
  668. $aArgs = self::flattenArray(func_get_args());
  669. foreach ($aArgs as $arg) {
  670. // Is it a numeric value?
  671. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
  672. if (is_bool($arg)) {
  673. $arg = (integer) $arg;
  674. } elseif (is_string($arg)) {
  675. $arg = 0;
  676. }
  677. if ((is_null($returnValue)) || ($arg < $returnValue)) {
  678. $returnValue = $arg;
  679. }
  680. }
  681. }
  682. // Return
  683. if(is_null($returnValue)) {
  684. return 0;
  685. }
  686. return $returnValue;
  687. } // function MINA()
  688. /**
  689. * SMALL
  690. *
  691. * Returns the nth smallest value in a data set. You can use this function to
  692. * select a value based on its relative standing.
  693. *
  694. * Excel Function:
  695. * SMALL(value1[,value2[, ...]],entry)
  696. *
  697. * @access public
  698. * @category Statistical Functions
  699. * @param mixed $arg,... Data values
  700. * @param int $entry Position (ordered from the smallest) in the array or range of data to return
  701. * @return float
  702. */
  703. public static function SMALL() {
  704. $aArgs = self::flattenArray(func_get_args());
  705. // Calculate
  706. $n = array_pop($aArgs);
  707. if ((is_numeric($n)) && (!is_string($n))) {
  708. $mArgs = array();
  709. foreach ($aArgs as $arg) {
  710. // Is it a numeric value?
  711. if ((is_numeric($arg)) && (!is_string($arg))) {
  712. $mArgs[] = $arg;
  713. }
  714. }
  715. $count = self::COUNT($mArgs);
  716. $n = floor(--$n);
  717. if (($n < 0) || ($n >= $count) || ($count == 0)) {
  718. return self::$_errorCodes['num'];
  719. }
  720. sort($mArgs);
  721. return $mArgs[$n];
  722. }
  723. return self::$_errorCodes['value'];
  724. } // function SMALL()
  725. /**
  726. * MAX
  727. *
  728. * MAX returns the value of the element of the values passed that has the highest value,
  729. * with negative numbers considered smaller than positive numbers.
  730. *
  731. * Excel Function:
  732. * MAX(value1[,value2[, ...]])
  733. *
  734. * @access public
  735. * @category Statistical Functions
  736. * @param mixed $arg,... Data values
  737. * @return float
  738. */
  739. public static function MAX() {
  740. // Return value
  741. $returnValue = null;
  742. // Loop through arguments
  743. $aArgs = self::flattenArray(func_get_args());
  744. foreach ($aArgs as $arg) {
  745. // Is it a numeric value?
  746. if ((is_numeric($arg)) && (!is_string($arg))) {
  747. if ((is_null($returnValue)) || ($arg > $returnValue)) {
  748. $returnValue = $arg;
  749. }
  750. }
  751. }
  752. // Return
  753. if(is_null($returnValue)) {
  754. return 0;
  755. }
  756. return $returnValue;
  757. } // function MAX()
  758. /**
  759. * MAXA
  760. *
  761. * Returns the greatest value in a list of arguments, including numbers, text, and logical values
  762. *
  763. * Excel Function:
  764. * MAXA(value1[,value2[, ...]])
  765. *
  766. * @access public
  767. * @category Statistical Functions
  768. * @param mixed $arg,... Data values
  769. * @return float
  770. */
  771. public static function MAXA() {
  772. // Return value
  773. $returnValue = null;
  774. // Loop through arguments
  775. $aArgs = self::flattenArray(func_get_args());
  776. foreach ($aArgs as $arg) {
  777. // Is it a numeric value?
  778. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
  779. if (is_bool($arg)) {
  780. $arg = (integer) $arg;
  781. } elseif (is_string($arg)) {
  782. $arg = 0;
  783. }
  784. if ((is_null($returnValue)) || ($arg > $returnValue)) {
  785. $returnValue = $arg;
  786. }
  787. }
  788. }
  789. // Return
  790. if(is_null($returnValue)) {
  791. return 0;
  792. }
  793. return $returnValue;
  794. } // function MAXA()
  795. /**
  796. * LARGE
  797. *
  798. * Returns the nth largest value in a data set. You can use this function to
  799. * select a value based on its relative standing.
  800. *
  801. * Excel Function:
  802. * LARGE(value1[,value2[, ...]],entry)
  803. *
  804. * @access public
  805. * @category Statistical Functions
  806. * @param mixed $arg,... Data values
  807. * @param int $entry Position (ordered from the largest) in the array or range of data to return
  808. * @return float
  809. *
  810. */
  811. public static function LARGE() {
  812. $aArgs = self::flattenArray(func_get_args());
  813. // Calculate
  814. $n = floor(array_pop($aArgs));
  815. if ((is_numeric($n)) && (!is_string($n))) {
  816. $mArgs = array();
  817. foreach ($aArgs as $arg) {
  818. // Is it a numeric value?
  819. if ((is_numeric($arg)) && (!is_string($arg))) {
  820. $mArgs[] = $arg;
  821. }
  822. }
  823. $count = self::COUNT($mArgs);
  824. $n = floor(--$n);
  825. if (($n < 0) || ($n >= $count) || ($count == 0)) {
  826. return self::$_errorCodes['num'];
  827. }
  828. rsort($mArgs);
  829. return $mArgs[$n];
  830. }
  831. return self::$_errorCodes['value'];
  832. } // function LARGE()
  833. /**
  834. * PERCENTILE
  835. *
  836. * Returns the nth percentile of values in a range..
  837. *
  838. * Excel Function:
  839. * PERCENTILE(value1[,value2[, ...]],entry)
  840. *
  841. * @access public
  842. * @category Statistical Functions
  843. * @param mixed $arg,... Data values
  844. * @param float $entry Percentile value in the range 0..1, inclusive.
  845. * @return float
  846. */
  847. public static function PERCENTILE() {
  848. $aArgs = self::flattenArray(func_get_args());
  849. // Calculate
  850. $entry = array_pop($aArgs);
  851. if ((is_numeric($entry)) && (!is_string($entry))) {
  852. if (($entry < 0) || ($entry > 1)) {
  853. return self::$_errorCodes['num'];
  854. }
  855. $mArgs = array();
  856. foreach ($aArgs as $arg) {
  857. // Is it a numeric value?
  858. if ((is_numeric($arg)) && (!is_string($arg))) {
  859. $mArgs[] = $arg;
  860. }
  861. }
  862. $mValueCount = count($mArgs);
  863. if ($mValueCount > 0) {
  864. sort($mArgs);
  865. $count = self::COUNT($mArgs);
  866. $index = $entry * ($count-1);
  867. $iBase = floor($index);
  868. if ($index == $iBase) {
  869. return $mArgs[$index];
  870. } else {
  871. $iNext = $iBase + 1;
  872. $iProportion = $index - $iBase;
  873. return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion) ;
  874. }
  875. }
  876. }
  877. return self::$_errorCodes['value'];
  878. } // function PERCENTILE()
  879. /**
  880. * QUARTILE
  881. *
  882. * Returns the quartile of a data set.
  883. *
  884. * Excel Function:
  885. * QUARTILE(value1[,value2[, ...]],entry)
  886. *
  887. * @access public
  888. * @category Statistical Functions
  889. * @param mixed $arg,... Data values
  890. * @param int $entry Quartile value in the range 1..3, inclusive.
  891. * @return float
  892. */
  893. public static function QUARTILE() {
  894. $aArgs = self::flattenArray(func_get_args());
  895. // Calculate
  896. $entry = floor(array_pop($aArgs));
  897. if ((is_numeric($entry)) && (!is_string($entry))) {
  898. $entry /= 4;
  899. if (($entry < 0) || ($entry > 1)) {
  900. return self::$_errorCodes['num'];
  901. }
  902. return self::PERCENTILE($aArgs,$entry);
  903. }
  904. return self::$_errorCodes['value'];
  905. } // function QUARTILE()
  906. /**
  907. * COUNT
  908. *
  909. * Counts the number of cells that contain numbers within the list of arguments
  910. *
  911. * Excel Function:
  912. * COUNT(value1[,value2[, ...]])
  913. *
  914. * @access public
  915. * @category Statistical Functions
  916. * @param mixed $arg,... Data values
  917. * @return int
  918. */
  919. public static function COUNT() {
  920. // Return value
  921. $returnValue = 0;
  922. // Loop through arguments
  923. $aArgs = self::flattenArray(func_get_args());
  924. foreach ($aArgs as $arg) {
  925. if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  926. $arg = (int) $arg;
  927. }
  928. // Is it a numeric value?
  929. if ((is_numeric($arg)) && (!is_string($arg))) {
  930. ++$returnValue;
  931. }
  932. }
  933. // Return
  934. return $returnValue;
  935. } // function COUNT()
  936. /**
  937. * COUNTBLANK
  938. *
  939. * Counts the number of empty cells within the list of arguments
  940. *
  941. * Excel Function:
  942. * COUNTBLANK(value1[,value2[, ...]])
  943. *
  944. * @access public
  945. * @category Statistical Functions
  946. * @param mixed $arg,... Data values
  947. * @return int
  948. */
  949. public static function COUNTBLANK() {
  950. // Return value
  951. $returnValue = 0;
  952. // Loop through arguments
  953. $aArgs = self::flattenArray(func_get_args());
  954. foreach ($aArgs as $arg) {
  955. // Is it a blank cell?
  956. if ((is_null($arg)) || ((is_string($arg)) && ($arg == ''))) {
  957. ++$returnValue;
  958. }
  959. }
  960. // Return
  961. return $returnValue;
  962. } // function COUNTBLANK()
  963. /**
  964. * COUNTA
  965. *
  966. * Counts the number of cells that are not empty within the list of arguments
  967. *
  968. * Excel Function:
  969. * COUNTA(value1[,value2[, ...]])
  970. *
  971. * @access public
  972. * @category Statistical Functions
  973. * @param mixed $arg,... Data values
  974. * @return int
  975. */
  976. public static function COUNTA() {
  977. // Return value
  978. $returnValue = 0;
  979. // Loop through arguments
  980. $aArgs = self::flattenArray(func_get_args());
  981. foreach ($aArgs as $arg) {
  982. // Is it a numeric, boolean or string value?
  983. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
  984. ++$returnValue;
  985. }
  986. }
  987. // Return
  988. return $returnValue;
  989. } // function COUNTA()
  990. /**
  991. * COUNTIF
  992. *
  993. * Counts the number of cells that contain numbers within the list of arguments
  994. *
  995. * Excel Function:
  996. * COUNTIF(value1[,value2[, ...]],condition)
  997. *
  998. * @access public
  999. * @category Statistical Functions
  1000. * @param mixed $arg,... Data values
  1001. * @param string $condition The criteria that defines which cells will be counted.
  1002. * @return int
  1003. */
  1004. public static function COUNTIF($aArgs,$condition) {
  1005. // Return value
  1006. $returnValue = 0;
  1007. $aArgs = self::flattenArray($aArgs);
  1008. if (!in_array($condition{0},array('>', '<', '='))) {
  1009. if (!is_numeric($condition)) { $condition = PHPExcel_Calculation::_wrapResult(strtoupper($condition)); }
  1010. $condition = '='.$condition;
  1011. } else {
  1012. preg_match('/([<>=]+)(.*)/',$condition,$matches);
  1013. list(,$operator,$operand) = $matches;
  1014. if (!is_numeric($operand)) { $operand = PHPExcel_Calculation::_wrapResult(strtoupper($operand)); }
  1015. $condition = $operator.$operand;
  1016. }
  1017. // Loop through arguments
  1018. foreach ($aArgs as $arg) {
  1019. if (!is_numeric($arg)) { $arg = PHPExcel_Calculation::_wrapResult(strtoupper($arg)); }
  1020. $testCondition = '='.$arg.$condition;
  1021. if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
  1022. // Is it a value within our criteria
  1023. ++$returnValue;
  1024. }
  1025. }
  1026. // Return
  1027. return $returnValue;
  1028. } // function COUNTIF()
  1029. /**
  1030. * SUMIF
  1031. *
  1032. * Counts the number of cells that contain numbers within the list of arguments
  1033. *
  1034. * Excel Function:
  1035. * SUMIF(value1[,value2[, ...]],condition)
  1036. *
  1037. * @access public
  1038. * @category Mathematical and Trigonometric Functions
  1039. * @param mixed $arg,... Data values
  1040. * @param string $condition The criteria that defines which cells will be summed.
  1041. * @return float
  1042. */
  1043. public static function SUMIF($aArgs,$condition,$sumArgs = array()) {
  1044. // Return value
  1045. $returnValue = 0;
  1046. $aArgs = self::flattenArray($aArgs);
  1047. $sumArgs = self::flattenArray($sumArgs);
  1048. if (count($sumArgs) == 0) {
  1049. $sumArgs = $aArgs;
  1050. }
  1051. if (!in_array($condition{0},array('>', '<', '='))) {
  1052. if (!is_numeric($condition)) { $condition = PHPExcel_Calculation::_wrapResult(strtoupper($condition)); }
  1053. $condition = '='.$condition;
  1054. } else {
  1055. preg_match('/([<>=]+)(.*)/',$condition,$matches);
  1056. list(,$operator,$operand) = $matches;
  1057. if (!is_numeric($operand)) { $operand = PHPExcel_Calculation::_wrapResult(strtoupper($operand)); }
  1058. $condition = $operator.$operand;
  1059. }
  1060. // Loop through arguments
  1061. foreach ($aArgs as $key => $arg) {
  1062. if (!is_numeric($arg)) { $arg = PHPExcel_Calculation::_wrapResult(strtoupper($arg)); }
  1063. $testCondition = '='.$arg.$condition;
  1064. if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
  1065. // Is it a value within our criteria
  1066. $returnValue += $sumArgs[$key];
  1067. }
  1068. }
  1069. // Return
  1070. return $returnValue;
  1071. } // function SUMIF()
  1072. /**
  1073. * AVERAGE
  1074. *
  1075. * Returns the average (arithmetic mean) of the arguments
  1076. *
  1077. * Excel Function:
  1078. * AVERAGE(value1[,value2[, ...]])
  1079. *
  1080. * @access public
  1081. * @category Statistical Functions
  1082. * @param mixed $arg,... Data values
  1083. * @return float
  1084. */
  1085. public static function AVERAGE() {
  1086. // Return value
  1087. $returnValue = 0;
  1088. // Loop through arguments
  1089. $aArgs = self::flattenArray(func_get_args());
  1090. $aCount = 0;
  1091. foreach ($aArgs as $arg) {
  1092. if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  1093. $arg = (integer) $arg;
  1094. }
  1095. // Is it a numeric value?
  1096. if ((is_numeric($arg)) && (!is_string($arg))) {
  1097. if (is_null($returnValue)) {
  1098. $returnValue = $arg;
  1099. } else {
  1100. $returnValue += $arg;
  1101. }
  1102. ++$aCount;
  1103. }
  1104. }
  1105. // Return
  1106. if ($aCount > 0) {
  1107. return $returnValue / $aCount;
  1108. } else {
  1109. return self::$_errorCodes['divisionbyzero'];
  1110. }
  1111. } // function AVERAGE()
  1112. /**
  1113. * AVERAGEA
  1114. *
  1115. * Returns the average of its arguments, including numbers, text, and logical values
  1116. *
  1117. * Excel Function:
  1118. * AVERAGEA(value1[,value2[, ...]])
  1119. *
  1120. * @access public
  1121. * @category Statistical Functions
  1122. * @param mixed $arg,... Data values
  1123. * @return float
  1124. */
  1125. public static function AVERAGEA() {
  1126. // Return value
  1127. $returnValue = null;
  1128. // Loop through arguments
  1129. $aArgs = self::flattenArray(func_get_args());
  1130. $aCount = 0;
  1131. foreach ($aArgs as $arg) {
  1132. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
  1133. if (is_bool($arg)) {
  1134. $arg = (integer) $arg;
  1135. } elseif (is_string($arg)) {
  1136. $arg = 0;
  1137. }
  1138. if (is_null($returnValue)) {
  1139. $returnValue = $arg;
  1140. } else {
  1141. $returnValue += $arg;
  1142. }
  1143. ++$aCount;
  1144. }
  1145. }
  1146. // Return
  1147. if ($aCount > 0) {
  1148. return $returnValue / $aCount;
  1149. } else {
  1150. return self::$_errorCodes['divisionbyzero'];
  1151. }
  1152. } // function AVERAGEA()
  1153. /**
  1154. * MEDIAN
  1155. *
  1156. * Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
  1157. *
  1158. * Excel Function:
  1159. * MEDIAN(value1[,value2[, ...]])
  1160. *
  1161. * @access public
  1162. * @category Statistical Functions
  1163. * @param mixed $arg,... Data values
  1164. * @return float
  1165. */
  1166. public static function MEDIAN() {
  1167. // Return value
  1168. $returnValue = self::$_errorCodes['num'];
  1169. $mArgs = array();
  1170. // Loop through arguments
  1171. $aArgs = self::flattenArray(func_get_args());
  1172. foreach ($aArgs as $arg) {
  1173. // Is it a numeric value?
  1174. if ((is_numeric($arg)) && (!is_string($arg))) {
  1175. $mArgs[] = $arg;
  1176. }
  1177. }
  1178. $mValueCount = count($mArgs);
  1179. if ($mValueCount > 0) {
  1180. sort($mArgs,SORT_NUMERIC);
  1181. $mValueCount = $mValueCount / 2;
  1182. if ($mValueCount == floor($mValueCount)) {
  1183. $returnValue = ($mArgs[$mValueCount--] + $mArgs[$mValueCount]) / 2;
  1184. } else {
  1185. $mValueCount == floor($mValueCount);
  1186. $returnValue = $mArgs[$mValueCount];
  1187. }
  1188. }
  1189. // Return
  1190. return $returnValue;
  1191. } // function MEDIAN()
  1192. //
  1193. // Special variant of array_count_values that isn't limited to strings and integers,
  1194. // but can work with floating point numbers as values
  1195. //
  1196. private static function _modeCalc($data) {
  1197. $frequencyArray = array();
  1198. foreach($data as $datum) {
  1199. $found = False;
  1200. foreach($frequencyArray as $key => $value) {
  1201. if ((string) $value['value'] == (string) $datum) {
  1202. ++$frequencyArray[$key]['frequency'];
  1203. $found = True;
  1204. break;
  1205. }
  1206. }
  1207. if (!$found) {
  1208. $frequencyArray[] = array('value' => $datum,
  1209. 'frequency' => 1 );
  1210. }
  1211. }
  1212. foreach($frequencyArray as $key => $value) {
  1213. $frequencyList[$key] = $value['frequency'];
  1214. $valueList[$key] = $value['value'];
  1215. }
  1216. array_multisort($frequencyList, SORT_DESC, $valueList, SORT_ASC, SORT_NUMERIC, $frequencyArray);
  1217. if ($frequencyArray[0]['frequency'] == 1) {
  1218. return self::NA();
  1219. }
  1220. return $frequencyArray[0]['value'];
  1221. } // function _modeCalc()
  1222. /**
  1223. * MODE
  1224. *
  1225. * Returns the most frequently occurring, or repetitive, value in an array or range of data
  1226. *
  1227. * Excel Function:
  1228. * MODE(value1[,value2[, ...]])
  1229. *
  1230. * @access public
  1231. * @category Statistical Functions
  1232. * @param mixed $arg,... Data values
  1233. * @return float
  1234. */
  1235. public static function MODE() {
  1236. // Return value
  1237. $returnValue = self::NA();
  1238. // Loop through arguments
  1239. $aArgs = self::flattenArray(func_get_args());
  1240. $mArgs = array();
  1241. foreach ($aArgs as $arg) {
  1242. // Is it a numeric value?
  1243. if ((is_numeric($arg)) && (!is_string($arg))) {
  1244. $mArgs[] = $arg;
  1245. }
  1246. }
  1247. if (count($mArgs) > 0) {
  1248. return self::_modeCalc($mArgs);
  1249. }
  1250. // Return
  1251. return $returnValue;
  1252. } // function MODE()
  1253. /**
  1254. * DEVSQ
  1255. *
  1256. * Returns the sum of squares of deviations of data points from their sample mean.
  1257. *
  1258. * Excel Function:
  1259. * DEVSQ(value1[,value2[, ...]])
  1260. *
  1261. * @access public
  1262. * @category Statistical Functions
  1263. * @param mixed $arg,... Data values
  1264. * @return float
  1265. */
  1266. public static function DEVSQ() {
  1267. // Return value
  1268. $returnValue = null;
  1269. $aMean = self::AVERAGE(func_get_args());
  1270. if (!is_null($aMean)) {
  1271. $aArgs = self::flattenArray(func_get_args());
  1272. $aCount = -1;
  1273. foreach ($aArgs as $arg) {
  1274. // Is it a numeric value?
  1275. if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  1276. $arg = (int) $arg;
  1277. }
  1278. if ((is_numeric($arg)) && (!is_string($arg))) {
  1279. if (is_null($returnValue)) {
  1280. $returnValue = pow(($arg - $aMean),2);
  1281. } else {
  1282. $returnValue += pow(($arg - $aMean),2);
  1283. }
  1284. ++$aCount;
  1285. }
  1286. }
  1287. // Return
  1288. if (is_null($returnValue)) {
  1289. return self::$_errorCodes['num'];
  1290. } else {
  1291. return $returnValue;
  1292. }
  1293. }
  1294. return self::NA();
  1295. } // function DEVSQ()
  1296. /**
  1297. * AVEDEV
  1298. *
  1299. * Returns the average of the absolute deviations of data points from their mean.
  1300. * AVEDEV is a measure of the variability in a data set.
  1301. *
  1302. * Excel Function:
  1303. * AVEDEV(value1[,value2[, ...]])
  1304. *
  1305. * @access public
  1306. * @category Statistical Functions
  1307. * @param mixed $arg,... Data values
  1308. * @return float
  1309. */
  1310. public static function AVEDEV() {
  1311. $aArgs = self::flattenArray(func_get_args());
  1312. // Return value
  1313. $returnValue = null;
  1314. $aMean = self::AVERAGE($aArgs);
  1315. if ($aMean != self::$_errorCodes['divisionbyzero']) {
  1316. $aCount = 0;
  1317. foreach ($aArgs as $arg) {
  1318. if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  1319. $arg = (integer) $arg;
  1320. }
  1321. // Is it a numeric value?
  1322. if ((is_numeric($arg)) && (!is_string($arg))) {
  1323. if (is_null($returnValue)) {
  1324. $returnValue = abs($arg - $aMean);
  1325. } else {
  1326. $returnValue += abs($arg - $aMean);
  1327. }
  1328. ++$aCount;
  1329. }
  1330. }
  1331. // Return
  1332. return $returnValue / $aCount ;
  1333. }
  1334. return self::$_errorCodes['num'];
  1335. } // function AVEDEV()
  1336. /**
  1337. * GEOMEAN
  1338. *
  1339. * Returns the geometric mean of an array or range of positive data. For example, you
  1340. * can use GEOMEAN to calculate average growth rate given compound interest with
  1341. * variable rates.
  1342. *
  1343. * Excel Function:
  1344. * GEOMEAN(value1[,value2[, ...]])
  1345. *
  1346. * @access public
  1347. * @category Statistical Functions
  1348. * @param mixed $arg,... Data values
  1349. * @return float
  1350. */
  1351. public static function GEOMEAN() {
  1352. $aMean = self::PRODUCT(func_get_args());
  1353. if (is_numeric($aMean) && ($aMean > 0)) {
  1354. $aArgs = self::flattenArray(func_get_args());
  1355. $aCount = self::COUNT($aArgs) ;
  1356. if (self::MIN($aArgs) > 0) {
  1357. return pow($aMean, (1 / $aCount));
  1358. }
  1359. }
  1360. return self::$_errorCodes['num'];
  1361. } // GEOMEAN()
  1362. /**
  1363. * HARMEAN
  1364. *
  1365. * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
  1366. * arithmetic mean of reciprocals.
  1367. *
  1368. * Excel Function:
  1369. * HARMEAN(value1[,value2[, ...]])
  1370. *
  1371. * @access public
  1372. * @category Statistical Functions
  1373. * @param mixed $arg,... Data values
  1374. * @return float
  1375. */
  1376. public static function HARMEAN() {
  1377. // Return value
  1378. $returnValue = self::NA();
  1379. // Loop through arguments
  1380. $aArgs = self::flattenArray(func_get_args());
  1381. if (self::MIN($aArgs) < 0) {
  1382. return self::$_errorCodes['num'];
  1383. }
  1384. $aCount = 0;
  1385. foreach ($aArgs as $arg) {
  1386. // Is it a numeric value?
  1387. if ((is_numeric($arg)) && (!is_string($arg))) {
  1388. if ($arg <= 0) {
  1389. return self::$_errorCodes['num'];
  1390. }
  1391. if (is_null($returnValue)) {
  1392. $returnValue = (1 / $arg);
  1393. } else {
  1394. $returnValue += (1 / $arg);
  1395. }
  1396. ++$aCount;
  1397. }
  1398. }
  1399. // Return
  1400. if ($aCount > 0) {
  1401. return 1 / ($returnValue / $aCount);
  1402. } else {
  1403. return $returnValue;
  1404. }
  1405. } // function HARMEAN()
  1406. /**
  1407. * TRIMMEAN
  1408. *
  1409. * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
  1410. * taken by excluding a percentage of data points from the top and bottom tails
  1411. * of a data set.
  1412. *
  1413. * Excel Function:
  1414. * TRIMEAN(value1[,value2[, ...]],$discard)
  1415. *
  1416. * @access public
  1417. * @category Statistical Functions
  1418. * @param mixed $arg,... Data values
  1419. * @param float $discard Percentage to discard
  1420. * @return float
  1421. */
  1422. public static function TRIMMEAN() {
  1423. $aArgs = self::flattenArray(func_get_args());
  1424. // Calculate
  1425. $percent = array_pop($aArgs);
  1426. if ((is_numeric($percent)) && (!is_string($percent))) {
  1427. if (($percent < 0) || ($percent > 1)) {
  1428. return self::$_errorCodes['num'];
  1429. }
  1430. $mArgs = array();
  1431. foreach ($aArgs as $arg) {
  1432. // Is it a numeric value?
  1433. if ((is_numeric($arg)) && (!is_string($arg))) {
  1434. $mArgs[] = $arg;
  1435. }
  1436. }
  1437. $discard = floor(self::COUNT($mArgs) * $percent / 2);
  1438. sort($mArgs);
  1439. for ($i=0; $i < $discard; ++$i) {
  1440. array_pop($mArgs);
  1441. array_shift($mArgs);
  1442. }
  1443. return self::AVERAGE($mArgs);
  1444. }
  1445. return self::$_errorCodes['value'];
  1446. } // function TRIMMEAN()
  1447. /**
  1448. * STDEV
  1449. *
  1450. * Estimates standard deviation based on a sample. The standard deviation is a measure of how
  1451. * widely values are dispersed from the average value (the mean).
  1452. *
  1453. * Excel Function:
  1454. * STDEV(value1[,value2[, ...]])
  1455. *
  1456. * @access public
  1457. * @category Statistical Functions
  1458. * @param mixed $arg,... Data values
  1459. * @return float
  1460. */
  1461. public static function STDEV() {
  1462. // Return value
  1463. $returnValue = null;
  1464. $aMean = self::AVERAGE(func_get_args());
  1465. if (!is_null($aMean)) {
  1466. $aArgs = self::flattenArray(func_get_args());
  1467. $aCount = -1;
  1468. foreach ($aArgs as $arg) {
  1469. // Is it a numeric value?
  1470. if ((is_numeric($arg)) && (!is_string($arg))) {
  1471. if (is_null($returnValue)) {
  1472. $returnValue = pow(($arg - $aMean),2);
  1473. } else {
  1474. $returnValue += pow(($arg - $aMean),2);
  1475. }
  1476. ++$aCount;
  1477. }
  1478. }
  1479. // Return
  1480. if (($aCount > 0) && ($returnValue > 0)) {
  1481. return sqrt($returnValue / $aCount);
  1482. }
  1483. }
  1484. return self::$_errorCodes['divisionbyzero'];
  1485. } // function STDEV()
  1486. /**
  1487. * STDEVA
  1488. *
  1489. * Estimates standard deviation based on a sample, including numbers, text, and logical values
  1490. *
  1491. * Excel Function:
  1492. * STDEVA(value1[,value2[, ...]])
  1493. *
  1494. * @access public
  1495. * @category Statistical Functions
  1496. * @param mixed $arg,... Data values
  1497. * @return float
  1498. */
  1499. public static function STDEVA() {
  1500. // Return value
  1501. $returnValue = null;
  1502. $aMean = self::AVERAGEA(func_get_args());
  1503. if (!is_null($aMean)) {
  1504. $aArgs = self::flattenArray(func_get_args());
  1505. $aCount = -1;
  1506. foreach ($aArgs as $arg) {
  1507. // Is it a numeric value?
  1508. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
  1509. if (is_bool($arg)) {
  1510. $arg = (integer) $arg;
  1511. } elseif (is_string($arg)) {
  1512. $arg = 0;
  1513. }
  1514. if (is_null($returnValue)) {
  1515. $returnValue = pow(($arg - $aMean),2);
  1516. } else {
  1517. $returnValue += pow(($arg - $aMean),2);
  1518. }
  1519. ++$aCount;
  1520. }
  1521. }
  1522. // Return
  1523. if (($aCount > 0) && ($returnValue > 0)) {
  1524. return sqrt($returnValue / $aCount);
  1525. }
  1526. }
  1527. return self::$_errorCodes['divisionbyzero'];
  1528. } // function STDEVA()
  1529. /**
  1530. * STDEVP
  1531. *
  1532. * Calculates standard deviation based on the entire population
  1533. *
  1534. * Excel Function:
  1535. * STDEVP(value1[,value2[, ...]])
  1536. *
  1537. * @access public
  1538. * @category Statistical Functions
  1539. * @param mixed $arg,... Data values
  1540. * @return float
  1541. */
  1542. public static function STDEVP() {
  1543. // Return value
  1544. $returnValue = null;
  1545. $aMean = self::AVERAGE(func_get_args());
  1546. if (!is_null($aMean)) {
  1547. $aArgs = self::flattenArray(func_get_args());
  1548. $aCount = 0;
  1549. foreach ($aArgs as $arg) {
  1550. // Is it a numeric value?
  1551. if ((is_numeric($arg)) && (!is_string($arg))) {
  1552. if (is_null($returnValue)) {
  1553. $returnValue = pow(($arg - $aMean),2);
  1554. } else {
  1555. $returnValue += pow(($arg - $aMean),2);
  1556. }
  1557. ++$aCount;
  1558. }
  1559. }
  1560. // Return
  1561. if (($aCount > 0) && ($returnValue > 0)) {
  1562. return sqrt($returnValue / $aCount);
  1563. }
  1564. }
  1565. return self::$_errorCodes['divisionbyzero'];
  1566. } // function STDEVP()
  1567. /**
  1568. * STDEVPA
  1569. *
  1570. * Calculates standard deviation based on the entire population, including numbers, text, and logical values
  1571. *
  1572. * Excel Function:
  1573. * STDEVPA(value1[,value2[, ...]])
  1574. *
  1575. * @access public
  1576. * @category Statistical Functions
  1577. * @param mixed $arg,... Data values
  1578. * @return float
  1579. */
  1580. public static function STDEVPA() {
  1581. // Return value
  1582. $returnValue = null;
  1583. $aMean = self::AVERAGEA(func_get_args());
  1584. if (!is_null($aMean)) {
  1585. $aArgs = self::flattenArray(func_get_args());
  1586. $aCount = 0;
  1587. foreach ($aArgs as $arg) {
  1588. // Is it a numeric value?
  1589. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
  1590. if (is_bool($arg)) {
  1591. $arg = (integer) $arg;
  1592. } elseif (is_string($arg)) {
  1593. $arg = 0;
  1594. }
  1595. if (is_null($returnValue)) {
  1596. $returnValue = pow(($arg - $aMean),2);
  1597. } else {
  1598. $returnValue += pow(($arg - $aMean),2);
  1599. }
  1600. ++$aCount;
  1601. }
  1602. }
  1603. // Return
  1604. if (($aCount > 0) && ($returnValue > 0)) {
  1605. return sqrt($returnValue / $aCount);
  1606. }
  1607. }
  1608. return self::$_errorCodes['divisionbyzero'];
  1609. } // function STDEVPA()
  1610. /**
  1611. * VARFunc
  1612. *
  1613. * Estimates variance based on a sample.
  1614. *
  1615. * Excel Function:
  1616. * VAR(value1[,value2[, ...]])
  1617. *
  1618. * @access public
  1619. * @category Statistical Functions
  1620. * @param mixed $arg,... Data values
  1621. * @return float
  1622. */
  1623. public static function VARFunc() {
  1624. // Return value
  1625. $returnValue = self::$_errorCodes['divisionbyzero'];
  1626. $summerA = $summerB = 0;
  1627. // Loop through arguments
  1628. $aArgs = self::flattenArray(func_get_args());
  1629. $aCount = 0;
  1630. foreach ($aArgs as $arg) {
  1631. // Is it a numeric value?
  1632. if ((is_numeric($arg)) && (!is_string($arg))) {
  1633. $summerA += ($arg * $arg);
  1634. $summerB += $arg;
  1635. ++$aCount;
  1636. }
  1637. }
  1638. // Return
  1639. if ($aCount > 1) {
  1640. $summerA *= $aCount;
  1641. $summerB *= $summerB;
  1642. $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
  1643. }
  1644. return $returnValue;
  1645. } // function VARFunc()
  1646. /**
  1647. * VARA
  1648. *
  1649. * Estimates variance based on a sample, including numbers, text, and logical values
  1650. *
  1651. * Excel Function:
  1652. * VARA(value1[,value2[, ...]])
  1653. *
  1654. * @access public
  1655. * @category Statistical Functions
  1656. * @param mixed $arg,... Data values
  1657. * @return float
  1658. */
  1659. public static function VARA() {
  1660. // Return value
  1661. $returnValue = self::$_errorCodes['divisionbyzero'];
  1662. $summerA = $summerB = 0;
  1663. // Loop through arguments
  1664. $aArgs = self::flattenArray(func_get_args());
  1665. $aCount = 0;
  1666. foreach ($aArgs as $arg) {
  1667. // Is it a numeric value?
  1668. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
  1669. if (is_bool($arg)) {
  1670. $arg = (integer) $arg;
  1671. } elseif (is_string($arg)) {
  1672. $arg = 0;
  1673. }
  1674. $summerA += ($arg * $arg);
  1675. $summerB += $arg;
  1676. ++$aCount;
  1677. }
  1678. }
  1679. // Return
  1680. if ($aCount > 1) {
  1681. $summerA *= $aCount;
  1682. $summerB *= $summerB;
  1683. $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
  1684. }
  1685. return $returnValue;
  1686. } // function VARA()
  1687. /**
  1688. * VARP
  1689. *
  1690. * Calculates variance based on the entire population
  1691. *
  1692. * Excel Function:
  1693. * VARP(value1[,value2[, ...]])
  1694. *
  1695. * @access public
  1696. * @category Statistical Functions
  1697. * @param mixed $arg,... Data values
  1698. * @return float
  1699. */
  1700. public static function VARP() {
  1701. // Return value
  1702. $returnValue = self::$_errorCodes['divisionbyzero'];
  1703. $summerA = $summerB = 0;
  1704. // Loop through arguments
  1705. $aArgs = self::flattenArray(func_get_args());
  1706. $aCount = 0;
  1707. foreach ($aArgs as $arg) {
  1708. // Is it a numeric value?
  1709. if ((is_numeric($arg)) && (!is_string($arg))) {
  1710. $summerA += ($arg * $arg);
  1711. $summerB += $arg;
  1712. ++$aCount;
  1713. }
  1714. }
  1715. // Return
  1716. if ($aCount > 0) {
  1717. $summerA *= $aCount;
  1718. $summerB *= $summerB;
  1719. $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
  1720. }
  1721. return $returnValue;
  1722. } // function VARP()
  1723. /**
  1724. * VARPA
  1725. *
  1726. * Calculates variance based on the entire population, including numbers, text, and logical values
  1727. *
  1728. * Excel Function:
  1729. * VARPA(value1[,value2[, ...]])
  1730. *
  1731. * @access public
  1732. * @category Statistical Functions
  1733. * @param mixed $arg,... Data values
  1734. * @return float
  1735. */
  1736. public static function VARPA() {
  1737. // Return value
  1738. $returnValue = self::$_errorCodes['divisionbyzero'];
  1739. $summerA = $summerB = 0;
  1740. // Loop through arguments
  1741. $aArgs = self::flattenArray(func_get_args());
  1742. $aCount = 0;
  1743. foreach ($aArgs as $arg) {
  1744. // Is it a numeric value?
  1745. if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
  1746. if (is_bool($arg)) {
  1747. $arg = (integer) $arg;
  1748. } elseif (is_string($arg)) {
  1749. $arg = 0;
  1750. }
  1751. $summerA += ($arg * $arg);
  1752. $summerB += $arg;
  1753. ++$aCount;
  1754. }
  1755. }
  1756. // Return
  1757. if ($aCount > 0) {
  1758. $summerA *= $aCount;
  1759. $summerB *= $summerB;
  1760. $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
  1761. }
  1762. return $returnValue;
  1763. } // function VARPA()
  1764. /**
  1765. * RANK
  1766. *
  1767. * Returns the rank of a number in a list of numbers.
  1768. *
  1769. * @param number The number whose rank you want to find.
  1770. * @param array of number An array of, or a reference to, a list of numbers.
  1771. * @param mixed Order to sort the values in the value set
  1772. * @return float
  1773. */
  1774. public static function RANK($value,$valueSet,$order=0) {
  1775. $value = self::flattenSingleValue($value);
  1776. $valueSet = self::flattenArray($valueSet);
  1777. $order = self::flattenSingleValue($order);
  1778. foreach($valueSet as $key => $valueEntry) {
  1779. if (!is_numeric($valueEntry)) {
  1780. unset($valueSet[$key]);
  1781. }
  1782. }
  1783. if ($order == 0) {
  1784. rsort($valueSet,SORT_NUMERIC);
  1785. } else {
  1786. sort($valueSet,SORT_NUMERIC);
  1787. }
  1788. $pos = array_search($value,$valueSet);
  1789. if ($pos === False) {
  1790. return self::$_errorCodes['na'];
  1791. }
  1792. return ++$pos;
  1793. } // function RANK()
  1794. /**
  1795. * PERCENTRANK
  1796. *
  1797. * Returns the rank of a value in a data set as a percentage of the data set.
  1798. *
  1799. * @param array of number An array of, or a reference to, a list of numbers.
  1800. * @param number The number whose rank you want to find.
  1801. * @param number The number of significant digits for the returned percentage value.
  1802. * @return float
  1803. */
  1804. public static function PERCENTRANK($valueSet,$value,$significance=3) {
  1805. $valueSet = self::flattenArray($valueSet);
  1806. $value = self::flattenSingleValue($value);
  1807. $significance = self::flattenSingleValue($significance);
  1808. foreach($valueSet as $key => $valueEntry) {
  1809. if (!is_numeric($valueEntry)) {
  1810. unset($valueSet[$key]);
  1811. }
  1812. }
  1813. sort($valueSet,SORT_NUMERIC);
  1814. $valueCount = count($valueSet);
  1815. if ($valueCount == 0) {
  1816. return self::$_errorCodes['num'];
  1817. }
  1818. $valueAdjustor = $valueCount - 1;
  1819. if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
  1820. return self::$_errorCodes['na'];
  1821. }
  1822. $pos = array_search($value,$valueSet);
  1823. if ($pos === False) {
  1824. $pos = 0;
  1825. $testValue = $valueSet[0];
  1826. while ($testValue < $value) {
  1827. $testValue = $valueSet[++$pos];
  1828. }
  1829. --$pos;
  1830. $pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
  1831. }
  1832. return round($pos / $valueAdjustor,$significance);
  1833. } // function PERCENTRANK()
  1834. private static function _checkTrendArray(&$values) {
  1835. if (!is_array($values)) {
  1836. return False;
  1837. }
  1838. $values = self::flattenArray($values);
  1839. foreach($values as $key => $value) {
  1840. if ((is_bool($value)) || ((is_string($value)) && (trim($value) == ''))) {
  1841. unset($values[$key]);
  1842. } elseif (is_string($value)) {
  1843. if (is_numeric($value)) {
  1844. $values[$key] = (float) $value;
  1845. } else {
  1846. unset($values[$key]);
  1847. }
  1848. }
  1849. }
  1850. return True;
  1851. } // function _checkTrendArray()
  1852. /**
  1853. * INTERCEPT
  1854. *
  1855. * Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
  1856. *
  1857. * @param array of mixed Data Series Y
  1858. * @param array of mixed Data Series X
  1859. * @return float
  1860. */
  1861. public static function INTERCEPT($yValues,$xValues) {
  1862. if ((!self::_checkTrendArray($yValues)) || (!self::_checkTrendArray($xValues))) {
  1863. return self::$_errorCodes['value'];
  1864. }
  1865. $yValueCount = count($yValues);
  1866. $xValueCount = count($xValues);
  1867. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  1868. return self::$_errorCodes['na'];
  1869. } elseif ($yValueCount == 1) {
  1870. return self::$_errorCodes['divisionbyzero'];
  1871. }
  1872. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
  1873. return $bestFitLinear->getIntersect();
  1874. } // function INTERCEPT()
  1875. /**
  1876. * RSQ
  1877. *
  1878. * Returns the square of the Pearson product moment correlation coefficient through data points in known_y's and known_x's.
  1879. *
  1880. * @param array of mixed Data Series Y
  1881. * @param array of mixed Data Series X
  1882. * @return float
  1883. */
  1884. public static function RSQ($yValues,$xValues) {
  1885. if ((!self::_checkTrendArray($yValues)) || (!self::_checkTrendArray($xValues))) {
  1886. return self::$_errorCodes['value'];
  1887. }
  1888. $yValueCount = count($yValues);
  1889. $xValueCount = count($xValues);
  1890. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  1891. return self::$_errorCodes['na'];
  1892. } elseif ($yValueCount == 1) {
  1893. return self::$_errorCodes['divisionbyzero'];
  1894. }
  1895. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
  1896. return $bestFitLinear->getGoodnessOfFit();
  1897. } // function RSQ()
  1898. /**
  1899. * SLOPE
  1900. *
  1901. * Returns the slope of the linear regression line through data points in known_y's and known_x's.
  1902. *
  1903. * @param array of mixed Data Series Y
  1904. * @param array of mixed Data Series X
  1905. * @return float
  1906. */
  1907. public static function SLOPE($yValues,$xValues) {
  1908. if ((!self::_checkTrendArray($yValues)) || (!self::_checkTrendArray($xValues))) {
  1909. return self::$_errorCodes['value'];
  1910. }
  1911. $yValueCount = count($yValues);
  1912. $xValueCount = count($xValues);
  1913. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  1914. return self::$_errorCodes['na'];
  1915. } elseif ($yValueCount == 1) {
  1916. return self::$_errorCodes['divisionbyzero'];
  1917. }
  1918. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
  1919. return $bestFitLinear->getSlope();
  1920. } // function SLOPE()
  1921. /**
  1922. * STEYX
  1923. *
  1924. * Returns the standard error of the predicted y-value for each x in the regression.
  1925. *
  1926. * @param array of mixed Data Series Y
  1927. * @param array of mixed Data Series X
  1928. * @return float
  1929. */
  1930. public static function STEYX($yValues,$xValues) {
  1931. if ((!self::_checkTrendArray($yValues)) || (!self::_checkTrendArray($xValues))) {
  1932. return self::$_errorCodes['value'];
  1933. }
  1934. $yValueCount = count($yValues);
  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->getStdevOfResiduals();
  1943. } // function STEYX()
  1944. /**
  1945. * COVAR
  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 COVAR($yValues,$xValues) {
  1954. if ((!self::_checkTrendArray($yValues)) || (!self::_checkTrendArray($xValues))) {
  1955. return self::$_errorCodes['value'];
  1956. }
  1957. $yValueCount = count($yValues);
  1958. $xValueCount = count($xValues);
  1959. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  1960. return self::$_errorCodes['na'];
  1961. } elseif ($yValueCount == 1) {
  1962. return self::$_errorCodes['divisionbyzero'];
  1963. }
  1964. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
  1965. return $bestFitLinear->getCovariance();
  1966. } // function COVAR()
  1967. /**
  1968. * CORREL
  1969. *
  1970. * Returns covariance, the average of the products of deviations for each data point pair.
  1971. *
  1972. * @param array of mixed Data Series Y
  1973. * @param array of mixed Data Series X
  1974. * @return float
  1975. */
  1976. public static function CORREL($yValues,$xValues) {
  1977. if ((!self::_checkTrendArray($yValues)) || (!self::_checkTrendArray($xValues))) {
  1978. return self::$_errorCodes['value'];
  1979. }
  1980. $yValueCount = count($yValues);
  1981. $xValueCount = count($xValues);
  1982. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  1983. return self::$_errorCodes['na'];
  1984. } elseif ($yValueCount == 1) {
  1985. return self::$_errorCodes['divisionbyzero'];
  1986. }
  1987. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
  1988. return $bestFitLinear->getCorrelation();
  1989. } // function CORREL()
  1990. /**
  1991. * LINEST
  1992. *
  1993. * Calculates the statistics for a line by using the "least squares" method to calculate a straight line that best fits your data,
  1994. * and then returns an array that describes the line.
  1995. *
  1996. * @param array of mixed Data Series Y
  1997. * @param array of mixed Data Series X
  1998. * @param boolean A logical value specifying whether to force the intersect to equal 0.
  1999. * @param boolean A logical value specifying whether to return additional regression statistics.
  2000. * @return array
  2001. */
  2002. public static function LINEST($yValues,$xValues,$const=True,$stats=False) {
  2003. $const = (boolean) self::flattenSingleValue($const);
  2004. $stats = (boolean) self::flattenSingleValue($stats);
  2005. if ((!self::_checkTrendArray($yValues)) || (!self::_checkTrendArray($xValues))) {
  2006. return self::$_errorCodes['value'];
  2007. }
  2008. $yValueCount = count($yValues);
  2009. $xValueCount = count($xValues);
  2010. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  2011. return self::$_errorCodes['na'];
  2012. } elseif ($yValueCount == 1) {
  2013. return self::$_errorCodes['divisionbyzero'];
  2014. }
  2015. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues,$const);
  2016. if ($stats) {
  2017. return array( array( $bestFitLinear->getSlope(),
  2018. $bestFitLinear->getSlopeSE(),
  2019. $bestFitLinear->getGoodnessOfFit(),
  2020. $bestFitLinear->getF(),
  2021. $bestFitLinear->getSSRegression(),
  2022. ),
  2023. array( $bestFitLinear->getIntersect(),
  2024. $bestFitLinear->getIntersectSE(),
  2025. $bestFitLinear->getStdevOfResiduals(),
  2026. $bestFitLinear->getDFResiduals(),
  2027. $bestFitLinear->getSSResiduals()
  2028. )
  2029. );
  2030. } else {
  2031. return array( $bestFitLinear->getSlope(),
  2032. $bestFitLinear->getIntersect()
  2033. );
  2034. }
  2035. } // function LINEST()
  2036. /**
  2037. * LOGEST
  2038. *
  2039. * Calculates an exponential curve that best fits the X and Y data series,
  2040. * and then returns an array that describes the line.
  2041. *
  2042. * @param array of mixed Data Series Y
  2043. * @param array of mixed Data Series X
  2044. * @param boolean A logical value specifying whether to force the intersect to equal 0.
  2045. * @param boolean A logical value specifying whether to return additional regression statistics.
  2046. * @return array
  2047. */
  2048. public static function LOGEST($yValues,$xValues,$const=True,$stats=False) {
  2049. $const = (boolean) self::flattenSingleValue($const);
  2050. $stats = (boolean) self::flattenSingleValue($stats);
  2051. if ((!self::_checkTrendArray($yValues)) || (!self::_checkTrendArray($xValues))) {
  2052. return self::$_errorCodes['value'];
  2053. }
  2054. $yValueCount = count($yValues);
  2055. $xValueCount = count($xValues);
  2056. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  2057. return self::$_errorCodes['na'];
  2058. } elseif ($yValueCount == 1) {
  2059. return self::$_errorCodes['divisionbyzero'];
  2060. }
  2061. $bestFitExponential = trendClass::calculate(trendClass::TREND_EXPONENTIAL,$yValues,$xValues,$const);
  2062. if ($stats) {
  2063. return array( array( $bestFitExponential->getSlope(),
  2064. $bestFitExponential->getSlopeSE(),
  2065. $bestFitExponential->getGoodnessOfFit(),
  2066. $bestFitExponential->getF(),
  2067. $bestFitExponential->getSSRegression(),
  2068. ),
  2069. array( $bestFitExponential->getIntersect(),
  2070. $bestFitExponential->getIntersectSE(),
  2071. $bestFitExponential->getStdevOfResiduals(),
  2072. $bestFitExponential->getDFResiduals(),
  2073. $bestFitExponential->getSSResiduals()
  2074. )
  2075. );
  2076. } else {
  2077. return array( $bestFitExponential->getSlope(),
  2078. $bestFitExponential->getIntersect()
  2079. );
  2080. }
  2081. } // function LOGEST()
  2082. /**
  2083. * FORECAST
  2084. *
  2085. * Calculates, or predicts, a future value by using existing values. The predicted value is a y-value for a given x-value.
  2086. *
  2087. * @param float Value of X for which we want to find Y
  2088. * @param array of mixed Data Series Y
  2089. * @param array of mixed Data Series X
  2090. * @return float
  2091. */
  2092. public static function FORECAST($xValue,$yValues,$xValues) {
  2093. $xValue = self::flattenSingleValue($xValue);
  2094. if (!is_numeric($xValue)) {
  2095. return self::$_errorCodes['value'];
  2096. }
  2097. if ((!self::_checkTrendArray($yValues)) || (!self::_checkTrendArray($xValues))) {
  2098. return self::$_errorCodes['value'];
  2099. }
  2100. $yValueCount = count($yValues);
  2101. $xValueCount = count($xValues);
  2102. if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
  2103. return self::$_errorCodes['na'];
  2104. } elseif ($yValueCount == 1) {
  2105. return self::$_errorCodes['divisionbyzero'];
  2106. }
  2107. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
  2108. return $bestFitLinear->getValueOfYForX($xValue);
  2109. } // function FORECAST()
  2110. /**
  2111. * TREND
  2112. *
  2113. * Returns values along a linear trend
  2114. *
  2115. * @param array of mixed Data Series Y
  2116. * @param array of mixed Data Series X
  2117. * @param array of mixed Values of X for which we want to find Y
  2118. * @param boolean A logical value specifying whether to force the intersect to equal 0.
  2119. * @return array of float
  2120. */
  2121. public static function TREND($yValues,$xValues=array(),$newValues=array(),$const=True) {
  2122. $yValues = self::flattenArray($yValues);
  2123. $xValues = self::flattenArray($xValues);
  2124. $newValues = self::flattenArray($newValues);
  2125. $const = (boolean) self::flattenSingleValue($const);
  2126. $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues,$const);
  2127. if (count($newValues) == 0) {
  2128. $newValues = $bestFitLinear->getXValues();
  2129. }
  2130. $returnArray = array();
  2131. foreach($newValues as $xValue) {
  2132. $returnArray[0][] = $bestFitLinear->getValueOfYForX($xValue);
  2133. }
  2134. return $returnArray;
  2135. } // function TREND()
  2136. /**
  2137. * GROWTH
  2138. *
  2139. * Returns values along a predicted emponential trend
  2140. *
  2141. * @param array of mixed Data Series Y
  2142. * @param array of mixed Data Series X
  2143. * @param array of mixed Values of X for which we want to find Y
  2144. * @param boolean A logical value specifying whether to force the intersect to equal 0.
  2145. * @return array of float
  2146. */
  2147. public static function GROWTH($yValues,$xValues=array(),$newValues=array(),$const=True) {
  2148. $yValues = self::flattenArray($yValues);
  2149. $xValues = self::flattenArray($xValues);
  2150. $newValues = self::flattenArray($newValues);
  2151. $const = (boolean) self::flattenSingleValue($const);
  2152. $bestFitExponential = trendClass::calculate(trendClass::TREND_EXPONENTIAL,$yValues,$xValues,$const);
  2153. if (count($newValues) == 0) {
  2154. $newValues = $bestFitExponential->getXValues();
  2155. }
  2156. $returnArray = array();
  2157. foreach($newValues as $xValue) {
  2158. $returnArray[0][] = $bestFitExponential->getValueOfYForX($xValue);
  2159. }
  2160. return $returnArray;
  2161. } // function GROWTH()
  2162. private static function _romanCut($num, $n) {
  2163. return ($num - ($num % $n ) ) / $n;
  2164. } // function _romanCut()
  2165. public static function ROMAN($aValue, $style=0) {
  2166. $aValue = (integer) self::flattenSingleValue($aValue);
  2167. if ((!is_numeric($aValue)) || ($aValue < 0) || ($aValue >= 4000)) {
  2168. return self::$_errorCodes['value'];
  2169. }
  2170. if ($aValue == 0) {
  2171. return '';
  2172. }
  2173. $mill = Array('', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM');
  2174. $cent = Array('', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM');
  2175. $tens = Array('', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC');
  2176. $ones = Array('', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX');
  2177. $roman = '';
  2178. while ($aValue > 5999) {
  2179. $roman .= 'M';
  2180. $aValue -= 1000;
  2181. }
  2182. $m = self::_romanCut($aValue, 1000); $aValue %= 1000;
  2183. $c = self::_romanCut($aValue, 100); $aValue %= 100;
  2184. $t = self::_romanCut($aValue, 10); $aValue %= 10;
  2185. return $roman.$mill[$m].$cent[$c].$tens[$t].$ones[$aValue];
  2186. } // function ROMAN()
  2187. /**
  2188. * SUBTOTAL
  2189. *
  2190. * Returns a subtotal in a list or database.
  2191. *
  2192. * @param int the number 1 to 11 that specifies which function to
  2193. * use in calculating subtotals within a list.
  2194. * @param array of mixed Data Series
  2195. * @return float
  2196. */
  2197. public static function SUBTOTAL() {
  2198. $aArgs = self::flattenArray(func_get_args());
  2199. // Calculate
  2200. $subtotal = array_shift($aArgs);
  2201. if ((is_numeric($subtotal)) && (!is_string($subtotal))) {
  2202. switch($subtotal) {
  2203. case 1 :
  2204. return self::AVERAGE($aArgs);
  2205. break;
  2206. case 2 :
  2207. return self::COUNT($aArgs);
  2208. break;
  2209. case 3 :
  2210. return self::COUNTA($aArgs);
  2211. break;
  2212. case 4 :
  2213. return self::MAX($aArgs);
  2214. break;
  2215. case 5 :
  2216. return self::MIN($aArgs);
  2217. break;
  2218. case 6 :
  2219. return self::PRODUCT($aArgs);
  2220. break;
  2221. case 7 :
  2222. return self::STDEV($aArgs);
  2223. break;
  2224. case 8 :
  2225. return self::STDEVP($aArgs);
  2226. break;
  2227. case 9 :
  2228. return self::SUM($aArgs);
  2229. break;
  2230. case 10 :
  2231. return self::VARFunc($aArgs);
  2232. break;
  2233. case 11 :
  2234. return self::VARP($aArgs);
  2235. break;
  2236. }
  2237. }
  2238. return self::$_errorCodes['value'];
  2239. } // function SUBTOTAL()
  2240. /**
  2241. * SQRTPI
  2242. *
  2243. * Returns the square root of (number * pi).
  2244. *
  2245. * @param float $number Number
  2246. * @return float Square Root of Number * Pi
  2247. */
  2248. public static function SQRTPI($number) {
  2249. $number = self::flattenSingleValue($number);
  2250. if (is_numeric($number)) {
  2251. if ($number < 0) {
  2252. return self::$_errorCodes['num'];
  2253. }
  2254. return sqrt($number * pi()) ;
  2255. }
  2256. return self::$_errorCodes['value'];
  2257. } // function SQRTPI()
  2258. /**
  2259. * FACT
  2260. *
  2261. * Returns the factorial of a number.
  2262. *
  2263. * @param float $factVal Factorial Value
  2264. * @return int Factorial
  2265. */
  2266. public static function FACT($factVal) {
  2267. $factVal = self::flattenSingleValue($factVal);
  2268. if (is_numeric($factVal)) {
  2269. if ($factVal < 0) {
  2270. return self::$_errorCodes['num'];
  2271. }
  2272. $factLoop = floor($factVal);
  2273. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  2274. if ($factVal > $factLoop) {
  2275. return self::$_errorCodes['num'];
  2276. }
  2277. }
  2278. $factorial = 1;
  2279. while ($factLoop > 1) {
  2280. $factorial *= $factLoop--;
  2281. }
  2282. return $factorial ;
  2283. }
  2284. return self::$_errorCodes['value'];
  2285. } // function FACT()
  2286. /**
  2287. * FACTDOUBLE
  2288. *
  2289. * Returns the double factorial of a number.
  2290. *
  2291. * @param float $factVal Factorial Value
  2292. * @return int Double Factorial
  2293. */
  2294. public static function FACTDOUBLE($factVal) {
  2295. $factLoop = floor(self::flattenSingleValue($factVal));
  2296. if (is_numeric($factLoop)) {
  2297. if ($factVal < 0) {
  2298. return self::$_errorCodes['num'];
  2299. }
  2300. $factorial = 1;
  2301. while ($factLoop > 1) {
  2302. $factorial *= $factLoop--;
  2303. --$factLoop;
  2304. }
  2305. return $factorial ;
  2306. }
  2307. return self::$_errorCodes['value'];
  2308. } // function FACTDOUBLE()
  2309. /**
  2310. * MULTINOMIAL
  2311. *
  2312. * Returns the ratio of the factorial of a sum of values to the product of factorials.
  2313. *
  2314. * @param array of mixed Data Series
  2315. * @return float
  2316. */
  2317. public static function MULTINOMIAL() {
  2318. // Loop through arguments
  2319. $aArgs = self::flattenArray(func_get_args());
  2320. $summer = 0;
  2321. $divisor = 1;
  2322. foreach ($aArgs as $arg) {
  2323. // Is it a numeric value?
  2324. if (is_numeric($arg)) {
  2325. if ($arg < 1) {
  2326. return self::$_errorCodes['num'];
  2327. }
  2328. $summer += floor($arg);
  2329. $divisor *= self::FACT($arg);
  2330. } else {
  2331. return self::$_errorCodes['value'];
  2332. }
  2333. }
  2334. // Return
  2335. if ($summer > 0) {
  2336. $summer = self::FACT($summer);
  2337. return $summer / $divisor;
  2338. }
  2339. return 0;
  2340. } // function MULTINOMIAL()
  2341. /**
  2342. * CEILING
  2343. *
  2344. * Returns number rounded up, away from zero, to the nearest multiple of significance.
  2345. *
  2346. * @param float $number Number to round
  2347. * @param float $significance Significance
  2348. * @return float Rounded Number
  2349. */
  2350. public static function CEILING($number,$significance=null) {
  2351. $number = self::flattenSingleValue($number);
  2352. $significance = self::flattenSingleValue($significance);
  2353. if ((is_null($significance)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
  2354. $significance = $number/abs($number);
  2355. }
  2356. if ((is_numeric($number)) && (is_numeric($significance))) {
  2357. if (self::SIGN($number) == self::SIGN($significance)) {
  2358. if ($significance == 0.0) {
  2359. return 0;
  2360. }
  2361. return ceil($number / $significance) * $significance;
  2362. } else {
  2363. return self::$_errorCodes['num'];
  2364. }
  2365. }
  2366. return self::$_errorCodes['value'];
  2367. } // function CEILING()
  2368. /**
  2369. * EVEN
  2370. *
  2371. * Returns number rounded up to the nearest even integer.
  2372. *
  2373. * @param float $number Number to round
  2374. * @return int Rounded Number
  2375. */
  2376. public static function EVEN($number) {
  2377. $number = self::flattenSingleValue($number);
  2378. if (is_numeric($number)) {
  2379. $significance = 2 * self::SIGN($number);
  2380. return self::CEILING($number,$significance);
  2381. }
  2382. return self::$_errorCodes['value'];
  2383. } // function EVEN()
  2384. /**
  2385. * ODD
  2386. *
  2387. * Returns number rounded up to the nearest odd integer.
  2388. *
  2389. * @param float $number Number to round
  2390. * @return int Rounded Number
  2391. */
  2392. public static function ODD($number) {
  2393. $number = self::flattenSingleValue($number);
  2394. if (is_numeric($number)) {
  2395. $significance = self::SIGN($number);
  2396. if ($significance == 0) {
  2397. return 1;
  2398. }
  2399. $result = self::CEILING($number,$significance);
  2400. if (self::IS_EVEN($result)) {
  2401. $result += $significance;
  2402. }
  2403. return $result;
  2404. }
  2405. return self::$_errorCodes['value'];
  2406. } // function ODD()
  2407. /**
  2408. * INTVALUE
  2409. *
  2410. * Casts a floating point value to an integer
  2411. *
  2412. * @param float $number Number to cast to an integer
  2413. * @return integer Integer value
  2414. */
  2415. public static function INTVALUE($number) {
  2416. $number = self::flattenSingleValue($number);
  2417. if (is_numeric($number)) {
  2418. return (int) floor($number);
  2419. }
  2420. return self::$_errorCodes['value'];
  2421. } // function INTVALUE()
  2422. /**
  2423. * ROUNDUP
  2424. *
  2425. * Rounds a number up to a specified number of decimal places
  2426. *
  2427. * @param float $number Number to round
  2428. * @param int $digits Number of digits to which you want to round $number
  2429. * @return float Rounded Number
  2430. */
  2431. public static function ROUNDUP($number,$digits) {
  2432. $number = self::flattenSingleValue($number);
  2433. $digits = self::flattenSingleValue($digits);
  2434. if ((is_numeric($number)) && (is_numeric($digits))) {
  2435. $significance = pow(10,$digits);
  2436. if ($number < 0.0) {
  2437. return floor($number * $significance) / $significance;
  2438. } else {
  2439. return ceil($number * $significance) / $significance;
  2440. }
  2441. }
  2442. return self::$_errorCodes['value'];
  2443. } // function ROUNDUP()
  2444. /**
  2445. * ROUNDDOWN
  2446. *
  2447. * Rounds a number down to a specified number of decimal places
  2448. *
  2449. * @param float $number Number to round
  2450. * @param int $digits Number of digits to which you want to round $number
  2451. * @return float Rounded Number
  2452. */
  2453. public static function ROUNDDOWN($number,$digits) {
  2454. $number = self::flattenSingleValue($number);
  2455. $digits = self::flattenSingleValue($digits);
  2456. if ((is_numeric($number)) && (is_numeric($digits))) {
  2457. $significance = pow(10,$digits);
  2458. if ($number < 0.0) {
  2459. return ceil($number * $significance) / $significance;
  2460. } else {
  2461. return floor($number * $significance) / $significance;
  2462. }
  2463. }
  2464. return self::$_errorCodes['value'];
  2465. } // function ROUNDDOWN()
  2466. /**
  2467. * MROUND
  2468. *
  2469. * Rounds a number to the nearest multiple of a specified value
  2470. *
  2471. * @param float $number Number to round
  2472. * @param int $multiple Multiple to which you want to round $number
  2473. * @return float Rounded Number
  2474. */
  2475. public static function MROUND($number,$multiple) {
  2476. $number = self::flattenSingleValue($number);
  2477. $multiple = self::flattenSingleValue($multiple);
  2478. if ((is_numeric($number)) && (is_numeric($multiple))) {
  2479. if ($multiple == 0) {
  2480. return 0;
  2481. }
  2482. if ((self::SIGN($number)) == (self::SIGN($multiple))) {
  2483. $multiplier = 1 / $multiple;
  2484. return round($number * $multiplier) / $multiplier;
  2485. }
  2486. return self::$_errorCodes['num'];
  2487. }
  2488. return self::$_errorCodes['value'];
  2489. } // function MROUND()
  2490. /**
  2491. * SIGN
  2492. *
  2493. * Determines the sign of a number. Returns 1 if the number is positive, zero (0)
  2494. * if the number is 0, and -1 if the number is negative.
  2495. *
  2496. * @param float $number Number to round
  2497. * @return int sign value
  2498. */
  2499. public static function SIGN($number) {
  2500. $number = self::flattenSingleValue($number);
  2501. if (is_numeric($number)) {
  2502. if ($number == 0.0) {
  2503. return 0;
  2504. }
  2505. return $number / abs($number);
  2506. }
  2507. return self::$_errorCodes['value'];
  2508. } // function SIGN()
  2509. /**
  2510. * FLOOR
  2511. *
  2512. * Rounds number down, toward zero, to the nearest multiple of significance.
  2513. *
  2514. * @param float $number Number to round
  2515. * @param float $significance Significance
  2516. * @return float Rounded Number
  2517. */
  2518. public static function FLOOR($number,$significance=null) {
  2519. $number = self::flattenSingleValue($number);
  2520. $significance = self::flattenSingleValue($significance);
  2521. if ((is_null($significance)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
  2522. $significance = $number/abs($number);
  2523. }
  2524. if ((is_numeric($number)) && (is_numeric($significance))) {
  2525. if ((float) $significance == 0.0) {
  2526. return self::$_errorCodes['divisionbyzero'];
  2527. }
  2528. if (self::SIGN($number) == self::SIGN($significance)) {
  2529. return floor($number / $significance) * $significance;
  2530. } else {
  2531. return self::$_errorCodes['num'];
  2532. }
  2533. }
  2534. return self::$_errorCodes['value'];
  2535. } // function FLOOR()
  2536. /**
  2537. * PERMUT
  2538. *
  2539. * Returns the number of permutations for a given number of objects that can be
  2540. * selected from number objects. A permutation is any set or subset of objects or
  2541. * events where internal order is significant. Permutations are different from
  2542. * combinations, for which the internal order is not significant. Use this function
  2543. * for lottery-style probability calculations.
  2544. *
  2545. * @param int $numObjs Number of different objects
  2546. * @param int $numInSet Number of objects in each permutation
  2547. * @return int Number of permutations
  2548. */
  2549. public static function PERMUT($numObjs,$numInSet) {
  2550. $numObjs = self::flattenSingleValue($numObjs);
  2551. $numInSet = self::flattenSingleValue($numInSet);
  2552. if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
  2553. $numInSet = floor($numInSet);
  2554. if ($numObjs < $numInSet) {
  2555. return self::$_errorCodes['num'];
  2556. }
  2557. return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet));
  2558. }
  2559. return self::$_errorCodes['value'];
  2560. } // function PERMUT()
  2561. /**
  2562. * COMBIN
  2563. *
  2564. * Returns the number of combinations for a given number of items. Use COMBIN to
  2565. * determine the total possible number of groups for a given number of items.
  2566. *
  2567. * @param int $numObjs Number of different objects
  2568. * @param int $numInSet Number of objects in each combination
  2569. * @return int Number of combinations
  2570. */
  2571. public static function COMBIN($numObjs,$numInSet) {
  2572. $numObjs = self::flattenSingleValue($numObjs);
  2573. $numInSet = self::flattenSingleValue($numInSet);
  2574. if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
  2575. if ($numObjs < $numInSet) {
  2576. return self::$_errorCodes['num'];
  2577. } elseif ($numInSet < 0) {
  2578. return self::$_errorCodes['num'];
  2579. }
  2580. return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet)) / self::FACT($numInSet);
  2581. }
  2582. return self::$_errorCodes['value'];
  2583. } // function COMBIN()
  2584. /**
  2585. * SERIESSUM
  2586. *
  2587. * Returns the sum of a power series
  2588. *
  2589. * @param float $x Input value to the power series
  2590. * @param float $n Initial power to which you want to raise $x
  2591. * @param float $m Step by which to increase $n for each term in the series
  2592. * @param array of mixed Data Series
  2593. * @return float
  2594. */
  2595. public static function SERIESSUM() {
  2596. // Return value
  2597. $returnValue = 0;
  2598. // Loop through arguments
  2599. $aArgs = self::flattenArray(func_get_args());
  2600. $x = array_shift($aArgs);
  2601. $n = array_shift($aArgs);
  2602. $m = array_shift($aArgs);
  2603. if ((is_numeric($x)) && (is_numeric($n)) && (is_numeric($m))) {
  2604. // Calculate
  2605. $i = 0;
  2606. foreach($aArgs as $arg) {
  2607. // Is it a numeric value?
  2608. if ((is_numeric($arg)) && (!is_string($arg))) {
  2609. $returnValue += $arg * pow($x,$n + ($m * $i++));
  2610. } else {
  2611. return self::$_errorCodes['value'];
  2612. }
  2613. }
  2614. // Return
  2615. return $returnValue;
  2616. }
  2617. return self::$_errorCodes['value'];
  2618. } // function SERIESSUM()
  2619. /**
  2620. * STANDARDIZE
  2621. *
  2622. * Returns a normalized value from a distribution characterized by mean and standard_dev.
  2623. *
  2624. * @param float $value Value to normalize
  2625. * @param float $mean Mean Value
  2626. * @param float $stdDev Standard Deviation
  2627. * @return float Standardized value
  2628. */
  2629. public static function STANDARDIZE($value,$mean,$stdDev) {
  2630. $value = self::flattenSingleValue($value);
  2631. $mean = self::flattenSingleValue($mean);
  2632. $stdDev = self::flattenSingleValue($stdDev);
  2633. if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  2634. if ($stdDev <= 0) {
  2635. return self::$_errorCodes['num'];
  2636. }
  2637. return ($value - $mean) / $stdDev ;
  2638. }
  2639. return self::$_errorCodes['value'];
  2640. } // function STANDARDIZE()
  2641. //
  2642. // Private method to return an array of the factors of the input value
  2643. //
  2644. private static function _factors($value) {
  2645. $startVal = floor(sqrt($value));
  2646. $factorArray = array();
  2647. for ($i = $startVal; $i > 1; --$i) {
  2648. if (($value % $i) == 0) {
  2649. $factorArray = array_merge($factorArray,self::_factors($value / $i));
  2650. $factorArray = array_merge($factorArray,self::_factors($i));
  2651. if ($i <= sqrt($value)) {
  2652. break;
  2653. }
  2654. }
  2655. }
  2656. if (count($factorArray) > 0) {
  2657. rsort($factorArray);
  2658. return $factorArray;
  2659. } else {
  2660. return array((integer) $value);
  2661. }
  2662. } // function _factors()
  2663. /**
  2664. * LCM
  2665. *
  2666. * Returns the lowest common multiplier of a series of numbers
  2667. *
  2668. * @param $array Values to calculate the Lowest Common Multiplier
  2669. * @return int Lowest Common Multiplier
  2670. */
  2671. public static function LCM() {
  2672. $aArgs = self::flattenArray(func_get_args());
  2673. $returnValue = 1;
  2674. $allPoweredFactors = array();
  2675. foreach($aArgs as $value) {
  2676. if (!is_numeric($value)) {
  2677. return self::$_errorCodes['value'];
  2678. }
  2679. if ($value == 0) {
  2680. return 0;
  2681. } elseif ($value < 0) {
  2682. return self::$_errorCodes['num'];
  2683. }
  2684. $myFactors = self::_factors(floor($value));
  2685. $myCountedFactors = array_count_values($myFactors);
  2686. $myPoweredFactors = array();
  2687. foreach($myCountedFactors as $myCountedFactor => $myCountedPower) {
  2688. $myPoweredFactors[$myCountedFactor] = pow($myCountedFactor,$myCountedPower);
  2689. }
  2690. foreach($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
  2691. if (array_key_exists($myPoweredValue,$allPoweredFactors)) {
  2692. if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
  2693. $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
  2694. }
  2695. } else {
  2696. $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
  2697. }
  2698. }
  2699. }
  2700. foreach($allPoweredFactors as $allPoweredFactor) {
  2701. $returnValue *= (integer) $allPoweredFactor;
  2702. }
  2703. return $returnValue;
  2704. } // function LCM()
  2705. /**
  2706. * GCD
  2707. *
  2708. * Returns the greatest common divisor of a series of numbers
  2709. *
  2710. * @param $array Values to calculate the Greatest Common Divisor
  2711. * @return int Greatest Common Divisor
  2712. */
  2713. public static function GCD() {
  2714. $aArgs = self::flattenArray(func_get_args());
  2715. $returnValue = 1;
  2716. $allPoweredFactors = array();
  2717. foreach($aArgs as $value) {
  2718. if ($value == 0) {
  2719. break;
  2720. }
  2721. $myFactors = self::_factors($value);
  2722. $myCountedFactors = array_count_values($myFactors);
  2723. $allValuesFactors[] = $myCountedFactors;
  2724. }
  2725. $allValuesCount = count($allValuesFactors);
  2726. $mergedArray = $allValuesFactors[0];
  2727. for ($i=1;$i < $allValuesCount; ++$i) {
  2728. $mergedArray = array_intersect_key($mergedArray,$allValuesFactors[$i]);
  2729. }
  2730. $mergedArrayValues = count($mergedArray);
  2731. if ($mergedArrayValues == 0) {
  2732. return $returnValue;
  2733. } elseif ($mergedArrayValues > 1) {
  2734. foreach($mergedArray as $mergedKey => $mergedValue) {
  2735. foreach($allValuesFactors as $highestPowerTest) {
  2736. foreach($highestPowerTest as $testKey => $testValue) {
  2737. if (($testKey == $mergedKey) && ($testValue < $mergedValue)) {
  2738. $mergedArray[$mergedKey] = $testValue;
  2739. $mergedValue = $testValue;
  2740. }
  2741. }
  2742. }
  2743. }
  2744. $returnValue = 1;
  2745. foreach($mergedArray as $key => $value) {
  2746. $returnValue *= pow($key,$value);
  2747. }
  2748. return $returnValue;
  2749. } else {
  2750. $keys = array_keys($mergedArray);
  2751. $key = $keys[0];
  2752. $value = $mergedArray[$key];
  2753. foreach($allValuesFactors as $testValue) {
  2754. foreach($testValue as $mergedKey => $mergedValue) {
  2755. if (($mergedKey == $key) && ($mergedValue < $value)) {
  2756. $value = $mergedValue;
  2757. }
  2758. }
  2759. }
  2760. return pow($key,$value);
  2761. }
  2762. } // function GCD()
  2763. /**
  2764. * BINOMDIST
  2765. *
  2766. * Returns the individual term binomial distribution probability. Use BINOMDIST in problems with
  2767. * a fixed number of tests or trials, when the outcomes of any trial are only success or failure,
  2768. * when trials are independent, and when the probability of success is constant throughout the
  2769. * experiment. For example, BINOMDIST can calculate the probability that two of the next three
  2770. * babies born are male.
  2771. *
  2772. * @param float $value Number of successes in trials
  2773. * @param float $trials Number of trials
  2774. * @param float $probability Probability of success on each trial
  2775. * @param boolean $cumulative
  2776. * @return float
  2777. *
  2778. * @todo Cumulative distribution function
  2779. *
  2780. */
  2781. public static function BINOMDIST($value, $trials, $probability, $cumulative) {
  2782. $value = floor(self::flattenSingleValue($value));
  2783. $trials = floor(self::flattenSingleValue($trials));
  2784. $probability = self::flattenSingleValue($probability);
  2785. if ((is_numeric($value)) && (is_numeric($trials)) && (is_numeric($probability))) {
  2786. if (($value < 0) || ($value > $trials)) {
  2787. return self::$_errorCodes['num'];
  2788. }
  2789. if (($probability < 0) || ($probability > 1)) {
  2790. return self::$_errorCodes['num'];
  2791. }
  2792. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  2793. if ($cumulative) {
  2794. $summer = 0;
  2795. for ($i = 0; $i <= $value; ++$i) {
  2796. $summer += self::COMBIN($trials,$i) * pow($probability,$i) * pow(1 - $probability,$trials - $i);
  2797. }
  2798. return $summer;
  2799. } else {
  2800. return self::COMBIN($trials,$value) * pow($probability,$value) * pow(1 - $probability,$trials - $value) ;
  2801. }
  2802. }
  2803. }
  2804. return self::$_errorCodes['value'];
  2805. } // function BINOMDIST()
  2806. /**
  2807. * NEGBINOMDIST
  2808. *
  2809. * Returns the negative binomial distribution. NEGBINOMDIST returns the probability that
  2810. * there will be number_f failures before the number_s-th success, when the constant
  2811. * probability of a success is probability_s. This function is similar to the binomial
  2812. * distribution, except that the number of successes is fixed, and the number of trials is
  2813. * variable. Like the binomial, trials are assumed to be independent.
  2814. *
  2815. * @param float $failures Number of Failures
  2816. * @param float $successes Threshold number of Successes
  2817. * @param float $probability Probability of success on each trial
  2818. * @return float
  2819. *
  2820. */
  2821. public static function NEGBINOMDIST($failures, $successes, $probability) {
  2822. $failures = floor(self::flattenSingleValue($failures));
  2823. $successes = floor(self::flattenSingleValue($successes));
  2824. $probability = self::flattenSingleValue($probability);
  2825. if ((is_numeric($failures)) && (is_numeric($successes)) && (is_numeric($probability))) {
  2826. if (($failures < 0) || ($successes < 1)) {
  2827. return self::$_errorCodes['num'];
  2828. }
  2829. if (($probability < 0) || ($probability > 1)) {
  2830. return self::$_errorCodes['num'];
  2831. }
  2832. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  2833. if (($failures + $successes - 1) <= 0) {
  2834. return self::$_errorCodes['num'];
  2835. }
  2836. }
  2837. return (self::COMBIN($failures + $successes - 1,$successes - 1)) * (pow($probability,$successes)) * (pow(1 - $probability,$failures)) ;
  2838. }
  2839. return self::$_errorCodes['value'];
  2840. } // function NEGBINOMDIST()
  2841. /**
  2842. * CRITBINOM
  2843. *
  2844. * Returns the smallest value for which the cumulative binomial distribution is greater
  2845. * than or equal to a criterion value
  2846. *
  2847. * See http://support.microsoft.com/kb/828117/ for details of the algorithm used
  2848. *
  2849. * @param float $trials number of Bernoulli trials
  2850. * @param float $probability probability of a success on each trial
  2851. * @param float $alpha criterion value
  2852. * @return int
  2853. *
  2854. * @todo Warning. This implementation differs from the algorithm detailed on the MS
  2855. * web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess
  2856. * This eliminates a potential endless loop error, but may have an adverse affect on the
  2857. * accuracy of the function (although all my tests have so far returned correct results).
  2858. *
  2859. */
  2860. public static function CRITBINOM($trials, $probability, $alpha) {
  2861. $trials = floor(self::flattenSingleValue($trials));
  2862. $probability = self::flattenSingleValue($probability);
  2863. $alpha = self::flattenSingleValue($alpha);
  2864. if ((is_numeric($trials)) && (is_numeric($probability)) && (is_numeric($alpha))) {
  2865. if ($trials < 0) {
  2866. return self::$_errorCodes['num'];
  2867. }
  2868. if (($probability < 0) || ($probability > 1)) {
  2869. return self::$_errorCodes['num'];
  2870. }
  2871. if (($alpha < 0) || ($alpha > 1)) {
  2872. return self::$_errorCodes['num'];
  2873. }
  2874. if ($alpha <= 0.5) {
  2875. $t = sqrt(log(1 / ($alpha * $alpha)));
  2876. $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));
  2877. } else {
  2878. $t = sqrt(log(1 / pow(1 - $alpha,2)));
  2879. $trialsApprox = $t - (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t);
  2880. }
  2881. $Guess = floor($trials * $probability + $trialsApprox * sqrt($trials * $probability * (1 - $probability)));
  2882. if ($Guess < 0) {
  2883. $Guess = 0;
  2884. } elseif ($Guess > $trials) {
  2885. $Guess = $trials;
  2886. }
  2887. $TotalUnscaledProbability = $UnscaledPGuess = $UnscaledCumPGuess = 0.0;
  2888. $EssentiallyZero = 10e-12;
  2889. $m = floor($trials * $probability);
  2890. ++$TotalUnscaledProbability;
  2891. if ($m == $Guess) { ++$UnscaledPGuess; }
  2892. if ($m <= $Guess) { ++$UnscaledCumPGuess; }
  2893. $PreviousValue = 1;
  2894. $Done = False;
  2895. $k = $m + 1;
  2896. while ((!$Done) && ($k <= $trials)) {
  2897. $CurrentValue = $PreviousValue * ($trials - $k + 1) * $probability / ($k * (1 - $probability));
  2898. $TotalUnscaledProbability += $CurrentValue;
  2899. if ($k == $Guess) { $UnscaledPGuess += $CurrentValue; }
  2900. if ($k <= $Guess) { $UnscaledCumPGuess += $CurrentValue; }
  2901. if ($CurrentValue <= $EssentiallyZero) { $Done = True; }
  2902. $PreviousValue = $CurrentValue;
  2903. ++$k;
  2904. }
  2905. $PreviousValue = 1;
  2906. $Done = False;
  2907. $k = $m - 1;
  2908. while ((!$Done) && ($k >= 0)) {
  2909. $CurrentValue = $PreviousValue * $k + 1 * (1 - $probability) / (($trials - $k) * $probability);
  2910. $TotalUnscaledProbability += $CurrentValue;
  2911. if ($k == $Guess) { $UnscaledPGuess += $CurrentValue; }
  2912. if ($k <= $Guess) { $UnscaledCumPGuess += $CurrentValue; }
  2913. if ($CurrentValue <= $EssentiallyZero) { $Done = True; }
  2914. $PreviousValue = $CurrentValue;
  2915. --$k;
  2916. }
  2917. $PGuess = $UnscaledPGuess / $TotalUnscaledProbability;
  2918. $CumPGuess = $UnscaledCumPGuess / $TotalUnscaledProbability;
  2919. // $CumPGuessMinus1 = $CumPGuess - $PGuess;
  2920. $CumPGuessMinus1 = $CumPGuess - 1;
  2921. while (True) {
  2922. if (($CumPGuessMinus1 < $alpha) && ($CumPGuess >= $alpha)) {
  2923. return $Guess;
  2924. } elseif (($CumPGuessMinus1 < $alpha) && ($CumPGuess < $alpha)) {
  2925. $PGuessPlus1 = $PGuess * ($trials - $Guess) * $probability / $Guess / (1 - $probability);
  2926. $CumPGuessMinus1 = $CumPGuess;
  2927. $CumPGuess = $CumPGuess + $PGuessPlus1;
  2928. $PGuess = $PGuessPlus1;
  2929. ++$Guess;
  2930. } elseif (($CumPGuessMinus1 >= $alpha) && ($CumPGuess >= $alpha)) {
  2931. $PGuessMinus1 = $PGuess * $Guess * (1 - $probability) / ($trials - $Guess + 1) / $probability;
  2932. $CumPGuess = $CumPGuessMinus1;
  2933. $CumPGuessMinus1 = $CumPGuessMinus1 - $PGuess;
  2934. $PGuess = $PGuessMinus1;
  2935. --$Guess;
  2936. }
  2937. }
  2938. }
  2939. return self::$_errorCodes['value'];
  2940. } // function CRITBINOM()
  2941. /**
  2942. * CHIDIST
  2943. *
  2944. * Returns the one-tailed probability of the chi-squared distribution.
  2945. *
  2946. * @param float $value Value for the function
  2947. * @param float $degrees degrees of freedom
  2948. * @return float
  2949. */
  2950. public static function CHIDIST($value, $degrees) {
  2951. $value = self::flattenSingleValue($value);
  2952. $degrees = floor(self::flattenSingleValue($degrees));
  2953. if ((is_numeric($value)) && (is_numeric($degrees))) {
  2954. if ($degrees < 1) {
  2955. return self::$_errorCodes['num'];
  2956. }
  2957. if ($value < 0) {
  2958. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  2959. return 1;
  2960. }
  2961. return self::$_errorCodes['num'];
  2962. }
  2963. return 1 - (self::_incompleteGamma($degrees/2,$value/2) / self::_gamma($degrees/2));
  2964. }
  2965. return self::$_errorCodes['value'];
  2966. } // function CHIDIST()
  2967. /**
  2968. * CHIINV
  2969. *
  2970. * Returns the one-tailed probability of the chi-squared distribution.
  2971. *
  2972. * @param float $probability Probability for the function
  2973. * @param float $degrees degrees of freedom
  2974. * @return float
  2975. */
  2976. public static function CHIINV($probability, $degrees) {
  2977. $probability = self::flattenSingleValue($probability);
  2978. $degrees = floor(self::flattenSingleValue($degrees));
  2979. if ((is_numeric($probability)) && (is_numeric($degrees))) {
  2980. $xLo = 100;
  2981. $xHi = 0;
  2982. $maxIteration = 100;
  2983. $x = $xNew = 1;
  2984. $dx = 1;
  2985. $i = 0;
  2986. while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
  2987. // Apply Newton-Raphson step
  2988. $result = self::CHIDIST($x, $degrees);
  2989. $error = $result - $probability;
  2990. if ($error == 0.0) {
  2991. $dx = 0;
  2992. } elseif ($error < 0.0) {
  2993. $xLo = $x;
  2994. } else {
  2995. $xHi = $x;
  2996. }
  2997. // Avoid division by zero
  2998. if ($result != 0.0) {
  2999. $dx = $error / $result;
  3000. $xNew = $x - $dx;
  3001. }
  3002. // If the NR fails to converge (which for example may be the
  3003. // case if the initial guess is too rough) we apply a bisection
  3004. // step to determine a more narrow interval around the root.
  3005. if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
  3006. $xNew = ($xLo + $xHi) / 2;
  3007. $dx = $xNew - $x;
  3008. }
  3009. $x = $xNew;
  3010. }
  3011. if ($i == MAX_ITERATIONS) {
  3012. return self::$_errorCodes['na'];
  3013. }
  3014. return round($x,12);
  3015. }
  3016. return self::$_errorCodes['value'];
  3017. } // function CHIINV()
  3018. /**
  3019. * EXPONDIST
  3020. *
  3021. * Returns the exponential distribution. Use EXPONDIST to model the time between events,
  3022. * such as how long an automated bank teller takes to deliver cash. For example, you can
  3023. * use EXPONDIST to determine the probability that the process takes at most 1 minute.
  3024. *
  3025. * @param float $value Value of the function
  3026. * @param float $lambda The parameter value
  3027. * @param boolean $cumulative
  3028. * @return float
  3029. */
  3030. public static function EXPONDIST($value, $lambda, $cumulative) {
  3031. $value = self::flattenSingleValue($value);
  3032. $lambda = self::flattenSingleValue($lambda);
  3033. $cumulative = self::flattenSingleValue($cumulative);
  3034. if ((is_numeric($value)) && (is_numeric($lambda))) {
  3035. if (($value < 0) || ($lambda < 0)) {
  3036. return self::$_errorCodes['num'];
  3037. }
  3038. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  3039. if ($cumulative) {
  3040. return 1 - exp(0-$value*$lambda);
  3041. } else {
  3042. return $lambda * exp(0-$value*$lambda);
  3043. }
  3044. }
  3045. }
  3046. return self::$_errorCodes['value'];
  3047. } // function EXPONDIST()
  3048. /**
  3049. * FISHER
  3050. *
  3051. * Returns the Fisher transformation at x. This transformation produces a function that
  3052. * is normally distributed rather than skewed. Use this function to perform hypothesis
  3053. * testing on the correlation coefficient.
  3054. *
  3055. * @param float $value
  3056. * @return float
  3057. */
  3058. public static function FISHER($value) {
  3059. $value = self::flattenSingleValue($value);
  3060. if (is_numeric($value)) {
  3061. if (($value <= -1) || ($value >= 1)) {
  3062. return self::$_errorCodes['num'];
  3063. }
  3064. return 0.5 * log((1+$value)/(1-$value));
  3065. }
  3066. return self::$_errorCodes['value'];
  3067. } // function FISHER()
  3068. /**
  3069. * FISHERINV
  3070. *
  3071. * Returns the inverse of the Fisher transformation. Use this transformation when
  3072. * analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
  3073. * FISHERINV(y) = x.
  3074. *
  3075. * @param float $value
  3076. * @return float
  3077. */
  3078. public static function FISHERINV($value) {
  3079. $value = self::flattenSingleValue($value);
  3080. if (is_numeric($value)) {
  3081. return (exp(2 * $value) - 1) / (exp(2 * $value) + 1);
  3082. }
  3083. return self::$_errorCodes['value'];
  3084. } // function FISHERINV()
  3085. // Function cache for _logBeta function
  3086. private static $_logBetaCache_p = 0.0;
  3087. private static $_logBetaCache_q = 0.0;
  3088. private static $_logBetaCache_result = 0.0;
  3089. /**
  3090. * The natural logarithm of the beta function.
  3091. * @param p require p>0
  3092. * @param q require q>0
  3093. * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
  3094. * @author Jaco van Kooten
  3095. */
  3096. private static function _logBeta($p, $q) {
  3097. if ($p != self::$_logBetaCache_p || $q != self::$_logBetaCache_q) {
  3098. self::$_logBetaCache_p = $p;
  3099. self::$_logBetaCache_q = $q;
  3100. if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
  3101. self::$_logBetaCache_result = 0.0;
  3102. } else {
  3103. self::$_logBetaCache_result = self::_logGamma($p) + self::_logGamma($q) - self::_logGamma($p + $q);
  3104. }
  3105. }
  3106. return self::$_logBetaCache_result;
  3107. } // function _logBeta()
  3108. /**
  3109. * Evaluates of continued fraction part of incomplete beta function.
  3110. * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
  3111. * @author Jaco van Kooten
  3112. */
  3113. private static function _betaFraction($x, $p, $q) {
  3114. $c = 1.0;
  3115. $sum_pq = $p + $q;
  3116. $p_plus = $p + 1.0;
  3117. $p_minus = $p - 1.0;
  3118. $h = 1.0 - $sum_pq * $x / $p_plus;
  3119. if (abs($h) < XMININ) {
  3120. $h = XMININ;
  3121. }
  3122. $h = 1.0 / $h;
  3123. $frac = $h;
  3124. $m = 1;
  3125. $delta = 0.0;
  3126. while ($m <= MAX_ITERATIONS && abs($delta-1.0) > PRECISION ) {
  3127. $m2 = 2 * $m;
  3128. // even index for d
  3129. $d = $m * ($q - $m) * $x / ( ($p_minus + $m2) * ($p + $m2));
  3130. $h = 1.0 + $d * $h;
  3131. if (abs($h) < XMININ) {
  3132. $h = XMININ;
  3133. }
  3134. $h = 1.0 / $h;
  3135. $c = 1.0 + $d / $c;
  3136. if (abs($c) < XMININ) {
  3137. $c = XMININ;
  3138. }
  3139. $frac *= $h * $c;
  3140. // odd index for d
  3141. $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
  3142. $h = 1.0 + $d * $h;
  3143. if (abs($h) < XMININ) {
  3144. $h = XMININ;
  3145. }
  3146. $h = 1.0 / $h;
  3147. $c = 1.0 + $d / $c;
  3148. if (abs($c) < XMININ) {
  3149. $c = XMININ;
  3150. }
  3151. $delta = $h * $c;
  3152. $frac *= $delta;
  3153. ++$m;
  3154. }
  3155. return $frac;
  3156. } // function _betaFraction()
  3157. /**
  3158. * logGamma function
  3159. *
  3160. * @version 1.1
  3161. * @author Jaco van Kooten
  3162. *
  3163. * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
  3164. *
  3165. * The natural logarithm of the gamma function. <br />
  3166. * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz <br />
  3167. * Applied Mathematics Division <br />
  3168. * Argonne National Laboratory <br />
  3169. * Argonne, IL 60439 <br />
  3170. * <p>
  3171. * References:
  3172. * <ol>
  3173. * <li>W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural
  3174. * Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.</li>
  3175. * <li>K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.</li>
  3176. * <li>Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.</li>
  3177. * </ol>
  3178. * </p>
  3179. * <p>
  3180. * From the original documentation:
  3181. * </p>
  3182. * <p>
  3183. * This routine calculates the LOG(GAMMA) function for a positive real argument X.
  3184. * Computation is based on an algorithm outlined in references 1 and 2.
  3185. * The program uses rational functions that theoretically approximate LOG(GAMMA)
  3186. * to at least 18 significant decimal digits. The approximation for X > 12 is from
  3187. * reference 3, while approximations for X < 12.0 are similar to those in reference
  3188. * 1, but are unpublished. The accuracy achieved depends on the arithmetic system,
  3189. * the compiler, the intrinsic functions, and proper selection of the
  3190. * machine-dependent constants.
  3191. * </p>
  3192. * <p>
  3193. * Error returns: <br />
  3194. * The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
  3195. * The computation is believed to be free of underflow and overflow.
  3196. * </p>
  3197. * @return MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
  3198. */
  3199. // Function cache for logGamma
  3200. private static $_logGammaCache_result = 0.0;
  3201. private static $_logGammaCache_x = 0.0;
  3202. private static function _logGamma($x) {
  3203. // Log Gamma related constants
  3204. static $lg_d1 = -0.5772156649015328605195174;
  3205. static $lg_d2 = 0.4227843350984671393993777;
  3206. static $lg_d4 = 1.791759469228055000094023;
  3207. static $lg_p1 = array( 4.945235359296727046734888,
  3208. 201.8112620856775083915565,
  3209. 2290.838373831346393026739,
  3210. 11319.67205903380828685045,
  3211. 28557.24635671635335736389,
  3212. 38484.96228443793359990269,
  3213. 26377.48787624195437963534,
  3214. 7225.813979700288197698961 );
  3215. static $lg_p2 = array( 4.974607845568932035012064,
  3216. 542.4138599891070494101986,
  3217. 15506.93864978364947665077,
  3218. 184793.2904445632425417223,
  3219. 1088204.76946882876749847,
  3220. 3338152.967987029735917223,
  3221. 5106661.678927352456275255,
  3222. 3074109.054850539556250927 );
  3223. static $lg_p4 = array( 14745.02166059939948905062,
  3224. 2426813.369486704502836312,
  3225. 121475557.4045093227939592,
  3226. 2663432449.630976949898078,
  3227. 29403789566.34553899906876,
  3228. 170266573776.5398868392998,
  3229. 492612579337.743088758812,
  3230. 560625185622.3951465078242 );
  3231. static $lg_q1 = array( 67.48212550303777196073036,
  3232. 1113.332393857199323513008,
  3233. 7738.757056935398733233834,
  3234. 27639.87074403340708898585,
  3235. 54993.10206226157329794414,
  3236. 61611.22180066002127833352,
  3237. 36351.27591501940507276287,
  3238. 8785.536302431013170870835 );
  3239. static $lg_q2 = array( 183.0328399370592604055942,
  3240. 7765.049321445005871323047,
  3241. 133190.3827966074194402448,
  3242. 1136705.821321969608938755,
  3243. 5267964.117437946917577538,
  3244. 13467014.54311101692290052,
  3245. 17827365.30353274213975932,
  3246. 9533095.591844353613395747 );
  3247. static $lg_q4 = array( 2690.530175870899333379843,
  3248. 639388.5654300092398984238,
  3249. 41355999.30241388052042842,
  3250. 1120872109.61614794137657,
  3251. 14886137286.78813811542398,
  3252. 101680358627.2438228077304,
  3253. 341747634550.7377132798597,
  3254. 446315818741.9713286462081 );
  3255. static $lg_c = array( -0.001910444077728,
  3256. 8.4171387781295e-4,
  3257. -5.952379913043012e-4,
  3258. 7.93650793500350248e-4,
  3259. -0.002777777777777681622553,
  3260. 0.08333333333333333331554247,
  3261. 0.0057083835261 );
  3262. // Rough estimate of the fourth root of logGamma_xBig
  3263. static $lg_frtbig = 2.25e76;
  3264. static $pnt68 = 0.6796875;
  3265. if ($x == self::$_logGammaCache_x) {
  3266. return self::$_logGammaCache_result;
  3267. }
  3268. $y = $x;
  3269. if ($y > 0.0 && $y <= LOG_GAMMA_X_MAX_VALUE) {
  3270. if ($y <= EPS) {
  3271. $res = -log(y);
  3272. } elseif ($y <= 1.5) {
  3273. // ---------------------
  3274. // EPS .LT. X .LE. 1.5
  3275. // ---------------------
  3276. if ($y < $pnt68) {
  3277. $corr = -log($y);
  3278. $xm1 = $y;
  3279. } else {
  3280. $corr = 0.0;
  3281. $xm1 = $y - 1.0;
  3282. }
  3283. if ($y <= 0.5 || $y >= $pnt68) {
  3284. $xden = 1.0;
  3285. $xnum = 0.0;
  3286. for ($i = 0; $i < 8; ++$i) {
  3287. $xnum = $xnum * $xm1 + $lg_p1[$i];
  3288. $xden = $xden * $xm1 + $lg_q1[$i];
  3289. }
  3290. $res = $corr + $xm1 * ($lg_d1 + $xm1 * ($xnum / $xden));
  3291. } else {
  3292. $xm2 = $y - 1.0;
  3293. $xden = 1.0;
  3294. $xnum = 0.0;
  3295. for ($i = 0; $i < 8; ++$i) {
  3296. $xnum = $xnum * $xm2 + $lg_p2[$i];
  3297. $xden = $xden * $xm2 + $lg_q2[$i];
  3298. }
  3299. $res = $corr + $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
  3300. }
  3301. } elseif ($y <= 4.0) {
  3302. // ---------------------
  3303. // 1.5 .LT. X .LE. 4.0
  3304. // ---------------------
  3305. $xm2 = $y - 2.0;
  3306. $xden = 1.0;
  3307. $xnum = 0.0;
  3308. for ($i = 0; $i < 8; ++$i) {
  3309. $xnum = $xnum * $xm2 + $lg_p2[$i];
  3310. $xden = $xden * $xm2 + $lg_q2[$i];
  3311. }
  3312. $res = $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
  3313. } elseif ($y <= 12.0) {
  3314. // ----------------------
  3315. // 4.0 .LT. X .LE. 12.0
  3316. // ----------------------
  3317. $xm4 = $y - 4.0;
  3318. $xden = -1.0;
  3319. $xnum = 0.0;
  3320. for ($i = 0; $i < 8; ++$i) {
  3321. $xnum = $xnum * $xm4 + $lg_p4[$i];
  3322. $xden = $xden * $xm4 + $lg_q4[$i];
  3323. }
  3324. $res = $lg_d4 + $xm4 * ($xnum / $xden);
  3325. } else {
  3326. // ---------------------------------
  3327. // Evaluate for argument .GE. 12.0
  3328. // ---------------------------------
  3329. $res = 0.0;
  3330. if ($y <= $lg_frtbig) {
  3331. $res = $lg_c[6];
  3332. $ysq = $y * $y;
  3333. for ($i = 0; $i < 6; ++$i)
  3334. $res = $res / $ysq + $lg_c[$i];
  3335. }
  3336. $res /= $y;
  3337. $corr = log($y);
  3338. $res = $res + log(SQRT2PI) - 0.5 * $corr;
  3339. $res += $y * ($corr - 1.0);
  3340. }
  3341. } else {
  3342. // --------------------------
  3343. // Return for bad arguments
  3344. // --------------------------
  3345. $res = MAX_VALUE;
  3346. }
  3347. // ------------------------------
  3348. // Final adjustments and return
  3349. // ------------------------------
  3350. self::$_logGammaCache_x = $x;
  3351. self::$_logGammaCache_result = $res;
  3352. return $res;
  3353. } // function _logGamma()
  3354. /**
  3355. * Beta function.
  3356. *
  3357. * @author Jaco van Kooten
  3358. *
  3359. * @param p require p>0
  3360. * @param q require q>0
  3361. * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
  3362. */
  3363. private static function _beta($p, $q) {
  3364. if ($p <= 0.0 || $q <= 0.0 || ($p + $q) > LOG_GAMMA_X_MAX_VALUE) {
  3365. return 0.0;
  3366. } else {
  3367. return exp(self::_logBeta($p, $q));
  3368. }
  3369. } // function _beta()
  3370. /**
  3371. * Incomplete beta function
  3372. *
  3373. * @author Jaco van Kooten
  3374. * @author Paul Meagher
  3375. *
  3376. * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
  3377. * @param x require 0<=x<=1
  3378. * @param p require p>0
  3379. * @param q require q>0
  3380. * @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
  3381. */
  3382. private static function _incompleteBeta($x, $p, $q) {
  3383. if ($x <= 0.0) {
  3384. return 0.0;
  3385. } elseif ($x >= 1.0) {
  3386. return 1.0;
  3387. } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
  3388. return 0.0;
  3389. }
  3390. $beta_gam = exp((0 - self::_logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
  3391. if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
  3392. return $beta_gam * self::_betaFraction($x, $p, $q) / $p;
  3393. } else {
  3394. return 1.0 - ($beta_gam * self::_betaFraction(1 - $x, $q, $p) / $q);
  3395. }
  3396. } // function _incompleteBeta()
  3397. /**
  3398. * BETADIST
  3399. *
  3400. * Returns the beta distribution.
  3401. *
  3402. * @param float $value Value at which you want to evaluate the distribution
  3403. * @param float $alpha Parameter to the distribution
  3404. * @param float $beta Parameter to the distribution
  3405. * @param boolean $cumulative
  3406. * @return float
  3407. *
  3408. */
  3409. public static function BETADIST($value,$alpha,$beta,$rMin=0,$rMax=1) {
  3410. $value = self::flattenSingleValue($value);
  3411. $alpha = self::flattenSingleValue($alpha);
  3412. $beta = self::flattenSingleValue($beta);
  3413. $rMin = self::flattenSingleValue($rMin);
  3414. $rMax = self::flattenSingleValue($rMax);
  3415. if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
  3416. if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
  3417. return self::$_errorCodes['num'];
  3418. }
  3419. if ($rMin > $rMax) {
  3420. $tmp = $rMin;
  3421. $rMin = $rMax;
  3422. $rMax = $tmp;
  3423. }
  3424. $value -= $rMin;
  3425. $value /= ($rMax - $rMin);
  3426. return self::_incompleteBeta($value,$alpha,$beta);
  3427. }
  3428. return self::$_errorCodes['value'];
  3429. } // function BETADIST()
  3430. /**
  3431. * BETAINV
  3432. *
  3433. * Returns the inverse of the beta distribution.
  3434. *
  3435. * @param float $probability Probability at which you want to evaluate the distribution
  3436. * @param float $alpha Parameter to the distribution
  3437. * @param float $beta Parameter to the distribution
  3438. * @param boolean $cumulative
  3439. * @return float
  3440. *
  3441. */
  3442. public static function BETAINV($probability,$alpha,$beta,$rMin=0,$rMax=1) {
  3443. $probability = self::flattenSingleValue($probability);
  3444. $alpha = self::flattenSingleValue($alpha);
  3445. $beta = self::flattenSingleValue($beta);
  3446. $rMin = self::flattenSingleValue($rMin);
  3447. $rMax = self::flattenSingleValue($rMax);
  3448. if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
  3449. if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0) || ($probability > 1)) {
  3450. return self::$_errorCodes['num'];
  3451. }
  3452. if ($rMin > $rMax) {
  3453. $tmp = $rMin;
  3454. $rMin = $rMax;
  3455. $rMax = $tmp;
  3456. }
  3457. $a = 0;
  3458. $b = 2;
  3459. $maxIteration = 100;
  3460. $i = 0;
  3461. while ((($b - $a) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
  3462. $guess = ($a + $b) / 2;
  3463. $result = self::BETADIST($guess, $alpha, $beta);
  3464. if (($result == $probability) || ($result == 0)) {
  3465. $b = $a;
  3466. } elseif ($result > $probability) {
  3467. $b = $guess;
  3468. } else {
  3469. $a = $guess;
  3470. }
  3471. }
  3472. if ($i == MAX_ITERATIONS) {
  3473. return self::$_errorCodes['na'];
  3474. }
  3475. return round($rMin + $guess * ($rMax - $rMin),12);
  3476. }
  3477. return self::$_errorCodes['value'];
  3478. } // function BETAINV()
  3479. //
  3480. // Private implementation of the incomplete Gamma function
  3481. //
  3482. private static function _incompleteGamma($a,$x) {
  3483. static $max = 32;
  3484. $summer = 0;
  3485. for ($n=0; $n<=$max; ++$n) {
  3486. $divisor = $a;
  3487. for ($i=1; $i<=$n; ++$i) {
  3488. $divisor *= ($a + $i);
  3489. }
  3490. $summer += (pow($x,$n) / $divisor);
  3491. }
  3492. return pow($x,$a) * exp(0-$x) * $summer;
  3493. } // function _incompleteGamma()
  3494. //
  3495. // Private implementation of the Gamma function
  3496. //
  3497. private static function _gamma($data) {
  3498. if ($data == 0.0) return 0;
  3499. static $p0 = 1.000000000190015;
  3500. static $p = array ( 1 => 76.18009172947146,
  3501. 2 => -86.50532032941677,
  3502. 3 => 24.01409824083091,
  3503. 4 => -1.231739572450155,
  3504. 5 => 1.208650973866179e-3,
  3505. 6 => -5.395239384953e-6
  3506. );
  3507. $y = $x = $data;
  3508. $tmp = $x + 5.5;
  3509. $tmp -= ($x + 0.5) * log($tmp);
  3510. $summer = $p0;
  3511. for ($j=1;$j<=6;++$j) {
  3512. $summer += ($p[$j] / ++$y);
  3513. }
  3514. return exp(0 - $tmp + log(2.5066282746310005 * $summer / $x));
  3515. } // function _gamma()
  3516. /**
  3517. * GAMMADIST
  3518. *
  3519. * Returns the gamma distribution.
  3520. *
  3521. * @param float $value Value at which you want to evaluate the distribution
  3522. * @param float $a Parameter to the distribution
  3523. * @param float $b Parameter to the distribution
  3524. * @param boolean $cumulative
  3525. * @return float
  3526. *
  3527. */
  3528. public static function GAMMADIST($value,$a,$b,$cumulative) {
  3529. $value = self::flattenSingleValue($value);
  3530. $a = self::flattenSingleValue($a);
  3531. $b = self::flattenSingleValue($b);
  3532. if ((is_numeric($value)) && (is_numeric($a)) && (is_numeric($b))) {
  3533. if (($value < 0) || ($a <= 0) || ($b <= 0)) {
  3534. return self::$_errorCodes['num'];
  3535. }
  3536. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  3537. if ($cumulative) {
  3538. return self::_incompleteGamma($a,$value / $b) / self::_gamma($a);
  3539. } else {
  3540. return (1 / (pow($b,$a) * self::_gamma($a))) * pow($value,$a-1) * exp(0-($value / $b));
  3541. }
  3542. }
  3543. }
  3544. return self::$_errorCodes['value'];
  3545. } // function GAMMADIST()
  3546. /**
  3547. * GAMMAINV
  3548. *
  3549. * Returns the inverse of the beta distribution.
  3550. *
  3551. * @param float $probability Probability at which you want to evaluate the distribution
  3552. * @param float $alpha Parameter to the distribution
  3553. * @param float $beta Parameter to the distribution
  3554. * @param boolean $cumulative
  3555. * @return float
  3556. *
  3557. */
  3558. public static function GAMMAINV($probability,$alpha,$beta) {
  3559. $probability = self::flattenSingleValue($probability);
  3560. $alpha = self::flattenSingleValue($alpha);
  3561. $beta = self::flattenSingleValue($beta);
  3562. // $rMin = self::flattenSingleValue($rMin);
  3563. // $rMax = self::flattenSingleValue($rMax);
  3564. if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta))) {
  3565. if (($alpha <= 0) || ($beta <= 0) || ($probability <= 0) || ($probability > 1)) {
  3566. return self::$_errorCodes['num'];
  3567. }
  3568. $xLo = 0;
  3569. $xHi = 100;
  3570. $maxIteration = 100;
  3571. $x = $xNew = 1;
  3572. $dx = 1;
  3573. $i = 0;
  3574. while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
  3575. // Apply Newton-Raphson step
  3576. $result = self::GAMMADIST($x, $alpha, $beta, True);
  3577. $error = $result - $probability;
  3578. if ($error == 0.0) {
  3579. $dx = 0;
  3580. } elseif ($error < 0.0) {
  3581. $xLo = $x;
  3582. } else {
  3583. $xHi = $x;
  3584. }
  3585. // Avoid division by zero
  3586. if ($result != 0.0) {
  3587. $dx = $error / $result;
  3588. $xNew = $x - $dx;
  3589. }
  3590. // If the NR fails to converge (which for example may be the
  3591. // case if the initial guess is too rough) we apply a bisection
  3592. // step to determine a more narrow interval around the root.
  3593. if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
  3594. $xNew = ($xLo + $xHi) / 2;
  3595. $dx = $xNew - $x;
  3596. }
  3597. $x = $xNew;
  3598. }
  3599. if ($i == MAX_ITERATIONS) {
  3600. return self::$_errorCodes['na'];
  3601. }
  3602. return round($x,12);
  3603. }
  3604. return self::$_errorCodes['value'];
  3605. } // function GAMMAINV()
  3606. /**
  3607. * GAMMALN
  3608. *
  3609. * Returns the natural logarithm of the gamma function.
  3610. *
  3611. * @param float $value
  3612. * @return float
  3613. */
  3614. public static function GAMMALN($value) {
  3615. $value = self::flattenSingleValue($value);
  3616. if (is_numeric($value)) {
  3617. if ($value <= 0) {
  3618. return self::$_errorCodes['num'];
  3619. }
  3620. return log(self::_gamma($value));
  3621. }
  3622. return self::$_errorCodes['value'];
  3623. } // function GAMMALN()
  3624. /**
  3625. * NORMDIST
  3626. *
  3627. * Returns the normal distribution for the specified mean and standard deviation. This
  3628. * function has a very wide range of applications in statistics, including hypothesis
  3629. * testing.
  3630. *
  3631. * @param float $value
  3632. * @param float $mean Mean Value
  3633. * @param float $stdDev Standard Deviation
  3634. * @param boolean $cumulative
  3635. * @return float
  3636. *
  3637. */
  3638. public static function NORMDIST($value, $mean, $stdDev, $cumulative) {
  3639. $value = self::flattenSingleValue($value);
  3640. $mean = self::flattenSingleValue($mean);
  3641. $stdDev = self::flattenSingleValue($stdDev);
  3642. if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  3643. if ($stdDev < 0) {
  3644. return self::$_errorCodes['num'];
  3645. }
  3646. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  3647. if ($cumulative) {
  3648. return 0.5 * (1 + self::_erfVal(($value - $mean) / ($stdDev * sqrt(2))));
  3649. } else {
  3650. return (1 / (SQRT2PI * $stdDev)) * exp(0 - (pow($value - $mean,2) / (2 * ($stdDev * $stdDev))));
  3651. }
  3652. }
  3653. }
  3654. return self::$_errorCodes['value'];
  3655. } // function NORMDIST()
  3656. /**
  3657. * NORMSDIST
  3658. *
  3659. * Returns the standard normal cumulative distribution function. The distribution has
  3660. * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
  3661. * table of standard normal curve areas.
  3662. *
  3663. * @param float $value
  3664. * @return float
  3665. */
  3666. public static function NORMSDIST($value) {
  3667. $value = self::flattenSingleValue($value);
  3668. return self::NORMDIST($value, 0, 1, True);
  3669. } // function NORMSDIST()
  3670. /**
  3671. * LOGNORMDIST
  3672. *
  3673. * Returns the cumulative lognormal distribution of x, where ln(x) is normally distributed
  3674. * with parameters mean and standard_dev.
  3675. *
  3676. * @param float $value
  3677. * @return float
  3678. */
  3679. public static function LOGNORMDIST($value, $mean, $stdDev) {
  3680. $value = self::flattenSingleValue($value);
  3681. $mean = self::flattenSingleValue($mean);
  3682. $stdDev = self::flattenSingleValue($stdDev);
  3683. if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  3684. if (($value <= 0) || ($stdDev <= 0)) {
  3685. return self::$_errorCodes['num'];
  3686. }
  3687. return self::NORMSDIST((log($value) - $mean) / $stdDev);
  3688. }
  3689. return self::$_errorCodes['value'];
  3690. } // function LOGNORMDIST()
  3691. /***************************************************************************
  3692. * inverse_ncdf.php
  3693. * -------------------
  3694. * begin : Friday, January 16, 2004
  3695. * copyright : (C) 2004 Michael Nickerson
  3696. * email : nickersonm@yahoo.com
  3697. *
  3698. ***************************************************************************/
  3699. private static function _inverse_ncdf($p) {
  3700. // Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
  3701. // PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
  3702. // a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
  3703. // I have not checked the accuracy of this implementation. Be aware that PHP
  3704. // will truncate the coeficcients to 14 digits.
  3705. // You have permission to use and distribute this function freely for
  3706. // whatever purpose you want, but please show common courtesy and give credit
  3707. // where credit is due.
  3708. // Input paramater is $p - probability - where 0 < p < 1.
  3709. // Coefficients in rational approximations
  3710. static $a = array( 1 => -3.969683028665376e+01,
  3711. 2 => 2.209460984245205e+02,
  3712. 3 => -2.759285104469687e+02,
  3713. 4 => 1.383577518672690e+02,
  3714. 5 => -3.066479806614716e+01,
  3715. 6 => 2.506628277459239e+00
  3716. );
  3717. static $b = array( 1 => -5.447609879822406e+01,
  3718. 2 => 1.615858368580409e+02,
  3719. 3 => -1.556989798598866e+02,
  3720. 4 => 6.680131188771972e+01,
  3721. 5 => -1.328068155288572e+01
  3722. );
  3723. static $c = array( 1 => -7.784894002430293e-03,
  3724. 2 => -3.223964580411365e-01,
  3725. 3 => -2.400758277161838e+00,
  3726. 4 => -2.549732539343734e+00,
  3727. 5 => 4.374664141464968e+00,
  3728. 6 => 2.938163982698783e+00
  3729. );
  3730. static $d = array( 1 => 7.784695709041462e-03,
  3731. 2 => 3.224671290700398e-01,
  3732. 3 => 2.445134137142996e+00,
  3733. 4 => 3.754408661907416e+00
  3734. );
  3735. // Define lower and upper region break-points.
  3736. $p_low = 0.02425; //Use lower region approx. below this
  3737. $p_high = 1 - $p_low; //Use upper region approx. above this
  3738. if (0 < $p && $p < $p_low) {
  3739. // Rational approximation for lower region.
  3740. $q = sqrt(-2 * log($p));
  3741. return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
  3742. (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
  3743. } elseif ($p_low <= $p && $p <= $p_high) {
  3744. // Rational approximation for central region.
  3745. $q = $p - 0.5;
  3746. $r = $q * $q;
  3747. return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
  3748. ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
  3749. } elseif ($p_high < $p && $p < 1) {
  3750. // Rational approximation for upper region.
  3751. $q = sqrt(-2 * log(1 - $p));
  3752. return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
  3753. (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
  3754. }
  3755. // If 0 < p < 1, return a null value
  3756. return self::$_errorCodes['null'];
  3757. } // function _inverse_ncdf()
  3758. private static function _inverse_ncdf2($prob) {
  3759. // Approximation of inverse standard normal CDF developed by
  3760. // B. Moro, "The Full Monte," Risk 8(2), Feb 1995, 57-58.
  3761. $a1 = 2.50662823884;
  3762. $a2 = -18.61500062529;
  3763. $a3 = 41.39119773534;
  3764. $a4 = -25.44106049637;
  3765. $b1 = -8.4735109309;
  3766. $b2 = 23.08336743743;
  3767. $b3 = -21.06224101826;
  3768. $b4 = 3.13082909833;
  3769. $c1 = 0.337475482272615;
  3770. $c2 = 0.976169019091719;
  3771. $c3 = 0.160797971491821;
  3772. $c4 = 2.76438810333863E-02;
  3773. $c5 = 3.8405729373609E-03;
  3774. $c6 = 3.951896511919E-04;
  3775. $c7 = 3.21767881768E-05;
  3776. $c8 = 2.888167364E-07;
  3777. $c9 = 3.960315187E-07;
  3778. $y = $prob - 0.5;
  3779. if (abs($y) < 0.42) {
  3780. $z = ($y * $y);
  3781. $z = $y * ((($a4 * $z + $a3) * $z + $a2) * $z + $a1) / (((($b4 * $z + $b3) * $z + $b2) * $z + $b1) * $z + 1);
  3782. } else {
  3783. if ($y > 0) {
  3784. $z = log(-log(1 - $prob));
  3785. } else {
  3786. $z = log(-log($prob));
  3787. }
  3788. $z = $c1 + $z * ($c2 + $z * ($c3 + $z * ($c4 + $z * ($c5 + $z * ($c6 + $z * ($c7 + $z * ($c8 + $z * $c9)))))));
  3789. if ($y < 0) {
  3790. $z = -$z;
  3791. }
  3792. }
  3793. return $z;
  3794. } // function _inverse_ncdf2()
  3795. private static function _inverse_ncdf3($p) {
  3796. // ALGORITHM AS241 APPL. STATIST. (1988) VOL. 37, NO. 3.
  3797. // Produces the normal deviate Z corresponding to a given lower
  3798. // tail area of P; Z is accurate to about 1 part in 10**16.
  3799. //
  3800. // This is a PHP version of the original FORTRAN code that can
  3801. // be found at http://lib.stat.cmu.edu/apstat/
  3802. $split1 = 0.425;
  3803. $split2 = 5;
  3804. $const1 = 0.180625;
  3805. $const2 = 1.6;
  3806. // coefficients for p close to 0.5
  3807. $a0 = 3.3871328727963666080;
  3808. $a1 = 1.3314166789178437745E+2;
  3809. $a2 = 1.9715909503065514427E+3;
  3810. $a3 = 1.3731693765509461125E+4;
  3811. $a4 = 4.5921953931549871457E+4;
  3812. $a5 = 6.7265770927008700853E+4;
  3813. $a6 = 3.3430575583588128105E+4;
  3814. $a7 = 2.5090809287301226727E+3;
  3815. $b1 = 4.2313330701600911252E+1;
  3816. $b2 = 6.8718700749205790830E+2;
  3817. $b3 = 5.3941960214247511077E+3;
  3818. $b4 = 2.1213794301586595867E+4;
  3819. $b5 = 3.9307895800092710610E+4;
  3820. $b6 = 2.8729085735721942674E+4;
  3821. $b7 = 5.2264952788528545610E+3;
  3822. // coefficients for p not close to 0, 0.5 or 1.
  3823. $c0 = 1.42343711074968357734;
  3824. $c1 = 4.63033784615654529590;
  3825. $c2 = 5.76949722146069140550;
  3826. $c3 = 3.64784832476320460504;
  3827. $c4 = 1.27045825245236838258;
  3828. $c5 = 2.41780725177450611770E-1;
  3829. $c6 = 2.27238449892691845833E-2;
  3830. $c7 = 7.74545014278341407640E-4;
  3831. $d1 = 2.05319162663775882187;
  3832. $d2 = 1.67638483018380384940;
  3833. $d3 = 6.89767334985100004550E-1;
  3834. $d4 = 1.48103976427480074590E-1;
  3835. $d5 = 1.51986665636164571966E-2;
  3836. $d6 = 5.47593808499534494600E-4;
  3837. $d7 = 1.05075007164441684324E-9;
  3838. // coefficients for p near 0 or 1.
  3839. $e0 = 6.65790464350110377720;
  3840. $e1 = 5.46378491116411436990;
  3841. $e2 = 1.78482653991729133580;
  3842. $e3 = 2.96560571828504891230E-1;
  3843. $e4 = 2.65321895265761230930E-2;
  3844. $e5 = 1.24266094738807843860E-3;
  3845. $e6 = 2.71155556874348757815E-5;
  3846. $e7 = 2.01033439929228813265E-7;
  3847. $f1 = 5.99832206555887937690E-1;
  3848. $f2 = 1.36929880922735805310E-1;
  3849. $f3 = 1.48753612908506148525E-2;
  3850. $f4 = 7.86869131145613259100E-4;
  3851. $f5 = 1.84631831751005468180E-5;
  3852. $f6 = 1.42151175831644588870E-7;
  3853. $f7 = 2.04426310338993978564E-15;
  3854. $q = $p - 0.5;
  3855. // computation for p close to 0.5
  3856. if (abs($q) <= split1) {
  3857. $R = $const1 - $q * $q;
  3858. $z = $q * ((((((($a7 * $R + $a6) * $R + $a5) * $R + $a4) * $R + $a3) * $R + $a2) * $R + $a1) * $R + $a0) /
  3859. ((((((($b7 * $R + $b6) * $R + $b5) * $R + $b4) * $R + $b3) * $R + $b2) * $R + $b1) * $R + 1);
  3860. } else {
  3861. if ($q < 0) {
  3862. $R = $p;
  3863. } else {
  3864. $R = 1 - $p;
  3865. }
  3866. $R = pow(-log($R),2);
  3867. // computation for p not close to 0, 0.5 or 1.
  3868. If ($R <= $split2) {
  3869. $R = $R - $const2;
  3870. $z = ((((((($c7 * $R + $c6) * $R + $c5) * $R + $c4) * $R + $c3) * $R + $c2) * $R + $c1) * $R + $c0) /
  3871. ((((((($d7 * $R + $d6) * $R + $d5) * $R + $d4) * $R + $d3) * $R + $d2) * $R + $d1) * $R + 1);
  3872. } else {
  3873. // computation for p near 0 or 1.
  3874. $R = $R - $split2;
  3875. $z = ((((((($e7 * $R + $e6) * $R + $e5) * $R + $e4) * $R + $e3) * $R + $e2) * $R + $e1) * $R + $e0) /
  3876. ((((((($f7 * $R + $f6) * $R + $f5) * $R + $f4) * $R + $f3) * $R + $f2) * $R + $f1) * $R + 1);
  3877. }
  3878. if ($q < 0) {
  3879. $z = -$z;
  3880. }
  3881. }
  3882. return $z;
  3883. } // function _inverse_ncdf3()
  3884. /**
  3885. * NORMINV
  3886. *
  3887. * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
  3888. *
  3889. * @param float $value
  3890. * @param float $mean Mean Value
  3891. * @param float $stdDev Standard Deviation
  3892. * @return float
  3893. *
  3894. */
  3895. public static function NORMINV($probability,$mean,$stdDev) {
  3896. $probability = self::flattenSingleValue($probability);
  3897. $mean = self::flattenSingleValue($mean);
  3898. $stdDev = self::flattenSingleValue($stdDev);
  3899. if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  3900. if (($probability < 0) || ($probability > 1)) {
  3901. return self::$_errorCodes['num'];
  3902. }
  3903. if ($stdDev < 0) {
  3904. return self::$_errorCodes['num'];
  3905. }
  3906. return (self::_inverse_ncdf($probability) * $stdDev) + $mean;
  3907. }
  3908. return self::$_errorCodes['value'];
  3909. } // function NORMINV()
  3910. /**
  3911. * NORMSINV
  3912. *
  3913. * Returns the inverse of the standard normal cumulative distribution
  3914. *
  3915. * @param float $value
  3916. * @return float
  3917. */
  3918. public static function NORMSINV($value) {
  3919. return self::NORMINV($value, 0, 1);
  3920. } // function NORMSINV()
  3921. /**
  3922. * LOGINV
  3923. *
  3924. * Returns the inverse of the normal cumulative distribution
  3925. *
  3926. * @param float $value
  3927. * @return float
  3928. *
  3929. * @todo Try implementing P J Acklam's refinement algorithm for greater
  3930. * accuracy if I can get my head round the mathematics
  3931. * (as described at) http://home.online.no/~pjacklam/notes/invnorm/
  3932. */
  3933. public static function LOGINV($probability, $mean, $stdDev) {
  3934. $probability = self::flattenSingleValue($probability);
  3935. $mean = self::flattenSingleValue($mean);
  3936. $stdDev = self::flattenSingleValue($stdDev);
  3937. if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  3938. if (($probability < 0) || ($probability > 1) || ($stdDev <= 0)) {
  3939. return self::$_errorCodes['num'];
  3940. }
  3941. return exp($mean + $stdDev * self::NORMSINV($probability));
  3942. }
  3943. return self::$_errorCodes['value'];
  3944. } // function LOGINV()
  3945. /**
  3946. * HYPGEOMDIST
  3947. *
  3948. * Returns the hypergeometric distribution. HYPGEOMDIST returns the probability of a given number of
  3949. * sample successes, given the sample size, population successes, and population size.
  3950. *
  3951. * @param float $sampleSuccesses Number of successes in the sample
  3952. * @param float $sampleNumber Size of the sample
  3953. * @param float $populationSuccesses Number of successes in the population
  3954. * @param float $populationNumber Population size
  3955. * @return float
  3956. *
  3957. */
  3958. public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber) {
  3959. $sampleSuccesses = floor(self::flattenSingleValue($sampleSuccesses));
  3960. $sampleNumber = floor(self::flattenSingleValue($sampleNumber));
  3961. $populationSuccesses = floor(self::flattenSingleValue($populationSuccesses));
  3962. $populationNumber = floor(self::flattenSingleValue($populationNumber));
  3963. if ((is_numeric($sampleSuccesses)) && (is_numeric($sampleNumber)) && (is_numeric($populationSuccesses)) && (is_numeric($populationNumber))) {
  3964. if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
  3965. return self::$_errorCodes['num'];
  3966. }
  3967. if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
  3968. return self::$_errorCodes['num'];
  3969. }
  3970. if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
  3971. return self::$_errorCodes['num'];
  3972. }
  3973. return self::COMBIN($populationSuccesses,$sampleSuccesses) *
  3974. self::COMBIN($populationNumber - $populationSuccesses,$sampleNumber - $sampleSuccesses) /
  3975. self::COMBIN($populationNumber,$sampleNumber);
  3976. }
  3977. return self::$_errorCodes['value'];
  3978. } // function HYPGEOMDIST()
  3979. /**
  3980. * TDIST
  3981. *
  3982. * Returns the probability of Student's T distribution.
  3983. *
  3984. * @param float $value Value for the function
  3985. * @param float $degrees degrees of freedom
  3986. * @param float $tails number of tails (1 or 2)
  3987. * @return float
  3988. */
  3989. public static function TDIST($value, $degrees, $tails) {
  3990. $value = self::flattenSingleValue($value);
  3991. $degrees = floor(self::flattenSingleValue($degrees));
  3992. $tails = floor(self::flattenSingleValue($tails));
  3993. if ((is_numeric($value)) && (is_numeric($degrees)) && (is_numeric($tails))) {
  3994. if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
  3995. return self::$_errorCodes['num'];
  3996. }
  3997. // tdist, which finds the probability that corresponds to a given value
  3998. // of t with k degrees of freedom. This algorithm is translated from a
  3999. // pascal function on p81 of "Statistical Computing in Pascal" by D
  4000. // Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
  4001. // London). The above Pascal algorithm is itself a translation of the
  4002. // fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
  4003. // Laboratory as reported in (among other places) "Applied Statistics
  4004. // Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
  4005. // Horwood Ltd.; W. Sussex, England).
  4006. // $ta = 2 / pi();
  4007. $ta = 0.636619772367581;
  4008. $tterm = $degrees;
  4009. $ttheta = atan2($value,sqrt($tterm));
  4010. $tc = cos($ttheta);
  4011. $ts = sin($ttheta);
  4012. $tsum = 0;
  4013. if (($degrees % 2) == 1) {
  4014. $ti = 3;
  4015. $tterm = $tc;
  4016. } else {
  4017. $ti = 2;
  4018. $tterm = 1;
  4019. }
  4020. $tsum = $tterm;
  4021. while ($ti < $degrees) {
  4022. $tterm *= $tc * $tc * ($ti - 1) / $ti;
  4023. $tsum += $tterm;
  4024. $ti += 2;
  4025. }
  4026. $tsum *= $ts;
  4027. if (($degrees % 2) == 1) { $tsum = $ta * ($tsum + $ttheta); }
  4028. $tValue = 0.5 * (1 + $tsum);
  4029. if ($tails == 1) {
  4030. return 1 - abs($tValue);
  4031. } else {
  4032. return 1 - abs((1 - $tValue) - $tValue);
  4033. }
  4034. }
  4035. return self::$_errorCodes['value'];
  4036. } // function TDIST()
  4037. /**
  4038. * TINV
  4039. *
  4040. * Returns the one-tailed probability of the chi-squared distribution.
  4041. *
  4042. * @param float $probability Probability for the function
  4043. * @param float $degrees degrees of freedom
  4044. * @return float
  4045. */
  4046. public static function TINV($probability, $degrees) {
  4047. $probability = self::flattenSingleValue($probability);
  4048. $degrees = floor(self::flattenSingleValue($degrees));
  4049. if ((is_numeric($probability)) && (is_numeric($degrees))) {
  4050. $xLo = 100;
  4051. $xHi = 0;
  4052. $maxIteration = 100;
  4053. $x = $xNew = 1;
  4054. $dx = 1;
  4055. $i = 0;
  4056. while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
  4057. // Apply Newton-Raphson step
  4058. $result = self::TDIST($x, $degrees, 2);
  4059. $error = $result - $probability;
  4060. if ($error == 0.0) {
  4061. $dx = 0;
  4062. } elseif ($error < 0.0) {
  4063. $xLo = $x;
  4064. } else {
  4065. $xHi = $x;
  4066. }
  4067. // Avoid division by zero
  4068. if ($result != 0.0) {
  4069. $dx = $error / $result;
  4070. $xNew = $x - $dx;
  4071. }
  4072. // If the NR fails to converge (which for example may be the
  4073. // case if the initial guess is too rough) we apply a bisection
  4074. // step to determine a more narrow interval around the root.
  4075. if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
  4076. $xNew = ($xLo + $xHi) / 2;
  4077. $dx = $xNew - $x;
  4078. }
  4079. $x = $xNew;
  4080. }
  4081. if ($i == MAX_ITERATIONS) {
  4082. return self::$_errorCodes['na'];
  4083. }
  4084. return round($x,12);
  4085. }
  4086. return self::$_errorCodes['value'];
  4087. } // function TINV()
  4088. /**
  4089. * CONFIDENCE
  4090. *
  4091. * Returns the confidence interval for a population mean
  4092. *
  4093. * @param float $alpha
  4094. * @param float $stdDev Standard Deviation
  4095. * @param float $size
  4096. * @return float
  4097. *
  4098. */
  4099. public static function CONFIDENCE($alpha,$stdDev,$size) {
  4100. $alpha = self::flattenSingleValue($alpha);
  4101. $stdDev = self::flattenSingleValue($stdDev);
  4102. $size = floor(self::flattenSingleValue($size));
  4103. if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
  4104. if (($alpha <= 0) || ($alpha >= 1)) {
  4105. return self::$_errorCodes['num'];
  4106. }
  4107. if (($stdDev <= 0) || ($size < 1)) {
  4108. return self::$_errorCodes['num'];
  4109. }
  4110. return self::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size);
  4111. }
  4112. return self::$_errorCodes['value'];
  4113. } // function CONFIDENCE()
  4114. /**
  4115. * POISSON
  4116. *
  4117. * Returns the Poisson distribution. A common application of the Poisson distribution
  4118. * is predicting the number of events over a specific time, such as the number of
  4119. * cars arriving at a toll plaza in 1 minute.
  4120. *
  4121. * @param float $value
  4122. * @param float $mean Mean Value
  4123. * @param boolean $cumulative
  4124. * @return float
  4125. *
  4126. */
  4127. public static function POISSON($value, $mean, $cumulative) {
  4128. $value = self::flattenSingleValue($value);
  4129. $mean = self::flattenSingleValue($mean);
  4130. if ((is_numeric($value)) && (is_numeric($mean))) {
  4131. if (($value <= 0) || ($mean <= 0)) {
  4132. return self::$_errorCodes['num'];
  4133. }
  4134. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  4135. if ($cumulative) {
  4136. $summer = 0;
  4137. for ($i = 0; $i <= floor($value); ++$i) {
  4138. $summer += pow($mean,$i) / self::FACT($i);
  4139. }
  4140. return exp(0-$mean) * $summer;
  4141. } else {
  4142. return (exp(0-$mean) * pow($mean,$value)) / self::FACT($value);
  4143. }
  4144. }
  4145. }
  4146. return self::$_errorCodes['value'];
  4147. } // function POISSON()
  4148. /**
  4149. * WEIBULL
  4150. *
  4151. * Returns the Weibull distribution. Use this distribution in reliability
  4152. * analysis, such as calculating a device's mean time to failure.
  4153. *
  4154. * @param float $value
  4155. * @param float $alpha Alpha Parameter
  4156. * @param float $beta Beta Parameter
  4157. * @param boolean $cumulative
  4158. * @return float
  4159. *
  4160. */
  4161. public static function WEIBULL($value, $alpha, $beta, $cumulative) {
  4162. $value = self::flattenSingleValue($value);
  4163. $alpha = self::flattenSingleValue($alpha);
  4164. $beta = self::flattenSingleValue($beta);
  4165. if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta))) {
  4166. if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
  4167. return self::$_errorCodes['num'];
  4168. }
  4169. if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  4170. if ($cumulative) {
  4171. return 1 - exp(0 - pow($value / $beta,$alpha));
  4172. } else {
  4173. return ($alpha / pow($beta,$alpha)) * pow($value,$alpha - 1) * exp(0 - pow($value / $beta,$alpha));
  4174. }
  4175. }
  4176. }
  4177. return self::$_errorCodes['value'];
  4178. } // function WEIBULL()
  4179. /**
  4180. * SKEW
  4181. *
  4182. * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
  4183. * of a distribution around its mean. Positive skewness indicates a distribution with an
  4184. * asymmetric tail extending toward more positive values. Negative skewness indicates a
  4185. * distribution with an asymmetric tail extending toward more negative values.
  4186. *
  4187. * @param array Data Series
  4188. * @return float
  4189. */
  4190. public static function SKEW() {
  4191. $aArgs = self::flattenArray(func_get_args());
  4192. $mean = self::AVERAGE($aArgs);
  4193. $stdDev = self::STDEV($aArgs);
  4194. $count = $summer = 0;
  4195. // Loop through arguments
  4196. foreach ($aArgs as $arg) {
  4197. // Is it a numeric value?
  4198. if ((is_numeric($arg)) && (!is_string($arg))) {
  4199. $summer += pow((($arg - $mean) / $stdDev),3) ;
  4200. ++$count;
  4201. }
  4202. }
  4203. // Return
  4204. if ($count > 2) {
  4205. return $summer * ($count / (($count-1) * ($count-2)));
  4206. }
  4207. return self::$_errorCodes['divisionbyzero'];
  4208. } // function SKEW()
  4209. /**
  4210. * KURT
  4211. *
  4212. * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
  4213. * or flatness of a distribution compared with the normal distribution. Positive
  4214. * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
  4215. * relatively flat distribution.
  4216. *
  4217. * @param array Data Series
  4218. * @return float
  4219. */
  4220. public static function KURT() {
  4221. $aArgs = self::flattenArray(func_get_args());
  4222. $mean = self::AVERAGE($aArgs);
  4223. $stdDev = self::STDEV($aArgs);
  4224. if ($stdDev > 0) {
  4225. $count = $summer = 0;
  4226. // Loop through arguments
  4227. foreach ($aArgs as $arg) {
  4228. // Is it a numeric value?
  4229. if ((is_numeric($arg)) && (!is_string($arg))) {
  4230. $summer += pow((($arg - $mean) / $stdDev),4) ;
  4231. ++$count;
  4232. }
  4233. }
  4234. // Return
  4235. if ($count > 3) {
  4236. return $summer * ($count * ($count+1) / (($count-1) * ($count-2) * ($count-3))) - (3 * pow($count-1,2) / (($count-2) * ($count-3)));
  4237. }
  4238. }
  4239. return self::$_errorCodes['divisionbyzero'];
  4240. } // function KURT()
  4241. /**
  4242. * RAND
  4243. *
  4244. * @param int $min Minimal value
  4245. * @param int $max Maximal value
  4246. * @return int Random number
  4247. */
  4248. public static function RAND($min = 0, $max = 0) {
  4249. $min = self::flattenSingleValue($min);
  4250. $max = self::flattenSingleValue($max);
  4251. if ($min == 0 && $max == 0) {
  4252. return (rand(0,10000000)) / 10000000;
  4253. } else {
  4254. return rand($min, $max);
  4255. }
  4256. } // function RAND()
  4257. /**
  4258. * MOD
  4259. *
  4260. * @param int $a Dividend
  4261. * @param int $b Divisor
  4262. * @return int Remainder
  4263. */
  4264. public static function MOD($a = 1, $b = 1) {
  4265. $a = self::flattenSingleValue($a);
  4266. $b = self::flattenSingleValue($b);
  4267. return fmod($a,$b);
  4268. } // function MOD()
  4269. /**
  4270. * CHARACTER
  4271. *
  4272. * @param string $character Value
  4273. * @return int
  4274. */
  4275. public static function CHARACTER($character) {
  4276. $character = self::flattenSingleValue($character);
  4277. if ((!is_numeric($character)) || ($character < 0)) {
  4278. return self::$_errorCodes['value'];
  4279. }
  4280. if (function_exists('mb_convert_encoding')) {
  4281. return mb_convert_encoding('&#'.intval($character).';', 'UTF-8', 'HTML-ENTITIES');
  4282. } else {
  4283. return chr(intval($character));
  4284. }
  4285. }
  4286. private static function _uniord($c) {
  4287. if (ord($c{0}) >=0 && ord($c{0}) <= 127)
  4288. return ord($c{0});
  4289. if (ord($c{0}) >= 192 && ord($c{0}) <= 223)
  4290. return (ord($c{0})-192)*64 + (ord($c{1})-128);
  4291. if (ord($c{0}) >= 224 && ord($c{0}) <= 239)
  4292. return (ord($c{0})-224)*4096 + (ord($c{1})-128)*64 + (ord($c{2})-128);
  4293. if (ord($c{0}) >= 240 && ord($c{0}) <= 247)
  4294. return (ord($c{0})-240)*262144 + (ord($c{1})-128)*4096 + (ord($c{2})-128)*64 + (ord($c{3})-128);
  4295. if (ord($c{0}) >= 248 && ord($c{0}) <= 251)
  4296. return (ord($c{0})-248)*16777216 + (ord($c{1})-128)*262144 + (ord($c{2})-128)*4096 + (ord($c{3})-128)*64 + (ord($c{4})-128);
  4297. if (ord($c{0}) >= 252 && ord($c{0}) <= 253)
  4298. return (ord($c{0})-252)*1073741824 + (ord($c{1})-128)*16777216 + (ord($c{2})-128)*262144 + (ord($c{3})-128)*4096 + (ord($c{4})-128)*64 + (ord($c{5})-128);
  4299. if (ord($c{0}) >= 254 && ord($c{0}) <= 255) //error
  4300. return self::$_errorCodes['value'];
  4301. return 0;
  4302. } // function _uniord()
  4303. /**
  4304. * ASCIICODE
  4305. *
  4306. * @param string $character Value
  4307. * @return int
  4308. */
  4309. public static function ASCIICODE($characters) {
  4310. $characters = self::flattenSingleValue($characters);
  4311. if (is_bool($characters)) {
  4312. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  4313. $characters = (int) $characters;
  4314. } else {
  4315. if ($characters) {
  4316. $characters = 'True';
  4317. } else {
  4318. $characters = 'False';
  4319. }
  4320. }
  4321. }
  4322. $character = $characters;
  4323. if ((function_exists('mb_strlen')) && (function_exists('mb_substr'))) {
  4324. if (mb_strlen($characters, 'UTF-8') > 1) { $character = mb_substr($characters, 0, 1, 'UTF-8'); }
  4325. return self::_uniord($character);
  4326. } else {
  4327. if (strlen($characters) > 0) { $character = substr($characters, 0, 1); }
  4328. return ord($character);
  4329. }
  4330. } // function ASCIICODE()
  4331. /**
  4332. * CONCATENATE
  4333. *
  4334. * @return string
  4335. */
  4336. public static function CONCATENATE() {
  4337. // Return value
  4338. $returnValue = '';
  4339. // Loop through arguments
  4340. $aArgs = self::flattenArray(func_get_args());
  4341. foreach ($aArgs as $arg) {
  4342. if (is_bool($arg)) {
  4343. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  4344. $arg = (int) $arg;
  4345. } else {
  4346. if ($arg) {
  4347. $arg = 'TRUE';
  4348. } else {
  4349. $arg = 'FALSE';
  4350. }
  4351. }
  4352. }
  4353. $returnValue .= $arg;
  4354. }
  4355. // Return
  4356. return $returnValue;
  4357. } // function CONCATENATE()
  4358. /**
  4359. * STRINGLENGTH
  4360. *
  4361. * @param string $value Value
  4362. * @param int $chars Number of characters
  4363. * @return string
  4364. */
  4365. public static function STRINGLENGTH($value = '') {
  4366. $value = self::flattenSingleValue($value);
  4367. if (is_bool($value)) {
  4368. $value = ($value) ? 'TRUE' : 'FALSE';
  4369. }
  4370. if (function_exists('mb_strlen')) {
  4371. return mb_strlen($value, 'UTF-8');
  4372. } else {
  4373. return strlen($value);
  4374. }
  4375. } // function STRINGLENGTH()
  4376. /**
  4377. * SEARCHSENSITIVE
  4378. *
  4379. * @param string $needle The string to look for
  4380. * @param string $haystack The string in which to look
  4381. * @param int $offset Offset within $haystack
  4382. * @return string
  4383. */
  4384. public static function SEARCHSENSITIVE($needle,$haystack,$offset=1) {
  4385. $needle = self::flattenSingleValue($needle);
  4386. $haystack = self::flattenSingleValue($haystack);
  4387. $offset = self::flattenSingleValue($offset);
  4388. if (!is_bool($needle)) {
  4389. if (is_bool($haystack)) {
  4390. $haystack = ($haystack) ? 'TRUE' : 'FALSE';
  4391. }
  4392. if (($offset > 0) && (strlen($haystack) > $offset)) {
  4393. if (function_exists('mb_strpos')) {
  4394. $pos = mb_strpos($haystack, $needle, --$offset,'UTF-8');
  4395. } else {
  4396. $pos = strpos($haystack, $needle, --$offset);
  4397. }
  4398. if ($pos !== false) {
  4399. return ++$pos;
  4400. }
  4401. }
  4402. }
  4403. return self::$_errorCodes['value'];
  4404. } // function SEARCHSENSITIVE()
  4405. /**
  4406. * SEARCHINSENSITIVE
  4407. *
  4408. * @param string $needle The string to look for
  4409. * @param string $haystack The string in which to look
  4410. * @param int $offset Offset within $haystack
  4411. * @return string
  4412. */
  4413. public static function SEARCHINSENSITIVE($needle,$haystack,$offset=1) {
  4414. $needle = self::flattenSingleValue($needle);
  4415. $haystack = self::flattenSingleValue($haystack);
  4416. $offset = self::flattenSingleValue($offset);
  4417. if (!is_bool($needle)) {
  4418. if (is_bool($haystack)) {
  4419. $haystack = ($haystack) ? 'TRUE' : 'FALSE';
  4420. }
  4421. if (($offset > 0) && (strlen($haystack) > $offset)) {
  4422. if (function_exists('mb_stripos')) {
  4423. $pos = mb_stripos($haystack, $needle, --$offset,'UTF-8');
  4424. } else {
  4425. $pos = stripos($haystack, $needle, --$offset);
  4426. }
  4427. if ($pos !== false) {
  4428. return ++$pos;
  4429. }
  4430. }
  4431. }
  4432. return self::$_errorCodes['value'];
  4433. } // function SEARCHINSENSITIVE()
  4434. /**
  4435. * LEFT
  4436. *
  4437. * @param string $value Value
  4438. * @param int $chars Number of characters
  4439. * @return string
  4440. */
  4441. public static function LEFT($value = '', $chars = 1) {
  4442. $value = self::flattenSingleValue($value);
  4443. $chars = self::flattenSingleValue($chars);
  4444. if ($chars < 0) {
  4445. return self::$_errorCodes['value'];
  4446. }
  4447. if (is_bool($value)) {
  4448. $value = ($value) ? 'TRUE' : 'FALSE';
  4449. }
  4450. if (function_exists('mb_substr')) {
  4451. return mb_substr($value, 0, $chars, 'UTF-8');
  4452. } else {
  4453. return substr($value, 0, $chars);
  4454. }
  4455. } // function LEFT()
  4456. /**
  4457. * RIGHT
  4458. *
  4459. * @param string $value Value
  4460. * @param int $chars Number of characters
  4461. * @return string
  4462. */
  4463. public static function RIGHT($value = '', $chars = 1) {
  4464. $value = self::flattenSingleValue($value);
  4465. $chars = self::flattenSingleValue($chars);
  4466. if ($chars < 0) {
  4467. return self::$_errorCodes['value'];
  4468. }
  4469. if (is_bool($value)) {
  4470. $value = ($value) ? 'TRUE' : 'FALSE';
  4471. }
  4472. if ((function_exists('mb_substr')) && (function_exists('mb_strlen'))) {
  4473. return mb_substr($value, mb_strlen($value, 'UTF-8') - $chars, $chars, 'UTF-8');
  4474. } else {
  4475. return substr($value, strlen($value) - $chars);
  4476. }
  4477. } // function RIGHT()
  4478. /**
  4479. * MID
  4480. *
  4481. * @param string $value Value
  4482. * @param int $start Start character
  4483. * @param int $chars Number of characters
  4484. * @return string
  4485. */
  4486. public static function MID($value = '', $start = 1, $chars = null) {
  4487. $value = self::flattenSingleValue($value);
  4488. $start = self::flattenSingleValue($start);
  4489. $chars = self::flattenSingleValue($chars);
  4490. if (($start < 1) || ($chars < 0)) {
  4491. return self::$_errorCodes['value'];
  4492. }
  4493. if (is_bool($value)) {
  4494. $value = ($value) ? 'TRUE' : 'FALSE';
  4495. }
  4496. if (function_exists('mb_substr')) {
  4497. return mb_substr($value, --$start, $chars, 'UTF-8');
  4498. } else {
  4499. return substr($value, --$start, $chars);
  4500. }
  4501. } // function MID()
  4502. /**
  4503. * REPLACE
  4504. *
  4505. * @param string $value Value
  4506. * @param int $start Start character
  4507. * @param int $chars Number of characters
  4508. * @return string
  4509. */
  4510. public static function REPLACE($oldText = '', $start = 1, $chars = null, $newText) {
  4511. $oldText = self::flattenSingleValue($oldText);
  4512. $start = self::flattenSingleValue($start);
  4513. $chars = self::flattenSingleValue($chars);
  4514. $newText = self::flattenSingleValue($newText);
  4515. $left = self::LEFT($oldText,$start-1);
  4516. $right = self::RIGHT($oldText,self::STRINGLENGTH($oldText)-($start+$chars)+1);
  4517. return $left.$newText.$right;
  4518. } // function REPLACE()
  4519. /**
  4520. * SUBSTITUTE
  4521. *
  4522. * @param string $text Value
  4523. * @param string $fromText From Value
  4524. * @param string $toText To Value
  4525. * @param integer $instance Instance Number
  4526. * @return string
  4527. */
  4528. public static function SUBSTITUTE($text = '', $fromText = '', $toText = '', $instance = 0) {
  4529. $text = self::flattenSingleValue($text);
  4530. $fromText = self::flattenSingleValue($fromText);
  4531. $toText = self::flattenSingleValue($toText);
  4532. $instance = floor(self::flattenSingleValue($instance));
  4533. if ($instance == 0) {
  4534. if(function_exists('mb_str_replace')) {
  4535. return mb_str_replace($fromText,$toText,$text);
  4536. } else {
  4537. return str_replace($fromText,$toText,$text);
  4538. }
  4539. } else {
  4540. $pos = -1;
  4541. while($instance > 0) {
  4542. if (function_exists('mb_strpos')) {
  4543. $pos = mb_strpos($text, $fromText, $pos+1, 'UTF-8');
  4544. } else {
  4545. $pos = strpos($text, $fromText, $pos+1);
  4546. }
  4547. if ($pos === false) {
  4548. break;
  4549. }
  4550. --$instance;
  4551. }
  4552. if ($pos !== false) {
  4553. if (function_exists('mb_strlen')) {
  4554. return self::REPLACE($text,++$pos,mb_strlen($fromText, 'UTF-8'),$toText);
  4555. } else {
  4556. return self::REPLACE($text,++$pos,strlen($fromText),$toText);
  4557. }
  4558. }
  4559. }
  4560. return $left.$newText.$right;
  4561. } // function SUBSTITUTE()
  4562. /**
  4563. * RETURNSTRING
  4564. *
  4565. * @param mixed $value Value to check
  4566. * @return boolean
  4567. */
  4568. public static function RETURNSTRING($testValue = '') {
  4569. $testValue = self::flattenSingleValue($testValue);
  4570. if (is_string($testValue)) {
  4571. return $testValue;
  4572. }
  4573. return Null;
  4574. } // function RETURNSTRING()
  4575. /**
  4576. * FIXEDFORMAT
  4577. *
  4578. * @param mixed $value Value to check
  4579. * @return boolean
  4580. */
  4581. public static function FIXEDFORMAT($value,$decimals=2,$no_commas=false) {
  4582. $value = self::flattenSingleValue($value);
  4583. $decimals = self::flattenSingleValue($decimals);
  4584. $no_commas = self::flattenSingleValue($no_commas);
  4585. $valueResult = round($value,$decimals);
  4586. if ($decimals < 0) { $decimals = 0; }
  4587. if (!$no_commas) {
  4588. $valueResult = number_format($valueResult,$decimals);
  4589. }
  4590. return (string) $valueResult;
  4591. } // function FIXEDFORMAT()
  4592. /**
  4593. * TEXTFORMAT
  4594. *
  4595. * @param mixed $value Value to check
  4596. * @return boolean
  4597. */
  4598. public static function TEXTFORMAT($value,$format) {
  4599. $value = self::flattenSingleValue($value);
  4600. $format = self::flattenSingleValue($format);
  4601. if ((is_string($value)) && (!is_numeric($value)) && PHPExcel_Shared_Date::isDateTimeFormatCode($format)) {
  4602. $value = self::DATEVALUE($value);
  4603. }
  4604. return (string) PHPExcel_Style_NumberFormat::toFormattedString($value,$format);
  4605. } // function TEXTFORMAT()
  4606. /**
  4607. * TRIMSPACES
  4608. *
  4609. * @param mixed $value Value to check
  4610. * @return string
  4611. */
  4612. public static function TRIMSPACES($stringValue = '') {
  4613. $stringValue = self::flattenSingleValue($stringValue);
  4614. if (is_string($stringValue) || is_numeric($stringValue)) {
  4615. return trim(preg_replace('/ +/',' ',$stringValue));
  4616. }
  4617. return Null;
  4618. } // function TRIMSPACES()
  4619. private static $_invalidChars = Null;
  4620. /**
  4621. * TRIMNONPRINTABLE
  4622. *
  4623. * @param mixed $value Value to check
  4624. * @return string
  4625. */
  4626. public static function TRIMNONPRINTABLE($stringValue = '') {
  4627. $stringValue = self::flattenSingleValue($stringValue);
  4628. if (is_bool($stringValue)) {
  4629. $stringValue = ($stringValue) ? 'TRUE' : 'FALSE';
  4630. }
  4631. if (self::$_invalidChars == Null) {
  4632. self::$_invalidChars = range(chr(0),chr(31));
  4633. }
  4634. if (is_string($stringValue) || is_numeric($stringValue)) {
  4635. return str_replace(self::$_invalidChars,'',trim($stringValue,"\x00..\x1F"));
  4636. }
  4637. return Null;
  4638. } // function TRIMNONPRINTABLE()
  4639. /**
  4640. * ERROR_TYPE
  4641. *
  4642. * @param mixed $value Value to check
  4643. * @return boolean
  4644. */
  4645. public static function ERROR_TYPE($value = '') {
  4646. $value = self::flattenSingleValue($value);
  4647. $i = 1;
  4648. foreach(self::$_errorCodes as $errorCode) {
  4649. if ($value == $errorCode) {
  4650. return $i;
  4651. }
  4652. ++$i;
  4653. }
  4654. return self::$_errorCodes['na'];
  4655. } // function ERROR_TYPE()
  4656. /**
  4657. * IS_BLANK
  4658. *
  4659. * @param mixed $value Value to check
  4660. * @return boolean
  4661. */
  4662. public static function IS_BLANK($value) {
  4663. if (!is_null($value)) {
  4664. $value = self::flattenSingleValue($value);
  4665. }
  4666. return is_null($value);
  4667. } // function IS_BLANK()
  4668. /**
  4669. * IS_ERR
  4670. *
  4671. * @param mixed $value Value to check
  4672. * @return boolean
  4673. */
  4674. public static function IS_ERR($value = '') {
  4675. $value = self::flattenSingleValue($value);
  4676. return self::IS_ERROR($value) && (!self::IS_NA($value));
  4677. } // function IS_ERR()
  4678. /**
  4679. * IS_ERROR
  4680. *
  4681. * @param mixed $value Value to check
  4682. * @return boolean
  4683. */
  4684. public static function IS_ERROR($value = '') {
  4685. $value = self::flattenSingleValue($value);
  4686. return in_array($value, array_values(self::$_errorCodes));
  4687. } // function IS_ERROR()
  4688. /**
  4689. * IS_NA
  4690. *
  4691. * @param mixed $value Value to check
  4692. * @return boolean
  4693. */
  4694. public static function IS_NA($value = '') {
  4695. $value = self::flattenSingleValue($value);
  4696. return ($value === self::$_errorCodes['na']);
  4697. } // function IS_NA()
  4698. /**
  4699. * IS_EVEN
  4700. *
  4701. * @param mixed $value Value to check
  4702. * @return boolean
  4703. */
  4704. public static function IS_EVEN($value = 0) {
  4705. $value = self::flattenSingleValue($value);
  4706. if ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
  4707. return self::$_errorCodes['value'];
  4708. }
  4709. return ($value % 2 == 0);
  4710. } // function IS_EVEN()
  4711. /**
  4712. * IS_ODD
  4713. *
  4714. * @param mixed $value Value to check
  4715. * @return boolean
  4716. */
  4717. public static function IS_ODD($value = null) {
  4718. $value = self::flattenSingleValue($value);
  4719. if ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
  4720. return self::$_errorCodes['value'];
  4721. }
  4722. return (abs($value) % 2 == 1);
  4723. } // function IS_ODD()
  4724. /**
  4725. * IS_NUMBER
  4726. *
  4727. * @param mixed $value Value to check
  4728. * @return boolean
  4729. */
  4730. public static function IS_NUMBER($value = 0) {
  4731. $value = self::flattenSingleValue($value);
  4732. if (is_string($value)) {
  4733. return False;
  4734. }
  4735. return is_numeric($value);
  4736. } // function IS_NUMBER()
  4737. /**
  4738. * IS_LOGICAL
  4739. *
  4740. * @param mixed $value Value to check
  4741. * @return boolean
  4742. */
  4743. public static function IS_LOGICAL($value = true) {
  4744. $value = self::flattenSingleValue($value);
  4745. return is_bool($value);
  4746. } // function IS_LOGICAL()
  4747. /**
  4748. * IS_TEXT
  4749. *
  4750. * @param mixed $value Value to check
  4751. * @return boolean
  4752. */
  4753. public static function IS_TEXT($value = '') {
  4754. $value = self::flattenSingleValue($value);
  4755. return is_string($value);
  4756. } // function IS_TEXT()
  4757. /**
  4758. * IS_NONTEXT
  4759. *
  4760. * @param mixed $value Value to check
  4761. * @return boolean
  4762. */
  4763. public static function IS_NONTEXT($value = '') {
  4764. return !self::IS_TEXT($value);
  4765. } // function IS_NONTEXT()
  4766. /**
  4767. * VERSION
  4768. *
  4769. * @return string Version information
  4770. */
  4771. public static function VERSION() {
  4772. return 'PHPExcel 1.7.1, 2009-11-02';
  4773. } // function VERSION()
  4774. /**
  4775. * DATE
  4776. *
  4777. * @param long $year
  4778. * @param long $month
  4779. * @param long $day
  4780. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4781. * depending on the value of the ReturnDateType flag
  4782. */
  4783. public static function DATE($year = 0, $month = 1, $day = 1) {
  4784. $year = (integer) self::flattenSingleValue($year);
  4785. $month = (integer) self::flattenSingleValue($month);
  4786. $day = (integer) self::flattenSingleValue($day);
  4787. $baseYear = PHPExcel_Shared_Date::getExcelCalendar();
  4788. // Validate parameters
  4789. if ($year < ($baseYear-1900)) {
  4790. return self::$_errorCodes['num'];
  4791. }
  4792. if ((($baseYear-1900) != 0) && ($year < $baseYear) && ($year >= 1900)) {
  4793. return self::$_errorCodes['num'];
  4794. }
  4795. if (($year < $baseYear) && ($year >= ($baseYear-1900))) {
  4796. $year += 1900;
  4797. }
  4798. if ($month < 1) {
  4799. // Handle year/month adjustment if month < 1
  4800. --$month;
  4801. $year += ceil($month / 12) - 1;
  4802. $month = 13 - abs($month % 12);
  4803. } elseif ($month > 12) {
  4804. // Handle year/month adjustment if month > 12
  4805. $year += floor($month / 12);
  4806. $month = ($month % 12);
  4807. }
  4808. // Re-validate the year parameter after adjustments
  4809. if (($year < $baseYear) || ($year >= 10000)) {
  4810. return self::$_errorCodes['num'];
  4811. }
  4812. // Execute function
  4813. $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel($year, $month, $day);
  4814. switch (self::getReturnDateType()) {
  4815. case self::RETURNDATE_EXCEL : return (float) $excelDateValue;
  4816. break;
  4817. case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
  4818. break;
  4819. case self::RETURNDATE_PHP_OBJECT : return PHPExcel_Shared_Date::ExcelToPHPObject($excelDateValue);
  4820. break;
  4821. }
  4822. } // function DATE()
  4823. /**
  4824. * TIME
  4825. *
  4826. * @param long $hour
  4827. * @param long $minute
  4828. * @param long $second
  4829. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4830. * depending on the value of the ReturnDateType flag
  4831. */
  4832. public static function TIME($hour = 0, $minute = 0, $second = 0) {
  4833. $hour = self::flattenSingleValue($hour);
  4834. $minute = self::flattenSingleValue($minute);
  4835. $second = self::flattenSingleValue($second);
  4836. if ($hour == '') { $hour = 0; }
  4837. if ($minute == '') { $minute = 0; }
  4838. if ($second == '') { $second = 0; }
  4839. if ((!is_numeric($hour)) || (!is_numeric($minute)) || (!is_numeric($second))) {
  4840. return self::$_errorCodes['value'];
  4841. }
  4842. $hour = (integer) $hour;
  4843. $minute = (integer) $minute;
  4844. $second = (integer) $second;
  4845. if ($second < 0) {
  4846. $minute += floor($second / 60);
  4847. $second = 60 - abs($second % 60);
  4848. if ($second == 60) { $second = 0; }
  4849. } elseif ($second >= 60) {
  4850. $minute += floor($second / 60);
  4851. $second = $second % 60;
  4852. }
  4853. if ($minute < 0) {
  4854. $hour += floor($minute / 60);
  4855. $minute = 60 - abs($minute % 60);
  4856. if ($minute == 60) { $minute = 0; }
  4857. } elseif ($minute >= 60) {
  4858. $hour += floor($minute / 60);
  4859. $minute = $minute % 60;
  4860. }
  4861. if ($hour > 23) {
  4862. $hour = $hour % 24;
  4863. } elseif ($hour < 0) {
  4864. return self::$_errorCodes['num'];
  4865. }
  4866. // Execute function
  4867. switch (self::getReturnDateType()) {
  4868. case self::RETURNDATE_EXCEL : $date = 0;
  4869. $calendar = PHPExcel_Shared_Date::getExcelCalendar();
  4870. if ($calendar != PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900) {
  4871. $date = 1;
  4872. }
  4873. return (float) PHPExcel_Shared_Date::FormattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
  4874. break;
  4875. 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
  4876. break;
  4877. case self::RETURNDATE_PHP_OBJECT : $dayAdjust = 0;
  4878. if ($hour < 0) {
  4879. $dayAdjust = floor($hour / 24);
  4880. $hour = 24 - abs($hour % 24);
  4881. if ($hour == 24) { $hour = 0; }
  4882. } elseif ($hour >= 24) {
  4883. $dayAdjust = floor($hour / 24);
  4884. $hour = $hour % 24;
  4885. }
  4886. $phpDateObject = new DateTime('1900-01-01 '.$hour.':'.$minute.':'.$second);
  4887. if ($dayAdjust != 0) {
  4888. $phpDateObject->modify($dayAdjust.' days');
  4889. }
  4890. return $phpDateObject;
  4891. break;
  4892. }
  4893. } // function TIME()
  4894. /**
  4895. * DATEVALUE
  4896. *
  4897. * @param string $dateValue
  4898. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4899. * depending on the value of the ReturnDateType flag
  4900. */
  4901. public static function DATEVALUE($dateValue = 1) {
  4902. $dateValue = str_replace(array('/','.',' '),array('-','-','-'),self::flattenSingleValue(trim($dateValue,'"')));
  4903. $yearFound = false;
  4904. $t1 = explode('-',$dateValue);
  4905. foreach($t1 as &$t) {
  4906. if ((is_numeric($t)) && (($t > 31) && ($t < 100))) {
  4907. if ($yearFound) {
  4908. return self::$_errorCodes['value'];
  4909. } else {
  4910. $t += 1900;
  4911. $yearFound = true;
  4912. }
  4913. }
  4914. }
  4915. unset($t);
  4916. $dateValue = implode('-',$t1);
  4917. $PHPDateArray = date_parse($dateValue);
  4918. if (($PHPDateArray === False) || ($PHPDateArray['error_count'] > 0)) {
  4919. $testVal1 = strtok($dateValue,'- ');
  4920. if ($testVal1 !== False) {
  4921. $testVal2 = strtok('- ');
  4922. if ($testVal2 !== False) {
  4923. $testVal3 = strtok('- ');
  4924. if ($testVal3 === False) {
  4925. $testVal3 = strftime('%Y');
  4926. }
  4927. } else {
  4928. return self::$_errorCodes['value'];
  4929. }
  4930. } else {
  4931. return self::$_errorCodes['value'];
  4932. }
  4933. $PHPDateArray = date_parse($testVal1.'-'.$testVal2.'-'.$testVal3);
  4934. if (($PHPDateArray === False) || ($PHPDateArray['error_count'] > 0)) {
  4935. $PHPDateArray = date_parse($testVal2.'-'.$testVal1.'-'.$testVal3);
  4936. if (($PHPDateArray === False) || ($PHPDateArray['error_count'] > 0)) {
  4937. return self::$_errorCodes['value'];
  4938. }
  4939. }
  4940. }
  4941. if (($PHPDateArray !== False) && ($PHPDateArray['error_count'] == 0)) {
  4942. // Execute function
  4943. if ($PHPDateArray['year'] == '') { $PHPDateArray['year'] = strftime('%Y'); }
  4944. if ($PHPDateArray['month'] == '') { $PHPDateArray['month'] = strftime('%m'); }
  4945. if ($PHPDateArray['day'] == '') { $PHPDateArray['day'] = strftime('%d'); }
  4946. $excelDateValue = floor(PHPExcel_Shared_Date::FormattedPHPToExcel($PHPDateArray['year'],$PHPDateArray['month'],$PHPDateArray['day'],$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']));
  4947. switch (self::getReturnDateType()) {
  4948. case self::RETURNDATE_EXCEL : return (float) $excelDateValue;
  4949. break;
  4950. case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
  4951. break;
  4952. case self::RETURNDATE_PHP_OBJECT : return new DateTime($PHPDateArray['year'].'-'.$PHPDateArray['month'].'-'.$PHPDateArray['day'].' 00:00:00');
  4953. break;
  4954. }
  4955. }
  4956. return self::$_errorCodes['value'];
  4957. } // function DATEVALUE()
  4958. /**
  4959. * _getDateValue
  4960. *
  4961. * @param string $dateValue
  4962. * @return mixed Excel date/time serial value, or string if error
  4963. */
  4964. private static function _getDateValue($dateValue) {
  4965. if (!is_numeric($dateValue)) {
  4966. if ((is_string($dateValue)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
  4967. return self::$_errorCodes['value'];
  4968. }
  4969. if ((is_object($dateValue)) && ($dateValue instanceof PHPExcel_Shared_Date::$dateTimeObjectType)) {
  4970. $dateValue = PHPExcel_Shared_Date::PHPToExcel($dateValue);
  4971. } else {
  4972. $saveReturnDateType = self::getReturnDateType();
  4973. self::setReturnDateType(self::RETURNDATE_EXCEL);
  4974. $dateValue = self::DATEVALUE($dateValue);
  4975. self::setReturnDateType($saveReturnDateType);
  4976. }
  4977. }
  4978. return $dateValue;
  4979. } // function _getDateValue()
  4980. /**
  4981. * TIMEVALUE
  4982. *
  4983. * @param string $timeValue
  4984. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4985. * depending on the value of the ReturnDateType flag
  4986. */
  4987. public static function TIMEVALUE($timeValue) {
  4988. $timeValue = self::flattenSingleValue($timeValue);
  4989. if ((($PHPDateArray = date_parse($timeValue)) !== False) && ($PHPDateArray['error_count'] == 0)) {
  4990. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  4991. $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel($PHPDateArray['year'],$PHPDateArray['month'],$PHPDateArray['day'],$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']);
  4992. } else {
  4993. $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel(1900,1,1,$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']) - 1;
  4994. }
  4995. switch (self::getReturnDateType()) {
  4996. case self::RETURNDATE_EXCEL : return (float) $excelDateValue;
  4997. break;
  4998. case self::RETURNDATE_PHP_NUMERIC : return (integer) $phpDateValue = PHPExcel_Shared_Date::ExcelToPHP($excelDateValue+25569) - 3600;;
  4999. break;
  5000. case self::RETURNDATE_PHP_OBJECT : return new DateTime('1900-01-01 '.$PHPDateArray['hour'].':'.$PHPDateArray['minute'].':'.$PHPDateArray['second']);
  5001. break;
  5002. }
  5003. }
  5004. return self::$_errorCodes['value'];
  5005. } // function TIMEVALUE()
  5006. /**
  5007. * _getTimeValue
  5008. *
  5009. * @param string $timeValue
  5010. * @return mixed Excel date/time serial value, or string if error
  5011. */
  5012. private static function _getTimeValue($timeValue) {
  5013. $saveReturnDateType = self::getReturnDateType();
  5014. self::setReturnDateType(self::RETURNDATE_EXCEL);
  5015. $timeValue = self::TIMEVALUE($timeValue);
  5016. self::setReturnDateType($saveReturnDateType);
  5017. return $timeValue;
  5018. } // function _getTimeValue()
  5019. /**
  5020. * DATETIMENOW
  5021. *
  5022. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  5023. * depending on the value of the ReturnDateType flag
  5024. */
  5025. public static function DATETIMENOW() {
  5026. $saveTimeZone = date_default_timezone_get();
  5027. date_default_timezone_set('UTC');
  5028. $retValue = False;
  5029. switch (self::getReturnDateType()) {
  5030. case self::RETURNDATE_EXCEL : $retValue = (float) PHPExcel_Shared_Date::PHPToExcel(time());
  5031. break;
  5032. case self::RETURNDATE_PHP_NUMERIC : $retValue = (integer) time();
  5033. break;
  5034. case self::RETURNDATE_PHP_OBJECT : $retValue = new DateTime();
  5035. break;
  5036. }
  5037. date_default_timezone_set($saveTimeZone);
  5038. return $retValue;
  5039. } // function DATETIMENOW()
  5040. /**
  5041. * DATENOW
  5042. *
  5043. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  5044. * depending on the value of the ReturnDateType flag
  5045. */
  5046. public static function DATENOW() {
  5047. $saveTimeZone = date_default_timezone_get();
  5048. date_default_timezone_set('UTC');
  5049. $retValue = False;
  5050. $excelDateTime = floor(PHPExcel_Shared_Date::PHPToExcel(time()));
  5051. switch (self::getReturnDateType()) {
  5052. case self::RETURNDATE_EXCEL : $retValue = (float) $excelDateTime;
  5053. break;
  5054. case self::RETURNDATE_PHP_NUMERIC : $retValue = (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateTime) - 3600;
  5055. break;
  5056. case self::RETURNDATE_PHP_OBJECT : $retValue = PHPExcel_Shared_Date::ExcelToPHPObject($excelDateTime);
  5057. break;
  5058. }
  5059. date_default_timezone_set($saveTimeZone);
  5060. return $retValue;
  5061. } // function DATENOW()
  5062. private static function _isLeapYear($year) {
  5063. return ((($year % 4) == 0) && (($year % 100) != 0) || (($year % 400) == 0));
  5064. } // function _isLeapYear()
  5065. private static function _dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, $methodUS) {
  5066. if ($startDay == 31) {
  5067. --$startDay;
  5068. } elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !self::_isLeapYear($startYear))))) {
  5069. $startDay = 30;
  5070. }
  5071. if ($endDay == 31) {
  5072. if ($methodUS && $startDay != 30) {
  5073. $endDay = 1;
  5074. if ($endMonth == 12) {
  5075. ++$endYear;
  5076. $endMonth = 1;
  5077. } else {
  5078. ++$endMonth;
  5079. }
  5080. } else {
  5081. $endDay = 30;
  5082. }
  5083. }
  5084. return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
  5085. } // function _dateDiff360()
  5086. /**
  5087. * DAYS360
  5088. *
  5089. * @param long $startDate Excel date serial value or a standard date string
  5090. * @param long $endDate Excel date serial value or a standard date string
  5091. * @param boolean $method US or European Method
  5092. * @return long PHP date/time serial
  5093. */
  5094. public static function DAYS360($startDate = 0, $endDate = 0, $method = false) {
  5095. $startDate = self::flattenSingleValue($startDate);
  5096. $endDate = self::flattenSingleValue($endDate);
  5097. if (is_string($startDate = self::_getDateValue($startDate))) {
  5098. return self::$_errorCodes['value'];
  5099. }
  5100. if (is_string($endDate = self::_getDateValue($endDate))) {
  5101. return self::$_errorCodes['value'];
  5102. }
  5103. // Execute function
  5104. $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
  5105. $startDay = $PHPStartDateObject->format('j');
  5106. $startMonth = $PHPStartDateObject->format('n');
  5107. $startYear = $PHPStartDateObject->format('Y');
  5108. $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  5109. $endDay = $PHPEndDateObject->format('j');
  5110. $endMonth = $PHPEndDateObject->format('n');
  5111. $endYear = $PHPEndDateObject->format('Y');
  5112. return self::_dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
  5113. } // function DAYS360()
  5114. /**
  5115. * DATEDIF
  5116. *
  5117. * @param long $startDate Excel date serial value or a standard date string
  5118. * @param long $endDate Excel date serial value or a standard date string
  5119. * @param string $unit
  5120. * @return long Interval between the dates
  5121. */
  5122. public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D') {
  5123. $startDate = self::flattenSingleValue($startDate);
  5124. $endDate = self::flattenSingleValue($endDate);
  5125. $unit = strtoupper(self::flattenSingleValue($unit));
  5126. if (is_string($startDate = self::_getDateValue($startDate))) {
  5127. return self::$_errorCodes['value'];
  5128. }
  5129. if (is_string($endDate = self::_getDateValue($endDate))) {
  5130. return self::$_errorCodes['value'];
  5131. }
  5132. // Validate parameters
  5133. if ($startDate >= $endDate) {
  5134. return self::$_errorCodes['num'];
  5135. }
  5136. // Execute function
  5137. $difference = $endDate - $startDate;
  5138. $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
  5139. $startDays = $PHPStartDateObject->format('j');
  5140. $startMonths = $PHPStartDateObject->format('n');
  5141. $startYears = $PHPStartDateObject->format('Y');
  5142. $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  5143. $endDays = $PHPEndDateObject->format('j');
  5144. $endMonths = $PHPEndDateObject->format('n');
  5145. $endYears = $PHPEndDateObject->format('Y');
  5146. $retVal = self::$_errorCodes['num'];
  5147. switch ($unit) {
  5148. case 'D':
  5149. $retVal = intval($difference);
  5150. break;
  5151. case 'M':
  5152. $retVal = intval($endMonths - $startMonths) + (intval($endYears - $startYears) * 12);
  5153. // We're only interested in full months
  5154. if ($endDays < $startDays) {
  5155. --$retVal;
  5156. }
  5157. break;
  5158. case 'Y':
  5159. $retVal = intval($endYears - $startYears);
  5160. // We're only interested in full months
  5161. if ($endMonths < $startMonths) {
  5162. --$retVal;
  5163. } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) {
  5164. --$retVal;
  5165. }
  5166. break;
  5167. case 'MD':
  5168. if ($endDays < $startDays) {
  5169. $retVal = $endDays;
  5170. $PHPEndDateObject->modify('-'.$endDays.' days');
  5171. $adjustDays = $PHPEndDateObject->format('j');
  5172. if ($adjustDays > $startDays) {
  5173. $retVal += ($adjustDays - $startDays);
  5174. }
  5175. } else {
  5176. $retVal = $endDays - $startDays;
  5177. }
  5178. break;
  5179. case 'YM':
  5180. $retVal = intval($endMonths - $startMonths);
  5181. if ($retVal < 0) $retVal = 12 + $retVal;
  5182. // We're only interested in full months
  5183. if ($endDays < $startDays) {
  5184. --$retVal;
  5185. }
  5186. break;
  5187. case 'YD':
  5188. $retVal = intval($difference);
  5189. if ($endYears > $startYears) {
  5190. while ($endYears > $startYears) {
  5191. $PHPEndDateObject->modify('-1 year');
  5192. $endYears = $PHPEndDateObject->format('Y');
  5193. }
  5194. $retVal = $PHPEndDateObject->format('z') - $PHPStartDateObject->format('z');
  5195. if ($retVal < 0) { $retVal += 365; }
  5196. }
  5197. break;
  5198. }
  5199. return $retVal;
  5200. } // function DATEDIF()
  5201. /**
  5202. * YEARFRAC
  5203. *
  5204. * Calculates the fraction of the year represented by the number of whole days between two dates (the start_date and the
  5205. * end_date). Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or obligations
  5206. * to assign to a specific term.
  5207. *
  5208. * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer) or date object, or a standard date string
  5209. * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer) or date object, or a standard date string
  5210. * @param integer $method Method used for the calculation
  5211. * 0 or omitted US (NASD) 30/360
  5212. * 1 Actual/actual
  5213. * 2 Actual/360
  5214. * 3 Actual/365
  5215. * 4 European 30/360
  5216. * @return float fraction of the year
  5217. */
  5218. public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0) {
  5219. $startDate = self::flattenSingleValue($startDate);
  5220. $endDate = self::flattenSingleValue($endDate);
  5221. $method = self::flattenSingleValue($method);
  5222. if (is_string($startDate = self::_getDateValue($startDate))) {
  5223. return self::$_errorCodes['value'];
  5224. }
  5225. if (is_string($endDate = self::_getDateValue($endDate))) {
  5226. return self::$_errorCodes['value'];
  5227. }
  5228. if ((is_numeric($method)) && (!is_string($method))) {
  5229. switch($method) {
  5230. case 0 :
  5231. return self::DAYS360($startDate,$endDate) / 360;
  5232. break;
  5233. case 1 :
  5234. $startYear = self::YEAR($startDate);
  5235. $endYear = self::YEAR($endDate);
  5236. $leapDay = 0;
  5237. if (self::_isLeapYear($startYear) || self::_isLeapYear($endYear)) {
  5238. $leapDay = 1;
  5239. }
  5240. return self::DATEDIF($startDate,$endDate) / (365 + $leapDay);
  5241. break;
  5242. case 2 :
  5243. return self::DATEDIF($startDate,$endDate) / 360;
  5244. break;
  5245. case 3 :
  5246. return self::DATEDIF($startDate,$endDate) / 365;
  5247. break;
  5248. case 4 :
  5249. return self::DAYS360($startDate,$endDate,True) / 360;
  5250. break;
  5251. }
  5252. }
  5253. return self::$_errorCodes['value'];
  5254. } // function YEARFRAC()
  5255. /**
  5256. * NETWORKDAYS
  5257. *
  5258. * @param mixed Start date
  5259. * @param mixed End date
  5260. * @param array of mixed Optional Date Series
  5261. * @return long Interval between the dates
  5262. */
  5263. public static function NETWORKDAYS($startDate,$endDate) {
  5264. // Flush the mandatory start and end date that are referenced in the function definition
  5265. $dateArgs = self::flattenArray(func_get_args());
  5266. array_shift($dateArgs);
  5267. array_shift($dateArgs);
  5268. // Validate the start and end dates
  5269. if (is_string($startDate = $sDate = self::_getDateValue($startDate))) {
  5270. return self::$_errorCodes['value'];
  5271. }
  5272. if (is_string($endDate = $eDate = self::_getDateValue($endDate))) {
  5273. return self::$_errorCodes['value'];
  5274. }
  5275. if ($sDate > $eDate) {
  5276. $startDate = $eDate;
  5277. $endDate = $sDate;
  5278. }
  5279. // Execute function
  5280. $startDoW = 6 - self::DAYOFWEEK($startDate,2);
  5281. if ($startDoW < 0) { $startDoW = 0; }
  5282. $endDoW = self::DAYOFWEEK($endDate,2);
  5283. if ($endDoW >= 6) { $endDoW = 0; }
  5284. $wholeWeekDays = floor(($endDate - $startDate) / 7) * 5;
  5285. $partWeekDays = $endDoW + $startDoW;
  5286. if ($partWeekDays > 5) {
  5287. $partWeekDays -= 5;
  5288. }
  5289. // Test any extra holiday parameters
  5290. $holidayCountedArray = array();
  5291. foreach ($dateArgs as $holidayDate) {
  5292. if (is_string($holidayDate = self::_getDateValue($holidayDate))) {
  5293. return self::$_errorCodes['value'];
  5294. }
  5295. if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
  5296. if ((self::DAYOFWEEK($holidayDate,2) < 6) && (!in_array($holidayDate,$holidayCountedArray))) {
  5297. --$partWeekDays;
  5298. $holidayCountedArray[] = $holidayDate;
  5299. }
  5300. }
  5301. }
  5302. if ($sDate > $eDate) {
  5303. return 0 - ($wholeWeekDays + $partWeekDays);
  5304. }
  5305. return $wholeWeekDays + $partWeekDays;
  5306. } // function NETWORKDAYS()
  5307. /**
  5308. * WORKDAY
  5309. *
  5310. * @param mixed Start date
  5311. * @param mixed number of days for adjustment
  5312. * @param array of mixed Optional Date Series
  5313. * @return long Interval between the dates
  5314. */
  5315. public static function WORKDAY($startDate,$endDays) {
  5316. $dateArgs = self::flattenArray(func_get_args());
  5317. array_shift($dateArgs);
  5318. array_shift($dateArgs);
  5319. if (is_string($startDate = self::_getDateValue($startDate))) {
  5320. return self::$_errorCodes['value'];
  5321. }
  5322. if (!is_numeric($endDays)) {
  5323. return self::$_errorCodes['value'];
  5324. }
  5325. $endDate = (float) $startDate + (floor($endDays / 5) * 7) + ($endDays % 5);
  5326. if ($endDays < 0) {
  5327. $endDate += 7;
  5328. }
  5329. $endDoW = self::DAYOFWEEK($endDate,3);
  5330. if ($endDoW >= 5) {
  5331. if ($endDays >= 0) {
  5332. $endDate += (7 - $endDoW);
  5333. } else {
  5334. $endDate -= ($endDoW - 5);
  5335. }
  5336. }
  5337. // Test any extra holiday parameters
  5338. if (count($dateArgs) > 0) {
  5339. $holidayCountedArray = $holidayDates = array();
  5340. foreach ($dateArgs as $holidayDate) {
  5341. if (is_string($holidayDate = self::_getDateValue($holidayDate))) {
  5342. return self::$_errorCodes['value'];
  5343. }
  5344. $holidayDates[] = $holidayDate;
  5345. }
  5346. if ($endDays >= 0) {
  5347. sort($holidayDates, SORT_NUMERIC);
  5348. } else {
  5349. rsort($holidayDates, SORT_NUMERIC);
  5350. }
  5351. foreach ($holidayDates as $holidayDate) {
  5352. if ($endDays >= 0) {
  5353. if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
  5354. if ((self::DAYOFWEEK($holidayDate,2) < 6) && (!in_array($holidayDate,$holidayCountedArray))) {
  5355. ++$endDate;
  5356. $holidayCountedArray[] = $holidayDate;
  5357. }
  5358. }
  5359. } else {
  5360. if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
  5361. if ((self::DAYOFWEEK($holidayDate,2) < 6) && (!in_array($holidayDate,$holidayCountedArray))) {
  5362. --$endDate;
  5363. $holidayCountedArray[] = $holidayDate;
  5364. }
  5365. }
  5366. }
  5367. $endDoW = self::DAYOFWEEK($endDate,3);
  5368. if ($endDoW >= 5) {
  5369. if ($endDays >= 0) {
  5370. $endDate += (7 - $endDoW);
  5371. } else {
  5372. $endDate -= ($endDoW - 5);
  5373. }
  5374. }
  5375. }
  5376. }
  5377. switch (self::getReturnDateType()) {
  5378. case self::RETURNDATE_EXCEL : return (float) $endDate;
  5379. break;
  5380. case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP($endDate);
  5381. break;
  5382. case self::RETURNDATE_PHP_OBJECT : return PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  5383. break;
  5384. }
  5385. } // function WORKDAY()
  5386. /**
  5387. * DAYOFMONTH
  5388. *
  5389. * @param long $dateValue Excel date serial value or a standard date string
  5390. * @return int Day
  5391. */
  5392. public static function DAYOFMONTH($dateValue = 1) {
  5393. $dateValue = self::flattenSingleValue($dateValue);
  5394. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5395. return self::$_errorCodes['value'];
  5396. } elseif ($dateValue < 0.0) {
  5397. return self::$_errorCodes['num'];
  5398. }
  5399. // Execute function
  5400. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  5401. return (int) $PHPDateObject->format('j');
  5402. } // function DAYOFMONTH()
  5403. /**
  5404. * DAYOFWEEK
  5405. *
  5406. * @param long $dateValue Excel date serial value or a standard date string
  5407. * @return int Day
  5408. */
  5409. public static function DAYOFWEEK($dateValue = 1, $style = 1) {
  5410. $dateValue = self::flattenSingleValue($dateValue);
  5411. $style = floor(self::flattenSingleValue($style));
  5412. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5413. return self::$_errorCodes['value'];
  5414. } elseif ($dateValue < 0.0) {
  5415. return self::$_errorCodes['num'];
  5416. }
  5417. // Execute function
  5418. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  5419. $DoW = $PHPDateObject->format('w');
  5420. $firstDay = 1;
  5421. switch ($style) {
  5422. case 1: ++$DoW;
  5423. break;
  5424. case 2: if ($DoW == 0) { $DoW = 7; }
  5425. break;
  5426. case 3: if ($DoW == 0) { $DoW = 7; }
  5427. $firstDay = 0;
  5428. --$DoW;
  5429. break;
  5430. default:
  5431. }
  5432. if (self::$compatibilityMode == self::COMPATIBILITY_EXCEL) {
  5433. // Test for Excel's 1900 leap year, and introduce the error as required
  5434. if (($PHPDateObject->format('Y') == 1900) && ($PHPDateObject->format('n') <= 2)) {
  5435. --$DoW;
  5436. if ($DoW < $firstDay) {
  5437. $DoW += 7;
  5438. }
  5439. }
  5440. }
  5441. return (int) $DoW;
  5442. } // function DAYOFWEEK()
  5443. /**
  5444. * WEEKOFYEAR
  5445. *
  5446. * @param long $dateValue Excel date serial value or a standard date string
  5447. * @param boolean $method Week begins on Sunday or Monday
  5448. * @return int Week Number
  5449. */
  5450. public static function WEEKOFYEAR($dateValue = 1, $method = 1) {
  5451. $dateValue = self::flattenSingleValue($dateValue);
  5452. $method = floor(self::flattenSingleValue($method));
  5453. if (!is_numeric($method)) {
  5454. return self::$_errorCodes['value'];
  5455. } elseif (($method < 1) || ($method > 2)) {
  5456. return self::$_errorCodes['num'];
  5457. }
  5458. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5459. return self::$_errorCodes['value'];
  5460. } elseif ($dateValue < 0.0) {
  5461. return self::$_errorCodes['num'];
  5462. }
  5463. // Execute function
  5464. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  5465. $dayOfYear = $PHPDateObject->format('z');
  5466. $dow = $PHPDateObject->format('w');
  5467. $PHPDateObject->modify('-'.$dayOfYear.' days');
  5468. $dow = $PHPDateObject->format('w');
  5469. $daysInFirstWeek = 7 - (($dow + (2 - $method)) % 7);
  5470. $dayOfYear -= $daysInFirstWeek;
  5471. $weekOfYear = ceil($dayOfYear / 7) + 1;
  5472. return (int) $weekOfYear;
  5473. } // function WEEKOFYEAR()
  5474. /**
  5475. * MONTHOFYEAR
  5476. *
  5477. * @param long $dateValue Excel date serial value or a standard date string
  5478. * @return int Month
  5479. */
  5480. public static function MONTHOFYEAR($dateValue = 1) {
  5481. $dateValue = self::flattenSingleValue($dateValue);
  5482. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5483. return self::$_errorCodes['value'];
  5484. } elseif ($dateValue < 0.0) {
  5485. return self::$_errorCodes['num'];
  5486. }
  5487. // Execute function
  5488. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  5489. return (int) $PHPDateObject->format('n');
  5490. } // function MONTHOFYEAR()
  5491. /**
  5492. * YEAR
  5493. *
  5494. * @param long $dateValue Excel date serial value or a standard date string
  5495. * @return int Year
  5496. */
  5497. public static function YEAR($dateValue = 1) {
  5498. $dateValue = self::flattenSingleValue($dateValue);
  5499. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5500. return self::$_errorCodes['value'];
  5501. } elseif ($dateValue < 0.0) {
  5502. return self::$_errorCodes['num'];
  5503. }
  5504. // Execute function
  5505. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  5506. return (int) $PHPDateObject->format('Y');
  5507. } // function YEAR()
  5508. /**
  5509. * HOUROFDAY
  5510. *
  5511. * @param mixed $timeValue Excel time serial value or a standard time string
  5512. * @return int Hour
  5513. */
  5514. public static function HOUROFDAY($timeValue = 0) {
  5515. $timeValue = self::flattenSingleValue($timeValue);
  5516. if (!is_numeric($timeValue)) {
  5517. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  5518. $testVal = strtok($timeValue,'/-: ');
  5519. if (strlen($testVal) < strlen($timeValue)) {
  5520. return self::$_errorCodes['value'];
  5521. }
  5522. }
  5523. $timeValue = self::_getTimeValue($timeValue);
  5524. if (is_string($timeValue)) {
  5525. return self::$_errorCodes['value'];
  5526. }
  5527. }
  5528. // Execute function
  5529. if ($timeValue >= 1) {
  5530. $timeValue = fmod($timeValue,1);
  5531. } elseif ($timeValue < 0.0) {
  5532. return self::$_errorCodes['num'];
  5533. }
  5534. $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
  5535. return (int) gmdate('G',$timeValue);
  5536. } // function HOUROFDAY()
  5537. /**
  5538. * MINUTEOFHOUR
  5539. *
  5540. * @param long $timeValue Excel time serial value or a standard time string
  5541. * @return int Minute
  5542. */
  5543. public static function MINUTEOFHOUR($timeValue = 0) {
  5544. $timeValue = $timeTester = self::flattenSingleValue($timeValue);
  5545. if (!is_numeric($timeValue)) {
  5546. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  5547. $testVal = strtok($timeValue,'/-: ');
  5548. if (strlen($testVal) < strlen($timeValue)) {
  5549. return self::$_errorCodes['value'];
  5550. }
  5551. }
  5552. $timeValue = self::_getTimeValue($timeValue);
  5553. if (is_string($timeValue)) {
  5554. return self::$_errorCodes['value'];
  5555. }
  5556. }
  5557. // Execute function
  5558. if ($timeValue >= 1) {
  5559. $timeValue = fmod($timeValue,1);
  5560. } elseif ($timeValue < 0.0) {
  5561. return self::$_errorCodes['num'];
  5562. }
  5563. $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
  5564. return (int) gmdate('i',$timeValue);
  5565. } // function MINUTEOFHOUR()
  5566. /**
  5567. * SECONDOFMINUTE
  5568. *
  5569. * @param long $timeValue Excel time serial value or a standard time string
  5570. * @return int Second
  5571. */
  5572. public static function SECONDOFMINUTE($timeValue = 0) {
  5573. $timeValue = self::flattenSingleValue($timeValue);
  5574. if (!is_numeric($timeValue)) {
  5575. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  5576. $testVal = strtok($timeValue,'/-: ');
  5577. if (strlen($testVal) < strlen($timeValue)) {
  5578. return self::$_errorCodes['value'];
  5579. }
  5580. }
  5581. $timeValue = self::_getTimeValue($timeValue);
  5582. if (is_string($timeValue)) {
  5583. return self::$_errorCodes['value'];
  5584. }
  5585. }
  5586. // Execute function
  5587. if ($timeValue >= 1) {
  5588. $timeValue = fmod($timeValue,1);
  5589. } elseif ($timeValue < 0.0) {
  5590. return self::$_errorCodes['num'];
  5591. }
  5592. $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
  5593. return (int) gmdate('s',$timeValue);
  5594. } // function SECONDOFMINUTE()
  5595. private static function _adjustDateByMonths($dateValue = 0, $adjustmentMonths = 0) {
  5596. // Execute function
  5597. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  5598. $oMonth = (int) $PHPDateObject->format('m');
  5599. $oYear = (int) $PHPDateObject->format('Y');
  5600. $adjustmentMonthsString = (string) $adjustmentMonths;
  5601. if ($adjustmentMonths > 0) {
  5602. $adjustmentMonthsString = '+'.$adjustmentMonths;
  5603. }
  5604. if ($adjustmentMonths != 0) {
  5605. $PHPDateObject->modify($adjustmentMonthsString.' months');
  5606. }
  5607. $nMonth = (int) $PHPDateObject->format('m');
  5608. $nYear = (int) $PHPDateObject->format('Y');
  5609. $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
  5610. if ($monthDiff != $adjustmentMonths) {
  5611. $adjustDays = (int) $PHPDateObject->format('d');
  5612. $adjustDaysString = '-'.$adjustDays.' days';
  5613. $PHPDateObject->modify($adjustDaysString);
  5614. }
  5615. return $PHPDateObject;
  5616. } // function _adjustDateByMonths()
  5617. /**
  5618. * EDATE
  5619. *
  5620. * Returns the serial number that represents the date that is the indicated number of months before or after a specified date
  5621. * (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.
  5622. *
  5623. * @param long $dateValue Excel date serial value or a standard date string
  5624. * @param int $adjustmentMonths Number of months to adjust by
  5625. * @return long Excel date serial value
  5626. */
  5627. public static function EDATE($dateValue = 1, $adjustmentMonths = 0) {
  5628. $dateValue = self::flattenSingleValue($dateValue);
  5629. $adjustmentMonths = floor(self::flattenSingleValue($adjustmentMonths));
  5630. if (!is_numeric($adjustmentMonths)) {
  5631. return self::$_errorCodes['value'];
  5632. }
  5633. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5634. return self::$_errorCodes['value'];
  5635. }
  5636. // Execute function
  5637. $PHPDateObject = self::_adjustDateByMonths($dateValue,$adjustmentMonths);
  5638. switch (self::getReturnDateType()) {
  5639. case self::RETURNDATE_EXCEL : return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
  5640. break;
  5641. case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
  5642. break;
  5643. case self::RETURNDATE_PHP_OBJECT : return $PHPDateObject;
  5644. break;
  5645. }
  5646. } // function EDATE()
  5647. /**
  5648. * EOMONTH
  5649. *
  5650. * Returns the serial number for the last day of the month that is the indicated number of months before or after start_date.
  5651. * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
  5652. *
  5653. * @param long $dateValue Excel date serial value or a standard date string
  5654. * @param int $adjustmentMonths Number of months to adjust by
  5655. * @return long Excel date serial value
  5656. */
  5657. public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0) {
  5658. $dateValue = self::flattenSingleValue($dateValue);
  5659. $adjustmentMonths = floor(self::flattenSingleValue($adjustmentMonths));
  5660. if (!is_numeric($adjustmentMonths)) {
  5661. return self::$_errorCodes['value'];
  5662. }
  5663. if (is_string($dateValue = self::_getDateValue($dateValue))) {
  5664. return self::$_errorCodes['value'];
  5665. }
  5666. // Execute function
  5667. $PHPDateObject = self::_adjustDateByMonths($dateValue,$adjustmentMonths+1);
  5668. $adjustDays = (int) $PHPDateObject->format('d');
  5669. $adjustDaysString = '-'.$adjustDays.' days';
  5670. $PHPDateObject->modify($adjustDaysString);
  5671. switch (self::getReturnDateType()) {
  5672. case self::RETURNDATE_EXCEL : return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
  5673. break;
  5674. case self::RETURNDATE_PHP_NUMERIC : return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
  5675. break;
  5676. case self::RETURNDATE_PHP_OBJECT : return $PHPDateObject;
  5677. break;
  5678. }
  5679. } // function EOMONTH()
  5680. /**
  5681. * TRUNC
  5682. *
  5683. * Truncates value to the number of fractional digits by number_digits.
  5684. *
  5685. * @param float $value
  5686. * @param int $number_digits
  5687. * @return float Truncated value
  5688. */
  5689. public static function TRUNC($value = 0, $number_digits = 0) {
  5690. $value = self::flattenSingleValue($value);
  5691. $number_digits = self::flattenSingleValue($number_digits);
  5692. // Validate parameters
  5693. if ($number_digits < 0) {
  5694. return self::$_errorCodes['value'];
  5695. }
  5696. // Truncate
  5697. if ($number_digits > 0) {
  5698. $value = $value * pow(10, $number_digits);
  5699. }
  5700. $value = intval($value);
  5701. if ($number_digits > 0) {
  5702. $value = $value / pow(10, $number_digits);
  5703. }
  5704. // Return
  5705. return $value;
  5706. } // function TRUNC()
  5707. /**
  5708. * POWER
  5709. *
  5710. * Computes x raised to the power y.
  5711. *
  5712. * @param float $x
  5713. * @param float $y
  5714. * @return float
  5715. */
  5716. public static function POWER($x = 0, $y = 2) {
  5717. $x = self::flattenSingleValue($x);
  5718. $y = self::flattenSingleValue($y);
  5719. // Validate parameters
  5720. if ($x == 0 && $y <= 0) {
  5721. return self::$_errorCodes['divisionbyzero'];
  5722. }
  5723. // Return
  5724. return pow($x, $y);
  5725. } // function POWER()
  5726. private static function _nbrConversionFormat($xVal,$places) {
  5727. if (!is_null($places)) {
  5728. if (strlen($xVal) <= $places) {
  5729. return substr(str_pad($xVal,$places,'0',STR_PAD_LEFT),-10);
  5730. } else {
  5731. return self::$_errorCodes['num'];
  5732. }
  5733. }
  5734. return substr($xVal,-10);
  5735. } // function _nbrConversionFormat()
  5736. /**
  5737. * BINTODEC
  5738. *
  5739. * Return a binary value as Decimal.
  5740. *
  5741. * @param string $x
  5742. * @return string
  5743. */
  5744. public static function BINTODEC($x) {
  5745. $x = self::flattenSingleValue($x);
  5746. if (is_bool($x)) {
  5747. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  5748. $x = (int) $x;
  5749. } else {
  5750. return self::$_errorCodes['value'];
  5751. }
  5752. }
  5753. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  5754. $x = floor($x);
  5755. }
  5756. $x = (string) $x;
  5757. if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
  5758. return self::$_errorCodes['num'];
  5759. }
  5760. if (strlen($x) > 10) {
  5761. return self::$_errorCodes['num'];
  5762. } elseif (strlen($x) == 10) {
  5763. // Two's Complement
  5764. $x = substr($x,-9);
  5765. return '-'.(512-bindec($x));
  5766. }
  5767. return bindec($x);
  5768. } // function BINTODEC()
  5769. /**
  5770. * BINTOHEX
  5771. *
  5772. * Return a binary value as Hex.
  5773. *
  5774. * @param string $x
  5775. * @return string
  5776. */
  5777. public static function BINTOHEX($x, $places=null) {
  5778. $x = floor(self::flattenSingleValue($x));
  5779. $places = self::flattenSingleValue($places);
  5780. if (is_bool($x)) {
  5781. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  5782. $x = (int) $x;
  5783. } else {
  5784. return self::$_errorCodes['value'];
  5785. }
  5786. }
  5787. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  5788. $x = floor($x);
  5789. }
  5790. $x = (string) $x;
  5791. if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
  5792. return self::$_errorCodes['num'];
  5793. }
  5794. if (strlen($x) > 10) {
  5795. return self::$_errorCodes['num'];
  5796. } elseif (strlen($x) == 10) {
  5797. // Two's Complement
  5798. return str_repeat('F',8).substr(strtoupper(dechex(bindec(substr($x,-9)))),-2);
  5799. }
  5800. $hexVal = (string) strtoupper(dechex(bindec($x)));
  5801. return self::_nbrConversionFormat($hexVal,$places);
  5802. } // function BINTOHEX()
  5803. /**
  5804. * BINTOOCT
  5805. *
  5806. * Return a binary value as Octal.
  5807. *
  5808. * @param string $x
  5809. * @return string
  5810. */
  5811. public static function BINTOOCT($x, $places=null) {
  5812. $x = floor(self::flattenSingleValue($x));
  5813. $places = self::flattenSingleValue($places);
  5814. if (is_bool($x)) {
  5815. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  5816. $x = (int) $x;
  5817. } else {
  5818. return self::$_errorCodes['value'];
  5819. }
  5820. }
  5821. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  5822. $x = floor($x);
  5823. }
  5824. $x = (string) $x;
  5825. if (strlen($x) > preg_match_all('/[01]/',$x,$out)) {
  5826. return self::$_errorCodes['num'];
  5827. }
  5828. if (strlen($x) > 10) {
  5829. return self::$_errorCodes['num'];
  5830. } elseif (strlen($x) == 10) {
  5831. // Two's Complement
  5832. return str_repeat('7',7).substr(strtoupper(decoct(bindec(substr($x,-9)))),-3);
  5833. }
  5834. $octVal = (string) decoct(bindec($x));
  5835. return self::_nbrConversionFormat($octVal,$places);
  5836. } // function BINTOOCT()
  5837. /**
  5838. * DECTOBIN
  5839. *
  5840. * Return an octal value as binary.
  5841. *
  5842. * @param string $x
  5843. * @return string
  5844. */
  5845. public static function DECTOBIN($x, $places=null) {
  5846. $x = self::flattenSingleValue($x);
  5847. $places = self::flattenSingleValue($places);
  5848. if (is_bool($x)) {
  5849. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  5850. $x = (int) $x;
  5851. } else {
  5852. return self::$_errorCodes['value'];
  5853. }
  5854. }
  5855. $x = (string) $x;
  5856. if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
  5857. return self::$_errorCodes['value'];
  5858. }
  5859. $x = (string) floor($x);
  5860. $r = decbin($x);
  5861. if (strlen($r) == 32) {
  5862. // Two's Complement
  5863. $r = substr($r,-10);
  5864. } elseif (strlen($r) > 11) {
  5865. return self::$_errorCodes['num'];
  5866. }
  5867. return self::_nbrConversionFormat($r,$places);
  5868. } // function DECTOBIN()
  5869. /**
  5870. * DECTOOCT
  5871. *
  5872. * Return an octal value as binary.
  5873. *
  5874. * @param string $x
  5875. * @return string
  5876. */
  5877. public static function DECTOOCT($x, $places=null) {
  5878. $x = self::flattenSingleValue($x);
  5879. $places = self::flattenSingleValue($places);
  5880. if (is_bool($x)) {
  5881. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  5882. $x = (int) $x;
  5883. } else {
  5884. return self::$_errorCodes['value'];
  5885. }
  5886. }
  5887. $x = (string) $x;
  5888. if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
  5889. return self::$_errorCodes['value'];
  5890. }
  5891. $x = (string) floor($x);
  5892. $r = decoct($x);
  5893. if (strlen($r) == 11) {
  5894. // Two's Complement
  5895. $r = substr($r,-10);
  5896. }
  5897. return self::_nbrConversionFormat($r,$places);
  5898. } // function DECTOOCT()
  5899. /**
  5900. * DECTOHEX
  5901. *
  5902. * Return an octal value as binary.
  5903. *
  5904. * @param string $x
  5905. * @return string
  5906. */
  5907. public static function DECTOHEX($x, $places=null) {
  5908. $x = self::flattenSingleValue($x);
  5909. $places = self::flattenSingleValue($places);
  5910. if (is_bool($x)) {
  5911. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  5912. $x = (int) $x;
  5913. } else {
  5914. return self::$_errorCodes['value'];
  5915. }
  5916. }
  5917. $x = (string) $x;
  5918. if (strlen($x) > preg_match_all('/[-0123456789.]/',$x,$out)) {
  5919. return self::$_errorCodes['value'];
  5920. }
  5921. $x = (string) floor($x);
  5922. $r = strtoupper(dechex($x));
  5923. if (strlen($r) == 8) {
  5924. // Two's Complement
  5925. $r = 'FF'.$r;
  5926. }
  5927. return self::_nbrConversionFormat($r,$places);
  5928. } // function DECTOHEX()
  5929. /**
  5930. * HEXTOBIN
  5931. *
  5932. * Return a hex value as binary.
  5933. *
  5934. * @param string $x
  5935. * @return string
  5936. */
  5937. public static function HEXTOBIN($x, $places=null) {
  5938. $x = self::flattenSingleValue($x);
  5939. $places = self::flattenSingleValue($places);
  5940. if (is_bool($x)) {
  5941. return self::$_errorCodes['value'];
  5942. }
  5943. $x = (string) $x;
  5944. if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
  5945. return self::$_errorCodes['num'];
  5946. }
  5947. $binVal = decbin(hexdec($x));
  5948. return substr(self::_nbrConversionFormat($binVal,$places),-10);
  5949. } // function HEXTOBIN()
  5950. /**
  5951. * HEXTOOCT
  5952. *
  5953. * Return a hex value as octal.
  5954. *
  5955. * @param string $x
  5956. * @return string
  5957. */
  5958. public static function HEXTOOCT($x, $places=null) {
  5959. $x = self::flattenSingleValue($x);
  5960. $places = self::flattenSingleValue($places);
  5961. if (is_bool($x)) {
  5962. return self::$_errorCodes['value'];
  5963. }
  5964. $x = (string) $x;
  5965. if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
  5966. return self::$_errorCodes['num'];
  5967. }
  5968. $octVal = decoct(hexdec($x));
  5969. return self::_nbrConversionFormat($octVal,$places);
  5970. } // function HEXTOOCT()
  5971. /**
  5972. * HEXTODEC
  5973. *
  5974. * Return a hex value as octal.
  5975. *
  5976. * @param string $x
  5977. * @return string
  5978. */
  5979. public static function HEXTODEC($x) {
  5980. $x = self::flattenSingleValue($x);
  5981. if (is_bool($x)) {
  5982. return self::$_errorCodes['value'];
  5983. }
  5984. $x = (string) $x;
  5985. if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
  5986. return self::$_errorCodes['num'];
  5987. }
  5988. return hexdec($x);
  5989. } // function HEXTODEC()
  5990. /**
  5991. * OCTTOBIN
  5992. *
  5993. * Return an octal value as binary.
  5994. *
  5995. * @param string $x
  5996. * @return string
  5997. */
  5998. public static function OCTTOBIN($x, $places=null) {
  5999. $x = self::flattenSingleValue($x);
  6000. $places = self::flattenSingleValue($places);
  6001. if (is_bool($x)) {
  6002. return self::$_errorCodes['value'];
  6003. }
  6004. $x = (string) $x;
  6005. if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
  6006. return self::$_errorCodes['num'];
  6007. }
  6008. $r = decbin(octdec($x));
  6009. return self::_nbrConversionFormat($r,$places);
  6010. } // function OCTTOBIN()
  6011. /**
  6012. * OCTTODEC
  6013. *
  6014. * Return an octal value as binary.
  6015. *
  6016. * @param string $x
  6017. * @return string
  6018. */
  6019. public static function OCTTODEC($x) {
  6020. $x = self::flattenSingleValue($x);
  6021. if (is_bool($x)) {
  6022. return self::$_errorCodes['value'];
  6023. }
  6024. $x = (string) $x;
  6025. if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
  6026. return self::$_errorCodes['num'];
  6027. }
  6028. return octdec($x);
  6029. } // function OCTTODEC()
  6030. /**
  6031. * OCTTOHEX
  6032. *
  6033. * Return an octal value as hex.
  6034. *
  6035. * @param string $x
  6036. * @return string
  6037. */
  6038. public static function OCTTOHEX($x, $places=null) {
  6039. $x = self::flattenSingleValue($x);
  6040. $places = self::flattenSingleValue($places);
  6041. if (is_bool($x)) {
  6042. return self::$_errorCodes['value'];
  6043. }
  6044. $x = (string) $x;
  6045. if (preg_match_all('/[01234567]/',$x,$out) != strlen($x)) {
  6046. return self::$_errorCodes['num'];
  6047. }
  6048. $hexVal = strtoupper(dechex(octdec($x)));
  6049. return self::_nbrConversionFormat($hexVal,$places);
  6050. } // function OCTTOHEX()
  6051. public static function _parseComplex($complexNumber) {
  6052. $workString = (string) $complexNumber;
  6053. $realNumber = $imaginary = 0;
  6054. // Extract the suffix, if there is one
  6055. $suffix = substr($workString,-1);
  6056. if (!is_numeric($suffix)) {
  6057. $workString = substr($workString,0,-1);
  6058. } else {
  6059. $suffix = '';
  6060. }
  6061. // Split the input into its Real and Imaginary components
  6062. $leadingSign = 0;
  6063. if (strlen($workString) > 0) {
  6064. $leadingSign = (($workString{0} == '+') || ($workString{0} == '-')) ? 1 : 0;
  6065. }
  6066. $power = '';
  6067. $realNumber = strtok($workString, '+-');
  6068. if (strtoupper(substr($realNumber,-1)) == 'E') {
  6069. $power = strtok('+-');
  6070. ++$leadingSign;
  6071. }
  6072. $realNumber = substr($workString,0,strlen($realNumber)+strlen($power)+$leadingSign);
  6073. if ($suffix != '') {
  6074. $imaginary = substr($workString,strlen($realNumber));
  6075. if (($imaginary == '') && (($realNumber == '') || ($realNumber == '+') || ($realNumber == '-'))) {
  6076. $imaginary = $realNumber.'1';
  6077. $realNumber = '0';
  6078. } else if ($imaginary == '') {
  6079. $imaginary = $realNumber;
  6080. $realNumber = '0';
  6081. } elseif (($imaginary == '+') || ($imaginary == '-')) {
  6082. $imaginary .= '1';
  6083. }
  6084. }
  6085. $complexArray = array( 'real' => $realNumber,
  6086. 'imaginary' => $imaginary,
  6087. 'suffix' => $suffix
  6088. );
  6089. return $complexArray;
  6090. } // function _parseComplex()
  6091. private static function _cleanComplex($complexNumber) {
  6092. if ($complexNumber{0} == '+') $complexNumber = substr($complexNumber,1);
  6093. if ($complexNumber{0} == '0') $complexNumber = substr($complexNumber,1);
  6094. if ($complexNumber{0} == '.') $complexNumber = '0'.$complexNumber;
  6095. if ($complexNumber{0} == '+') $complexNumber = substr($complexNumber,1);
  6096. return $complexNumber;
  6097. }
  6098. /**
  6099. * COMPLEX
  6100. *
  6101. * returns a complex number of the form x + yi or x + yj.
  6102. *
  6103. * @param float $realNumber
  6104. * @param float $imaginary
  6105. * @param string $suffix
  6106. * @return string
  6107. */
  6108. public static function COMPLEX($realNumber=0.0, $imaginary=0.0, $suffix='i') {
  6109. $realNumber = self::flattenSingleValue($realNumber);
  6110. $imaginary = self::flattenSingleValue($imaginary);
  6111. $suffix = self::flattenSingleValue($suffix);
  6112. if (((is_numeric($realNumber)) && (is_numeric($imaginary))) &&
  6113. (($suffix == 'i') || ($suffix == 'j') || ($suffix == ''))) {
  6114. if ($realNumber == 0.0) {
  6115. if ($imaginary == 0.0) {
  6116. return (string) '0';
  6117. } elseif ($imaginary == 1.0) {
  6118. return (string) $suffix;
  6119. } elseif ($imaginary == -1.0) {
  6120. return (string) '-'.$suffix;
  6121. }
  6122. return (string) $imaginary.$suffix;
  6123. } elseif ($imaginary == 0.0) {
  6124. return (string) $realNumber;
  6125. } elseif ($imaginary == 1.0) {
  6126. return (string) $realNumber.'+'.$suffix;
  6127. } elseif ($imaginary == -1.0) {
  6128. return (string) $realNumber.'-'.$suffix;
  6129. }
  6130. if ($imaginary > 0) { $imaginary = (string) '+'.$imaginary; }
  6131. return (string) $realNumber.$imaginary.$suffix;
  6132. }
  6133. return self::$_errorCodes['value'];
  6134. } // function COMPLEX()
  6135. /**
  6136. * IMAGINARY
  6137. *
  6138. * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
  6139. *
  6140. * @param string $complexNumber
  6141. * @return real
  6142. */
  6143. public static function IMAGINARY($complexNumber) {
  6144. $complexNumber = self::flattenSingleValue($complexNumber);
  6145. $parsedComplex = self::_parseComplex($complexNumber);
  6146. if (!is_array($parsedComplex)) {
  6147. return $parsedComplex;
  6148. }
  6149. return $parsedComplex['imaginary'];
  6150. } // function IMAGINARY()
  6151. /**
  6152. * IMREAL
  6153. *
  6154. * Returns the real coefficient of a complex number in x + yi or x + yj text format.
  6155. *
  6156. * @param string $complexNumber
  6157. * @return real
  6158. */
  6159. public static function IMREAL($complexNumber) {
  6160. $complexNumber = self::flattenSingleValue($complexNumber);
  6161. $parsedComplex = self::_parseComplex($complexNumber);
  6162. if (!is_array($parsedComplex)) {
  6163. return $parsedComplex;
  6164. }
  6165. return $parsedComplex['real'];
  6166. } // function IMREAL()
  6167. /**
  6168. * IMABS
  6169. *
  6170. * Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.
  6171. *
  6172. * @param string $complexNumber
  6173. * @return real
  6174. */
  6175. public static function IMABS($complexNumber) {
  6176. $complexNumber = self::flattenSingleValue($complexNumber);
  6177. $parsedComplex = self::_parseComplex($complexNumber);
  6178. if (!is_array($parsedComplex)) {
  6179. return $parsedComplex;
  6180. }
  6181. return sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary']));
  6182. } // function IMABS()
  6183. /**
  6184. * IMARGUMENT
  6185. *
  6186. * 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.
  6187. *
  6188. * @param string $complexNumber
  6189. * @return string
  6190. */
  6191. public static function IMARGUMENT($complexNumber) {
  6192. $complexNumber = self::flattenSingleValue($complexNumber);
  6193. $parsedComplex = self::_parseComplex($complexNumber);
  6194. if (!is_array($parsedComplex)) {
  6195. return $parsedComplex;
  6196. }
  6197. if ($parsedComplex['real'] == 0.0) {
  6198. if ($parsedComplex['imaginary'] == 0.0) {
  6199. return 0.0;
  6200. } elseif($parsedComplex['imaginary'] < 0.0) {
  6201. return pi() / -2;
  6202. } else {
  6203. return pi() / 2;
  6204. }
  6205. } elseif ($parsedComplex['real'] > 0.0) {
  6206. return atan($parsedComplex['imaginary'] / $parsedComplex['real']);
  6207. } elseif ($parsedComplex['imaginary'] < 0.0) {
  6208. return 0 - (pi() - atan(abs($parsedComplex['imaginary']) / abs($parsedComplex['real'])));
  6209. } else {
  6210. return pi() - atan($parsedComplex['imaginary'] / abs($parsedComplex['real']));
  6211. }
  6212. } // function IMARGUMENT()
  6213. /**
  6214. * IMCONJUGATE
  6215. *
  6216. * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
  6217. *
  6218. * @param string $complexNumber
  6219. * @return string
  6220. */
  6221. public static function IMCONJUGATE($complexNumber) {
  6222. $complexNumber = self::flattenSingleValue($complexNumber);
  6223. $parsedComplex = self::_parseComplex($complexNumber);
  6224. if (!is_array($parsedComplex)) {
  6225. return $parsedComplex;
  6226. }
  6227. if ($parsedComplex['imaginary'] == 0.0) {
  6228. return $parsedComplex['real'];
  6229. } else {
  6230. return self::_cleanComplex(self::COMPLEX($parsedComplex['real'], 0 - $parsedComplex['imaginary'], $parsedComplex['suffix']));
  6231. }
  6232. } // function IMCONJUGATE()
  6233. /**
  6234. * IMCOS
  6235. *
  6236. * Returns the cosine of a complex number in x + yi or x + yj text format.
  6237. *
  6238. * @param string $complexNumber
  6239. * @return string
  6240. */
  6241. public static function IMCOS($complexNumber) {
  6242. $complexNumber = self::flattenSingleValue($complexNumber);
  6243. $parsedComplex = self::_parseComplex($complexNumber);
  6244. if (!is_array($parsedComplex)) {
  6245. return $parsedComplex;
  6246. }
  6247. if ($parsedComplex['imaginary'] == 0.0) {
  6248. return cos($parsedComplex['real']);
  6249. } else {
  6250. return self::IMCONJUGATE(self::COMPLEX(cos($parsedComplex['real']) * cosh($parsedComplex['imaginary']),sin($parsedComplex['real']) * sinh($parsedComplex['imaginary']),$parsedComplex['suffix']));
  6251. }
  6252. } // function IMCOS()
  6253. /**
  6254. * IMSIN
  6255. *
  6256. * Returns the sine of a complex number in x + yi or x + yj text format.
  6257. *
  6258. * @param string $complexNumber
  6259. * @return string
  6260. */
  6261. public static function IMSIN($complexNumber) {
  6262. $complexNumber = self::flattenSingleValue($complexNumber);
  6263. $parsedComplex = self::_parseComplex($complexNumber);
  6264. if (!is_array($parsedComplex)) {
  6265. return $parsedComplex;
  6266. }
  6267. if ($parsedComplex['imaginary'] == 0.0) {
  6268. return sin($parsedComplex['real']);
  6269. } else {
  6270. return self::COMPLEX(sin($parsedComplex['real']) * cosh($parsedComplex['imaginary']),cos($parsedComplex['real']) * sinh($parsedComplex['imaginary']),$parsedComplex['suffix']);
  6271. }
  6272. } // function IMSIN()
  6273. /**
  6274. * IMSQRT
  6275. *
  6276. * Returns the square root of a complex number in x + yi or x + yj text format.
  6277. *
  6278. * @param string $complexNumber
  6279. * @return string
  6280. */
  6281. public static function IMSQRT($complexNumber) {
  6282. $complexNumber = self::flattenSingleValue($complexNumber);
  6283. $parsedComplex = self::_parseComplex($complexNumber);
  6284. if (!is_array($parsedComplex)) {
  6285. return $parsedComplex;
  6286. }
  6287. $theta = self::IMARGUMENT($complexNumber);
  6288. $d1 = cos($theta / 2);
  6289. $d2 = sin($theta / 2);
  6290. $r = sqrt(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
  6291. if ($parsedComplex['suffix'] == '') {
  6292. return self::COMPLEX($d1 * $r,$d2 * $r);
  6293. } else {
  6294. return self::COMPLEX($d1 * $r,$d2 * $r,$parsedComplex['suffix']);
  6295. }
  6296. } // function IMSQRT()
  6297. /**
  6298. * IMLN
  6299. *
  6300. * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
  6301. *
  6302. * @param string $complexNumber
  6303. * @return string
  6304. */
  6305. public static function IMLN($complexNumber) {
  6306. $complexNumber = self::flattenSingleValue($complexNumber);
  6307. $parsedComplex = self::_parseComplex($complexNumber);
  6308. if (!is_array($parsedComplex)) {
  6309. return $parsedComplex;
  6310. }
  6311. if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
  6312. return self::$_errorCodes['num'];
  6313. }
  6314. $logR = log(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
  6315. $t = self::IMARGUMENT($complexNumber);
  6316. if ($parsedComplex['suffix'] == '') {
  6317. return self::COMPLEX($logR,$t);
  6318. } else {
  6319. return self::COMPLEX($logR,$t,$parsedComplex['suffix']);
  6320. }
  6321. } // function IMLN()
  6322. /**
  6323. * IMLOG10
  6324. *
  6325. * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
  6326. *
  6327. * @param string $complexNumber
  6328. * @return string
  6329. */
  6330. public static function IMLOG10($complexNumber) {
  6331. $complexNumber = self::flattenSingleValue($complexNumber);
  6332. $parsedComplex = self::_parseComplex($complexNumber);
  6333. if (!is_array($parsedComplex)) {
  6334. return $parsedComplex;
  6335. }
  6336. if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
  6337. return self::$_errorCodes['num'];
  6338. } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
  6339. return log10($parsedComplex['real']);
  6340. }
  6341. return self::IMPRODUCT(log10(EULER),self::IMLN($complexNumber));
  6342. } // function IMLOG10()
  6343. /**
  6344. * IMLOG2
  6345. *
  6346. * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
  6347. *
  6348. * @param string $complexNumber
  6349. * @return string
  6350. */
  6351. public static function IMLOG2($complexNumber) {
  6352. $complexNumber = self::flattenSingleValue($complexNumber);
  6353. $parsedComplex = self::_parseComplex($complexNumber);
  6354. if (!is_array($parsedComplex)) {
  6355. return $parsedComplex;
  6356. }
  6357. if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
  6358. return self::$_errorCodes['num'];
  6359. } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
  6360. return log($parsedComplex['real'],2);
  6361. }
  6362. return self::IMPRODUCT(log(EULER,2),self::IMLN($complexNumber));
  6363. } // function IMLOG2()
  6364. /**
  6365. * IMEXP
  6366. *
  6367. * Returns the exponential of a complex number in x + yi or x + yj text format.
  6368. *
  6369. * @param string $complexNumber
  6370. * @return string
  6371. */
  6372. public static function IMEXP($complexNumber) {
  6373. $complexNumber = self::flattenSingleValue($complexNumber);
  6374. $parsedComplex = self::_parseComplex($complexNumber);
  6375. if (!is_array($parsedComplex)) {
  6376. return $parsedComplex;
  6377. }
  6378. if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
  6379. return '1';
  6380. }
  6381. $e = exp($parsedComplex['real']);
  6382. $eX = $e * cos($parsedComplex['imaginary']);
  6383. $eY = $e * sin($parsedComplex['imaginary']);
  6384. if ($parsedComplex['suffix'] == '') {
  6385. return self::COMPLEX($eX,$eY);
  6386. } else {
  6387. return self::COMPLEX($eX,$eY,$parsedComplex['suffix']);
  6388. }
  6389. } // function IMEXP()
  6390. /**
  6391. * IMPOWER
  6392. *
  6393. * Returns a complex number in x + yi or x + yj text format raised to a power.
  6394. *
  6395. * @param string $complexNumber
  6396. * @return string
  6397. */
  6398. public static function IMPOWER($complexNumber,$realNumber) {
  6399. $complexNumber = self::flattenSingleValue($complexNumber);
  6400. $realNumber = self::flattenSingleValue($realNumber);
  6401. if (!is_numeric($realNumber)) {
  6402. return self::$_errorCodes['value'];
  6403. }
  6404. $parsedComplex = self::_parseComplex($complexNumber);
  6405. if (!is_array($parsedComplex)) {
  6406. return $parsedComplex;
  6407. }
  6408. $r = sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary']));
  6409. $rPower = pow($r,$realNumber);
  6410. $theta = self::IMARGUMENT($complexNumber) * $realNumber;
  6411. if ($theta == 0) {
  6412. return 1;
  6413. } elseif ($parsedComplex['imaginary'] == 0.0) {
  6414. return self::COMPLEX($rPower * cos($theta),$rPower * sin($theta),$parsedComplex['suffix']);
  6415. } else {
  6416. return self::COMPLEX($rPower * cos($theta),$rPower * sin($theta),$parsedComplex['suffix']);
  6417. }
  6418. } // function IMPOWER()
  6419. /**
  6420. * IMDIV
  6421. *
  6422. * Returns the quotient of two complex numbers in x + yi or x + yj text format.
  6423. *
  6424. * @param string $complexDividend
  6425. * @param string $complexDivisor
  6426. * @return real
  6427. */
  6428. public static function IMDIV($complexDividend,$complexDivisor) {
  6429. $complexDividend = self::flattenSingleValue($complexDividend);
  6430. $complexDivisor = self::flattenSingleValue($complexDivisor);
  6431. $parsedComplexDividend = self::_parseComplex($complexDividend);
  6432. if (!is_array($parsedComplexDividend)) {
  6433. return $parsedComplexDividend;
  6434. }
  6435. $parsedComplexDivisor = self::_parseComplex($complexDivisor);
  6436. if (!is_array($parsedComplexDivisor)) {
  6437. return $parsedComplexDividend;
  6438. }
  6439. if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] != '') &&
  6440. ($parsedComplexDividend['suffix'] != $parsedComplexDivisor['suffix'])) {
  6441. return self::$_errorCodes['num'];
  6442. }
  6443. if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] == '')) {
  6444. $parsedComplexDivisor['suffix'] = $parsedComplexDividend['suffix'];
  6445. }
  6446. $d1 = ($parsedComplexDividend['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['imaginary']);
  6447. $d2 = ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['real']) - ($parsedComplexDividend['real'] * $parsedComplexDivisor['imaginary']);
  6448. $d3 = ($parsedComplexDivisor['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDivisor['imaginary'] * $parsedComplexDivisor['imaginary']);
  6449. $r = $d1/$d3;
  6450. $i = $d2/$d3;
  6451. if ($i > 0.0) {
  6452. return self::_cleanComplex($r.'+'.$i.$parsedComplexDivisor['suffix']);
  6453. } elseif ($i < 0.0) {
  6454. return self::_cleanComplex($r.$i.$parsedComplexDivisor['suffix']);
  6455. } else {
  6456. return $r;
  6457. }
  6458. } // function IMDIV()
  6459. /**
  6460. * IMSUB
  6461. *
  6462. * Returns the difference of two complex numbers in x + yi or x + yj text format.
  6463. *
  6464. * @param string $complexNumber1
  6465. * @param string $complexNumber2
  6466. * @return real
  6467. */
  6468. public static function IMSUB($complexNumber1,$complexNumber2) {
  6469. $complexNumber1 = self::flattenSingleValue($complexNumber1);
  6470. $complexNumber2 = self::flattenSingleValue($complexNumber2);
  6471. $parsedComplex1 = self::_parseComplex($complexNumber1);
  6472. if (!is_array($parsedComplex1)) {
  6473. return $parsedComplex1;
  6474. }
  6475. $parsedComplex2 = self::_parseComplex($complexNumber2);
  6476. if (!is_array($parsedComplex2)) {
  6477. return $parsedComplex2;
  6478. }
  6479. if ((($parsedComplex1['suffix'] != '') && ($parsedComplex2['suffix'] != '')) &&
  6480. ($parsedComplex1['suffix'] != $parsedComplex2['suffix'])) {
  6481. return self::$_errorCodes['num'];
  6482. } elseif (($parsedComplex1['suffix'] == '') && ($parsedComplex2['suffix'] != '')) {
  6483. $parsedComplex1['suffix'] = $parsedComplex2['suffix'];
  6484. }
  6485. $d1 = $parsedComplex1['real'] - $parsedComplex2['real'];
  6486. $d2 = $parsedComplex1['imaginary'] - $parsedComplex2['imaginary'];
  6487. return self::COMPLEX($d1,$d2,$parsedComplex1['suffix']);
  6488. } // function IMSUB()
  6489. /**
  6490. * IMSUM
  6491. *
  6492. * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
  6493. *
  6494. * @param array of mixed Data Series
  6495. * @return real
  6496. */
  6497. public static function IMSUM() {
  6498. // Return value
  6499. $returnValue = self::_parseComplex('0');
  6500. $activeSuffix = '';
  6501. // Loop through the arguments
  6502. $aArgs = self::flattenArray(func_get_args());
  6503. foreach ($aArgs as $arg) {
  6504. $parsedComplex = self::_parseComplex($arg);
  6505. if (!is_array($parsedComplex)) {
  6506. return $parsedComplex;
  6507. }
  6508. if ($activeSuffix == '') {
  6509. $activeSuffix = $parsedComplex['suffix'];
  6510. } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
  6511. return self::$_errorCodes['value'];
  6512. }
  6513. $returnValue['real'] += $parsedComplex['real'];
  6514. $returnValue['imaginary'] += $parsedComplex['imaginary'];
  6515. }
  6516. if ($returnValue['imaginary'] == 0.0) { $activeSuffix = ''; }
  6517. return self::COMPLEX($returnValue['real'],$returnValue['imaginary'],$activeSuffix);
  6518. } // function IMSUM()
  6519. /**
  6520. * IMPRODUCT
  6521. *
  6522. * Returns the product of two or more complex numbers in x + yi or x + yj text format.
  6523. *
  6524. * @param array of mixed Data Series
  6525. * @return real
  6526. */
  6527. public static function IMPRODUCT() {
  6528. // Return value
  6529. $returnValue = self::_parseComplex('1');
  6530. $activeSuffix = '';
  6531. // Loop through the arguments
  6532. $aArgs = self::flattenArray(func_get_args());
  6533. foreach ($aArgs as $arg) {
  6534. $parsedComplex = self::_parseComplex($arg);
  6535. if (!is_array($parsedComplex)) {
  6536. return $parsedComplex;
  6537. }
  6538. $workValue = $returnValue;
  6539. if (($parsedComplex['suffix'] != '') && ($activeSuffix == '')) {
  6540. $activeSuffix = $parsedComplex['suffix'];
  6541. } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
  6542. return self::$_errorCodes['num'];
  6543. }
  6544. $returnValue['real'] = ($workValue['real'] * $parsedComplex['real']) - ($workValue['imaginary'] * $parsedComplex['imaginary']);
  6545. $returnValue['imaginary'] = ($workValue['real'] * $parsedComplex['imaginary']) + ($workValue['imaginary'] * $parsedComplex['real']);
  6546. }
  6547. if ($returnValue['imaginary'] == 0.0) { $activeSuffix = ''; }
  6548. return self::COMPLEX($returnValue['real'],$returnValue['imaginary'],$activeSuffix);
  6549. } // function IMPRODUCT()
  6550. private static $_conversionUnits = array( 'g' => array( 'Group' => 'Mass', 'Unit Name' => 'Gram', 'AllowPrefix' => True ),
  6551. 'sg' => array( 'Group' => 'Mass', 'Unit Name' => 'Slug', 'AllowPrefix' => False ),
  6552. 'lbm' => array( 'Group' => 'Mass', 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => False ),
  6553. 'u' => array( 'Group' => 'Mass', 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => True ),
  6554. 'ozm' => array( 'Group' => 'Mass', 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => False ),
  6555. 'm' => array( 'Group' => 'Distance', 'Unit Name' => 'Meter', 'AllowPrefix' => True ),
  6556. 'mi' => array( 'Group' => 'Distance', 'Unit Name' => 'Statute mile', 'AllowPrefix' => False ),
  6557. 'Nmi' => array( 'Group' => 'Distance', 'Unit Name' => 'Nautical mile', 'AllowPrefix' => False ),
  6558. 'in' => array( 'Group' => 'Distance', 'Unit Name' => 'Inch', 'AllowPrefix' => False ),
  6559. 'ft' => array( 'Group' => 'Distance', 'Unit Name' => 'Foot', 'AllowPrefix' => False ),
  6560. 'yd' => array( 'Group' => 'Distance', 'Unit Name' => 'Yard', 'AllowPrefix' => False ),
  6561. 'ang' => array( 'Group' => 'Distance', 'Unit Name' => 'Angstrom', 'AllowPrefix' => True ),
  6562. 'Pica' => array( 'Group' => 'Distance', 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => False ),
  6563. 'yr' => array( 'Group' => 'Time', 'Unit Name' => 'Year', 'AllowPrefix' => False ),
  6564. 'day' => array( 'Group' => 'Time', 'Unit Name' => 'Day', 'AllowPrefix' => False ),
  6565. 'hr' => array( 'Group' => 'Time', 'Unit Name' => 'Hour', 'AllowPrefix' => False ),
  6566. 'mn' => array( 'Group' => 'Time', 'Unit Name' => 'Minute', 'AllowPrefix' => False ),
  6567. 'sec' => array( 'Group' => 'Time', 'Unit Name' => 'Second', 'AllowPrefix' => True ),
  6568. 'Pa' => array( 'Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => True ),
  6569. 'p' => array( 'Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => True ),
  6570. 'atm' => array( 'Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => True ),
  6571. 'at' => array( 'Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => True ),
  6572. 'mmHg' => array( 'Group' => 'Pressure', 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => True ),
  6573. 'N' => array( 'Group' => 'Force', 'Unit Name' => 'Newton', 'AllowPrefix' => True ),
  6574. 'dyn' => array( 'Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => True ),
  6575. 'dy' => array( 'Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => True ),
  6576. 'lbf' => array( 'Group' => 'Force', 'Unit Name' => 'Pound force', 'AllowPrefix' => False ),
  6577. 'J' => array( 'Group' => 'Energy', 'Unit Name' => 'Joule', 'AllowPrefix' => True ),
  6578. 'e' => array( 'Group' => 'Energy', 'Unit Name' => 'Erg', 'AllowPrefix' => True ),
  6579. 'c' => array( 'Group' => 'Energy', 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => True ),
  6580. 'cal' => array( 'Group' => 'Energy', 'Unit Name' => 'IT calorie', 'AllowPrefix' => True ),
  6581. 'eV' => array( 'Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => True ),
  6582. 'ev' => array( 'Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => True ),
  6583. 'HPh' => array( 'Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => False ),
  6584. 'hh' => array( 'Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => False ),
  6585. 'Wh' => array( 'Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => True ),
  6586. 'wh' => array( 'Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => True ),
  6587. 'flb' => array( 'Group' => 'Energy', 'Unit Name' => 'Foot-pound', 'AllowPrefix' => False ),
  6588. 'BTU' => array( 'Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => False ),
  6589. 'btu' => array( 'Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => False ),
  6590. 'HP' => array( 'Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => False ),
  6591. 'h' => array( 'Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => False ),
  6592. 'W' => array( 'Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => True ),
  6593. 'w' => array( 'Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => True ),
  6594. 'T' => array( 'Group' => 'Magnetism', 'Unit Name' => 'Tesla', 'AllowPrefix' => True ),
  6595. 'ga' => array( 'Group' => 'Magnetism', 'Unit Name' => 'Gauss', 'AllowPrefix' => True ),
  6596. 'C' => array( 'Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => False ),
  6597. 'cel' => array( 'Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => False ),
  6598. 'F' => array( 'Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => False ),
  6599. 'fah' => array( 'Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => False ),
  6600. 'K' => array( 'Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => False ),
  6601. 'kel' => array( 'Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => False ),
  6602. 'tsp' => array( 'Group' => 'Liquid', 'Unit Name' => 'Teaspoon', 'AllowPrefix' => False ),
  6603. 'tbs' => array( 'Group' => 'Liquid', 'Unit Name' => 'Tablespoon', 'AllowPrefix' => False ),
  6604. 'oz' => array( 'Group' => 'Liquid', 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => False ),
  6605. 'cup' => array( 'Group' => 'Liquid', 'Unit Name' => 'Cup', 'AllowPrefix' => False ),
  6606. 'pt' => array( 'Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => False ),
  6607. 'us_pt' => array( 'Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => False ),
  6608. 'uk_pt' => array( 'Group' => 'Liquid', 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => False ),
  6609. 'qt' => array( 'Group' => 'Liquid', 'Unit Name' => 'Quart', 'AllowPrefix' => False ),
  6610. 'gal' => array( 'Group' => 'Liquid', 'Unit Name' => 'Gallon', 'AllowPrefix' => False ),
  6611. 'l' => array( 'Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => True ),
  6612. 'lt' => array( 'Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => True )
  6613. );
  6614. private static $_conversionMultipliers = array( 'Y' => array( 'multiplier' => 1E24, 'name' => 'yotta' ),
  6615. 'Z' => array( 'multiplier' => 1E21, 'name' => 'zetta' ),
  6616. 'E' => array( 'multiplier' => 1E18, 'name' => 'exa' ),
  6617. 'P' => array( 'multiplier' => 1E15, 'name' => 'peta' ),
  6618. 'T' => array( 'multiplier' => 1E12, 'name' => 'tera' ),
  6619. 'G' => array( 'multiplier' => 1E9, 'name' => 'giga' ),
  6620. 'M' => array( 'multiplier' => 1E6, 'name' => 'mega' ),
  6621. 'k' => array( 'multiplier' => 1E3, 'name' => 'kilo' ),
  6622. 'h' => array( 'multiplier' => 1E2, 'name' => 'hecto' ),
  6623. 'e' => array( 'multiplier' => 1E1, 'name' => 'deka' ),
  6624. 'd' => array( 'multiplier' => 1E-1, 'name' => 'deci' ),
  6625. 'c' => array( 'multiplier' => 1E-2, 'name' => 'centi' ),
  6626. 'm' => array( 'multiplier' => 1E-3, 'name' => 'milli' ),
  6627. 'u' => array( 'multiplier' => 1E-6, 'name' => 'micro' ),
  6628. 'n' => array( 'multiplier' => 1E-9, 'name' => 'nano' ),
  6629. 'p' => array( 'multiplier' => 1E-12, 'name' => 'pico' ),
  6630. 'f' => array( 'multiplier' => 1E-15, 'name' => 'femto' ),
  6631. 'a' => array( 'multiplier' => 1E-18, 'name' => 'atto' ),
  6632. 'z' => array( 'multiplier' => 1E-21, 'name' => 'zepto' ),
  6633. 'y' => array( 'multiplier' => 1E-24, 'name' => 'yocto' )
  6634. );
  6635. private static $_unitConversions = array( 'Mass' => array( 'g' => array( 'g' => 1.0,
  6636. 'sg' => 6.85220500053478E-05,
  6637. 'lbm' => 2.20462291469134E-03,
  6638. 'u' => 6.02217000000000E+23,
  6639. 'ozm' => 3.52739718003627E-02
  6640. ),
  6641. 'sg' => array( 'g' => 1.45938424189287E+04,
  6642. 'sg' => 1.0,
  6643. 'lbm' => 3.21739194101647E+01,
  6644. 'u' => 8.78866000000000E+27,
  6645. 'ozm' => 5.14782785944229E+02
  6646. ),
  6647. 'lbm' => array( 'g' => 4.5359230974881148E+02,
  6648. 'sg' => 3.10810749306493E-02,
  6649. 'lbm' => 1.0,
  6650. 'u' => 2.73161000000000E+26,
  6651. 'ozm' => 1.60000023429410E+01
  6652. ),
  6653. 'u' => array( 'g' => 1.66053100460465E-24,
  6654. 'sg' => 1.13782988532950E-28,
  6655. 'lbm' => 3.66084470330684E-27,
  6656. 'u' => 1.0,
  6657. 'ozm' => 5.85735238300524E-26
  6658. ),
  6659. 'ozm' => array( 'g' => 2.83495152079732E+01,
  6660. 'sg' => 1.94256689870811E-03,
  6661. 'lbm' => 6.24999908478882E-02,
  6662. 'u' => 1.70725600000000E+25,
  6663. 'ozm' => 1.0
  6664. )
  6665. ),
  6666. 'Distance' => array( 'm' => array( 'm' => 1.0,
  6667. 'mi' => 6.21371192237334E-04,
  6668. 'Nmi' => 5.39956803455724E-04,
  6669. 'in' => 3.93700787401575E+01,
  6670. 'ft' => 3.28083989501312E+00,
  6671. 'yd' => 1.09361329797891E+00,
  6672. 'ang' => 1.00000000000000E+10,
  6673. 'Pica' => 2.83464566929116E+03
  6674. ),
  6675. 'mi' => array( 'm' => 1.60934400000000E+03,
  6676. 'mi' => 1.0,
  6677. 'Nmi' => 8.68976241900648E-01,
  6678. 'in' => 6.33600000000000E+04,
  6679. 'ft' => 5.28000000000000E+03,
  6680. 'yd' => 1.76000000000000E+03,
  6681. 'ang' => 1.60934400000000E+13,
  6682. 'Pica' => 4.56191999999971E+06
  6683. ),
  6684. 'Nmi' => array( 'm' => 1.85200000000000E+03,
  6685. 'mi' => 1.15077944802354E+00,
  6686. 'Nmi' => 1.0,
  6687. 'in' => 7.29133858267717E+04,
  6688. 'ft' => 6.07611548556430E+03,
  6689. 'yd' => 2.02537182785694E+03,
  6690. 'ang' => 1.85200000000000E+13,
  6691. 'Pica' => 5.24976377952723E+06
  6692. ),
  6693. 'in' => array( 'm' => 2.54000000000000E-02,
  6694. 'mi' => 1.57828282828283E-05,
  6695. 'Nmi' => 1.37149028077754E-05,
  6696. 'in' => 1.0,
  6697. 'ft' => 8.33333333333333E-02,
  6698. 'yd' => 2.77777777686643E-02,
  6699. 'ang' => 2.54000000000000E+08,
  6700. 'Pica' => 7.19999999999955E+01
  6701. ),
  6702. 'ft' => array( 'm' => 3.04800000000000E-01,
  6703. 'mi' => 1.89393939393939E-04,
  6704. 'Nmi' => 1.64578833693305E-04,
  6705. 'in' => 1.20000000000000E+01,
  6706. 'ft' => 1.0,
  6707. 'yd' => 3.33333333223972E-01,
  6708. 'ang' => 3.04800000000000E+09,
  6709. 'Pica' => 8.63999999999946E+02
  6710. ),
  6711. 'yd' => array( 'm' => 9.14400000300000E-01,
  6712. 'mi' => 5.68181818368230E-04,
  6713. 'Nmi' => 4.93736501241901E-04,
  6714. 'in' => 3.60000000118110E+01,
  6715. 'ft' => 3.00000000000000E+00,
  6716. 'yd' => 1.0,
  6717. 'ang' => 9.14400000300000E+09,
  6718. 'Pica' => 2.59200000085023E+03
  6719. ),
  6720. 'ang' => array( 'm' => 1.00000000000000E-10,
  6721. 'mi' => 6.21371192237334E-14,
  6722. 'Nmi' => 5.39956803455724E-14,
  6723. 'in' => 3.93700787401575E-09,
  6724. 'ft' => 3.28083989501312E-10,
  6725. 'yd' => 1.09361329797891E-10,
  6726. 'ang' => 1.0,
  6727. 'Pica' => 2.83464566929116E-07
  6728. ),
  6729. 'Pica' => array( 'm' => 3.52777777777800E-04,
  6730. 'mi' => 2.19205948372629E-07,
  6731. 'Nmi' => 1.90484761219114E-07,
  6732. 'in' => 1.38888888888898E-02,
  6733. 'ft' => 1.15740740740748E-03,
  6734. 'yd' => 3.85802469009251E-04,
  6735. 'ang' => 3.52777777777800E+06,
  6736. 'Pica' => 1.0
  6737. )
  6738. ),
  6739. 'Time' => array( 'yr' => array( 'yr' => 1.0,
  6740. 'day' => 365.25,
  6741. 'hr' => 8766.0,
  6742. 'mn' => 525960.0,
  6743. 'sec' => 31557600.0
  6744. ),
  6745. 'day' => array( 'yr' => 2.73785078713210E-03,
  6746. 'day' => 1.0,
  6747. 'hr' => 24.0,
  6748. 'mn' => 1440.0,
  6749. 'sec' => 86400.0
  6750. ),
  6751. 'hr' => array( 'yr' => 1.14077116130504E-04,
  6752. 'day' => 4.16666666666667E-02,
  6753. 'hr' => 1.0,
  6754. 'mn' => 60.0,
  6755. 'sec' => 3600.0
  6756. ),
  6757. 'mn' => array( 'yr' => 1.90128526884174E-06,
  6758. 'day' => 6.94444444444444E-04,
  6759. 'hr' => 1.66666666666667E-02,
  6760. 'mn' => 1.0,
  6761. 'sec' => 60.0
  6762. ),
  6763. 'sec' => array( 'yr' => 3.16880878140289E-08,
  6764. 'day' => 1.15740740740741E-05,
  6765. 'hr' => 2.77777777777778E-04,
  6766. 'mn' => 1.66666666666667E-02,
  6767. 'sec' => 1.0
  6768. )
  6769. ),
  6770. 'Pressure' => array( 'Pa' => array( 'Pa' => 1.0,
  6771. 'p' => 1.0,
  6772. 'atm' => 9.86923299998193E-06,
  6773. 'at' => 9.86923299998193E-06,
  6774. 'mmHg' => 7.50061707998627E-03
  6775. ),
  6776. 'p' => array( 'Pa' => 1.0,
  6777. 'p' => 1.0,
  6778. 'atm' => 9.86923299998193E-06,
  6779. 'at' => 9.86923299998193E-06,
  6780. 'mmHg' => 7.50061707998627E-03
  6781. ),
  6782. 'atm' => array( 'Pa' => 1.01324996583000E+05,
  6783. 'p' => 1.01324996583000E+05,
  6784. 'atm' => 1.0,
  6785. 'at' => 1.0,
  6786. 'mmHg' => 760.0
  6787. ),
  6788. 'at' => array( 'Pa' => 1.01324996583000E+05,
  6789. 'p' => 1.01324996583000E+05,
  6790. 'atm' => 1.0,
  6791. 'at' => 1.0,
  6792. 'mmHg' => 760.0
  6793. ),
  6794. 'mmHg' => array( 'Pa' => 1.33322363925000E+02,
  6795. 'p' => 1.33322363925000E+02,
  6796. 'atm' => 1.31578947368421E-03,
  6797. 'at' => 1.31578947368421E-03,
  6798. 'mmHg' => 1.0
  6799. )
  6800. ),
  6801. 'Force' => array( 'N' => array( 'N' => 1.0,
  6802. 'dyn' => 1.0E+5,
  6803. 'dy' => 1.0E+5,
  6804. 'lbf' => 2.24808923655339E-01
  6805. ),
  6806. 'dyn' => array( 'N' => 1.0E-5,
  6807. 'dyn' => 1.0,
  6808. 'dy' => 1.0,
  6809. 'lbf' => 2.24808923655339E-06
  6810. ),
  6811. 'dy' => array( 'N' => 1.0E-5,
  6812. 'dyn' => 1.0,
  6813. 'dy' => 1.0,
  6814. 'lbf' => 2.24808923655339E-06
  6815. ),
  6816. 'lbf' => array( 'N' => 4.448222,
  6817. 'dyn' => 4.448222E+5,
  6818. 'dy' => 4.448222E+5,
  6819. 'lbf' => 1.0
  6820. )
  6821. ),
  6822. 'Energy' => array( 'J' => array( 'J' => 1.0,
  6823. 'e' => 9.99999519343231E+06,
  6824. 'c' => 2.39006249473467E-01,
  6825. 'cal' => 2.38846190642017E-01,
  6826. 'eV' => 6.24145700000000E+18,
  6827. 'ev' => 6.24145700000000E+18,
  6828. 'HPh' => 3.72506430801000E-07,
  6829. 'hh' => 3.72506430801000E-07,
  6830. 'Wh' => 2.77777916238711E-04,
  6831. 'wh' => 2.77777916238711E-04,
  6832. 'flb' => 2.37304222192651E+01,
  6833. 'BTU' => 9.47815067349015E-04,
  6834. 'btu' => 9.47815067349015E-04
  6835. ),
  6836. 'e' => array( 'J' => 1.00000048065700E-07,
  6837. 'e' => 1.0,
  6838. 'c' => 2.39006364353494E-08,
  6839. 'cal' => 2.38846305445111E-08,
  6840. 'eV' => 6.24146000000000E+11,
  6841. 'ev' => 6.24146000000000E+11,
  6842. 'HPh' => 3.72506609848824E-14,
  6843. 'hh' => 3.72506609848824E-14,
  6844. 'Wh' => 2.77778049754611E-11,
  6845. 'wh' => 2.77778049754611E-11,
  6846. 'flb' => 2.37304336254586E-06,
  6847. 'BTU' => 9.47815522922962E-11,
  6848. 'btu' => 9.47815522922962E-11
  6849. ),
  6850. 'c' => array( 'J' => 4.18399101363672E+00,
  6851. 'e' => 4.18398900257312E+07,
  6852. 'c' => 1.0,
  6853. 'cal' => 9.99330315287563E-01,
  6854. 'eV' => 2.61142000000000E+19,
  6855. 'ev' => 2.61142000000000E+19,
  6856. 'HPh' => 1.55856355899327E-06,
  6857. 'hh' => 1.55856355899327E-06,
  6858. 'Wh' => 1.16222030532950E-03,
  6859. 'wh' => 1.16222030532950E-03,
  6860. 'flb' => 9.92878733152102E+01,
  6861. 'BTU' => 3.96564972437776E-03,
  6862. 'btu' => 3.96564972437776E-03
  6863. ),
  6864. 'cal' => array( 'J' => 4.18679484613929E+00,
  6865. 'e' => 4.18679283372801E+07,
  6866. 'c' => 1.00067013349059E+00,
  6867. 'cal' => 1.0,
  6868. 'eV' => 2.61317000000000E+19,
  6869. 'ev' => 2.61317000000000E+19,
  6870. 'HPh' => 1.55960800463137E-06,
  6871. 'hh' => 1.55960800463137E-06,
  6872. 'Wh' => 1.16299914807955E-03,
  6873. 'wh' => 1.16299914807955E-03,
  6874. 'flb' => 9.93544094443283E+01,
  6875. 'BTU' => 3.96830723907002E-03,
  6876. 'btu' => 3.96830723907002E-03
  6877. ),
  6878. 'eV' => array( 'J' => 1.60219000146921E-19,
  6879. 'e' => 1.60218923136574E-12,
  6880. 'c' => 3.82933423195043E-20,
  6881. 'cal' => 3.82676978535648E-20,
  6882. 'eV' => 1.0,
  6883. 'ev' => 1.0,
  6884. 'HPh' => 5.96826078912344E-26,
  6885. 'hh' => 5.96826078912344E-26,
  6886. 'Wh' => 4.45053000026614E-23,
  6887. 'wh' => 4.45053000026614E-23,
  6888. 'flb' => 3.80206452103492E-18,
  6889. 'BTU' => 1.51857982414846E-22,
  6890. 'btu' => 1.51857982414846E-22
  6891. ),
  6892. 'ev' => array( 'J' => 1.60219000146921E-19,
  6893. 'e' => 1.60218923136574E-12,
  6894. 'c' => 3.82933423195043E-20,
  6895. 'cal' => 3.82676978535648E-20,
  6896. 'eV' => 1.0,
  6897. 'ev' => 1.0,
  6898. 'HPh' => 5.96826078912344E-26,
  6899. 'hh' => 5.96826078912344E-26,
  6900. 'Wh' => 4.45053000026614E-23,
  6901. 'wh' => 4.45053000026614E-23,
  6902. 'flb' => 3.80206452103492E-18,
  6903. 'BTU' => 1.51857982414846E-22,
  6904. 'btu' => 1.51857982414846E-22
  6905. ),
  6906. 'HPh' => array( 'J' => 2.68451741316170E+06,
  6907. 'e' => 2.68451612283024E+13,
  6908. 'c' => 6.41616438565991E+05,
  6909. 'cal' => 6.41186757845835E+05,
  6910. 'eV' => 1.67553000000000E+25,
  6911. 'ev' => 1.67553000000000E+25,
  6912. 'HPh' => 1.0,
  6913. 'hh' => 1.0,
  6914. 'Wh' => 7.45699653134593E+02,
  6915. 'wh' => 7.45699653134593E+02,
  6916. 'flb' => 6.37047316692964E+07,
  6917. 'BTU' => 2.54442605275546E+03,
  6918. 'btu' => 2.54442605275546E+03
  6919. ),
  6920. 'hh' => array( 'J' => 2.68451741316170E+06,
  6921. 'e' => 2.68451612283024E+13,
  6922. 'c' => 6.41616438565991E+05,
  6923. 'cal' => 6.41186757845835E+05,
  6924. 'eV' => 1.67553000000000E+25,
  6925. 'ev' => 1.67553000000000E+25,
  6926. 'HPh' => 1.0,
  6927. 'hh' => 1.0,
  6928. 'Wh' => 7.45699653134593E+02,
  6929. 'wh' => 7.45699653134593E+02,
  6930. 'flb' => 6.37047316692964E+07,
  6931. 'BTU' => 2.54442605275546E+03,
  6932. 'btu' => 2.54442605275546E+03
  6933. ),
  6934. 'Wh' => array( 'J' => 3.59999820554720E+03,
  6935. 'e' => 3.59999647518369E+10,
  6936. 'c' => 8.60422069219046E+02,
  6937. 'cal' => 8.59845857713046E+02,
  6938. 'eV' => 2.24692340000000E+22,
  6939. 'ev' => 2.24692340000000E+22,
  6940. 'HPh' => 1.34102248243839E-03,
  6941. 'hh' => 1.34102248243839E-03,
  6942. 'Wh' => 1.0,
  6943. 'wh' => 1.0,
  6944. 'flb' => 8.54294774062316E+04,
  6945. 'BTU' => 3.41213254164705E+00,
  6946. 'btu' => 3.41213254164705E+00
  6947. ),
  6948. 'wh' => array( 'J' => 3.59999820554720E+03,
  6949. 'e' => 3.59999647518369E+10,
  6950. 'c' => 8.60422069219046E+02,
  6951. 'cal' => 8.59845857713046E+02,
  6952. 'eV' => 2.24692340000000E+22,
  6953. 'ev' => 2.24692340000000E+22,
  6954. 'HPh' => 1.34102248243839E-03,
  6955. 'hh' => 1.34102248243839E-03,
  6956. 'Wh' => 1.0,
  6957. 'wh' => 1.0,
  6958. 'flb' => 8.54294774062316E+04,
  6959. 'BTU' => 3.41213254164705E+00,
  6960. 'btu' => 3.41213254164705E+00
  6961. ),
  6962. 'flb' => array( 'J' => 4.21400003236424E-02,
  6963. 'e' => 4.21399800687660E+05,
  6964. 'c' => 1.00717234301644E-02,
  6965. 'cal' => 1.00649785509554E-02,
  6966. 'eV' => 2.63015000000000E+17,
  6967. 'ev' => 2.63015000000000E+17,
  6968. 'HPh' => 1.56974211145130E-08,
  6969. 'hh' => 1.56974211145130E-08,
  6970. 'Wh' => 1.17055614802000E-05,
  6971. 'wh' => 1.17055614802000E-05,
  6972. 'flb' => 1.0,
  6973. 'BTU' => 3.99409272448406E-05,
  6974. 'btu' => 3.99409272448406E-05
  6975. ),
  6976. 'BTU' => array( 'J' => 1.05505813786749E+03,
  6977. 'e' => 1.05505763074665E+10,
  6978. 'c' => 2.52165488508168E+02,
  6979. 'cal' => 2.51996617135510E+02,
  6980. 'eV' => 6.58510000000000E+21,
  6981. 'ev' => 6.58510000000000E+21,
  6982. 'HPh' => 3.93015941224568E-04,
  6983. 'hh' => 3.93015941224568E-04,
  6984. 'Wh' => 2.93071851047526E-01,
  6985. 'wh' => 2.93071851047526E-01,
  6986. 'flb' => 2.50369750774671E+04,
  6987. 'BTU' => 1.0,
  6988. 'btu' => 1.0,
  6989. ),
  6990. 'btu' => array( 'J' => 1.05505813786749E+03,
  6991. 'e' => 1.05505763074665E+10,
  6992. 'c' => 2.52165488508168E+02,
  6993. 'cal' => 2.51996617135510E+02,
  6994. 'eV' => 6.58510000000000E+21,
  6995. 'ev' => 6.58510000000000E+21,
  6996. 'HPh' => 3.93015941224568E-04,
  6997. 'hh' => 3.93015941224568E-04,
  6998. 'Wh' => 2.93071851047526E-01,
  6999. 'wh' => 2.93071851047526E-01,
  7000. 'flb' => 2.50369750774671E+04,
  7001. 'BTU' => 1.0,
  7002. 'btu' => 1.0,
  7003. )
  7004. ),
  7005. 'Power' => array( 'HP' => array( 'HP' => 1.0,
  7006. 'h' => 1.0,
  7007. 'W' => 7.45701000000000E+02,
  7008. 'w' => 7.45701000000000E+02
  7009. ),
  7010. 'h' => array( 'HP' => 1.0,
  7011. 'h' => 1.0,
  7012. 'W' => 7.45701000000000E+02,
  7013. 'w' => 7.45701000000000E+02
  7014. ),
  7015. 'W' => array( 'HP' => 1.34102006031908E-03,
  7016. 'h' => 1.34102006031908E-03,
  7017. 'W' => 1.0,
  7018. 'w' => 1.0
  7019. ),
  7020. 'w' => array( 'HP' => 1.34102006031908E-03,
  7021. 'h' => 1.34102006031908E-03,
  7022. 'W' => 1.0,
  7023. 'w' => 1.0
  7024. )
  7025. ),
  7026. 'Magnetism' => array( 'T' => array( 'T' => 1.0,
  7027. 'ga' => 10000.0
  7028. ),
  7029. 'ga' => array( 'T' => 0.0001,
  7030. 'ga' => 1.0
  7031. )
  7032. ),
  7033. 'Liquid' => array( 'tsp' => array( 'tsp' => 1.0,
  7034. 'tbs' => 3.33333333333333E-01,
  7035. 'oz' => 1.66666666666667E-01,
  7036. 'cup' => 2.08333333333333E-02,
  7037. 'pt' => 1.04166666666667E-02,
  7038. 'us_pt' => 1.04166666666667E-02,
  7039. 'uk_pt' => 8.67558516821960E-03,
  7040. 'qt' => 5.20833333333333E-03,
  7041. 'gal' => 1.30208333333333E-03,
  7042. 'l' => 4.92999408400710E-03,
  7043. 'lt' => 4.92999408400710E-03
  7044. ),
  7045. 'tbs' => array( 'tsp' => 3.00000000000000E+00,
  7046. 'tbs' => 1.0,
  7047. 'oz' => 5.00000000000000E-01,
  7048. 'cup' => 6.25000000000000E-02,
  7049. 'pt' => 3.12500000000000E-02,
  7050. 'us_pt' => 3.12500000000000E-02,
  7051. 'uk_pt' => 2.60267555046588E-02,
  7052. 'qt' => 1.56250000000000E-02,
  7053. 'gal' => 3.90625000000000E-03,
  7054. 'l' => 1.47899822520213E-02,
  7055. 'lt' => 1.47899822520213E-02
  7056. ),
  7057. 'oz' => array( 'tsp' => 6.00000000000000E+00,
  7058. 'tbs' => 2.00000000000000E+00,
  7059. 'oz' => 1.0,
  7060. 'cup' => 1.25000000000000E-01,
  7061. 'pt' => 6.25000000000000E-02,
  7062. 'us_pt' => 6.25000000000000E-02,
  7063. 'uk_pt' => 5.20535110093176E-02,
  7064. 'qt' => 3.12500000000000E-02,
  7065. 'gal' => 7.81250000000000E-03,
  7066. 'l' => 2.95799645040426E-02,
  7067. 'lt' => 2.95799645040426E-02
  7068. ),
  7069. 'cup' => array( 'tsp' => 4.80000000000000E+01,
  7070. 'tbs' => 1.60000000000000E+01,
  7071. 'oz' => 8.00000000000000E+00,
  7072. 'cup' => 1.0,
  7073. 'pt' => 5.00000000000000E-01,
  7074. 'us_pt' => 5.00000000000000E-01,
  7075. 'uk_pt' => 4.16428088074541E-01,
  7076. 'qt' => 2.50000000000000E-01,
  7077. 'gal' => 6.25000000000000E-02,
  7078. 'l' => 2.36639716032341E-01,
  7079. 'lt' => 2.36639716032341E-01
  7080. ),
  7081. 'pt' => array( 'tsp' => 9.60000000000000E+01,
  7082. 'tbs' => 3.20000000000000E+01,
  7083. 'oz' => 1.60000000000000E+01,
  7084. 'cup' => 2.00000000000000E+00,
  7085. 'pt' => 1.0,
  7086. 'us_pt' => 1.0,
  7087. 'uk_pt' => 8.32856176149081E-01,
  7088. 'qt' => 5.00000000000000E-01,
  7089. 'gal' => 1.25000000000000E-01,
  7090. 'l' => 4.73279432064682E-01,
  7091. 'lt' => 4.73279432064682E-01
  7092. ),
  7093. 'us_pt' => array( 'tsp' => 9.60000000000000E+01,
  7094. 'tbs' => 3.20000000000000E+01,
  7095. 'oz' => 1.60000000000000E+01,
  7096. 'cup' => 2.00000000000000E+00,
  7097. 'pt' => 1.0,
  7098. 'us_pt' => 1.0,
  7099. 'uk_pt' => 8.32856176149081E-01,
  7100. 'qt' => 5.00000000000000E-01,
  7101. 'gal' => 1.25000000000000E-01,
  7102. 'l' => 4.73279432064682E-01,
  7103. 'lt' => 4.73279432064682E-01
  7104. ),
  7105. 'uk_pt' => array( 'tsp' => 1.15266000000000E+02,
  7106. 'tbs' => 3.84220000000000E+01,
  7107. 'oz' => 1.92110000000000E+01,
  7108. 'cup' => 2.40137500000000E+00,
  7109. 'pt' => 1.20068750000000E+00,
  7110. 'us_pt' => 1.20068750000000E+00,
  7111. 'uk_pt' => 1.0,
  7112. 'qt' => 6.00343750000000E-01,
  7113. 'gal' => 1.50085937500000E-01,
  7114. 'l' => 5.68260698087162E-01,
  7115. 'lt' => 5.68260698087162E-01
  7116. ),
  7117. 'qt' => array( 'tsp' => 1.92000000000000E+02,
  7118. 'tbs' => 6.40000000000000E+01,
  7119. 'oz' => 3.20000000000000E+01,
  7120. 'cup' => 4.00000000000000E+00,
  7121. 'pt' => 2.00000000000000E+00,
  7122. 'us_pt' => 2.00000000000000E+00,
  7123. 'uk_pt' => 1.66571235229816E+00,
  7124. 'qt' => 1.0,
  7125. 'gal' => 2.50000000000000E-01,
  7126. 'l' => 9.46558864129363E-01,
  7127. 'lt' => 9.46558864129363E-01
  7128. ),
  7129. 'gal' => array( 'tsp' => 7.68000000000000E+02,
  7130. 'tbs' => 2.56000000000000E+02,
  7131. 'oz' => 1.28000000000000E+02,
  7132. 'cup' => 1.60000000000000E+01,
  7133. 'pt' => 8.00000000000000E+00,
  7134. 'us_pt' => 8.00000000000000E+00,
  7135. 'uk_pt' => 6.66284940919265E+00,
  7136. 'qt' => 4.00000000000000E+00,
  7137. 'gal' => 1.0,
  7138. 'l' => 3.78623545651745E+00,
  7139. 'lt' => 3.78623545651745E+00
  7140. ),
  7141. 'l' => array( 'tsp' => 2.02840000000000E+02,
  7142. 'tbs' => 6.76133333333333E+01,
  7143. 'oz' => 3.38066666666667E+01,
  7144. 'cup' => 4.22583333333333E+00,
  7145. 'pt' => 2.11291666666667E+00,
  7146. 'us_pt' => 2.11291666666667E+00,
  7147. 'uk_pt' => 1.75975569552166E+00,
  7148. 'qt' => 1.05645833333333E+00,
  7149. 'gal' => 2.64114583333333E-01,
  7150. 'l' => 1.0,
  7151. 'lt' => 1.0
  7152. ),
  7153. 'lt' => array( 'tsp' => 2.02840000000000E+02,
  7154. 'tbs' => 6.76133333333333E+01,
  7155. 'oz' => 3.38066666666667E+01,
  7156. 'cup' => 4.22583333333333E+00,
  7157. 'pt' => 2.11291666666667E+00,
  7158. 'us_pt' => 2.11291666666667E+00,
  7159. 'uk_pt' => 1.75975569552166E+00,
  7160. 'qt' => 1.05645833333333E+00,
  7161. 'gal' => 2.64114583333333E-01,
  7162. 'l' => 1.0,
  7163. 'lt' => 1.0
  7164. )
  7165. )
  7166. );
  7167. /**
  7168. * getConversionGroups
  7169. *
  7170. * @return array
  7171. */
  7172. public static function getConversionGroups() {
  7173. $conversionGroups = array();
  7174. foreach(self::$_conversionUnits as $conversionUnit) {
  7175. $conversionGroups[] = $conversionUnit['Group'];
  7176. }
  7177. return array_merge(array_unique($conversionGroups));
  7178. } // function getConversionGroups()
  7179. /**
  7180. * getConversionGroupUnits
  7181. *
  7182. * @return array
  7183. */
  7184. public static function getConversionGroupUnits($group = NULL) {
  7185. $conversionGroups = array();
  7186. foreach(self::$_conversionUnits as $conversionUnit => $conversionGroup) {
  7187. if ((is_null($group)) || ($conversionGroup['Group'] == $group)) {
  7188. $conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
  7189. }
  7190. }
  7191. return $conversionGroups;
  7192. } // function getConversionGroupUnits()
  7193. /**
  7194. * getConversionGroupUnitDetails
  7195. *
  7196. * @return array
  7197. */
  7198. public static function getConversionGroupUnitDetails($group = NULL) {
  7199. $conversionGroups = array();
  7200. foreach(self::$_conversionUnits as $conversionUnit => $conversionGroup) {
  7201. if ((is_null($group)) || ($conversionGroup['Group'] == $group)) {
  7202. $conversionGroups[$conversionGroup['Group']][] = array( 'unit' => $conversionUnit,
  7203. 'description' => $conversionGroup['Unit Name']
  7204. );
  7205. }
  7206. }
  7207. return $conversionGroups;
  7208. } // function getConversionGroupUnitDetails()
  7209. /**
  7210. * getConversionGroups
  7211. *
  7212. * @return array
  7213. */
  7214. public static function getConversionMultipliers() {
  7215. return self::$_conversionMultipliers;
  7216. } // function getConversionGroups()
  7217. /**
  7218. * CONVERTUOM
  7219. *
  7220. * @param float $value
  7221. * @param string $fromUOM
  7222. * @param string $toUOM
  7223. * @return float
  7224. */
  7225. public static function CONVERTUOM($value, $fromUOM, $toUOM) {
  7226. $value = self::flattenSingleValue($value);
  7227. $fromUOM = self::flattenSingleValue($fromUOM);
  7228. $toUOM = self::flattenSingleValue($toUOM);
  7229. if (!is_numeric($value)) {
  7230. return self::$_errorCodes['value'];
  7231. }
  7232. $fromMultiplier = 1;
  7233. if (isset(self::$_conversionUnits[$fromUOM])) {
  7234. $unitGroup1 = self::$_conversionUnits[$fromUOM]['Group'];
  7235. } else {
  7236. $fromMultiplier = substr($fromUOM,0,1);
  7237. $fromUOM = substr($fromUOM,1);
  7238. if (isset(self::$_conversionMultipliers[$fromMultiplier])) {
  7239. $fromMultiplier = self::$_conversionMultipliers[$fromMultiplier]['multiplier'];
  7240. } else {
  7241. return self::$_errorCodes['na'];
  7242. }
  7243. if ((isset(self::$_conversionUnits[$fromUOM])) && (self::$_conversionUnits[$fromUOM]['AllowPrefix'])) {
  7244. $unitGroup1 = self::$_conversionUnits[$fromUOM]['Group'];
  7245. } else {
  7246. return self::$_errorCodes['na'];
  7247. }
  7248. }
  7249. $value *= $fromMultiplier;
  7250. $toMultiplier = 1;
  7251. if (isset(self::$_conversionUnits[$toUOM])) {
  7252. $unitGroup2 = self::$_conversionUnits[$toUOM]['Group'];
  7253. } else {
  7254. $toMultiplier = substr($toUOM,0,1);
  7255. $toUOM = substr($toUOM,1);
  7256. if (isset(self::$_conversionMultipliers[$toMultiplier])) {
  7257. $toMultiplier = self::$_conversionMultipliers[$toMultiplier]['multiplier'];
  7258. } else {
  7259. return self::$_errorCodes['na'];
  7260. }
  7261. if ((isset(self::$_conversionUnits[$toUOM])) && (self::$_conversionUnits[$toUOM]['AllowPrefix'])) {
  7262. $unitGroup2 = self::$_conversionUnits[$toUOM]['Group'];
  7263. } else {
  7264. return self::$_errorCodes['na'];
  7265. }
  7266. }
  7267. if ($unitGroup1 != $unitGroup2) {
  7268. return self::$_errorCodes['na'];
  7269. }
  7270. if ($fromUOM == $toUOM) {
  7271. return 1.0;
  7272. } elseif ($unitGroup1 == 'Temperature') {
  7273. if (($fromUOM == 'F') || ($fromUOM == 'fah')) {
  7274. if (($toUOM == 'F') || ($toUOM == 'fah')) {
  7275. return 1.0;
  7276. } else {
  7277. $value = (($value - 32) / 1.8);
  7278. if (($toUOM == 'K') || ($toUOM == 'kel')) {
  7279. $value += 273.15;
  7280. }
  7281. return $value;
  7282. }
  7283. } elseif ((($fromUOM == 'K') || ($fromUOM == 'kel')) &&
  7284. (($toUOM == 'K') || ($toUOM == 'kel'))) {
  7285. return 1.0;
  7286. } elseif ((($fromUOM == 'C') || ($fromUOM == 'cel')) &&
  7287. (($toUOM == 'C') || ($toUOM == 'cel'))) {
  7288. return 1.0;
  7289. }
  7290. if (($toUOM == 'F') || ($toUOM == 'fah')) {
  7291. if (($fromUOM == 'K') || ($fromUOM == 'kel')) {
  7292. $value -= 273.15;
  7293. }
  7294. return ($value * 1.8) + 32;
  7295. }
  7296. if (($toUOM == 'C') || ($toUOM == 'cel')) {
  7297. return $value - 273.15;
  7298. }
  7299. return $value + 273.15;
  7300. }
  7301. return ($value * self::$_unitConversions[$unitGroup1][$fromUOM][$toUOM]) / $toMultiplier;
  7302. } // function CONVERTUOM()
  7303. /**
  7304. * BESSELI
  7305. *
  7306. * Returns the modified Bessel function, which is equivalent to the Bessel function evaluated for purely imaginary arguments
  7307. *
  7308. * @param float $x
  7309. * @param float $n
  7310. * @return int
  7311. */
  7312. public static function BESSELI($x, $n) {
  7313. $x = self::flattenSingleValue($x);
  7314. $n = floor(self::flattenSingleValue($n));
  7315. if ((is_numeric($x)) && (is_numeric($n))) {
  7316. if ($n < 0) {
  7317. return self::$_errorCodes['num'];
  7318. }
  7319. $f_2_PI = 2 * pi();
  7320. if (abs($x) <= 30) {
  7321. $fTerm = pow($x / 2, $n) / self::FACT($n);
  7322. $nK = 1;
  7323. $fResult = $fTerm;
  7324. $fSqrX = ($x * $x) / 4;
  7325. do {
  7326. $fTerm *= $fSqrX;
  7327. $fTerm /= ($nK * ($nK + $n));
  7328. $fResult += $fTerm;
  7329. } while ((abs($fTerm) > 1e-10) && (++$nK < 100));
  7330. } else {
  7331. $fXAbs = abs($x);
  7332. $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs);
  7333. if (($n && 1) && ($x < 0)) {
  7334. $fResult = -$fResult;
  7335. }
  7336. }
  7337. return $fResult;
  7338. }
  7339. return self::$_errorCodes['value'];
  7340. } // function BESSELI()
  7341. /**
  7342. * BESSELJ
  7343. *
  7344. * Returns the Bessel function
  7345. *
  7346. * @param float $x
  7347. * @param float $n
  7348. * @return int
  7349. */
  7350. public static function BESSELJ($x, $n) {
  7351. $x = self::flattenSingleValue($x);
  7352. $n = floor(self::flattenSingleValue($n));
  7353. if ((is_numeric($x)) && (is_numeric($n))) {
  7354. if ($n < 0) {
  7355. return self::$_errorCodes['num'];
  7356. }
  7357. $f_2_DIV_PI = 2 / pi();
  7358. $f_PI_DIV_2 = pi() / 2;
  7359. $f_PI_DIV_4 = pi() / 4;
  7360. $fResult = 0;
  7361. if (abs($x) <= 30) {
  7362. $fTerm = pow($x / 2, $n) / self::FACT($n);
  7363. $nK = 1;
  7364. $fResult = $fTerm;
  7365. $fSqrX = ($x * $x) / -4;
  7366. do {
  7367. $fTerm *= $fSqrX;
  7368. $fTerm /= ($nK * ($nK + $n));
  7369. $fResult += $fTerm;
  7370. } while ((abs($fTerm) > 1e-10) && (++$nK < 100));
  7371. } else {
  7372. $fXAbs = abs($x);
  7373. $fResult = sqrt($f_2_DIV_PI / $fXAbs) * cos($fXAbs - $n * $f_PI_DIV_2 - $f_PI_DIV_4);
  7374. if (($n && 1) && ($x < 0)) {
  7375. $fResult = -$fResult;
  7376. }
  7377. }
  7378. return $fResult;
  7379. }
  7380. return self::$_errorCodes['value'];
  7381. } // function BESSELJ()
  7382. private static function _Besselk0($fNum) {
  7383. if ($fNum <= 2) {
  7384. $fNum2 = $fNum * 0.5;
  7385. $y = ($fNum2 * $fNum2);
  7386. $fRet = -log($fNum2) * self::BESSELI($fNum, 0) +
  7387. (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
  7388. (0.10750e-3 + $y * 0.74e-5))))));
  7389. } else {
  7390. $y = 2 / $fNum;
  7391. $fRet = exp(-$fNum) / sqrt($fNum) *
  7392. (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
  7393. (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
  7394. }
  7395. return $fRet;
  7396. } // function _Besselk0()
  7397. private static function _Besselk1($fNum) {
  7398. if ($fNum <= 2) {
  7399. $fNum2 = $fNum * 0.5;
  7400. $y = ($fNum2 * $fNum2);
  7401. $fRet = log($fNum2) * self::BESSELI($fNum, 1) +
  7402. (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
  7403. (-0.110404e-2 + $y * (-0.4686e-4))))))) / $fNum;
  7404. } else {
  7405. $y = 2 / $fNum;
  7406. $fRet = exp(-$fNum) / sqrt($fNum) *
  7407. (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
  7408. (0.325614e-2 + $y * (-0.68245e-3)))))));
  7409. }
  7410. return $fRet;
  7411. } // function _Besselk1()
  7412. /**
  7413. * BESSELK
  7414. *
  7415. * Returns the modified Bessel function, which is equivalent to the Bessel functions evaluated for purely imaginary arguments.
  7416. *
  7417. * @param float $x
  7418. * @param float $ord
  7419. * @return float
  7420. */
  7421. public static function BESSELK($x, $ord) {
  7422. $x = self::flattenSingleValue($x);
  7423. $ord = floor(self::flattenSingleValue($ord));
  7424. if ((is_numeric($x)) && (is_numeric($ord))) {
  7425. if ($ord < 0) {
  7426. return self::$_errorCodes['num'];
  7427. }
  7428. switch($ord) {
  7429. case 0 : return self::_Besselk0($x);
  7430. break;
  7431. case 1 : return self::_Besselk1($x);
  7432. break;
  7433. default : $fTox = 2 / $x;
  7434. $fBkm = self::_Besselk0($x);
  7435. $fBk = self::_Besselk1($x);
  7436. for ($n = 1; $n < $ord; ++$n) {
  7437. $fBkp = $fBkm + $n * $fTox * $fBk;
  7438. $fBkm = $fBk;
  7439. $fBk = $fBkp;
  7440. }
  7441. }
  7442. return $fBk;
  7443. }
  7444. return self::$_errorCodes['value'];
  7445. } // function BESSELK()
  7446. private static function _Bessely0($fNum) {
  7447. if ($fNum < 8.0) {
  7448. $y = ($fNum * $fNum);
  7449. $f1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y * (-86327.92757 + $y * 228.4622733))));
  7450. $f2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y * (47447.26470 + $y * (226.1030244 + $y))));
  7451. $fRet = $f1 / $f2 + 0.636619772 * self::BESSELJ($fNum, 0) * log($fNum);
  7452. } else {
  7453. $z = 8.0 / $fNum;
  7454. $y = ($z * $z);
  7455. $xx = $fNum - 0.785398164;
  7456. $f1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
  7457. $f2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y * (-0.934945152e-7))));
  7458. $fRet = sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
  7459. }
  7460. return $fRet;
  7461. } // function _Bessely0()
  7462. private static function _Bessely1($fNum) {
  7463. if ($fNum < 8.0) {
  7464. $y = ($fNum * $fNum);
  7465. $f1 = $fNum * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y * (0.7349264551e9 + $y *
  7466. (-0.4237922726e7 + $y * 0.8511937935e4)))));
  7467. $f2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
  7468. (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
  7469. $fRet = $f1 / $f2 + 0.636619772 * ( self::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum);
  7470. } else {
  7471. $z = 8.0 / $fNum;
  7472. $y = ($z * $z);
  7473. $xx = $fNum - 2.356194491;
  7474. $f1 = 1 + $y * (0.183105e-2 + $y * (-0.3516396496e-4 + $y * (0.2457520174e-5 + $y * (-0.240337019e6))));
  7475. $f2 = 0.04687499995 + $y * (-0.2002690873e-3 + $y * (0.8449199096e-5 + $y * (-0.88228987e-6 + $y * 0.105787412e-6)));
  7476. $fRet = sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
  7477. #i12430# ...but this seems to work much better.
  7478. // $fRet = sqrt(0.636619772 / $fNum) * sin($fNum - 2.356194491);
  7479. }
  7480. return $fRet;
  7481. } // function _Bessely1()
  7482. /**
  7483. * BESSELY
  7484. *
  7485. * Returns the Bessel function, which is also called the Weber function or the Neumann function.
  7486. *
  7487. * @param float $x
  7488. * @param float $n
  7489. * @return int
  7490. */
  7491. public static function BESSELY($x, $ord) {
  7492. $x = self::flattenSingleValue($x);
  7493. $ord = floor(self::flattenSingleValue($ord));
  7494. if ((is_numeric($x)) && (is_numeric($ord))) {
  7495. if ($ord < 0) {
  7496. return self::$_errorCodes['num'];
  7497. }
  7498. switch($ord) {
  7499. case 0 : return self::_Bessely0($x);
  7500. break;
  7501. case 1 : return self::_Bessely1($x);
  7502. break;
  7503. default: $fTox = 2 / $x;
  7504. $fBym = self::_Bessely0($x);
  7505. $fBy = self::_Bessely1($x);
  7506. for ($n = 1; $n < $ord; ++$n) {
  7507. $fByp = $n * $fTox * $fBy - $fBym;
  7508. $fBym = $fBy;
  7509. $fBy = $fByp;
  7510. }
  7511. }
  7512. return $fBy;
  7513. }
  7514. return self::$_errorCodes['value'];
  7515. } // function BESSELY()
  7516. /**
  7517. * DELTA
  7518. *
  7519. * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
  7520. *
  7521. * @param float $a
  7522. * @param float $b
  7523. * @return int
  7524. */
  7525. public static function DELTA($a, $b=0) {
  7526. $a = self::flattenSingleValue($a);
  7527. $b = self::flattenSingleValue($b);
  7528. return (int) ($a == $b);
  7529. } // function DELTA()
  7530. /**
  7531. * GESTEP
  7532. *
  7533. * Returns 1 if number = step; returns 0 (zero) otherwise
  7534. *
  7535. * @param float $number
  7536. * @param float $step
  7537. * @return int
  7538. */
  7539. public static function GESTEP($number, $step=0) {
  7540. $number = self::flattenSingleValue($number);
  7541. $step = self::flattenSingleValue($step);
  7542. return (int) ($number >= $step);
  7543. } // function GESTEP()
  7544. //
  7545. // Private method to calculate the erf value
  7546. //
  7547. private static $_two_sqrtpi = 1.128379167095512574;
  7548. private static $_rel_error = 1E-15;
  7549. private static function _erfVal($x) {
  7550. if (abs($x) > 2.2) {
  7551. return 1 - self::_erfcVal($x);
  7552. }
  7553. $sum = $term = $x;
  7554. $xsqr = ($x * $x);
  7555. $j = 1;
  7556. do {
  7557. $term *= $xsqr / $j;
  7558. $sum -= $term / (2 * $j + 1);
  7559. ++$j;
  7560. $term *= $xsqr / $j;
  7561. $sum += $term / (2 * $j + 1);
  7562. ++$j;
  7563. if ($sum == 0) {
  7564. break;
  7565. }
  7566. } while (abs($term / $sum) > self::$_rel_error);
  7567. return self::$_two_sqrtpi * $sum;
  7568. } // function _erfVal()
  7569. /**
  7570. * ERF
  7571. *
  7572. * Returns the error function integrated between lower_limit and upper_limit
  7573. *
  7574. * @param float $lower lower bound for integrating ERF
  7575. * @param float $upper upper bound for integrating ERF.
  7576. * If omitted, ERF integrates between zero and lower_limit
  7577. * @return int
  7578. */
  7579. public static function ERF($lower, $upper = null) {
  7580. $lower = self::flattenSingleValue($lower);
  7581. $upper = self::flattenSingleValue($upper);
  7582. if (is_numeric($lower)) {
  7583. if ($lower < 0) {
  7584. return self::$_errorCodes['num'];
  7585. }
  7586. if (is_null($upper)) {
  7587. return self::_erfVal($lower);
  7588. }
  7589. if (is_numeric($upper)) {
  7590. if ($upper < 0) {
  7591. return self::$_errorCodes['num'];
  7592. }
  7593. return self::_erfVal($upper) - self::_erfVal($lower);
  7594. }
  7595. }
  7596. return self::$_errorCodes['value'];
  7597. } // function ERF()
  7598. //
  7599. // Private method to calculate the erfc value
  7600. //
  7601. private static $_one_sqrtpi = 0.564189583547756287;
  7602. private static function _erfcVal($x) {
  7603. if (abs($x) < 2.2) {
  7604. return 1 - self::_erfVal($x);
  7605. }
  7606. if ($x < 0) {
  7607. return 2 - self::erfc(-$x);
  7608. }
  7609. $a = $n = 1;
  7610. $b = $c = $x;
  7611. $d = ($x * $x) + 0.5;
  7612. $q1 = $q2 = $b / $d;
  7613. $t = 0;
  7614. do {
  7615. $t = $a * $n + $b * $x;
  7616. $a = $b;
  7617. $b = $t;
  7618. $t = $c * $n + $d * $x;
  7619. $c = $d;
  7620. $d = $t;
  7621. $n += 0.5;
  7622. $q1 = $q2;
  7623. $q2 = $b / $d;
  7624. } while ((abs($q1 - $q2) / $q2) > self::$_rel_error);
  7625. return self::$_one_sqrtpi * exp(-$x * $x) * $q2;
  7626. } // function _erfcVal()
  7627. /**
  7628. * ERFC
  7629. *
  7630. * Returns the complementary ERF function integrated between x and infinity
  7631. *
  7632. * @param float $x The lower bound for integrating ERF
  7633. * @return int
  7634. */
  7635. public static function ERFC($x) {
  7636. $x = self::flattenSingleValue($x);
  7637. if (is_numeric($x)) {
  7638. if ($x < 0) {
  7639. return self::$_errorCodes['num'];
  7640. }
  7641. return self::_erfcVal($x);
  7642. }
  7643. return self::$_errorCodes['value'];
  7644. } // function ERFC()
  7645. /**
  7646. * LOWERCASE
  7647. *
  7648. * Converts a string value to upper case.
  7649. *
  7650. * @param string $mixedCaseString
  7651. * @return string
  7652. */
  7653. public static function LOWERCASE($mixedCaseString) {
  7654. $mixedCaseString = self::flattenSingleValue($mixedCaseString);
  7655. if (is_bool($mixedCaseString)) {
  7656. $mixedCaseString = ($mixedCaseString) ? 'TRUE' : 'FALSE';
  7657. }
  7658. if (function_exists('mb_convert_case')) {
  7659. return mb_convert_case($mixedCaseString, MB_CASE_LOWER, 'UTF-8');
  7660. } else {
  7661. return strtoupper($mixedCaseString);
  7662. }
  7663. } // function LOWERCASE()
  7664. /**
  7665. * UPPERCASE
  7666. *
  7667. * Converts a string value to upper case.
  7668. *
  7669. * @param string $mixedCaseString
  7670. * @return string
  7671. */
  7672. public static function UPPERCASE($mixedCaseString) {
  7673. $mixedCaseString = self::flattenSingleValue($mixedCaseString);
  7674. if (is_bool($mixedCaseString)) {
  7675. $mixedCaseString = ($mixedCaseString) ? 'TRUE' : 'FALSE';
  7676. }
  7677. if (function_exists('mb_convert_case')) {
  7678. return mb_convert_case($mixedCaseString, MB_CASE_UPPER, 'UTF-8');
  7679. } else {
  7680. return strtoupper($mixedCaseString);
  7681. }
  7682. } // function UPPERCASE()
  7683. /**
  7684. * PROPERCASE
  7685. *
  7686. * Converts a string value to upper case.
  7687. *
  7688. * @param string $mixedCaseString
  7689. * @return string
  7690. */
  7691. public static function PROPERCASE($mixedCaseString) {
  7692. $mixedCaseString = self::flattenSingleValue($mixedCaseString);
  7693. if (is_bool($mixedCaseString)) {
  7694. $mixedCaseString = ($mixedCaseString) ? 'TRUE' : 'FALSE';
  7695. }
  7696. if (function_exists('mb_convert_case')) {
  7697. return mb_convert_case($mixedCaseString, MB_CASE_TITLE, 'UTF-8');
  7698. } else {
  7699. return ucwords($mixedCaseString);
  7700. }
  7701. } // function PROPERCASE()
  7702. /**
  7703. * DOLLAR
  7704. *
  7705. * This function converts a number to text using currency format, with the decimals rounded to the specified place.
  7706. * The format used is $#,##0.00_);($#,##0.00)..
  7707. *
  7708. * @param float $value The value to format
  7709. * @param int $decimals The number of digits to display to the right of the decimal point.
  7710. * If decimals is negative, number is rounded to the left of the decimal point.
  7711. * If you omit decimals, it is assumed to be 2
  7712. * @return string
  7713. */
  7714. public static function DOLLAR($value = 0, $decimals = 2) {
  7715. $value = self::flattenSingleValue($value);
  7716. $decimals = is_null($decimals) ? 0 : self::flattenSingleValue($decimals);
  7717. // Validate parameters
  7718. if (!is_numeric($value) || !is_numeric($decimals)) {
  7719. return self::$_errorCodes['num'];
  7720. }
  7721. $decimals = floor($decimals);
  7722. if ($decimals > 0) {
  7723. return money_format('%.'.$decimals.'n',$value);
  7724. } else {
  7725. $round = pow(10,abs($decimals));
  7726. if ($value < 0) { $round = 0-$round; }
  7727. $value = self::MROUND($value,$round);
  7728. // The implementation of money_format used if the standard PHP function is not available can't handle decimal places of 0,
  7729. // so we display to 1 dp and chop off that character and the decimal separator using substr
  7730. return substr(money_format('%.1n',$value),0,-2);
  7731. }
  7732. } // function DOLLAR()
  7733. /**
  7734. * DOLLARDE
  7735. *
  7736. * Converts a dollar price expressed as an integer part and a fraction part into a dollar price expressed as a decimal number.
  7737. * Fractional dollar numbers are sometimes used for security prices.
  7738. *
  7739. * @param float $fractional_dollar Fractional Dollar
  7740. * @param int $fraction Fraction
  7741. * @return float
  7742. */
  7743. public static function DOLLARDE($fractional_dollar = Null, $fraction = 0) {
  7744. $fractional_dollar = self::flattenSingleValue($fractional_dollar);
  7745. $fraction = (int)self::flattenSingleValue($fraction);
  7746. // Validate parameters
  7747. if (is_null($fractional_dollar) || $fraction < 0) {
  7748. return self::$_errorCodes['num'];
  7749. }
  7750. if ($fraction == 0) {
  7751. return self::$_errorCodes['divisionbyzero'];
  7752. }
  7753. $dollars = floor($fractional_dollar);
  7754. $cents = fmod($fractional_dollar,1);
  7755. $cents /= $fraction;
  7756. $cents *= pow(10,ceil(log10($fraction)));
  7757. return $dollars + $cents;
  7758. } // function DOLLARDE()
  7759. /**
  7760. * DOLLARFR
  7761. *
  7762. * Converts a dollar price expressed as a decimal number into a dollar price expressed as a fraction.
  7763. * Fractional dollar numbers are sometimes used for security prices.
  7764. *
  7765. * @param float $decimal_dollar Decimal Dollar
  7766. * @param int $fraction Fraction
  7767. * @return float
  7768. */
  7769. public static function DOLLARFR($decimal_dollar = Null, $fraction = 0) {
  7770. $decimal_dollar = self::flattenSingleValue($decimal_dollar);
  7771. $fraction = (int)self::flattenSingleValue($fraction);
  7772. // Validate parameters
  7773. if (is_null($decimal_dollar) || $fraction < 0) {
  7774. return self::$_errorCodes['num'];
  7775. }
  7776. if ($fraction == 0) {
  7777. return self::$_errorCodes['divisionbyzero'];
  7778. }
  7779. $dollars = floor($decimal_dollar);
  7780. $cents = fmod($decimal_dollar,1);
  7781. $cents *= $fraction;
  7782. $cents *= pow(10,-ceil(log10($fraction)));
  7783. return $dollars + $cents;
  7784. } // function DOLLARFR()
  7785. /**
  7786. * EFFECT
  7787. *
  7788. * Returns the effective interest rate given the nominal rate and the number of compounding payments per year.
  7789. *
  7790. * @param float $nominal_rate Nominal interest rate
  7791. * @param int $npery Number of compounding payments per year
  7792. * @return float
  7793. */
  7794. public static function EFFECT($nominal_rate = 0, $npery = 0) {
  7795. $nominal_rate = self::flattenSingleValue($nominal_rate);
  7796. $npery = (int)self::flattenSingleValue($npery);
  7797. // Validate parameters
  7798. if ($nominal_rate <= 0 || $npery < 1) {
  7799. return self::$_errorCodes['num'];
  7800. }
  7801. return pow((1 + $nominal_rate / $npery), $npery) - 1;
  7802. } // function EFFECT()
  7803. /**
  7804. * NOMINAL
  7805. *
  7806. * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
  7807. *
  7808. * @param float $effect_rate Effective interest rate
  7809. * @param int $npery Number of compounding payments per year
  7810. * @return float
  7811. */
  7812. public static function NOMINAL($effect_rate = 0, $npery = 0) {
  7813. $effect_rate = self::flattenSingleValue($effect_rate);
  7814. $npery = (int)self::flattenSingleValue($npery);
  7815. // Validate parameters
  7816. if ($effect_rate <= 0 || $npery < 1) {
  7817. return self::$_errorCodes['num'];
  7818. }
  7819. // Calculate
  7820. return $npery * (pow($effect_rate + 1, 1 / $npery) - 1);
  7821. } // function NOMINAL()
  7822. /**
  7823. * PV
  7824. *
  7825. * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
  7826. *
  7827. * @param float $rate Interest rate per period
  7828. * @param int $nper Number of periods
  7829. * @param float $pmt Periodic payment (annuity)
  7830. * @param float $fv Future Value
  7831. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7832. * @return float
  7833. */
  7834. public static function PV($rate = 0, $nper = 0, $pmt = 0, $fv = 0, $type = 0) {
  7835. $rate = self::flattenSingleValue($rate);
  7836. $nper = self::flattenSingleValue($nper);
  7837. $pmt = self::flattenSingleValue($pmt);
  7838. $fv = self::flattenSingleValue($fv);
  7839. $type = self::flattenSingleValue($type);
  7840. // Validate parameters
  7841. if ($type != 0 && $type != 1) {
  7842. return self::$_errorCodes['num'];
  7843. }
  7844. // Calculate
  7845. if (!is_null($rate) && $rate != 0) {
  7846. return (-$pmt * (1 + $rate * $type) * ((pow(1 + $rate, $nper) - 1) / $rate) - $fv) / pow(1 + $rate, $nper);
  7847. } else {
  7848. return -$fv - $pmt * $nper;
  7849. }
  7850. } // function PV()
  7851. /**
  7852. * FV
  7853. *
  7854. * Returns the Future Value of a cash flow with constant payments and interest rate (annuities).
  7855. *
  7856. * @param float $rate Interest rate per period
  7857. * @param int $nper Number of periods
  7858. * @param float $pmt Periodic payment (annuity)
  7859. * @param float $pv Present Value
  7860. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7861. * @return float
  7862. */
  7863. public static function FV($rate = 0, $nper = 0, $pmt = 0, $pv = 0, $type = 0) {
  7864. $rate = self::flattenSingleValue($rate);
  7865. $nper = self::flattenSingleValue($nper);
  7866. $pmt = self::flattenSingleValue($pmt);
  7867. $pv = self::flattenSingleValue($pv);
  7868. $type = self::flattenSingleValue($type);
  7869. // Validate parameters
  7870. if ($type != 0 && $type != 1) {
  7871. return self::$_errorCodes['num'];
  7872. }
  7873. // Calculate
  7874. if (!is_null($rate) && $rate != 0) {
  7875. return -$pv * pow(1 + $rate, $nper) - $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1) / $rate;
  7876. } else {
  7877. return -$pv - $pmt * $nper;
  7878. }
  7879. } // function FV()
  7880. /**
  7881. * PMT
  7882. *
  7883. * Returns the constant payment (annuity) for a cash flow with a constant interest rate.
  7884. *
  7885. * @param float $rate Interest rate per period
  7886. * @param int $nper Number of periods
  7887. * @param float $pv Present Value
  7888. * @param float $fv Future Value
  7889. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7890. * @return float
  7891. */
  7892. public static function PMT($rate = 0, $nper = 0, $pv = 0, $fv = 0, $type = 0) {
  7893. $rate = self::flattenSingleValue($rate);
  7894. $nper = self::flattenSingleValue($nper);
  7895. $pv = self::flattenSingleValue($pv);
  7896. $fv = self::flattenSingleValue($fv);
  7897. $type = self::flattenSingleValue($type);
  7898. // Validate parameters
  7899. if ($type != 0 && $type != 1) {
  7900. return self::$_errorCodes['num'];
  7901. }
  7902. // Calculate
  7903. if (!is_null($rate) && $rate != 0) {
  7904. return (-$fv - $pv * pow(1 + $rate, $nper)) / (1 + $rate * $type) / ((pow(1 + $rate, $nper) - 1) / $rate);
  7905. } else {
  7906. return (-$pv - $fv) / $nper;
  7907. }
  7908. } // function PMT()
  7909. /**
  7910. * NPER
  7911. *
  7912. * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
  7913. *
  7914. * @param float $rate Interest rate per period
  7915. * @param int $pmt Periodic payment (annuity)
  7916. * @param float $pv Present Value
  7917. * @param float $fv Future Value
  7918. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7919. * @return float
  7920. */
  7921. public static function NPER($rate = 0, $pmt = 0, $pv = 0, $fv = 0, $type = 0) {
  7922. $rate = self::flattenSingleValue($rate);
  7923. $pmt = self::flattenSingleValue($pmt);
  7924. $pv = self::flattenSingleValue($pv);
  7925. $fv = self::flattenSingleValue($fv);
  7926. $type = self::flattenSingleValue($type);
  7927. // Validate parameters
  7928. if ($type != 0 && $type != 1) {
  7929. return self::$_errorCodes['num'];
  7930. }
  7931. // Calculate
  7932. if (!is_null($rate) && $rate != 0) {
  7933. if ($pmt == 0 && $pv == 0) {
  7934. return self::$_errorCodes['num'];
  7935. }
  7936. return log(($pmt * (1 + $rate * $type) / $rate - $fv) / ($pv + $pmt * (1 + $rate * $type) / $rate)) / log(1 + $rate);
  7937. } else {
  7938. if ($pmt == 0) {
  7939. return self::$_errorCodes['num'];
  7940. }
  7941. return (-$pv -$fv) / $pmt;
  7942. }
  7943. } // function NPER()
  7944. private static function _interestAndPrincipal($rate=0, $per=0, $nper=0, $pv=0, $fv=0, $type=0) {
  7945. $pmt = self::PMT($rate, $nper, $pv, $fv, $type);
  7946. $capital = $pv;
  7947. for ($i = 1; $i<= $per; ++$i) {
  7948. $interest = ($type && $i == 1)? 0 : -$capital * $rate;
  7949. $principal = $pmt - $interest;
  7950. $capital += $principal;
  7951. }
  7952. return array($interest, $principal);
  7953. } // function _interestAndPrincipal()
  7954. /**
  7955. * IPMT
  7956. *
  7957. * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
  7958. *
  7959. * @param float $rate Interest rate per period
  7960. * @param int $per Period for which we want to find the interest
  7961. * @param int $nper Number of periods
  7962. * @param float $pv Present Value
  7963. * @param float $fv Future Value
  7964. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7965. * @return float
  7966. */
  7967. public static function IPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0) {
  7968. $rate = self::flattenSingleValue($rate);
  7969. $per = (int) self::flattenSingleValue($per);
  7970. $nper = (int) self::flattenSingleValue($nper);
  7971. $pv = self::flattenSingleValue($pv);
  7972. $fv = self::flattenSingleValue($fv);
  7973. $type = (int) self::flattenSingleValue($type);
  7974. // Validate parameters
  7975. if ($type != 0 && $type != 1) {
  7976. return self::$_errorCodes['num'];
  7977. }
  7978. if ($per <= 0 || $per > $nper) {
  7979. return self::$_errorCodes['value'];
  7980. }
  7981. // Calculate
  7982. $interestAndPrincipal = self::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
  7983. return $interestAndPrincipal[0];
  7984. } // function IPMT()
  7985. /**
  7986. * CUMIPMT
  7987. *
  7988. * Returns the cumulative interest paid on a loan between start_period and end_period.
  7989. *
  7990. * @param float $rate Interest rate per period
  7991. * @param int $nper Number of periods
  7992. * @param float $pv Present Value
  7993. * @param int start The first period in the calculation.
  7994. * Payment periods are numbered beginning with 1.
  7995. * @param int end The last period in the calculation.
  7996. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  7997. * @return float
  7998. */
  7999. public static function CUMIPMT($rate, $nper, $pv, $start, $end, $type = 0) {
  8000. $rate = self::flattenSingleValue($rate);
  8001. $nper = (int) self::flattenSingleValue($nper);
  8002. $pv = self::flattenSingleValue($pv);
  8003. $start = (int) self::flattenSingleValue($start);
  8004. $end = (int) self::flattenSingleValue($end);
  8005. $type = (int) self::flattenSingleValue($type);
  8006. // Validate parameters
  8007. if ($type != 0 && $type != 1) {
  8008. return self::$_errorCodes['num'];
  8009. }
  8010. if ($start < 1 || $start > $end) {
  8011. return self::$_errorCodes['value'];
  8012. }
  8013. // Calculate
  8014. $interest = 0;
  8015. for ($per = $start; $per <= $end; ++$per) {
  8016. $interest += self::IPMT($rate, $per, $nper, $pv, 0, $type);
  8017. }
  8018. return $interest;
  8019. } // function CUMIPMT()
  8020. /**
  8021. * PPMT
  8022. *
  8023. * Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.
  8024. *
  8025. * @param float $rate Interest rate per period
  8026. * @param int $per Period for which we want to find the interest
  8027. * @param int $nper Number of periods
  8028. * @param float $pv Present Value
  8029. * @param float $fv Future Value
  8030. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  8031. * @return float
  8032. */
  8033. public static function PPMT($rate, $per, $nper, $pv, $fv = 0, $type = 0) {
  8034. $rate = self::flattenSingleValue($rate);
  8035. $per = (int) self::flattenSingleValue($per);
  8036. $nper = (int) self::flattenSingleValue($nper);
  8037. $pv = self::flattenSingleValue($pv);
  8038. $fv = self::flattenSingleValue($fv);
  8039. $type = (int) self::flattenSingleValue($type);
  8040. // Validate parameters
  8041. if ($type != 0 && $type != 1) {
  8042. return self::$_errorCodes['num'];
  8043. }
  8044. if ($per <= 0 || $per > $nper) {
  8045. return self::$_errorCodes['value'];
  8046. }
  8047. // Calculate
  8048. $interestAndPrincipal = self::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
  8049. return $interestAndPrincipal[1];
  8050. } // function PPMT()
  8051. /**
  8052. * CUMPRINC
  8053. *
  8054. * Returns the cumulative principal paid on a loan between start_period and end_period.
  8055. *
  8056. * @param float $rate Interest rate per period
  8057. * @param int $nper Number of periods
  8058. * @param float $pv Present Value
  8059. * @param int start The first period in the calculation.
  8060. * Payment periods are numbered beginning with 1.
  8061. * @param int end The last period in the calculation.
  8062. * @param int $type Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  8063. * @return float
  8064. */
  8065. public static function CUMPRINC($rate, $nper, $pv, $start, $end, $type = 0) {
  8066. $rate = self::flattenSingleValue($rate);
  8067. $nper = (int) self::flattenSingleValue($nper);
  8068. $pv = self::flattenSingleValue($pv);
  8069. $start = (int) self::flattenSingleValue($start);
  8070. $end = (int) self::flattenSingleValue($end);
  8071. $type = (int) self::flattenSingleValue($type);
  8072. // Validate parameters
  8073. if ($type != 0 && $type != 1) {
  8074. return self::$_errorCodes['num'];
  8075. }
  8076. if ($start < 1 || $start > $end) {
  8077. return self::$_errorCodes['value'];
  8078. }
  8079. // Calculate
  8080. $principal = 0;
  8081. for ($per = $start; $per <= $end; ++$per) {
  8082. $principal += self::PPMT($rate, $per, $nper, $pv, 0, $type);
  8083. }
  8084. return $principal;
  8085. } // function CUMPRINC()
  8086. /**
  8087. * NPV
  8088. *
  8089. * Returns the Net Present Value of a cash flow series given a discount rate.
  8090. *
  8091. * @param float Discount interest rate
  8092. * @param array Cash flow series
  8093. * @return float
  8094. */
  8095. public static function NPV() {
  8096. // Return value
  8097. $returnValue = 0;
  8098. // Loop through arguments
  8099. $aArgs = self::flattenArray(func_get_args());
  8100. // Calculate
  8101. $rate = array_shift($aArgs);
  8102. for ($i = 1; $i <= count($aArgs); ++$i) {
  8103. // Is it a numeric value?
  8104. if (is_numeric($aArgs[$i - 1])) {
  8105. $returnValue += $aArgs[$i - 1] / pow(1 + $rate, $i);
  8106. }
  8107. }
  8108. // Return
  8109. return $returnValue;
  8110. } // function NPV()
  8111. /**
  8112. * DB
  8113. *
  8114. * Returns the depreciation of an asset for a specified period using the fixed-declining balance method.
  8115. * This form of depreciation is used if you want to get a higher depreciation value at the beginning of the depreciation
  8116. * (as opposed to linear depreciation). The depreciation value is reduced with every depreciation period by the
  8117. * depreciation already deducted from the initial cost.
  8118. *
  8119. * @param float cost Initial cost of the asset.
  8120. * @param float salvage Value at the end of the depreciation. (Sometimes called the salvage value of the asset)
  8121. * @param int life Number of periods over which the asset is depreciated. (Sometimes called the useful life of the asset)
  8122. * @param int period The period for which you want to calculate the depreciation. Period must use the same units as life.
  8123. * @param float month Number of months in the first year. If month is omitted, it defaults to 12.
  8124. * @return float
  8125. */
  8126. public static function DB($cost, $salvage, $life, $period, $month=12) {
  8127. $cost = (float) self::flattenSingleValue($cost);
  8128. $salvage = (float) self::flattenSingleValue($salvage);
  8129. $life = (int) self::flattenSingleValue($life);
  8130. $period = (int) self::flattenSingleValue($period);
  8131. $month = (int) self::flattenSingleValue($month);
  8132. // Validate
  8133. if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($month))) {
  8134. if ($cost == 0) {
  8135. return 0.0;
  8136. } elseif (($cost < 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($month < 1)) {
  8137. return self::$_errorCodes['num'];
  8138. }
  8139. // Set Fixed Depreciation Rate
  8140. $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life));
  8141. $fixedDepreciationRate = round($fixedDepreciationRate, 3);
  8142. // Loop through each period calculating the depreciation
  8143. $previousDepreciation = 0;
  8144. for ($per = 1; $per <= $period; ++$per) {
  8145. if ($per == 1) {
  8146. $depreciation = $cost * $fixedDepreciationRate * $month / 12;
  8147. } elseif ($per == ($life + 1)) {
  8148. $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate * (12 - $month) / 12;
  8149. } else {
  8150. $depreciation = ($cost - $previousDepreciation) * $fixedDepreciationRate;
  8151. }
  8152. $previousDepreciation += $depreciation;
  8153. }
  8154. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  8155. $depreciation = round($depreciation,2);
  8156. }
  8157. return $depreciation;
  8158. }
  8159. return self::$_errorCodes['value'];
  8160. } // function DB()
  8161. /**
  8162. * DDB
  8163. *
  8164. * Returns the depreciation of an asset for a specified period using the double-declining balance method or some other method you specify.
  8165. *
  8166. * @param float cost Initial cost of the asset.
  8167. * @param float salvage Value at the end of the depreciation. (Sometimes called the salvage value of the asset)
  8168. * @param int life Number of periods over which the asset is depreciated. (Sometimes called the useful life of the asset)
  8169. * @param int period The period for which you want to calculate the depreciation. Period must use the same units as life.
  8170. * @param float factor The rate at which the balance declines.
  8171. * If factor is omitted, it is assumed to be 2 (the double-declining balance method).
  8172. * @return float
  8173. */
  8174. public static function DDB($cost, $salvage, $life, $period, $factor=2.0) {
  8175. $cost = (float) self::flattenSingleValue($cost);
  8176. $salvage = (float) self::flattenSingleValue($salvage);
  8177. $life = (int) self::flattenSingleValue($life);
  8178. $period = (int) self::flattenSingleValue($period);
  8179. $factor = (float) self::flattenSingleValue($factor);
  8180. // Validate
  8181. if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period)) && (is_numeric($factor))) {
  8182. if (($cost <= 0) || (($salvage / $cost) < 0) || ($life <= 0) || ($period < 1) || ($factor <= 0.0) || ($period > $life)) {
  8183. return self::$_errorCodes['num'];
  8184. }
  8185. // Set Fixed Depreciation Rate
  8186. $fixedDepreciationRate = 1 - pow(($salvage / $cost), (1 / $life));
  8187. $fixedDepreciationRate = round($fixedDepreciationRate, 3);
  8188. // Loop through each period calculating the depreciation
  8189. $previousDepreciation = 0;
  8190. for ($per = 1; $per <= $period; ++$per) {
  8191. $depreciation = min( ($cost - $previousDepreciation) * ($factor / $life), ($cost - $salvage - $previousDepreciation) );
  8192. $previousDepreciation += $depreciation;
  8193. }
  8194. if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC) {
  8195. $depreciation = round($depreciation,2);
  8196. }
  8197. return $depreciation;
  8198. }
  8199. return self::$_errorCodes['value'];
  8200. } // function DDB()
  8201. private static function _daysPerYear($year,$basis) {
  8202. switch ($basis) {
  8203. case 0 :
  8204. case 2 :
  8205. case 4 :
  8206. $daysPerYear = 360;
  8207. break;
  8208. case 3 :
  8209. $daysPerYear = 365;
  8210. break;
  8211. case 1 :
  8212. if (self::_isLeapYear(self::YEAR($year))) {
  8213. $daysPerYear = 366;
  8214. } else {
  8215. $daysPerYear = 365;
  8216. }
  8217. break;
  8218. default :
  8219. return self::$_errorCodes['num'];
  8220. }
  8221. return $daysPerYear;
  8222. } // function _daysPerYear()
  8223. /**
  8224. * ACCRINT
  8225. *
  8226. * Returns the discount rate for a security.
  8227. *
  8228. * @param mixed issue The security's issue date.
  8229. * @param mixed firstinter The security's first interest date.
  8230. * @param mixed settlement The security's settlement date.
  8231. * @param float rate The security's annual coupon rate.
  8232. * @param float par The security's par value.
  8233. * @param int basis The type of day count to use.
  8234. * 0 or omitted US (NASD) 30/360
  8235. * 1 Actual/actual
  8236. * 2 Actual/360
  8237. * 3 Actual/365
  8238. * 4 European 30/360
  8239. * @return float
  8240. */
  8241. public static function ACCRINT($issue, $firstinter, $settlement, $rate, $par=1000, $frequency=1, $basis=0) {
  8242. $issue = self::flattenSingleValue($issue);
  8243. $firstinter = self::flattenSingleValue($firstinter);
  8244. $settlement = self::flattenSingleValue($settlement);
  8245. $rate = (float) self::flattenSingleValue($rate);
  8246. $par = (is_null($par)) ? 1000 : (float) self::flattenSingleValue($par);
  8247. $frequency = (is_null($frequency)) ? 1 : (int) self::flattenSingleValue($frequency);
  8248. $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
  8249. // Validate
  8250. if ((is_numeric($rate)) && (is_numeric($par))) {
  8251. if (($rate <= 0) || ($par <= 0)) {
  8252. return self::$_errorCodes['num'];
  8253. }
  8254. $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
  8255. if (!is_numeric($daysBetweenIssueAndSettlement)) {
  8256. return $daysBetweenIssueAndSettlement;
  8257. }
  8258. $daysPerYear = self::_daysPerYear(self::YEAR($issue),$basis);
  8259. if (!is_numeric($daysPerYear)) {
  8260. return $daysPerYear;
  8261. }
  8262. $daysBetweenIssueAndSettlement *= $daysPerYear;
  8263. return $par * $rate * ($daysBetweenIssueAndSettlement / $daysPerYear);
  8264. }
  8265. return self::$_errorCodes['value'];
  8266. } // function ACCRINT()
  8267. /**
  8268. * ACCRINTM
  8269. *
  8270. * Returns the discount rate for a security.
  8271. *
  8272. * @param mixed issue The security's issue date.
  8273. * @param mixed settlement The security's settlement date.
  8274. * @param float rate The security's annual coupon rate.
  8275. * @param float par The security's par value.
  8276. * @param int basis The type of day count to use.
  8277. * 0 or omitted US (NASD) 30/360
  8278. * 1 Actual/actual
  8279. * 2 Actual/360
  8280. * 3 Actual/365
  8281. * 4 European 30/360
  8282. * @return float
  8283. */
  8284. public static function ACCRINTM($issue, $settlement, $rate, $par=1000, $basis=0) {
  8285. $issue = self::flattenSingleValue($issue);
  8286. $settlement = self::flattenSingleValue($settlement);
  8287. $rate = (float) self::flattenSingleValue($rate);
  8288. $par = (is_null($par)) ? 1000 : (float) self::flattenSingleValue($par);
  8289. $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis);
  8290. // Validate
  8291. if ((is_numeric($rate)) && (is_numeric($par))) {
  8292. if (($rate <= 0) || ($par <= 0)) {
  8293. return self::$_errorCodes['num'];
  8294. }
  8295. $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
  8296. if (!is_numeric($daysBetweenIssueAndSettlement)) {
  8297. return $daysBetweenIssueAndSettlement;
  8298. }
  8299. $daysPerYear = self::_daysPerYear(self::YEAR($issue),$basis);
  8300. if (!is_numeric($daysPerYear)) {
  8301. return $daysPerYear;
  8302. }
  8303. $daysBetweenIssueAndSettlement *= $daysPerYear;
  8304. return $par * $rate * ($daysBetweenIssueAndSettlement / $daysPerYear);
  8305. }
  8306. return self::$_errorCodes['value'];
  8307. } // function ACCRINTM()
  8308. public static function AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis=0) {
  8309. $cost = self::flattenSingleValue($cost);
  8310. $purchased = self::flattenSingleValue($purchased);
  8311. $firstPeriod = self::flattenSingleValue($firstPeriod);
  8312. $salvage = self::flattenSingleValue($salvage);
  8313. $period = floor(self::flattenSingleValue($period));
  8314. $rate = self::flattenSingleValue($rate);
  8315. $basis = floor(self::flattenSingleValue($basis));
  8316. $fUsePer = 1.0 / $rate;
  8317. if ($fUsePer < 3.0) {
  8318. $amortiseCoeff = 1.0;
  8319. } elseif ($fUsePer < 5.0) {
  8320. $amortiseCoeff = 1.5;
  8321. } elseif ($fUsePer <= 6.0) {
  8322. $amortiseCoeff = 2.0;
  8323. } else {
  8324. $amortiseCoeff = 2.5;
  8325. }
  8326. $rate *= $amortiseCoeff;
  8327. $fNRate = floor((self::YEARFRAC($purchased, $firstPeriod, $basis) * $rate * $cost) + 0.5);
  8328. $cost -= $fNRate;
  8329. $fRest = $cost - $salvage;
  8330. for ($n = 0; $n < $period; ++$n) {
  8331. $fNRate = floor(($rate * $cost) + 0.5);
  8332. $fRest -= $fNRate;
  8333. if ($fRest < 0.0) {
  8334. switch ($period - $n) {
  8335. case 0 :
  8336. case 1 : return floor(($cost * 0.5) + 0.5);
  8337. break;
  8338. default : return 0.0;
  8339. break;
  8340. }
  8341. }
  8342. $cost -= $fNRate;
  8343. }
  8344. return $fNRate;
  8345. } // function AMORDEGRC()
  8346. public static function AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis=0) {
  8347. $cost = self::flattenSingleValue($cost);
  8348. $purchased = self::flattenSingleValue($purchased);
  8349. $firstPeriod = self::flattenSingleValue($firstPeriod);
  8350. $salvage = self::flattenSingleValue($salvage);
  8351. $period = self::flattenSingleValue($period);
  8352. $rate = self::flattenSingleValue($rate);
  8353. $basis = self::flattenSingleValue($basis);
  8354. $fOneRate = $cost * $rate;
  8355. $fCostDelta = $cost - $salvage;
  8356. $f0Rate = self::YEARFRAC($purchased, $firstPeriod, $basis) * $rate * $cost;
  8357. $nNumOfFullPeriods = intval(($cost - $salvage - $f0Rate) / $fOneRate);
  8358. if ($period == 0) {
  8359. return $f0Rate;
  8360. } elseif ($period <= $nNumOfFullPeriods) {
  8361. return $fOneRate;
  8362. } elseif ($period == ($nNumOfFullPeriods + 1)) {
  8363. return ($fCostDelta - $fOneRate * $nNumOfFullPeriods - $f0Rate);
  8364. } else {
  8365. return 0.0;
  8366. }
  8367. } // function AMORLINC()
  8368. public static function COUPNUM($settlement, $maturity, $frequency, $basis=0) {
  8369. $settlement = self::flattenSingleValue($settlement);
  8370. $maturity = self::flattenSingleValue($maturity);
  8371. $frequency = self::flattenSingleValue($frequency);
  8372. $basis = self::flattenSingleValue($basis);
  8373. $dsm = self::YEARFRAC($settlement, $maturity, $basis) * 365;
  8374. switch ($frequency) {
  8375. case 1: // anual payments
  8376. return ceil($dsm / 360);
  8377. case 2: // semiannual
  8378. return ceil($dsm / 180);
  8379. case 4: // quarterly
  8380. return ceil($dsm / 90);
  8381. }
  8382. return null;
  8383. } // function COUPNUM()
  8384. /**
  8385. * DISC
  8386. *
  8387. * Returns the discount rate for a security.
  8388. *
  8389. * @param mixed settlement The security's settlement date.
  8390. * The security settlement date is the date after the issue date when the security is traded to the buyer.
  8391. * @param mixed maturity The security's maturity date.
  8392. * The maturity date is the date when the security expires.
  8393. * @param int price The security's price per $100 face value.
  8394. * @param int redemption the security's redemption value per $100 face value.
  8395. * @param int basis The type of day count to use.
  8396. * 0 or omitted US (NASD) 30/360
  8397. * 1 Actual/actual
  8398. * 2 Actual/360
  8399. * 3 Actual/365
  8400. * 4 European 30/360
  8401. * @return float
  8402. */
  8403. public static function DISC($settlement, $maturity, $price, $redemption, $basis=0) {
  8404. $settlement = self::flattenSingleValue($settlement);
  8405. $maturity = self::flattenSingleValue($maturity);
  8406. $price = (float) self::flattenSingleValue($price);
  8407. $redemption = (float) self::flattenSingleValue($redemption);
  8408. $basis = (int) self::flattenSingleValue($basis);
  8409. // Validate
  8410. if ((is_numeric($price)) && (is_numeric($redemption)) && (is_numeric($basis))) {
  8411. if (($price <= 0) || ($redemption <= 0)) {
  8412. return self::$_errorCodes['num'];
  8413. }
  8414. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
  8415. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8416. return $daysBetweenSettlementAndMaturity;
  8417. }
  8418. return ((1 - $price / $redemption) / $daysBetweenSettlementAndMaturity);
  8419. }
  8420. return self::$_errorCodes['value'];
  8421. } // function DISC()
  8422. /**
  8423. * PRICEDISC
  8424. *
  8425. * Returns the price per $100 face value of a discounted security.
  8426. *
  8427. * @param mixed settlement The security's settlement date.
  8428. * The security settlement date is the date after the issue date when the security is traded to the buyer.
  8429. * @param mixed maturity The security's maturity date.
  8430. * The maturity date is the date when the security expires.
  8431. * @param int discount The security's discount rate.
  8432. * @param int redemption The security's redemption value per $100 face value.
  8433. * @param int basis The type of day count to use.
  8434. * 0 or omitted US (NASD) 30/360
  8435. * 1 Actual/actual
  8436. * 2 Actual/360
  8437. * 3 Actual/365
  8438. * 4 European 30/360
  8439. * @return float
  8440. */
  8441. public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis=0) {
  8442. $settlement = self::flattenSingleValue($settlement);
  8443. $maturity = self::flattenSingleValue($maturity);
  8444. $discount = (float) self::flattenSingleValue($discount);
  8445. $redemption = (float) self::flattenSingleValue($redemption);
  8446. $basis = (int) self::flattenSingleValue($basis);
  8447. // Validate
  8448. if ((is_numeric($discount)) && (is_numeric($redemption)) && (is_numeric($basis))) {
  8449. if (($discount <= 0) || ($redemption <= 0)) {
  8450. return self::$_errorCodes['num'];
  8451. }
  8452. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
  8453. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8454. return $daysBetweenSettlementAndMaturity;
  8455. }
  8456. return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
  8457. }
  8458. return self::$_errorCodes['value'];
  8459. } // function PRICEDISC()
  8460. /**
  8461. * PRICEMAT
  8462. *
  8463. * Returns the price per $100 face value of a security that pays interest at maturity.
  8464. *
  8465. * @param mixed settlement The security's settlement date.
  8466. * The security's settlement date is the date after the issue date when the security is traded to the buyer.
  8467. * @param mixed maturity The security's maturity date.
  8468. * The maturity date is the date when the security expires.
  8469. * @param mixed issue The security's issue date.
  8470. * @param int rate The security's interest rate at date of issue.
  8471. * @param int yield The security's annual yield.
  8472. * @param int basis The type of day count to use.
  8473. * 0 or omitted US (NASD) 30/360
  8474. * 1 Actual/actual
  8475. * 2 Actual/360
  8476. * 3 Actual/365
  8477. * 4 European 30/360
  8478. * @return float
  8479. */
  8480. public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis=0) {
  8481. $settlement = self::flattenSingleValue($settlement);
  8482. $maturity = self::flattenSingleValue($maturity);
  8483. $issue = self::flattenSingleValue($issue);
  8484. $rate = self::flattenSingleValue($rate);
  8485. $yield = self::flattenSingleValue($yield);
  8486. $basis = (int) self::flattenSingleValue($basis);
  8487. // Validate
  8488. if (is_numeric($rate) && is_numeric($yield)) {
  8489. if (($rate <= 0) || ($yield <= 0)) {
  8490. return self::$_errorCodes['num'];
  8491. }
  8492. $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
  8493. if (!is_numeric($daysPerYear)) {
  8494. return $daysPerYear;
  8495. }
  8496. $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
  8497. if (!is_numeric($daysBetweenIssueAndSettlement)) {
  8498. return $daysBetweenIssueAndSettlement;
  8499. }
  8500. $daysBetweenIssueAndSettlement *= $daysPerYear;
  8501. $daysBetweenIssueAndMaturity = self::YEARFRAC($issue, $maturity, $basis);
  8502. if (!is_numeric($daysBetweenIssueAndMaturity)) {
  8503. return $daysBetweenIssueAndMaturity;
  8504. }
  8505. $daysBetweenIssueAndMaturity *= $daysPerYear;
  8506. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
  8507. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8508. return $daysBetweenSettlementAndMaturity;
  8509. }
  8510. $daysBetweenSettlementAndMaturity *= $daysPerYear;
  8511. return ((100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100)) /
  8512. (1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
  8513. (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100));
  8514. }
  8515. return self::$_errorCodes['value'];
  8516. } // function PRICEMAT()
  8517. /**
  8518. * RECEIVED
  8519. *
  8520. * Returns the price per $100 face value of a discounted security.
  8521. *
  8522. * @param mixed settlement The security's settlement date.
  8523. * The security settlement date is the date after the issue date when the security is traded to the buyer.
  8524. * @param mixed maturity The security's maturity date.
  8525. * The maturity date is the date when the security expires.
  8526. * @param int investment The amount invested in the security.
  8527. * @param int discount The security's discount rate.
  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 RECEIVED($settlement, $maturity, $investment, $discount, $basis=0) {
  8537. $settlement = self::flattenSingleValue($settlement);
  8538. $maturity = self::flattenSingleValue($maturity);
  8539. $investment = (float) self::flattenSingleValue($investment);
  8540. $discount = (float) self::flattenSingleValue($discount);
  8541. $basis = (int) self::flattenSingleValue($basis);
  8542. // Validate
  8543. if ((is_numeric($investment)) && (is_numeric($discount)) && (is_numeric($basis))) {
  8544. if (($investment <= 0) || ($discount <= 0)) {
  8545. return self::$_errorCodes['num'];
  8546. }
  8547. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
  8548. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8549. return $daysBetweenSettlementAndMaturity;
  8550. }
  8551. return $investment / ( 1 - ($discount * $daysBetweenSettlementAndMaturity));
  8552. }
  8553. return self::$_errorCodes['value'];
  8554. } // function RECEIVED()
  8555. /**
  8556. * INTRATE
  8557. *
  8558. * Returns the interest rate for a fully invested security.
  8559. *
  8560. * @param mixed settlement The security's settlement date.
  8561. * The security settlement date is the date after the issue date when the security is traded to the buyer.
  8562. * @param mixed maturity The security's maturity date.
  8563. * The maturity date is the date when the security expires.
  8564. * @param int investment The amount invested in the security.
  8565. * @param int redemption The amount to be received at maturity.
  8566. * @param int basis The type of day count to use.
  8567. * 0 or omitted US (NASD) 30/360
  8568. * 1 Actual/actual
  8569. * 2 Actual/360
  8570. * 3 Actual/365
  8571. * 4 European 30/360
  8572. * @return float
  8573. */
  8574. public static function INTRATE($settlement, $maturity, $investment, $redemption, $basis=0) {
  8575. $settlement = self::flattenSingleValue($settlement);
  8576. $maturity = self::flattenSingleValue($maturity);
  8577. $investment = (float) self::flattenSingleValue($investment);
  8578. $redemption = (float) self::flattenSingleValue($redemption);
  8579. $basis = (int) self::flattenSingleValue($basis);
  8580. // Validate
  8581. if ((is_numeric($investment)) && (is_numeric($redemption)) && (is_numeric($basis))) {
  8582. if (($investment <= 0) || ($redemption <= 0)) {
  8583. return self::$_errorCodes['num'];
  8584. }
  8585. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
  8586. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8587. return $daysBetweenSettlementAndMaturity;
  8588. }
  8589. return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
  8590. }
  8591. return self::$_errorCodes['value'];
  8592. } // function INTRATE()
  8593. /**
  8594. * TBILLEQ
  8595. *
  8596. * Returns the bond-equivalent yield for a Treasury bill.
  8597. *
  8598. * @param mixed settlement The Treasury bill's settlement date.
  8599. * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
  8600. * @param mixed maturity The Treasury bill's maturity date.
  8601. * The maturity date is the date when the Treasury bill expires.
  8602. * @param int discount The Treasury bill's discount rate.
  8603. * @return float
  8604. */
  8605. public static function TBILLEQ($settlement, $maturity, $discount) {
  8606. $settlement = self::flattenSingleValue($settlement);
  8607. $maturity = self::flattenSingleValue($maturity);
  8608. $discount = self::flattenSingleValue($discount);
  8609. // Use TBILLPRICE for validation
  8610. $testValue = self::TBILLPRICE($settlement, $maturity, $discount);
  8611. if (is_string($testValue)) {
  8612. return $testValue;
  8613. }
  8614. if (is_string($maturity = self::_getDateValue($maturity))) {
  8615. return self::$_errorCodes['value'];
  8616. }
  8617. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  8618. ++$maturity;
  8619. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity) * 360;
  8620. } else {
  8621. $daysBetweenSettlementAndMaturity = (self::_getDateValue($maturity) - self::_getDateValue($settlement));
  8622. }
  8623. return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
  8624. } // function TBILLEQ()
  8625. /**
  8626. * TBILLPRICE
  8627. *
  8628. * Returns the yield for a Treasury bill.
  8629. *
  8630. * @param mixed settlement The Treasury bill's settlement date.
  8631. * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
  8632. * @param mixed maturity The Treasury bill's maturity date.
  8633. * The maturity date is the date when the Treasury bill expires.
  8634. * @param int discount The Treasury bill's discount rate.
  8635. * @return float
  8636. */
  8637. public static function TBILLPRICE($settlement, $maturity, $discount) {
  8638. $settlement = self::flattenSingleValue($settlement);
  8639. $maturity = self::flattenSingleValue($maturity);
  8640. $discount = self::flattenSingleValue($discount);
  8641. if (is_string($maturity = self::_getDateValue($maturity))) {
  8642. return self::$_errorCodes['value'];
  8643. }
  8644. // Validate
  8645. if (is_numeric($discount)) {
  8646. if ($discount <= 0) {
  8647. return self::$_errorCodes['num'];
  8648. }
  8649. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  8650. ++$maturity;
  8651. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity) * 360;
  8652. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8653. return $daysBetweenSettlementAndMaturity;
  8654. }
  8655. } else {
  8656. $daysBetweenSettlementAndMaturity = (self::_getDateValue($maturity) - self::_getDateValue($settlement));
  8657. }
  8658. if ($daysBetweenSettlementAndMaturity > 360) {
  8659. return self::$_errorCodes['num'];
  8660. }
  8661. $price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
  8662. if ($price <= 0) {
  8663. return self::$_errorCodes['num'];
  8664. }
  8665. return $price;
  8666. }
  8667. return self::$_errorCodes['value'];
  8668. } // function TBILLPRICE()
  8669. /**
  8670. * TBILLYIELD
  8671. *
  8672. * Returns the yield for a Treasury bill.
  8673. *
  8674. * @param mixed settlement The Treasury bill's settlement date.
  8675. * The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.
  8676. * @param mixed maturity The Treasury bill's maturity date.
  8677. * The maturity date is the date when the Treasury bill expires.
  8678. * @param int price The Treasury bill's price per $100 face value.
  8679. * @return float
  8680. */
  8681. public static function TBILLYIELD($settlement, $maturity, $price) {
  8682. $settlement = self::flattenSingleValue($settlement);
  8683. $maturity = self::flattenSingleValue($maturity);
  8684. $price = self::flattenSingleValue($price);
  8685. // Validate
  8686. if (is_numeric($price)) {
  8687. if ($price <= 0) {
  8688. return self::$_errorCodes['num'];
  8689. }
  8690. if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE) {
  8691. ++$maturity;
  8692. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity) * 360;
  8693. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8694. return $daysBetweenSettlementAndMaturity;
  8695. }
  8696. } else {
  8697. $daysBetweenSettlementAndMaturity = (self::_getDateValue($maturity) - self::_getDateValue($settlement));
  8698. }
  8699. if ($daysBetweenSettlementAndMaturity > 360) {
  8700. return self::$_errorCodes['num'];
  8701. }
  8702. return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
  8703. }
  8704. return self::$_errorCodes['value'];
  8705. } // function TBILLYIELD()
  8706. /**
  8707. * SLN
  8708. *
  8709. * Returns the straight-line depreciation of an asset for one period
  8710. *
  8711. * @param cost Initial cost of the asset
  8712. * @param salvage Value at the end of the depreciation
  8713. * @param life Number of periods over which the asset is depreciated
  8714. * @return float
  8715. */
  8716. public static function SLN($cost, $salvage, $life) {
  8717. $cost = self::flattenSingleValue($cost);
  8718. $salvage = self::flattenSingleValue($salvage);
  8719. $life = self::flattenSingleValue($life);
  8720. // Calculate
  8721. if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life))) {
  8722. if ($life < 0) {
  8723. return self::$_errorCodes['num'];
  8724. }
  8725. return ($cost - $salvage) / $life;
  8726. }
  8727. return self::$_errorCodes['value'];
  8728. } // function SLN()
  8729. /**
  8730. * YIELDMAT
  8731. *
  8732. * Returns the annual yield of a security that pays interest at maturity.
  8733. *
  8734. * @param mixed settlement The security's settlement date.
  8735. * The security's settlement date is the date after the issue date when the security is traded to the buyer.
  8736. * @param mixed maturity The security's maturity date.
  8737. * The maturity date is the date when the security expires.
  8738. * @param mixed issue The security's issue date.
  8739. * @param int rate The security's interest rate at date of issue.
  8740. * @param int price The security's price per $100 face value.
  8741. * @param int basis The type of day count to use.
  8742. * 0 or omitted US (NASD) 30/360
  8743. * 1 Actual/actual
  8744. * 2 Actual/360
  8745. * 3 Actual/365
  8746. * 4 European 30/360
  8747. * @return float
  8748. */
  8749. public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis=0) {
  8750. $settlement = self::flattenSingleValue($settlement);
  8751. $maturity = self::flattenSingleValue($maturity);
  8752. $issue = self::flattenSingleValue($issue);
  8753. $rate = self::flattenSingleValue($rate);
  8754. $price = self::flattenSingleValue($price);
  8755. $basis = (int) self::flattenSingleValue($basis);
  8756. // Validate
  8757. if (is_numeric($rate) && is_numeric($price)) {
  8758. if (($rate <= 0) || ($price <= 0)) {
  8759. return self::$_errorCodes['num'];
  8760. }
  8761. $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
  8762. if (!is_numeric($daysPerYear)) {
  8763. return $daysPerYear;
  8764. }
  8765. $daysBetweenIssueAndSettlement = self::YEARFRAC($issue, $settlement, $basis);
  8766. if (!is_numeric($daysBetweenIssueAndSettlement)) {
  8767. return $daysBetweenIssueAndSettlement;
  8768. }
  8769. $daysBetweenIssueAndSettlement *= $daysPerYear;
  8770. $daysBetweenIssueAndMaturity = self::YEARFRAC($issue, $maturity, $basis);
  8771. if (!is_numeric($daysBetweenIssueAndMaturity)) {
  8772. return $daysBetweenIssueAndMaturity;
  8773. }
  8774. $daysBetweenIssueAndMaturity *= $daysPerYear;
  8775. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity, $basis);
  8776. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8777. return $daysBetweenSettlementAndMaturity;
  8778. }
  8779. $daysBetweenSettlementAndMaturity *= $daysPerYear;
  8780. return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) - (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
  8781. (($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
  8782. ($daysPerYear / $daysBetweenSettlementAndMaturity);
  8783. }
  8784. return self::$_errorCodes['value'];
  8785. } // function YIELDMAT()
  8786. /**
  8787. * YIELDDISC
  8788. *
  8789. * Returns the annual yield of a security that pays interest at maturity.
  8790. *
  8791. * @param mixed settlement The security's settlement date.
  8792. * The security's settlement date is the date after the issue date when the security is traded to the buyer.
  8793. * @param mixed maturity The security's maturity date.
  8794. * The maturity date is the date when the security expires.
  8795. * @param int price The security's price per $100 face value.
  8796. * @param int redemption The security's redemption value per $100 face value.
  8797. * @param int basis The type of day count to use.
  8798. * 0 or omitted US (NASD) 30/360
  8799. * 1 Actual/actual
  8800. * 2 Actual/360
  8801. * 3 Actual/365
  8802. * 4 European 30/360
  8803. * @return float
  8804. */
  8805. public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis=0) {
  8806. $settlement = self::flattenSingleValue($settlement);
  8807. $maturity = self::flattenSingleValue($maturity);
  8808. $price = self::flattenSingleValue($price);
  8809. $redemption = self::flattenSingleValue($redemption);
  8810. $basis = (int) self::flattenSingleValue($basis);
  8811. // Validate
  8812. if (is_numeric($price) && is_numeric($redemption)) {
  8813. if (($price <= 0) || ($redemption <= 0)) {
  8814. return self::$_errorCodes['num'];
  8815. }
  8816. $daysPerYear = self::_daysPerYear(self::YEAR($settlement),$basis);
  8817. if (!is_numeric($daysPerYear)) {
  8818. return $daysPerYear;
  8819. }
  8820. $daysBetweenSettlementAndMaturity = self::YEARFRAC($settlement, $maturity,$basis);
  8821. if (!is_numeric($daysBetweenSettlementAndMaturity)) {
  8822. return $daysBetweenSettlementAndMaturity;
  8823. }
  8824. $daysBetweenSettlementAndMaturity *= $daysPerYear;
  8825. return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
  8826. }
  8827. return self::$_errorCodes['value'];
  8828. } // function YIELDDISC()
  8829. /**
  8830. * CELL_ADDRESS
  8831. *
  8832. * Creates a cell address as text, given specified row and column numbers.
  8833. *
  8834. * @param row Row number to use in the cell reference
  8835. * @param column Column number to use in the cell reference
  8836. * @param relativity Flag indicating the type of reference to return
  8837. * 1 or omitted Absolute
  8838. * 2 Absolute row; relative column
  8839. * 3 Relative row; absolute column
  8840. * 4 Relative
  8841. * @param referenceStyle A logical value that specifies the A1 or R1C1 reference style.
  8842. * TRUE or omitted CELL_ADDRESS returns an A1-style reference
  8843. * FALSE CELL_ADDRESS returns an R1C1-style reference
  8844. * @param sheetText Optional Name of worksheet to use
  8845. * @return string
  8846. */
  8847. public static function CELL_ADDRESS($row, $column, $relativity=1, $referenceStyle=True, $sheetText='') {
  8848. $row = self::flattenSingleValue($row);
  8849. $column = self::flattenSingleValue($column);
  8850. $relativity = self::flattenSingleValue($relativity);
  8851. $sheetText = self::flattenSingleValue($sheetText);
  8852. if (($row < 1) || ($column < 1)) {
  8853. return self::$_errorCodes['value'];
  8854. }
  8855. if ($sheetText > '') {
  8856. if (strpos($sheetText,' ') !== False) { $sheetText = "'".$sheetText."'"; }
  8857. $sheetText .='!';
  8858. }
  8859. if ((!is_bool($referenceStyle)) || $referenceStyle) {
  8860. $rowRelative = $columnRelative = '$';
  8861. $column = PHPExcel_Cell::stringFromColumnIndex($column-1);
  8862. if (($relativity == 2) || ($relativity == 4)) { $columnRelative = ''; }
  8863. if (($relativity == 3) || ($relativity == 4)) { $rowRelative = ''; }
  8864. return $sheetText.$columnRelative.$column.$rowRelative.$row;
  8865. } else {
  8866. if (($relativity == 2) || ($relativity == 4)) { $column = '['.$column.']'; }
  8867. if (($relativity == 3) || ($relativity == 4)) { $row = '['.$row.']'; }
  8868. return $sheetText.'R'.$row.'C'.$column;
  8869. }
  8870. } // function CELL_ADDRESS()
  8871. /**
  8872. * COLUMN
  8873. *
  8874. * Returns the column number of the given cell reference
  8875. * If the cell reference is a range of cells, COLUMN returns the column numbers of each column in the reference as a horizontal array.
  8876. * If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the
  8877. * reference of the cell in which the COLUMN function appears; otherwise this function returns 0.
  8878. *
  8879. * @param cellAddress A reference to a range of cells for which you want the column numbers
  8880. * @return integer or array of integer
  8881. */
  8882. public static function COLUMN($cellAddress=Null) {
  8883. if (is_null($cellAddress) || trim($cellAddress) === '') { return 0; }
  8884. if (is_array($cellAddress)) {
  8885. foreach($cellAddress as $columnKey => $value) {
  8886. $columnKey = preg_replace('/[^a-z]/i','',$columnKey);
  8887. return (integer) PHPExcel_Cell::columnIndexFromString($columnKey);
  8888. }
  8889. } else {
  8890. if (strpos($cellAddress,'!') !== false) {
  8891. list($sheet,$cellAddress) = explode('!',$cellAddress);
  8892. }
  8893. if (strpos($cellAddress,':') !== false) {
  8894. list($startAddress,$endAddress) = explode(':',$cellAddress);
  8895. $startAddress = preg_replace('/[^a-z]/i','',$startAddress);
  8896. $endAddress = preg_replace('/[^a-z]/i','',$endAddress);
  8897. $returnValue = array();
  8898. do {
  8899. $returnValue[] = (integer) PHPExcel_Cell::columnIndexFromString($startAddress);
  8900. } while ($startAddress++ != $endAddress);
  8901. return $returnValue;
  8902. } else {
  8903. $cellAddress = preg_replace('/[^a-z]/i','',$cellAddress);
  8904. return (integer) PHPExcel_Cell::columnIndexFromString($cellAddress);
  8905. }
  8906. }
  8907. } // function COLUMN()
  8908. /**
  8909. * COLUMNS
  8910. *
  8911. * Returns the number of columns in an array or reference.
  8912. *
  8913. * @param cellAddress An array or array formula, or a reference to a range of cells for which you want the number of columns
  8914. * @return integer
  8915. */
  8916. public static function COLUMNS($cellAddress=Null) {
  8917. if (is_null($cellAddress) || $cellAddress === '') {
  8918. return 0;
  8919. } elseif (!is_array($cellAddress)) {
  8920. return self::$_errorCodes['value'];
  8921. }
  8922. $isMatrix = (is_numeric(array_shift(array_keys($cellAddress))));
  8923. list($columns,$rows) = PHPExcel_Calculation::_getMatrixDimensions($cellAddress);
  8924. if ($isMatrix) {
  8925. return $rows;
  8926. } else {
  8927. return $columns;
  8928. }
  8929. } // function COLUMNS()
  8930. /**
  8931. * ROW
  8932. *
  8933. * Returns the row number of the given cell reference
  8934. * If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference as a vertical array.
  8935. * If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the
  8936. * reference of the cell in which the ROW function appears; otherwise this function returns 0.
  8937. *
  8938. * @param cellAddress A reference to a range of cells for which you want the row numbers
  8939. * @return integer or array of integer
  8940. */
  8941. public static function ROW($cellAddress=Null) {
  8942. if (is_null($cellAddress) || trim($cellAddress) === '') { return 0; }
  8943. if (is_array($cellAddress)) {
  8944. foreach($cellAddress as $columnKey => $rowValue) {
  8945. foreach($rowValue as $rowKey => $cellValue) {
  8946. return (integer) preg_replace('/[^0-9]/i','',$rowKey);
  8947. }
  8948. }
  8949. } else {
  8950. if (strpos($cellAddress,'!') !== false) {
  8951. list($sheet,$cellAddress) = explode('!',$cellAddress);
  8952. }
  8953. if (strpos($cellAddress,':') !== false) {
  8954. list($startAddress,$endAddress) = explode(':',$cellAddress);
  8955. $startAddress = preg_replace('/[^0-9]/','',$startAddress);
  8956. $endAddress = preg_replace('/[^0-9]/','',$endAddress);
  8957. $returnValue = array();
  8958. do {
  8959. $returnValue[][] = (integer) $startAddress;
  8960. } while ($startAddress++ != $endAddress);
  8961. return $returnValue;
  8962. } else {
  8963. list($cellAddress) = explode(':',$cellAddress);
  8964. return (integer) preg_replace('/[^0-9]/','',$cellAddress);
  8965. }
  8966. }
  8967. } // function ROW()
  8968. /**
  8969. * ROWS
  8970. *
  8971. * Returns the number of rows in an array or reference.
  8972. *
  8973. * @param cellAddress An array or array formula, or a reference to a range of cells for which you want the number of rows
  8974. * @return integer
  8975. */
  8976. public static function ROWS($cellAddress=Null) {
  8977. if (is_null($cellAddress) || $cellAddress === '') {
  8978. return 0;
  8979. } elseif (!is_array($cellAddress)) {
  8980. return self::$_errorCodes['value'];
  8981. }
  8982. $isMatrix = (is_numeric(array_shift(array_keys($cellAddress))));
  8983. list($columns,$rows) = PHPExcel_Calculation::_getMatrixDimensions($cellAddress);
  8984. if ($isMatrix) {
  8985. return $columns;
  8986. } else {
  8987. return $rows;
  8988. }
  8989. } // function ROWS()
  8990. /**
  8991. * INDIRECT
  8992. *
  8993. * Returns the number of rows in an array or reference.
  8994. *
  8995. * @param cellAddress An array or array formula, or a reference to a range of cells for which you want the number of rows
  8996. * @return integer
  8997. */
  8998. public static function INDIRECT($cellAddress=Null, PHPExcel_Cell $pCell = null) {
  8999. if (is_null($cellAddress) || $cellAddress === '') {
  9000. return self::REF();
  9001. }
  9002. $cellAddress1 = $cellAddress;
  9003. $cellAddress2 = NULL;
  9004. if (strpos($cellAddress,':') !== false) {
  9005. list($cellAddress1,$cellAddress2) = explode(':',$cellAddress);
  9006. }
  9007. if ((!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_CELLREF.'$/i', $cellAddress1, $matches)) ||
  9008. ((!is_null($cellAddress2)) && (!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_CELLREF.'$/i', $cellAddress2, $matches)))) {
  9009. return self::REF();
  9010. }
  9011. if (strpos($cellAddress,'!') !== false) {
  9012. list($sheetName,$cellAddress) = explode('!',$cellAddress);
  9013. $pSheet = $pCell->getParent()->getParent()->getSheetByName($sheetName);
  9014. } else {
  9015. $pSheet = $pCell->getParent();
  9016. }
  9017. return PHPExcel_Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, False);
  9018. } // function INDIRECT()
  9019. /**
  9020. * OFFSET
  9021. *
  9022. * Returns a reference to a range that is a specified number of rows and columns from a cell or range of cells.
  9023. * The reference that is returned can be a single cell or a range of cells. You can specify the number of rows and
  9024. * the number of columns to be returned.
  9025. *
  9026. * @param cellAddress The reference from which you want to base the offset. Reference must refer to a cell or
  9027. * range of adjacent cells; otherwise, OFFSET returns the #VALUE! error value.
  9028. * @param rows The number of rows, up or down, that you want the upper-left cell to refer to.
  9029. * Using 5 as the rows argument specifies that the upper-left cell in the reference is
  9030. * five rows below reference. Rows can be positive (which means below the starting reference)
  9031. * or negative (which means above the starting reference).
  9032. * @param cols The number of columns, to the left or right, that you want the upper-left cell of the result
  9033. * to refer to. Using 5 as the cols argument specifies that the upper-left cell in the
  9034. * reference is five columns to the right of reference. Cols can be positive (which means
  9035. * to the right of the starting reference) or negative (which means to the left of the
  9036. * starting reference).
  9037. * @param height The height, in number of rows, that you want the returned reference to be. Height must be a positive number.
  9038. * @param width The width, in number of columns, that you want the returned reference to be. Width must be a positive number.
  9039. * @return string A reference to a cell or range of cells
  9040. */
  9041. public static function OFFSET($cellAddress=Null,$rows=0,$columns=0,$height=null,$width=null) {
  9042. if ($cellAddress == Null) {
  9043. return 0;
  9044. }
  9045. $pCell = array_pop(func_get_args());
  9046. if (!is_object($pCell)) {
  9047. return self::$_errorCodes['reference'];
  9048. }
  9049. $sheetName = null;
  9050. if (strpos($cellAddress,"!")) {
  9051. list($sheetName,$cellAddress) = explode("!",$cellAddress);
  9052. }
  9053. if (strpos($cellAddress,":")) {
  9054. list($startCell,$endCell) = explode(":",$cellAddress);
  9055. } else {
  9056. $startCell = $endCell = $cellAddress;
  9057. }
  9058. list($startCellColumn,$startCellRow) = PHPExcel_Cell::coordinateFromString($startCell);
  9059. list($endCellColumn,$endCellRow) = PHPExcel_Cell::coordinateFromString($endCell);
  9060. $startCellRow += $rows;
  9061. $startCellColumn = PHPExcel_Cell::columnIndexFromString($startCellColumn) - 1;
  9062. $startCellColumn += $columns;
  9063. if (($startCellRow <= 0) || ($startCellColumn < 0)) {
  9064. return self::$_errorCodes['reference'];
  9065. }
  9066. $endCellColumn = PHPExcel_Cell::columnIndexFromString($endCellColumn) - 1;
  9067. if (($width != null) && (!is_object($width))) {
  9068. $endCellColumn = $startCellColumn + $width - 1;
  9069. } else {
  9070. $endCellColumn += $columns;
  9071. }
  9072. $startCellColumn = PHPExcel_Cell::stringFromColumnIndex($startCellColumn);
  9073. if (($height != null) && (!is_object($height))) {
  9074. $endCellRow = $startCellRow + $height - 1;
  9075. } else {
  9076. $endCellRow += $rows;
  9077. }
  9078. if (($endCellRow <= 0) || ($endCellColumn < 0)) {
  9079. return self::$_errorCodes['reference'];
  9080. }
  9081. $endCellColumn = PHPExcel_Cell::stringFromColumnIndex($endCellColumn);
  9082. $cellAddress = $startCellColumn.$startCellRow;
  9083. if (($startCellColumn != $endCellColumn) || ($startCellRow != $endCellRow)) {
  9084. $cellAddress .= ':'.$endCellColumn.$endCellRow;
  9085. }
  9086. if ($sheetName !== null) {
  9087. $pSheet = $pCell->getParent()->getParent()->getSheetByName($sheetName);
  9088. } else {
  9089. $pSheet = $pCell->getParent();
  9090. }
  9091. return PHPExcel_Calculation::getInstance()->extractCellRange($cellAddress, $pSheet, False);
  9092. } // function OFFSET()
  9093. public static function CHOOSE() {
  9094. $chooseArgs = func_get_args();
  9095. $chosenEntry = self::flattenArray(array_shift($chooseArgs));
  9096. $entryCount = count($chooseArgs) - 1;
  9097. if(is_array($chosenEntry)) {
  9098. $chosenEntry = array_shift($chosenEntry);
  9099. }
  9100. if ((is_numeric($chosenEntry)) && (!is_bool($chosenEntry))) {
  9101. --$chosenEntry;
  9102. } else {
  9103. return self::$_errorCodes['value'];
  9104. }
  9105. $chosenEntry = floor($chosenEntry);
  9106. if (($chosenEntry <= 0) || ($chosenEntry > $entryCount)) {
  9107. return self::$_errorCodes['value'];
  9108. }
  9109. if (is_array($chooseArgs[$chosenEntry])) {
  9110. return self::flattenArray($chooseArgs[$chosenEntry]);
  9111. } else {
  9112. return $chooseArgs[$chosenEntry];
  9113. }
  9114. } // function CHOOSE()
  9115. /**
  9116. * MATCH
  9117. *
  9118. * The MATCH function searches for a specified item in a range of cells
  9119. *
  9120. * @param lookup_value The value that you want to match in lookup_array
  9121. * @param lookup_array The range of cells being searched
  9122. * @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.
  9123. * @return integer The relative position of the found item
  9124. */
  9125. public static function MATCH($lookup_value, $lookup_array, $match_type=1) {
  9126. // flatten the lookup_array
  9127. $lookup_array = self::flattenArray($lookup_array);
  9128. // flatten lookup_value since it may be a cell reference to a value or the value itself
  9129. $lookup_value = self::flattenSingleValue($lookup_value);
  9130. // MATCH is not case sensitive
  9131. $lookup_value = strtolower($lookup_value);
  9132. /*
  9133. echo "--------------------<br>looking for $lookup_value in <br>";
  9134. print_r($lookup_array);
  9135. echo "<br>";
  9136. //return 1;
  9137. /**/
  9138. // **
  9139. // check inputs
  9140. // **
  9141. // lookup_value type has to be number, text, or logical values
  9142. if (!is_numeric($lookup_value) && !is_string($lookup_value) && !is_bool($lookup_value)){
  9143. // error: lookup_array should contain only number, text, or logical values
  9144. //echo "error: lookup_array should contain only number, text, or logical values<br>";
  9145. return self::$_errorCodes['na'];
  9146. }
  9147. // match_type is 0, 1 or -1
  9148. if ($match_type!==0 && $match_type!==-1 && $match_type!==1){
  9149. // error: wrong value for match_type
  9150. //echo "error: wrong value for match_type<br>";
  9151. return self::$_errorCodes['na'];
  9152. }
  9153. // lookup_array should not be empty
  9154. if (sizeof($lookup_array)<=0){
  9155. // error: empty range
  9156. //echo "error: empty range ".sizeof($lookup_array)."<br>";
  9157. return self::$_errorCodes['na'];
  9158. }
  9159. // lookup_array should contain only number, text, or logical values
  9160. for ($i=0;$i<sizeof($lookup_array);++$i){
  9161. // check the type of the value
  9162. if (!is_numeric($lookup_array[$i]) && !is_string($lookup_array[$i]) && !is_bool($lookup_array[$i])){
  9163. // error: lookup_array should contain only number, text, or logical values
  9164. //echo "error: lookup_array should contain only number, text, or logical values<br>";
  9165. return self::$_errorCodes['na'];
  9166. }
  9167. // convert tpo lowercase
  9168. if (is_string($lookup_array[$i]))
  9169. $lookup_array[$i] = strtolower($lookup_array[$i]);
  9170. }
  9171. // if match_type is 1 or -1, the list has to be ordered
  9172. if($match_type==1 || $match_type==-1){
  9173. // **
  9174. // iniitialization
  9175. // store the last value
  9176. $iLastValue=$lookup_array[0];
  9177. // **
  9178. // loop on the cells
  9179. for ($i=0;$i<sizeof($lookup_array);++$i){
  9180. // check ascending order
  9181. if(($match_type==1 && $lookup_array[$i]<$iLastValue)
  9182. // OR check descending order
  9183. || ($match_type==-1 && $lookup_array[$i]>$iLastValue)){
  9184. // error: list is not ordered correctly
  9185. //echo "error: list is not ordered correctly<br>";
  9186. return self::$_errorCodes['na'];
  9187. }
  9188. }
  9189. }
  9190. // **
  9191. // find the match
  9192. // **
  9193. // loop on the cells
  9194. for ($i=0; $i < sizeof($lookup_array); ++$i){
  9195. // if match_type is 0 <=> find the first value that is exactly equal to lookup_value
  9196. if ($match_type==0 && $lookup_array[$i]==$lookup_value){
  9197. // this is the exact match
  9198. return $i+1;
  9199. }
  9200. // if match_type is -1 <=> find the smallest value that is greater than or equal to lookup_value
  9201. if ($match_type==-1 && $lookup_array[$i] < $lookup_value){
  9202. if ($i<1){
  9203. // 1st cell was allready smaller than the lookup_value
  9204. break;
  9205. }
  9206. else
  9207. // the previous cell was the match
  9208. return $i;
  9209. }
  9210. // if match_type is 1 <=> find the largest value that is less than or equal to lookup_value
  9211. if ($match_type==1 && $lookup_array[$i] > $lookup_value){
  9212. if ($i<1){
  9213. // 1st cell was allready bigger than the lookup_value
  9214. break;
  9215. }
  9216. else
  9217. // the previous cell was the match
  9218. return $i;
  9219. }
  9220. }
  9221. // unsuccessful in finding a match, return #N/A error value
  9222. //echo "unsuccessful in finding a match<br>";
  9223. return self::$_errorCodes['na'];
  9224. } // function MATCH()
  9225. /**
  9226. * Uses an index to choose a value from a reference or array
  9227. * implemented: Return the value of a specified cell or array of cells Array form
  9228. * not implemented: Return a reference to specified cells Reference form
  9229. *
  9230. * @param range_array a range of cells or an array constant
  9231. * @param row_num selects the row in array from which to return a value. If row_num is omitted, column_num is required.
  9232. * @param column_num selects the column in array from which to return a value. If column_num is omitted, row_num is required.
  9233. */
  9234. public static function INDEX($arrayValues,$rowNum = 0,$columnNum = 0) {
  9235. if (($rowNum < 0) || ($columnNum < 0)) {
  9236. return self::$_errorCodes['value'];
  9237. }
  9238. $rowKeys = array_keys($arrayValues);
  9239. $columnKeys = @array_keys($arrayValues[$rowKeys[0]]);
  9240. if ($columnNum > count($columnKeys)) {
  9241. return self::$_errorCodes['value'];
  9242. } elseif ($columnNum == 0) {
  9243. if ($rowNum == 0) {
  9244. return $arrayValues;
  9245. }
  9246. $rowNum = $rowKeys[--$rowNum];
  9247. $returnArray = array();
  9248. foreach($arrayValues as $arrayColumn) {
  9249. if (is_array($arrayColumn)) {
  9250. if (isset($arrayColumn[$rowNum])) {
  9251. $returnArray[] = $arrayColumn[$rowNum];
  9252. } else {
  9253. return $arrayValues[$rowNum];
  9254. }
  9255. } else {
  9256. return $arrayValues[$rowNum];
  9257. }
  9258. }
  9259. return $returnArray;
  9260. }
  9261. $columnNum = $columnKeys[--$columnNum];
  9262. if ($rowNum > count($rowKeys)) {
  9263. return self::$_errorCodes['value'];
  9264. } elseif ($rowNum == 0) {
  9265. return $arrayValues[$columnNum];
  9266. }
  9267. $rowNum = $rowKeys[--$rowNum];
  9268. return $arrayValues[$rowNum][$columnNum];
  9269. } // function INDEX()
  9270. /**
  9271. * SYD
  9272. *
  9273. * Returns the sum-of-years' digits depreciation of an asset for a specified period.
  9274. *
  9275. * @param cost Initial cost of the asset
  9276. * @param salvage Value at the end of the depreciation
  9277. * @param life Number of periods over which the asset is depreciated
  9278. * @param period Period
  9279. * @return float
  9280. */
  9281. public static function SYD($cost, $salvage, $life, $period) {
  9282. $cost = self::flattenSingleValue($cost);
  9283. $salvage = self::flattenSingleValue($salvage);
  9284. $life = self::flattenSingleValue($life);
  9285. $period = self::flattenSingleValue($period);
  9286. // Calculate
  9287. if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period))) {
  9288. if (($life < 1) || ($period > $life)) {
  9289. return self::$_errorCodes['num'];
  9290. }
  9291. return (($cost - $salvage) * ($life - $period + 1) * 2) / ($life * ($life + 1));
  9292. }
  9293. return self::$_errorCodes['value'];
  9294. } // function SYD()
  9295. /**
  9296. * TRANSPOSE
  9297. *
  9298. * @param array $matrixData A matrix of values
  9299. * @return array
  9300. *
  9301. * Unlike the Excel TRANSPOSE function, which will only work on a single row or column, this function will transpose a full matrix.
  9302. */
  9303. public static function TRANSPOSE($matrixData) {
  9304. $returnMatrix = array();
  9305. if (!is_array($matrixData)) { $matrixData = array(array($matrixData)); }
  9306. $column = 0;
  9307. foreach($matrixData as $matrixRow) {
  9308. $row = 0;
  9309. foreach($matrixRow as $matrixCell) {
  9310. $returnMatrix[$row][$column] = $matrixCell;
  9311. ++$row;
  9312. }
  9313. ++$column;
  9314. }
  9315. return $returnMatrix;
  9316. } // function TRANSPOSE()
  9317. /**
  9318. * MMULT
  9319. *
  9320. * @param array $matrixData1 A matrix of values
  9321. * @param array $matrixData2 A matrix of values
  9322. * @return array
  9323. */
  9324. public static function MMULT($matrixData1,$matrixData2) {
  9325. $matrixAData = $matrixBData = array();
  9326. if (!is_array($matrixData1)) { $matrixData1 = array(array($matrixData1)); }
  9327. if (!is_array($matrixData2)) { $matrixData2 = array(array($matrixData2)); }
  9328. $rowA = 0;
  9329. foreach($matrixData1 as $matrixRow) {
  9330. $columnA = 0;
  9331. foreach($matrixRow as $matrixCell) {
  9332. if ((is_string($matrixCell)) || ($matrixCell === null)) {
  9333. return self::$_errorCodes['value'];
  9334. }
  9335. $matrixAData[$rowA][$columnA] = $matrixCell;
  9336. ++$columnA;
  9337. }
  9338. ++$rowA;
  9339. }
  9340. try {
  9341. $matrixA = new Matrix($matrixAData);
  9342. $rowB = 0;
  9343. foreach($matrixData2 as $matrixRow) {
  9344. $columnB = 0;
  9345. foreach($matrixRow as $matrixCell) {
  9346. if ((is_string($matrixCell)) || ($matrixCell === null)) {
  9347. return self::$_errorCodes['value'];
  9348. }
  9349. $matrixBData[$rowB][$columnB] = $matrixCell;
  9350. ++$columnB;
  9351. }
  9352. ++$rowB;
  9353. }
  9354. $matrixB = new Matrix($matrixBData);
  9355. if (($rowA != $columnB) || ($rowB != $columnA)) {
  9356. return self::$_errorCodes['value'];
  9357. }
  9358. return $matrixA->times($matrixB)->getArray();
  9359. } catch (Exception $ex) {
  9360. return self::$_errorCodes['value'];
  9361. }
  9362. } // function MMULT()
  9363. /**
  9364. * MINVERSE
  9365. *
  9366. * @param array $matrixValues A matrix of values
  9367. * @return array
  9368. */
  9369. public static function MINVERSE($matrixValues) {
  9370. $matrixData = array();
  9371. if (!is_array($matrixValues)) { $matrixValues = array(array($matrixValues)); }
  9372. $row = $maxColumn = 0;
  9373. foreach($matrixValues as $matrixRow) {
  9374. $column = 0;
  9375. foreach($matrixRow as $matrixCell) {
  9376. if ((is_string($matrixCell)) || ($matrixCell === null)) {
  9377. return self::$_errorCodes['value'];
  9378. }
  9379. $matrixData[$column][$row] = $matrixCell;
  9380. ++$column;
  9381. }
  9382. if ($column > $maxColumn) { $maxColumn = $column; }
  9383. ++$row;
  9384. }
  9385. if ($row != $maxColumn) { return self::$_errorCodes['value']; }
  9386. try {
  9387. $matrix = new Matrix($matrixData);
  9388. return $matrix->inverse()->getArray();
  9389. } catch (Exception $ex) {
  9390. return self::$_errorCodes['value'];
  9391. }
  9392. } // function MINVERSE()
  9393. /**
  9394. * MDETERM
  9395. *
  9396. * @param array $matrixValues A matrix of values
  9397. * @return float
  9398. */
  9399. public static function MDETERM($matrixValues) {
  9400. $matrixData = array();
  9401. if (!is_array($matrixValues)) { $matrixValues = array(array($matrixValues)); }
  9402. $row = $maxColumn = 0;
  9403. foreach($matrixValues as $matrixRow) {
  9404. $column = 0;
  9405. foreach($matrixRow as $matrixCell) {
  9406. if ((is_string($matrixCell)) || ($matrixCell === null)) {
  9407. return self::$_errorCodes['value'];
  9408. }
  9409. $matrixData[$column][$row] = $matrixCell;
  9410. ++$column;
  9411. }
  9412. if ($column > $maxColumn) { $maxColumn = $column; }
  9413. ++$row;
  9414. }
  9415. if ($row != $maxColumn) { return self::$_errorCodes['value']; }
  9416. try {
  9417. $matrix = new Matrix($matrixData);
  9418. return $matrix->det();
  9419. } catch (Exception $ex) {
  9420. return self::$_errorCodes['value'];
  9421. }
  9422. } // function MDETERM()
  9423. /**
  9424. * SUMX2MY2
  9425. *
  9426. * @param mixed $value Value to check
  9427. * @return float
  9428. */
  9429. public static function SUMX2MY2($matrixData1,$matrixData2) {
  9430. $array1 = self::flattenArray($matrixData1);
  9431. $array2 = self::flattenArray($matrixData2);
  9432. $count1 = count($array1);
  9433. $count2 = count($array2);
  9434. if ($count1 < $count2) {
  9435. $count = $count1;
  9436. } else {
  9437. $count = $count2;
  9438. }
  9439. $result = 0;
  9440. for ($i = 0; $i < $count; ++$i) {
  9441. if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
  9442. ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
  9443. $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
  9444. }
  9445. }
  9446. return $result;
  9447. } // function SUMX2MY2()
  9448. /**
  9449. * SUMX2PY2
  9450. *
  9451. * @param mixed $value Value to check
  9452. * @return float
  9453. */
  9454. public static function SUMX2PY2($matrixData1,$matrixData2) {
  9455. $array1 = self::flattenArray($matrixData1);
  9456. $array2 = self::flattenArray($matrixData2);
  9457. $count1 = count($array1);
  9458. $count2 = count($array2);
  9459. if ($count1 < $count2) {
  9460. $count = $count1;
  9461. } else {
  9462. $count = $count2;
  9463. }
  9464. $result = 0;
  9465. for ($i = 0; $i < $count; ++$i) {
  9466. if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
  9467. ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
  9468. $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
  9469. }
  9470. }
  9471. return $result;
  9472. } // function SUMX2PY2()
  9473. /**
  9474. * SUMXMY2
  9475. *
  9476. * @param mixed $value Value to check
  9477. * @return float
  9478. */
  9479. public static function SUMXMY2($matrixData1,$matrixData2) {
  9480. $array1 = self::flattenArray($matrixData1);
  9481. $array2 = self::flattenArray($matrixData2);
  9482. $count1 = count($array1);
  9483. $count2 = count($array2);
  9484. if ($count1 < $count2) {
  9485. $count = $count1;
  9486. } else {
  9487. $count = $count2;
  9488. }
  9489. $result = 0;
  9490. for ($i = 0; $i < $count; ++$i) {
  9491. if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
  9492. ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
  9493. $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
  9494. }
  9495. }
  9496. return $result;
  9497. } // function SUMXMY2()
  9498. private static function _vlookupSort($a,$b) {
  9499. $firstColumn = array_shift(array_keys($a));
  9500. if (strtolower($a[$firstColumn]) == strtolower($b[$firstColumn])) {
  9501. return 0;
  9502. }
  9503. return (strtolower($a[$firstColumn]) < strtolower($b[$firstColumn])) ? -1 : 1;
  9504. } // function _vlookupSort()
  9505. /**
  9506. * VLOOKUP
  9507. * 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.
  9508. * @param lookup_value The value that you want to match in lookup_array
  9509. * @param lookup_array The range of cells being searched
  9510. * @param index_number The column number in table_array from which the matching value must be returned. The first column is 1.
  9511. * @param not_exact_match Determines if you are looking for an exact match based on lookup_value.
  9512. * @return mixed The value of the found cell
  9513. */
  9514. public static function VLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match=true) {
  9515. // index_number must be greater than or equal to 1
  9516. if ($index_number < 1) {
  9517. return self::$_errorCodes['value'];
  9518. }
  9519. // index_number must be less than or equal to the number of columns in lookup_array
  9520. if ((!is_array($lookup_array)) || (count($lookup_array) < 1)) {
  9521. return self::$_errorCodes['reference'];
  9522. } else {
  9523. $firstRow = array_pop(array_keys($lookup_array));
  9524. if ((!is_array($lookup_array[$firstRow])) || ($index_number > count($lookup_array[$firstRow]))) {
  9525. return self::$_errorCodes['reference'];
  9526. } else {
  9527. $columnKeys = array_keys($lookup_array[$firstRow]);
  9528. $returnColumn = $columnKeys[--$index_number];
  9529. $firstColumn = array_shift($columnKeys);
  9530. }
  9531. }
  9532. if (!$not_exact_match) {
  9533. uasort($lookup_array,array('self','_vlookupSort'));
  9534. }
  9535. $rowNumber = $rowValue = False;
  9536. foreach($lookup_array as $rowKey => $rowData) {
  9537. if (strtolower($rowData[$firstColumn]) > strtolower($lookup_value)) {
  9538. break;
  9539. }
  9540. $rowNumber = $rowKey;
  9541. $rowValue = $rowData[$firstColumn];
  9542. }
  9543. if ($rowNumber !== false) {
  9544. if ((!$not_exact_match) && ($rowValue != $lookup_value)) {
  9545. // if an exact match is required, we have what we need to return an appropriate response
  9546. return self::$_errorCodes['na'];
  9547. } else {
  9548. // otherwise return the appropriate value
  9549. return $lookup_array[$rowNumber][$returnColumn];
  9550. }
  9551. }
  9552. return self::$_errorCodes['na'];
  9553. } // function VLOOKUP()
  9554. /**
  9555. * LOOKUP
  9556. * The LOOKUP function searches for value either from a one-row or one-column range or from an array.
  9557. * @param lookup_value The value that you want to match in lookup_array
  9558. * @param lookup_vector The range of cells being searched
  9559. * @param result_vector The column from which the matching value must be returned
  9560. * @return mixed The value of the found cell
  9561. */
  9562. public static function LOOKUP($lookup_value, $lookup_vector, $result_vector=null) {
  9563. if (!is_array($lookup_vector)) {
  9564. return self::$_errorCodes['na'];
  9565. }
  9566. $lookupRows = count($lookup_vector);
  9567. $lookupColumns = count($lookup_vector[array_shift(array_keys($lookup_vector))]);
  9568. if ((($lookupRows == 1) && ($lookupColumns > 1)) || (($lookupRows == 2) && ($lookupColumns != 2))) {
  9569. $lookup_vector = self::TRANSPOSE($lookup_vector);
  9570. $lookupRows = count($lookup_vector);
  9571. $lookupColumns = count($lookup_vector[array_shift(array_keys($lookup_vector))]);
  9572. }
  9573. if (is_null($result_vector)) {
  9574. $result_vector = $lookup_vector;
  9575. }
  9576. $resultRows = count($result_vector);
  9577. $resultColumns = count($result_vector[array_shift(array_keys($result_vector))]);
  9578. if ((($resultRows == 1) && ($resultColumns > 1)) || (($resultRows == 2) && ($resultColumns != 2))) {
  9579. $result_vector = self::TRANSPOSE($result_vector);
  9580. $resultRows = count($result_vector);
  9581. $resultColumns = count($result_vector[array_shift(array_keys($result_vector))]);
  9582. }
  9583. if ($lookupRows == 2) {
  9584. $result_vector = array_pop($lookup_vector);
  9585. $lookup_vector = array_shift($lookup_vector);
  9586. }
  9587. if ($lookupColumns != 2) {
  9588. foreach($lookup_vector as &$value) {
  9589. if (is_array($value)) {
  9590. $key1 = $key2 = array_shift(array_keys($value));
  9591. $key2++;
  9592. $dataValue1 = $value[$key1];
  9593. } else {
  9594. $key1 = 0;
  9595. $key2 = 1;
  9596. $dataValue1 = $value;
  9597. }
  9598. $dataValue2 = array_shift($result_vector);
  9599. if (is_array($dataValue2)) {
  9600. $dataValue2 = array_shift($dataValue2);
  9601. }
  9602. $value = array($key1 => $dataValue1, $key2 => $dataValue2);
  9603. }
  9604. unset($value);
  9605. }
  9606. return self::VLOOKUP($lookup_value,$lookup_vector,2);
  9607. } // function LOOKUP()
  9608. /**
  9609. * Flatten multidemensional array
  9610. *
  9611. * @param array $array Array to be flattened
  9612. * @return array Flattened array
  9613. */
  9614. public static function flattenArray($array) {
  9615. if (is_scalar($array)) {
  9616. return $array;
  9617. }
  9618. $arrayValues = array();
  9619. foreach ($array as $value) {
  9620. if (is_array($value)) {
  9621. $arrayValues = array_merge($arrayValues, self::flattenArray($value));
  9622. } else {
  9623. $arrayValues[] = $value;
  9624. }
  9625. }
  9626. return $arrayValues;
  9627. } // function flattenArray()
  9628. /**
  9629. * Convert an array with one element to a flat value
  9630. *
  9631. * @param mixed $value Array or flat value
  9632. * @return mixed
  9633. */
  9634. public static function flattenSingleValue($value = '') {
  9635. if (is_array($value)) {
  9636. return self::flattenSingleValue(array_pop($value));
  9637. }
  9638. return $value;
  9639. } // function flattenSingleValue()
  9640. } // class PHPExcel_Calculation_Functions
  9641. //
  9642. // There are a few mathematical functions that aren't available on all versions of PHP for all platforms
  9643. // These functions aren't available in Windows implementations of PHP prior to version 5.3.0
  9644. // So we test if they do exist for this version of PHP/operating platform; and if not we create them
  9645. //
  9646. if (!function_exists('acosh')) {
  9647. function acosh($x) {
  9648. return 2 * log(sqrt(($x + 1) / 2) + sqrt(($x - 1) / 2));
  9649. } // function acosh()
  9650. }
  9651. if (!function_exists('asinh')) {
  9652. function asinh($x) {
  9653. return log($x + sqrt(1 + $x * $x));
  9654. } // function asinh()
  9655. }
  9656. if (!function_exists('atanh')) {
  9657. function atanh($x) {
  9658. return (log(1 + $x) - log(1 - $x)) / 2;
  9659. } // function atanh()
  9660. }
  9661. if (!function_exists('money_format')) {
  9662. function money_format($format, $number) {
  9663. $regex = array( '/%((?:[\^!\-]|\+|\(|\=.)*)([0-9]+)?(?:#([0-9]+))?',
  9664. '(?:\.([0-9]+))?([in%])/'
  9665. );
  9666. $regex = implode('', $regex);
  9667. if (setlocale(LC_MONETARY, null) == '') {
  9668. setlocale(LC_MONETARY, '');
  9669. }
  9670. $locale = localeconv();
  9671. $number = floatval($number);
  9672. if (!preg_match($regex, $format, $fmatch)) {
  9673. trigger_error("No format specified or invalid format", E_USER_WARNING);
  9674. return $number;
  9675. }
  9676. $flags = array( 'fillchar' => preg_match('/\=(.)/', $fmatch[1], $match) ? $match[1] : ' ',
  9677. 'nogroup' => preg_match('/\^/', $fmatch[1]) > 0,
  9678. 'usesignal' => preg_match('/\+|\(/', $fmatch[1], $match) ? $match[0] : '+',
  9679. 'nosimbol' => preg_match('/\!/', $fmatch[1]) > 0,
  9680. 'isleft' => preg_match('/\-/', $fmatch[1]) > 0
  9681. );
  9682. $width = trim($fmatch[2]) ? (int)$fmatch[2] : 0;
  9683. $left = trim($fmatch[3]) ? (int)$fmatch[3] : 0;
  9684. $right = trim($fmatch[4]) ? (int)$fmatch[4] : $locale['int_frac_digits'];
  9685. $conversion = $fmatch[5];
  9686. $positive = true;
  9687. if ($number < 0) {
  9688. $positive = false;
  9689. $number *= -1;
  9690. }
  9691. $letter = $positive ? 'p' : 'n';
  9692. $prefix = $suffix = $cprefix = $csuffix = $signal = '';
  9693. if (!$positive) {
  9694. $signal = $locale['negative_sign'];
  9695. switch (true) {
  9696. case $locale['n_sign_posn'] == 0 || $flags['usesignal'] == '(':
  9697. $prefix = '(';
  9698. $suffix = ')';
  9699. break;
  9700. case $locale['n_sign_posn'] == 1:
  9701. $prefix = $signal;
  9702. break;
  9703. case $locale['n_sign_posn'] == 2:
  9704. $suffix = $signal;
  9705. break;
  9706. case $locale['n_sign_posn'] == 3:
  9707. $cprefix = $signal;
  9708. break;
  9709. case $locale['n_sign_posn'] == 4:
  9710. $csuffix = $signal;
  9711. break;
  9712. }
  9713. }
  9714. if (!$flags['nosimbol']) {
  9715. $currency = $cprefix;
  9716. $currency .= ($conversion == 'i' ? $locale['int_curr_symbol'] : $locale['currency_symbol']);
  9717. $currency .= $csuffix;
  9718. $currency = iconv('ISO-8859-1','UTF-8',$currency);
  9719. } else {
  9720. $currency = '';
  9721. }
  9722. $space = $locale["{$letter}_sep_by_space"] ? ' ' : '';
  9723. $number = number_format($number, $right, $locale['mon_decimal_point'], $flags['nogroup'] ? '' : $locale['mon_thousands_sep'] );
  9724. $number = explode($locale['mon_decimal_point'], $number);
  9725. $n = strlen($prefix) + strlen($currency);
  9726. if ($left > 0 && $left > $n) {
  9727. if ($flags['isleft']) {
  9728. $number[0] .= str_repeat($flags['fillchar'], $left - $n);
  9729. } else {
  9730. $number[0] = str_repeat($flags['fillchar'], $left - $n) . $number[0];
  9731. }
  9732. }
  9733. $number = implode($locale['mon_decimal_point'], $number);
  9734. if ($locale["{$letter}_cs_precedes"]) {
  9735. $number = $prefix . $currency . $space . $number . $suffix;
  9736. } else {
  9737. $number = $prefix . $number . $space . $currency . $suffix;
  9738. }
  9739. if ($width > 0) {
  9740. $number = str_pad($number, $width, $flags['fillchar'], $flags['isleft'] ? STR_PAD_RIGHT : STR_PAD_LEFT);
  9741. }
  9742. $format = str_replace($fmatch[0], $number, $format);
  9743. return $format;
  9744. } // function money_format()
  9745. }
  9746. //
  9747. // Strangely, PHP doesn't have a mb_str_replace multibyte function
  9748. // As we'll only ever use this function with UTF-8 characters, we can simply "hard-code" the character set
  9749. //
  9750. if ((!function_exists('mb_str_replace')) &&
  9751. (function_exists('mb_substr')) && (function_exists('mb_strlen')) && (function_exists('mb_strpos'))) {
  9752. function mb_str_replace($search, $replace, $subject) {
  9753. if(is_array($subject)) {
  9754. $ret = array();
  9755. foreach($subject as $key => $val) {
  9756. $ret[$key] = mb_str_replace($search, $replace, $val);
  9757. }
  9758. return $ret;
  9759. }
  9760. foreach((array) $search as $key => $s) {
  9761. if($s == '') {
  9762. continue;
  9763. }
  9764. $r = !is_array($replace) ? $replace : (array_key_exists($key, $replace) ? $replace[$key] : '');
  9765. $pos = mb_strpos($subject, $s, 0, 'UTF-8');
  9766. while($pos !== false) {
  9767. $subject = mb_substr($subject, 0, $pos, 'UTF-8') . $r . mb_substr($subject, $pos + mb_strlen($s, 'UTF-8'), 65535, 'UTF-8');
  9768. $pos = mb_strpos($subject, $s, $pos + mb_strlen($r, 'UTF-8'), 'UTF-8');
  9769. }
  9770. }
  9771. return $subject;
  9772. }
  9773. }