PageRenderTime 100ms CodeModel.GetById 7ms app.highlight 70ms RepoModel.GetById 1ms app.codeStats 2ms

/branches/wi16-graphs/Classes/PHPExcel/Calculation/Functions.php

#
PHP | 9539 lines | 5840 code | 883 blank | 2816 comment | 1625 complexity | 3add6adc98cb272c1888cc27bbd91a7f MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2
   3/**
   4 * PHPExcel
   5 *
   6 * Copyright (c) 2006 - 2009 PHPExcel
   7 *
   8 * This library is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU Lesser General Public
  10 * License as published by the Free Software Foundation; either
  11 * version 2.1 of the License, or (at your option) any later version.
  12 *
  13 * This library is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16 * Lesser General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU Lesser General Public
  19 * License along with this library; if not, write to the Free Software
  20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  21 *
  22 * @category   PHPExcel
  23 * @package	PHPExcel_Calculation
  24 * @copyright  Copyright (c) 2006 - 2009 PHPExcel (http://www.codeplex.com/PHPExcel)
  25 * @license	http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt	LGPL
  26 * @version	##VERSION##, ##DATE##
  27 */
  28
  29
  30define('EPS', 2.22e-16);
  31define('MAX_VALUE', 1.2e308);
  32define('LOG_GAMMA_X_MAX_VALUE', 2.55e305);
  33define('SQRT2PI', 2.5066282746310005024157652848110452530069867406099);
  34define('XMININ', 2.23e-308);
  35define('MAX_ITERATIONS', 150);
  36define('PRECISION', 8.88E-016);
  37define('EULER', 2.71828182845904523536);
  38
  39$savedPrecision = ini_get('precision');
  40if ($savedPrecision < 16) {
  41	ini_set('precision',16);
  42}
  43
  44
  45/** PHPExcel_Cell */
  46require_once 'PHPExcel/Cell.php';
  47
  48/** PHPExcel_Cell_DataType */
  49require_once 'PHPExcel/Cell/DataType.php';
  50
  51/** PHPExcel_Shared_Date */
  52require_once 'PHPExcel/Shared/Date.php';
  53
  54/** PHPExcel_Shared_Date */
  55require_once 'PHPExcel/Shared/JAMA/Matrix.php';
  56include_once('PHPExcel/Shared/trend/trendClass.php');
  57
  58
  59/**
  60 * PHPExcel_Calculation_Functions
  61 *
  62 * @category   PHPExcel
  63 * @package	PHPExcel_Calculation
  64 * @copyright  Copyright (c) 2006 - 2009 PHPExcel (http://www.codeplex.com/PHPExcel)
  65 */
  66class PHPExcel_Calculation_Functions {
  67
  68	/** constants */
  69	const COMPATIBILITY_EXCEL		= 'Excel';
  70	const COMPATIBILITY_GNUMERIC	= 'Gnumeric';
  71	const COMPATIBILITY_OPENOFFICE	= 'OpenOfficeCalc';
  72
  73	const RETURNDATE_PHP_NUMERIC = 'P';
  74	const RETURNDATE_PHP_OBJECT = 'O';
  75	const RETURNDATE_EXCEL = 'E';
  76
  77
  78	/**
  79	 *	Compatibility mode to use for error checking and responses
  80	 *
  81	 *	@var string
  82	 */
  83	private static $compatibilityMode	= self::COMPATIBILITY_EXCEL;
  84
  85	/**
  86	 *	Data Type to use when returning date values
  87	 *
  88	 *	@var integer
  89	 */
  90	private static $ReturnDateType	= self::RETURNDATE_PHP_NUMERIC;
  91
  92	/**
  93	 *	List of error codes
  94	 *
  95	 *	@var array
  96	 */
  97	private static $_errorCodes	= array( 'null'				=> '#NULL!',
  98										 'divisionbyzero'	=> '#DIV/0!',
  99										 'value'			=> '#VALUE!',
 100										 'reference'		=> '#REF!',
 101										 'name'				=> '#NAME?',
 102										 'num'				=> '#NUM!',
 103										 'na'				=> '#N/A',
 104										 'gettingdata'		=> '#GETTING_DATA'
 105									   );
 106
 107
 108	/**
 109	 *	Set the Compatibility Mode
 110	 *
 111	 *	@param	 string		$compatibilityMode		Compatibility Mode
 112	 *												Permitted values are:
 113	 *													PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL			'Excel'
 114	 *													PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC		'Gnumeric'
 115	 *													PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE	'OpenOfficeCalc'
 116	 *	@return	 boolean	(Success or Failure)
 117	 */
 118	public static function setCompatibilityMode($compatibilityMode) {
 119		if (($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
 120			($compatibilityMode == self::COMPATIBILITY_GNUMERIC) ||
 121			($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
 122			self::$compatibilityMode = $compatibilityMode;
 123			return True;
 124		}
 125		return False;
 126	}	//	function setCompatibilityMode()
 127
 128
 129	/**
 130	 *	Return the current Compatibility Mode
 131	 *
 132	 *	@return	 string		$compatibilityMode		Compatibility Mode
 133	 *												Possible Return values are:
 134	 *													PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL			'Excel'
 135	 *													PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC		'Gnumeric'
 136	 *													PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE	'OpenOfficeCalc'
 137	 */
 138	public static function getCompatibilityMode() {
 139		return self::$compatibilityMode;
 140	}	//	function getCompatibilityMode()
 141
 142
 143	/**
 144	 *	Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized or PHP Object)
 145	 *
 146	 *	@param	 integer	$returnDateType			Return Date Format
 147	 *												Permitted values are:
 148	 *													PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC		'P'
 149	 *													PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT		'O'
 150	 *													PHPExcel_Calculation_Functions::RETURNDATE_EXCEL			'E'
 151	 *	@return	 boolean							Success or failure
 152	 */
 153	public static function setReturnDateType($returnDateType) {
 154		if (($returnDateType == self::RETURNDATE_PHP_NUMERIC) ||
 155			($returnDateType == self::RETURNDATE_PHP_OBJECT) ||
 156			($returnDateType == self::RETURNDATE_EXCEL)) {
 157			self::$ReturnDateType = $returnDateType;
 158			return True;
 159		}
 160		return False;
 161	}	//	function setReturnDateType()
 162
 163
 164	/**
 165	 *	Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized or PHP Object)
 166	 *
 167	 *	@return	 integer	$returnDateType			Return Date Format
 168	 *												Possible Return values are:
 169	 *													PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC		'P'
 170	 *													PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT		'O'
 171	 *													PHPExcel_Calculation_Functions::RETURNDATE_EXCEL			'E'
 172	 */
 173	public static function getReturnDateType() {
 174		return self::$ReturnDateType;
 175	}	//	function getReturnDateType()
 176
 177
 178	/**
 179	 *	DUMMY
 180	 *
 181	 *	@return  string	#NAME?
 182	 */
 183	public static function DUMMY() {
 184		return '#Not Yet Implemented';
 185	}	//	function DUMMY()
 186
 187
 188	/**
 189	 *	NA
 190	 *
 191	 *	@return  string	#N/A!
 192	 */
 193	public static function NA() {
 194		return self::$_errorCodes['na'];
 195	}	//	function NA()
 196
 197
 198	/**
 199	 *	LOGICAL_AND
 200	 *
 201	 *	Returns boolean TRUE if all its arguments are TRUE; returns FALSE if one or more argument is FALSE.
 202	 *
 203	 *	Booleans arguments are treated as True or False as appropriate
 204	 *	Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
 205	 *	If any argument value is a string, or a Null, it is ignored
 206	 *
 207	 *	Quirk of Excel:
 208	 *		String values passed directly to the function rather than through a cell reference
 209	 *			e.g.=AND(1,"A",1)
 210	 *		will return a #VALUE! error, _not_ ignoring the string.
 211	 *		This behaviour is not replicated
 212	 *
 213	 *	@param	array of mixed		Data Series
 214	 *	@return  boolean
 215	 */
 216	public static function LOGICAL_AND() {
 217		// Return value
 218		$returnValue = True;
 219
 220		// Loop through the arguments
 221		$aArgs = self::flattenArray(func_get_args());
 222		$argCount = 0;
 223		foreach ($aArgs as $arg) {
 224			// Is it a boolean value?
 225			if (is_bool($arg)) {
 226				$returnValue = $returnValue && $arg;
 227				++$argCount;
 228			} elseif ((is_numeric($arg)) && (!is_string($arg))) {
 229				$returnValue = $returnValue && ($arg != 0);
 230				++$argCount;
 231			}
 232		}
 233
 234		// Return
 235		if ($argCount == 0) {
 236			return self::$_errorCodes['value'];
 237		}
 238		return $returnValue;
 239	}	//	function LOGICAL_AND()
 240
 241
 242	/**
 243	 *	LOGICAL_OR
 244	 *
 245	 *	Returns boolean TRUE if any argument is TRUE; returns FALSE if all arguments are FALSE.
 246	 *
 247	 *	Booleans arguments are treated as True or False as appropriate
 248	 *	Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
 249	 *	If any argument value is a string, or a Null, it is ignored
 250	 *
 251	 *	@param	array of mixed		Data Series
 252	 *	@return  boolean
 253	 */
 254	public static function LOGICAL_OR() {
 255		// Return value
 256		$returnValue = False;
 257
 258		// Loop through the arguments
 259		$aArgs = self::flattenArray(func_get_args());
 260		$argCount = 0;
 261		foreach ($aArgs as $arg) {
 262			// Is it a boolean value?
 263			if (is_bool($arg)) {
 264				$returnValue = $returnValue || $arg;
 265				++$argCount;
 266			} elseif ((is_numeric($arg)) && (!is_string($arg))) {
 267				$returnValue = $returnValue || ($arg != 0);
 268				++$argCount;
 269			}
 270		}
 271
 272		// Return
 273		if ($argCount == 0) {
 274			return self::$_errorCodes['value'];
 275		}
 276		return $returnValue;
 277	}	//	function LOGICAL_OR()
 278
 279
 280	/**
 281	 *	LOGICAL_FALSE
 282	 *
 283	 *	Returns FALSE.
 284	 *
 285	 *	@return  boolean
 286	 */
 287	public static function LOGICAL_FALSE() {
 288		return False;
 289	}	//	function LOGICAL_FALSE()
 290
 291
 292	/**
 293	 *	LOGICAL_TRUE
 294	 *
 295	 *	Returns TRUE.
 296	 *
 297	 *	@return  boolean
 298	 */
 299	public static function LOGICAL_TRUE() {
 300		return True;
 301	}	//	function LOGICAL_TRUE()
 302
 303
 304	/**
 305	 *	ATAN2
 306	 *
 307	 *	This function calculates the arc tangent of the two variables x and y. It is similar to
 308	 *		calculating the arc tangent of y / x, except that the signs of both arguments are used
 309	 *		to determine the quadrant of the result.
 310	 *	Note that Excel reverses the arguments, so we need to reverse them here before calling the
 311	 *		standard PHP atan() function
 312	 *
 313	 *	@param	float	$x		Number
 314	 *	@param	float	$y		Number
 315	 *	@return  float	Square Root of Number * Pi
 316	 */
 317	public static function REVERSE_ATAN2($x, $y) {
 318		$x	= self::flattenSingleValue($x);
 319		$y	= self::flattenSingleValue($y);
 320
 321		return atan2($y, $x);
 322	}	//	function REVERSE_ATAN2()
 323
 324
 325	/**
 326	 *	SUM
 327	 *
 328	 *	SUM computes the sum of all the values and cells referenced in the argument list.
 329	 *
 330	 *	@param	array of mixed		Data Series
 331	 *	@return  float
 332	 */
 333	public static function SUM() {
 334		// Return value
 335		$returnValue = 0;
 336
 337		// Loop through the arguments
 338		$aArgs = self::flattenArray(func_get_args());
 339		foreach ($aArgs as $arg) {
 340			// Is it a numeric value?
 341			if ((is_numeric($arg)) && (!is_string($arg))) {
 342				$returnValue += $arg;
 343			}
 344		}
 345
 346		// Return
 347		return $returnValue;
 348	}	//	function SUM()
 349
 350
 351	/**
 352	 *	SUMSQ
 353	 *
 354	 *	Returns the sum of the squares of the arguments
 355	 *
 356	 *	@param	array of mixed		Data Series
 357	 *	@return  float
 358	 */
 359	public static function SUMSQ() {
 360		// Return value
 361		$returnValue = 0;
 362
 363		// Loop trough arguments
 364		$aArgs = self::flattenArray(func_get_args());
 365		foreach ($aArgs as $arg) {
 366			// Is it a numeric value?
 367			if ((is_numeric($arg)) && (!is_string($arg))) {
 368				$returnValue += pow($arg,2);
 369			}
 370		}
 371
 372		// Return
 373		return $returnValue;
 374	}	//	function SUMSQ()
 375
 376
 377	/**
 378	 *	PRODUCT
 379	 *
 380	 *	PRODUCT returns the product of all the values and cells referenced in the argument list.
 381	 *
 382	 *	@param	array of mixed		Data Series
 383	 *	@return  float
 384	 */
 385	public static function PRODUCT() {
 386		// Return value
 387		$returnValue = null;
 388
 389		// Loop trough arguments
 390		$aArgs = self::flattenArray(func_get_args());
 391		foreach ($aArgs as $arg) {
 392			// Is it a numeric value?
 393			if ((is_numeric($arg)) && (!is_string($arg))) {
 394				if (is_null($returnValue)) {
 395					$returnValue = $arg;
 396				} else {
 397					$returnValue *= $arg;
 398				}
 399			}
 400		}
 401
 402		// Return
 403		if (is_null($returnValue)) {
 404			return 0;
 405		}
 406		return $returnValue;
 407	}	//	function PRODUCT()
 408
 409
 410	/**
 411	 *	QUOTIENT
 412	 *
 413	 *	QUOTIENT function returns the integer portion of a division.numerator is the divided number
 414	 *		and denominator is the divisor.
 415	 *
 416	 *	@param	array of mixed		Data Series
 417	 *	@return  float
 418	 */
 419	public static function QUOTIENT() {
 420		// Return value
 421		$returnValue = null;
 422
 423		// Loop trough arguments
 424		$aArgs = self::flattenArray(func_get_args());
 425		foreach ($aArgs as $arg) {
 426			// Is it a numeric value?
 427			if ((is_numeric($arg)) && (!is_string($arg))) {
 428				if (is_null($returnValue)) {
 429					if (($returnValue == 0) || ($arg == 0)) {
 430						$returnValue = 0;
 431					} else {
 432						$returnValue = $arg;
 433					}
 434				} else {
 435					if (($returnValue == 0) || ($arg == 0)) {
 436						$returnValue = 0;
 437					} else {
 438						$returnValue /= $arg;
 439					}
 440				}
 441			}
 442		}
 443
 444		// Return
 445		return intval($returnValue);
 446	}	//	function QUOTIENT()
 447
 448
 449	/**
 450	 *	MIN
 451	 *
 452	 *	MIN returns the value of the element of the values passed that has the smallest value,
 453	 *		with negative numbers considered smaller than positive numbers.
 454	 *
 455	 *	@param	array of mixed		Data Series
 456	 *	@return  float
 457	 */
 458	public static function MIN() {
 459		// Return value
 460		$returnValue = null;
 461
 462		// Loop trough arguments
 463		$aArgs = self::flattenArray(func_get_args());
 464		foreach ($aArgs as $arg) {
 465			// Is it a numeric value?
 466			if ((is_numeric($arg)) && (!is_string($arg))) {
 467				if ((is_null($returnValue)) || ($arg < $returnValue)) {
 468					$returnValue = $arg;
 469				}
 470			}
 471		}
 472
 473		// Return
 474		if(is_null($returnValue)) {
 475			return 0;
 476		}
 477		return $returnValue;
 478	}	//	function MIN()
 479
 480
 481	/**
 482	 *	MINA
 483	 *
 484	 *	Returns the smallest value in a list of arguments, including numbers, text, and logical values
 485	 *
 486	 *	@param	array of mixed		Data Series
 487	 *	@return  float
 488	 */
 489	public static function MINA() {
 490		// Return value
 491		$returnValue = null;
 492
 493		// Loop through arguments
 494		$aArgs = self::flattenArray(func_get_args());
 495		foreach ($aArgs as $arg) {
 496			// Is it a numeric value?
 497			if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
 498				if (is_bool($arg)) {
 499					$arg = (integer) $arg;
 500				} elseif (is_string($arg)) {
 501					$arg = 0;
 502				}
 503				if ((is_null($returnValue)) || ($arg < $returnValue)) {
 504					$returnValue = $arg;
 505				}
 506			}
 507		}
 508
 509		// Return
 510		if(is_null($returnValue)) {
 511			return 0;
 512		}
 513		return $returnValue;
 514	}	//	function MINA()
 515
 516
 517	/**
 518	 * SMALL
 519	 *
 520	 * Returns the nth smallest value in a data set. You can use this function to
 521	 * select a value based on its relative standing.
 522	 *
 523	 * @param	array of mixed		Data Series
 524	 * @param	float	Entry in the series to return
 525	 * @return	float
 526	 */
 527	public static function SMALL() {
 528		$aArgs = self::flattenArray(func_get_args());
 529
 530		// Calculate
 531		$n = array_pop($aArgs);
 532
 533		if ((is_numeric($n)) && (!is_string($n))) {
 534			$mArgs = array();
 535			foreach ($aArgs as $arg) {
 536				// Is it a numeric value?
 537				if ((is_numeric($arg)) && (!is_string($arg))) {
 538					$mArgs[] = $arg;
 539				}
 540			}
 541			$count = self::COUNT($mArgs);
 542			$n = floor(--$n);
 543			if (($n < 0) || ($n >= $count) || ($count == 0)) {
 544				return self::$_errorCodes['num'];
 545			}
 546			sort($mArgs);
 547			return $mArgs[$n];
 548		}
 549		return self::$_errorCodes['value'];
 550	}
 551
 552	/**
 553	 * MAX
 554	 *
 555	 * MAX returns the value of the element of the values passed that has the highest value,
 556	 * with negative numbers considered smaller than positive numbers.
 557	 *
 558	 * @param	array of mixed		Data Series
 559	 * @return  float
 560	 */
 561	public static function MAX() {
 562		// Return value
 563		$returnValue = null;
 564
 565		// Loop trough arguments
 566		$aArgs = self::flattenArray(func_get_args());
 567		foreach ($aArgs as $arg) {
 568			// Is it a numeric value?
 569			if ((is_numeric($arg)) && (!is_string($arg))) {
 570				if ((is_null($returnValue)) || ($arg > $returnValue)) {
 571					$returnValue = $arg;
 572				}
 573			}
 574		}
 575
 576		// Return
 577		if(is_null($returnValue)) {
 578			return 0;
 579		}
 580		return $returnValue;
 581	}
 582
 583	/**
 584	 * MAXA
 585	 *
 586	 * Returns the greatest value in a list of arguments, including numbers, text, and logical values
 587	 *
 588	 * @param	array of mixed		Data Series
 589	 * @return  float
 590	 */
 591	public static function MAXA() {
 592		// Return value
 593		$returnValue = null;
 594
 595		// Loop through arguments
 596		$aArgs = self::flattenArray(func_get_args());
 597		foreach ($aArgs as $arg) {
 598			// Is it a numeric value?
 599			if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
 600				if (is_bool($arg)) {
 601					$arg = (integer) $arg;
 602				} elseif (is_string($arg)) {
 603					$arg = 0;
 604				}
 605				if ((is_null($returnValue)) || ($arg > $returnValue)) {
 606					$returnValue = $arg;
 607				}
 608			}
 609		}
 610
 611		// Return
 612		if(is_null($returnValue)) {
 613			return 0;
 614		}
 615		return $returnValue;
 616	}
 617
 618	/**
 619	 * LARGE
 620	 *
 621	 * Returns the nth largest value in a data set. You can use this function to
 622	 * select a value based on its relative standing.
 623	 *
 624	 * @param	array of mixed		Data Series
 625	 * @param	float	Entry in the series to return
 626	 * @return	float
 627	 *
 628	 */
 629	public static function LARGE() {
 630		$aArgs = self::flattenArray(func_get_args());
 631
 632		// Calculate
 633		$n = floor(array_pop($aArgs));
 634
 635		if ((is_numeric($n)) && (!is_string($n))) {
 636			$mArgs = array();
 637			foreach ($aArgs as $arg) {
 638				// Is it a numeric value?
 639				if ((is_numeric($arg)) && (!is_string($arg))) {
 640					$mArgs[] = $arg;
 641				}
 642			}
 643			$count = self::COUNT($mArgs);
 644			$n = floor(--$n);
 645			if (($n < 0) || ($n >= $count) || ($count == 0)) {
 646				return self::$_errorCodes['num'];
 647			}
 648			rsort($mArgs);
 649			return $mArgs[$n];
 650		}
 651		return self::$_errorCodes['value'];
 652	}
 653
 654	/**
 655	 * PERCENTILE
 656	 *
 657	 * Returns the nth percentile of values in a range..
 658	 *
 659	 * @param	array of mixed		Data Series
 660	 * @param	float	$entry		Entry in the series to return
 661	 * @return	float
 662	 */
 663	public static function PERCENTILE() {
 664		$aArgs = self::flattenArray(func_get_args());
 665
 666		// Calculate
 667		$entry = array_pop($aArgs);
 668
 669		if ((is_numeric($entry)) && (!is_string($entry))) {
 670			if (($entry < 0) || ($entry > 1)) {
 671				return self::$_errorCodes['num'];
 672			}
 673			$mArgs = array();
 674			foreach ($aArgs as $arg) {
 675				// Is it a numeric value?
 676				if ((is_numeric($arg)) && (!is_string($arg))) {
 677					$mArgs[] = $arg;
 678				}
 679			}
 680			$mValueCount = count($mArgs);
 681			if ($mValueCount > 0) {
 682				sort($mArgs);
 683				$count = self::COUNT($mArgs);
 684				$index = $entry * ($count-1);
 685				$iBase = floor($index);
 686				if ($index == $iBase) {
 687					return $mArgs[$index];
 688				} else {
 689					$iNext = $iBase + 1;
 690					$iProportion = $index - $iBase;
 691					return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion) ;
 692				}
 693			}
 694		}
 695		return self::$_errorCodes['value'];
 696	}
 697
 698	/**
 699	 * QUARTILE
 700	 *
 701	 * Returns the quartile of a data set.
 702	 *
 703	 * @param	array of mixed		Data Series
 704	 * @param	float	$entry		Entry in the series to return
 705	 * @return	float
 706	 */
 707	public static function QUARTILE() {
 708		$aArgs = self::flattenArray(func_get_args());
 709
 710		// Calculate
 711		$entry = floor(array_pop($aArgs));
 712
 713		if ((is_numeric($entry)) && (!is_string($entry))) {
 714			$entry /= 4;
 715			if (($entry < 0) || ($entry > 1)) {
 716				return self::$_errorCodes['num'];
 717			}
 718			return self::PERCENTILE($aArgs,$entry);
 719		}
 720		return self::$_errorCodes['value'];
 721	}
 722
 723	/**
 724	 * COUNT
 725	 *
 726	 * Counts the number of cells that contain numbers within the list of arguments
 727	 *
 728	 * @param	array of mixed		Data Series
 729	 * @return  int
 730	 */
 731	public static function COUNT() {
 732		// Return value
 733		$returnValue = 0;
 734
 735		// Loop trough arguments
 736		$aArgs = self::flattenArray(func_get_args());
 737		foreach ($aArgs as $arg) {
 738			if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
 739				$arg = (int) $arg;
 740			}
 741			// Is it a numeric value?
 742			if ((is_numeric($arg)) && (!is_string($arg))) {
 743				++$returnValue;
 744			}
 745		}
 746
 747		// Return
 748		return $returnValue;
 749	}
 750
 751	/**
 752	 * COUNTBLANK
 753	 *
 754	 * Counts the number of empty cells within the list of arguments
 755	 *
 756	 * @param	array of mixed		Data Series
 757	 * @return  int
 758	 */
 759	public static function COUNTBLANK() {
 760		// Return value
 761		$returnValue = 0;
 762
 763		// Loop trough arguments
 764		$aArgs = self::flattenArray(func_get_args());
 765		foreach ($aArgs as $arg) {
 766			// Is it a blank cell?
 767			if ((is_null($arg)) || ((is_string($arg)) && ($arg == ''))) {
 768				++$returnValue;
 769			}
 770		}
 771
 772		// Return
 773		return $returnValue;
 774	}
 775
 776	/**
 777	 * COUNTA
 778	 *
 779	 * Counts the number of cells that are not empty within the list of arguments
 780	 *
 781	 * @param	array of mixed		Data Series
 782	 * @return  int
 783	 */
 784	public static function COUNTA() {
 785		// Return value
 786		$returnValue = 0;
 787
 788		// Loop through arguments
 789		$aArgs = self::flattenArray(func_get_args());
 790		foreach ($aArgs as $arg) {
 791			// Is it a numeric, boolean or string value?
 792			if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
 793				++$returnValue;
 794			}
 795		}
 796
 797		// Return
 798		return $returnValue;
 799	}
 800
 801	/**
 802	 * AVERAGE
 803	 *
 804	 * Returns the average (arithmetic mean) of the arguments
 805	 *
 806	 * @param	array of mixed		Data Series
 807	 * @return  float
 808	 */
 809	public static function AVERAGE() {
 810		// Return value
 811		$returnValue = 0;
 812
 813		// Loop through arguments
 814		$aArgs = self::flattenArray(func_get_args());
 815		$aCount = 0;
 816		foreach ($aArgs as $arg) {
 817			if ((is_bool($arg))  && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
 818				$arg = (integer) $arg;
 819			}
 820			// Is it a numeric value?
 821			if ((is_numeric($arg)) && (!is_string($arg))) {
 822				if (is_null($returnValue)) {
 823					$returnValue = $arg;
 824				} else {
 825					$returnValue += $arg;
 826				}
 827				++$aCount;
 828			}
 829		}
 830
 831		// Return
 832		if ($aCount > 0) {
 833			return $returnValue / $aCount;
 834		} else {
 835			return self::$_errorCodes['divisionbyzero'];
 836		}
 837	}
 838
 839	/**
 840	 * AVERAGEA
 841	 *
 842	 * Returns the average of its arguments, including numbers, text, and logical values
 843	 *
 844	 * @param	array of mixed		Data Series
 845	 * @return  float
 846	 */
 847	public static function AVERAGEA() {
 848		// Return value
 849		$returnValue = null;
 850
 851		// Loop through arguments
 852		$aArgs = self::flattenArray(func_get_args());
 853		$aCount = 0;
 854		foreach ($aArgs as $arg) {
 855			if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
 856				if (is_bool($arg)) {
 857					$arg = (integer) $arg;
 858				} elseif (is_string($arg)) {
 859					$arg = 0;
 860				}
 861				if (is_null($returnValue)) {
 862					$returnValue = $arg;
 863				} else {
 864					$returnValue += $arg;
 865				}
 866				++$aCount;
 867			}
 868		}
 869
 870		// Return
 871		if ($aCount > 0) {
 872			return $returnValue / $aCount;
 873		} else {
 874			return self::$_errorCodes['divisionbyzero'];
 875		}
 876	}
 877
 878	/**
 879	 * MEDIAN
 880	 *
 881	 * Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
 882	 *
 883	 * @param	array of mixed		Data Series
 884	 * @return  float
 885	 */
 886	public static function MEDIAN() {
 887		// Return value
 888		$returnValue = self::$_errorCodes['num'];
 889
 890		$mArgs = array();
 891		// Loop through arguments
 892		$aArgs = self::flattenArray(func_get_args());
 893		foreach ($aArgs as $arg) {
 894			// Is it a numeric value?
 895			if ((is_numeric($arg)) && (!is_string($arg))) {
 896				$mArgs[] = $arg;
 897			}
 898		}
 899
 900		$mValueCount = count($mArgs);
 901		if ($mValueCount > 0) {
 902			sort($mArgs,SORT_NUMERIC);
 903			$mValueCount = $mValueCount / 2;
 904			if ($mValueCount == floor($mValueCount)) {
 905				$returnValue = ($mArgs[$mValueCount--] + $mArgs[$mValueCount]) / 2;
 906			} else {
 907				$mValueCount == floor($mValueCount);
 908				$returnValue = $mArgs[$mValueCount];
 909			}
 910		}
 911
 912		// Return
 913		return $returnValue;
 914	}
 915
 916	//
 917	//	Special variant of array_count_values that isn't limited to strings and integers,
 918	//		but can work with floating point numbers as values
 919	//
 920	private static function modeCalc($data) {
 921		$frequencyArray = array();
 922		foreach($data as $datum) {
 923			$found = False;
 924			foreach($frequencyArray as $key => $value) {
 925				if ((string)$value['value'] == (string)$datum) {
 926					++$frequencyArray[$key]['frequency'];
 927					$found = True;
 928					break;
 929				}
 930			}
 931			if (!$found) {
 932				$frequencyArray[] = array('value'		=> $datum,
 933										  'frequency'	=>	1 );
 934			}
 935		}
 936
 937		foreach($frequencyArray as $key => $value) {
 938			$frequencyList[$key] = $value['frequency'];
 939			$valueList[$key] = $value['value'];
 940		}
 941		array_multisort($frequencyList, SORT_DESC, $valueList, SORT_ASC, SORT_NUMERIC, $frequencyArray);
 942
 943		if ($frequencyArray[0]['frequency'] == 1) {
 944			return self::NA();
 945		}
 946		return $frequencyArray[0]['value'];
 947	}
 948
 949	/**
 950	 * MODE
 951	 *
 952	 * Returns the most frequently occurring, or repetitive, value in an array or range of data
 953	 *
 954	 * @param	array of mixed		Data Series
 955	 * @return  float
 956	 */
 957	public static function MODE() {
 958		// Return value
 959		$returnValue = self::NA();
 960
 961		// Loop through arguments
 962		$aArgs = self::flattenArray(func_get_args());
 963
 964		$mArgs = array();
 965		foreach ($aArgs as $arg) {
 966			// Is it a numeric value?
 967			if ((is_numeric($arg)) && (!is_string($arg))) {
 968				$mArgs[] = $arg;
 969			}
 970		}
 971
 972		if (count($mArgs) > 0) {
 973			return self::modeCalc($mArgs);
 974		}
 975
 976		// Return
 977		return $returnValue;
 978	}
 979
 980	/**
 981	 * DEVSQ
 982	 *
 983	 * Returns the sum of squares of deviations of data points from their sample mean.
 984	 *
 985	 * @param	array of mixed		Data Series
 986	 * @return  float
 987	 */
 988	public static function DEVSQ() {
 989		// Return value
 990		$returnValue = null;
 991
 992		$aMean = self::AVERAGE(func_get_args());
 993		if (!is_null($aMean)) {
 994			$aArgs = self::flattenArray(func_get_args());
 995
 996			$aCount = -1;
 997			foreach ($aArgs as $arg) {
 998				// Is it a numeric value?
 999				if ((is_bool($arg))  && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
1000					$arg = (int) $arg;
1001				}
1002				if ((is_numeric($arg)) && (!is_string($arg))) {
1003					if (is_null($returnValue)) {
1004						$returnValue = pow(($arg - $aMean),2);
1005					} else {
1006						$returnValue += pow(($arg - $aMean),2);
1007					}
1008					++$aCount;
1009				}
1010			}
1011
1012			// Return
1013			if (is_null($returnValue)) {
1014				return self::$_errorCodes['num'];
1015			} else {
1016				return $returnValue;
1017			}
1018		}
1019		return self::NA();
1020	}
1021
1022	/**
1023	 * AVEDEV
1024	 *
1025	 * Returns the average of the absolute deviations of data points from their mean.
1026	 * AVEDEV is a measure of the variability in a data set.
1027	 *
1028	 * @param	array of mixed		Data Series
1029	 * @return  float
1030	 */
1031	public static function AVEDEV() {
1032		$aArgs = self::flattenArray(func_get_args());
1033
1034		// Return value
1035		$returnValue = null;
1036
1037		$aMean = self::AVERAGE($aArgs);
1038		if ($aMean != self::$_errorCodes['divisionbyzero']) {
1039			$aCount = 0;
1040			foreach ($aArgs as $arg) {
1041				if ((is_bool($arg))  && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
1042					$arg = (integer) $arg;
1043				}
1044				// Is it a numeric value?
1045				if ((is_numeric($arg)) && (!is_string($arg))) {
1046					if (is_null($returnValue)) {
1047						$returnValue = abs($arg - $aMean);
1048					} else {
1049						$returnValue += abs($arg - $aMean);
1050					}
1051					++$aCount;
1052				}
1053			}
1054
1055			// Return
1056			return $returnValue / $aCount ;
1057		}
1058		return self::$_errorCodes['num'];
1059	}
1060
1061	/**
1062	 * GEOMEAN
1063	 *
1064	 * Returns the geometric mean of an array or range of positive data. For example, you
1065	 * can use GEOMEAN to calculate average growth rate given compound interest with
1066	 * variable rates.
1067	 *
1068	 * @param	array of mixed		Data Series
1069	 * @return  float
1070	 */
1071	public static function GEOMEAN() {
1072		$aMean = self::PRODUCT(func_get_args());
1073		if (is_numeric($aMean) && ($aMean > 0)) {
1074			$aArgs = self::flattenArray(func_get_args());
1075			$aCount = self::COUNT($aArgs) ;
1076			if (self::MIN($aArgs) > 0) {
1077				return pow($aMean, (1 / $aCount));
1078			}
1079		}
1080		return self::$_errorCodes['num'];
1081	}
1082
1083	/**
1084	 * HARMEAN
1085	 *
1086	 * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
1087	 * arithmetic mean of reciprocals.
1088	 *
1089	 * @param	array of mixed		Data Series
1090	 * @return  float
1091	 */
1092	public static function HARMEAN() {
1093		// Return value
1094		$returnValue = self::NA();
1095
1096		// Loop through arguments
1097		$aArgs = self::flattenArray(func_get_args());
1098		if (self::MIN($aArgs) < 0) {
1099			return self::$_errorCodes['num'];
1100		}
1101		$aCount = 0;
1102		foreach ($aArgs as $arg) {
1103			// Is it a numeric value?
1104			if ((is_numeric($arg)) && (!is_string($arg))) {
1105				if ($arg <= 0) {
1106					return self::$_errorCodes['num'];
1107				}
1108				if (is_null($returnValue)) {
1109					$returnValue = (1 / $arg);
1110				} else {
1111					$returnValue += (1 / $arg);
1112				}
1113				++$aCount;
1114			}
1115		}
1116
1117		// Return
1118		if ($aCount > 0) {
1119			return 1 / ($returnValue / $aCount);
1120		} else {
1121			return $returnValue;
1122		}
1123	}
1124
1125	/**
1126	 * TRIMMEAN
1127	 *
1128	 * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
1129	 * taken by excluding a percentage of data points from the top and bottom tails
1130	 * of a data set.
1131	 *
1132	 * @param	array of mixed		Data Series
1133	 * @param	float	Percentage to discard
1134	 * @return	float
1135	 */
1136	public static function TRIMMEAN() {
1137		$aArgs = self::flattenArray(func_get_args());
1138
1139		// Calculate
1140		$percent = array_pop($aArgs);
1141
1142		if ((is_numeric($percent)) && (!is_string($percent))) {
1143			if (($percent < 0) || ($percent > 1)) {
1144				return self::$_errorCodes['num'];
1145			}
1146			$mArgs = array();
1147			foreach ($aArgs as $arg) {
1148				// Is it a numeric value?
1149				if ((is_numeric($arg)) && (!is_string($arg))) {
1150					$mArgs[] = $arg;
1151				}
1152			}
1153			$discard = floor(self::COUNT($mArgs) * $percent / 2);
1154			sort($mArgs);
1155			for ($i=0; $i < $discard; ++$i) {
1156				array_pop($mArgs);
1157				array_shift($mArgs);
1158			}
1159			return self::AVERAGE($mArgs);
1160		}
1161		return self::$_errorCodes['value'];
1162	}
1163
1164	/**
1165	 * STDEV
1166	 *
1167	 * Estimates standard deviation based on a sample. The standard deviation is a measure of how
1168	 * widely values are dispersed from the average value (the mean).
1169	 *
1170	 * @param	array of mixed		Data Series
1171	 * @return  float
1172	 */
1173	public static function STDEV() {
1174		// Return value
1175		$returnValue = null;
1176
1177		$aMean = self::AVERAGE(func_get_args());
1178		if (!is_null($aMean)) {
1179			$aArgs = self::flattenArray(func_get_args());
1180
1181			$aCount = -1;
1182			foreach ($aArgs as $arg) {
1183				// Is it a numeric value?
1184				if ((is_numeric($arg)) && (!is_string($arg))) {
1185					if (is_null($returnValue)) {
1186						$returnValue = pow(($arg - $aMean),2);
1187					} else {
1188						$returnValue += pow(($arg - $aMean),2);
1189					}
1190					++$aCount;
1191				}
1192			}
1193
1194			// Return
1195			if (($aCount > 0) && ($returnValue > 0)) {
1196				return sqrt($returnValue / $aCount);
1197			}
1198		}
1199		return self::$_errorCodes['divisionbyzero'];
1200	}
1201
1202	/**
1203	 * STDEVA
1204	 *
1205	 * Estimates standard deviation based on a sample, including numbers, text, and logical values
1206	 *
1207	 * @param	array of mixed		Data Series
1208	 * @return  float
1209	 */
1210	public static function STDEVA() {
1211		// Return value
1212		$returnValue = null;
1213
1214		$aMean = self::AVERAGEA(func_get_args());
1215		if (!is_null($aMean)) {
1216			$aArgs = self::flattenArray(func_get_args());
1217
1218			$aCount = -1;
1219			foreach ($aArgs as $arg) {
1220				// Is it a numeric value?
1221				if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
1222					if (is_bool($arg)) {
1223						$arg = (integer) $arg;
1224					} elseif (is_string($arg)) {
1225						$arg = 0;
1226					}
1227					if (is_null($returnValue)) {
1228						$returnValue = pow(($arg - $aMean),2);
1229					} else {
1230						$returnValue += pow(($arg - $aMean),2);
1231					}
1232					++$aCount;
1233				}
1234			}
1235
1236			// Return
1237			if (($aCount > 0) && ($returnValue > 0)) {
1238				return sqrt($returnValue / $aCount);
1239			}
1240		}
1241		return self::$_errorCodes['divisionbyzero'];
1242	}
1243
1244	/**
1245	 * STDEVP
1246	 *
1247	 * Calculates standard deviation based on the entire population
1248	 *
1249	 * @param	array of mixed		Data Series
1250	 * @return  float
1251	 */
1252	public static function STDEVP() {
1253		// Return value
1254		$returnValue = null;
1255
1256		$aMean = self::AVERAGE(func_get_args());
1257		if (!is_null($aMean)) {
1258			$aArgs = self::flattenArray(func_get_args());
1259
1260			$aCount = 0;
1261			foreach ($aArgs as $arg) {
1262				// Is it a numeric value?
1263				if ((is_numeric($arg)) && (!is_string($arg))) {
1264					if (is_null($returnValue)) {
1265						$returnValue = pow(($arg - $aMean),2);
1266					} else {
1267						$returnValue += pow(($arg - $aMean),2);
1268					}
1269					++$aCount;
1270				}
1271			}
1272
1273			// Return
1274			if (($aCount > 0) && ($returnValue > 0)) {
1275				return sqrt($returnValue / $aCount);
1276			}
1277		}
1278		return self::$_errorCodes['divisionbyzero'];
1279	}
1280
1281	/**
1282	 * STDEVPA
1283	 *
1284	 * Calculates standard deviation based on the entire population, including numbers, text, and logical values
1285	 *
1286	 * @param	array of mixed		Data Series
1287	 * @return  float
1288	 */
1289	public static function STDEVPA() {
1290		// Return value
1291		$returnValue = null;
1292
1293		$aMean = self::AVERAGEA(func_get_args());
1294		if (!is_null($aMean)) {
1295			$aArgs = self::flattenArray(func_get_args());
1296
1297			$aCount = 0;
1298			foreach ($aArgs as $arg) {
1299				// Is it a numeric value?
1300				if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
1301					if (is_bool($arg)) {
1302						$arg = (integer) $arg;
1303					} elseif (is_string($arg)) {
1304						$arg = 0;
1305					}
1306					if (is_null($returnValue)) {
1307						$returnValue = pow(($arg - $aMean),2);
1308					} else {
1309						$returnValue += pow(($arg - $aMean),2);
1310					}
1311					++$aCount;
1312				}
1313			}
1314
1315			// Return
1316			if (($aCount > 0) && ($returnValue > 0)) {
1317				return sqrt($returnValue / $aCount);
1318			}
1319		}
1320		return self::$_errorCodes['divisionbyzero'];
1321	}
1322
1323	/**
1324	 * VARFunc
1325	 *
1326	 * Estimates variance based on a sample.
1327	 *
1328	 * @param	array of mixed		Data Series
1329	 * @return  float
1330	 */
1331	public static function VARFunc() {
1332		// Return value
1333		$returnValue = self::$_errorCodes['divisionbyzero'];
1334
1335		$summerA = $summerB = 0;
1336
1337		// Loop through arguments
1338		$aArgs = self::flattenArray(func_get_args());
1339		$aCount = 0;
1340		foreach ($aArgs as $arg) {
1341			// Is it a numeric value?
1342			if ((is_numeric($arg)) && (!is_string($arg))) {
1343				$summerA += ($arg * $arg);
1344				$summerB += $arg;
1345				++$aCount;
1346			}
1347		}
1348
1349		// Return
1350		if ($aCount > 1) {
1351			$summerA = $summerA * $aCount;
1352			$summerB = ($summerB * $summerB);
1353			$returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
1354		}
1355		return $returnValue;
1356	}
1357
1358	/**
1359	 * VARA
1360	 *
1361	 * Estimates variance based on a sample, including numbers, text, and logical values
1362	 *
1363	 * @param	array of mixed		Data Series
1364	 * @return  float
1365	 */
1366	public static function VARA() {
1367		// Return value
1368		$returnValue = self::$_errorCodes['divisionbyzero'];
1369
1370		$summerA = $summerB = 0;
1371
1372		// Loop through arguments
1373		$aArgs = self::flattenArray(func_get_args());
1374		$aCount = 0;
1375		foreach ($aArgs as $arg) {
1376			// Is it a numeric value?
1377				if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
1378				if (is_bool($arg)) {
1379					$arg = (integer) $arg;
1380				} elseif (is_string($arg)) {
1381					$arg = 0;
1382				}
1383				$summerA += ($arg * $arg);
1384				$summerB += $arg;
1385				++$aCount;
1386			}
1387		}
1388
1389		// Return
1390		if ($aCount > 1) {
1391			$summerA = $summerA * $aCount;
1392			$summerB = ($summerB * $summerB);
1393			$returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
1394		}
1395		return $returnValue;
1396	}
1397
1398	/**
1399	 * VARP
1400	 *
1401	 * Calculates variance based on the entire population
1402	 *
1403	 * @param	array of mixed		Data Series
1404	 * @return  float
1405	 */
1406	public static function VARP() {
1407		// Return value
1408		$returnValue = self::$_errorCodes['divisionbyzero'];
1409
1410		$summerA = $summerB = 0;
1411
1412		// Loop through arguments
1413		$aArgs = self::flattenArray(func_get_args());
1414		$aCount = 0;
1415		foreach ($aArgs as $arg) {
1416			// Is it a numeric value?
1417			if ((is_numeric($arg)) && (!is_string($arg))) {
1418				$summerA += ($arg * $arg);
1419				$summerB += $arg;
1420				++$aCount;
1421			}
1422		}
1423
1424		// Return
1425		if ($aCount > 0) {
1426			$summerA = $summerA * $aCount;
1427			$summerB = ($summerB * $summerB);
1428			$returnValue = ($summerA - $summerB) / ($aCount * $aCount);
1429		}
1430		return $returnValue;
1431	}
1432
1433	/**
1434	 * VARPA
1435	 *
1436	 * Calculates variance based on the entire population, including numbers, text, and logical values
1437	 *
1438	 * @param	array of mixed		Data Series
1439	 * @return  float
1440	 */
1441	public static function VARPA() {
1442		// Return value
1443		$returnValue = self::$_errorCodes['divisionbyzero'];
1444
1445		$summerA = $summerB = 0;
1446
1447		// Loop through arguments
1448		$aArgs = self::flattenArray(func_get_args());
1449		$aCount = 0;
1450		foreach ($aArgs as $arg) {
1451			// Is it a numeric value?
1452			if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
1453				if (is_bool($arg)) {
1454					$arg = (integer) $arg;
1455				} elseif (is_string($arg)) {
1456					$arg = 0;
1457				}
1458				$summerA += ($arg * $arg);
1459				$summerB += $arg;
1460				++$aCount;
1461			}
1462		}
1463
1464		// Return
1465		if ($aCount > 0) {
1466			$summerA = $summerA * $aCount;
1467			$summerB = ($summerB * $summerB);
1468			$returnValue = ($summerA - $summerB) / ($aCount * $aCount);
1469		}
1470		return $returnValue;
1471	}
1472
1473	/**
1474	 *	RANK
1475	 *
1476	 *	Returns the rank of a number in a list of numbers.
1477	 *
1478	 *	@param	number				The number whose rank you want to find.
1479	 *	@param	array of number		An array of, or a reference to, a list of numbers.
1480	 *	@param	mixed				Order to sort the values in the value set
1481	 *	@return  float
1482	 */
1483	public static function RANK($value,$valueSet,$order=0) {
1484		$value = self::flattenSingleValue($value);
1485		$valueSet = self::flattenArray($valueSet);
1486		$order = self::flattenSingleValue($order);
1487
1488		foreach($valueSet as $key => $valueEntry) {
1489			if (!is_numeric($valueEntry)) {
1490				unset($valueSet[$key]);
1491			}
1492		}
1493
1494		if ($order == 0) {
1495			rsort($valueSet,SORT_NUMERIC);
1496		} else {
1497			sort($valueSet,SORT_NUMERIC);
1498		}
1499		$pos = array_search($value,$valueSet);
1500		if ($pos === False) {
1501			return self::$_errorCodes['na'];
1502		}
1503
1504		return ++$pos;
1505	}	//	function RANK()
1506
1507	/**
1508	 *	PERCENTRANK
1509	 *
1510	 *	Returns the rank of a value in a data set as a percentage of the data set.
1511	 *
1512	 *	@param	array of number		An array of, or a reference to, a list of numbers.
1513	 *	@param	number				The number whose rank you want to find.
1514	 *	@param	number				The number of significant digits for the returned percentage value.
1515	 *	@return  float
1516	 */
1517	public static function PERCENTRANK($valueSet,$value,$significance=3) {
1518		$valueSet = self::flattenArray($valueSet);
1519		$value = self::flattenSingleValue($value);
1520		$significance = self::flattenSingleValue($significance);
1521
1522		foreach($valueSet as $key => $valueEntry) {
1523			if (!is_numeric($valueEntry)) {
1524				unset($valueSet[$key]);
1525			}
1526		}
1527		sort($valueSet,SORT_NUMERIC);
1528		$valueCount = count($valueSet);
1529		if ($valueCount == 0) {
1530			return self::$_errorCodes['num'];
1531		}
1532
1533		$valueAdjustor = $valueCount - 1;
1534		if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
1535			return self::$_errorCodes['na'];
1536		}
1537
1538		$pos = array_search($value,$valueSet);
1539		if ($pos === False) {
1540			$pos = 0;
1541			$testValue = $valueSet[0];
1542			while ($testValue < $value) {
1543				$testValue = $valueSet[++$pos];
1544			}
1545			--$pos;
1546			$pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
1547		}
1548
1549		return round($pos / $valueAdjustor,$significance);
1550	}	//	function PERCENTRANK()
1551
1552
1553	private static function _checkTrendArray($values) {
1554		foreach($values as $key => $value) {
1555			if ((is_bool($value)) || ($value == '')) {
1556				unset($values[$key]);
1557			} elseif (is_string($value)) {
1558				if (is_numeric($value)) {
1559					$values[$key] = (float) $value;
1560				} else {
1561					unset($values[$key]);
1562				}
1563			}
1564		}
1565		return $values;
1566	}	//	function _checkTrendArray()
1567
1568	/**
1569	 *	INTERCEPT
1570	 *
1571	 *	Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
1572	 *
1573	 *	@param	array of mixed		Data Series Y
1574	 *	@param	array of mixed		Data Series X
1575	 *	@return  float
1576	 */
1577	public static function INTERCEPT($yValues,$xValues) {
1578		$yValues = self::flattenArray($yValues);
1579		$xValues = self::flattenArray($xValues);
1580
1581		$yValues = self::_checkTrendArray($yValues);
1582		$yValueCount = count($yValues);
1583		$xValues = self::_checkTrendArray($xValues);
1584		$xValueCount = count($xValues);
1585
1586		if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1587			return self::$_errorCodes['na'];
1588		} elseif ($yValueCount == 1) {
1589			return self::$_errorCodes['divisionbyzero'];
1590		}
1591
1592		$bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
1593		return $bestFitLinear->getIntersect();
1594	}	//	function INTERCEPT()
1595
1596	/**
1597	 *	RSQ
1598	 *
1599	 *	Returns the square of the Pearson product moment correlation coefficient through data points in known_y's and known_x's.
1600	 *
1601	 *	@param	array of mixed		Data Series Y
1602	 *	@param	array of mixed		Data Series X
1603	 *	@return  float
1604	 */
1605	public static function RSQ($yValues,$xValues) {
1606		$yValues = self::flattenArray($yValues);
1607		$xValues = self::flattenArray($xValues);
1608
1609		$yValues = self::_checkTrendArray($yValues);
1610		$yValueCount = count($yValues);
1611		$xValues = self::_checkTrendArray($xValues);
1612		$xValueCount = count($xValues);
1613
1614		if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1615			return self::$_errorCodes['na'];
1616		} elseif ($yValueCount == 1) {
1617			return self::$_errorCodes['divisionbyzero'];
1618		}
1619
1620		$bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
1621		return $bestFitLinear->getGoodnessOfFit();
1622	}	//	function RSQ()
1623
1624	/**
1625	 *	SLOPE
1626	 *
1627	 *	Returns the slope of the linear regression line through data points in known_y's and known_x's.
1628	 *
1629	 *	@param	array of mixed		Data Series Y
1630	 *	@param	array of mixed		Data Series X
1631	 *	@return  float
1632	 */
1633	public static function SLOPE($yValues,$xValues) {
1634		$yValues = self::flattenArray($yValues);
1635		$xValues = self::flattenArray($xValues);
1636
1637		$yValues = self::_checkTrendArray($yValues);
1638		$yValueCount = count($yValues);
1639		$xValues = self::_checkTrendArray($xValues);
1640		$xValueCount = count($xValues);
1641
1642		if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1643			return self::$_errorCodes['na'];
1644		} elseif ($yValueCount == 1) {
1645			return self::$_errorCodes['divisionbyzero'];
1646		}
1647
1648		$bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
1649		return $bestFitLinear->getSlope();
1650	}	//	function SLOPE()
1651
1652	/**
1653	 *	STEYX
1654	 *
1655	 *	Returns the standard error of the predicted y-value for each x in the regression.
1656	 *
1657	 *	@param	array of mixed		Data Series Y
1658	 *	@param	array of mixed		Data Series X
1659	 *	@return  float
1660	 */
1661	public static function STEYX($yValues,$xValues) {
1662		$yValues = self::flattenArray($yValues);
1663		$xValues = self::flattenArray($xValues);
1664
1665		$yValues = self::_checkTrendArray($yValues);
1666		$yValueCount = count($yValues);
1667		$xValues = self::_checkTrendArray($xValues);
1668		$xValueCount = count($xValues);
1669
1670		if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1671			return self::$_errorCodes['na'];
1672		} elseif ($yValueCount == 1) {
1673			return self::$_errorCodes['divisionbyzero'];
1674		}
1675
1676		$bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
1677		return $bestFitLinear->getStdevOfResiduals();
1678	}	//	function STEYX()
1679
1680	/**
1681	 *	COVAR
1682	 *
1683	 *	Returns covariance, the average of the products of deviations for each data point pair.
1684	 *
1685	 *	@param	array of mixed		Data Series Y
1686	 *	@param	array of mixed		Data Series X
1687	 *	@return  float
1688	 */
1689	public static function COVAR($yValues,$xValues) {
1690		$yValues = self::flattenArray($yValues);
1691		$xValues = self::flattenArray($xValues);
1692
1693		$yValues = self::_checkTrendArray($yValues);
1694		$yValueCount = count($yValues);
1695		$xValues = self::_checkTrendArray($xValues);
1696		$xValueCount = count($xValues);
1697
1698		if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1699			return self::$_errorCodes['na'];
1700		} elseif ($yValueCount == 1) {
1701			return self::$_errorCodes['divisionbyzero'];
1702		}
1703
1704		$bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
1705		return $bestFitLinear->getCovariance();
1706	}	//	function COVAR()
1707
1708	/**
1709	 *	CORREL
1710	 *
1711	 *	Returns covariance, the average of the products of deviations for each data point pair.
1712	 *
1713	 *	@param	array of mixed		Data Series Y
1714	 *	@param	array of mixed		Data Series X
1715	 *	@return  float
1716	 */
1717	public static function CORREL($yValues,$xValues) {
1718		$yValues = self::flattenArray($yValues);
1719		$xValues = self::flattenArray($xValues);
1720
1721		$yValues = self::_checkTrendArray($yValues);
1722		$yValueCount = count($yValues);
1723		$xValues = self::_checkTrendArray($xValues);
1724		$xValueCount = count($xValues);
1725
1726		if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1727			return self::$_errorCodes['na'];
1728		} elseif ($yValueCount == 1) {
1729			return self::$_errorCodes['divisionbyzero'];
1730		}
1731
1732		$bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
1733		return $bestFitLinear->getCorrelation();
1734	}	//	function CORREL()
1735
1736	/**
1737	 *	LINEST
1738	 *
1739	 *	Calculates the statistics for a line by using the "least squares" method to calculate a straight line that best fits your data,
1740	 *		and then returns an array that describes the line.
1741	 *
1742	 *	@param	array of mixed		Data Series Y
1743	 *	@param	array of mixed		Data Series X
1744	 *	@param	boolean				A logical value specifying whether to force the intersect to equal 0.
1745	 *	@param	boolean				A logical value specifying whether to return additional regression statistics.
1746	 *	@return  array
1747	 */
1748	public static function LINEST($yValues,$xValues,$const=True,$stats=False) {
1749		$yValues = self::flattenArray($yValues);
1750		$xValues = self::flattenArray($xValues);
1751		$const	= (boolean) self::flattenSingleValue($const);
1752		$stats	= (boolean) self::flattenSingleValue($stats);
1753
1754		$yValues = self::_checkTrendArray($yValues);
1755		$yValueCount = count($yValues);
1756		$xValues = self::_checkTrendArray($xValues);
1757		$xValueCount = count($xValues);
1758
1759		if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1760			return self::$_errorCodes['na'];
1761		} elseif ($yValueCount == 1) {
1762			return self::$_errorCodes['divisionbyzero'];
1763		}
1764
1765		$bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues,$const);
1766		if ($stats) {
1767			return array( array( $bestFitLinear->getSlope(),
1768						 		 $bestFitLinear->getSlopeSE(),
1769						 		 $bestFitLinear->getGoodnessOfFit(),
1770						 		 $bestFitLinear->getF(),
1771						 		 $bestFitLinear->getSSRegression(),
1772							   ),
1773						  array( $bestFitLinear->getIntersect(),
1774								 $bestFitLinear->getIntersectSE(),
1775								 $bestFitLinear->getStdevOfResiduals(),
1776								 $bestFitLinear->getDFResiduals(),
1777								 $bestFitLinear->getSSResiduals()
1778							   )
1779						);
1780		} else {
1781			return array( $bestFitLinear->getSlope(),
1782						  $bestFitLinear->getIntersect()
1783						);
1784		}
1785	}	//	function LINEST()
1786
1787	/**
1788	 *	LOGEST
1789	 *
1790	 *	Calculates an exponential curve that best fits the X and Y data series,
1791	 *		and then returns an array that describes the line.
1792	 *
1793	 *	@param	array of mixed		Data Series Y
1794	 *	@param	array of mixed		Data Series X
1795	 *	@param	boolean				A logical value specifying whether to force the intersect to equal 0.
1796	 *	@param	boolean				A logical value specifying whether to return additional regression statistics.
1797	 *	@return  array
1798	 */
1799	public static function LOGEST($yValues,$xValues,$const=True,$stats=False) {
1800		$yValues = self::flattenArray($yValues);
1801		$xValues = self::flattenArray($xValues);
1802		$const	= (boolean) self::flattenSingleValue($const);
1803		$stats	= (boolean) self::flattenSingleValue($stats);
1804
1805		$yValues = self::_checkTrendArray($yValues);
1806		$yValueCount = count($yValues);
1807		$xValues = self::_checkTrendArray($xValues);
1808		$xValueCount = count($xValues);
1809
1810		if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1811			return self::$_errorCodes['na'];
1812		} elseif ($yValueCount == 1) {
1813			return self::$_errorCodes['divisionbyzero'];
1814		}
1815
1816		$bestFitExponential = trendClass::calculate(trendClass::TREND_EXPONENTIAL,$yValues,$xValues,$const);
1817		if ($stats) {
1818			return array( array( $bestFitExponential->getSlope(),
1819						 		 $bestFitExponential->getSlopeSE(),
1820						 		 $bestFitExponential->getGoodnessOfFit(),
1821						 		 $bestFitExponential->getF(),
1822						 		 $bestFitExponential->getSSRegression(),
1823							   ),
1824						  array( $bestFitExponential->getIntersect(),
1825								 $bestFitExponential->getIntersectSE(),
1826								 $bestFitExponential->getStdevOfResiduals(),
1827								 $bestFitExponential->getDFResiduals(),
1828								 $bestFitExponential->getSSResiduals()
1829							   )
1830						);
1831		} else {
1832			return array( $bestFitExponential->getSlope(),
1833						  $bestFitExponential->getIntersect()
1834						);
1835		}
1836	}	//	function LOGEST()
1837
1838	/**
1839	 *	FORECAST
1840	 *
1841	 *	Calculates, or predicts, a future value by using existing values. The predicted value is a y-value for a given x-value.
1842	 *
1843	 *	@param	float				Value of X for which we want to find Y
1844	 *	@param	array of mixed		Data Series Y
1845	 *	@param	array of mixed		Data Series X
1846	 *	@return  float
1847	 */
1848	public static function FORECAST($xValue,$yValues,$xValues) {
1849		$xValue	= self::flattenSingleValue($xValue);
1850		$yValues = self::flattenArray($yValues);
1851		$xValues = self::flattenArray($xValues);
1852
1853		if (!is_numeric($xValue)) {
1854			return self::$_errorCodes['value'];
1855		}
1856
1857		$yValues = self::_checkTrendArray($yValues);
1858		$yValueCount = count($yValues);
1859		$xValues = self::_checkTrendArray($xValues);
1860		$xValueCount = count($xValues);
1861
1862		if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1863			return self::$_errorCodes['na'];
1864		} elseif ($yValueCount == 1) {
1865			return self::$_errorCodes['divisionbyzero'];
1866		}
1867
1868		$bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);
1869		return $bestFitLinear->getValueOfYForX($xValue);
1870	}	//	function FORECAST()
1871
1872
1873	/**
1874	 *	TREND
1875	 *
1876	 *	Returns values along a linear trend
1877	 *
1878	 *	@param	array of mixed		Data Series Y
1879	 *	@param	array of mixed		Data Series X
1880	 *	@param	array of mixed		Values of X for which we want to find Y
1881	 *	@param	boolean				A logical value specifying whether to force the intersect to equal 0.
1882	 *	@return  array of float
1883	 */
1884	public static function TREND($yValues,$xValues=array(),$newValues=array(),$const=True) {
1885		$yValues = self::flattenArray($yValues);
1886		$xValues = self::flattenArray($xValues);
1887		$newValues = self::flattenArray($newValues);
1888		$const	= (boolean) self::flattenSingleValue($const);
1889
1890		$bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues,$const);
1891		if (count($newValues) == 0) {
1892			$newValues = $bestFitLinear->getXValues();
1893		}
1894
1895		$returnArray = array();
1896		foreach($newValues as $xValue) {
1897			$returnArray[0][] = $bestFitLinear->getValueOfYForX($xValue);
1898		}
1899
1900		return $returnArray;
1901	}	//	function TREND()
1902
1903
1904	/**
1905	 *	GROWTH
1906	 *
1907	 *	Returns values along a predicted emponential trend
1908	 *
1909	 *	@param	array of mixed		Data Series Y
1910	 *	@param	array of mixed		Data Series X
1911	 *	@param	array of mixed		Values of X for which we want to find Y
1912	 *	@param	boolean				A logical value specifying whether to force the intersect to equal 0.
1913	 *	@return  array of float
1914	 */
1915	public static function GROWTH($yValues,$xValues=array(),$newValues=array(),$const=True) {
1916		$yValues = self::flattenArray($yValues);
1917		$xValues = self::flattenArray($xValues);
1918		$newValues = self::flattenArray($newValues);
1919		$const	= (boolean) self::flattenSingleValue($const);
1920
1921		$bestFitExponential = trendClass::calculate(trendClass::TREND_EXPONENTIAL,$yValues,$xValues,$const);
1922		if (count($newValues) == 0) {
1923			$newValues = $bestFitExponential->getXValues();
1924		}
1925
1926		$returnArray = array();
1927		foreach($newValues as $xValue) {
1928			$returnArray[0][] = $bestFitExponential->getValueOfYForX($xValue);
1929		}
1930
1931		return $returnArray;
1932	}	//	function GROWTH()
1933
1934
1935	private static function _romanCut($num, $n) {
1936		return ($num - ($num % $n ) ) / $n;
1937	}
1938
1939    public static function ROMAN ($aValue, $style=0)

Large files files are truncated, but you can click here to view the full file