Source for file Functions.php
Documentation is available at Functions.php 
 * Copyright (c) 2006 - 2010 PHPExcel  
 * This library is free software; you can redistribute it and/or  
 * modify it under the terms of the GNU Lesser General Public  
 * License as published by the Free Software Foundation; either  
 * version 2.1 of the License, or (at your option) any later version.  
 * This library is distributed in the hope that it will be useful,  
 * but WITHOUT ANY WARRANTY; without even the implied warranty of  
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU  
 * Lesser General Public License for more details.  
 * You should have received a copy of the GNU Lesser General Public  
 * License along with this library; if not, write to the Free Software  
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA  
 * @package        PHPExcel_Calculation  
 * @copyright    Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel)  
 * @license        http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL  
 * @version        1.7.3c, 2010-06-01  
/** PHPExcel root directory */  
    define('PHPEXCEL_ROOT', dirname(__FILE__ ) .  '/../../');  
    require (PHPEXCEL_ROOT .  'PHPExcel/Autoloader.php'); 
    // check mbstring.func_overload  
    if (ini_get('mbstring.func_overload') & 2) {  
        throw  new Exception('Multibyte function overloading in PHP must be disabled for string functions (2).'); 
/** LOG_GAMMA_X_MAX_VALUE */  
define('LOG_GAMMA_X_MAX_VALUE', 2.55e305);  
define('SQRT2PI', 2.5066282746310005024157652848110452530069867406099);  
define('M_2DIVPI', 0.63661977236758134307553505349006);  
define('MAX_ITERATIONS', 256);  
/** FINANCIAL_MAX_ITERATIONS */  
define('FINANCIAL_MAX_ITERATIONS', 128);  
define('PRECISION', 8.88E-016);  
/** FINANCIAL_PRECISION */  
define('FINANCIAL_PRECISION', 1.0e-08);  
define('EULER', 2.71828182845904523536);  
$savedPrecision =  ini_get('precision');  
if ($savedPrecision <  16) {  
require_once PHPEXCEL_ROOT .  'PHPExcel/Shared/JAMA/Matrix.php';  
require_once PHPEXCEL_ROOT .  'PHPExcel/Shared/trend/trendClass.php';  
 * PHPExcel_Calculation_Functions  
 * @package        PHPExcel_Calculation  
 * @copyright    Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel)  
    const COMPATIBILITY_EXCEL        =  'Excel';  
    const COMPATIBILITY_GNUMERIC    =  'Gnumeric';  
    const COMPATIBILITY_OPENOFFICE    =  'OpenOfficeCalc';  
    const RETURNDATE_PHP_NUMERIC    =  'P';  
    const RETURNDATE_PHP_OBJECT        =  'O';  
    const RETURNDATE_EXCEL            =  'E';  
     *    Compatibility mode to use for error checking and responses  
    private static $compatibilityMode    =  self::COMPATIBILITY_EXCEL;  
     *    Data Type to use when returning date values  
    private static $ReturnDateType    =  self::RETURNDATE_EXCEL;  
    private static $_errorCodes    =  array( 'null'                =>  '#NULL!',  
                                         'divisionbyzero'    =>  '#DIV/0!',  
                                         'gettingdata'        =>  '#GETTING_DATA'  
     *    Set the Compatibility Mode  
     *    @category Function Configuration  
     *    @param     string        $compatibilityMode        Compatibility Mode  
     *                                                     PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL            'Excel'  
     *                                                     PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC        'Gnumeric'  
     *                                                     PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE    'OpenOfficeCalc'  
     *    @return     boolean    (Success or Failure)  
        if (($compatibilityMode ==  self::COMPATIBILITY_EXCEL) ||   
            ($compatibilityMode ==  self::COMPATIBILITY_GNUMERIC) ||   
            ($compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE)) {  
            self::$compatibilityMode =  $compatibilityMode;  
    }    //    function setCompatibilityMode()  
     *    Return the current Compatibility Mode  
     *    @category Function Configuration  
     *    @return     string        Compatibility Mode  
     *                             Possible Return values are:  
     *                                 PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL            'Excel'  
     *                                 PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC        'Gnumeric'  
     *                                 PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE    'OpenOfficeCalc'  
        return self::$compatibilityMode;  
    }    //    function getCompatibilityMode()  
     *    Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object)  
     *    @category Function Configuration  
     *    @param     string    $returnDateType            Return Date Format  
     *                                                     PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC        'P'  
     *                                                     PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT        'O'  
     *                                                     PHPExcel_Calculation_Functions::RETURNDATE_EXCEL            'E'  
     *    @return     boolean                            Success or failure  
        if (($returnDateType ==  self::RETURNDATE_PHP_NUMERIC) ||   
            ($returnDateType ==  self::RETURNDATE_PHP_OBJECT) ||   
            ($returnDateType ==  self::RETURNDATE_EXCEL)) {  
            self::$ReturnDateType =  $returnDateType;  
    }    //    function setReturnDateType()  
     *    Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object)  
     *    @category Function Configuration  
     *    @return     string        Return Date Format  
     *                             Possible Return values are:  
     *                                 PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC        'P'  
     *                                 PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT        'O'  
     *                                 PHPExcel_Calculation_Functions::RETURNDATE_EXCEL            'E'  
        return self::$ReturnDateType;  
    }    //    function getReturnDateType()  
     *    @category Error Returns  
     *    @return    string    #Not Yet Implemented  
    public static function DUMMY() {  
        return '#Not Yet Implemented';  
     *    Returns the error value #N/A  
     *        #N/A is the error value that means "no value is available."  
     *    @category Logical Functions  
    public static function NA() {  
        return self::$_errorCodes['na'];  
     *    Returns the error value #NUM!  
     *    @category Error Returns  
    public static function NaN() {  
        return self::$_errorCodes['num'];  
     *    Returns the error value #NAME?  
     *    @category Error Returns  
    public static function NAME() {  
        return self::$_errorCodes['name'];  
     *    Returns the error value #REF!  
     *    @category Error Returns  
    public static function REF() {  
        return self::$_errorCodes['reference'];  
     *    Returns the error value #VALUE!  
     *    @category Error Returns  
    public static function VALUE() {  
        return self::$_errorCodes['value'];  
    private static function isMatrixValue($idx) {  
        return ((substr_count($idx,'.') <=  1) ||  (preg_match('/\.[A-Z]/',$idx) >  0));  
    private static function isValue($idx) {  
    private static function isCellValue($idx) {  
     *    Returns boolean TRUE if all its arguments are TRUE; returns FALSE if one or more argument is FALSE.  
     *        =AND(logical1[,logical2[, ...]])  
     *        The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays  
     *            or references that contain logical values.  
     *        Boolean arguments are treated as True or False as appropriate  
     *        Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False  
     *        If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds  
     *            the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value  
     *    @category Logical Functions  
     *    @param    mixed        $arg,...        Data values  
     *    @return    boolean        The logical AND of the arguments.  
        // Loop through the arguments  
        foreach ($aArgs as $arg) {  
            // Is it a boolean value?  
                $returnValue =  $returnValue &&  $arg;  
                $returnValue =  $returnValue &&  ($arg !=  0);  
                } elseif ($arg ==  'FALSE') {  
                    return self::$_errorCodes['value'];  
                $returnValue =  $returnValue &&  ($arg !=  0);  
            return self::$_errorCodes['value'];  
    }    //    function LOGICAL_AND()  
     *    Returns boolean TRUE if any argument is TRUE; returns FALSE if all arguments are FALSE.  
     *        =OR(logical1[,logical2[, ...]])  
     *        The arguments must evaluate to logical values such as TRUE or FALSE, or the arguments must be arrays  
     *            or references that contain logical values.  
     *        Boolean arguments are treated as True or False as appropriate  
     *        Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False  
     *        If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds  
     *            the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value  
     *    @category Logical Functions  
     *    @param    mixed        $arg,...        Data values  
     *    @return    boolean        The logical OR of the arguments.  
        // Loop through the arguments  
        foreach ($aArgs as $arg) {  
            // Is it a boolean value?  
                $returnValue =  $returnValue ||  $arg;  
                $returnValue =  $returnValue ||  ($arg !=  0);  
                } elseif ($arg ==  'FALSE') {  
                    return self::$_errorCodes['value'];  
                $returnValue =  $returnValue ||  ($arg !=  0);  
            return self::$_errorCodes['value'];  
    }    //    function LOGICAL_OR()  
     *    Returns the boolean FALSE.  
     *    @category Logical Functions  
    }    //    function LOGICAL_FALSE()  
     *    Returns the boolean TRUE.  
     *    @category Logical Functions  
    }    //    function LOGICAL_TRUE()  
     *    Returns the boolean inverse of the argument.  
     *        The argument must evaluate to a logical value such as TRUE or FALSE  
     *        Boolean arguments are treated as True or False as appropriate  
     *        Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False  
     *        If any argument value is a string, or a Null, the function returns a #VALUE! error, unless the string holds  
     *            the value TRUE or FALSE, in which case it is evaluated as the corresponding boolean value  
     *    @category Logical Functions  
     *    @param    mixed        $logical    A value or expression that can be evaluated to TRUE or FALSE  
     *    @return    boolean        The boolean inverse of the argument.  
        $logical =  self::flattenSingleValue($logical);  
            if ($logical ==  'TRUE') {  
            } elseif ($logical ==  'FALSE') {  
                return self::$_errorCodes['value'];  
    }    //    function LOGICAL_NOT()  
     *    Returns one value if a condition you specify evaluates to TRUE and another value if it evaluates to FALSE.  
     *        =IF(condition[,returnIfTrue[,returnIfFalse]])  
     *        Condition is any value or expression that can be evaluated to TRUE or FALSE.  
     *            For example, A10=100 is a logical expression; if the value in cell A10 is equal to 100,  
     *            the expression evaluates to TRUE. Otherwise, the expression evaluates to FALSE.  
     *            This argument can use any comparison calculation operator.  
     *        ReturnIfTrue is the value that is returned if condition evaluates to TRUE.  
     *            For example, if this argument is the text string "Within budget" and the condition argument evaluates to TRUE,  
     *            then the IF function returns the text "Within budget"  
     *            If condition is TRUE and ReturnIfTrue is blank, this argument returns 0 (zero). To display the word TRUE, use  
     *            the logical value TRUE for this argument.  
     *            ReturnIfTrue can be another formula.  
     *        ReturnIfFalse is the value that is returned if condition evaluates to FALSE.  
     *            For example, if this argument is the text string "Over budget" and the condition argument evaluates to FALSE,  
     *            then the IF function returns the text "Over budget".  
     *            If condition is FALSE and ReturnIfFalse is omitted, then the logical value FALSE is returned.  
     *            If condition is FALSE and ReturnIfFalse is blank, then the value 0 (zero) is returned.  
     *            ReturnIfFalse can be another formula.  
     *    @category Logical Functions  
     *    @param    mixed    $condition        Condition to evaluate  
     *    @param    mixed    $returnIfTrue    Value to return when condition is true  
     *    @param    mixed    $returnIfFalse    Optional value to return when condition is false  
     *    @return    mixed    The value of returnIfTrue or returnIfFalse determined by condition  
    public static function STATEMENT_IF($condition =  true, $returnIfTrue =  0, $returnIfFalse =  False) {  
        $condition        =  (is_null($condition))        ?  True :    (boolean)  self::flattenSingleValue($condition);  
        $returnIfTrue    =  (is_null($returnIfTrue))    ?  0 :         self::flattenSingleValue($returnIfTrue);  
        $returnIfFalse    =  (is_null($returnIfFalse))    ?  False :     self::flattenSingleValue($returnIfFalse);  
        return ($condition ?  $returnIfTrue :  $returnIfFalse);  
    }    //    function STATEMENT_IF()  
     *        =IFERROR(testValue,errorpart)  
     *    @category Logical Functions  
     *    @param    mixed    $testValue    Value to check, is also the value returned when no error  
     *    @param    mixed    $errorpart    Value to return when testValue is an error condition  
     *    @return    mixed    The value of errorpart or testValue determined by error condition  
        $testValue    =  (is_null($testValue))    ?  '' :     self::flattenSingleValue($testValue);  
        $errorpart    =  (is_null($errorpart))    ?  '' :     self::flattenSingleValue($errorpart);  
        return self::STATEMENT_IF(self::IS_ERROR($testValue), $errorpart, $testValue);  
    }    //    function STATEMENT_IFERROR()  
     *        =HYPERLINK(linkURL,displayName)  
     *    @category Logical Functions  
     *    @param    string    $linkURL        Value to check, is also the value returned when no error  
     *    @param    string    $displayName    Value to return when testValue is an error condition  
     *    @return    mixed    The value of errorpart or testValue determined by error condition  
    public static function HYPERLINK($linkURL =  '', $displayName =  null, PHPExcel_Cell $pCell =  null) {  
        $linkURL        =  (is_null($linkURL))        ?  '' :     self::flattenSingleValue($linkURL);  
        $displayName    =  (is_null($displayName))    ?  '' :     self::flattenSingleValue($displayName);  
            return self::$_errorCodes['reference'];  
        if ((is_object($displayName)) ||  trim($displayName) ==  '') {  
        $pCell->getHyperlink()->setUrl($linkURL);  
    }    //    function HYPERLINK()  
     *    This function calculates the arc tangent of the two variables x and y. It is similar to  
     *        calculating the arc tangent of y ÷ x, except that the signs of both arguments are used  
     *        to determine the quadrant of the result.  
     *    The arctangent is the angle from the x-axis to a line containing the origin (0, 0) and a  
     *        point with coordinates (xCoordinate, yCoordinate). The angle is given in radians between  
     *        -pi and pi, excluding -pi.  
     *    Note that the Excel ATAN2() function accepts its arguments in the reverse order to the standard  
     *        PHP atan2() function, so we need to reverse them here before calling the PHP atan() function.  
     *        ATAN2(xCoordinate,yCoordinate)  
     *    @category Mathematical and Trigonometric Functions  
     *    @param    float    $xCoordinate        The x-coordinate of the point.  
     *    @param    float    $yCoordinate        The y-coordinate of the point.  
     *    @return    float    The inverse tangent of the specified x- and y-coordinates.  
    public static function REVERSE_ATAN2($xCoordinate, $yCoordinate) {  
        $xCoordinate    = (float)  self::flattenSingleValue($xCoordinate);  
        $yCoordinate    = (float)  self::flattenSingleValue($yCoordinate);  
        if (($xCoordinate ==  0) &&  ($yCoordinate ==  0)) {  
            return self::$_errorCodes['divisionbyzero'];  
        return atan2($yCoordinate, $xCoordinate);  
    }    //    function REVERSE_ATAN2()  
     *    Returns the logarithm of a number to a specified base. The default base is 10.  
     *    @category Mathematical and Trigonometric Functions  
     *    @param    float    $value        The positive real number for which you want the logarithm  
     *    @param    float    $base        The base of the logarithm. If base is omitted, it is assumed to be 10.  
    public static function LOG_BASE($number, $base= 10) {  
        $number    =  self::flattenSingleValue($number);  
        $base    =  (is_null($base))    ?  10 :    (float)  self::flattenSingleValue($base);  
        return log($number, $base);  
     *    SUM computes the sum of all the values and cells referenced in the argument list.  
     *        SUM(value1[,value2[, ...]])  
     *    @category Mathematical and Trigonometric Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function SUM() {  
        // Loop through the arguments  
        foreach ($aArgs as $arg) {  
            // Is it a numeric value?  
     *    SUMSQ returns the sum of the squares of the arguments  
     *        SUMSQ(value1[,value2[, ...]])  
     *    @category Mathematical and Trigonometric Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function SUMSQ() {  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            // Is it a numeric value?  
                $returnValue +=  ($arg *  $arg);  
     *    PRODUCT returns the product of all the values and cells referenced in the argument list.  
     *        PRODUCT(value1[,value2[, ...]])  
     *    @category Mathematical and Trigonometric Functions  
     *    @param    mixed        $arg,...        Data values  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            // Is it a numeric value?  
     *    QUOTIENT function returns the integer portion of a division. Numerator is the divided number  
     *        and denominator is the divisor.  
     *        QUOTIENT(value1[,value2[, ...]])  
     *    @category Mathematical and Trigonometric Functions  
     *    @param    mixed        $arg,...        Data values  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            // Is it a numeric value?  
                    $returnValue =  ($arg ==  0) ?  0 :  $arg;  
                    if (($returnValue ==  0) ||  ($arg ==  0)) {  
     *    MIN returns the value of the element of the values passed that has the smallest value,  
     *        with negative numbers considered smaller than positive numbers.  
     *        MIN(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function MIN() {  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            // Is it a numeric value?  
                if ((is_null($returnValue)) ||  ($arg <  $returnValue)) {  
     *    Returns the smallest value in a list of arguments, including numbers, text, and logical values  
     *        MINA(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function MINA() {  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            // Is it a numeric value?  
                if ((is_null($returnValue)) ||  ($arg <  $returnValue)) {  
     *    Returns the minimum value within a range of cells that contain numbers within the list of arguments  
     *        MINIF(value1[,value2[, ...]],condition)  
     *    @category Mathematical and Trigonometric Functions  
     *    @param    mixed        $arg,...        Data values  
     *    @param    string        $condition        The criteria that defines which cells will be checked.  
    public static function MINIF($aArgs,$condition,$sumArgs =  array()) {  
        $aArgs =  self::flattenArray($aArgs);  
        $sumArgs =  self::flattenArray($sumArgs);  
        if (count($sumArgs) ==  0) {  
        $condition =  self::_ifCondition($condition);  
        // Loop through arguments  
        foreach ($aArgs as $key =>  $arg) {  
            $testCondition =  '='. $arg. $condition;  
                if ((is_null($returnValue)) ||  ($arg <  $returnValue)) {  
     *    Returns the nth smallest value in a data set. You can use this function to  
     *        select a value based on its relative standing.  
     *        SMALL(value1[,value2[, ...]],entry)  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
     *    @param    int            $entry            Position (ordered from the smallest) in the array or range of data to return  
    public static function SMALL() {  
            foreach ($aArgs as $arg) {  
                // Is it a numeric value?  
            $count =  self::COUNT($mArgs);  
            $entry =  floor(-- $entry);  
            if (($entry <  0) ||  ($entry >=  $count) ||  ($count ==  0)) {  
                return self::$_errorCodes['num'];  
        return self::$_errorCodes['value'];  
     *    MAX returns the value of the element of the values passed that has the highest value,  
     *        with negative numbers considered smaller than positive numbers.  
     *        MAX(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function MAX() {  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            // Is it a numeric value?  
                if ((is_null($returnValue)) ||  ($arg >  $returnValue)) {  
     *    Returns the greatest value in a list of arguments, including numbers, text, and logical values  
     *        MAXA(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function MAXA() {  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            // Is it a numeric value?  
                if ((is_null($returnValue)) ||  ($arg >  $returnValue)) {  
    private static function _ifCondition($condition) {  
        $condition    =  self::flattenSingleValue($condition);  
        if (!in_array($condition{0},array('>', '<', '='))) {  
            list (,$operator,$operand) =  $matches; 
            return $operator. $operand;  
    }    //    function _ifCondition()  
     *    Counts the maximum value within a range of cells that contain numbers within the list of arguments  
     *        MAXIF(value1[,value2[, ...]],condition)  
     *    @category Mathematical and Trigonometric Functions  
     *    @param    mixed        $arg,...        Data values  
     *    @param    string        $condition        The criteria that defines which cells will be checked.  
    public static function MAXIF($aArgs,$condition,$sumArgs =  array()) {  
        $aArgs =  self::flattenArray($aArgs);  
        $sumArgs =  self::flattenArray($sumArgs);  
        if (count($sumArgs) ==  0) {  
        $condition =  self::_ifCondition($condition);  
        // Loop through arguments  
        foreach ($aArgs as $key =>  $arg) {  
            $testCondition =  '='. $arg. $condition;  
                if ((is_null($returnValue)) ||  ($arg >  $returnValue)) {  
     *    Returns the nth largest value in a data set. You can use this function to  
     *        select a value based on its relative standing.  
     *        LARGE(value1[,value2[, ...]],entry)  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
     *    @param    int            $entry            Position (ordered from the largest) in the array or range of data to return  
    public static function LARGE() {  
            foreach ($aArgs as $arg) {  
                // Is it a numeric value?  
            $count =  self::COUNT($mArgs);  
            $entry =  floor(-- $entry);  
            if (($entry <  0) ||  ($entry >=  $count) ||  ($count ==  0)) {  
                return self::$_errorCodes['num'];  
        return self::$_errorCodes['value'];  
     *    Returns the nth percentile of values in a range..  
     *        PERCENTILE(value1[,value2[, ...]],entry)  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
     *    @param    float        $entry            Percentile value in the range 0..1, inclusive.  
            if (($entry <  0) ||  ($entry >  1)) {  
                return self::$_errorCodes['num'];  
            foreach ($aArgs as $arg) {  
                // Is it a numeric value?  
                if ((is_numeric($arg)) &&  (!is_string($arg))) {  
            $mValueCount =  count($mArgs);  
                $count =  self::COUNT($mArgs);  
                $index =  $entry *  ($count- 1);  
                    $iProportion =  $index -  $iBase;  
                    return $mArgs[$iBase] +  (($mArgs[$iNext] -  $mArgs[$iBase]) *  $iProportion) ;  
        return self::$_errorCodes['value'];  
    }    //    function PERCENTILE()  
     *    Returns the quartile of a data set.  
     *        QUARTILE(value1[,value2[, ...]],entry)  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
     *    @param    int            $entry            Quartile value in the range 1..3, inclusive.  
            if (($entry <  0) ||  ($entry >  1)) {  
                return self::$_errorCodes['num'];  
            return self::PERCENTILE($aArgs,$entry);  
        return self::$_errorCodes['value'];  
     *    Counts the number of cells that contain numbers within the list of arguments  
     *        COUNT(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function COUNT() {  
        // Loop through arguments  
        foreach ($aArgs as $k =>  $arg) {  
                ((!self::isCellValue($k)) ||  (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE))) {  
            // Is it a numeric value?  
     *    Counts the number of empty cells within the list of arguments  
     *        COUNTBLANK(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
    }    //    function COUNTBLANK()  
     *    Counts the number of cells that are not empty within the list of arguments  
     *        COUNTA(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function COUNTA() {  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            // Is it a numeric, boolean or string value?  
     *    Counts the number of cells that contain numbers within the list of arguments  
     *        COUNTIF(value1[,value2[, ...]],condition)  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
     *    @param    string        $condition        The criteria that defines which cells will be counted.  
    public static function COUNTIF($aArgs,$condition) {  
        $aArgs =  self::flattenArray($aArgs);  
        $condition =  self::_ifCondition($condition);  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            $testCondition =  '='. $arg. $condition;  
                // Is it a value within our criteria  
     *    Counts the number of cells that contain numbers within the list of arguments  
     *        SUMIF(value1[,value2[, ...]],condition)  
     *    @category Mathematical and Trigonometric Functions  
     *    @param    mixed        $arg,...        Data values  
     *    @param    string        $condition        The criteria that defines which cells will be summed.  
    public static function SUMIF($aArgs,$condition,$sumArgs =  array()) {  
        $aArgs =  self::flattenArray($aArgs);  
        $sumArgs =  self::flattenArray($sumArgs);  
        if (count($sumArgs) ==  0) {  
        $condition =  self::_ifCondition($condition);  
        // Loop through arguments  
        foreach ($aArgs as $key =>  $arg) {  
            $testCondition =  '='. $arg. $condition;  
                // Is it a value within our criteria  
                $returnValue +=  $sumArgs[$key];  
     *    Returns the average (arithmetic mean) of the arguments  
     *        AVERAGE(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
        $returnValue =  $aCount =  0;  
        // Loop through arguments  
        foreach ($aArgs as $k =>  $arg) {  
                ((!self::isCellValue($k)) ||  (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE))) {  
            // Is it a numeric value?  
            return $returnValue /  $aCount;  
            return self::$_errorCodes['divisionbyzero'];  
     *    Returns the average of its arguments, including numbers, text, and logical values  
     *        AVERAGEA(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
        // Loop through arguments  
        foreach ($aArgs as $k =>  $arg) {  
                (!self::isMatrixValue($k))) {  
            return $returnValue /  $aCount;  
            return self::$_errorCodes['divisionbyzero'];  
     *    Returns the average value from a range of cells that contain numbers within the list of arguments  
     *        AVERAGEIF(value1[,value2[, ...]],condition)  
     *    @category Mathematical and Trigonometric Functions  
     *    @param    mixed        $arg,...        Data values  
     *    @param    string        $condition        The criteria that defines which cells will be checked.  
    public static function AVERAGEIF($aArgs,$condition,$averageArgs =  array()) {  
        $aArgs =  self::flattenArray($aArgs);  
        $averageArgs =  self::flattenArray($averageArgs);  
        if (count($averageArgs) ==  0) {  
        $condition =  self::_ifCondition($condition);  
        // Loop through arguments  
        foreach ($aArgs as $key =>  $arg) {  
            $testCondition =  '='. $arg. $condition;  
                if ((is_null($returnValue)) ||  ($arg >  $returnValue)) {  
            return $returnValue /  $aCount;  
            return self::$_errorCodes['divisionbyzero'];  
    }    //    function AVERAGEIF()  
     *    Returns the median of the given numbers. The median is the number in the middle of a set of numbers.  
     *        MEDIAN(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function MEDIAN() {  
        $returnValue =  self::$_errorCodes['num'];  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            // Is it a numeric value?  
        $mValueCount =  count($mArgs);  
            sort($mArgs,SORT_NUMERIC);  
            $mValueCount =  $mValueCount /  2;  
            if ($mValueCount ==  floor($mValueCount)) {  
                $returnValue =  ($mArgs[$mValueCount-- ] +  $mArgs[$mValueCount]) /  2;  
                $mValueCount ==  floor($mValueCount);  
                $returnValue =  $mArgs[$mValueCount];  
    //    Special variant of array_count_values that isn't limited to strings and integers,  
    //        but can work with floating point numbers as values  
    private static function _modeCalc($data) {  
        $frequencyArray =  array();  
        foreach($data as $datum) {  
            foreach($frequencyArray as $key =>  $value) {  
                if ((string)  $value['value'] == (string)  $datum) {  
                    ++ $frequencyArray[$key]['frequency']; 
                $frequencyArray[] =  array('value'        =>  $datum,  
        foreach($frequencyArray as $key =>  $value) {  
            $frequencyList[$key] =  $value['frequency'];  
            $valueList[$key] =  $value['value'];  
        array_multisort($frequencyList, SORT_DESC, $valueList, SORT_ASC, SORT_NUMERIC, $frequencyArray);  
        if ($frequencyArray[0]['frequency'] ==  1) {  
        return $frequencyArray[0]['value'];  
    }    //    function _modeCalc()  
     *    Returns the most frequently occurring, or repetitive, value in an array or range of data  
     *        MODE(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function MODE() {  
        $returnValue =  self::NA();  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            // Is it a numeric value?  
            return self::_modeCalc($mArgs);  
     *    Returns the sum of squares of deviations of data points from their sample mean.  
     *        DEVSQ(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function DEVSQ() {  
        $aMean =  self::AVERAGE($aArgs);  
        if ($aMean !=  self::$_errorCodes['divisionbyzero']) {  
            foreach ($aArgs as $k =>  $arg) {  
                // Is it a numeric value?  
                    ((!self::isCellValue($k)) ||  (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE))) {  
                        $returnValue =  pow(($arg -  $aMean),2);  
                        $returnValue +=  pow(($arg -  $aMean),2);  
                return self::$_errorCodes['num'];  
     *    Returns the average of the absolute deviations of data points from their mean.  
     *    AVEDEV is a measure of the variability in a data set.  
     *        AVEDEV(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function AVEDEV() {  
        $aMean =  self::AVERAGE($aArgs);  
        if ($aMean !=  self::$_errorCodes['divisionbyzero']) {  
            foreach ($aArgs as $k =>  $arg) {  
                    ((!self::isCellValue($k)) ||  (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE))) {  
                // Is it a numeric value?  
                        $returnValue =  abs($arg -  $aMean);  
                        $returnValue +=  abs($arg -  $aMean);  
                return self::$_errorCodes['divisionbyzero'];  
            return $returnValue /  $aCount;  
        return self::$_errorCodes['num'];  
     *    Returns the geometric mean of an array or range of positive data. For example, you  
     *        can use GEOMEAN to calculate average growth rate given compound interest with  
     *        GEOMEAN(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
        $aMean =  self::PRODUCT($aArgs);  
            $aCount =  self::COUNT($aArgs) ;  
            if (self::MIN($aArgs) >  0) {  
                return pow($aMean, (1 /  $aCount));  
        return self::$_errorCodes['num'];  
     *    Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the  
     *        arithmetic mean of reciprocals.  
     *        HARMEAN(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
        $returnValue =  self::NA();  
        // Loop through arguments  
        if (self::MIN($aArgs) <  0) {  
            return self::$_errorCodes['num'];  
        foreach ($aArgs as $arg) {  
            // Is it a numeric value?  
            if ((is_numeric($arg)) &&  (!is_string($arg))) {  
                    return self::$_errorCodes['num'];  
                if (is_null($returnValue)) {  
                    $returnValue =  (1 /  $arg);  
                    $returnValue +=  (1 /  $arg);  
            return 1 /  ($returnValue /  $aCount);  
     *    Returns the mean of the interior of a data set. TRIMMEAN calculates the mean  
     *    taken by excluding a percentage of data points from the top and bottom tails  
     *        TRIMEAN(value1[,value2[, ...]],$discard)  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
     *    @param    float        $discard        Percentage to discard  
            if (($percent <  0) ||  ($percent >  1)) {  
                return self::$_errorCodes['num'];  
            foreach ($aArgs as $arg) {  
                // Is it a numeric value?  
                if ((is_numeric($arg)) &&  (!is_string($arg))) {  
            $discard =  floor(self::COUNT($mArgs) *  $percent /  2);  
            for ($i= 0; $i <  $discard; ++ $i) {  
            return self::AVERAGE($mArgs);  
        return self::$_errorCodes['value'];  
     *    Estimates standard deviation based on a sample. The standard deviation is a measure of how  
     *    widely values are dispersed from the average value (the mean).  
     *        STDEV(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function STDEV() {  
        $aMean =  self::AVERAGE($aArgs);  
            foreach ($aArgs as $k =>  $arg) {  
                    ((!self::isCellValue($k)) ||  (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE))) {  
                // Is it a numeric value?  
                        $returnValue =  pow(($arg -  $aMean),2);  
                        $returnValue +=  pow(($arg -  $aMean),2);  
            if (($aCount >  0) &&  ($returnValue >  0)) {  
                return sqrt($returnValue /  $aCount);  
        return self::$_errorCodes['divisionbyzero'];  
     *    Estimates standard deviation based on a sample, including numbers, text, and logical values  
     *        STDEVA(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function STDEVA() {  
        $aMean =  self::AVERAGEA($aArgs);  
            foreach ($aArgs as $k =>  $arg) {  
                    (!self::isMatrixValue($k))) {  
                    // Is it a numeric value?  
                            $returnValue =  pow(($arg -  $aMean),2);  
                            $returnValue +=  pow(($arg -  $aMean),2);  
            if (($aCount >  0) &&  ($returnValue >  0)) {  
                return sqrt($returnValue /  $aCount);  
        return self::$_errorCodes['divisionbyzero'];  
     *    Calculates standard deviation based on the entire population  
     *        STDEVP(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function STDEVP() {  
        $aMean =  self::AVERAGE($aArgs);  
            foreach ($aArgs as $k =>  $arg) {  
                    ((!self::isCellValue($k)) ||  (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE))) {  
                // Is it a numeric value?  
                        $returnValue =  pow(($arg -  $aMean),2);  
                        $returnValue +=  pow(($arg -  $aMean),2);  
            if (($aCount >  0) &&  ($returnValue >  0)) {  
                return sqrt($returnValue /  $aCount);  
        return self::$_errorCodes['divisionbyzero'];  
     *    Calculates standard deviation based on the entire population, including numbers, text, and logical values  
     *        STDEVPA(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
        $aMean =  self::AVERAGEA($aArgs);  
            foreach ($aArgs as $k =>  $arg) {  
                    (!self::isMatrixValue($k))) {  
                    // Is it a numeric value?  
                            $returnValue =  pow(($arg -  $aMean),2);  
                            $returnValue +=  pow(($arg -  $aMean),2);  
            if (($aCount >  0) &&  ($returnValue >  0)) {  
                return sqrt($returnValue /  $aCount);  
        return self::$_errorCodes['divisionbyzero'];  
     *    Estimates variance based on a sample.  
     *        VAR(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
        $returnValue =  self::$_errorCodes['divisionbyzero'];  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            if (is_bool($arg)) { $arg = (integer)  $arg; }  
            // Is it a numeric value?  
                $summerA +=  ($arg *  $arg);  
            $returnValue =  ($summerA -  $summerB) /  ($aCount *  ($aCount -  1));  
     *    Estimates variance based on a sample, including numbers, text, and logical values  
     *        VARA(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function VARA() {  
        $returnValue =  self::$_errorCodes['divisionbyzero'];  
        // Loop through arguments  
        foreach ($aArgs as $k =>  $arg) {  
                return self::$_errorCodes['value'];  
            } elseif ((is_string($arg)) &&   
                (!self::isMatrixValue($k))) {  
                // Is it a numeric value?  
                    $summerA +=  ($arg *  $arg);  
            $returnValue =  ($summerA -  $summerB) /  ($aCount *  ($aCount -  1));  
     *    Calculates variance based on the entire population  
     *        VARP(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function VARP() {  
        $returnValue =  self::$_errorCodes['divisionbyzero'];  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            if (is_bool($arg)) { $arg = (integer)  $arg; }  
            // Is it a numeric value?  
                $summerA +=  ($arg *  $arg);  
            $returnValue =  ($summerA -  $summerB) /  ($aCount *  $aCount);  
     *    Calculates variance based on the entire population, including numbers, text, and logical values  
     *        VARPA(value1[,value2[, ...]])  
     *    @category Statistical Functions  
     *    @param    mixed        $arg,...        Data values  
    public static function VARPA() {  
        $returnValue =  self::$_errorCodes['divisionbyzero'];  
        // Loop through arguments  
        foreach ($aArgs as $k =>  $arg) {  
                return self::$_errorCodes['value'];  
            } elseif ((is_string($arg)) &&   
                (!self::isMatrixValue($k))) {  
                // Is it a numeric value?  
                    $summerA +=  ($arg *  $arg);  
            $returnValue =  ($summerA -  $summerB) /  ($aCount *  $aCount);  
     *    Returns the rank of a number in a list of numbers.  
     *    @param    number                The number whose rank you want to find.  
     *    @param    array of number        An array of, or a reference to, a list of numbers.  
     *    @param    mixed                Order to sort the values in the value set  
    public static function RANK($value,$valueSet,$order= 0) {  
        $value =  self::flattenSingleValue($value);  
        $valueSet =  self::flattenArray($valueSet);  
        $order    =  (is_null($order))    ?  0 :    (integer)  self::flattenSingleValue($order);  
        foreach($valueSet as $key =>  $valueEntry) {  
            rsort($valueSet,SORT_NUMERIC);  
            sort($valueSet,SORT_NUMERIC);  
            return self::$_errorCodes['na'];  
     *    Returns the rank of a value in a data set as a percentage of the data set.  
     *    @param    array of number        An array of, or a reference to, a list of numbers.  
     *    @param    number                The number whose rank you want to find.  
     *    @param    number                The number of significant digits for the returned percentage value.  
    public static function PERCENTRANK($valueSet,$value,$significance= 3) {  
        $valueSet    =  self::flattenArray($valueSet);  
        $value        =  self::flattenSingleValue($value);  
        $significance    =  (is_null($significance))    ?  3 :    (integer)  self::flattenSingleValue($significance);  
        foreach($valueSet as $key =>  $valueEntry) {  
        sort($valueSet,SORT_NUMERIC);  
        $valueCount =  count($valueSet);  
            return self::$_errorCodes['num'];  
        $valueAdjustor =  $valueCount -  1;  
        if (($value <  $valueSet[0]) ||  ($value >  $valueSet[$valueAdjustor])) {  
            return self::$_errorCodes['na'];  
        $pos =  array_search($value,$valueSet);  
            $testValue =  $valueSet[0];  
            while ($testValue <  $value) {  
                $testValue =  $valueSet[++ $pos];  
            $pos +=  (($value -  $valueSet[$pos]) /  ($testValue -  $valueSet[$pos]));  
        return round($pos /  $valueAdjustor,$significance);  
    }    //    function PERCENTRANK()  
    private static function _checkTrendArrays(&$array1,&$array2) {  
        if (!is_array($array1)) { $array1 =  array($array1); }  
        if (!is_array($array2)) { $array2 =  array($array2); }  
        $array1 =  self::flattenArray($array1);  
        $array2 =  self::flattenArray($array2);  
        foreach($array1 as $key =>  $value) {  
        foreach($array2 as $key =>  $value) {  
    }    //    function _checkTrendArrays()  
     *    Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.  
     *    @param    array of mixed        Data Series Y  
     *    @param    array of mixed        Data Series X  
    public static function INTERCEPT($yValues,$xValues) {  
        if (!self::_checkTrendArrays($yValues,$xValues)) {  
            return self::$_errorCodes['value'];  
        $yValueCount =  count($yValues);  
        $xValueCount =  count($xValues);  
        if (($yValueCount ==  0) ||  ($yValueCount !=  $xValueCount)) {  
            return self::$_errorCodes['na'];  
        } elseif ($yValueCount ==  1) {  
            return self::$_errorCodes['divisionbyzero'];  
        $bestFitLinear =  trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);  
        return $bestFitLinear->getIntersect();  
    }    //    function INTERCEPT()  
     *    Returns the square of the Pearson product moment correlation coefficient through data points in known_y's and known_x's.  
     *    @param    array of mixed        Data Series Y  
     *    @param    array of mixed        Data Series X  
    public static function RSQ($yValues,$xValues) {  
        if (!self::_checkTrendArrays($yValues,$xValues)) {  
            return self::$_errorCodes['value'];  
        $yValueCount =  count($yValues);  
        $xValueCount =  count($xValues);  
        if (($yValueCount ==  0) ||  ($yValueCount !=  $xValueCount)) {  
            return self::$_errorCodes['na'];  
        } elseif ($yValueCount ==  1) {  
            return self::$_errorCodes['divisionbyzero'];  
        $bestFitLinear =  trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);  
        return $bestFitLinear->getGoodnessOfFit();  
     *    Returns the slope of the linear regression line through data points in known_y's and known_x's.  
     *    @param    array of mixed        Data Series Y  
     *    @param    array of mixed        Data Series X  
    public static function SLOPE($yValues,$xValues) {  
        if (!self::_checkTrendArrays($yValues,$xValues)) {  
            return self::$_errorCodes['value'];  
        $yValueCount =  count($yValues);  
        $xValueCount =  count($xValues);  
        if (($yValueCount ==  0) ||  ($yValueCount !=  $xValueCount)) {  
            return self::$_errorCodes['na'];  
        } elseif ($yValueCount ==  1) {  
            return self::$_errorCodes['divisionbyzero'];  
        $bestFitLinear =  trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);  
        return $bestFitLinear->getSlope();  
     *    Returns the standard error of the predicted y-value for each x in the regression.  
     *    @param    array of mixed        Data Series Y  
     *    @param    array of mixed        Data Series X  
    public static function STEYX($yValues,$xValues) {  
        if (!self::_checkTrendArrays($yValues,$xValues)) {  
            return self::$_errorCodes['value'];  
        $yValueCount =  count($yValues);  
        $xValueCount =  count($xValues);  
        if (($yValueCount ==  0) ||  ($yValueCount !=  $xValueCount)) {  
            return self::$_errorCodes['na'];  
        } elseif ($yValueCount ==  1) {  
            return self::$_errorCodes['divisionbyzero'];  
        $bestFitLinear =  trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);  
        return $bestFitLinear->getStdevOfResiduals();  
     *    Returns covariance, the average of the products of deviations for each data point pair.  
     *    @param    array of mixed        Data Series Y  
     *    @param    array of mixed        Data Series X  
    public static function COVAR($yValues,$xValues) {  
        if (!self::_checkTrendArrays($yValues,$xValues)) {  
            return self::$_errorCodes['value'];  
        $yValueCount =  count($yValues);  
        $xValueCount =  count($xValues);  
        if (($yValueCount ==  0) ||  ($yValueCount !=  $xValueCount)) {  
            return self::$_errorCodes['na'];  
        } elseif ($yValueCount ==  1) {  
            return self::$_errorCodes['divisionbyzero'];  
        $bestFitLinear =  trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);  
        return $bestFitLinear->getCovariance();  
     *    Returns covariance, the average of the products of deviations for each data point pair.  
     *    @param    array of mixed        Data Series Y  
     *    @param    array of mixed        Data Series X  
    public static function CORREL($yValues,$xValues= null) {  
            return self::$_errorCodes['value'];  
        if (!self::_checkTrendArrays($yValues,$xValues)) {  
            return self::$_errorCodes['value'];  
        $yValueCount =  count($yValues);  
        $xValueCount =  count($xValues);  
        if (($yValueCount ==  0) ||  ($yValueCount !=  $xValueCount)) {  
            return self::$_errorCodes['na'];  
        } elseif ($yValueCount ==  1) {  
            return self::$_errorCodes['divisionbyzero'];  
        $bestFitLinear =  trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);  
        return $bestFitLinear->getCorrelation();  
     *    Calculates the statistics for a line by using the "least squares" method to calculate a straight line that best fits your data,  
     *        and then returns an array that describes the line.  
     *    @param    array of mixed        Data Series Y  
     *    @param    array of mixed        Data Series X  
     *    @param    boolean                A logical value specifying whether to force the intersect to equal 0.  
     *    @param    boolean                A logical value specifying whether to return additional regression statistics.  
    public static function LINEST($yValues,$xValues= null,$const= True,$stats= False) {  
        $const    =  (is_null($const))    ?  True :    (boolean)  self::flattenSingleValue($const);  
        $stats    =  (is_null($stats))    ?  False :    (boolean)  self::flattenSingleValue($stats);  
        if (!self::_checkTrendArrays($yValues,$xValues)) {  
            return self::$_errorCodes['value'];  
        $yValueCount =  count($yValues);  
        $xValueCount =  count($xValues);  
        if (($yValueCount ==  0) ||  ($yValueCount !=  $xValueCount)) {  
            return self::$_errorCodes['na'];  
        } elseif ($yValueCount ==  1) {  
        $bestFitLinear =  trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues,$const);  
            return array( array( $bestFitLinear->getSlope(),  
                                  $bestFitLinear->getSlopeSE(),  
                                  $bestFitLinear->getGoodnessOfFit(),  
                                  $bestFitLinear->getSSRegression(),  
                          array( $bestFitLinear->getIntersect(),  
                                 $bestFitLinear->getIntersectSE(),  
                                 $bestFitLinear->getStdevOfResiduals(),  
                                 $bestFitLinear->getDFResiduals(),  
                                 $bestFitLinear->getSSResiduals()  
            return array( $bestFitLinear->getSlope(),  
                          $bestFitLinear->getIntersect()  
     *    Calculates an exponential curve that best fits the X and Y data series,  
     *        and then returns an array that describes the line.  
     *    @param    array of mixed        Data Series Y  
     *    @param    array of mixed        Data Series X  
     *    @param    boolean                A logical value specifying whether to force the intersect to equal 0.  
     *    @param    boolean                A logical value specifying whether to return additional regression statistics.  
    public static function LOGEST($yValues,$xValues= null,$const= True,$stats= False) {  
        $const    =  (is_null($const))    ?  True :    (boolean)  self::flattenSingleValue($const);  
        $stats    =  (is_null($stats))    ?  False :    (boolean)  self::flattenSingleValue($stats);  
        if (!self::_checkTrendArrays($yValues,$xValues)) {  
            return self::$_errorCodes['value'];  
        $yValueCount =  count($yValues);  
        $xValueCount =  count($xValues);  
        foreach($yValues as $value) {  
                return self::$_errorCodes['num'];  
        if (($yValueCount ==  0) ||  ($yValueCount !=  $xValueCount)) {  
            return self::$_errorCodes['na'];  
        } elseif ($yValueCount ==  1) {  
        $bestFitExponential =  trendClass::calculate(trendClass::TREND_EXPONENTIAL,$yValues,$xValues,$const);  
            return array( array( $bestFitExponential->getSlope(),  
                                  $bestFitExponential->getSlopeSE(),  
                                  $bestFitExponential->getGoodnessOfFit(),  
                                  $bestFitExponential->getF(),  
                                  $bestFitExponential->getSSRegression(),  
                          array( $bestFitExponential->getIntersect(),  
                                 $bestFitExponential->getIntersectSE(),  
                                 $bestFitExponential->getStdevOfResiduals(),  
                                 $bestFitExponential->getDFResiduals(),  
                                 $bestFitExponential->getSSResiduals()  
            return array( $bestFitExponential->getSlope(),  
                          $bestFitExponential->getIntersect()  
     *    Calculates, or predicts, a future value by using existing values. The predicted value is a y-value for a given x-value.  
     *    @param    float                Value of X for which we want to find Y  
     *    @param    array of mixed        Data Series Y  
     *    @param    array of mixed        Data Series X  
    public static function FORECAST($xValue,$yValues,$xValues) {  
        $xValue    =  self::flattenSingleValue($xValue);  
            return self::$_errorCodes['value'];  
        if (!self::_checkTrendArrays($yValues,$xValues)) {  
            return self::$_errorCodes['value'];  
        $yValueCount =  count($yValues);  
        $xValueCount =  count($xValues);  
        if (($yValueCount ==  0) ||  ($yValueCount !=  $xValueCount)) {  
            return self::$_errorCodes['na'];  
        } elseif ($yValueCount ==  1) {  
            return self::$_errorCodes['divisionbyzero'];  
        $bestFitLinear =  trendClass::calculate(trendClass::TREND_LINEAR,$yValues,$xValues);  
        return $bestFitLinear->getValueOfYForX($xValue);  
     *    Returns values along a linear trend  
     *    @param    array of mixed        Data Series Y  
     *    @param    array of mixed        Data Series X  
     *    @param    array of mixed        Values of X for which we want to find Y  
     *    @param    boolean                A logical value specifying whether to force the intersect to equal 0.  
    public static function TREND($yValues,$xValues= array(),$newValues= array(),$const= True) {  
        $yValues =  self::flattenArray($yValues);  
        $xValues =  self::flattenArray($xValues);  
        $newValues =  self::flattenArray($newValues);  
        $const    =  (is_null($const))    ?  True :    (boolean)  self::flattenSingleValue($const);  
        if (count($newValues) ==  0) {  
            $newValues =  $bestFitLinear->getXValues();  
        foreach($newValues as $xValue) {  
            $returnArray[0][] =  $bestFitLinear->getValueOfYForX($xValue);  
     *    Returns values along a predicted emponential trend  
     *    @param    array of mixed        Data Series Y  
     *    @param    array of mixed        Data Series X  
     *    @param    array of mixed        Values of X for which we want to find Y  
     *    @param    boolean                A logical value specifying whether to force the intersect to equal 0.  
    public static function GROWTH($yValues,$xValues= array(),$newValues= array(),$const= True) {  
        $yValues =  self::flattenArray($yValues);  
        $xValues =  self::flattenArray($xValues);  
        $newValues =  self::flattenArray($newValues);  
        $const    =  (is_null($const))    ?  True :    (boolean)  self::flattenSingleValue($const);  
        if (count($newValues) ==  0) {  
            $newValues =  $bestFitExponential->getXValues();  
        foreach($newValues as $xValue) {  
            $returnArray[0][] =  $bestFitExponential->getValueOfYForX($xValue);  
    private static function _romanCut($num, $n) {  
        return ($num -  ($num %  $n ) ) /  $n;  
    }    //    function _romanCut()  
    public static function ROMAN($aValue, $style= 0) {  
        $aValue    = (integer)  self::flattenSingleValue($aValue);  
        $style    =  (is_null($style))    ?  0 :    (integer)  self::flattenSingleValue($style);  
        if ((!is_numeric($aValue)) ||  ($aValue <  0) ||  ($aValue >=  4000)) {  
            return self::$_errorCodes['value'];  
        $mill =  Array('', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM');  
        $cent =  Array('', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM');  
        $tens =  Array('', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC');  
        $ones =  Array('', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX');  
        $m =  self::_romanCut($aValue, 1000);    $aValue %=  1000;  
        $c =  self::_romanCut($aValue, 100);        $aValue %=  100;  
        $t =  self::_romanCut($aValue, 10);        $aValue %=  10;  
        return $roman. $mill[$m]. $cent[$c]. $tens[$t]. $ones[$aValue];  
     *    Returns a subtotal in a list or database.  
     *    @param    int        the number 1 to 11 that specifies which function to  
     *                     use in calculating subtotals within a list.  
     *    @param    array of mixed        Data Series  
                    return self::AVERAGE($aArgs);  
                    return self::COUNT($aArgs);  
                    return self::COUNTA($aArgs);  
                    return self::MAX($aArgs);  
                    return self::MIN($aArgs);  
                    return self::PRODUCT($aArgs);  
                    return self::STDEV($aArgs);  
                    return self::STDEVP($aArgs);  
                    return self::SUM($aArgs);  
                    return self::VARFunc($aArgs);  
                    return self::VARP($aArgs);  
        return self::$_errorCodes['value'];  
     *    Returns the square root of (number * pi).  
     *    @param    float    $number        Number  
     *    @return    float    Square Root of Number * Pi  
    public static function SQRTPI($number) {  
        $number    =  self::flattenSingleValue($number);  
                return self::$_errorCodes['num'];  
            return sqrt($number *  M_PI) ;  
        return self::$_errorCodes['value'];  
     *    Returns the factorial of a number.  
     *    @param    float    $factVal    Factorial Value  
    public static function FACT($factVal) {  
        $factVal    =  self::flattenSingleValue($factVal);  
                return self::$_errorCodes['num'];  
            $factLoop =  floor($factVal);  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC) {  
                if ($factVal >  $factLoop) {  
                    return self::$_errorCodes['num'];  
                $factorial *=  $factLoop-- ;  
        return self::$_errorCodes['value'];  
     *    Returns the double factorial of a number.  
     *    @param    float    $factVal    Factorial Value  
     *    @return    int        Double Factorial  
        $factLoop    =  floor(self::flattenSingleValue($factVal));  
                return self::$_errorCodes['num'];  
                $factorial *=  $factLoop-- ;  
        return self::$_errorCodes['value'];  
    }    //    function FACTDOUBLE()  
     *    Returns the ratio of the factorial of a sum of values to the product of factorials.  
     *    @param    array of mixed        Data Series  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
            // Is it a numeric value?  
                    return self::$_errorCodes['num'];  
                $divisor *=  self::FACT($arg);  
                return self::$_errorCodes['value'];  
            $summer =  self::FACT($summer);  
            return $summer /  $divisor;  
    }    //    function MULTINOMIAL()  
     *    Returns number rounded up, away from zero, to the nearest multiple of significance.  
     *    @param    float    $number            Number to round  
     *    @param    float    $significance    Significance  
     *    @return    float    Rounded Number  
    public static function CEILING($number,$significance= null) {  
        $number            =  self::flattenSingleValue($number);  
        $significance    =  self::flattenSingleValue($significance);  
        if ((is_null($significance)) &&  (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC)) {  
            $significance =  $number/ abs($number);  
            if (self::SIGN($number) ==  self::SIGN($significance)) {  
                if ($significance ==  0.0) {  
                return ceil($number /  $significance) *  $significance;  
                return self::$_errorCodes['num'];  
        return self::$_errorCodes['value'];  
     *    Returns number rounded up to the nearest even integer.  
     *    @param    float    $number            Number to round  
     *    @return    int        Rounded Number  
    public static function EVEN($number) {  
        $number    =  self::flattenSingleValue($number);  
            $significance =  2 *  self::SIGN($number);  
            return self::CEILING($number,$significance);  
        return self::$_errorCodes['value'];  
     *    Returns number rounded up to the nearest odd integer.  
     *    @param    float    $number            Number to round  
     *    @return    int        Rounded Number  
    public static function ODD($number) {  
        $number    =  self::flattenSingleValue($number);  
            $significance =  self::SIGN($number);  
            if ($significance ==  0) {  
            $result =  self::CEILING($number,$significance);  
            if (self::IS_EVEN($result)) {  
                $result +=  $significance;  
        return self::$_errorCodes['value'];  
     *    Casts a floating point value to an integer  
     *    @param    float    $number            Number to cast to an integer  
     *    @return    integer    Integer value  
    public static function INTVALUE($number) {  
        $number    =  self::flattenSingleValue($number);  
            return (int)  floor($number);  
        return self::$_errorCodes['value'];  
     *    Rounds a number up to a specified number of decimal places  
     *    @param    float    $number            Number to round  
     *    @param    int        $digits            Number of digits to which you want to round $number  
     *    @return    float    Rounded Number  
    public static function ROUNDUP($number,$digits) {  
        $number    =  self::flattenSingleValue($number);  
        $digits    =  self::flattenSingleValue($digits);  
            $significance =  pow(10,$digits);  
                return floor($number *  $significance) /  $significance;  
                return ceil($number *  $significance) /  $significance;  
        return self::$_errorCodes['value'];  
     *    Rounds a number down to a specified number of decimal places  
     *    @param    float    $number            Number to round  
     *    @param    int        $digits            Number of digits to which you want to round $number  
     *    @return    float    Rounded Number  
    public static function ROUNDDOWN($number,$digits) {  
        $number    =  self::flattenSingleValue($number);  
        $digits    =  self::flattenSingleValue($digits);  
            $significance =  pow(10,$digits);  
                return ceil($number *  $significance) /  $significance;  
                return floor($number *  $significance) /  $significance;  
        return self::$_errorCodes['value'];  
    }    //    function ROUNDDOWN()  
     *    Rounds a number to the nearest multiple of a specified value  
     *    @param    float    $number            Number to round  
     *    @param    int        $multiple        Multiple to which you want to round $number  
     *    @return    float    Rounded Number  
    public static function MROUND($number,$multiple) {  
        $number        =  self::flattenSingleValue($number);  
        $multiple    =  self::flattenSingleValue($multiple);  
            if ((self::SIGN($number)) ==  (self::SIGN($multiple))) {  
                $multiplier =  1 /  $multiple;  
                return round($number *  $multiplier) /  $multiplier;  
            return self::$_errorCodes['num'];  
        return self::$_errorCodes['value'];  
     *    Determines the sign of a number. Returns 1 if the number is positive, zero (0)  
     *    if the number is 0, and -1 if the number is negative.  
     *    @param    float    $number            Number to round  
    public static function SIGN($number) {  
        $number    =  self::flattenSingleValue($number);  
            return $number /  abs($number);  
        return self::$_errorCodes['value'];  
     *    Rounds number down, toward zero, to the nearest multiple of significance.  
     *    @param    float    $number            Number to round  
     *    @param    float    $significance    Significance  
     *    @return    float    Rounded Number  
    public static function FLOOR($number,$significance= null) {  
        $number            =  self::flattenSingleValue($number);  
        $significance    =  self::flattenSingleValue($significance);  
        if ((is_null($significance)) &&  (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC)) {  
            $significance =  $number/ abs($number);  
            if ((float)  $significance ==  0.0) {  
                return self::$_errorCodes['divisionbyzero'];  
            if (self::SIGN($number) ==  self::SIGN($significance)) {  
                return floor($number /  $significance) *  $significance;  
                return self::$_errorCodes['num'];  
        return self::$_errorCodes['value'];  
     *    Returns the number of permutations for a given number of objects that can be  
     *    selected from number objects. A permutation is any set or subset of objects or  
     *    events where internal order is significant. Permutations are different from  
     *    combinations, for which the internal order is not significant. Use this function  
     *    for lottery-style probability calculations.  
     *    @param    int        $numObjs    Number of different objects  
     *    @param    int        $numInSet    Number of objects in each permutation  
     *    @return    int        Number of permutations  
    public static function PERMUT($numObjs,$numInSet) {  
        $numObjs    =  self::flattenSingleValue($numObjs);  
        $numInSet    =  self::flattenSingleValue($numInSet);  
            $numInSet =  floor($numInSet);  
            if ($numObjs <  $numInSet) {  
                return self::$_errorCodes['num'];  
            return round(self::FACT($numObjs) /  self::FACT($numObjs -  $numInSet));  
        return self::$_errorCodes['value'];  
     *    Returns the number of combinations for a given number of items. Use COMBIN to  
     *    determine the total possible number of groups for a given number of items.  
     *    @param    int        $numObjs    Number of different objects  
     *    @param    int        $numInSet    Number of objects in each combination  
     *    @return    int        Number of combinations  
    public static function COMBIN($numObjs,$numInSet) {  
        $numObjs    =  self::flattenSingleValue($numObjs);  
        $numInSet    =  self::flattenSingleValue($numInSet);  
            if ($numObjs <  $numInSet) {  
                return self::$_errorCodes['num'];  
            } elseif ($numInSet <  0) {  
                return self::$_errorCodes['num'];  
            return round(self::FACT($numObjs) /  self::FACT($numObjs -  $numInSet)) /  self::FACT($numInSet);  
        return self::$_errorCodes['value'];  
     *    Returns the sum of a power series  
     *    @param    float            $x    Input value to the power series  
     *    @param    float            $n    Initial power to which you want to raise $x  
     *    @param    float            $m    Step by which to increase $n for each term in the series  
     *    @param    array of mixed        Data Series  
        // Loop through arguments  
            foreach($aArgs as $arg) {  
                // Is it a numeric value?  
                    $returnValue +=  $arg *  pow($x,$n +  ($m *  $i++ ));  
                    return self::$_errorCodes['value'];  
        return self::$_errorCodes['value'];  
    }    //    function SERIESSUM()  
     *    Returns a normalized value from a distribution characterized by mean and standard_dev.  
     *    @param    float    $value        Value to normalize  
     *    @param    float    $mean        Mean Value  
     *    @param    float    $stdDev        Standard Deviation  
     *    @return    float    Standardized value  
    public static function STANDARDIZE($value,$mean,$stdDev) {  
        $value    =  self::flattenSingleValue($value);  
        $mean    =  self::flattenSingleValue($mean);  
        $stdDev    =  self::flattenSingleValue($stdDev);  
                return self::$_errorCodes['num'];  
            return ($value -  $mean) /  $stdDev ;  
        return self::$_errorCodes['value'];  
    }    //    function STANDARDIZE()  
    //    Private method to return an array of the factors of the input value  
    private static function _factors($value) {  
        $startVal =  floor(sqrt($value));  
        for ($i =  $startVal; $i >  1; -- $i) {  
            if (($value %  $i) ==  0) {  
                $factorArray =  array_merge($factorArray,self::_factors($value /  $i));  
                $factorArray =  array_merge($factorArray,self::_factors($i));  
                if ($i <=  sqrt($value)) {  
        if (count($factorArray) >  0) {  
            return array((integer)  $value);  
     *    Returns the lowest common multiplier of a series of numbers  
     *    @param    $array    Values to calculate the Lowest Common Multiplier  
     *    @return    int        Lowest Common Multiplier  
    public static function LCM() {  
        $allPoweredFactors =  array();  
        foreach($aArgs as $value) {  
                return self::$_errorCodes['value'];  
                return self::$_errorCodes['num'];  
            $myFactors =  self::_factors(floor($value));  
            $myPoweredFactors =  array();  
            foreach($myCountedFactors as $myCountedFactor =>  $myCountedPower) {  
                $myPoweredFactors[$myCountedFactor] =  pow($myCountedFactor,$myCountedPower);  
            foreach($myPoweredFactors as $myPoweredValue =>  $myPoweredFactor) {  
                    if ($allPoweredFactors[$myPoweredValue] <  $myPoweredFactor) {  
                        $allPoweredFactors[$myPoweredValue] =  $myPoweredFactor;  
                    $allPoweredFactors[$myPoweredValue] =  $myPoweredFactor;  
        foreach($allPoweredFactors as $allPoweredFactor) {  
            $returnValue *= (integer)  $allPoweredFactor;  
     *    Returns the greatest common divisor of a series of numbers  
     *    @param    $array    Values to calculate the Greatest Common Divisor  
     *    @return    int        Greatest Common Divisor  
    public static function GCD() {  
        $allPoweredFactors =  array();  
        foreach($aArgs as $value) {  
            $myFactors =  self::_factors($value);  
            $allValuesFactors[] =  $myCountedFactors;  
        $allValuesCount =  count($allValuesFactors);  
        $mergedArray =  $allValuesFactors[0];  
        for ($i= 1;$i <  $allValuesCount; ++ $i) {  
        $mergedArrayValues =  count($mergedArray);  
        if ($mergedArrayValues ==  0) {  
        } elseif ($mergedArrayValues >  1) {  
            foreach($mergedArray as $mergedKey =>  $mergedValue) {  
                foreach($allValuesFactors as $highestPowerTest) {  
                    foreach($highestPowerTest as $testKey =>  $testValue) {  
                        if (($testKey ==  $mergedKey) &&  ($testValue <  $mergedValue)) {  
                            $mergedArray[$mergedKey] =  $testValue;  
                            $mergedValue =  $testValue;  
            foreach($mergedArray as $key =>  $value) {  
                $returnValue *=  pow($key,$value);  
            $value =  $mergedArray[$key];  
            foreach($allValuesFactors as $testValue) {  
                foreach($testValue as $mergedKey =>  $mergedValue) {  
                    if (($mergedKey ==  $key) &&  ($mergedValue <  $value)) {  
     *    Returns the individual term binomial distribution probability. Use BINOMDIST in problems with  
     *    a fixed number of tests or trials, when the outcomes of any trial are only success or failure,  
     *    when trials are independent, and when the probability of success is constant throughout the  
     *    experiment. For example, BINOMDIST can calculate the probability that two of the next three  
     *    @param    float        $value            Number of successes in trials  
     *    @param    float        $trials            Number of trials  
     *    @param    float        $probability    Probability of success on each trial  
     *    @param    boolean        $cumulative   
     *    @todo    Cumulative distribution function  
    public static function BINOMDIST($value, $trials, $probability, $cumulative) {  
        $value            =  floor(self::flattenSingleValue($value));  
        $trials            =  floor(self::flattenSingleValue($trials));  
        $probability    =  self::flattenSingleValue($probability);  
            if (($value <  0) ||  ($value >  $trials)) {  
                return self::$_errorCodes['num'];  
            if (($probability <  0) ||  ($probability >  1)) {  
                return self::$_errorCodes['num'];  
            if ((is_numeric($cumulative)) ||  (is_bool($cumulative))) {  
                    for ($i =  0; $i <=  $value; ++ $i) {  
                        $summer +=  self::COMBIN($trials,$i) *  pow($probability,$i) *  pow(1 -  $probability,$trials -  $i);  
                    return self::COMBIN($trials,$value) *  pow($probability,$value) *  pow(1 -  $probability,$trials -  $value) ;  
        return self::$_errorCodes['value'];  
    }    //    function BINOMDIST()  
     *    Returns the negative binomial distribution. NEGBINOMDIST returns the probability that  
     *    there will be number_f failures before the number_s-th success, when the constant  
     *    probability of a success is probability_s. This function is similar to the binomial  
     *    distribution, except that the number of successes is fixed, and the number of trials is  
     *    variable. Like the binomial, trials are assumed to be independent.  
     *    @param    float        $failures        Number of Failures  
     *    @param    float        $successes        Threshold number of Successes  
     *    @param    float        $probability    Probability of success on each trial  
    public static function NEGBINOMDIST($failures, $successes, $probability) {  
        $failures        =  floor(self::flattenSingleValue($failures));  
        $successes        =  floor(self::flattenSingleValue($successes));  
        $probability    =  self::flattenSingleValue($probability);  
            if (($failures <  0) ||  ($successes <  1)) {  
                return self::$_errorCodes['num'];  
            if (($probability <  0) ||  ($probability >  1)) {  
                return self::$_errorCodes['num'];  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC) {  
                if (($failures +  $successes -  1) <=  0) {  
                    return self::$_errorCodes['num'];  
            return (self::COMBIN($failures +  $successes -  1,$successes -  1)) *  (pow($probability,$successes)) *  (pow(1 -  $probability,$failures)) ;  
        return self::$_errorCodes['value'];  
    }    //    function NEGBINOMDIST()  
     *    Returns the smallest value for which the cumulative binomial distribution is greater  
     *    than or equal to a criterion value  
     *    See http://support.microsoft.com/kb/828117/ for details of the algorithm used  
     *    @param    float        $trials            number of Bernoulli trials  
     *    @param    float        $probability    probability of a success on each trial  
     *    @param    float        $alpha            criterion value  
     *    @todo    Warning. This implementation differs from the algorithm detailed on the MS  
     *             web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess  
     *             This eliminates a potential endless loop error, but may have an adverse affect on the  
     *             accuracy of the function (although all my tests have so far returned correct results).  
    public static function CRITBINOM($trials, $probability, $alpha) {  
        $trials            =  floor(self::flattenSingleValue($trials));  
        $probability    =  self::flattenSingleValue($probability);  
        $alpha            =  self::flattenSingleValue($alpha);  
                return self::$_errorCodes['num'];  
            if (($probability <  0) ||  ($probability >  1)) {  
                return self::$_errorCodes['num'];  
            if (($alpha <  0) ||  ($alpha >  1)) {  
                return self::$_errorCodes['num'];  
                $t =  sqrt(log(1 /  ($alpha *  $alpha)));  
                $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));  
                $trialsApprox =  $t -  (2.515517 +  0.802853 *  $t +  0.010328 *  $t *  $t) /  (1 +  1.432788 *  $t +  0.189269 *  $t *  $t +  0.001308 *  $t *  $t *  $t);  
            $Guess =  floor($trials *  $probability +  $trialsApprox *  sqrt($trials *  $probability *  (1 -  $probability)));  
            } elseif ($Guess >  $trials) {  
            $TotalUnscaledProbability =  $UnscaledPGuess =  $UnscaledCumPGuess =  0.0;  
            $EssentiallyZero =  10e-12;  
            $m =  floor($trials *  $probability);  
            ++ $TotalUnscaledProbability; 
            if ($m ==  $Guess) { ++ $UnscaledPGuess; }  
            if ($m <=  $Guess) { ++ $UnscaledCumPGuess; }  
            while ((!$Done) &&  ($k <=  $trials)) {  
                $CurrentValue =  $PreviousValue *  ($trials -  $k +  1) *  $probability /  ($k *  (1 -  $probability));  
                $TotalUnscaledProbability +=  $CurrentValue;  
                if ($k ==  $Guess) { $UnscaledPGuess +=  $CurrentValue; }  
                if ($k <=  $Guess) { $UnscaledCumPGuess +=  $CurrentValue; }  
                if ($CurrentValue <=  $EssentiallyZero) { $Done =  True; }  
                $PreviousValue =  $CurrentValue;  
            while ((!$Done) &&  ($k >=  0)) {  
                $CurrentValue =  $PreviousValue *  $k +  1 *  (1 -  $probability) /  (($trials -  $k) *  $probability);  
                $TotalUnscaledProbability +=  $CurrentValue;  
                if ($k ==  $Guess) { $UnscaledPGuess +=  $CurrentValue; }  
                if ($k <=  $Guess) { $UnscaledCumPGuess +=  $CurrentValue; }  
                if ($CurrentValue <=  $EssentiallyZero) { $Done =  True; }  
                $PreviousValue =  $CurrentValue;  
            $PGuess =  $UnscaledPGuess /  $TotalUnscaledProbability;  
            $CumPGuess =  $UnscaledCumPGuess /  $TotalUnscaledProbability;  
//            $CumPGuessMinus1 = $CumPGuess - $PGuess;  
            $CumPGuessMinus1 =  $CumPGuess -  1;  
                if (($CumPGuessMinus1 <  $alpha) &&  ($CumPGuess >=  $alpha)) {  
                } elseif (($CumPGuessMinus1 <  $alpha) &&  ($CumPGuess <  $alpha)) {  
                    $PGuessPlus1 =  $PGuess *  ($trials -  $Guess) *  $probability /  $Guess /  (1 -  $probability);  
                    $CumPGuessMinus1 =  $CumPGuess;  
                    $CumPGuess =  $CumPGuess +  $PGuessPlus1;  
                } elseif (($CumPGuessMinus1 >=  $alpha) &&  ($CumPGuess >=  $alpha)) {  
                    $PGuessMinus1 =  $PGuess *  $Guess *  (1 -  $probability) /  ($trials -  $Guess +  1) /  $probability;  
                    $CumPGuess =  $CumPGuessMinus1;  
                    $CumPGuessMinus1 =  $CumPGuessMinus1 -  $PGuess;  
        return self::$_errorCodes['value'];  
    }    //    function CRITBINOM()  
     *    Returns the one-tailed probability of the chi-squared distribution.  
     *    @param    float        $value            Value for the function  
     *    @param    float        $degrees        degrees of freedom  
    public static function CHIDIST($value, $degrees) {  
        $value        =  self::flattenSingleValue($value);  
        $degrees    =  floor(self::flattenSingleValue($degrees));  
                return self::$_errorCodes['num'];  
                if (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC) {  
                return self::$_errorCodes['num'];  
            return 1 -  (self::_incompleteGamma($degrees/ 2,$value/ 2) /  self::_gamma($degrees/ 2));  
        return self::$_errorCodes['value'];  
     *    Returns the one-tailed probability of the chi-squared distribution.  
     *    @param    float        $probability    Probability for the function  
     *    @param    float        $degrees        degrees of freedom  
    public static function CHIINV($probability, $degrees) {  
        $probability    =  self::flattenSingleValue($probability);  
        $degrees        =  floor(self::flattenSingleValue($degrees));  
                // Apply Newton-Raphson step  
                $result =  self::CHIDIST($x, $degrees);  
                $error =  $result -  $probability;  
                } elseif ($error <  0.0) {  
                // Avoid division by zero  
                // If the NR fails to converge (which for example may be the  
                // case if the initial guess is too rough) we apply a bisection  
                // step to determine a more narrow interval around the root.  
                if (($xNew <  $xLo) ||  ($xNew >  $xHi) ||  ($result ==  0.0)) {  
                    $xNew =  ($xLo +  $xHi) /  2;  
                return self::$_errorCodes['na'];  
        return self::$_errorCodes['value'];  
     *    Returns the exponential distribution. Use EXPONDIST to model the time between events,  
     *    such as how long an automated bank teller takes to deliver cash. For example, you can  
     *    use EXPONDIST to determine the probability that the process takes at most 1 minute.  
     *    @param    float        $value            Value of the function  
     *    @param    float        $lambda            The parameter value  
     *    @param    boolean        $cumulative   
    public static function EXPONDIST($value, $lambda, $cumulative) {  
        $value    =  self::flattenSingleValue($value);  
        $lambda    =  self::flattenSingleValue($lambda);  
        $cumulative    =  self::flattenSingleValue($cumulative);  
            if (($value <  0) ||  ($lambda <  0)) {  
                return self::$_errorCodes['num'];  
            if ((is_numeric($cumulative)) ||  (is_bool($cumulative))) {  
                    return 1 -  exp(0- $value* $lambda);  
                    return $lambda *  exp(0- $value* $lambda);  
        return self::$_errorCodes['value'];  
    }    //    function EXPONDIST()  
     *    Returns the Fisher transformation at x. This transformation produces a function that  
     *    is normally distributed rather than skewed. Use this function to perform hypothesis  
     *    testing on the correlation coefficient.  
    public static function FISHER($value) {  
        $value    =  self::flattenSingleValue($value);  
            if (($value <= - 1) ||  ($value >=  1)) {  
                return self::$_errorCodes['num'];  
            return 0.5 *  log((1+ $value)/ (1- $value));  
        return self::$_errorCodes['value'];  
     *    Returns the inverse of the Fisher transformation. Use this transformation when  
     *    analyzing correlations between ranges or arrays of data. If y = FISHER(x), then  
        $value    =  self::flattenSingleValue($value);  
            return (exp(2 *  $value) -  1) /  (exp(2 *  $value) +  1);  
        return self::$_errorCodes['value'];  
    }    //    function FISHERINV()  
    // Function cache for _logBeta function  
    private static $_logBetaCache_p            =  0.0;  
    private static $_logBetaCache_q            =  0.0;  
    private static $_logBetaCache_result    =  0.0;  
     *    The natural logarithm of the beta function.  
     *    @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow  
     *    @author Jaco van Kooten  
    private static function _logBeta($p, $q) {  
        if ($p !=  self::$_logBetaCache_p ||  $q !=  self::$_logBetaCache_q) {  
            self::$_logBetaCache_p =  $p;  
            self::$_logBetaCache_q =  $q;  
            if (($p <=  0.0) ||  ($q <=  0.0) ||  (($p +  $q) >  LOG_GAMMA_X_MAX_VALUE)) {  
                self::$_logBetaCache_result =  0.0;  
                self::$_logBetaCache_result =  self::_logGamma($p) +  self::_logGamma($q) -  self::_logGamma($p +  $q);  
        return self::$_logBetaCache_result;  
     *    Evaluates of continued fraction part of incomplete beta function.  
     *    Based on an idea from Numerical Recipes (W.H. Press et al, 1992).  
     *    @author Jaco van Kooten  
    private static function _betaFraction($x, $p, $q) {  
        $h =  1.0 -  $sum_pq *  $x /  $p_plus;  
            $d =  $m *  ($q -  $m) *  $x /  ( ($p_minus +  $m2) *  ($p +  $m2));  
            $d = - ($p +  $m) *  ($sum_pq +  $m) *  $x /  (($p +  $m2) *  ($p_plus +  $m2));  
    }    //    function _betaFraction()  
     * @author Jaco van Kooten  
     *  Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.  
     *  The natural logarithm of the gamma function. <br />  
     *  Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz <br />  
     *  Applied Mathematics Division <br />  
     *  Argonne National Laboratory <br />  
     *  Argonne, IL 60439 <br />  
     *  <li>W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural  
     *      Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.</li>  
     *  <li>K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.</li>  
     *  <li>Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.</li>  
     *  From the original documentation:  
     *  This routine calculates the LOG(GAMMA) function for a positive real argument X.  
     *  Computation is based on an algorithm outlined in references 1 and 2.  
     *  The program uses rational functions that theoretically approximate LOG(GAMMA)  
     *  to at least 18 significant decimal digits. The approximation for X > 12 is from  
     *  reference 3, while approximations for X < 12.0 are similar to those in reference  
     *  1, but are unpublished. The accuracy achieved depends on the arithmetic system,  
     *  the compiler, the intrinsic functions, and proper selection of the  
     *  machine-dependent constants.  
     *  The program returns the value XINF for X .LE. 0.0 or when overflow would occur.  
     *  The computation is believed to be free of underflow and overflow.  
     * @return MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305  
    // Function cache for logGamma  
    private static $_logGammaCache_result    =  0.0;  
    private static $_logGammaCache_x        =  0.0;  
    private static function _logGamma($x) {  
        // Log Gamma related constants  
        static $lg_d1 = - 0.5772156649015328605195174;  
        static $lg_d2 =  0.4227843350984671393993777;  
        static $lg_d4 =  1.791759469228055000094023;  
        static $lg_p1 =  array(    4.945235359296727046734888,  
                                201.8112620856775083915565,  
                                2290.838373831346393026739,  
                                11319.67205903380828685045,  
                                28557.24635671635335736389,  
                                38484.96228443793359990269,  
                                26377.48787624195437963534,  
                                7225.813979700288197698961 );  
        static $lg_p2 =  array(    4.974607845568932035012064,  
                                542.4138599891070494101986,  
                                15506.93864978364947665077,  
                                184793.2904445632425417223,  
                                1088204.76946882876749847,  
                                3338152.967987029735917223,  
                                5106661.678927352456275255,  
                                3074109.054850539556250927 );  
        static $lg_p4 =  array(    14745.02166059939948905062,  
                                2426813.369486704502836312,  
                                121475557.4045093227939592,  
                                2663432449.630976949898078,  
                                29403789566.34553899906876,  
                                170266573776.5398868392998,  
                                492612579337.743088758812,  
                                560625185622.3951465078242 );  
        static $lg_q1 =  array(    67.48212550303777196073036,  
                                1113.332393857199323513008,  
                                7738.757056935398733233834,  
                                27639.87074403340708898585,  
                                54993.10206226157329794414,  
                                61611.22180066002127833352,  
                                36351.27591501940507276287,  
                                8785.536302431013170870835 );  
        static $lg_q2 =  array(    183.0328399370592604055942,  
                                7765.049321445005871323047,  
                                133190.3827966074194402448,  
                                1136705.821321969608938755,  
                                5267964.117437946917577538,  
                                13467014.54311101692290052,  
                                17827365.30353274213975932,  
                                9533095.591844353613395747 );  
        static $lg_q4 =  array(    2690.530175870899333379843,  
                                639388.5654300092398984238,  
                                41355999.30241388052042842,  
                                1120872109.61614794137657,  
                                14886137286.78813811542398,  
                                101680358627.2438228077304,  
                                341747634550.7377132798597,  
                                446315818741.9713286462081 );  
        static $lg_c  =  array(    - 0.001910444077728,  
                                - 0.002777777777777681622553, 
                                0.08333333333333333331554247,  
    // Rough estimate of the fourth root of logGamma_xBig  
    static $lg_frtbig =  2.25e76;  
    static $pnt68     =  0.6796875;  
    if ($x ==  self::$_logGammaCache_x) {  
        return self::$_logGammaCache_result;  
    if ($y >  0.0 &&  $y <=  LOG_GAMMA_X_MAX_VALUE) {  
            if ($y <=  0.5 ||  $y >=  $pnt68) {  
                for ($i =  0; $i <  8; ++ $i) {  
                    $xnum =  $xnum *  $xm1 +  $lg_p1[$i];  
                    $xden =  $xden *  $xm1 +  $lg_q1[$i];  
                $res =  $corr +  $xm1 *  ($lg_d1 +  $xm1 *  ($xnum /  $xden));  
                for ($i =  0; $i <  8; ++ $i) {  
                    $xnum =  $xnum *  $xm2 +  $lg_p2[$i];  
                    $xden =  $xden *  $xm2 +  $lg_q2[$i];  
                $res =  $corr +  $xm2 *  ($lg_d2 +  $xm2 *  ($xnum /  $xden));  
            for ($i =  0; $i <  8; ++ $i) {  
                $xnum =  $xnum *  $xm2 +  $lg_p2[$i];  
                $xden =  $xden *  $xm2 +  $lg_q2[$i];  
            $res =  $xm2 *  ($lg_d2 +  $xm2 *  ($xnum /  $xden));  
            // ----------------------  
            // ----------------------  
            for ($i =  0; $i <  8; ++ $i) {  
                $xnum =  $xnum *  $xm4 +  $lg_p4[$i];  
                $xden =  $xden *  $xm4 +  $lg_q4[$i];  
            $res =  $lg_d4 +  $xm4 *  ($xnum /  $xden);  
            // ---------------------------------  
            //    Evaluate for argument .GE. 12.0  
            // ---------------------------------  
                for ($i =  0; $i <  6; ++ $i)  
                    $res =  $res /  $ysq +  $lg_c[$i];  
                $res +=  $y *  ($corr -  1.0);  
            // --------------------------  
            //    Return for bad arguments  
            // --------------------------  
        // ------------------------------  
        //    Final adjustments and return  
        // ------------------------------  
        self::$_logGammaCache_x =  $x;  
        self::$_logGammaCache_result =  $res;  
    }    //    function _logGamma()  
     * @author Jaco van Kooten  
     * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow  
    private static function _beta($p, $q) {  
            return exp(self::_logBeta($p, $q));  
     * Incomplete beta function  
     * @author Jaco van Kooten  
     *  The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).  
     * @param x require 0<=x<=1  
     * @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  
    private static function _incompleteBeta($x, $p, $q) {  
        $beta_gam =  exp((0 -  self::_logBeta($p, $q)) +  $p *  log($x) +  $q *  log(1.0 -  $x));  
        if ($x <  ($p +  1.0) /  ($p +  $q +  2.0)) {  
            return $beta_gam *  self::_betaFraction($x, $p, $q) /  $p;  
            return 1.0 -  ($beta_gam *  self::_betaFraction(1 -  $x, $q, $p) /  $q);  
    }    //    function _incompleteBeta()  
     * Returns the beta distribution.  
     * @param    float        $value            Value at which you want to evaluate the distribution  
     * @param    float        $alpha            Parameter to the distribution  
     * @param    float        $beta            Parameter to the distribution  
     * @param    boolean        $cumulative   
    public static function BETADIST($value,$alpha,$beta,$rMin= 0,$rMax= 1) {  
        $value    =  self::flattenSingleValue($value);  
        $alpha    =  self::flattenSingleValue($alpha);  
        $beta    =  self::flattenSingleValue($beta);  
        $rMin    =  self::flattenSingleValue($rMin);  
        $rMax    =  self::flattenSingleValue($rMax);  
            if (($value <  $rMin) ||  ($value >  $rMax) ||  ($alpha <=  0) ||  ($beta <=  0) ||  ($rMin ==  $rMax)) {  
                return self::$_errorCodes['num'];  
            $value /=  ($rMax -  $rMin);  
            return self::_incompleteBeta($value,$alpha,$beta);  
        return self::$_errorCodes['value'];  
     * Returns the inverse of the beta distribution.  
     * @param    float        $probability    Probability at which you want to evaluate the distribution  
     * @param    float        $alpha            Parameter to the distribution  
     * @param    float        $beta            Parameter to the distribution  
     * @param    boolean        $cumulative   
    public static function BETAINV($probability,$alpha,$beta,$rMin= 0,$rMax= 1) {  
        $probability    =  self::flattenSingleValue($probability);  
        $alpha            =  self::flattenSingleValue($alpha);  
        $beta            =  self::flattenSingleValue($beta);  
        $rMin            =  self::flattenSingleValue($rMin);  
        $rMax            =  self::flattenSingleValue($rMax);  
            if (($alpha <=  0) ||  ($beta <=  0) ||  ($rMin ==  $rMax) ||  ($probability <=  0) ||  ($probability >  1)) {  
                return self::$_errorCodes['num'];  
                $result =  self::BETADIST($guess, $alpha, $beta);  
                if (($result ==  $probability) ||  ($result ==  0)) {  
                } elseif ($result >  $probability) {  
                return self::$_errorCodes['na'];  
            return round($rMin +  $guess *  ($rMax -  $rMin),12);  
        return self::$_errorCodes['value'];  
    //    Private implementation of the incomplete Gamma function  
    private static function _incompleteGamma($a,$x) {  
        for ($n= 0; $n<= $max; ++ $n) {  
            for ($i= 1; $i<= $n; ++ $i) {  
            $summer +=  (pow($x,$n) /  $divisor);  
        return pow($x,$a) *  exp(0- $x) *  $summer;  
    }    //    function _incompleteGamma()  
    //    Private implementation of the Gamma function  
    private static function _gamma($data) {  
        if ($data ==  0.0) return 0;  
        static $p0 =  1.000000000190015;  
        static $p =  array ( 1 =>  76.18009172947146,  
                            5 =>  1.208650973866179e-3,  
        $tmp -=  ($x +  0.5) *  log($tmp);  
            $summer +=  ($p[$j] / ++ $y);  
     * Returns the gamma distribution.  
     * @param    float        $value            Value at which you want to evaluate the distribution  
     * @param    float        $a                Parameter to the distribution  
     * @param    float        $b                Parameter to the distribution  
     * @param    boolean        $cumulative   
    public static function GAMMADIST($value,$a,$b,$cumulative) {  
        $value    =  self::flattenSingleValue($value);  
        $a        =  self::flattenSingleValue($a);  
        $b        =  self::flattenSingleValue($b);  
            if (($value <  0) ||  ($a <=  0) ||  ($b <=  0)) {  
                return self::$_errorCodes['num'];  
            if ((is_numeric($cumulative)) ||  (is_bool($cumulative))) {  
                    return self::_incompleteGamma($a,$value /  $b) /  self::_gamma($a);  
                    return (1 /  (pow($b,$a) *  self::_gamma($a))) *  pow($value,$a- 1) *  exp(0- ($value /  $b));  
        return self::$_errorCodes['value'];  
    }    //    function GAMMADIST()  
     * Returns the inverse of the beta distribution.  
     * @param    float        $probability    Probability at which you want to evaluate the distribution  
     * @param    float        $alpha            Parameter to the distribution  
     * @param    float        $beta            Parameter to the distribution  
    public static function GAMMAINV($probability,$alpha,$beta) {  
        $probability    =  self::flattenSingleValue($probability);  
        $alpha            =  self::flattenSingleValue($alpha);  
        $beta            =  self::flattenSingleValue($beta);  
            if (($alpha <=  0) ||  ($beta <=  0) ||  ($probability <  0) ||  ($probability >  1)) {  
                return self::$_errorCodes['num'];  
            $xHi =  $alpha *  $beta *  5;  
                // Apply Newton-Raphson step  
                $error =  self::GAMMADIST($x, $alpha, $beta, True) -  $probability;  
                $pdf =  self::GAMMADIST($x, $alpha, $beta, False);  
                // Avoid division by zero  
                // If the NR fails to converge (which for example may be the  
                // case if the initial guess is too rough) we apply a bisection  
                // step to determine a more narrow interval around the root.  
                if (($xNew <  $xLo) ||  ($xNew >  $xHi) ||  ($pdf ==  0.0)) {  
                    $xNew =  ($xLo +  $xHi) /  2;  
                return self::$_errorCodes['na'];  
        return self::$_errorCodes['value'];  
     * Returns the natural logarithm of the gamma function.  
    public static function GAMMALN($value) {  
        $value    =  self::flattenSingleValue($value);  
                return self::$_errorCodes['num'];  
            return log(self::_gamma($value));  
        return self::$_errorCodes['value'];  
     * Returns the normal distribution for the specified mean and standard deviation. This  
     * function has a very wide range of applications in statistics, including hypothesis  
     * @param    float        $mean        Mean Value  
     * @param    float        $stdDev        Standard Deviation  
     * @param    boolean        $cumulative   
    public static function NORMDIST($value, $mean, $stdDev, $cumulative) {  
        $value    =  self::flattenSingleValue($value);  
        $mean    =  self::flattenSingleValue($mean);  
        $stdDev    =  self::flattenSingleValue($stdDev);  
                return self::$_errorCodes['num'];  
            if ((is_numeric($cumulative)) ||  (is_bool($cumulative))) {  
                    return 0.5 *  (1 +  self::_erfVal(($value -  $mean) /  ($stdDev *  sqrt(2))));  
                    return (1 /  (SQRT2PI *  $stdDev)) *  exp(0 -  (pow($value -  $mean,2) /  (2 *  ($stdDev *  $stdDev))));  
        return self::$_errorCodes['value'];  
     * Returns the standard normal cumulative distribution function. The distribution has  
     * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a  
     * table of standard normal curve areas.  
    public static function NORMSDIST($value) {  
        $value    =  self::flattenSingleValue($value);  
        return self::NORMDIST($value, 0, 1, True);  
    }    //    function NORMSDIST()  
     * Returns the cumulative lognormal distribution of x, where ln(x) is normally distributed  
     * with parameters mean and standard_dev.  
    public static function LOGNORMDIST($value, $mean, $stdDev) {  
        $value    =  self::flattenSingleValue($value);  
        $mean    =  self::flattenSingleValue($mean);  
        $stdDev    =  self::flattenSingleValue($stdDev);  
            if (($value <=  0) ||  ($stdDev <=  0)) {  
                return self::$_errorCodes['num'];  
            return self::NORMSDIST((log($value) -  $mean) /  $stdDev);  
        return self::$_errorCodes['value'];  
    }    //    function LOGNORMDIST()  
    /***************************************************************************  
     *    begin                : Friday, January 16, 2004  
     *    copyright            : (C) 2004 Michael Nickerson  
     *    email                : nickersonm@yahoo.com  
     ***************************************************************************/  
    private static function _inverse_ncdf($p) {  
        //    Inverse ncdf approximation by Peter J. Acklam, implementation adapted to  
        //    PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as  
        //    a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html  
        //    I have not checked the accuracy of this implementation. Be aware that PHP  
        //    will truncate the coeficcients to 14 digits.  
        //    You have permission to use and distribute this function freely for  
        //    whatever purpose you want, but please show common courtesy and give credit  
        //    Input paramater is $p - probability - where 0 < p < 1.  
        //    Coefficients in rational approximations  
        static $a =  array(    1 => - 3.969683028665376e+01,  
                            2 =>  2.209460984245205e+02,  
                            3 => - 2.759285104469687e+02,  
                            4 =>  1.383577518672690e+02,  
                            5 => - 3.066479806614716e+01,  
                            6 =>  2.506628277459239e+00  
        static $b =  array(    1 => - 5.447609879822406e+01,  
                            2 =>  1.615858368580409e+02,  
                            3 => - 1.556989798598866e+02,  
                            4 =>  6.680131188771972e+01,  
                            5 => - 1.328068155288572e+01  
        static $c =  array(    1 => - 7.784894002430293e-03,  
                            2 => - 3.223964580411365e-01,  
                            3 => - 2.400758277161838e+00,  
                            4 => - 2.549732539343734e+00,  
                            5 =>  4.374664141464968e+00,  
                            6 =>  2.938163982698783e+00  
        static $d =  array(    1 =>  7.784695709041462e-03,  
                            2 =>  3.224671290700398e-01,  
                            3 =>  2.445134137142996e+00,  
                            4 =>  3.754408661907416e+00  
        //    Define lower and upper region break-points.  
        $p_low =  0.02425;            //Use lower region approx. below this  
        $p_high =  1 -  $p_low;        //Use upper region approx. above this  
        if (0 <  $p &&  $p <  $p_low) {  
            //    Rational approximation for lower region.  
            return ((((($c[1] *  $q +  $c[2]) *  $q +  $c[3]) *  $q +  $c[4]) *  $q +  $c[5]) *  $q +  $c[6]) /   
                    (((($d[1] *  $q +  $d[2]) *  $q +  $d[3]) *  $q +  $d[4]) *  $q +  1);  
        } elseif ($p_low <=  $p &&  $p <=  $p_high) {  
            //    Rational approximation for central region.  
            return ((((($a[1] *  $r +  $a[2]) *  $r +  $a[3]) *  $r +  $a[4]) *  $r +  $a[5]) *  $r +  $a[6]) *  $q /   
                   ((((($b[1] *  $r +  $b[2]) *  $r +  $b[3]) *  $r +  $b[4]) *  $r +  $b[5]) *  $r +  1);  
        } elseif ($p_high <  $p &&  $p <  1) {  
            //    Rational approximation for upper region.  
            return - ((((($c[1] *  $q +  $c[2]) *  $q +  $c[3]) *  $q +  $c[4]) *  $q +  $c[5]) *  $q +  $c[6]) /   
                     (((($d[1] *  $q +  $d[2]) *  $q +  $d[3]) *  $q +  $d[4]) *  $q +  1);  
        //    If 0 < p < 1, return a null value  
        return self::$_errorCodes['null'];  
    }    //    function _inverse_ncdf()  
    private static function _inverse_ncdf2($prob) {  
        //    Approximation of inverse standard normal CDF developed by  
        //    B. Moro, "The Full Monte," Risk 8(2), Feb 1995, 57-58.  
        $c4 =  2.76438810333863E-02;  
        $c5 =  3.8405729373609E-03;  
        $c6 =  3.951896511919E-04;  
            $z =  $y *  ((($a4 *  $z +  $a3) *  $z +  $a2) *  $z +  $a1) /  (((($b4 *  $z +  $b3) *  $z +  $b2) *  $z +  $b1) *  $z +  1);  
            $z =  $c1 +  $z *  ($c2 +  $z *  ($c3 +  $z *  ($c4 +  $z *  ($c5 +  $z *  ($c6 +  $z *  ($c7 +  $z *  ($c8 +  $z *  $c9)))))));  
    }    //    function _inverse_ncdf2()  
    private static function _inverse_ncdf3($p) {  
        //    ALGORITHM AS241 APPL. STATIST. (1988) VOL. 37, NO. 3.  
        //    Produces the normal deviate Z corresponding to a given lower  
        //    tail area of P; Z is accurate to about 1 part in 10**16.  
        //    This is a PHP version of the original FORTRAN code that can  
        //    be found at http://lib.stat.cmu.edu/apstat/  
        //    coefficients for p close to 0.5  
        $a0 =  3.3871328727963666080;  
        $a1 =  1.3314166789178437745E+2;  
        $a2 =  1.9715909503065514427E+3;  
        $a3 =  1.3731693765509461125E+4;  
        $a4 =  4.5921953931549871457E+4;  
        $a5 =  6.7265770927008700853E+4;  
        $a6 =  3.3430575583588128105E+4;  
        $a7 =  2.5090809287301226727E+3;  
        $b1 =  4.2313330701600911252E+1;  
        $b2 =  6.8718700749205790830E+2;  
        $b3 =  5.3941960214247511077E+3;  
        $b4 =  2.1213794301586595867E+4;  
        $b5 =  3.9307895800092710610E+4;  
        $b6 =  2.8729085735721942674E+4;  
        $b7 =  5.2264952788528545610E+3;  
        //    coefficients for p not close to 0, 0.5 or 1.  
        $c0 =  1.42343711074968357734;  
        $c1 =  4.63033784615654529590;  
        $c2 =  5.76949722146069140550;  
        $c3 =  3.64784832476320460504;  
        $c4 =  1.27045825245236838258;  
        $c5 =  2.41780725177450611770E-1;  
        $c6 =  2.27238449892691845833E-2;  
        $c7 =  7.74545014278341407640E-4;  
        $d1 =  2.05319162663775882187;  
        $d2 =  1.67638483018380384940;  
        $d3 =  6.89767334985100004550E-1;  
        $d4 =  1.48103976427480074590E-1;  
        $d5 =  1.51986665636164571966E-2;  
        $d6 =  5.47593808499534494600E-4;  
        $d7 =  1.05075007164441684324E-9;  
        //    coefficients for p near 0 or 1.  
        $e0 =  6.65790464350110377720;  
        $e1 =  5.46378491116411436990;  
        $e2 =  1.78482653991729133580;  
        $e3 =  2.96560571828504891230E-1;  
        $e4 =  2.65321895265761230930E-2;  
        $e5 =  1.24266094738807843860E-3;  
        $e6 =  2.71155556874348757815E-5;  
        $e7 =  2.01033439929228813265E-7;  
        $f1 =  5.99832206555887937690E-1;  
        $f2 =  1.36929880922735805310E-1;  
        $f3 =  1.48753612908506148525E-2;  
        $f4 =  7.86869131145613259100E-4;  
        $f5 =  1.84631831751005468180E-5;  
        $f6 =  1.42151175831644588870E-7;  
        $f7 =  2.04426310338993978564E-15;  
        //    computation for p close to 0.5  
            $z =  $q *  ((((((($a7 *  $R +  $a6) *  $R +  $a5) *  $R +  $a4) *  $R +  $a3) *  $R +  $a2) *  $R +  $a1) *  $R +  $a0) /   
                      ((((((($b7 *  $R +  $b6) *  $R +  $b5) *  $R +  $b4) *  $R +  $b3) *  $R +  $b2) *  $R +  $b1) *  $R +  1);  
            //    computation for p not close to 0, 0.5 or 1.  
                $z =  ((((((($c7 *  $R +  $c6) *  $R +  $c5) *  $R +  $c4) *  $R +  $c3) *  $R +  $c2) *  $R +  $c1) *  $R +  $c0) /   
                     ((((((($d7 *  $R +  $d6) *  $R +  $d5) *  $R +  $d4) *  $R +  $d3) *  $R +  $d2) *  $R +  $d1) *  $R +  1);  
            //    computation for p near 0 or 1.  
                $z =  ((((((($e7 *  $R +  $e6) *  $R +  $e5) *  $R +  $e4) *  $R +  $e3) *  $R +  $e2) *  $R +  $e1) *  $R +  $e0) /   
                     ((((((($f7 *  $R +  $f6) *  $R +  $f5) *  $R +  $f4) *  $R +  $f3) *  $R +  $f2) *  $R +  $f1) *  $R +  1);  
    }    //    function _inverse_ncdf3()  
     * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.  
     * @param    float        $mean        Mean Value  
     * @param    float        $stdDev        Standard Deviation  
    public static function NORMINV($probability,$mean,$stdDev) {  
        $probability    =  self::flattenSingleValue($probability);  
        $mean            =  self::flattenSingleValue($mean);  
        $stdDev            =  self::flattenSingleValue($stdDev);  
            if (($probability <  0) ||  ($probability >  1)) {  
                return self::$_errorCodes['num'];  
                return self::$_errorCodes['num'];  
            return (self::_inverse_ncdf($probability) *  $stdDev) +  $mean;  
        return self::$_errorCodes['value'];  
     * Returns the inverse of the standard normal cumulative distribution  
    public static function NORMSINV($value) {  
        return self::NORMINV($value, 0, 1);  
     * Returns the inverse of the normal cumulative distribution  
     * @todo    Try implementing P J Acklam's refinement algorithm for greater  
     *             accuracy if I can get my head round the mathematics  
     *             (as described at) http://home.online.no/~pjacklam/notes/invnorm/  
    public static function LOGINV($probability, $mean, $stdDev) {  
        $probability    =  self::flattenSingleValue($probability);  
        $mean            =  self::flattenSingleValue($mean);  
        $stdDev            =  self::flattenSingleValue($stdDev);  
            if (($probability <  0) ||  ($probability >  1) ||  ($stdDev <=  0)) {  
                return self::$_errorCodes['num'];  
            return exp($mean +  $stdDev *  self::NORMSINV($probability));  
        return self::$_errorCodes['value'];  
     * Returns the hypergeometric distribution. HYPGEOMDIST returns the probability of a given number of  
     * sample successes, given the sample size, population successes, and population size.  
     * @param    float        $sampleSuccesses        Number of successes in the sample  
     * @param    float        $sampleNumber            Size of the sample  
     * @param    float        $populationSuccesses    Number of successes in the population  
     * @param    float        $populationNumber        Population size  
    public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber) {  
        $sampleSuccesses        =  floor(self::flattenSingleValue($sampleSuccesses));  
        $sampleNumber            =  floor(self::flattenSingleValue($sampleNumber));  
        $populationSuccesses    =  floor(self::flattenSingleValue($populationSuccesses));  
        $populationNumber        =  floor(self::flattenSingleValue($populationNumber));  
            if (($sampleSuccesses <  0) ||  ($sampleSuccesses >  $sampleNumber) ||  ($sampleSuccesses >  $populationSuccesses)) {  
                return self::$_errorCodes['num'];  
            if (($sampleNumber <=  0) ||  ($sampleNumber >  $populationNumber)) {  
                return self::$_errorCodes['num'];  
            if (($populationSuccesses <=  0) ||  ($populationSuccesses >  $populationNumber)) {  
                return self::$_errorCodes['num'];  
            return self::COMBIN($populationSuccesses,$sampleSuccesses) *   
                   self::COMBIN($populationNumber -  $populationSuccesses,$sampleNumber -  $sampleSuccesses) /   
                   self::COMBIN($populationNumber,$sampleNumber);  
        return self::$_errorCodes['value'];  
    }    //    function HYPGEOMDIST()  
     * Returns the probability of Student's T distribution.  
     * @param    float        $value            Value for the function  
     * @param    float        $degrees        degrees of freedom  
     * @param    float        $tails            number of tails (1 or 2)  
    public static function TDIST($value, $degrees, $tails) {  
        $value        =  self::flattenSingleValue($value);  
        $degrees    =  floor(self::flattenSingleValue($degrees));  
        $tails        =  floor(self::flattenSingleValue($tails));  
            if (($value <  0) ||  ($degrees <  1) ||  ($tails <  1) ||  ($tails >  2)) {  
                return self::$_errorCodes['num'];  
            //    tdist, which finds the probability that corresponds to a given value  
            //    of t with k degrees of freedom. This algorithm is translated from a  
            //    pascal function on p81 of "Statistical Computing in Pascal" by D  
            //    Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:  
            //    London). The above Pascal algorithm is itself a translation of the  
            //    fortran algoritm "AS 3" by B E Cooper of the Atlas Computer  
            //    Laboratory as reported in (among other places) "Applied Statistics  
            //    Algorithms", editied by P Griffiths and I D Hill (1985; Ellis  
            //    Horwood Ltd.; W. Sussex, England).  
            $ttheta =  atan2($value,sqrt($tterm));  
            if (($degrees %  2) ==  1) {  
                $tterm *=  $tc *  $tc *  ($ti -  1) /  $ti;  
            if (($degrees %  2) ==  1) { $tsum =  M_2DIVPI *  ($tsum +  $ttheta); }  
            $tValue =  0.5 *  (1 +  $tsum);  
                return 1 -  abs((1 -  $tValue) -  $tValue);  
        return self::$_errorCodes['value'];  
     * Returns the one-tailed probability of the chi-squared distribution.  
     * @param    float        $probability    Probability for the function  
     * @param    float        $degrees        degrees of freedom  
    public static function TINV($probability, $degrees) {  
        $probability    =  self::flattenSingleValue($probability);  
        $degrees        =  floor(self::flattenSingleValue($degrees));  
                // Apply Newton-Raphson step  
                $result =  self::TDIST($x, $degrees, 2);  
                $error =  $result -  $probability;  
                } elseif ($error <  0.0) {  
                // Avoid division by zero  
                // If the NR fails to converge (which for example may be the  
                // case if the initial guess is too rough) we apply a bisection  
                // step to determine a more narrow interval around the root.  
                if (($xNew <  $xLo) ||  ($xNew >  $xHi) ||  ($result ==  0.0)) {  
                    $xNew =  ($xLo +  $xHi) /  2;  
                return self::$_errorCodes['na'];  
        return self::$_errorCodes['value'];  
     * Returns the confidence interval for a population mean  
     * @param    float        $stdDev        Standard Deviation  
    public static function CONFIDENCE($alpha,$stdDev,$size) {  
        $alpha    =  self::flattenSingleValue($alpha);  
        $stdDev    =  self::flattenSingleValue($stdDev);  
        $size    =  floor(self::flattenSingleValue($size));  
            if (($alpha <=  0) ||  ($alpha >=  1)) {  
                return self::$_errorCodes['num'];  
            if (($stdDev <=  0) ||  ($size <  1)) {  
                return self::$_errorCodes['num'];  
            return self::NORMSINV(1 -  $alpha /  2) *  $stdDev /  sqrt($size);  
        return self::$_errorCodes['value'];  
    }    //    function CONFIDENCE()  
     * Returns the Poisson distribution. A common application of the Poisson distribution  
     * is predicting the number of events over a specific time, such as the number of  
     * cars arriving at a toll plaza in 1 minute.  
     * @param    float        $mean        Mean Value  
     * @param    boolean        $cumulative   
    public static function POISSON($value, $mean, $cumulative) {  
        $value    =  self::flattenSingleValue($value);  
        $mean    =  self::flattenSingleValue($mean);  
            if (($value <=  0) ||  ($mean <=  0)) {  
                return self::$_errorCodes['num'];  
            if ((is_numeric($cumulative)) ||  (is_bool($cumulative))) {  
                    for ($i =  0; $i <=  floor($value); ++ $i) {  
                        $summer +=  pow($mean,$i) /  self::FACT($i);  
                    return exp(0- $mean) *  $summer;  
                    return (exp(0- $mean) *  pow($mean,$value)) /  self::FACT($value);  
        return self::$_errorCodes['value'];  
     * Returns the Weibull distribution. Use this distribution in reliability  
     * analysis, such as calculating a device's mean time to failure.  
     * @param    float        $alpha        Alpha Parameter  
     * @param    float        $beta        Beta Parameter  
     * @param    boolean        $cumulative   
    public static function WEIBULL($value, $alpha, $beta, $cumulative) {  
        $value    =  self::flattenSingleValue($value);  
        $alpha    =  self::flattenSingleValue($alpha);  
        $beta    =  self::flattenSingleValue($beta);  
            if (($value <  0) ||  ($alpha <=  0) ||  ($beta <=  0)) {  
                return self::$_errorCodes['num'];  
            if ((is_numeric($cumulative)) ||  (is_bool($cumulative))) {  
                    return 1 -  exp(0 -  pow($value /  $beta,$alpha));  
                    return ($alpha /  pow($beta,$alpha)) *  pow($value,$alpha -  1) *  exp(0 -  pow($value /  $beta,$alpha));  
        return self::$_errorCodes['value'];  
     * Returns the Weibull distribution. Use this distribution in reliability  
     * analysis, such as calculating a device's mean time to failure.  
     * @param    float        $alpha        Alpha Parameter  
     * @param    float        $beta        Beta Parameter  
     * @param    boolean        $cumulative   
    public static function ZTEST($dataSet, $m0, $sigma= null) {  
        $dataSet    =  self::flattenArrayIndexed($dataSet);  
        $m0            =  self::flattenSingleValue($m0);  
        $sigma        =  self::flattenSingleValue($sigma);  
            $sigma =  self::STDEV($dataSet);  
        return 1 -  self::NORMSDIST((self::AVERAGE($dataSet) -  $m0)/ ($sigma/ SQRT($n)));  
     * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry  
     * of a distribution around its mean. Positive skewness indicates a distribution with an  
     * asymmetric tail extending toward more positive values. Negative skewness indicates a  
     * distribution with an asymmetric tail extending toward more negative values.  
     * @param    array    Data Series  
    public static function SKEW() {  
        $mean =  self::AVERAGE($aArgs);  
        $stdDev =  self::STDEV($aArgs);  
        // Loop through arguments  
        foreach ($aArgs as $k =>  $arg) {  
                (!self::isMatrixValue($k))) {  
                // Is it a numeric value?  
                    $summer +=  pow((($arg -  $mean) /  $stdDev),3) ;  
            return $summer *  ($count /  (($count- 1) *  ($count- 2)));  
        return self::$_errorCodes['divisionbyzero'];  
     * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness  
     * or flatness of a distribution compared with the normal distribution. Positive  
     * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a  
     * relatively flat distribution.  
     * @param    array    Data Series  
    public static function KURT() {  
        $mean =  self::AVERAGE($aArgs);  
        $stdDev =  self::STDEV($aArgs);  
            // Loop through arguments  
            foreach ($aArgs as $k =>  $arg) {  
                    (!self::isMatrixValue($k))) {  
                    // Is it a numeric value?  
                        $summer +=  pow((($arg -  $mean) /  $stdDev),4) ;  
                return $summer *  ($count *  ($count+ 1) /  (($count- 1) *  ($count- 2) *  ($count- 3))) -  (3 *  pow($count- 1,2) /  (($count- 2) *  ($count- 3)));  
        return self::$_errorCodes['divisionbyzero'];  
     * @param    int        $min    Minimal value  
     * @param    int        $max    Maximal value  
     * @return    int        Random number  
    public static function RAND($min =  0, $max =  0) {  
        $min        =  self::flattenSingleValue($min);  
        $max        =  self::flattenSingleValue($max);  
        if ($min ==  0 &&  $max ==  0) {  
            return (rand(0,10000000)) /  10000000;  
    public static function MOD($a =  1, $b =  1) {  
        $a        =  self::flattenSingleValue($a);  
        $b        =  self::flattenSingleValue($b);  
            return self::$_errorCodes['divisionbyzero'];  
        } elseif (($a <  0.0) &&  ($b >  0.0)) {  
            return $b -  fmod(abs($a),$b);  
        } elseif (($a >  0.0) &&  ($b <  0.0)) {  
     * @param    string    $character    Value  
    public static function CHARACTER($character) {  
        $character    =  self::flattenSingleValue($character);  
        if ((!is_numeric($character)) ||  ($character <  0)) {  
            return self::$_errorCodes['value'];  
        if (function_exists('mb_convert_encoding')) {  
    private static function _uniord($c) {  
        if (ord($c{0}) >= 0 &&  ord($c{0}) <=  127)  
        if (ord($c{0}) >=  192 &&  ord($c{0}) <=  223)  
            return (ord($c{0})- 192)* 64 +  (ord($c{1})- 128);  
        if (ord($c{0}) >=  224 &&  ord($c{0}) <=  239)  
            return (ord($c{0})- 224)* 4096 +  (ord($c{1})- 128)* 64 +  (ord($c{2})- 128);  
        if (ord($c{0}) >=  240 &&  ord($c{0}) <=  247)  
            return (ord($c{0})- 240)* 262144 +  (ord($c{1})- 128)* 4096 +  (ord($c{2})- 128)* 64 +  (ord($c{3})- 128);  
        if (ord($c{0}) >=  248 &&  ord($c{0}) <=  251)  
            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);  
        if (ord($c{0}) >=  252 &&  ord($c{0}) <=  253)  
            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);  
        if (ord($c{0}) >=  254 &&  ord($c{0}) <=  255) //error  
            return self::$_errorCodes['value'];  
     * @param    string    $character    Value  
    public static function ASCIICODE($characters) {  
        $characters    =  self::flattenSingleValue($characters);  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE) {  
                $characters = (int)  $characters;  
        $character =  $characters;  
            if (mb_strlen($characters, 'UTF-8') >  1) { $character =  mb_substr($characters, 0, 1, 'UTF-8'); }  
            return self::_uniord($character);  
            if (strlen($characters) >  0) { $character =  substr($characters, 0, 1); }  
    }    //    function ASCIICODE()  
        // Loop through arguments  
        foreach ($aArgs as $arg) {  
                if (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE) {  
    }    //    function CONCATENATE()  
     * @param    string    $value    Value  
     * @param    int        $chars    Number of characters  
        $value        =  self::flattenSingleValue($value);  
            $value =  ($value) ?  'TRUE' :  'FALSE';  
    }    //    function STRINGLENGTH()  
     * @param    string    $needle        The string to look for  
     * @param    string    $haystack    The string in which to look  
     * @param    int        $offset        Offset within $haystack  
        $needle        =  self::flattenSingleValue($needle);  
        $haystack    =  self::flattenSingleValue($haystack);  
        $offset        =  self::flattenSingleValue($offset);  
                $haystack =  ($haystack) ?  'TRUE' :  'FALSE';  
            if (($offset >  0) &&  (strlen($haystack) >  $offset)) {  
                    $pos =  mb_strpos($haystack, $needle, -- $offset,'UTF-8');  
                    $pos =  strpos($haystack, $needle, -- $offset);  
        return self::$_errorCodes['value'];  
    }    //    function SEARCHSENSITIVE()  
     * @param    string    $needle        The string to look for  
     * @param    string    $haystack    The string in which to look  
     * @param    int        $offset        Offset within $haystack  
    public static function SEARCHINSENSITIVE($needle,$haystack,$offset= 1) {  
        $needle        =  self::flattenSingleValue($needle);  
        $haystack    =  self::flattenSingleValue($haystack);  
        $offset        =  self::flattenSingleValue($offset);  
                $haystack =  ($haystack) ?  'TRUE' :  'FALSE';  
            if (($offset >  0) &&  (strlen($haystack) >  $offset)) {  
                    $pos =  mb_stripos($haystack, $needle, -- $offset,'UTF-8');  
                    $pos =  stripos($haystack, $needle, -- $offset);  
        return self::$_errorCodes['value'];  
    }    //    function SEARCHINSENSITIVE()  
     * @param    string    $value    Value  
     * @param    int        $chars    Number of characters  
    public static function LEFT($value =  '', $chars =  1) {  
        $value        =  self::flattenSingleValue($value);  
        $chars        =  self::flattenSingleValue($chars);  
            return self::$_errorCodes['value'];  
            $value =  ($value) ?  'TRUE' :  'FALSE';  
            return mb_substr($value, 0, $chars, 'UTF-8');  
            return substr($value, 0, $chars);  
     *    @param    string    $value    Value  
     *    @param    int        $chars    Number of characters  
    public static function RIGHT($value =  '', $chars =  1) {  
        $value        =  self::flattenSingleValue($value);  
        $chars        =  self::flattenSingleValue($chars);  
            return self::$_errorCodes['value'];  
            $value =  ($value) ?  'TRUE' :  'FALSE';  
     *    @param    string    $value    Value  
     *    @param    int        $start    Start character  
     *    @param    int        $chars    Number of characters  
    public static function MID($value =  '', $start =  1, $chars =  null) {  
        $value        =  self::flattenSingleValue($value);  
        $start        =  self::flattenSingleValue($start);  
        $chars        =  self::flattenSingleValue($chars);  
        if (($start <  1) ||  ($chars <  0)) {  
            return self::$_errorCodes['value'];  
            $value =  ($value) ?  'TRUE' :  'FALSE';  
            return mb_substr($value, -- $start, $chars, 'UTF-8');  
            return substr($value, -- $start, $chars);  
     *    @param    string    $value    Value  
     *    @param    int        $start    Start character  
     *    @param    int        $chars    Number of characters  
    public static function REPLACE($oldText =  '', $start =  1, $chars =  null, $newText) {  
        $oldText    =  self::flattenSingleValue($oldText);  
        $start        =  self::flattenSingleValue($start);  
        $chars        =  self::flattenSingleValue($chars);  
        $newText    =  self::flattenSingleValue($newText);  
        $left =  self::LEFT($oldText,$start- 1);  
        $right =  self::RIGHT($oldText,self::STRINGLENGTH($oldText)- ($start+ $chars)+ 1);  
        return $left. $newText. $right;  
     *    @param    string    $text        Value  
     *    @param    string    $fromText    From Value  
     *    @param    string    $toText        To Value  
     *    @param    integer    $instance    Instance Number  
    public static function SUBSTITUTE($text =  '', $fromText =  '', $toText =  '', $instance =  0) {  
        $text        =  self::flattenSingleValue($text);  
        $fromText    =  self::flattenSingleValue($fromText);  
        $toText        =  self::flattenSingleValue($toText);  
        $instance    =  floor(self::flattenSingleValue($instance));  
                    $pos =  mb_strpos($text, $fromText, $pos+ 1, 'UTF-8');  
                    $pos =  strpos($text, $fromText, $pos+ 1);  
                    return self::REPLACE($text,++ $pos,mb_strlen($fromText, 'UTF-8'),$toText);  
                    return self::REPLACE($text,++ $pos,strlen($fromText),$toText);  
        return $left. $newText. $right;  
    }    //    function SUBSTITUTE()  
     *    @param    mixed    $value    Value to check  
        $testValue    =  self::flattenSingleValue($testValue);  
    }    //    function RETURNSTRING()  
     *    @param    mixed    $value    Value to check  
    public static function FIXEDFORMAT($value,$decimals= 2,$no_commas= false) {  
        $value        =  self::flattenSingleValue($value);  
        $decimals    =  self::flattenSingleValue($decimals);  
        $no_commas        =  self::flattenSingleValue($no_commas);  
        $valueResult =  round($value,$decimals);  
        if ($decimals <  0) { $decimals =  0; }  
        return (string)  $valueResult;  
    }    //    function FIXEDFORMAT()  
     *    @param    mixed    $value    Value to check  
    public static function TEXTFORMAT($value,$format) {  
        $value    =  self::flattenSingleValue($value);  
        $format    =  self::flattenSingleValue($format);  
            $value =  self::DATEVALUE($value);  
    }    //    function TEXTFORMAT()  
     *    @param    mixed    $value    Value to check  
    public static function TRIMSPACES($stringValue =  '') {  
        $stringValue    =  self::flattenSingleValue($stringValue);  
    }    //    function TRIMSPACES()  
    private static $_invalidChars =  Null;  
     *    @param    mixed    $value    Value to check  
        $stringValue    =  self::flattenSingleValue($stringValue);  
            $stringValue =  ($stringValue) ?  'TRUE' :  'FALSE';  
        if (self::$_invalidChars ==  Null) {  
            self::$_invalidChars =  range(chr(0),chr(31));  
            return str_replace(self::$_invalidChars,'',trim($stringValue,"\x00..\x1F"));  
    }    //    function TRIMNONPRINTABLE()  
     *    @param    mixed    $value    Value to check  
        $value    =  self::flattenSingleValue($value);  
        foreach(self::$_errorCodes as $errorCode) {  
            if ($value ==  $errorCode) {  
        return self::$_errorCodes['na'];  
    }    //    function ERROR_TYPE()  
     *    @param    mixed    $value    Value to check  
    public static function IS_BLANK($value= null) {  
            $value    =  self::flattenSingleValue($value);  
     *    @param    mixed    $value    Value to check  
    public static function IS_ERR($value =  '') {  
        $value        =  self::flattenSingleValue($value);  
        return self::IS_ERROR($value) &&  (!self::IS_NA($value));  
     *    @param    mixed    $value    Value to check  
    public static function IS_ERROR($value =  '') {  
        $value        =  self::flattenSingleValue($value);  
     *    @param    mixed    $value    Value to check  
    public static function IS_NA($value =  '') {  
        $value        =  self::flattenSingleValue($value);  
        return ($value ===  self::$_errorCodes['na']);  
     *    @param    mixed    $value    Value to check  
    public static function IS_EVEN($value =  0) {  
        $value        =  self::flattenSingleValue($value);  
            return self::$_errorCodes['value'];  
        return ($value %  2 ==  0);  
     *    @param    mixed    $value    Value to check  
    public static function IS_ODD($value =  null) {  
        $value        =  self::flattenSingleValue($value);  
            return self::$_errorCodes['value'];  
        return (abs($value) %  2 ==  1);  
     *    @param    mixed    $value        Value to check  
    public static function IS_NUMBER($value =  0) {  
        $value        =  self::flattenSingleValue($value);  
    }    //    function IS_NUMBER()  
     *    @param    mixed    $value        Value to check  
    public static function IS_LOGICAL($value =  true) {  
        $value        =  self::flattenSingleValue($value);  
    }    //    function IS_LOGICAL()  
     *    @param    mixed    $value        Value to check  
    public static function IS_TEXT($value =  '') {  
        $value        =  self::flattenSingleValue($value);  
     *    @param    mixed    $value        Value to check  
        return !self::IS_TEXT($value);  
    }    //    function IS_NONTEXT()  
     *    @return    string    Version information  
        return 'PHPExcel 1.7.3c, 2010-06-01';  
     * @return    mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,  
     *                         depending on the value of the ReturnDateType flag  
    public static function DATE($year =  0, $month =  1, $day =  1) {  
        $year    = (integer)  self::flattenSingleValue($year);  
        $month    = (integer)  self::flattenSingleValue($month);  
        $day    = (integer)  self::flattenSingleValue($day);  
        if ($year <  ($baseYear- 1900)) {  
            return self::$_errorCodes['num'];  
        if ((($baseYear- 1900) !=  0) &&  ($year <  $baseYear) &&  ($year >=  1900)) {  
            return self::$_errorCodes['num'];  
        if (($year <  $baseYear) &&  ($year >=  ($baseYear- 1900))) {  
            //    Handle year/month adjustment if month < 1  
            $year +=  ceil($month /  12) -  1;  
            $month =  13 -  abs($month %  12);  
            //    Handle year/month adjustment if month > 12  
            $year +=  floor($month /  12);  
        // Re-validate the year parameter after adjustments  
        if (($year <  $baseYear) ||  ($year >=  10000)) {  
            return self::$_errorCodes['num'];  
        $excelDateValue =  PHPExcel_Shared_Date::FormattedPHPToExcel($year, $month, $day);  
        switch (self::getReturnDateType()) {  
            case self::RETURNDATE_EXCEL            :  return (float)  $excelDateValue;  
     * @return    mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,  
     *                         depending on the value of the ReturnDateType flag  
    public static function TIME($hour =  0, $minute =  0, $second =  0) {  
        $hour    =  self::flattenSingleValue($hour);  
        $minute    =  self::flattenSingleValue($minute);  
        $second    =  self::flattenSingleValue($second);  
        if ($hour ==  '') { $hour =  0; }  
        if ($minute ==  '') { $minute =  0; }  
        if ($second ==  '') { $second =  0; }  
            return self::$_errorCodes['value'];  
        $minute    = (integer)  $minute;  
        $second    = (integer)  $second;  
            $minute +=  floor($second /  60);  
            $second =  60 -  abs($second %  60);  
            if ($second ==  60) { $second =  0; }  
        } elseif ($second >=  60) {  
            $minute +=  floor($second /  60);  
            $hour +=  floor($minute /  60);  
            $minute =  60 -  abs($minute %  60);  
            if ($minute ==  60) { $minute =  0; }  
        } elseif ($minute >=  60) {  
            $hour +=  floor($minute /  60);  
            return self::$_errorCodes['num'];  
        switch (self::getReturnDateType()) {  
            case self::RETURNDATE_EXCEL            :  $date =  0;  
            case self::RETURNDATE_PHP_OBJECT    :  $dayAdjust =  0;  
                                                     $dayAdjust =  floor($hour /  24);  
                                                     $hour =  24 -  abs($hour %  24);  
                                                     if ($hour ==  24) { $hour =  0; }  
                                                     $dayAdjust =  floor($hour /  24);  
                                                  $phpDateObject =  new DateTime('1900-01-01 '. $hour. ':'. $minute. ':'. $second);  
                                                     $phpDateObject->modify($dayAdjust. ' days');  
     * @param    string    $dateValue   
     * @return    mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,  
     *                         depending on the value of the ReturnDateType flag  
    public static function DATEVALUE($dateValue =  1) {  
        $dateValue =  trim(self::flattenSingleValue($dateValue),'"');  
        //    Strip any ordinals because they're allowed in Excel (English only)  
        $dateValue =  preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui','$1$3',$dateValue);  
        //    Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)  
        $dateValue    =  str_replace(array('/','.','-','  '),array(' ',' ',' ',' '),$dateValue);  
                    return self::$_errorCodes['value'];  
                    if ($t <  100) { $t +=  1900; }  
            //    We've been fed a time value without any date  
        } elseif (count($t1) ==  2) {  
            //    We only have two parts of the date: either day/month or month/year  
        if (($PHPDateArray ===  False) ||  ($PHPDateArray['error_count'] >  0)) {  
            $testVal1 =  strtok($dateValue,'- ');  
            if ($testVal1 !==  False) {  
                if ($testVal2 !==  False) {  
                    if ($testVal3 ===  False) {  
                    return self::$_errorCodes['value'];  
                return self::$_errorCodes['value'];  
            $PHPDateArray =  date_parse($testVal1. '-'. $testVal2. '-'. $testVal3);  
            if (($PHPDateArray ===  False) ||  ($PHPDateArray['error_count'] >  0)) {  
                $PHPDateArray =  date_parse($testVal2. '-'. $testVal1. '-'. $testVal3);  
                if (($PHPDateArray ===  False) ||  ($PHPDateArray['error_count'] >  0)) {  
                    return self::$_errorCodes['value'];  
        if (($PHPDateArray !==  False) &&  ($PHPDateArray['error_count'] ==  0)) {  
            if ($PHPDateArray['year'] ==  '')    { $PHPDateArray['year'] =  strftime('%Y'); }  
            if ($PHPDateArray['month'] ==  '')    { $PHPDateArray['month'] =  strftime('%m'); }  
            if ($PHPDateArray['day'] ==  '')        { $PHPDateArray['day'] =  strftime('%d'); }  
            switch (self::getReturnDateType()) {  
                case self::RETURNDATE_EXCEL            :  return (float)  $excelDateValue;  
                case self::RETURNDATE_PHP_OBJECT    :  return new DateTime($PHPDateArray['year']. '-'. $PHPDateArray['month']. '-'. $PHPDateArray['day']. ' 00:00:00');  
        return self::$_errorCodes['value'];  
    }    //    function DATEVALUE()  
     * @param    string    $dateValue   
     * @return    mixed    Excel date/time serial value, or string if error  
    private static function _getDateValue($dateValue) {  
            if ((is_string($dateValue)) &&  (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC)) {  
                return self::$_errorCodes['value'];  
                $dateValue =  PHPExcel_Shared_Date::PHPToExcel($dateValue);  
                $saveReturnDateType =  self::getReturnDateType();  
                self::setReturnDateType(self::RETURNDATE_EXCEL);  
                $dateValue =  self::DATEVALUE($dateValue);  
                self::setReturnDateType($saveReturnDateType);  
    }    //    function _getDateValue()  
     * @param    string    $timeValue   
     * @return    mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,  
     *                         depending on the value of the ReturnDateType flag  
    public static function TIMEVALUE($timeValue) {  
        $timeValue =  trim(self::flattenSingleValue($timeValue),'"');  
        $timeValue    =  str_replace(array('/','.'),array('-','-'),$timeValue);  
        if (($PHPDateArray !==  False) &&  ($PHPDateArray['error_count'] ==  0)) {  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE) {  
                $excelDateValue =  PHPExcel_Shared_Date::FormattedPHPToExcel($PHPDateArray['year'],$PHPDateArray['month'],$PHPDateArray['day'],$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']);  
            switch (self::getReturnDateType()) {  
                case self::RETURNDATE_EXCEL            :  return (float)  $excelDateValue;  
                case self::RETURNDATE_PHP_OBJECT    :  return new DateTime('1900-01-01 '. $PHPDateArray['hour']. ':'. $PHPDateArray['minute']. ':'. $PHPDateArray['second']);  
        return self::$_errorCodes['value'];  
    }    //    function TIMEVALUE()  
     * @param    string    $timeValue   
     * @return    mixed    Excel date/time serial value, or string if error  
    private static function _getTimeValue($timeValue) {  
        $saveReturnDateType =  self::getReturnDateType();  
        self::setReturnDateType(self::RETURNDATE_EXCEL);  
        $timeValue =  self::TIMEVALUE($timeValue);  
        self::setReturnDateType($saveReturnDateType);  
    }    //    function _getTimeValue()  
     * @return    mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,  
     *                         depending on the value of the ReturnDateType flag  
        switch (self::getReturnDateType()) {  
            case self::RETURNDATE_PHP_NUMERIC    :  $retValue = (integer)  time();  
            case self::RETURNDATE_PHP_OBJECT    :  $retValue =  new DateTime();  
    }    //    function DATETIMENOW()  
     * @return    mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,  
     *                         depending on the value of the ReturnDateType flag  
        switch (self::getReturnDateType()) {  
            case self::RETURNDATE_EXCEL            :  $retValue = (float)  $excelDateTime;  
    private static function _isLeapYear($year) {  
        return ((($year %  4) ==  0) &&  (($year %  100) !=  0) ||  (($year %  400) ==  0));  
    }    //    function _isLeapYear()  
    private static function _dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, $methodUS) {  
        } elseif ($methodUS &&  ($startMonth ==  2 &&  ($startDay ==  29 ||  ($startDay ==  28 &&  !self::_isLeapYear($startYear))))) {  
            if ($methodUS &&  $startDay !=  30) {  
        return $endDay +  $endMonth *  30 +  $endYear *  360 -  $startDay -  $startMonth *  30 -  $startYear *  360;  
    }    //    function _dateDiff360()  
     * @param    long    $startDate        Excel date serial value or a standard date string  
     * @param    long    $endDate        Excel date serial value or a standard date string  
     * @param    boolean    $method            US or European Method  
     * @return    long    PHP date/time serial  
    public static function DAYS360($startDate =  0, $endDate =  0, $method =  false) {  
        $startDate    =  self::flattenSingleValue($startDate);  
        $endDate    =  self::flattenSingleValue($endDate);  
        if (is_string($startDate =  self::_getDateValue($startDate))) {  
            return self::$_errorCodes['value'];  
        if (is_string($endDate =  self::_getDateValue($endDate))) {  
            return self::$_errorCodes['value'];  
        $PHPStartDateObject =  PHPExcel_Shared_Date::ExcelToPHPObject($startDate);  
        $startDay =  $PHPStartDateObject->format('j');  
        $startMonth =  $PHPStartDateObject->format('n');  
        $startYear =  $PHPStartDateObject->format('Y');  
        $endDay =  $PHPEndDateObject->format('j');  
        $endMonth =  $PHPEndDateObject->format('n');  
        $endYear =  $PHPEndDateObject->format('Y');  
        return self::_dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);  
     * @param    long    $startDate        Excel date serial value or a standard date string  
     * @param    long    $endDate        Excel date serial value or a standard date string  
     * @return    long    Interval between the dates  
    public static function DATEDIF($startDate =  0, $endDate =  0, $unit =  'D') {  
        $startDate    =  self::flattenSingleValue($startDate);  
        $endDate    =  self::flattenSingleValue($endDate);  
        $unit        =  strtoupper(self::flattenSingleValue($unit));  
        if (is_string($startDate =  self::_getDateValue($startDate))) {  
            return self::$_errorCodes['value'];  
        if (is_string($endDate =  self::_getDateValue($endDate))) {  
            return self::$_errorCodes['value'];  
        if ($startDate >=  $endDate) {  
            return self::$_errorCodes['num'];  
        $difference =  $endDate -  $startDate;  
        $PHPStartDateObject =  PHPExcel_Shared_Date::ExcelToPHPObject($startDate);  
        $startDays =  $PHPStartDateObject->format('j');  
        $startMonths =  $PHPStartDateObject->format('n');  
        $startYears =  $PHPStartDateObject->format('Y');  
        $endDays =  $PHPEndDateObject->format('j');  
        $endMonths =  $PHPEndDateObject->format('n');  
        $endYears =  $PHPEndDateObject->format('Y');  
        $retVal =  self::$_errorCodes['num'];  
                $retVal =  intval($difference);  
                $retVal =  intval($endMonths -  $startMonths) +  (intval($endYears -  $startYears) *  12);  
                //    We're only interested in full months  
                if ($endDays <  $startDays) {  
                $retVal =  intval($endYears -  $startYears);  
                //    We're only interested in full months  
                if ($endMonths <  $startMonths) {  
                } elseif (($endMonths ==  $startMonths) &&  ($endDays <  $startDays)) {  
                if ($endDays <  $startDays) {  
                    $PHPEndDateObject->modify('-'. $endDays. ' days');  
                    $adjustDays =  $PHPEndDateObject->format('j');  
                    if ($adjustDays >  $startDays) {  
                        $retVal +=  ($adjustDays -  $startDays);  
                    $retVal =  $endDays -  $startDays;  
                $retVal =  intval($endMonths -  $startMonths);  
                if ($retVal <  0) $retVal =  12 +  $retVal;  
                //    We're only interested in full months  
                if ($endDays <  $startDays) {  
                $retVal =  intval($difference);  
                if ($endYears >  $startYears) {  
                    while ($endYears >  $startYears) {  
                        $PHPEndDateObject->modify('-1 year');  
                        $endYears =  $PHPEndDateObject->format('Y');  
                    $retVal =  $PHPEndDateObject->format('z') -  $PHPStartDateObject->format('z');  
                    if ($retVal <  0) { $retVal +=  365; }  
     *    Calculates the fraction of the year represented by the number of whole days between two dates (the start_date and the  
     *    end_date). Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or obligations  
     *    to assign to a specific term.  
     *    @param    mixed    $startDate        Excel date serial value (float), PHP date timestamp (integer) or date object, or a standard date string  
     *    @param    mixed    $endDate        Excel date serial value (float), PHP date timestamp (integer) or date object, or a standard date string  
     *    @param    integer    $method            Method used for the calculation  
     *                                         0 or omitted    US (NASD) 30/360  
     *    @return    float    fraction of the year  
    public static function YEARFRAC($startDate =  0, $endDate =  0, $method =  0) {  
        $startDate    =  self::flattenSingleValue($startDate);  
        $endDate    =  self::flattenSingleValue($endDate);  
        $method        =  self::flattenSingleValue($method);  
        if (is_string($startDate =  self::_getDateValue($startDate))) {  
            return self::$_errorCodes['value'];  
        if (is_string($endDate =  self::_getDateValue($endDate))) {  
            return self::$_errorCodes['value'];  
        if (((is_numeric($method)) &&  (!is_string($method))) ||  ($method ==  '')) {  
                    return self::DAYS360($startDate,$endDate) /  360;  
                    $days =  self::DATEDIF($startDate,$endDate);  
                    $startYear =  self::YEAR($startDate);  
                    $endYear =  self::YEAR($endDate);  
                    $years =  $endYear -  $startYear +  1;  
                        if (self::_isLeapYear($endYear)) {  
                            $startMonth =  self::MONTHOFYEAR($startDate);  
                            $endMonth =  self::MONTHOFYEAR($endDate);  
                            $endDay =  self::DAYOFMONTH($endDate);  
                                (($endMonth *  100 +  $endDay) >=  (2 *  100 +  29))) {  
                        for($year =  $startYear; $year <=  $endYear; ++ $year) {  
                            if ($year ==  $startYear) {  
                                $startMonth =  self::MONTHOFYEAR($startDate);  
                                $startDay =  self::DAYOFMONTH($startDate);  
                                    $leapDays +=  (self::_isLeapYear($year)) ?  1 :  0;  
                            } elseif($year ==  $endYear) {  
                                $endMonth =  self::MONTHOFYEAR($endDate);  
                                $endDay =  self::DAYOFMONTH($endDate);  
                                if (($endMonth *  100 +  $endDay) >=  (2 *  100 +  29)) {  
                                    $leapDays +=  (self::_isLeapYear($year)) ?  1 :  0;  
                                $leapDays +=  (self::_isLeapYear($year)) ?  1 :  0;  
                            if (($leapDays ==  0) &&  (self::_isLeapYear($startYear)) &&  ($days >  365)) {  
                    return $days /  (365 +  $leapDays);  
                    return self::DATEDIF($startDate,$endDate) /  360;  
                    return self::DATEDIF($startDate,$endDate) /  365;  
                    return self::DAYS360($startDate,$endDate,True) /  360;  
        return self::$_errorCodes['value'];  
     * @param    mixed                Start date  
     * @param    array of mixed        Optional Date Series  
     * @return    long    Interval between the dates  
    public static function NETWORKDAYS($startDate,$endDate) {  
        //    Retrieve the mandatory start and end date that are referenced in the function definition  
        $startDate    =  self::flattenSingleValue($startDate);  
        $endDate    =  self::flattenSingleValue($endDate);  
        //    Flush the mandatory start and end date that are referenced in the function definition, and get the optional days  
        //    Validate the start and end dates  
        if (is_string($startDate =  $sDate =  self::_getDateValue($startDate))) {  
            return self::$_errorCodes['value'];  
        $startDate = (float)  floor($startDate);  
        if (is_string($endDate =  $eDate =  self::_getDateValue($endDate))) {  
            return self::$_errorCodes['value'];  
        $endDate = (float)  floor($endDate);  
        $startDoW =  6 -  self::DAYOFWEEK($startDate,2);  
        if ($startDoW <  0) { $startDoW =  0; }  
        $endDoW =  self::DAYOFWEEK($endDate,2);  
        if ($endDoW >=  6) { $endDoW =  0; }  
        $wholeWeekDays =  floor(($endDate -  $startDate) /  7) *  5;  
        $partWeekDays =  $endDoW +  $startDoW;  
        //    Test any extra holiday parameters  
        $holidayCountedArray =  array();  
        foreach ($dateArgs as $holidayDate) {  
            if (is_string($holidayDate =  self::_getDateValue($holidayDate))) {  
                return self::$_errorCodes['value'];  
            if (($holidayDate >=  $startDate) &&  ($holidayDate <=  $endDate)) {  
                if ((self::DAYOFWEEK($holidayDate,2) <  6) &&  (!in_array($holidayDate,$holidayCountedArray))) {  
                    $holidayCountedArray[] =  $holidayDate;  
            return 0 -  ($wholeWeekDays +  $partWeekDays);  
        return $wholeWeekDays +  $partWeekDays;  
    }    //    function NETWORKDAYS()  
     * @param    mixed                Start date  
     * @param    mixed                number of days for adjustment  
     * @param    array of mixed        Optional Date Series  
     * @return    long    Interval between the dates  
    public static function WORKDAY($startDate,$endDays) {  
        //    Retrieve the mandatory start date and days that are referenced in the function definition  
        $startDate    =  self::flattenSingleValue($startDate);  
        $endDays    = (int)  self::flattenSingleValue($endDays);  
        //    Flush the mandatory start date and days that are referenced in the function definition, and get the optional days  
            return self::$_errorCodes['value'];  
        $startDate = (float)  floor($startDate);  
        //    If endDays is 0, we always return startDate  
        if ($endDays ==  0) { return $startDate; }  
        $decrementing =  ($endDays <  0) ?  True :  False;  
        //    Adjust the start date if it falls over a weekend  
        $startDoW =  self::DAYOFWEEK($startDate,3);  
        if (self::DAYOFWEEK($startDate,3) >=  5) {  
            $startDate +=  ($decrementing) ? - $startDoW +  4:  7 -  $startDoW;  
            ($decrementing) ?  $endDays++ :  $endDays-- ;  
        $endDate = (float)  $startDate +  (intval($endDays /  5) *  7) +  ($endDays %  5);  
        //    Adjust the calculated end date if it falls over a weekend  
        $endDoW =  self::DAYOFWEEK($endDate,3);  
            $endDate +=  ($decrementing) ? - $endDoW +  4:  7 -  $endDoW;  
        //    Test any extra holiday parameters  
        if (count($dateArgs) >  0) {  
            $holidayCountedArray =  $holidayDates =  array();  
            foreach ($dateArgs as $holidayDate) {  
                if ((!is_null($holidayDate)) &&  (trim($holidayDate) >  '')) {  
                    if (is_string($holidayDate =  self::_getDateValue($holidayDate))) {  
                        return self::$_errorCodes['value'];  
                    if (self::DAYOFWEEK($holidayDate,3) <  5) {  
                        $holidayDates[] =  $holidayDate;  
                rsort($holidayDates, SORT_NUMERIC);  
                sort($holidayDates, SORT_NUMERIC);  
            foreach ($holidayDates as $holidayDate) {  
                    if (($holidayDate <=  $startDate) &&  ($holidayDate >=  $endDate)) {  
                        if (!in_array($holidayDate,$holidayCountedArray)) {  
                            $holidayCountedArray[] =  $holidayDate;  
                    if (($holidayDate >=  $startDate) &&  ($holidayDate <=  $endDate)) {  
                        if (!in_array($holidayDate,$holidayCountedArray)) {  
                            $holidayCountedArray[] =  $holidayDate;  
                //    Adjust the calculated end date if it falls over a weekend  
                $endDoW =  self::DAYOFWEEK($endDate,3);  
                    $endDate +=  ($decrementing) ? - $endDoW +  4:  7 -  $endDoW;  
        switch (self::getReturnDateType()) {  
            case self::RETURNDATE_EXCEL            :  return (float)  $endDate;  
     * @param    long    $dateValue        Excel date serial value or a standard date string  
    public static function DAYOFMONTH($dateValue =  1) {  
        $dateValue    =  self::flattenSingleValue($dateValue);  
        if (is_string($dateValue =  self::_getDateValue($dateValue))) {  
            return self::$_errorCodes['value'];  
        } elseif ($dateValue ==  0.0) {  
        } elseif ($dateValue <  0.0) {  
            return self::$_errorCodes['num'];  
        $PHPDateObject =  PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);  
        return (int)  $PHPDateObject->format('j');  
    }    //    function DAYOFMONTH()  
     * @param    long    $dateValue        Excel date serial value or a standard date string  
    public static function DAYOFWEEK($dateValue =  1, $style =  1) {  
        $dateValue    =  self::flattenSingleValue($dateValue);  
        $style        =  floor(self::flattenSingleValue($style));  
        if (is_string($dateValue =  self::_getDateValue($dateValue))) {  
            return self::$_errorCodes['value'];  
        } elseif ($dateValue <  0.0) {  
            return self::$_errorCodes['num'];  
        $PHPDateObject =  PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);  
        $DoW =  $PHPDateObject->format('w');  
            case 2:  if ($DoW ==  0) { $DoW =  7; }  
            case 3:  if ($DoW ==  0) { $DoW =  7; }  
        if (self::$compatibilityMode ==  self::COMPATIBILITY_EXCEL) {  
            //    Test for Excel's 1900 leap year, and introduce the error as required  
            if (($PHPDateObject->format('Y') ==  1900) &&  ($PHPDateObject->format('n') <=  2)) {  
    }    //    function DAYOFWEEK()  
     * @param    long    $dateValue        Excel date serial value or a standard date string  
     * @param    boolean    $method            Week begins on Sunday or Monday  
     * @return    int        Week Number  
    public static function WEEKOFYEAR($dateValue =  1, $method =  1) {  
        $dateValue    =  self::flattenSingleValue($dateValue);  
        $method        =  floor(self::flattenSingleValue($method));  
            return self::$_errorCodes['value'];  
        } elseif (($method <  1) ||  ($method >  2)) {  
            return self::$_errorCodes['num'];  
        if (is_string($dateValue =  self::_getDateValue($dateValue))) {  
            return self::$_errorCodes['value'];  
        } elseif ($dateValue <  0.0) {  
            return self::$_errorCodes['num'];  
        $PHPDateObject =  PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);  
        $dayOfYear =  $PHPDateObject->format('z');  
        $dow =  $PHPDateObject->format('w');  
        $PHPDateObject->modify('-'. $dayOfYear. ' days');  
        $dow =  $PHPDateObject->format('w');  
        $daysInFirstWeek =  7 -  (($dow +  (2 -  $method)) %  7);  
        $dayOfYear -=  $daysInFirstWeek;  
        $weekOfYear =  ceil($dayOfYear /  7) +  1;  
        return (int)  $weekOfYear;  
    }    //    function WEEKOFYEAR()  
     * @param    long    $dateValue        Excel date serial value or a standard date string  
        $dateValue    =  self::flattenSingleValue($dateValue);  
        if (is_string($dateValue =  self::_getDateValue($dateValue))) {  
            return self::$_errorCodes['value'];  
        } elseif ($dateValue <  0.0) {  
            return self::$_errorCodes['num'];  
        $PHPDateObject =  PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);  
        return (int)  $PHPDateObject->format('n');  
    }    //    function MONTHOFYEAR()  
     * @param    long    $dateValue        Excel date serial value or a standard date string  
    public static function YEAR($dateValue =  1) {  
        $dateValue    =  self::flattenSingleValue($dateValue);  
        if (is_string($dateValue =  self::_getDateValue($dateValue))) {  
            return self::$_errorCodes['value'];  
        } elseif ($dateValue <  0.0) {  
            return self::$_errorCodes['num'];  
        $PHPDateObject =  PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);  
        return (int)  $PHPDateObject->format('Y');  
     * @param    mixed    $timeValue        Excel time serial value or a standard time string  
    public static function HOUROFDAY($timeValue =  0) {  
        $timeValue    =  self::flattenSingleValue($timeValue);  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC) {  
                $testVal =  strtok($timeValue,'/-: ');  
                    return self::$_errorCodes['value'];  
            $timeValue =  self::_getTimeValue($timeValue);  
                return self::$_errorCodes['value'];  
            $timeValue =  fmod($timeValue,1);  
        } elseif ($timeValue <  0.0) {  
            return self::$_errorCodes['num'];  
        $timeValue =  PHPExcel_Shared_Date::ExcelToPHP($timeValue);  
        return (int)  gmdate('G',$timeValue);  
    }    //    function HOUROFDAY()  
     * @param    long    $timeValue        Excel time serial value or a standard time string  
        $timeValue =  $timeTester    =  self::flattenSingleValue($timeValue);  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC) {  
                $testVal =  strtok($timeValue,'/-: ');  
                    return self::$_errorCodes['value'];  
            $timeValue =  self::_getTimeValue($timeValue);  
                return self::$_errorCodes['value'];  
            $timeValue =  fmod($timeValue,1);  
        } elseif ($timeValue <  0.0) {  
            return self::$_errorCodes['num'];  
        $timeValue =  PHPExcel_Shared_Date::ExcelToPHP($timeValue);  
        return (int)  gmdate('i',$timeValue);  
    }    //    function MINUTEOFHOUR()  
     * @param    long    $timeValue        Excel time serial value or a standard time string  
        $timeValue    =  self::flattenSingleValue($timeValue);  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC) {  
                $testVal =  strtok($timeValue,'/-: ');  
                    return self::$_errorCodes['value'];  
            $timeValue =  self::_getTimeValue($timeValue);  
                return self::$_errorCodes['value'];  
            $timeValue =  fmod($timeValue,1);  
        } elseif ($timeValue <  0.0) {  
            return self::$_errorCodes['num'];  
        $timeValue =  PHPExcel_Shared_Date::ExcelToPHP($timeValue);  
        return (int)  gmdate('s',$timeValue);  
    }    //    function SECONDOFMINUTE()  
    private static function _adjustDateByMonths($dateValue =  0, $adjustmentMonths =  0) {  
        $oMonth = (int)  $PHPDateObject->format('m');  
        $oYear = (int)  $PHPDateObject->format('Y');  
        $adjustmentMonthsString = (string)  $adjustmentMonths;  
        if ($adjustmentMonths >  0) {  
            $adjustmentMonthsString =  '+'. $adjustmentMonths;  
        if ($adjustmentMonths !=  0) {  
            $PHPDateObject->modify($adjustmentMonthsString. ' months');  
        $nMonth = (int)  $PHPDateObject->format('m');  
        $nYear = (int)  $PHPDateObject->format('Y');  
        $monthDiff =  ($nMonth -  $oMonth) +  (($nYear -  $oYear) *  12);  
        if ($monthDiff !=  $adjustmentMonths) {  
            $adjustDays = (int)  $PHPDateObject->format('d');  
            $adjustDaysString =  '-'. $adjustDays. ' days';  
            $PHPDateObject->modify($adjustDaysString);  
    }    //    function _adjustDateByMonths()  
     * Returns the serial number that represents the date that is the indicated number of months before or after a specified date  
     * (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.  
     * @param    long    $dateValue                Excel date serial value or a standard date string  
     * @param    int        $adjustmentMonths        Number of months to adjust by  
     * @return    long    Excel date serial value  
    public static function EDATE($dateValue =  1, $adjustmentMonths =  0) {  
        $dateValue            =  self::flattenSingleValue($dateValue);  
        $adjustmentMonths    =  floor(self::flattenSingleValue($adjustmentMonths));  
            return self::$_errorCodes['value'];  
        if (is_string($dateValue =  self::_getDateValue($dateValue))) {  
            return self::$_errorCodes['value'];  
        $PHPDateObject =  self::_adjustDateByMonths($dateValue,$adjustmentMonths);  
        switch (self::getReturnDateType()) {  
            case self::RETURNDATE_PHP_OBJECT    :  return $PHPDateObject;  
     * Returns the serial number for the last day of the month that is the indicated number of months before or after start_date.  
     * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.  
     * @param    long    $dateValue            Excel date serial value or a standard date string  
     * @param    int        $adjustmentMonths    Number of months to adjust by  
     * @return    long    Excel date serial value  
    public static function EOMONTH($dateValue =  1, $adjustmentMonths =  0) {  
        $dateValue            =  self::flattenSingleValue($dateValue);  
        $adjustmentMonths    =  floor(self::flattenSingleValue($adjustmentMonths));  
            return self::$_errorCodes['value'];  
        if (is_string($dateValue =  self::_getDateValue($dateValue))) {  
            return self::$_errorCodes['value'];  
        $PHPDateObject =  self::_adjustDateByMonths($dateValue,$adjustmentMonths+ 1);  
        $adjustDays = (int)  $PHPDateObject->format('d');  
        $adjustDaysString =  '-'. $adjustDays. ' days';  
        $PHPDateObject->modify($adjustDaysString);  
        switch (self::getReturnDateType()) {  
            case self::RETURNDATE_PHP_OBJECT    :  return $PHPDateObject;  
     *    Truncates value to the number of fractional digits by number_digits.  
     *    @param    int            $number_digits   
     *    @return    float        Truncated value  
    public static function TRUNC($value =  0, $number_digits =  0) {  
        $value            =  self::flattenSingleValue($value);  
        $number_digits    =  self::flattenSingleValue($number_digits);  
        if ($number_digits <  0) {  
            return self::$_errorCodes['value'];  
        if ($number_digits >  0) {  
            $value =  $value *  pow(10, $number_digits);  
        if ($number_digits >  0) {  
            $value =  $value /  pow(10, $number_digits);  
     *    Computes x raised to the power y.  
    public static function POWER($x =  0, $y =  2) {  
        $x    =  self::flattenSingleValue($x);  
        $y    =  self::flattenSingleValue($y);  
        if ($x ==  0 &&  $y <=  0) {  
            return self::$_errorCodes['divisionbyzero'];  
    private static function _nbrConversionFormat($xVal,$places) {  
            if (strlen($xVal) <=  $places) {  
                return self::$_errorCodes['num'];  
        return substr($xVal,- 10);  
    }    //    function _nbrConversionFormat()  
     * Return a binary value as Decimal.  
        $x    =  self::flattenSingleValue($x);  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE) {  
                return self::$_errorCodes['value'];  
        if (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC) {  
            return self::$_errorCodes['num'];  
            return self::$_errorCodes['num'];  
        } elseif (strlen($x) ==  10) {  
     * Return a binary value as Hex.  
    public static function BINTOHEX($x, $places= null) {  
        $x    =  floor(self::flattenSingleValue($x));  
        $places    =  self::flattenSingleValue($places);  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE) {  
                return self::$_errorCodes['value'];  
        if (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC) {  
            return self::$_errorCodes['num'];  
            return self::$_errorCodes['num'];  
        } elseif (strlen($x) ==  10) {  
        return self::_nbrConversionFormat($hexVal,$places);  
     * Return a binary value as Octal.  
    public static function BINTOOCT($x, $places= null) {  
        $x    =  floor(self::flattenSingleValue($x));  
        $places    =  self::flattenSingleValue($places);  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE) {  
                return self::$_errorCodes['value'];  
        if (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC) {  
            return self::$_errorCodes['num'];  
            return self::$_errorCodes['num'];  
        } elseif (strlen($x) ==  10) {  
        return self::_nbrConversionFormat($octVal,$places);  
     * Return an octal value as binary.  
    public static function DECTOBIN($x, $places= null) {  
        $x    =  self::flattenSingleValue($x);  
        $places    =  self::flattenSingleValue($places);  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE) {  
                return self::$_errorCodes['value'];  
            return self::$_errorCodes['value'];  
            return self::$_errorCodes['num'];  
        return self::_nbrConversionFormat($r,$places);  
     * Return an octal value as binary.  
    public static function DECTOOCT($x, $places= null) {  
        $x    =  self::flattenSingleValue($x);  
        $places    =  self::flattenSingleValue($places);  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE) {  
                return self::$_errorCodes['value'];  
            return self::$_errorCodes['value'];  
        return self::_nbrConversionFormat($r,$places);  
     * Return an octal value as binary.  
    public static function DECTOHEX($x, $places= null) {  
        $x    =  self::flattenSingleValue($x);  
        $places    =  self::flattenSingleValue($places);  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE) {  
                return self::$_errorCodes['value'];  
            return self::$_errorCodes['value'];  
        return self::_nbrConversionFormat($r,$places);  
     * Return a hex value as binary.  
    public static function HEXTOBIN($x, $places= null) {  
        $x    =  self::flattenSingleValue($x);  
        $places    =  self::flattenSingleValue($places);  
            return self::$_errorCodes['value'];  
            return self::$_errorCodes['num'];  
        return substr(self::_nbrConversionFormat($binVal,$places),- 10);  
     * Return a hex value as octal.  
    public static function HEXTOOCT($x, $places= null) {  
        $x    =  self::flattenSingleValue($x);  
        $places    =  self::flattenSingleValue($places);  
            return self::$_errorCodes['value'];  
            return self::$_errorCodes['num'];  
        return self::_nbrConversionFormat($octVal,$places);  
     * Return a hex value as octal.  
        $x    =  self::flattenSingleValue($x);  
            return self::$_errorCodes['value'];  
            return self::$_errorCodes['num'];  
     * Return an octal value as binary.  
    public static function OCTTOBIN($x, $places= null) {  
        $x    =  self::flattenSingleValue($x);  
        $places    =  self::flattenSingleValue($places);  
            return self::$_errorCodes['value'];  
        if (preg_match_all('/[01234567]/',$x,$out) !=  strlen($x)) {  
            return self::$_errorCodes['num'];  
        return self::_nbrConversionFormat($r,$places);  
     * Return an octal value as binary.  
        $x    =  self::flattenSingleValue($x);  
            return self::$_errorCodes['value'];  
        if (preg_match_all('/[01234567]/',$x,$out) !=  strlen($x)) {  
            return self::$_errorCodes['num'];  
     * Return an octal value as hex.  
    public static function OCTTOHEX($x, $places= null) {  
        $x    =  self::flattenSingleValue($x);  
        $places    =  self::flattenSingleValue($places);  
            return self::$_errorCodes['value'];  
        if (preg_match_all('/[01234567]/',$x,$out) !=  strlen($x)) {  
            return self::$_errorCodes['num'];  
        return self::_nbrConversionFormat($hexVal,$places);  
        $workString = (string)  $complexNumber;  
        $realNumber =  $imaginary =  0;  
        //    Extract the suffix, if there is one  
        $suffix =  substr($workString,- 1);  
            $workString =  substr($workString,0,- 1);  
        //    Split the input into its Real and Imaginary components  
        if (strlen($workString) >  0) {  
            $leadingSign =  (($workString{0} ==  '+') ||  ($workString{0} ==  '-')) ?  1 :  0;  
        $realNumber =  strtok($workString, '+-');  
            if (($imaginary ==  '') &&  (($realNumber ==  '') ||  ($realNumber ==  '+') ||  ($realNumber ==  '-'))) {  
                $imaginary =  $realNumber. '1';  
            } else if ($imaginary ==  '') {  
                $imaginary =  $realNumber;  
            } elseif (($imaginary ==  '+') ||  ($imaginary ==  '-')) {  
        $complexArray =  array( 'real'        =>  $realNumber,  
                               'imaginary'    =>  $imaginary,  
    }    //    function _parseComplex()  
    private static function _cleanComplex($complexNumber) {  
        if ($complexNumber{0} ==  '+') $complexNumber =  substr($complexNumber,1);  
        if ($complexNumber{0} ==  '0') $complexNumber =  substr($complexNumber,1);  
        if ($complexNumber{0} ==  '.') $complexNumber =  '0'. $complexNumber;  
        if ($complexNumber{0} ==  '+') $complexNumber =  substr($complexNumber,1);  
     * returns a complex number of the form x + yi or x + yj.  
     * @param    float        $realNumber   
     * @param    float        $imaginary   
    public static function COMPLEX($realNumber= 0.0, $imaginary= 0.0, $suffix= 'i') {  
        $realNumber    =  (is_null($realNumber))    ?  0.0 :    (float)  self::flattenSingleValue($realNumber);  
        $imaginary    =  (is_null($imaginary))        ?  0.0 :    (float)  self::flattenSingleValue($imaginary);  
        $suffix        =  (is_null($suffix))        ?  'i' :     self::flattenSingleValue($suffix);  
            (($suffix ==  'i') ||  ($suffix ==  'j') ||  ($suffix ==  ''))) {  
            if ($suffix ==  '') $suffix =  'i';  
            if ($realNumber ==  0.0) {  
                } elseif ($imaginary ==  1.0) {  
                } elseif ($imaginary == - 1.0) {  
                    return (string)  '-'. $suffix;  
                return (string)  $imaginary. $suffix;  
            } elseif ($imaginary ==  0.0) {  
                return (string)  $realNumber;  
            } elseif ($imaginary ==  1.0) {  
                return (string)  $realNumber. '+'. $suffix;  
            } elseif ($imaginary == - 1.0) {  
                return (string)  $realNumber. '-'. $suffix;  
            if ($imaginary >  0) { $imaginary = (string)  '+'. $imaginary; }  
            return (string)  $realNumber. $imaginary. $suffix;  
        return self::$_errorCodes['value'];  
     * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.  
     * @param    string        $complexNumber   
    public static function IMAGINARY($complexNumber) {  
        $complexNumber    =  self::flattenSingleValue($complexNumber);  
        $parsedComplex =  self::_parseComplex($complexNumber);  
        return $parsedComplex['imaginary'];  
    }    //    function IMAGINARY()  
     * Returns the real coefficient of a complex number in x + yi or x + yj text format.  
     * @param    string        $complexNumber   
    public static function IMREAL($complexNumber) {  
        $complexNumber    =  self::flattenSingleValue($complexNumber);  
        $parsedComplex =  self::_parseComplex($complexNumber);  
        return $parsedComplex['real'];  
     * Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.  
     * @param    string        $complexNumber   
    public static function IMABS($complexNumber) {  
        $complexNumber    =  self::flattenSingleValue($complexNumber);  
        $parsedComplex =  self::_parseComplex($complexNumber);  
        return sqrt(($parsedComplex['real'] *  $parsedComplex['real']) +  ($parsedComplex['imaginary'] *  $parsedComplex['imaginary']));  
     * 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.  
     * @param    string        $complexNumber   
    public static function IMARGUMENT($complexNumber) {  
        $complexNumber    =  self::flattenSingleValue($complexNumber);  
        $parsedComplex =  self::_parseComplex($complexNumber);  
        if ($parsedComplex['real'] ==  0.0) {  
            if ($parsedComplex['imaginary'] ==  0.0) {  
            } elseif($parsedComplex['imaginary'] <  0.0) {  
        } elseif ($parsedComplex['real'] >  0.0) {  
            return atan($parsedComplex['imaginary'] /  $parsedComplex['real']);  
        } elseif ($parsedComplex['imaginary'] <  0.0) {  
            return 0 -  (M_PI -  atan(abs($parsedComplex['imaginary']) /  abs($parsedComplex['real'])));  
            return M_PI -  atan($parsedComplex['imaginary'] /  abs($parsedComplex['real']));  
    }    //    function IMARGUMENT()  
     * Returns the complex conjugate of a complex number in x + yi or x + yj text format.  
     * @param    string        $complexNumber   
        $complexNumber    =  self::flattenSingleValue($complexNumber);  
        $parsedComplex =  self::_parseComplex($complexNumber);  
        if ($parsedComplex['imaginary'] ==  0.0) {  
            return $parsedComplex['real'];  
            return self::_cleanComplex(self::COMPLEX($parsedComplex['real'], 0 -  $parsedComplex['imaginary'], $parsedComplex['suffix']));  
    }    //    function IMCONJUGATE()  
     * Returns the cosine of a complex number in x + yi or x + yj text format.  
     * @param    string        $complexNumber   
    public static function IMCOS($complexNumber) {  
        $complexNumber    =  self::flattenSingleValue($complexNumber);  
        $parsedComplex =  self::_parseComplex($complexNumber);  
        if ($parsedComplex['imaginary'] ==  0.0) {  
            return cos($parsedComplex['real']);  
            return self::IMCONJUGATE(self::COMPLEX(cos($parsedComplex['real']) *  cosh($parsedComplex['imaginary']),sin($parsedComplex['real']) *  sinh($parsedComplex['imaginary']),$parsedComplex['suffix']));  
     * Returns the sine of a complex number in x + yi or x + yj text format.  
     * @param    string        $complexNumber   
    public static function IMSIN($complexNumber) {  
        $complexNumber    =  self::flattenSingleValue($complexNumber);  
        $parsedComplex =  self::_parseComplex($complexNumber);  
        if ($parsedComplex['imaginary'] ==  0.0) {  
            return sin($parsedComplex['real']);  
            return self::COMPLEX(sin($parsedComplex['real']) *  cosh($parsedComplex['imaginary']),cos($parsedComplex['real']) *  sinh($parsedComplex['imaginary']),$parsedComplex['suffix']);  
     * Returns the square root of a complex number in x + yi or x + yj text format.  
     * @param    string        $complexNumber   
    public static function IMSQRT($complexNumber) {  
        $complexNumber    =  self::flattenSingleValue($complexNumber);  
        $parsedComplex =  self::_parseComplex($complexNumber);  
        $theta =  self::IMARGUMENT($complexNumber);  
        $r =  sqrt(sqrt(($parsedComplex['real'] *  $parsedComplex['real']) +  ($parsedComplex['imaginary'] *  $parsedComplex['imaginary'])));  
        if ($parsedComplex['suffix'] ==  '') {  
            return self::COMPLEX($d1 *  $r,$d2 *  $r);  
            return self::COMPLEX($d1 *  $r,$d2 *  $r,$parsedComplex['suffix']);  
     * Returns the natural logarithm of a complex number in x + yi or x + yj text format.  
     * @param    string        $complexNumber   
    public static function IMLN($complexNumber) {  
        $complexNumber    =  self::flattenSingleValue($complexNumber);  
        $parsedComplex =  self::_parseComplex($complexNumber);  
        if (($parsedComplex['real'] ==  0.0) &&  ($parsedComplex['imaginary'] ==  0.0)) {  
            return self::$_errorCodes['num'];  
        $logR =  log(sqrt(($parsedComplex['real'] *  $parsedComplex['real']) +  ($parsedComplex['imaginary'] *  $parsedComplex['imaginary'])));  
        $t =  self::IMARGUMENT($complexNumber);  
        if ($parsedComplex['suffix'] ==  '') {  
            return self::COMPLEX($logR,$t);  
            return self::COMPLEX($logR,$t,$parsedComplex['suffix']);  
     * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.  
     * @param    string        $complexNumber   
    public static function IMLOG10($complexNumber) {  
        $complexNumber    =  self::flattenSingleValue($complexNumber);  
        $parsedComplex =  self::_parseComplex($complexNumber);  
        if (($parsedComplex['real'] ==  0.0) &&  ($parsedComplex['imaginary'] ==  0.0)) {  
            return self::$_errorCodes['num'];  
        } elseif (($parsedComplex['real'] >  0.0) &&  ($parsedComplex['imaginary'] ==  0.0)) {  
            return log10($parsedComplex['real']);  
        return self::IMPRODUCT(log10(EULER),self::IMLN($complexNumber));  
     * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.  
     * @param    string        $complexNumber   
    public static function IMLOG2($complexNumber) {  
        $complexNumber    =  self::flattenSingleValue($complexNumber);  
        $parsedComplex =  self::_parseComplex($complexNumber);  
        if (($parsedComplex['real'] ==  0.0) &&  ($parsedComplex['imaginary'] ==  0.0)) {  
            return self::$_errorCodes['num'];  
        } elseif (($parsedComplex['real'] >  0.0) &&  ($parsedComplex['imaginary'] ==  0.0)) {  
            return log($parsedComplex['real'],2);  
        return self::IMPRODUCT(log(EULER,2),self::IMLN($complexNumber));  
     * Returns the exponential of a complex number in x + yi or x + yj text format.  
     * @param    string        $complexNumber   
    public static function IMEXP($complexNumber) {  
        $complexNumber    =  self::flattenSingleValue($complexNumber);  
        $parsedComplex =  self::_parseComplex($complexNumber);  
        if (($parsedComplex['real'] ==  0.0) &&  ($parsedComplex['imaginary'] ==  0.0)) {  
        $e =  exp($parsedComplex['real']);  
        $eX =  $e *  cos($parsedComplex['imaginary']);  
        $eY =  $e *  sin($parsedComplex['imaginary']);  
        if ($parsedComplex['suffix'] ==  '') {  
            return self::COMPLEX($eX,$eY);  
            return self::COMPLEX($eX,$eY,$parsedComplex['suffix']);  
     * Returns a complex number in x + yi or x + yj text format raised to a power.  
     * @param    string        $complexNumber   
    public static function IMPOWER($complexNumber,$realNumber) {  
        $complexNumber    =  self::flattenSingleValue($complexNumber);  
        $realNumber        =  self::flattenSingleValue($realNumber);  
            return self::$_errorCodes['value'];  
        $parsedComplex =  self::_parseComplex($complexNumber);  
        $r =  sqrt(($parsedComplex['real'] *  $parsedComplex['real']) +  ($parsedComplex['imaginary'] *  $parsedComplex['imaginary']));  
        $rPower =  pow($r,$realNumber);  
        $theta =  self::IMARGUMENT($complexNumber) *  $realNumber;  
        } elseif ($parsedComplex['imaginary'] ==  0.0) {  
            return self::COMPLEX($rPower *  cos($theta),$rPower *  sin($theta),$parsedComplex['suffix']);  
            return self::COMPLEX($rPower *  cos($theta),$rPower *  sin($theta),$parsedComplex['suffix']);  
     * Returns the quotient of two complex numbers in x + yi or x + yj text format.  
     * @param    string        $complexDividend   
     * @param    string        $complexDivisor   
    public static function IMDIV($complexDividend,$complexDivisor) {  
        $complexDividend    =  self::flattenSingleValue($complexDividend);  
        $complexDivisor    =  self::flattenSingleValue($complexDivisor);  
        $parsedComplexDividend =  self::_parseComplex($complexDividend);  
        if (!is_array($parsedComplexDividend)) {  
            return $parsedComplexDividend;  
        $parsedComplexDivisor =  self::_parseComplex($complexDivisor);  
            return $parsedComplexDividend;  
        if (($parsedComplexDividend['suffix'] !=  '') &&  ($parsedComplexDivisor['suffix'] !=  '') &&   
            ($parsedComplexDividend['suffix'] !=  $parsedComplexDivisor['suffix'])) {  
            return self::$_errorCodes['num'];  
        if (($parsedComplexDividend['suffix'] !=  '') &&  ($parsedComplexDivisor['suffix'] ==  '')) {  
            $parsedComplexDivisor['suffix'] =  $parsedComplexDividend['suffix'];  
        $d1 =  ($parsedComplexDividend['real'] *  $parsedComplexDivisor['real']) +  ($parsedComplexDividend['imaginary'] *  $parsedComplexDivisor['imaginary']);  
        $d2 =  ($parsedComplexDividend['imaginary'] *  $parsedComplexDivisor['real']) -  ($parsedComplexDividend['real'] *  $parsedComplexDivisor['imaginary']);  
        $d3 =  ($parsedComplexDivisor['real'] *  $parsedComplexDivisor['real']) +  ($parsedComplexDivisor['imaginary'] *  $parsedComplexDivisor['imaginary']);  
            return self::_cleanComplex($r. '+'. $i. $parsedComplexDivisor['suffix']);  
            return self::_cleanComplex($r. $i. $parsedComplexDivisor['suffix']);  
     * Returns the difference of two complex numbers in x + yi or x + yj text format.  
     * @param    string        $complexNumber1   
     * @param    string        $complexNumber2   
    public static function IMSUB($complexNumber1,$complexNumber2) {  
        $complexNumber1    =  self::flattenSingleValue($complexNumber1);  
        $complexNumber2    =  self::flattenSingleValue($complexNumber2);  
        $parsedComplex1 =  self::_parseComplex($complexNumber1);  
        $parsedComplex2 =  self::_parseComplex($complexNumber2);  
        if ((($parsedComplex1['suffix'] !=  '') &&  ($parsedComplex2['suffix'] !=  '')) &&   
            ($parsedComplex1['suffix'] !=  $parsedComplex2['suffix'])) {  
            return self::$_errorCodes['num'];  
        } elseif (($parsedComplex1['suffix'] ==  '') &&  ($parsedComplex2['suffix'] !=  '')) {  
            $parsedComplex1['suffix'] =  $parsedComplex2['suffix'];  
        $d1 =  $parsedComplex1['real'] -  $parsedComplex2['real'];  
        $d2 =  $parsedComplex1['imaginary'] -  $parsedComplex2['imaginary'];  
        return self::COMPLEX($d1,$d2,$parsedComplex1['suffix']);  
     * Returns the sum of two or more complex numbers in x + yi or x + yj text format.  
     * @param    array of mixed        Data Series  
    public static function IMSUM() {  
        $returnValue =  self::_parseComplex('0');  
        // Loop through the arguments  
        foreach ($aArgs as $arg) {  
            $parsedComplex =  self::_parseComplex($arg);  
            if ($activeSuffix ==  '') {  
                $activeSuffix =  $parsedComplex['suffix'];  
            } elseif (($parsedComplex['suffix'] !=  '') &&  ($activeSuffix !=  $parsedComplex['suffix'])) {  
                return self::$_errorCodes['value'];  
            $returnValue['real'] +=  $parsedComplex['real'];  
            $returnValue['imaginary'] +=  $parsedComplex['imaginary'];  
        if ($returnValue['imaginary'] ==  0.0) { $activeSuffix =  ''; }  
        return self::COMPLEX($returnValue['real'],$returnValue['imaginary'],$activeSuffix);  
     * Returns the product of two or more complex numbers in x + yi or x + yj text format.  
     * @param    array of mixed        Data Series  
        $returnValue =  self::_parseComplex('1');  
        // Loop through the arguments  
        foreach ($aArgs as $arg) {  
            $parsedComplex =  self::_parseComplex($arg);  
            $workValue =  $returnValue;  
            if (($parsedComplex['suffix'] !=  '') &&  ($activeSuffix ==  '')) {  
                $activeSuffix =  $parsedComplex['suffix'];  
            } elseif (($parsedComplex['suffix'] !=  '') &&  ($activeSuffix !=  $parsedComplex['suffix'])) {  
                return self::$_errorCodes['num'];  
            $returnValue['real'] =  ($workValue['real'] *  $parsedComplex['real']) -  ($workValue['imaginary'] *  $parsedComplex['imaginary']);  
            $returnValue['imaginary'] =  ($workValue['real'] *  $parsedComplex['imaginary']) +  ($workValue['imaginary'] *  $parsedComplex['real']);  
        if ($returnValue['imaginary'] ==  0.0) { $activeSuffix =  ''; }  
        return self::COMPLEX($returnValue['real'],$returnValue['imaginary'],$activeSuffix);  
    }    //    function IMPRODUCT()  
    private static $_conversionUnits =  array( 'g'        =>  array(    'Group'    =>  'Mass',            'Unit Name'    =>  'Gram',                        'AllowPrefix'    =>  True        ),  
                                              'sg'        =>  array(    'Group'    =>  'Mass',            'Unit Name'    =>  'Slug',                        'AllowPrefix'    =>  False    ),  
                                              'lbm'        =>  array(    'Group'    =>  'Mass',            'Unit Name'    =>  'Pound mass (avoirdupois)',    'AllowPrefix'    =>  False    ),  
                                              'u'        =>  array(    'Group'    =>  'Mass',            'Unit Name'    =>  'U (atomic mass unit)',        'AllowPrefix'    =>  True        ),  
                                              'ozm'        =>  array(    'Group'    =>  'Mass',            'Unit Name'    =>  'Ounce mass (avoirdupois)',    'AllowPrefix'    =>  False    ),  
                                              'm'        =>  array(    'Group'    =>  'Distance',        'Unit Name'    =>  'Meter',                        'AllowPrefix'    =>  True        ),  
                                              'mi'        =>  array(    'Group'    =>  'Distance',        'Unit Name'    =>  'Statute mile',                'AllowPrefix'    =>  False    ),  
                                              'Nmi'        =>  array(    'Group'    =>  'Distance',        'Unit Name'    =>  'Nautical mile',                'AllowPrefix'    =>  False    ),  
                                              'in'        =>  array(    'Group'    =>  'Distance',        'Unit Name'    =>  'Inch',                        'AllowPrefix'    =>  False    ),  
                                              'ft'        =>  array(    'Group'    =>  'Distance',        'Unit Name'    =>  'Foot',                        'AllowPrefix'    =>  False    ),  
                                              'yd'        =>  array(    'Group'    =>  'Distance',        'Unit Name'    =>  'Yard',                        'AllowPrefix'    =>  False    ),  
                                              'ang'        =>  array(    'Group'    =>  'Distance',        'Unit Name'    =>  'Angstrom',                    'AllowPrefix'    =>  True        ),  
                                              'Pica'    =>  array(    'Group'    =>  'Distance',        'Unit Name'    =>  'Pica (1/72 in)',            'AllowPrefix'    =>  False    ),  
                                              'yr'        =>  array(    'Group'    =>  'Time',            'Unit Name'    =>  'Year',                        'AllowPrefix'    =>  False    ),  
                                              'day'        =>  array(    'Group'    =>  'Time',            'Unit Name'    =>  'Day',                        'AllowPrefix'    =>  False    ),  
                                              'hr'        =>  array(    'Group'    =>  'Time',            'Unit Name'    =>  'Hour',                        'AllowPrefix'    =>  False    ),  
                                              'mn'        =>  array(    'Group'    =>  'Time',            'Unit Name'    =>  'Minute',                    'AllowPrefix'    =>  False    ),  
                                              'sec'        =>  array(    'Group'    =>  'Time',            'Unit Name'    =>  'Second',                    'AllowPrefix'    =>  True        ),  
                                              'Pa'        =>  array(    'Group'    =>  'Pressure',        'Unit Name'    =>  'Pascal',                    'AllowPrefix'    =>  True        ),  
                                              'p'        =>  array(    'Group'    =>  'Pressure',        'Unit Name'    =>  'Pascal',                    'AllowPrefix'    =>  True        ),  
                                              'atm'        =>  array(    'Group'    =>  'Pressure',        'Unit Name'    =>  'Atmosphere',                'AllowPrefix'    =>  True        ),  
                                              'at'        =>  array(    'Group'    =>  'Pressure',        'Unit Name'    =>  'Atmosphere',                'AllowPrefix'    =>  True        ),  
                                              'mmHg'    =>  array(    'Group'    =>  'Pressure',        'Unit Name'    =>  'mm of Mercury',                'AllowPrefix'    =>  True        ),  
                                              'N'        =>  array(    'Group'    =>  'Force',            'Unit Name'    =>  'Newton',                    'AllowPrefix'    =>  True        ),  
                                              'dyn'        =>  array(    'Group'    =>  'Force',            'Unit Name'    =>  'Dyne',                        'AllowPrefix'    =>  True        ),  
                                              'dy'        =>  array(    'Group'    =>  'Force',            'Unit Name'    =>  'Dyne',                        'AllowPrefix'    =>  True        ),  
                                              'lbf'        =>  array(    'Group'    =>  'Force',            'Unit Name'    =>  'Pound force',                'AllowPrefix'    =>  False    ),  
                                              'J'        =>  array(    'Group'    =>  'Energy',        'Unit Name'    =>  'Joule',                        'AllowPrefix'    =>  True        ),  
                                              'e'        =>  array(    'Group'    =>  'Energy',        'Unit Name'    =>  'Erg',                        'AllowPrefix'    =>  True        ),  
                                              'c'        =>  array(    'Group'    =>  'Energy',        'Unit Name'    =>  'Thermodynamic calorie',        'AllowPrefix'    =>  True        ),  
                                              'cal'        =>  array(    'Group'    =>  'Energy',        'Unit Name'    =>  'IT calorie',                'AllowPrefix'    =>  True        ),  
                                              'eV'        =>  array(    'Group'    =>  'Energy',        'Unit Name'    =>  'Electron volt',                'AllowPrefix'    =>  True        ),  
                                              'ev'        =>  array(    'Group'    =>  'Energy',        'Unit Name'    =>  'Electron volt',                'AllowPrefix'    =>  True        ),  
                                              'HPh'        =>  array(    'Group'    =>  'Energy',        'Unit Name'    =>  'Horsepower-hour',            'AllowPrefix'    =>  False    ),  
                                              'hh'        =>  array(    'Group'    =>  'Energy',        'Unit Name'    =>  'Horsepower-hour',            'AllowPrefix'    =>  False    ),  
                                              'Wh'        =>  array(    'Group'    =>  'Energy',        'Unit Name'    =>  'Watt-hour',                    'AllowPrefix'    =>  True        ),  
                                              'wh'        =>  array(    'Group'    =>  'Energy',        'Unit Name'    =>  'Watt-hour',                    'AllowPrefix'    =>  True        ),  
                                              'flb'        =>  array(    'Group'    =>  'Energy',        'Unit Name'    =>  'Foot-pound',                'AllowPrefix'    =>  False    ),  
                                              'BTU'        =>  array(    'Group'    =>  'Energy',        'Unit Name'    =>  'BTU',                        'AllowPrefix'    =>  False    ),  
                                              'btu'        =>  array(    'Group'    =>  'Energy',        'Unit Name'    =>  'BTU',                        'AllowPrefix'    =>  False    ),  
                                              'HP'        =>  array(    'Group'    =>  'Power',            'Unit Name'    =>  'Horsepower',                'AllowPrefix'    =>  False    ),  
                                              'h'        =>  array(    'Group'    =>  'Power',            'Unit Name'    =>  'Horsepower',                'AllowPrefix'    =>  False    ),  
                                              'W'        =>  array(    'Group'    =>  'Power',            'Unit Name'    =>  'Watt',                        'AllowPrefix'    =>  True        ),  
                                              'w'        =>  array(    'Group'    =>  'Power',            'Unit Name'    =>  'Watt',                        'AllowPrefix'    =>  True        ),  
                                              'T'        =>  array(    'Group'    =>  'Magnetism',        'Unit Name'    =>  'Tesla',                        'AllowPrefix'    =>  True        ),  
                                              'ga'        =>  array(    'Group'    =>  'Magnetism',        'Unit Name'    =>  'Gauss',                        'AllowPrefix'    =>  True        ),  
                                              'C'        =>  array(    'Group'    =>  'Temperature',    'Unit Name'    =>  'Celsius',                    'AllowPrefix'    =>  False    ),  
                                              'cel'        =>  array(    'Group'    =>  'Temperature',    'Unit Name'    =>  'Celsius',                    'AllowPrefix'    =>  False    ),  
                                              'F'        =>  array(    'Group'    =>  'Temperature',    'Unit Name'    =>  'Fahrenheit',                'AllowPrefix'    =>  False    ),  
                                              'fah'        =>  array(    'Group'    =>  'Temperature',    'Unit Name'    =>  'Fahrenheit',                'AllowPrefix'    =>  False    ),  
                                              'K'        =>  array(    'Group'    =>  'Temperature',    'Unit Name'    =>  'Kelvin',                    'AllowPrefix'    =>  False    ),  
                                              'kel'        =>  array(    'Group'    =>  'Temperature',    'Unit Name'    =>  'Kelvin',                    'AllowPrefix'    =>  False    ),  
                                              'tsp'        =>  array(    'Group'    =>  'Liquid',        'Unit Name'    =>  'Teaspoon',                    'AllowPrefix'    =>  False    ),  
                                              'tbs'        =>  array(    'Group'    =>  'Liquid',        'Unit Name'    =>  'Tablespoon',                'AllowPrefix'    =>  False    ),  
                                              'oz'        =>  array(    'Group'    =>  'Liquid',        'Unit Name'    =>  'Fluid Ounce',                'AllowPrefix'    =>  False    ),  
                                              'cup'        =>  array(    'Group'    =>  'Liquid',        'Unit Name'    =>  'Cup',                        'AllowPrefix'    =>  False    ),  
                                              'pt'        =>  array(    'Group'    =>  'Liquid',        'Unit Name'    =>  'U.S. Pint',                    'AllowPrefix'    =>  False    ),  
                                              'us_pt'    =>  array(    'Group'    =>  'Liquid',        'Unit Name'    =>  'U.S. Pint',                    'AllowPrefix'    =>  False    ),  
                                              'uk_pt'    =>  array(    'Group'    =>  'Liquid',        'Unit Name'    =>  'U.K. Pint',                    'AllowPrefix'    =>  False    ),  
                                              'qt'        =>  array(    'Group'    =>  'Liquid',        'Unit Name'    =>  'Quart',                        'AllowPrefix'    =>  False    ),  
                                              'gal'        =>  array(    'Group'    =>  'Liquid',        'Unit Name'    =>  'Gallon',                    'AllowPrefix'    =>  False    ),  
                                              'l'        =>  array(    'Group'    =>  'Liquid',        'Unit Name'    =>  'Litre',                        'AllowPrefix'    =>  True        ),  
                                              'lt'        =>  array(    'Group'    =>  'Liquid',        'Unit Name'    =>  'Litre',                        'AllowPrefix'    =>  True        )  
    private static $_conversionMultipliers =  array(    'Y'    =>  array(    'multiplier'    =>  1E24,    'name'    =>  'yotta'    ),  
                                                    'Z'    =>  array(    'multiplier'    =>  1E21,    'name'    =>  'zetta'    ),  
                                                    'E'    =>  array(    'multiplier'    =>  1E18,    'name'    =>  'exa'    ),  
                                                    'P'    =>  array(    'multiplier'    =>  1E15,    'name'    =>  'peta'    ),  
                                                    'T'    =>  array(    'multiplier'    =>  1E12,    'name'    =>  'tera'    ),  
                                                    'G'    =>  array(    'multiplier'    =>  1E9,        'name'    =>  'giga'    ),  
                                                    'M'    =>  array(    'multiplier'    =>  1E6,        'name'    =>  'mega'    ),  
                                                    'k'    =>  array(    'multiplier'    =>  1E3,        'name'    =>  'kilo'    ),  
                                                    'h'    =>  array(    'multiplier'    =>  1E2,        'name'    =>  'hecto'    ),  
                                                    'e'    =>  array(    'multiplier'    =>  1E1,        'name'    =>  'deka'    ),  
                                                    'd'    =>  array(    'multiplier'    =>  1E-1,    'name'    =>  'deci'    ),  
                                                    'c'    =>  array(    'multiplier'    =>  1E-2,    'name'    =>  'centi'    ),  
                                                    'm'    =>  array(    'multiplier'    =>  1E-3,    'name'    =>  'milli'    ),  
                                                    'u'    =>  array(    'multiplier'    =>  1E-6,    'name'    =>  'micro'    ),  
                                                    'n'    =>  array(    'multiplier'    =>  1E-9,    'name'    =>  'nano'    ),  
                                                    'p'    =>  array(    'multiplier'    =>  1E-12,    'name'    =>  'pico'    ),  
                                                    'f'    =>  array(    'multiplier'    =>  1E-15,    'name'    =>  'femto'    ),  
                                                    'a'    =>  array(    'multiplier'    =>  1E-18,    'name'    =>  'atto'    ),  
                                                    'z'    =>  array(    'multiplier'    =>  1E-21,    'name'    =>  'zepto'    ),  
                                                    'y'    =>  array(    'multiplier'    =>  1E-24,    'name'    =>  'yocto'    )  
    private static $_unitConversions =  array(    'Mass'        =>  array(    'g'        =>  array(    'g'        =>  1.0,  
                                                                                            'sg'    =>  6.85220500053478E-05,  
                                                                                            'lbm'    =>  2.20462291469134E-03,  
                                                                                            'u'        =>  6.02217000000000E+23,  
                                                                                            'ozm'    =>  3.52739718003627E-02  
                                                                        'sg'    =>  array(    'g'        =>  1.45938424189287E+04,  
                                                                                            'lbm'    =>  3.21739194101647E+01,  
                                                                                            'u'        =>  8.78866000000000E+27,  
                                                                                            'ozm'    =>  5.14782785944229E+02  
                                                                        'lbm'    =>  array(    'g'        =>  4.5359230974881148E+02,  
                                                                                            'sg'    =>  3.10810749306493E-02,  
                                                                                            'u'        =>  2.73161000000000E+26,  
                                                                                            'ozm'    =>  1.60000023429410E+01  
                                                                        'u'        =>  array(    'g'        =>  1.66053100460465E-24,  
                                                                                            'sg'    =>  1.13782988532950E-28,  
                                                                                            'lbm'    =>  3.66084470330684E-27,  
                                                                                            'ozm'    =>  5.85735238300524E-26  
                                                                        'ozm'    =>  array(    'g'        =>  2.83495152079732E+01,  
                                                                                            'sg'    =>  1.94256689870811E-03,  
                                                                                            'lbm'    =>  6.24999908478882E-02,  
                                                                                            'u'        =>  1.70725600000000E+25,  
                                                'Distance'    =>  array(    'm'        =>  array(    'm'        =>  1.0,  
                                                                                            'mi'    =>  6.21371192237334E-04,  
                                                                                            'Nmi'    =>  5.39956803455724E-04,  
                                                                                            'in'    =>  3.93700787401575E+01,  
                                                                                            'ft'    =>  3.28083989501312E+00,  
                                                                                            'yd'    =>  1.09361329797891E+00,  
                                                                                            'ang'    =>  1.00000000000000E+10,  
                                                                                            'Pica'    =>  2.83464566929116E+03  
                                                                        'mi'    =>  array(    'm'        =>  1.60934400000000E+03,  
                                                                                            'Nmi'    =>  8.68976241900648E-01,  
                                                                                            'in'    =>  6.33600000000000E+04,  
                                                                                            'ft'    =>  5.28000000000000E+03,  
                                                                                            'yd'    =>  1.76000000000000E+03,  
                                                                                            'ang'    =>  1.60934400000000E+13,  
                                                                                            'Pica'    =>  4.56191999999971E+06  
                                                                        'Nmi'    =>  array(    'm'        =>  1.85200000000000E+03,  
                                                                                            'mi'    =>  1.15077944802354E+00,  
                                                                                            'in'    =>  7.29133858267717E+04,  
                                                                                            'ft'    =>  6.07611548556430E+03,  
                                                                                            'yd'    =>  2.02537182785694E+03,  
                                                                                            'ang'    =>  1.85200000000000E+13,  
                                                                                            'Pica'    =>  5.24976377952723E+06  
                                                                        'in'    =>  array(    'm'        =>  2.54000000000000E-02,  
                                                                                            'mi'    =>  1.57828282828283E-05,  
                                                                                            'Nmi'    =>  1.37149028077754E-05,  
                                                                                            'ft'    =>  8.33333333333333E-02,  
                                                                                            'yd'    =>  2.77777777686643E-02,  
                                                                                            'ang'    =>  2.54000000000000E+08,  
                                                                                            'Pica'    =>  7.19999999999955E+01  
                                                                        'ft'    =>  array(    'm'        =>  3.04800000000000E-01,  
                                                                                            'mi'    =>  1.89393939393939E-04,  
                                                                                            'Nmi'    =>  1.64578833693305E-04,  
                                                                                            'in'    =>  1.20000000000000E+01,  
                                                                                            'yd'    =>  3.33333333223972E-01,  
                                                                                            'ang'    =>  3.04800000000000E+09,  
                                                                                            'Pica'    =>  8.63999999999946E+02  
                                                                        'yd'    =>  array(    'm'        =>  9.14400000300000E-01,  
                                                                                            'mi'    =>  5.68181818368230E-04,  
                                                                                            'Nmi'    =>  4.93736501241901E-04,  
                                                                                            'in'    =>  3.60000000118110E+01,  
                                                                                            'ft'    =>  3.00000000000000E+00,  
                                                                                            'ang'    =>  9.14400000300000E+09,  
                                                                                            'Pica'    =>  2.59200000085023E+03  
                                                                        'ang'    =>  array(    'm'        =>  1.00000000000000E-10,  
                                                                                            'mi'    =>  6.21371192237334E-14,  
                                                                                            'Nmi'    =>  5.39956803455724E-14,  
                                                                                            'in'    =>  3.93700787401575E-09,  
                                                                                            'ft'    =>  3.28083989501312E-10,  
                                                                                            'yd'    =>  1.09361329797891E-10,  
                                                                                            'Pica'    =>  2.83464566929116E-07  
                                                                        'Pica'    =>  array(    'm'        =>  3.52777777777800E-04,  
                                                                                            'mi'    =>  2.19205948372629E-07,  
                                                                                            'Nmi'    =>  1.90484761219114E-07,  
                                                                                            'in'    =>  1.38888888888898E-02,  
                                                                                            'ft'    =>  1.15740740740748E-03,  
                                                                                            'yd'    =>  3.85802469009251E-04,  
                                                                                            'ang'    =>  3.52777777777800E+06,  
                                                'Time'        =>  array(    'yr'    =>  array(    'yr'        =>  1.0,  
                                                                        'day'    =>  array(    'yr'        =>  2.73785078713210E-03,  
                                                                        'hr'    =>  array(    'yr'        =>  1.14077116130504E-04,  
                                                                                            'day'        =>  4.16666666666667E-02,  
                                                                        'mn'    =>  array(    'yr'        =>  1.90128526884174E-06,  
                                                                                            'day'        =>  6.94444444444444E-04,  
                                                                                            'hr'        =>  1.66666666666667E-02,  
                                                                        'sec'    =>  array(    'yr'        =>  3.16880878140289E-08,  
                                                                                            'day'        =>  1.15740740740741E-05,  
                                                                                            'hr'        =>  2.77777777777778E-04,  
                                                                                            'mn'        =>  1.66666666666667E-02,  
                                                'Pressure'    =>  array(    'Pa'    =>  array(    'Pa'        =>  1.0,  
                                                                                            'atm'        =>  9.86923299998193E-06,  
                                                                                            'at'        =>  9.86923299998193E-06,  
                                                                                            'mmHg'        =>  7.50061707998627E-03  
                                                                        'p'        =>  array(    'Pa'        =>  1.0,  
                                                                                            'atm'        =>  9.86923299998193E-06,  
                                                                                            'at'        =>  9.86923299998193E-06,  
                                                                                            'mmHg'        =>  7.50061707998627E-03  
                                                                        'atm'    =>  array(    'Pa'        =>  1.01324996583000E+05,  
                                                                                            'p'            =>  1.01324996583000E+05,  
                                                                        'at'    =>  array(    'Pa'        =>  1.01324996583000E+05,  
                                                                                            'p'            =>  1.01324996583000E+05,  
                                                                        'mmHg'    =>  array(    'Pa'        =>  1.33322363925000E+02,  
                                                                                            'p'            =>  1.33322363925000E+02,  
                                                                                            'atm'        =>  1.31578947368421E-03,  
                                                                                            'at'        =>  1.31578947368421E-03,  
                                                'Force'        =>  array(    'N'        =>  array(    'N'            =>  1.0,  
                                                                                            'lbf'        =>  2.24808923655339E-01  
                                                                        'dyn'    =>  array(    'N'            =>  1.0E-5,  
                                                                                            'lbf'        =>  2.24808923655339E-06  
                                                                        'dy'    =>  array(    'N'            =>  1.0E-5,  
                                                                                            'lbf'        =>  2.24808923655339E-06  
                                                                        'lbf'    =>  array(    'N'            =>  4.448222,  
                                                'Energy'    =>  array(    'J'        =>  array(    'J'            =>  1.0,  
                                                                                            'e'            =>  9.99999519343231E+06,  
                                                                                            'c'            =>  2.39006249473467E-01,  
                                                                                            'cal'        =>  2.38846190642017E-01,  
                                                                                            'eV'        =>  6.24145700000000E+18,  
                                                                                            'ev'        =>  6.24145700000000E+18,  
                                                                                            'HPh'        =>  3.72506430801000E-07,  
                                                                                            'hh'        =>  3.72506430801000E-07,  
                                                                                            'Wh'        =>  2.77777916238711E-04,  
                                                                                            'wh'        =>  2.77777916238711E-04,  
                                                                                            'flb'        =>  2.37304222192651E+01,  
                                                                                            'BTU'        =>  9.47815067349015E-04,  
                                                                                            'btu'        =>  9.47815067349015E-04  
                                                                        'e'        =>  array(    'J'            =>  1.00000048065700E-07,  
                                                                                            'c'            =>  2.39006364353494E-08,  
                                                                                            'cal'        =>  2.38846305445111E-08,  
                                                                                            'eV'        =>  6.24146000000000E+11,  
                                                                                            'ev'        =>  6.24146000000000E+11,  
                                                                                            'HPh'        =>  3.72506609848824E-14,  
                                                                                            'hh'        =>  3.72506609848824E-14,  
                                                                                            'Wh'        =>  2.77778049754611E-11,  
                                                                                            'wh'        =>  2.77778049754611E-11,  
                                                                                            'flb'        =>  2.37304336254586E-06,  
                                                                                            'BTU'        =>  9.47815522922962E-11,  
                                                                                            'btu'        =>  9.47815522922962E-11  
                                                                        'c'        =>  array(    'J'            =>  4.18399101363672E+00,  
                                                                                            'e'            =>  4.18398900257312E+07,  
                                                                                            'cal'        =>  9.99330315287563E-01,  
                                                                                            'eV'        =>  2.61142000000000E+19,  
                                                                                            'ev'        =>  2.61142000000000E+19,  
                                                                                            'HPh'        =>  1.55856355899327E-06,  
                                                                                            'hh'        =>  1.55856355899327E-06,  
                                                                                            'Wh'        =>  1.16222030532950E-03,  
                                                                                            'wh'        =>  1.16222030532950E-03,  
                                                                                            'flb'        =>  9.92878733152102E+01,  
                                                                                            'BTU'        =>  3.96564972437776E-03,  
                                                                                            'btu'        =>  3.96564972437776E-03  
                                                                        'cal'    =>  array(    'J'            =>  4.18679484613929E+00,  
                                                                                            'e'            =>  4.18679283372801E+07,  
                                                                                            'c'            =>  1.00067013349059E+00,  
                                                                                            'eV'        =>  2.61317000000000E+19,  
                                                                                            'ev'        =>  2.61317000000000E+19,  
                                                                                            'HPh'        =>  1.55960800463137E-06,  
                                                                                            'hh'        =>  1.55960800463137E-06,  
                                                                                            'Wh'        =>  1.16299914807955E-03,  
                                                                                            'wh'        =>  1.16299914807955E-03,  
                                                                                            'flb'        =>  9.93544094443283E+01,  
                                                                                            'BTU'        =>  3.96830723907002E-03,  
                                                                                            'btu'        =>  3.96830723907002E-03  
                                                                        'eV'    =>  array(    'J'            =>  1.60219000146921E-19,  
                                                                                            'e'            =>  1.60218923136574E-12,  
                                                                                            'c'            =>  3.82933423195043E-20,  
                                                                                            'cal'        =>  3.82676978535648E-20,  
                                                                                            'HPh'        =>  5.96826078912344E-26,  
                                                                                            'hh'        =>  5.96826078912344E-26,  
                                                                                            'Wh'        =>  4.45053000026614E-23,  
                                                                                            'wh'        =>  4.45053000026614E-23,  
                                                                                            'flb'        =>  3.80206452103492E-18,  
                                                                                            'BTU'        =>  1.51857982414846E-22,  
                                                                                            'btu'        =>  1.51857982414846E-22  
                                                                        'ev'    =>  array(    'J'            =>  1.60219000146921E-19,  
                                                                                            'e'            =>  1.60218923136574E-12,  
                                                                                            'c'            =>  3.82933423195043E-20,  
                                                                                            'cal'        =>  3.82676978535648E-20,  
                                                                                            'HPh'        =>  5.96826078912344E-26,  
                                                                                            'hh'        =>  5.96826078912344E-26,  
                                                                                            'Wh'        =>  4.45053000026614E-23,  
                                                                                            'wh'        =>  4.45053000026614E-23,  
                                                                                            'flb'        =>  3.80206452103492E-18,  
                                                                                            'BTU'        =>  1.51857982414846E-22,  
                                                                                            'btu'        =>  1.51857982414846E-22  
                                                                        'HPh'    =>  array(    'J'            =>  2.68451741316170E+06,  
                                                                                            'e'            =>  2.68451612283024E+13,  
                                                                                            'c'            =>  6.41616438565991E+05,  
                                                                                            'cal'        =>  6.41186757845835E+05,  
                                                                                            'eV'        =>  1.67553000000000E+25,  
                                                                                            'ev'        =>  1.67553000000000E+25,  
                                                                                            'Wh'        =>  7.45699653134593E+02,  
                                                                                            'wh'        =>  7.45699653134593E+02,  
                                                                                            'flb'        =>  6.37047316692964E+07,  
                                                                                            'BTU'        =>  2.54442605275546E+03,  
                                                                                            'btu'        =>  2.54442605275546E+03  
                                                                        'hh'    =>  array(    'J'            =>  2.68451741316170E+06,  
                                                                                            'e'            =>  2.68451612283024E+13,  
                                                                                            'c'            =>  6.41616438565991E+05,  
                                                                                            'cal'        =>  6.41186757845835E+05,  
                                                                                            'eV'        =>  1.67553000000000E+25,  
                                                                                            'ev'        =>  1.67553000000000E+25,  
                                                                                            'Wh'        =>  7.45699653134593E+02,  
                                                                                            'wh'        =>  7.45699653134593E+02,  
                                                                                            'flb'        =>  6.37047316692964E+07,  
                                                                                            'BTU'        =>  2.54442605275546E+03,  
                                                                                            'btu'        =>  2.54442605275546E+03  
                                                                        'Wh'    =>  array(    'J'            =>  3.59999820554720E+03,  
                                                                                            'e'            =>  3.59999647518369E+10,  
                                                                                            'c'            =>  8.60422069219046E+02,  
                                                                                            'cal'        =>  8.59845857713046E+02,  
                                                                                            'eV'        =>  2.24692340000000E+22,  
                                                                                            'ev'        =>  2.24692340000000E+22,  
                                                                                            'HPh'        =>  1.34102248243839E-03,  
                                                                                            'hh'        =>  1.34102248243839E-03,  
                                                                                            'flb'        =>  8.54294774062316E+04,  
                                                                                            'BTU'        =>  3.41213254164705E+00,  
                                                                                            'btu'        =>  3.41213254164705E+00  
                                                                        'wh'    =>  array(    'J'            =>  3.59999820554720E+03,  
                                                                                            'e'            =>  3.59999647518369E+10,  
                                                                                            'c'            =>  8.60422069219046E+02,  
                                                                                            'cal'        =>  8.59845857713046E+02,  
                                                                                            'eV'        =>  2.24692340000000E+22,  
                                                                                            'ev'        =>  2.24692340000000E+22,  
                                                                                            'HPh'        =>  1.34102248243839E-03,  
                                                                                            'hh'        =>  1.34102248243839E-03,  
                                                                                            'flb'        =>  8.54294774062316E+04,  
                                                                                            'BTU'        =>  3.41213254164705E+00,  
                                                                                            'btu'        =>  3.41213254164705E+00  
                                                                        'flb'    =>  array(    'J'            =>  4.21400003236424E-02,  
                                                                                            'e'            =>  4.21399800687660E+05,  
                                                                                            'c'            =>  1.00717234301644E-02,  
                                                                                            'cal'        =>  1.00649785509554E-02,  
                                                                                            'eV'        =>  2.63015000000000E+17,  
                                                                                            'ev'        =>  2.63015000000000E+17,  
                                                                                            'HPh'        =>  1.56974211145130E-08,  
                                                                                            'hh'        =>  1.56974211145130E-08,  
                                                                                            'Wh'        =>  1.17055614802000E-05,  
                                                                                            'wh'        =>  1.17055614802000E-05,  
                                                                                            'BTU'        =>  3.99409272448406E-05,  
                                                                                            'btu'        =>  3.99409272448406E-05  
                                                                        'BTU'    =>  array(    'J'            =>  1.05505813786749E+03,  
                                                                                            'e'            =>  1.05505763074665E+10,  
                                                                                            'c'            =>  2.52165488508168E+02,  
                                                                                            'cal'        =>  2.51996617135510E+02,  
                                                                                            'eV'        =>  6.58510000000000E+21,  
                                                                                            'ev'        =>  6.58510000000000E+21,  
                                                                                            'HPh'        =>  3.93015941224568E-04,  
                                                                                            'hh'        =>  3.93015941224568E-04,  
                                                                                            'Wh'        =>  2.93071851047526E-01,  
                                                                                            'wh'        =>  2.93071851047526E-01,  
                                                                                            'flb'        =>  2.50369750774671E+04,  
                                                                        'btu'    =>  array(    'J'            =>  1.05505813786749E+03,  
                                                                                            'e'            =>  1.05505763074665E+10,  
                                                                                            'c'            =>  2.52165488508168E+02,  
                                                                                            'cal'        =>  2.51996617135510E+02,  
                                                                                            'eV'        =>  6.58510000000000E+21,  
                                                                                            'ev'        =>  6.58510000000000E+21,  
                                                                                            'HPh'        =>  3.93015941224568E-04,  
                                                                                            'hh'        =>  3.93015941224568E-04,  
                                                                                            'Wh'        =>  2.93071851047526E-01,  
                                                                                            'wh'        =>  2.93071851047526E-01,  
                                                                                            'flb'        =>  2.50369750774671E+04,  
                                                'Power'        =>  array(    'HP'    =>  array(    'HP'        =>  1.0,  
                                                                                            'W'            =>  7.45701000000000E+02,  
                                                                                            'w'            =>  7.45701000000000E+02  
                                                                        'h'        =>  array(    'HP'        =>  1.0,  
                                                                                            'W'            =>  7.45701000000000E+02,  
                                                                                            'w'            =>  7.45701000000000E+02  
                                                                        'W'        =>  array(    'HP'        =>  1.34102006031908E-03,  
                                                                                            'h'            =>  1.34102006031908E-03,  
                                                                        'w'        =>  array(    'HP'        =>  1.34102006031908E-03,  
                                                                                            'h'            =>  1.34102006031908E-03,  
                                                'Magnetism'    =>  array(    'T'        =>  array(    'T'            =>  1.0,  
                                                                        'ga'    =>  array(    'T'            =>  0.0001,  
                                                'Liquid'    =>  array(    'tsp'    =>  array(    'tsp'        =>  1.0,  
                                                                                            'tbs'        =>  3.33333333333333E-01,  
                                                                                            'oz'        =>  1.66666666666667E-01,  
                                                                                            'cup'        =>  2.08333333333333E-02,  
                                                                                            'pt'        =>  1.04166666666667E-02,  
                                                                                            'us_pt'        =>  1.04166666666667E-02,  
                                                                                            'uk_pt'        =>  8.67558516821960E-03,  
                                                                                            'qt'        =>  5.20833333333333E-03,  
                                                                                            'gal'        =>  1.30208333333333E-03,  
                                                                                            'l'            =>  4.92999408400710E-03,  
                                                                                            'lt'        =>  4.92999408400710E-03  
                                                                        'tbs'    =>  array(    'tsp'        =>  3.00000000000000E+00,  
                                                                                            'oz'        =>  5.00000000000000E-01,  
                                                                                            'cup'        =>  6.25000000000000E-02,  
                                                                                            'pt'        =>  3.12500000000000E-02,  
                                                                                            'us_pt'        =>  3.12500000000000E-02,  
                                                                                            'uk_pt'        =>  2.60267555046588E-02,  
                                                                                            'qt'        =>  1.56250000000000E-02,  
                                                                                            'gal'        =>  3.90625000000000E-03,  
                                                                                            'l'            =>  1.47899822520213E-02,  
                                                                                            'lt'        =>  1.47899822520213E-02  
                                                                        'oz'    =>  array(    'tsp'        =>  6.00000000000000E+00,  
                                                                                            'tbs'        =>  2.00000000000000E+00,  
                                                                                            'cup'        =>  1.25000000000000E-01,  
                                                                                            'pt'        =>  6.25000000000000E-02,  
                                                                                            'us_pt'        =>  6.25000000000000E-02,  
                                                                                            'uk_pt'        =>  5.20535110093176E-02,  
                                                                                            'qt'        =>  3.12500000000000E-02,  
                                                                                            'gal'        =>  7.81250000000000E-03,  
                                                                                            'l'            =>  2.95799645040426E-02,  
                                                                                            'lt'        =>  2.95799645040426E-02  
                                                                        'cup'    =>  array(    'tsp'        =>  4.80000000000000E+01,  
                                                                                            'tbs'        =>  1.60000000000000E+01,  
                                                                                            'oz'        =>  8.00000000000000E+00,  
                                                                                            'pt'        =>  5.00000000000000E-01,  
                                                                                            'us_pt'        =>  5.00000000000000E-01,  
                                                                                            'uk_pt'        =>  4.16428088074541E-01,  
                                                                                            'qt'        =>  2.50000000000000E-01,  
                                                                                            'gal'        =>  6.25000000000000E-02,  
                                                                                            'l'            =>  2.36639716032341E-01,  
                                                                                            'lt'        =>  2.36639716032341E-01  
                                                                        'pt'    =>  array(    'tsp'        =>  9.60000000000000E+01,  
                                                                                            'tbs'        =>  3.20000000000000E+01,  
                                                                                            'oz'        =>  1.60000000000000E+01,  
                                                                                            'cup'        =>  2.00000000000000E+00,  
                                                                                            'uk_pt'        =>  8.32856176149081E-01,  
                                                                                            'qt'        =>  5.00000000000000E-01,  
                                                                                            'gal'        =>  1.25000000000000E-01,  
                                                                                            'l'            =>  4.73279432064682E-01,  
                                                                                            'lt'        =>  4.73279432064682E-01  
                                                                        'us_pt'    =>  array(    'tsp'        =>  9.60000000000000E+01,  
                                                                                            'tbs'        =>  3.20000000000000E+01,  
                                                                                            'oz'        =>  1.60000000000000E+01,  
                                                                                            'cup'        =>  2.00000000000000E+00,  
                                                                                            'uk_pt'        =>  8.32856176149081E-01,  
                                                                                            'qt'        =>  5.00000000000000E-01,  
                                                                                            'gal'        =>  1.25000000000000E-01,  
                                                                                            'l'            =>  4.73279432064682E-01,  
                                                                                            'lt'        =>  4.73279432064682E-01  
                                                                        'uk_pt'    =>  array(    'tsp'        =>  1.15266000000000E+02,  
                                                                                            'tbs'        =>  3.84220000000000E+01,  
                                                                                            'oz'        =>  1.92110000000000E+01,  
                                                                                            'cup'        =>  2.40137500000000E+00,  
                                                                                            'pt'        =>  1.20068750000000E+00,  
                                                                                            'us_pt'        =>  1.20068750000000E+00,  
                                                                                            'qt'        =>  6.00343750000000E-01,  
                                                                                            'gal'        =>  1.50085937500000E-01,  
                                                                                            'l'            =>  5.68260698087162E-01,  
                                                                                            'lt'        =>  5.68260698087162E-01  
                                                                        'qt'    =>  array(    'tsp'        =>  1.92000000000000E+02,  
                                                                                            'tbs'        =>  6.40000000000000E+01,  
                                                                                            'oz'        =>  3.20000000000000E+01,  
                                                                                            'cup'        =>  4.00000000000000E+00,  
                                                                                            'pt'        =>  2.00000000000000E+00,  
                                                                                            'us_pt'        =>  2.00000000000000E+00,  
                                                                                            'uk_pt'        =>  1.66571235229816E+00,  
                                                                                            'gal'        =>  2.50000000000000E-01,  
                                                                                            'l'            =>  9.46558864129363E-01,  
                                                                                            'lt'        =>  9.46558864129363E-01  
                                                                        'gal'    =>  array(    'tsp'        =>  7.68000000000000E+02,  
                                                                                            'tbs'        =>  2.56000000000000E+02,  
                                                                                            'oz'        =>  1.28000000000000E+02,  
                                                                                            'cup'        =>  1.60000000000000E+01,  
                                                                                            'pt'        =>  8.00000000000000E+00,  
                                                                                            'us_pt'        =>  8.00000000000000E+00,  
                                                                                            'uk_pt'        =>  6.66284940919265E+00,  
                                                                                            'qt'        =>  4.00000000000000E+00,  
                                                                                            'l'            =>  3.78623545651745E+00,  
                                                                                            'lt'        =>  3.78623545651745E+00  
                                                                        'l'        =>  array(    'tsp'        =>  2.02840000000000E+02,  
                                                                                            'tbs'        =>  6.76133333333333E+01,  
                                                                                            'oz'        =>  3.38066666666667E+01,  
                                                                                            'cup'        =>  4.22583333333333E+00,  
                                                                                            'pt'        =>  2.11291666666667E+00,  
                                                                                            'us_pt'        =>  2.11291666666667E+00,  
                                                                                            'uk_pt'        =>  1.75975569552166E+00,  
                                                                                            'qt'        =>  1.05645833333333E+00,  
                                                                                            'gal'        =>  2.64114583333333E-01,  
                                                                        'lt'    =>  array(    'tsp'        =>  2.02840000000000E+02,  
                                                                                            'tbs'        =>  6.76133333333333E+01,  
                                                                                            'oz'        =>  3.38066666666667E+01,  
                                                                                            'cup'        =>  4.22583333333333E+00,  
                                                                                            'pt'        =>  2.11291666666667E+00,  
                                                                                            'us_pt'        =>  2.11291666666667E+00,  
                                                                                            'uk_pt'        =>  1.75975569552166E+00,  
                                                                                            'qt'        =>  1.05645833333333E+00,  
                                                                                            'gal'        =>  2.64114583333333E-01,  
        $conversionGroups =  array();  
        foreach(self::$_conversionUnits as $conversionUnit) {  
            $conversionGroups[] =  $conversionUnit['Group'];  
    }    //    function getConversionGroups()  
     * getConversionGroupUnits  
        $conversionGroups =  array();  
        foreach(self::$_conversionUnits as $conversionUnit =>  $conversionGroup) {  
            if ((is_null($group)) ||  ($conversionGroup['Group'] ==  $group)) {  
                $conversionGroups[$conversionGroup['Group']][] =  $conversionUnit;  
        return $conversionGroups;  
    }    //    function getConversionGroupUnits()  
     * getConversionGroupUnitDetails  
        $conversionGroups =  array();  
        foreach(self::$_conversionUnits as $conversionUnit =>  $conversionGroup) {  
            if ((is_null($group)) ||  ($conversionGroup['Group'] ==  $group)) {  
                $conversionGroups[$conversionGroup['Group']][] =  array(    'unit'            =>  $conversionUnit,  
                                                                        'description'    =>  $conversionGroup['Unit Name']  
        return $conversionGroups;  
    }    //    function getConversionGroupUnitDetails()  
        return self::$_conversionMultipliers;  
    }    //    function getConversionGroups()  
    public static function CONVERTUOM($value, $fromUOM, $toUOM) {  
        $value        =  self::flattenSingleValue($value);  
        $fromUOM    =  self::flattenSingleValue($fromUOM);  
        $toUOM        =  self::flattenSingleValue($toUOM);  
            return self::$_errorCodes['value'];  
        if (isset (self::$_conversionUnits[$fromUOM])) {  
            $unitGroup1 =  self::$_conversionUnits[$fromUOM]['Group'];  
            $fromMultiplier =  substr($fromUOM,0,1);  
            $fromUOM =  substr($fromUOM,1);  
            if (isset (self::$_conversionMultipliers[$fromMultiplier])) {  
                $fromMultiplier =  self::$_conversionMultipliers[$fromMultiplier]['multiplier'];  
                return self::$_errorCodes['na'];  
            if ((isset (self::$_conversionUnits[$fromUOM])) &&  (self::$_conversionUnits[$fromUOM]['AllowPrefix'])) {  
                $unitGroup1 =  self::$_conversionUnits[$fromUOM]['Group'];  
                return self::$_errorCodes['na'];  
        $value *=  $fromMultiplier;  
        if (isset (self::$_conversionUnits[$toUOM])) {  
            $unitGroup2 =  self::$_conversionUnits[$toUOM]['Group'];  
            $toMultiplier =  substr($toUOM,0,1);  
            if (isset (self::$_conversionMultipliers[$toMultiplier])) {  
                $toMultiplier =  self::$_conversionMultipliers[$toMultiplier]['multiplier'];  
                return self::$_errorCodes['na'];  
            if ((isset (self::$_conversionUnits[$toUOM])) &&  (self::$_conversionUnits[$toUOM]['AllowPrefix'])) {  
                $unitGroup2 =  self::$_conversionUnits[$toUOM]['Group'];  
                return self::$_errorCodes['na'];  
        if ($unitGroup1 !=  $unitGroup2) {  
            return self::$_errorCodes['na'];  
        if ($fromUOM ==  $toUOM) {  
        } elseif ($unitGroup1 ==  'Temperature') {  
            if (($fromUOM ==  'F') ||  ($fromUOM ==  'fah')) {  
                if (($toUOM ==  'F') ||  ($toUOM ==  'fah')) {  
                    $value =  (($value -  32) /  1.8);  
                    if (($toUOM ==  'K') ||  ($toUOM ==  'kel')) {  
            } elseif ((($fromUOM ==  'K') ||  ($fromUOM ==  'kel')) &&   
                      (($toUOM ==  'K') ||  ($toUOM ==  'kel'))) {  
            } elseif ((($fromUOM ==  'C') ||  ($fromUOM ==  'cel')) &&   
                      (($toUOM ==  'C') ||  ($toUOM ==  'cel'))) {  
            if (($toUOM ==  'F') ||  ($toUOM ==  'fah')) {  
                if (($fromUOM ==  'K') ||  ($fromUOM ==  'kel')) {  
                return ($value *  1.8) +  32;  
            if (($toUOM ==  'C') ||  ($toUOM ==  'cel')) {  
        return ($value *  self::$_unitConversions[$unitGroup1][$fromUOM][$toUOM]) /  $toMultiplier;  
    }    //    function CONVERTUOM()  
     * Returns the modified Bessel function, which is equivalent to the Bessel function evaluated for purely imaginary arguments  
    public static function BESSELI($x, $n) {  
        $x    =  (is_null($x))    ?  0.0 :     self::flattenSingleValue($x);  
        $n    =  (is_null($n))    ?  0.0 :     self::flattenSingleValue($n);  
                return self::$_errorCodes['num'];  
                $fTerm =  pow($x /  2, $n) /  self::FACT($n);  
                    $fTerm /=  ($nK *  ($nK +  $n));  
                } while ((abs($fTerm) >  1e-10) &&  (++ $nK <  100));  
                $fResult =  exp($fXAbs) /  sqrt($f_2_PI *  $fXAbs);  
                if (($n &&  1) &&  ($x <  0)) {  
        return self::$_errorCodes['value'];  
     * Returns the Bessel function  
    public static function BESSELJ($x, $n) {  
        $x    =  (is_null($x))    ?  0.0 :     self::flattenSingleValue($x);  
        $n    =  (is_null($n))    ?  0.0 :     self::flattenSingleValue($n);  
                return self::$_errorCodes['num'];  
                $fTerm =  pow($x /  2, $n) /  self::FACT($n);  
                    $fTerm /=  ($nK *  ($nK +  $n));  
                } while ((abs($fTerm) >  1e-10) &&  (++ $nK <  100));  
                $fResult =  sqrt(M_2DIVPI /  $fXAbs) *  cos($fXAbs -  $n *  $f_PI_DIV_2 -  $f_PI_DIV_4);  
                if (($n &&  1) &&  ($x <  0)) {  
        return self::$_errorCodes['value'];  
    private static function _Besselk0($fNum) {  
            $fRet = - log($fNum2) *  self::BESSELI($fNum, 0) +   
                    (- 0.57721566 +  $y *  (0.42278420 +  $y *  (0.23069756 +  $y *  (0.3488590e-1 +  $y *  (0.262698e-2 +  $y *   
                    (0.10750e-3 +  $y *  0.74e-5))))));  
            $fRet =  exp(- $fNum) /  sqrt($fNum) *   
                    (1.25331414 +  $y *  (- 0.7832358e-1 +  $y *  (0.2189568e-1 +  $y *  (- 0.1062446e-1 +  $y *   
                    (0.587872e-2 +  $y *  (- 0.251540e-2 +  $y *  0.53208e-3))))));  
    }    //    function _Besselk0()  
    private static function _Besselk1($fNum) {  
            $fRet =  log($fNum2) *  self::BESSELI($fNum, 1) +   
                    (1 +  $y *  (0.15443144 +  $y *  (- 0.67278579 +  $y *  (- 0.18156897 +  $y *  (- 0.1919402e-1 +  $y *   
                    (- 0.110404e-2 +  $y *  (- 0.4686e-4))))))) /  $fNum;  
            $fRet =  exp(- $fNum) /  sqrt($fNum) *   
                    (1.25331414 +  $y *  (0.23498619 +  $y *  (- 0.3655620e-1 +  $y *  (0.1504268e-1 +  $y *  (- 0.780353e-2 +  $y *   
                    (0.325614e-2 +  $y *  (- 0.68245e-3)))))));  
    }    //    function _Besselk1()  
     * Returns the modified Bessel function, which is equivalent to the Bessel functions evaluated for purely imaginary arguments.  
    public static function BESSELK($x, $ord) {  
        $x        =  (is_null($x))        ?  0.0 :     self::flattenSingleValue($x);  
        $ord    =  (is_null($ord))    ?  0.0 :     self::flattenSingleValue($ord);  
            if (($ord <  0) ||  ($x ==  0.0)) {  
                return self::$_errorCodes['num'];  
                case 0 :     return self::_Besselk0($x);  
                case 1 :     return self::_Besselk1($x);  
                default :     $fTox    =  2 /  $x;  
                            $fBkm    =  self::_Besselk0($x);  
                            $fBk    =  self::_Besselk1($x);  
                            for ($n =  1; $n <  $ord; ++ $n) {  
                                $fBkp    =  $fBkm +  $n *  $fTox *  $fBk;  
        return self::$_errorCodes['value'];  
    private static function _Bessely0($fNum) {  
            $f1 = - 2957821389.0 +  $y *  (7062834065.0 +  $y *  (- 512359803.6 +  $y *  (10879881.29 +  $y *  (- 86327.92757 +  $y *  228.4622733))));  
            $f2 =  40076544269.0 +  $y *  (745249964.8 +  $y *  (7189466.438 +  $y *  (47447.26470 +  $y *  (226.1030244 +  $y))));  
            $fRet =  $f1 /  $f2 +  M_2DIVPI *  self::BESSELJ($fNum, 0) *  log($fNum);  
            $xx =  $fNum -  0.785398164;  
            $f1 =  1 +  $y *  (- 0.1098628627e-2 +  $y *  (0.2734510407e-4 +  $y *  (- 0.2073370639e-5 +  $y *  0.2093887211e-6)));  
            $f2 = - 0.1562499995e-1 +  $y *  (0.1430488765e-3 +  $y *  (- 0.6911147651e-5 +  $y *  (0.7621095161e-6 +  $y *  (- 0.934945152e-7))));  
    }    //    function _Bessely0()  
    private static function _Bessely1($fNum) {  
            $f1 =  $fNum *  (- 0.4900604943e13 +  $y *  (0.1275274390e13 +  $y *  (- 0.5153438139e11 +  $y *  (0.7349264551e9 +  $y *   
                (- 0.4237922726e7 +  $y *  0.8511937935e4)))));  
            $f2 =  0.2499580570e14 +  $y *  (0.4244419664e12 +  $y *  (0.3733650367e10 +  $y *  (0.2245904002e8 +  $y *   
                (0.1020426050e6 +  $y *  (0.3549632885e3 +  $y)))));  
            $fRet =  $f1 /  $f2 +  M_2DIVPI *  ( self::BESSELJ($fNum, 1) *  log($fNum) -  1 /  $fNum);  
            $xx =  $fNum -  2.356194491;  
            $f1 =  1 +  $y *  (0.183105e-2 +  $y *  (- 0.3516396496e-4 +  $y *  (0.2457520174e-5 +  $y *  (- 0.240337019e6))));  
            $f2 =  0.04687499995 +  $y *  (- 0.2002690873e-3 +  $y *  (0.8449199096e-5 +  $y *  (- 0.88228987e-6 +  $y *  0.105787412e-6)));  
            #i12430# ...but this seems to work much better.  
//            $fRet = sqrt(M_2DIVPI / $fNum) * sin($fNum - 2.356194491);  
    }    //    function _Bessely1()  
     * Returns the Bessel function, which is also called the Weber function or the Neumann function.  
    public static function BESSELY($x, $ord) {  
        $x        =  (is_null($x))        ?  0.0 :     self::flattenSingleValue($x);  
        $ord    =  (is_null($ord))    ?  0.0 :     self::flattenSingleValue($ord);  
            if (($ord <  0) ||  ($x ==  0.0)) {  
                return self::$_errorCodes['num'];  
                case 0 :     return self::_Bessely0($x);  
                case 1 :     return self::_Bessely1($x);  
                            $fBym    =  self::_Bessely0($x);  
                            $fBy    =  self::_Bessely1($x);  
                            for ($n =  1; $n <  $ord; ++ $n) {  
                                $fByp    =  $n *  $fTox *  $fBy -  $fBym;  
        return self::$_errorCodes['value'];  
     * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.  
    public static function DELTA($a, $b= 0) {  
        $a    =  self::flattenSingleValue($a);  
        $b    =  self::flattenSingleValue($b);  
     * Returns 1 if number = step; returns 0 (zero) otherwise  
    public static function GESTEP($number, $step= 0) {  
        $number    =  self::flattenSingleValue($number);  
        $step    =  self::flattenSingleValue($step);  
        return (int)  ($number >=  $step);  
    //    Private method to calculate the erf value  
    private static $_two_sqrtpi =  1.128379167095512574;  
    private static function _erfVal($x) {  
            return 1 -  self::_erfcVal($x);  
            $sum -=  $term /  (2 *  $j +  1);  
            $sum +=  $term /  (2 *  $j +  1);  
        return self::$_two_sqrtpi *  $sum;  
     * Returns the error function integrated between lower_limit and upper_limit  
     * @param    float        $lower    lower bound for integrating ERF  
     * @param    float        $upper    upper bound for integrating ERF.  
     *                                 If omitted, ERF integrates between zero and lower_limit  
    public static function ERF($lower, $upper =  null) {  
        $lower    =  self::flattenSingleValue($lower);  
        $upper    =  self::flattenSingleValue($upper);  
                return self::$_errorCodes['num'];  
                return self::_erfVal($lower);  
                    return self::$_errorCodes['num'];  
                return self::_erfVal($upper) -  self::_erfVal($lower);  
        return self::$_errorCodes['value'];  
    //    Private method to calculate the erfc value  
    private static $_one_sqrtpi =  0.564189583547756287;  
    private static function _erfcVal($x) {  
            return 1 -  self::_erfVal($x);  
            return 2 -  self::erfc(- $x);  
        return self::$_one_sqrtpi *  exp(- $x *  $x) *  $q2;  
     * Returns the complementary ERF function integrated between x and infinity  
     * @param    float        $x        The lower bound for integrating ERF  
    public static function ERFC($x) {  
        $x    =  self::flattenSingleValue($x);  
                return self::$_errorCodes['num'];  
            return self::_erfcVal($x);  
        return self::$_errorCodes['value'];  
     *    Converts a string value to upper case.  
     *    @param    string        $mixedCaseString   
    public static function LOWERCASE($mixedCaseString) {  
        $mixedCaseString    =  self::flattenSingleValue($mixedCaseString);  
            $mixedCaseString =  ($mixedCaseString) ?  'TRUE' :  'FALSE';  
    }    //    function LOWERCASE()  
     *    Converts a string value to upper case.  
     *    @param    string        $mixedCaseString   
    public static function UPPERCASE($mixedCaseString) {  
        $mixedCaseString    =  self::flattenSingleValue($mixedCaseString);  
            $mixedCaseString =  ($mixedCaseString) ?  'TRUE' :  'FALSE';  
    }    //    function UPPERCASE()  
     *    Converts a string value to upper case.  
     *    @param    string        $mixedCaseString   
    public static function PROPERCASE($mixedCaseString) {  
        $mixedCaseString    =  self::flattenSingleValue($mixedCaseString);  
            $mixedCaseString =  ($mixedCaseString) ?  'TRUE' :  'FALSE';  
    }    //    function PROPERCASE()  
     *    This function converts a number to text using currency format, with the decimals rounded to the specified place.  
     *    The format used is $#,##0.00_);($#,##0.00)..  
     *    @param    float    $value            The value to format  
     *    @param    int        $decimals        The number of digits to display to the right of the decimal point.  
     *                                     If decimals is negative, number is rounded to the left of the decimal point.  
     *                                     If you omit decimals, it is assumed to be 2  
    public static function DOLLAR($value =  0, $decimals =  2) {  
        $value        =  self::flattenSingleValue($value);  
        $decimals    =  is_null($decimals) ?  0 :  self::flattenSingleValue($decimals);  
            return self::$_errorCodes['num'];  
        $decimals =  floor($decimals);  
            $round =  pow(10,abs($decimals));  
            if ($value <  0) { $round =  0- $round; }  
            $value =  self::MROUND($value,$round);  
            //    The implementation of money_format used if the standard PHP function is not available can't handle decimal places of 0,  
            //        so we display to 1 dp and chop off that character and the decimal separator using substr  
     * Converts a dollar price expressed as an integer part and a fraction part into a dollar price expressed as a decimal number.  
     * Fractional dollar numbers are sometimes used for security prices.  
     * @param    float    $fractional_dollar    Fractional Dollar  
     * @param    int        $fraction            Fraction  
    public static function DOLLARDE($fractional_dollar =  Null, $fraction =  0) {  
        $fractional_dollar    =  self::flattenSingleValue($fractional_dollar);  
        $fraction            = (int) self::flattenSingleValue($fraction);  
        if (is_null($fractional_dollar) ||  $fraction <  0) {  
            return self::$_errorCodes['num'];  
            return self::$_errorCodes['divisionbyzero'];  
        $dollars =  floor($fractional_dollar);  
        $cents =  fmod($fractional_dollar,1);  
        return $dollars +  $cents;  
     * Converts a dollar price expressed as a decimal number into a dollar price expressed as a fraction.  
     * Fractional dollar numbers are sometimes used for security prices.  
     * @param    float    $decimal_dollar        Decimal Dollar  
     * @param    int        $fraction            Fraction  
    public static function DOLLARFR($decimal_dollar =  Null, $fraction =  0) {  
        $decimal_dollar    =  self::flattenSingleValue($decimal_dollar);  
        $fraction        = (int) self::flattenSingleValue($fraction);  
        if (is_null($decimal_dollar) ||  $fraction <  0) {  
            return self::$_errorCodes['num'];  
            return self::$_errorCodes['divisionbyzero'];  
        $dollars =  floor($decimal_dollar);  
        $cents =  fmod($decimal_dollar,1);  
        return $dollars +  $cents;  
     * Returns the effective interest rate given the nominal rate and the number of compounding payments per year.  
     * @param    float    $nominal_rate        Nominal interest rate  
     * @param    int        $npery                Number of compounding payments per year  
    public static function EFFECT($nominal_rate =  0, $npery =  0) {  
        $nominal_rate    =  self::flattenSingleValue($nominal_rate);  
        $npery            = (int) self::flattenSingleValue($npery);  
        if ($nominal_rate <=  0 ||  $npery <  1) {  
            return self::$_errorCodes['num'];  
        return pow((1 +  $nominal_rate /  $npery), $npery) -  1;  
     * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.  
     * @param    float    $effect_rate    Effective interest rate  
     * @param    int        $npery            Number of compounding payments per year  
    public static function NOMINAL($effect_rate =  0, $npery =  0) {  
        $effect_rate    =  self::flattenSingleValue($effect_rate);  
        $npery            = (int) self::flattenSingleValue($npery);  
        if ($effect_rate <=  0 ||  $npery <  1) {  
            return self::$_errorCodes['num'];  
        return $npery *  (pow($effect_rate +  1, 1 /  $npery) -  1);  
     * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).  
     * @param    float    $rate    Interest rate per period  
     * @param    int        $nper    Number of periods  
     * @param    float    $pmt    Periodic payment (annuity)  
     * @param    float    $fv        Future Value  
     * @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period  
    public static function PV($rate =  0, $nper =  0, $pmt =  0, $fv =  0, $type =  0) {  
        $rate    =  self::flattenSingleValue($rate);  
        $nper    =  self::flattenSingleValue($nper);  
        $pmt    =  self::flattenSingleValue($pmt);  
        $fv        =  self::flattenSingleValue($fv);  
        $type    =  self::flattenSingleValue($type);  
        if ($type !=  0 &&  $type !=  1) {  
            return self::$_errorCodes['num'];  
        if (!is_null($rate) &&  $rate !=  0) {  
            return (- $pmt *  (1 +  $rate *  $type) *  ((pow(1 +  $rate, $nper) -  1) /  $rate) -  $fv) /  pow(1 +  $rate, $nper);  
            return - $fv -  $pmt *  $nper;  
     * Returns the Future Value of a cash flow with constant payments and interest rate (annuities).  
     * @param    float    $rate    Interest rate per period  
     * @param    int        $nper    Number of periods  
     * @param    float    $pmt    Periodic payment (annuity)  
     * @param    float    $pv        Present Value  
     * @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period  
    public static function FV($rate =  0, $nper =  0, $pmt =  0, $pv =  0, $type =  0) {  
        $rate    =  self::flattenSingleValue($rate);  
        $nper    =  self::flattenSingleValue($nper);  
        $pmt    =  self::flattenSingleValue($pmt);  
        $pv        =  self::flattenSingleValue($pv);  
        $type    =  self::flattenSingleValue($type);  
        if ($type !=  0 &&  $type !=  1) {  
            return self::$_errorCodes['num'];  
        if (!is_null($rate) &&  $rate !=  0) {  
            return - $pv *  pow(1 +  $rate, $nper) -  $pmt *  (1 +  $rate *  $type) *  (pow(1 +  $rate, $nper) -  1) /  $rate;  
            return - $pv -  $pmt *  $nper;  
    public static function FVSCHEDULE($principal, $schedule) {  
        $principal    =  self::flattenSingleValue($principal);  
        $schedule    =  self::flattenArray($schedule);  
        foreach($schedule as $n) {  
    }    //    function FVSCHEDULE()  
     * Returns the constant payment (annuity) for a cash flow with a constant interest rate.  
     * @param    float    $rate    Interest rate per period  
     * @param    int        $nper    Number of periods  
     * @param    float    $pv        Present Value  
     * @param    float    $fv        Future Value  
     * @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period  
    public static function PMT($rate =  0, $nper =  0, $pv =  0, $fv =  0, $type =  0) {  
        $rate    =  self::flattenSingleValue($rate);  
        $nper    =  self::flattenSingleValue($nper);  
        $pv        =  self::flattenSingleValue($pv);  
        $fv        =  self::flattenSingleValue($fv);  
        $type    =  self::flattenSingleValue($type);  
        if ($type !=  0 &&  $type !=  1) {  
            return self::$_errorCodes['num'];  
        if (!is_null($rate) &&  $rate !=  0) {  
            return (- $fv -  $pv *  pow(1 +  $rate, $nper)) /  (1 +  $rate *  $type) /  ((pow(1 +  $rate, $nper) -  1) /  $rate);  
            return (- $pv -  $fv) /  $nper;  
     * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.  
     *    @param    float    $rate    Interest rate per period  
     *    @param    int        $pmt    Periodic payment (annuity)  
     *    @param    float    $pv        Present Value  
     *    @param    float    $fv        Future Value  
     *    @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period  
    public static function NPER($rate =  0, $pmt =  0, $pv =  0, $fv =  0, $type =  0) {  
        $rate    =  self::flattenSingleValue($rate);  
        $pmt    =  self::flattenSingleValue($pmt);  
        $pv        =  self::flattenSingleValue($pv);  
        $fv        =  self::flattenSingleValue($fv);  
        $type    =  self::flattenSingleValue($type);  
        if ($type !=  0 &&  $type !=  1) {  
            return self::$_errorCodes['num'];  
        if (!is_null($rate) &&  $rate !=  0) {  
            if ($pmt ==  0 &&  $pv ==  0) {  
                return self::$_errorCodes['num'];  
            return log(($pmt *  (1 +  $rate *  $type) /  $rate -  $fv) /  ($pv +  $pmt *  (1 +  $rate *  $type) /  $rate)) /  log(1 +  $rate);  
                return self::$_errorCodes['num'];  
            return (- $pv - $fv) /  $pmt;  
    private static function _interestAndPrincipal($rate= 0, $per= 0, $nper= 0, $pv= 0, $fv= 0, $type= 0) {  
        $pmt =  self::PMT($rate, $nper, $pv, $fv, $type);  
        for ($i =  1; $i<=  $per; ++ $i) {  
            $interest =  ($type &&  $i ==  1)?  0 : - $capital *  $rate;  
            $principal =  $pmt -  $interest;  
        return array($interest, $principal);  
    }    //    function _interestAndPrincipal()  
     *    Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.  
     *    @param    float    $rate    Interest rate per period  
     *    @param    int        $per    Period for which we want to find the interest  
     *    @param    int        $nper    Number of periods  
     *    @param    float    $pv        Present Value  
     *    @param    float    $fv        Future Value  
     *    @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period  
    public static function IPMT($rate, $per, $nper, $pv, $fv =  0, $type =  0) {  
        $rate    =  self::flattenSingleValue($rate);  
        $per    = (int)  self::flattenSingleValue($per);  
        $nper    = (int)  self::flattenSingleValue($nper);  
        $pv        =  self::flattenSingleValue($pv);  
        $fv        =  self::flattenSingleValue($fv);  
        $type    = (int)  self::flattenSingleValue($type);  
        if ($type !=  0 &&  $type !=  1) {  
            return self::$_errorCodes['num'];  
        if ($per <=  0 ||  $per >  $nper) {  
            return self::$_errorCodes['value'];  
        $interestAndPrincipal =  self::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);  
        return $interestAndPrincipal[0];  
     *    Returns the cumulative interest paid on a loan between start_period and end_period.  
     *    @param    float    $rate    Interest rate per period  
     *    @param    int        $nper    Number of periods  
     *    @param    float    $pv        Present Value  
     *    @param    int        start    The first period in the calculation.  
     *                                 Payment periods are numbered beginning with 1.  
     *    @param    int        end        The last period in the calculation.  
     *    @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period  
    public static function CUMIPMT($rate, $nper, $pv, $start, $end, $type =  0) {  
        $rate    =  self::flattenSingleValue($rate);  
        $nper    = (int)  self::flattenSingleValue($nper);  
        $pv        =  self::flattenSingleValue($pv);  
        $start    = (int)  self::flattenSingleValue($start);  
        $end    = (int)  self::flattenSingleValue($end);  
        $type    = (int)  self::flattenSingleValue($type);  
        if ($type !=  0 &&  $type !=  1) {  
            return self::$_errorCodes['num'];  
        if ($start <  1 ||  $start >  $end) {  
            return self::$_errorCodes['value'];  
        for ($per =  $start; $per <=  $end; ++ $per) {  
            $interest +=  self::IPMT($rate, $per, $nper, $pv, 0, $type);  
     *    Returns the interest payment for a given period for an investment based on periodic, constant payments and a constant interest rate.  
     *    @param    float    $rate    Interest rate per period  
     *    @param    int        $per    Period for which we want to find the interest  
     *    @param    int        $nper    Number of periods  
     *    @param    float    $pv        Present Value  
     *    @param    float    $fv        Future Value  
     *    @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period  
    public static function PPMT($rate, $per, $nper, $pv, $fv =  0, $type =  0) {  
        $rate    =  self::flattenSingleValue($rate);  
        $per    = (int)  self::flattenSingleValue($per);  
        $nper    = (int)  self::flattenSingleValue($nper);  
        $pv        =  self::flattenSingleValue($pv);  
        $fv        =  self::flattenSingleValue($fv);  
        $type    = (int)  self::flattenSingleValue($type);  
        if ($type !=  0 &&  $type !=  1) {  
            return self::$_errorCodes['num'];  
        if ($per <=  0 ||  $per >  $nper) {  
            return self::$_errorCodes['value'];  
        $interestAndPrincipal =  self::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);  
        return $interestAndPrincipal[1];  
     *    Returns the cumulative principal paid on a loan between start_period and end_period.  
     *    @param    float    $rate    Interest rate per period  
     *    @param    int        $nper    Number of periods  
     *    @param    float    $pv        Present Value  
     *    @param    int        start    The first period in the calculation.  
     *                                 Payment periods are numbered beginning with 1.  
     *    @param    int        end        The last period in the calculation.  
     *    @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period  
    public static function CUMPRINC($rate, $nper, $pv, $start, $end, $type =  0) {  
        $rate    =  self::flattenSingleValue($rate);  
        $nper    = (int)  self::flattenSingleValue($nper);  
        $pv        =  self::flattenSingleValue($pv);  
        $start    = (int)  self::flattenSingleValue($start);  
        $end    = (int)  self::flattenSingleValue($end);  
        $type    = (int)  self::flattenSingleValue($type);  
        if ($type !=  0 &&  $type !=  1) {  
            return self::$_errorCodes['num'];  
        if ($start <  1 ||  $start >  $end) {  
            return self::$_errorCodes['value'];  
        for ($per =  $start; $per <=  $end; ++ $per) {  
            $principal +=  self::PPMT($rate, $per, $nper, $pv, 0, $type);  
     *      Returns the interest payment for an investment based on an interest rate and a constant payment schedule.  
     *          =ISPMT(interest_rate, period, number_payments, PV)  
     *      interest_rate is the interest rate for the investment  
     *      period is the period to calculate the interest rate.  It must be betweeen 1 and number_payments.  
     *      number_payments is the number of payments for the annuity  
     *      PV is the loan amount or present value of the payments  
    public static function ISPMT() {  
        $principlePayment =  ($principleRemaining *  1.0) /  ($numberPeriods *  1.0);  
        for($i= 0; $i <=  $period; ++ $i) {  
            $returnValue =  $interestRate *  $principleRemaining * - 1;  
            $principleRemaining -=  $principlePayment;  
            // principle needs to be 0 after the last payment, don't let floating point screw it up  
            if($i ==  $numberPeriods) {  
     * Returns the Net Present Value of a cash flow series given a discount rate.  
     * @param    float    Discount interest rate  
     * @param    array    Cash flow series  
    public static function NPV() {  
        // Loop through arguments  
        for ($i =  1; $i <=  count($aArgs); ++ $i) {  
            // Is it a numeric value?  
                $returnValue +=  $aArgs[$i -  1] /  pow(1 +  $rate, $i);  
     *    Returns the net present value for a schedule of cash flows that is not necessarily periodic.  
     *    To calculate the net present value for a series of cash flows that is periodic, use the NPV function.  
     *    @param    float    Discount interest rate  
     *    @param    array    Cash flow series  
    public static function XNPV($rate, $values, $dates) {  
        if ((!is_array($values)) ||  (!is_array($dates))) return self::$_errorCodes['value'];  
        $values    =  self::flattenArray($values);  
        $dates    =  self::flattenArray($dates);  
        $valCount =  count($values);  
        if ($valCount !=  count($dates)) return self::$_errorCodes['num'];  
        for ($i =  0; $i <  $valCount; ++ $i) {  
            $xnpv +=  $values[$i] /  pow(1 +  $rate, self::DATEDIF($dates[0],$dates[$i],'d') /  365);  
        return (is_finite($xnpv) ?  $xnpv :  self::$_errorCodes['value']);  
    public static function IRR($values, $guess =  0.1) {  
        if (!is_array($values)) return self::$_errorCodes['value'];  
        $values =  self::flattenArray($values);  
        $guess =  self::flattenSingleValue($guess);  
        // create an initial range, with a root somewhere between 0 and guess  
        $f1 =  self::NPV($x1, $values);  
        $f2 =  self::NPV($x2, $values);  
            if (($f1 *  $f2) <  0.0) break;  
                $f1 =  self::NPV($x1 +=  1.6 *  ($x1 -  $x2), $values);  
                $f2 =  self::NPV($x2 +=  1.6 *  ($x2 -  $x1), $values);  
        if (($f1 *  $f2) >  0.0) return self::$_errorCodes['value'];  
        $f =  self::NPV($x1, $values);  
            $f_mid =  self::NPV($x_mid, $values);  
            if ($f_mid <=  0.0) $rtb =  $x_mid;  
        return self::$_errorCodes['value'];  
    public static function MIRR($values, $finance_rate, $reinvestment_rate) {  
        if (!is_array($values)) return self::$_errorCodes['value'];  
        $values                =  self::flattenArray($values);  
        $finance_rate        =  self::flattenSingleValue($finance_rate);  
        $reinvestment_rate    =  self::flattenSingleValue($reinvestment_rate);  
        $rr =  1.0 +  $reinvestment_rate;  
        $fr =  1.0 +  $finance_rate;  
        $npv_pos =  $npv_neg =  0.0;  
        foreach($values as $i =>  $v) {  
                $npv_pos +=  $v /  pow($rr, $i);  
                $npv_neg +=  $v /  pow($fr, $i);  
        if (($npv_neg ==  0) ||  ($npv_pos ==  0) ||  ($reinvestment_rate <= - 1)) {  
            return self::$_errorCodes['value'];  
        $mirr =  pow((- $npv_pos *  pow($rr, $n))  
                /  ($npv_neg *  ($rr)), (1.0 /  ($n -  1))) -  1.0; 
        return (is_finite($mirr) ?  $mirr :  self::$_errorCodes['value']);  
    public static function XIRR($values, $dates, $guess =  0.1) {  
        if ((!is_array($values)) &&  (!is_array($dates))) return self::$_errorCodes['value'];  
        $values    =  self::flattenArray($values);  
        $dates    =  self::flattenArray($dates);  
        $guess =  self::flattenSingleValue($guess);  
        if (count($values) !=  count($dates)) return self::$_errorCodes['num'];  
        // create an initial range, with a root somewhere between 0 and guess  
        $f1 =  self::XNPV($x1, $values, $dates);  
        $f2 =  self::XNPV($x2, $values, $dates);  
            if (($f1 *  $f2) <  0.0) break;  
                $f1 =  self::XNPV($x1 +=  1.6 *  ($x1 -  $x2), $values, $dates);  
                $f2 =  self::XNPV($x2 +=  1.6 *  ($x2 -  $x1), $values, $dates);  
        if (($f1 *  $f2) >  0.0) return self::$_errorCodes['value'];  
        $f =  self::XNPV($x1, $values, $dates);  
            $f_mid =  self::XNPV($x_mid, $values, $dates);  
            if ($f_mid <=  0.0) $rtb =  $x_mid;  
        return self::$_errorCodes['value'];  
    public static function RATE($nper, $pmt, $pv, $fv =  0.0, $type =  0, $guess =  0.1) {  
        $nper    = (int)  self::flattenSingleValue($nper);  
        $pmt    =  self::flattenSingleValue($pmt);  
        $pv        =  self::flattenSingleValue($pv);  
        $fv        =  (is_null($fv))    ?  0.0    :     self::flattenSingleValue($fv);  
        $type    =  (is_null($type))    ?  0        :    (int)  self::flattenSingleValue($type);  
        $guess    =  (is_null($guess))    ?  0.1    :     self::flattenSingleValue($guess);  
            $y =  $pv *  (1 +  $nper *  $rate) +  $pmt *  (1 +  $rate *  $type) *  $nper +  $fv;  
            $f =  exp($nper *  log(1 +  $rate));  
            $y =  $pv *  $f +  $pmt *  (1 /  $rate +  $type) *  ($f -  1) +  $fv;  
        $y0 =  $pv +  $pmt *  $nper +  $fv;  
        $y1 =  $pv *  $f +  $pmt *  (1 /  $rate +  $type) *  ($f -  1) +  $fv;  
        // find root by secant method  
            $rate =  ($y1 *  $x0 -  $y0 *  $x1) /  ($y1 -  $y0);  
                $y =  $pv *  (1 +  $nper *  $rate) +  $pmt *  (1 +  $rate *  $type) *  $nper +  $fv;  
                $f =  exp($nper *  log(1 +  $rate));  
                $y =  $pv *  $f +  $pmt *  (1 /  $rate +  $type) *  ($f -  1) +  $fv;  
     *    Returns the depreciation of an asset for a specified period using the fixed-declining balance method.  
     *    This form of depreciation is used if you want to get a higher depreciation value at the beginning of the depreciation  
     *        (as opposed to linear depreciation). The depreciation value is reduced with every depreciation period by the  
     *        depreciation already deducted from the initial cost.  
     *    @param    float    cost        Initial cost of the asset.  
     *    @param    float    salvage        Value at the end of the depreciation. (Sometimes called the salvage value of the asset)  
     *    @param    int        life        Number of periods over which the asset is depreciated. (Sometimes called the useful life of the asset)  
     *    @param    int        period        The period for which you want to calculate the depreciation. Period must use the same units as life.  
     *    @param    float    month        Number of months in the first year. If month is omitted, it defaults to 12.  
    public static function DB($cost, $salvage, $life, $period, $month= 12) {  
        $cost        = (float)  self::flattenSingleValue($cost);  
        $salvage    = (float)  self::flattenSingleValue($salvage);  
        $life        = (int)  self::flattenSingleValue($life);  
        $period        = (int)  self::flattenSingleValue($period);  
        $month        = (int)  self::flattenSingleValue($month);  
            } elseif (($cost <  0) ||  (($salvage /  $cost) <  0) ||  ($life <=  0) ||  ($period <  1) ||  ($month <  1)) {  
                return self::$_errorCodes['num'];  
            //    Set Fixed Depreciation Rate  
            $fixedDepreciationRate =  1 -  pow(($salvage /  $cost), (1 /  $life));  
            $fixedDepreciationRate =  round($fixedDepreciationRate, 3);  
            //    Loop through each period calculating the depreciation  
            $previousDepreciation =  0;  
            for ($per =  1; $per <=  $period; ++ $per) {  
                    $depreciation =  $cost *  $fixedDepreciationRate *  $month /  12;  
                } elseif ($per ==  ($life +  1)) {  
                    $depreciation =  ($cost -  $previousDepreciation) *  $fixedDepreciationRate *  (12 -  $month) /  12;  
                    $depreciation =  ($cost -  $previousDepreciation) *  $fixedDepreciationRate;  
                $previousDepreciation +=  $depreciation;  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC) {  
                $depreciation =  round($depreciation,2);  
        return self::$_errorCodes['value'];  
     *    Returns the depreciation of an asset for a specified period using the double-declining balance method or some other method you specify.  
     *    @param    float    cost        Initial cost of the asset.  
     *    @param    float    salvage        Value at the end of the depreciation. (Sometimes called the salvage value of the asset)  
     *    @param    int        life        Number of periods over which the asset is depreciated. (Sometimes called the useful life of the asset)  
     *    @param    int        period        The period for which you want to calculate the depreciation. Period must use the same units as life.  
     *    @param    float    factor        The rate at which the balance declines.  
     *                                 If factor is omitted, it is assumed to be 2 (the double-declining balance method).  
    public static function DDB($cost, $salvage, $life, $period, $factor= 2.0) {  
        $cost        = (float)  self::flattenSingleValue($cost);  
        $salvage    = (float)  self::flattenSingleValue($salvage);  
        $life        = (int)  self::flattenSingleValue($life);  
        $period        = (int)  self::flattenSingleValue($period);  
        $factor        = (float)  self::flattenSingleValue($factor);  
            if (($cost <=  0) ||  (($salvage /  $cost) <  0) ||  ($life <=  0) ||  ($period <  1) ||  ($factor <=  0.0) ||  ($period >  $life)) {  
                return self::$_errorCodes['num'];  
            //    Set Fixed Depreciation Rate  
            $fixedDepreciationRate =  1 -  pow(($salvage /  $cost), (1 /  $life));  
            $fixedDepreciationRate =  round($fixedDepreciationRate, 3);  
            //    Loop through each period calculating the depreciation  
            $previousDepreciation =  0;  
            for ($per =  1; $per <=  $period; ++ $per) {  
                $depreciation =  min( ($cost -  $previousDepreciation) *  ($factor /  $life), ($cost -  $salvage -  $previousDepreciation) );  
                $previousDepreciation +=  $depreciation;  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC) {  
                $depreciation =  round($depreciation,2);  
        return self::$_errorCodes['value'];  
    private static function _daysPerYear($year,$basis) {  
                if (self::_isLeapYear($year)) {  
                return self::$_errorCodes['num'];  
    }    //    function _daysPerYear()  
     *    Returns the discount rate for a security.  
     *    @param    mixed    issue        The security's issue date.  
     *    @param    mixed    firstinter    The security's first interest date.  
     *    @param    mixed    settlement    The security's settlement date.  
     *    @param    float    rate        The security's annual coupon rate.  
     *    @param    float    par            The security's par value.  
     *    @param    int        basis        The type of day count to use.  
     *                                         0 or omitted    US (NASD) 30/360  
    public static function ACCRINT($issue, $firstinter, $settlement, $rate, $par= 1000, $frequency= 1, $basis= 0) {  
        $issue        =  self::flattenSingleValue($issue);  
        $firstinter    =  self::flattenSingleValue($firstinter);  
        $settlement    =  self::flattenSingleValue($settlement);  
        $rate        = (float)  self::flattenSingleValue($rate);  
        $par        =  (is_null($par))        ?  1000 :    (float)  self::flattenSingleValue($par);  
        $frequency    =  (is_null($frequency))    ?  1    :         (int)  self::flattenSingleValue($frequency);  
        $basis        =  (is_null($basis))        ?  0    :        (int)  self::flattenSingleValue($basis);  
            if (($rate <=  0) ||  ($par <=  0)) {  
                return self::$_errorCodes['num'];  
            $daysBetweenIssueAndSettlement =  self::YEARFRAC($issue, $settlement, $basis);  
            if (!is_numeric($daysBetweenIssueAndSettlement)) {  
                return $daysBetweenIssueAndSettlement;  
            return $par *  $rate *  $daysBetweenIssueAndSettlement;  
        return self::$_errorCodes['value'];  
     *    Returns the discount rate for a security.  
     *    @param    mixed    issue        The security's issue date.  
     *    @param    mixed    settlement    The security's settlement date.  
     *    @param    float    rate        The security's annual coupon rate.  
     *    @param    float    par            The security's par value.  
     *    @param    int        basis        The type of day count to use.  
     *                                         0 or omitted    US (NASD) 30/360  
    public static function ACCRINTM($issue, $settlement, $rate, $par= 1000, $basis= 0) {  
        $issue        =  self::flattenSingleValue($issue);  
        $settlement    =  self::flattenSingleValue($settlement);  
        $rate        = (float)  self::flattenSingleValue($rate);  
        $par        =  (is_null($par))    ?  1000 :    (float)  self::flattenSingleValue($par);  
        $basis        =  (is_null($basis))    ?  0 :        (int)  self::flattenSingleValue($basis);  
            if (($rate <=  0) ||  ($par <=  0)) {  
                return self::$_errorCodes['num'];  
            $daysBetweenIssueAndSettlement =  self::YEARFRAC($issue, $settlement, $basis);  
            if (!is_numeric($daysBetweenIssueAndSettlement)) {  
                return $daysBetweenIssueAndSettlement;  
            return $par *  $rate *  $daysBetweenIssueAndSettlement;  
        return self::$_errorCodes['value'];  
    public static function AMORDEGRC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis= 0) {  
        $cost            =  self::flattenSingleValue($cost);  
        $purchased        =  self::flattenSingleValue($purchased);  
        $firstPeriod    =  self::flattenSingleValue($firstPeriod);  
        $salvage        =  self::flattenSingleValue($salvage);  
        $period            =  floor(self::flattenSingleValue($period));  
        $rate            =  self::flattenSingleValue($rate);  
        $basis            =  (is_null($basis))    ?  0 :    (int)  self::flattenSingleValue($basis);  
        } elseif ($fUsePer <  5.0) {  
        } elseif ($fUsePer <=  6.0) {  
//        $fNRate = floor((self::YEARFRAC($purchased, $firstPeriod, $basis) * $rate * $cost) + 0.5);  
        $fNRate =  round(self::YEARFRAC($purchased, $firstPeriod, $basis) *  $rate *  $cost,0);  
        $fRest =  $cost -  $salvage;  
        for ($n =  0; $n <  $period; ++ $n) {  
//            $fNRate = floor(($rate * $cost) + 0.5);  
            $fNRate =  round($rate *  $cost,0);  
//                              return floor(($cost * 0.5) + 0.5);  
                              return round($cost *  0.5,0);  
    }    //    function AMORDEGRC()  
    public static function AMORLINC($cost, $purchased, $firstPeriod, $salvage, $period, $rate, $basis= 0) {  
        $cost            =  self::flattenSingleValue($cost);  
        $purchased        =  self::flattenSingleValue($purchased);  
        $firstPeriod    =  self::flattenSingleValue($firstPeriod);  
        $salvage        =  self::flattenSingleValue($salvage);  
        $period            =  self::flattenSingleValue($period);  
        $rate            =  self::flattenSingleValue($rate);  
        $basis            =  (is_null($basis))    ?  0 :    (int)  self::flattenSingleValue($basis);  
        $fOneRate =  $cost *  $rate;  
        $fCostDelta =  $cost -  $salvage;  
        //    Note, quirky variation for leap years on the YEARFRAC for this function  
        $purchasedYear =  self::YEAR($purchased);  
        $yearFrac =  self::YEARFRAC($purchased, $firstPeriod, $basis);  
        if (($basis ==  1) &&  ($yearFrac <  1) &&  (self::_isLeapYear($purchasedYear))) {  
        $f0Rate =  $yearFrac *  $rate *  $cost;  
        $nNumOfFullPeriods =  intval(($cost -  $salvage -  $f0Rate) /  $fOneRate);  
        } elseif ($period <=  $nNumOfFullPeriods) {  
        } elseif ($period ==  ($nNumOfFullPeriods +  1)) {  
            return ($fCostDelta -  $fOneRate *  $nNumOfFullPeriods -  $f0Rate);  
    private static function _lastDayOfMonth($testDate) {  
        return ($date->format('d') ==  1);  
    }    //    function _lastDayOfMonth()  
    private static function _firstDayOfMonth($testDate) {  
        return ($date->format('d') ==  1);  
    }    //    function _lastDayOfMonth()  
    private static function _coupFirstPeriodDate($settlement, $maturity, $frequency, $next) {  
        $months =  12 /  $frequency;  
        $eom =  self::_lastDayOfMonth($result);  
            $result->modify('-'. $months. ' months');  
            $result->modify('+'. $months. ' months');  
            $result->modify('-1 day');  
    }    //    function _coupFirstPeriodDate()  
    private static function _validFrequency($frequency) {  
        if (($frequency ==  1) ||  ($frequency ==  2) ||  ($frequency ==  4)) {  
        if ((self::$compatibilityMode ==  self::COMPATIBILITY_GNUMERIC) &&   
            (($frequency ==  6) ||  ($frequency ==  12))) {  
    }    //    function _validFrequency()  
    public static function COUPDAYS($settlement, $maturity, $frequency, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $frequency    = (int)  self::flattenSingleValue($frequency);  
        $basis        =  (is_null($basis))    ?  0 :    (int)  self::flattenSingleValue($basis);  
        if (is_string($settlement =  self::_getDateValue($settlement))) {  
            return self::$_errorCodes['value'];  
        if (is_string($maturity =  self::_getDateValue($maturity))) {  
            return self::$_errorCodes['value'];  
        if (($settlement >  $maturity) ||   
            (!self::_validFrequency($frequency)) ||   
            (($basis <  0) ||  ($basis >  4))) {  
            return self::$_errorCodes['num'];  
                        $daysPerYear =  self::_daysPerYear(self::YEAR($maturity),$basis);  
                        return ($daysPerYear /  $frequency);  
                        $prev =  self::_coupFirstPeriodDate($settlement, $maturity, $frequency, False);  
                        $next =  self::_coupFirstPeriodDate($settlement, $maturity, $frequency, True);  
            default:  // US (NASD) 30/360, Actual/360 or European 30/360  
        return self::$_errorCodes['value'];  
    public static function COUPDAYBS($settlement, $maturity, $frequency, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $frequency    = (int)  self::flattenSingleValue($frequency);  
        $basis        =  (is_null($basis))    ?  0 :    (int)  self::flattenSingleValue($basis);  
        if (is_string($settlement =  self::_getDateValue($settlement))) {  
            return self::$_errorCodes['value'];  
        if (is_string($maturity =  self::_getDateValue($maturity))) {  
            return self::$_errorCodes['value'];  
        if (($settlement >  $maturity) ||   
            (!self::_validFrequency($frequency)) ||   
            (($basis <  0) ||  ($basis >  4))) {  
            return self::$_errorCodes['num'];  
        $daysPerYear =  self::_daysPerYear(self::YEAR($settlement),$basis);  
        $prev =  self::_coupFirstPeriodDate($settlement, $maturity, $frequency, False);  
        return self::YEARFRAC($prev, $settlement, $basis) *  $daysPerYear;  
    }    //    function COUPDAYBS()  
    public static function COUPDAYSNC($settlement, $maturity, $frequency, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $frequency    = (int)  self::flattenSingleValue($frequency);  
        $basis        =  (is_null($basis))    ?  0 :    (int)  self::flattenSingleValue($basis);  
        if (is_string($settlement =  self::_getDateValue($settlement))) {  
            return self::$_errorCodes['value'];  
        if (is_string($maturity =  self::_getDateValue($maturity))) {  
            return self::$_errorCodes['value'];  
        if (($settlement >  $maturity) ||   
            (!self::_validFrequency($frequency)) ||   
            (($basis <  0) ||  ($basis >  4))) {  
            return self::$_errorCodes['num'];  
        $daysPerYear =  self::_daysPerYear(self::YEAR($settlement),$basis);  
        $next =  self::_coupFirstPeriodDate($settlement, $maturity, $frequency, True);  
        return self::YEARFRAC($settlement, $next, $basis) *  $daysPerYear;  
    }    //    function COUPDAYSNC()  
    public static function COUPNCD($settlement, $maturity, $frequency, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $frequency    = (int)  self::flattenSingleValue($frequency);  
        $basis        =  (is_null($basis))    ?  0 :    (int)  self::flattenSingleValue($basis);  
        if (is_string($settlement =  self::_getDateValue($settlement))) {  
            return self::$_errorCodes['value'];  
        if (is_string($maturity =  self::_getDateValue($maturity))) {  
            return self::$_errorCodes['value'];  
        if (($settlement >  $maturity) ||   
            (!self::_validFrequency($frequency)) ||   
            (($basis <  0) ||  ($basis >  4))) {  
            return self::$_errorCodes['num'];  
        return self::_coupFirstPeriodDate($settlement, $maturity, $frequency, True);  
    public static function COUPPCD($settlement, $maturity, $frequency, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $frequency    = (int)  self::flattenSingleValue($frequency);  
        $basis        =  (is_null($basis))    ?  0 :    (int)  self::flattenSingleValue($basis);  
        if (is_string($settlement =  self::_getDateValue($settlement))) {  
            return self::$_errorCodes['value'];  
        if (is_string($maturity =  self::_getDateValue($maturity))) {  
            return self::$_errorCodes['value'];  
        if (($settlement >  $maturity) ||   
            (!self::_validFrequency($frequency)) ||   
            (($basis <  0) ||  ($basis >  4))) {  
            return self::$_errorCodes['num'];  
        return self::_coupFirstPeriodDate($settlement, $maturity, $frequency, False);  
    public static function COUPNUM($settlement, $maturity, $frequency, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $frequency    = (int)  self::flattenSingleValue($frequency);  
        $basis        =  (is_null($basis))    ?  0 :    (int)  self::flattenSingleValue($basis);  
        if (is_string($settlement =  self::_getDateValue($settlement))) {  
            return self::$_errorCodes['value'];  
        if (is_string($maturity =  self::_getDateValue($maturity))) {  
            return self::$_errorCodes['value'];  
        if (($settlement >  $maturity) ||   
            (!self::_validFrequency($frequency)) ||   
            (($basis <  0) ||  ($basis >  4))) {  
            return self::$_errorCodes['num'];  
        $settlement =  self::_coupFirstPeriodDate($settlement, $maturity, $frequency, True);  
        $daysBetweenSettlementAndMaturity =  self::YEARFRAC($settlement, $maturity, $basis) *  365;  
            case 1:  // annual payments  
                    return ceil($daysBetweenSettlementAndMaturity /  360);  
                    return ceil($daysBetweenSettlementAndMaturity /  180);  
                    return ceil($daysBetweenSettlementAndMaturity /  90);  
                    return ceil($daysBetweenSettlementAndMaturity /  60);  
                    return ceil($daysBetweenSettlementAndMaturity /  30);  
        return self::$_errorCodes['value'];  
    public static function PRICE($settlement, $maturity, $rate, $yield, $redemption, $frequency, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $rate        = (float)  self::flattenSingleValue($rate);  
        $yield        = (float)  self::flattenSingleValue($yield);  
        $redemption    = (float)  self::flattenSingleValue($redemption);  
        $frequency    = (int)  self::flattenSingleValue($frequency);  
        $basis        =  (is_null($basis))    ?  0 :    (int)  self::flattenSingleValue($basis);  
        if (is_string($settlement =  self::_getDateValue($settlement))) {  
            return self::$_errorCodes['value'];  
        if (is_string($maturity =  self::_getDateValue($maturity))) {  
            return self::$_errorCodes['value'];  
        if (($settlement >  $maturity) ||   
            (!self::_validFrequency($frequency)) ||   
            (($basis <  0) ||  ($basis >  4))) {  
            return self::$_errorCodes['num'];  
        $dsc =  self::COUPDAYSNC($settlement, $maturity, $frequency, $basis);  
        $e =  self::COUPDAYS($settlement, $maturity, $frequency, $basis);  
        $n =  self::COUPNUM($settlement, $maturity, $frequency, $basis);  
        $a =  self::COUPDAYBS($settlement, $maturity, $frequency, $basis);  
        $baseYF    =  1.0 +  ($yield /  $frequency);  
        $rfp    =  100 *  ($rate /  $frequency);  
        $result =  $redemption /  pow($baseYF, (-- $n +  $de));  
        for($k =  0; $k <=  $n; ++ $k) {  
            $result +=  $rfp /  (pow($baseYF, ($k +  $de)));  
        $result -=  $rfp *  ($a /  $e);  
     *    Returns the discount rate for a security.  
     *    @param    mixed    settlement    The security's settlement date.  
     *                                 The security settlement date is the date after the issue date when the security is traded to the buyer.  
     *    @param    mixed    maturity    The security's maturity date.  
     *                                 The maturity date is the date when the security expires.  
     *    @param    int        price        The security's price per $100 face value.  
     *    @param    int        redemption    the security's redemption value per $100 face value.  
     *    @param    int        basis        The type of day count to use.  
     *                                         0 or omitted    US (NASD) 30/360  
    public static function DISC($settlement, $maturity, $price, $redemption, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $price        = (float)  self::flattenSingleValue($price);  
        $redemption    = (float)  self::flattenSingleValue($redemption);  
        $basis        = (int)  self::flattenSingleValue($basis);  
            if (($price <=  0) ||  ($redemption <=  0)) {  
                return self::$_errorCodes['num'];  
            $daysBetweenSettlementAndMaturity =  self::YEARFRAC($settlement, $maturity, $basis);  
            if (!is_numeric($daysBetweenSettlementAndMaturity)) {  
                return $daysBetweenSettlementAndMaturity;  
            return ((1 -  $price /  $redemption) /  $daysBetweenSettlementAndMaturity);  
        return self::$_errorCodes['value'];  
     *    Returns the price per $100 face value of a discounted security.  
     *    @param    mixed    settlement    The security's settlement date.  
     *                                 The security settlement date is the date after the issue date when the security is traded to the buyer.  
     *    @param    mixed    maturity    The security's maturity date.  
     *                                 The maturity date is the date when the security expires.  
     *    @param    int        discount    The security's discount rate.  
     *    @param    int        redemption    The security's redemption value per $100 face value.  
     *    @param    int        basis        The type of day count to use.  
     *                                         0 or omitted    US (NASD) 30/360  
    public static function PRICEDISC($settlement, $maturity, $discount, $redemption, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $discount    = (float)  self::flattenSingleValue($discount);  
        $redemption    = (float)  self::flattenSingleValue($redemption);  
        $basis        = (int)  self::flattenSingleValue($basis);  
            if (($discount <=  0) ||  ($redemption <=  0)) {  
                return self::$_errorCodes['num'];  
            $daysBetweenSettlementAndMaturity =  self::YEARFRAC($settlement, $maturity, $basis);  
            if (!is_numeric($daysBetweenSettlementAndMaturity)) {  
                return $daysBetweenSettlementAndMaturity;  
            return $redemption *  (1 -  $discount *  $daysBetweenSettlementAndMaturity);  
        return self::$_errorCodes['value'];  
    }    //    function PRICEDISC()  
     *    Returns the price per $100 face value of a security that pays interest at maturity.  
     *    @param    mixed    settlement    The security's settlement date.  
     *                                 The security's settlement date is the date after the issue date when the security is traded to the buyer.  
     *    @param    mixed    maturity    The security's maturity date.  
     *                                 The maturity date is the date when the security expires.  
     *    @param    mixed    issue        The security's issue date.  
     *    @param    int        rate        The security's interest rate at date of issue.  
     *    @param    int        yield        The security's annual yield.  
     *    @param    int        basis        The type of day count to use.  
     *                                         0 or omitted    US (NASD) 30/360  
    public static function PRICEMAT($settlement, $maturity, $issue, $rate, $yield, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $issue        =  self::flattenSingleValue($issue);  
        $rate        =  self::flattenSingleValue($rate);  
        $yield        =  self::flattenSingleValue($yield);  
        $basis        = (int)  self::flattenSingleValue($basis);  
            if (($rate <=  0) ||  ($yield <=  0)) {  
                return self::$_errorCodes['num'];  
            $daysPerYear =  self::_daysPerYear(self::YEAR($settlement),$basis);  
            $daysBetweenIssueAndSettlement =  self::YEARFRAC($issue, $settlement, $basis);  
            if (!is_numeric($daysBetweenIssueAndSettlement)) {  
                return $daysBetweenIssueAndSettlement;  
            $daysBetweenIssueAndSettlement *=  $daysPerYear;  
            $daysBetweenIssueAndMaturity =  self::YEARFRAC($issue, $maturity, $basis);  
                return $daysBetweenIssueAndMaturity;  
            $daysBetweenIssueAndMaturity *=  $daysPerYear;  
            $daysBetweenSettlementAndMaturity =  self::YEARFRAC($settlement, $maturity, $basis);  
            if (!is_numeric($daysBetweenSettlementAndMaturity)) {  
                return $daysBetweenSettlementAndMaturity;  
            $daysBetweenSettlementAndMaturity *=  $daysPerYear;  
            return ((100 +  (($daysBetweenIssueAndMaturity /  $daysPerYear) *  $rate *  100)) /   
                   (1 +  (($daysBetweenSettlementAndMaturity /  $daysPerYear) *  $yield)) -   
                   (($daysBetweenIssueAndSettlement /  $daysPerYear) *  $rate *  100));  
        return self::$_errorCodes['value'];  
     *    Returns the price per $100 face value of a discounted security.  
     *    @param    mixed    settlement    The security's settlement date.  
     *                                 The security settlement date is the date after the issue date when the security is traded to the buyer.  
     *    @param    mixed    maturity    The security's maturity date.  
     *                                 The maturity date is the date when the security expires.  
     *    @param    int        investment    The amount invested in the security.  
     *    @param    int        discount    The security's discount rate.  
     *    @param    int        basis        The type of day count to use.  
     *                                         0 or omitted    US (NASD) 30/360  
    public static function RECEIVED($settlement, $maturity, $investment, $discount, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $investment    = (float)  self::flattenSingleValue($investment);  
        $discount    = (float)  self::flattenSingleValue($discount);  
        $basis        = (int)  self::flattenSingleValue($basis);  
            if (($investment <=  0) ||  ($discount <=  0)) {  
                return self::$_errorCodes['num'];  
            $daysBetweenSettlementAndMaturity =  self::YEARFRAC($settlement, $maturity, $basis);  
            if (!is_numeric($daysBetweenSettlementAndMaturity)) {  
                return $daysBetweenSettlementAndMaturity;  
            return $investment /  ( 1 -  ($discount *  $daysBetweenSettlementAndMaturity));  
        return self::$_errorCodes['value'];  
     *    Returns the interest rate for a fully invested security.  
     *    @param    mixed    settlement    The security's settlement date.  
     *                                 The security settlement date is the date after the issue date when the security is traded to the buyer.  
     *    @param    mixed    maturity    The security's maturity date.  
     *                                 The maturity date is the date when the security expires.  
     *    @param    int        investment    The amount invested in the security.  
     *    @param    int        redemption    The amount to be received at maturity.  
     *    @param    int        basis        The type of day count to use.  
     *                                         0 or omitted    US (NASD) 30/360  
    public static function INTRATE($settlement, $maturity, $investment, $redemption, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $investment    = (float)  self::flattenSingleValue($investment);  
        $redemption    = (float)  self::flattenSingleValue($redemption);  
        $basis        = (int)  self::flattenSingleValue($basis);  
            if (($investment <=  0) ||  ($redemption <=  0)) {  
                return self::$_errorCodes['num'];  
            $daysBetweenSettlementAndMaturity =  self::YEARFRAC($settlement, $maturity, $basis);  
            if (!is_numeric($daysBetweenSettlementAndMaturity)) {  
                return $daysBetweenSettlementAndMaturity;  
            return (($redemption /  $investment) -  1) /  ($daysBetweenSettlementAndMaturity);  
        return self::$_errorCodes['value'];  
     *    Returns the bond-equivalent yield for a Treasury bill.  
     *    @param    mixed    settlement    The Treasury bill's settlement date.  
     *                                 The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.  
     *    @param    mixed    maturity    The Treasury bill's maturity date.  
     *                                 The maturity date is the date when the Treasury bill expires.  
     *    @param    int        discount    The Treasury bill's discount rate.  
    public static function TBILLEQ($settlement, $maturity, $discount) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $discount    =  self::flattenSingleValue($discount);  
        //    Use TBILLPRICE for validation  
        $testValue =  self::TBILLPRICE($settlement, $maturity, $discount);  
        if (is_string($maturity =  self::_getDateValue($maturity))) {  
            return self::$_errorCodes['value'];  
        if (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE) {  
            $daysBetweenSettlementAndMaturity =  self::YEARFRAC($settlement, $maturity) *  360;  
            $daysBetweenSettlementAndMaturity =  (self::_getDateValue($maturity) -  self::_getDateValue($settlement));  
        return (365 *  $discount) /  (360 -  $discount *  $daysBetweenSettlementAndMaturity);  
     *    Returns the yield for a Treasury bill.  
     *    @param    mixed    settlement    The Treasury bill's settlement date.  
     *                                 The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.  
     *    @param    mixed    maturity    The Treasury bill's maturity date.  
     *                                 The maturity date is the date when the Treasury bill expires.  
     *    @param    int        discount    The Treasury bill's discount rate.  
    public static function TBILLPRICE($settlement, $maturity, $discount) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $discount    =  self::flattenSingleValue($discount);  
        if (is_string($maturity =  self::_getDateValue($maturity))) {  
            return self::$_errorCodes['value'];  
        if (is_numeric($discount)) {  
                return self::$_errorCodes['num'];  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE) {  
                $daysBetweenSettlementAndMaturity =  self::YEARFRAC($settlement, $maturity) *  360;  
                if (!is_numeric($daysBetweenSettlementAndMaturity)) {  
                    return $daysBetweenSettlementAndMaturity;  
                $daysBetweenSettlementAndMaturity =  (self::_getDateValue($maturity) -  self::_getDateValue($settlement));  
            if ($daysBetweenSettlementAndMaturity >  360) {  
                return self::$_errorCodes['num'];  
            $price =  100 *  (1 -  (($discount *  $daysBetweenSettlementAndMaturity) /  360));  
                return self::$_errorCodes['num'];  
        return self::$_errorCodes['value'];  
    }    //    function TBILLPRICE()  
     *    Returns the yield for a Treasury bill.  
     *    @param    mixed    settlement    The Treasury bill's settlement date.  
     *                                 The Treasury bill's settlement date is the date after the issue date when the Treasury bill is traded to the buyer.  
     *    @param    mixed    maturity    The Treasury bill's maturity date.  
     *                                 The maturity date is the date when the Treasury bill expires.  
     *    @param    int        price        The Treasury bill's price per $100 face value.  
    public static function TBILLYIELD($settlement, $maturity, $price) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $price        =  self::flattenSingleValue($price);  
                return self::$_errorCodes['num'];  
            if (self::$compatibilityMode ==  self::COMPATIBILITY_OPENOFFICE) {  
                $daysBetweenSettlementAndMaturity =  self::YEARFRAC($settlement, $maturity) *  360;  
                if (!is_numeric($daysBetweenSettlementAndMaturity)) {  
                    return $daysBetweenSettlementAndMaturity;  
                $daysBetweenSettlementAndMaturity =  (self::_getDateValue($maturity) -  self::_getDateValue($settlement));  
            if ($daysBetweenSettlementAndMaturity >  360) {  
                return self::$_errorCodes['num'];  
            return ((100 -  $price) /  $price) *  (360 /  $daysBetweenSettlementAndMaturity);  
        return self::$_errorCodes['value'];  
    }    //    function TBILLYIELD()  
     * Returns the straight-line depreciation of an asset for one period  
     * @param    cost        Initial cost of the asset  
     * @param    salvage        Value at the end of the depreciation  
     * @param    life        Number of periods over which the asset is depreciated  
    public static function SLN($cost, $salvage, $life) {  
        $cost        =  self::flattenSingleValue($cost);  
        $salvage    =  self::flattenSingleValue($salvage);  
        $life        =  self::flattenSingleValue($life);  
                return self::$_errorCodes['num'];  
            return ($cost -  $salvage) /  $life;  
        return self::$_errorCodes['value'];  
     *    Returns the annual yield of a security that pays interest at maturity.  
     *    @param    mixed    settlement    The security's settlement date.  
     *                                 The security's settlement date is the date after the issue date when the security is traded to the buyer.  
     *    @param    mixed    maturity    The security's maturity date.  
     *                                 The maturity date is the date when the security expires.  
     *    @param    mixed    issue        The security's issue date.  
     *    @param    int        rate        The security's interest rate at date of issue.  
     *    @param    int        price        The security's price per $100 face value.  
     *    @param    int        basis        The type of day count to use.  
     *                                         0 or omitted    US (NASD) 30/360  
    public static function YIELDMAT($settlement, $maturity, $issue, $rate, $price, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $issue        =  self::flattenSingleValue($issue);  
        $rate        =  self::flattenSingleValue($rate);  
        $price        =  self::flattenSingleValue($price);  
        $basis        = (int)  self::flattenSingleValue($basis);  
            if (($rate <=  0) ||  ($price <=  0)) {  
                return self::$_errorCodes['num'];  
            $daysPerYear =  self::_daysPerYear(self::YEAR($settlement),$basis);  
            $daysBetweenIssueAndSettlement =  self::YEARFRAC($issue, $settlement, $basis);  
            if (!is_numeric($daysBetweenIssueAndSettlement)) {  
                return $daysBetweenIssueAndSettlement;  
            $daysBetweenIssueAndSettlement *=  $daysPerYear;  
            $daysBetweenIssueAndMaturity =  self::YEARFRAC($issue, $maturity, $basis);  
                return $daysBetweenIssueAndMaturity;  
            $daysBetweenIssueAndMaturity *=  $daysPerYear;  
            $daysBetweenSettlementAndMaturity =  self::YEARFRAC($settlement, $maturity, $basis);  
            if (!is_numeric($daysBetweenSettlementAndMaturity)) {  
                return $daysBetweenSettlementAndMaturity;  
            $daysBetweenSettlementAndMaturity *=  $daysPerYear;  
            return ((1 +  (($daysBetweenIssueAndMaturity /  $daysPerYear) *  $rate) -  (($price /  100) +  (($daysBetweenIssueAndSettlement /  $daysPerYear) *  $rate))) /   
                   (($price /  100) +  (($daysBetweenIssueAndSettlement /  $daysPerYear) *  $rate))) *   
                   ($daysPerYear /  $daysBetweenSettlementAndMaturity);  
        return self::$_errorCodes['value'];  
     *    Returns the annual yield of a security that pays interest at maturity.  
     *    @param    mixed    settlement    The security's settlement date.  
     *                                 The security's settlement date is the date after the issue date when the security is traded to the buyer.  
     *    @param    mixed    maturity    The security's maturity date.  
     *                                 The maturity date is the date when the security expires.  
     *    @param    int        price        The security's price per $100 face value.  
     *    @param    int        redemption    The security's redemption value per $100 face value.  
     *    @param    int        basis        The type of day count to use.  
     *                                         0 or omitted    US (NASD) 30/360  
    public static function YIELDDISC($settlement, $maturity, $price, $redemption, $basis= 0) {  
        $settlement    =  self::flattenSingleValue($settlement);  
        $maturity    =  self::flattenSingleValue($maturity);  
        $price        =  self::flattenSingleValue($price);  
        $redemption    =  self::flattenSingleValue($redemption);  
        $basis        = (int)  self::flattenSingleValue($basis);  
            if (($price <=  0) ||  ($redemption <=  0)) {  
                return self::$_errorCodes['num'];  
            $daysPerYear =  self::_daysPerYear(self::YEAR($settlement),$basis);  
            $daysBetweenSettlementAndMaturity =  self::YEARFRAC($settlement, $maturity,$basis);  
            if (!is_numeric($daysBetweenSettlementAndMaturity)) {  
                return $daysBetweenSettlementAndMaturity;  
            $daysBetweenSettlementAndMaturity *=  $daysPerYear;  
            return (($redemption -  $price) /  $price) *  ($daysPerYear /  $daysBetweenSettlementAndMaturity);  
        return self::$_errorCodes['value'];  
    }    //    function YIELDDISC()  
     *    Creates a cell address as text, given specified row and column numbers.  
     *    @param    row                Row number to use in the cell reference  
     *    @param    column            Column number to use in the cell reference  
     *    @param    relativity        Flag indicating the type of reference to return  
     *                                 2                Absolute row; relative column  
     *                                 3                Relative row; absolute column  
     *    @param    referenceStyle    A logical value that specifies the A1 or R1C1 reference style.  
     *                                 TRUE or omitted        CELL_ADDRESS returns an A1-style reference  
     *                                 FALSE                CELL_ADDRESS returns an R1C1-style reference  
     *    @param    sheetText        Optional Name of worksheet to use  
    public static function CELL_ADDRESS($row, $column, $relativity= 1, $referenceStyle= True, $sheetText= '') {  
        $row        =  self::flattenSingleValue($row);  
        $column        =  self::flattenSingleValue($column);  
        $relativity    =  self::flattenSingleValue($relativity);  
        $sheetText    =  self::flattenSingleValue($sheetText);  
        if (($row <  1) ||  ($column <  1)) {  
            return self::$_errorCodes['value'];  
            if (strpos($sheetText,' ') !==  False) { $sheetText =  "'". $sheetText. "'"; }  
        if ((!is_bool($referenceStyle)) ||  $referenceStyle) {  
            $rowRelative =  $columnRelative =  '$';  
            if (($relativity ==  2) ||  ($relativity ==  4)) { $columnRelative =  ''; }  
            if (($relativity ==  3) ||  ($relativity ==  4)) { $rowRelative =  ''; }  
            return $sheetText. $columnRelative. $column. $rowRelative. $row;  
            if (($relativity ==  2) ||  ($relativity ==  4)) { $column =  '['. $column. ']'; }  
            if (($relativity ==  3) ||  ($relativity ==  4)) { $row =  '['. $row. ']'; }  
            return $sheetText. 'R'. $row. 'C'. $column;  
    }    //    function CELL_ADDRESS()  
     *    Returns the column number of the given cell reference  
     *    If the cell reference is a range of cells, COLUMN returns the column numbers of each column in the reference as a horizontal array.  
     *    If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the  
     *        reference of the cell in which the COLUMN function appears; otherwise this function returns 0.  
     *    @param    cellAddress        A reference to a range of cells for which you want the column numbers  
     *    @return    integer or array of integer  
    public static function COLUMN($cellAddress= Null) {  
        if (is_null($cellAddress) ||  trim($cellAddress) ===  '') { return 0; }  
            foreach($cellAddress as $columnKey =>  $value) {  
            if (strpos($cellAddress,'!') !==  false) {  
                list ($sheet,$cellAddress) =  explode('!',$cellAddress); 
            if (strpos($cellAddress,':') !==  false) {  
                list ($startAddress,$endAddress) =  explode(':',$cellAddress); 
                } while ($startAddress++ !=  $endAddress);  
     *    Returns the number of columns in an array or reference.  
     *    @param    cellAddress        An array or array formula, or a reference to a range of cells for which you want the number of columns  
    public static function COLUMNS($cellAddress= Null) {  
        if (is_null($cellAddress) ||  $cellAddress ===  '') {  
            return self::$_errorCodes['value'];  
        $x =  array_keys($cellAddress);  
     *    Returns the row number of the given cell reference  
     *    If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference as a vertical array.  
     *    If cell reference is omitted, and the function is being called through the calculation engine, then it is assumed to be the  
     *        reference of the cell in which the ROW function appears; otherwise this function returns 0.  
     *    @param    cellAddress        A reference to a range of cells for which you want the row numbers  
     *    @return    integer or array of integer  
    public static function ROW($cellAddress= Null) {  
        if (is_null($cellAddress) ||  trim($cellAddress) ===  '') { return 0; }  
            foreach($cellAddress as $columnKey =>  $rowValue) {  
                foreach($rowValue as $rowKey =>  $cellValue) {  
            if (strpos($cellAddress,'!') !==  false) {  
                list ($sheet,$cellAddress) =  explode('!',$cellAddress); 
            if (strpos($cellAddress,':') !==  false) {  
                list ($startAddress,$endAddress) =  explode(':',$cellAddress); 
                    $returnValue[][] = (integer)  $startAddress;  
                } while ($startAddress++ !=  $endAddress);  
                list ($cellAddress) =  explode(':',$cellAddress); 
     *    Returns the number of rows in an array or reference.  
     *    @param    cellAddress        An array or array formula, or a reference to a range of cells for which you want the number of rows  
    public static function ROWS($cellAddress= Null) {  
        if (is_null($cellAddress) ||  $cellAddress ===  '') {  
            return self::$_errorCodes['value'];  
        $i =  array_keys($cellAddress);  
     *    Returns the number of rows in an array or reference.  
     *    @param    cellAddress        An array or array formula, or a reference to a range of cells for which you want the number of rows  
    public static function INDIRECT($cellAddress= Null, PHPExcel_Cell $pCell =  null) {  
        $cellAddress    =  self::flattenSingleValue($cellAddress);  
        if (is_null($cellAddress) ||  $cellAddress ===  '') {  
        $cellAddress1 =  $cellAddress;  
        if (strpos($cellAddress,':') !==  false) {  
            list ($cellAddress1,$cellAddress2) =  explode(':',$cellAddress); 
        if (strpos($cellAddress,'!') !==  false) {  
            list ($sheetName,$cellAddress) =  explode('!',$cellAddress); 
            $pSheet =  $pCell->getParent()->getParent()->getSheetByName($sheetName);  
            $pSheet =  $pCell->getParent();  
     *    Returns a reference to a range that is a specified number of rows and columns from a cell or range of cells.  
     *    The reference that is returned can be a single cell or a range of cells. You can specify the number of rows and  
     *    the number of columns to be returned.  
     *    @param    cellAddress        The reference from which you want to base the offset. Reference must refer to a cell or  
     *                                 range of adjacent cells; otherwise, OFFSET returns the #VALUE! error value.  
     *    @param    rows            The number of rows, up or down, that you want the upper-left cell to refer to.  
     *                                 Using 5 as the rows argument specifies that the upper-left cell in the reference is  
     *                                 five rows below reference. Rows can be positive (which means below the starting reference)  
     *                                 or negative (which means above the starting reference).  
     *    @param    cols            The number of columns, to the left or right, that you want the upper-left cell of the result  
     *                                 to refer to. Using 5 as the cols argument specifies that the upper-left cell in the  
     *                                 reference is five columns to the right of reference. Cols can be positive (which means  
     *                                 to the right of the starting reference) or negative (which means to the left of the  
     *    @param    height            The height, in number of rows, that you want the returned reference to be. Height must be a positive number.  
     *    @param    width            The width, in number of columns, that you want the returned reference to be. Width must be a positive number.  
     *    @return    string            A reference to a cell or range of cells  
    public static function OFFSET($cellAddress= Null,$rows= 0,$columns= 0,$height= null,$width= null) {  
        $rows        =  self::flattenSingleValue($rows);  
        $columns    =  self::flattenSingleValue($columns);  
        $height        =  self::flattenSingleValue($height);  
        $width        =  self::flattenSingleValue($width);  
        if ($cellAddress ==  Null) {  
            return self::$_errorCodes['reference'];  
        if (strpos($cellAddress,"!")) {  
            list ($sheetName,$cellAddress) =  explode("!",$cellAddress); 
        if (strpos($cellAddress,":")) {  
            list ($startCell,$endCell) =  explode(":",$cellAddress); 
            $startCell =  $endCell =  $cellAddress;  
        $startCellColumn +=  $columns;  
        if (($startCellRow <=  0) ||  ($startCellColumn <  0)) {  
            return self::$_errorCodes['reference'];  
        $endCellColumn =  PHPExcel_Cell::columnIndexFromString($endCellColumn) -  1;  
        if (($width !=  null) &&  (!is_object($width))) {  
            $endCellColumn =  $startCellColumn +  $width -  1;  
            $endCellColumn +=  $columns;  
        if (($height !=  null) &&  (!is_object($height))) {  
            $endCellRow =  $startCellRow +  $height -  1;  
        if (($endCellRow <=  0) ||  ($endCellColumn <  0)) {  
            return self::$_errorCodes['reference'];  
        $endCellColumn =  PHPExcel_Cell::stringFromColumnIndex($endCellColumn);  
        $cellAddress =  $startCellColumn. $startCellRow;  
        if (($startCellColumn !=  $endCellColumn) ||  ($startCellRow !=  $endCellRow)) {  
            $cellAddress .=  ':'. $endCellColumn. $endCellRow;  
        if ($sheetName !==  null) {  
            $pSheet =  $pCell->getParent()->getParent()->getSheetByName($sheetName);  
            $pSheet =  $pCell->getParent();  
    public static function CHOOSE() {  
        $chosenEntry =  self::flattenArray(array_shift($chooseArgs));  
        $entryCount =  count($chooseArgs) -  1;  
            return self::$_errorCodes['value'];  
        $chosenEntry =  floor($chosenEntry);  
        if (($chosenEntry <=  0) ||  ($chosenEntry >  $entryCount)) {  
            return self::$_errorCodes['value'];  
        if (is_array($chooseArgs[$chosenEntry])) {  
            return self::flattenArray($chooseArgs[$chosenEntry]);  
            return $chooseArgs[$chosenEntry];  
     *    The MATCH function searches for a specified item in a range of cells  
     *    @param    lookup_value    The value that you want to match in lookup_array  
     *    @param    lookup_array    The range of cells being searched  
     *    @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.  
     *    @return    integer            The relative position of the found item  
    public static function MATCH($lookup_value, $lookup_array, $match_type= 1) {  
        // flatten the lookup_array  
        $lookup_array =  self::flattenArray($lookup_array);  
        // flatten lookup_value since it may be a cell reference to a value or the value itself  
        $lookup_value =  self::flattenSingleValue($lookup_value);  
        // MATCH is not case sensitive  
        echo "--------------------<br>looking for $lookup_value in <br>";  
        // lookup_value type has to be number, text, or logical values  
            // error: lookup_array should contain only number, text, or logical values  
            //echo "error: lookup_array should contain only number, text, or logical values<br>";  
            return self::$_errorCodes['na'];  
        // match_type is 0, 1 or -1  
        if ($match_type!== 0 &&  $match_type!==- 1 &&  $match_type!== 1){  
            // error: wrong value for match_type  
            //echo "error: wrong value for match_type<br>";  
            return self::$_errorCodes['na'];  
        // lookup_array should not be empty  
        if (sizeof($lookup_array)<= 0){  
            //echo "error: empty range ".sizeof($lookup_array)."<br>";  
            return self::$_errorCodes['na'];  
        // lookup_array should contain only number, text, or logical values  
        for ($i= 0;$i< sizeof($lookup_array);++ $i){  
            // check the type of the value  
                // error: lookup_array should contain only number, text, or logical values  
                //echo "error: lookup_array should contain only number, text, or logical values<br>";  
                return self::$_errorCodes['na'];  
            if (is_string($lookup_array[$i]))  
                $lookup_array[$i] =  strtolower($lookup_array[$i]);  
        // if match_type is 1 or -1, the list has to be ordered  
        if($match_type== 1 ||  $match_type==- 1){  
            $iLastValue= $lookup_array[0];  
            for ($i= 0;$i< sizeof($lookup_array);++ $i){  
                if(($match_type== 1 &&  $lookup_array[$i]< $iLastValue)  
                    // OR check descending order  
                    ||  ($match_type==- 1 &&  $lookup_array[$i]> $iLastValue)){ 
                    // error: list is not ordered correctly  
                    //echo "error: list is not ordered correctly<br>";  
                    return self::$_errorCodes['na'];  
        for ($i= 0; $i <  sizeof($lookup_array); ++ $i){  
            // if match_type is 0 <=> find the first value that is exactly equal to lookup_value  
            if ($match_type== 0 &&  $lookup_array[$i]== $lookup_value){  
                // this is the exact match  
            // if match_type is -1 <=> find the smallest value that is greater than or equal to lookup_value  
            if ($match_type==- 1 &&  $lookup_array[$i] <  $lookup_value){  
                    // 1st cell was allready smaller than the lookup_value  
                    // the previous cell was the match  
            // if match_type is 1 <=> find the largest value that is less than or equal to lookup_value  
            if ($match_type== 1 &&  $lookup_array[$i] >  $lookup_value){  
                    // 1st cell was allready bigger than the lookup_value  
                    // the previous cell was the match  
        // unsuccessful in finding a match, return #N/A error value  
        //echo "unsuccessful in finding a match<br>";  
        return self::$_errorCodes['na'];  
     * Uses an index to choose a value from a reference or array  
     * implemented: Return the value of a specified cell or array of cells    Array form  
     * not implemented: Return a reference to specified cells    Reference form  
     * @param    range_array    a range of cells or an array constant  
     * @param    row_num        selects the row in array from which to return a value. If row_num is omitted, column_num is required.  
     * @param    column_num    selects the column in array from which to return a value. If column_num is omitted, row_num is required.  
    public static function INDEX($arrayValues,$rowNum =  0,$columnNum =  0) {  
        if (($rowNum <  0) ||  ($columnNum <  0)) {  
            return self::$_errorCodes['value'];  
        if (!is_array($arrayValues)) {  
            return self::$_errorCodes['reference'];  
        $rowKeys =  array_keys($arrayValues);  
        $columnKeys =  @array_keys($arrayValues[$rowKeys[0]]);  
        if ($columnNum >  count($columnKeys)) {  
            return self::$_errorCodes['value'];  
        } elseif ($columnNum ==  0) {  
            $rowNum =  $rowKeys[-- $rowNum];  
            foreach($arrayValues as $arrayColumn) {  
                if (is_array($arrayColumn)) {  
                    if (isset ($arrayColumn[$rowNum])) {  
                        $returnArray[] =  $arrayColumn[$rowNum];  
                        return $arrayValues[$rowNum];  
                    return $arrayValues[$rowNum];  
        $columnNum =  $columnKeys[-- $columnNum];  
        if ($rowNum >  count($rowKeys)) {  
            return self::$_errorCodes['value'];  
        } elseif ($rowNum ==  0) {  
            return $arrayValues[$columnNum];  
        $rowNum =  $rowKeys[-- $rowNum];  
        return $arrayValues[$rowNum][$columnNum];  
     *    Returns a value converted to a number  
     *    @param    value        The value you want converted  
     *    @return    number        N converts values listed in the following table  
     *         If value is or refers to N returns  
     *         A date                The serial number of that date  
     *         An error value        The error value  
    public static function N($value) {  
                if ((strlen($value) >  0) &&  ($value{0} ==  '#')) {  
     *    Returns a number that identifies the type of a value  
     *    @param    value        The value you want tested  
     *    @return    number        N converts values listed in the following table  
     *         If value is or refers to N returns  
    public static function TYPE($value) {  
        $value    =  self::flattenArrayIndexed($value);  
            //    Range of cells is an error  
            if (self::isCellValue($a)) {  
            } elseif (self::isMatrixValue($a)) {  
        } elseif(count($value) ==  0) {  
        $value    =  self::flattenSingleValue($value);  
                if ((strlen($value) >  0) &&  ($value{0} ==  '#')) {  
     * Returns the sum-of-years' digits depreciation of an asset for a specified period.  
     * @param    cost        Initial cost of the asset  
     * @param    salvage        Value at the end of the depreciation  
     * @param    life        Number of periods over which the asset is depreciated  
    public static function SYD($cost, $salvage, $life, $period) {  
        $cost        =  self::flattenSingleValue($cost);  
        $salvage    =  self::flattenSingleValue($salvage);  
        $life        =  self::flattenSingleValue($life);  
        $period        =  self::flattenSingleValue($period);  
            if (($life <  1) ||  ($period >  $life)) {  
                return self::$_errorCodes['num'];  
            return (($cost -  $salvage) *  ($life -  $period +  1) *  2) /  ($life *  ($life +  1));  
        return self::$_errorCodes['value'];  
     * @param    array    $matrixData    A matrix of values  
     *  Unlike the Excel TRANSPOSE function, which will only work on a single row or column, this function will transpose a full matrix.  
    public static function TRANSPOSE($matrixData) {  
        if (!is_array($matrixData)) { $matrixData =  array(array($matrixData)); }  
        foreach($matrixData as $matrixRow) {  
            foreach($matrixRow as $matrixCell) {  
                $returnMatrix[$row][$column] =  $matrixCell;  
    }    //    function TRANSPOSE()  
     * @param    array    $matrixData1    A matrix of values  
     * @param    array    $matrixData2    A matrix of values  
    public static function MMULT($matrixData1,$matrixData2) {  
        $matrixAData =  $matrixBData =  array();  
        if (!is_array($matrixData1)) { $matrixData1 =  array(array($matrixData1)); }  
        if (!is_array($matrixData2)) { $matrixData2 =  array(array($matrixData2)); }  
        foreach($matrixData1 as $matrixRow) {  
            foreach($matrixRow as $matrixCell) {  
                if ((is_string($matrixCell)) ||  ($matrixCell ===  null)) {  
                    return self::$_errorCodes['value'];  
                $matrixAData[$rowA][$columnA] =  $matrixCell;  
            $matrixA =  new Matrix($matrixAData);  
            foreach($matrixData2 as $matrixRow) {  
                foreach($matrixRow as $matrixCell) {  
                    if ((is_string($matrixCell)) ||  ($matrixCell ===  null)) {  
                        return self::$_errorCodes['value'];  
                    $matrixBData[$rowB][$columnB] =  $matrixCell;  
            $matrixB =  new Matrix($matrixBData);  
            if (($rowA !=  $columnB) ||  ($rowB !=  $columnA)) {  
                return self::$_errorCodes['value'];  
            return $matrixA->times($matrixB)->getArray();  
        } catch  (Exception $ex) {  
            return self::$_errorCodes['value'];  
     * @param    array    $matrixValues    A matrix of values  
    public static function MINVERSE($matrixValues) {  
        if (!is_array($matrixValues)) { $matrixValues =  array(array($matrixValues)); }  
        foreach($matrixValues as $matrixRow) {  
            foreach($matrixRow as $matrixCell) {  
                if ((is_string($matrixCell)) ||  ($matrixCell ===  null)) {  
                    return self::$_errorCodes['value'];  
                $matrixData[$column][$row] =  $matrixCell;  
            if ($column >  $maxColumn) { $maxColumn =  $column; }  
        if ($row !=  $maxColumn) { return self::$_errorCodes['value']; }  
            $matrix =  new Matrix($matrixData);  
            return $matrix->inverse()->getArray();  
        } catch  (Exception $ex) {  
            return self::$_errorCodes['value'];  
     * @param    array    $matrixValues    A matrix of values  
    public static function MDETERM($matrixValues) {  
        if (!is_array($matrixValues)) { $matrixValues =  array(array($matrixValues)); }  
        foreach($matrixValues as $matrixRow) {  
            foreach($matrixRow as $matrixCell) {  
                if ((is_string($matrixCell)) ||  ($matrixCell ===  null)) {  
                    return self::$_errorCodes['value'];  
                $matrixData[$column][$row] =  $matrixCell;  
            if ($column >  $maxColumn) { $maxColumn =  $column; }  
        if ($row !=  $maxColumn) { return self::$_errorCodes['value']; }  
            $matrix =  new Matrix($matrixData);  
        } catch  (Exception $ex) {  
            return self::$_errorCodes['value'];  
     * @param    mixed    $value    Value to check  
    public static function SUMPRODUCT() {  
        $wrkArray =  self::flattenArray(array_shift($arrayList));  
        $wrkCellCount =  count($wrkArray);  
        foreach($arrayList as $matrixData) {  
            $array2 =  self::flattenArray($matrixData);  
            if ($wrkCellCount !=  $count) {  
                return self::$_errorCodes['value'];  
            foreach ($array2 as $i =>  $val) {  
                if (((is_numeric($wrkArray[$i])) &&  (!is_string($wrkArray[$i]))) &&   
    }    //    function SUMPRODUCT()  
     * @param    mixed    $value    Value to check  
    public static function SUMX2MY2($matrixData1,$matrixData2) {  
        $array1 =  self::flattenArray($matrixData1);  
        $array2 =  self::flattenArray($matrixData2);  
        $count1 =  count($array1);  
        $count2 =  count($array2);  
        for ($i =  0; $i <  $count; ++ $i) {  
                $result +=  ($array1[$i] *  $array1[$i]) -  ($array2[$i] *  $array2[$i]);  
     * @param    mixed    $value    Value to check  
    public static function SUMX2PY2($matrixData1,$matrixData2) {  
        $array1 =  self::flattenArray($matrixData1);  
        $array2 =  self::flattenArray($matrixData2);  
        $count1 =  count($array1);  
        $count2 =  count($array2);  
        for ($i =  0; $i <  $count; ++ $i) {  
                $result +=  ($array1[$i] *  $array1[$i]) +  ($array2[$i] *  $array2[$i]);  
     * @param    mixed    $value    Value to check  
    public static function SUMXMY2($matrixData1,$matrixData2) {  
        $array1 =  self::flattenArray($matrixData1);  
        $array2 =  self::flattenArray($matrixData2);  
        $count1 =  count($array1);  
        $count2 =  count($array2);  
        for ($i =  0; $i <  $count; ++ $i) {  
                $result +=  ($array1[$i] -  $array2[$i]) *  ($array1[$i] -  $array2[$i]);  
    private static function _vlookupSort($a,$b) {  
    }    //    function _vlookupSort()  
    * 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.  
    * @param    lookup_value    The value that you want to match in lookup_array  
    * @param    lookup_array    The range of cells being searched  
    * @param    index_number    The column number in table_array from which the matching value must be returned. The first column is 1.  
    * @param    not_exact_match    Determines if you are looking for an exact match based on lookup_value.  
    * @return    mixed            The value of the found cell  
    public static function VLOOKUP($lookup_value, $lookup_array, $index_number, $not_exact_match= true) {  
        $lookup_value    =  self::flattenSingleValue($lookup_value);  
        $index_number    =  self::flattenSingleValue($index_number);  
        $not_exact_match    =  self::flattenSingleValue($not_exact_match);  
        // index_number must be greater than or equal to 1  
            return self::$_errorCodes['value'];  
        // index_number must be less than or equal to the number of columns in lookup_array  
        if ((!is_array($lookup_array)) ||  (count($lookup_array) <  1)) {  
            return self::$_errorCodes['reference'];  
            $f =  array_keys($lookup_array);  
            if ((!is_array($lookup_array[$firstRow])) ||  ($index_number >  count($lookup_array[$firstRow]))) {  
                return self::$_errorCodes['reference'];  
                $columnKeys =  array_keys($lookup_array[$firstRow]);  
                $returnColumn =  $columnKeys[-- $index_number];  
            uasort($lookup_array,array('self','_vlookupSort'));  
        $rowNumber =  $rowValue =  False;  
        foreach($lookup_array as $rowKey =>  $rowData) {  
            $rowValue =  $rowData[$firstColumn];  
        if ($rowNumber !==  false) {  
            if ((!$not_exact_match) &&  ($rowValue !=  $lookup_value)) {  
                //    if an exact match is required, we have what we need to return an appropriate response  
                return self::$_errorCodes['na'];  
                //    otherwise return the appropriate value  
                return $lookup_array[$rowNumber][$returnColumn];  
        return self::$_errorCodes['na'];  
     * The LOOKUP function searches for value either from a one-row or one-column range or from an array.  
     * @param    lookup_value    The value that you want to match in lookup_array  
     * @param    lookup_vector    The range of cells being searched  
     * @param    result_vector    The column from which the matching value must be returned  
     * @return    mixed            The value of the found cell  
    public static function LOOKUP($lookup_value, $lookup_vector, $result_vector= null) {  
        $lookup_value    =  self::flattenSingleValue($lookup_value);  
            return self::$_errorCodes['na'];  
        $lookupRows =  count($lookup_vector);  
        $lookupColumns =  count($lookup_vector[$l]);  
        if ((($lookupRows ==  1) &&  ($lookupColumns >  1)) ||  (($lookupRows ==  2) &&  ($lookupColumns !=  2))) {  
            $lookup_vector =  self::TRANSPOSE($lookup_vector);  
            $lookupRows =  count($lookup_vector);  
            $result_vector =  $lookup_vector;  
        $resultRows =  count($result_vector);  
        $resultColumns =  count($result_vector[$l]);  
        if ((($resultRows ==  1) &&  ($resultColumns >  1)) ||  (($resultRows ==  2) &&  ($resultColumns !=  2))) {  
            $result_vector =  self::TRANSPOSE($result_vector);  
            $resultRows =  count($result_vector);  
        if ($lookupColumns !=  2) {  
            foreach($lookup_vector as &$value) {  
                    $dataValue1 =  $value[$key1];  
                $value =  array($key1 =>  $dataValue1, $key2 =>  $dataValue2);  
        return self::VLOOKUP($lookup_value,$lookup_vector,2);  
     *    Convert a multi-dimensional array to a simple 1-dimensional array  
     *    @param    array    $array    Array to be flattened  
     *    @return    array    Flattened array  
        foreach ($array as $value) {  
                foreach ($value as $val) {  
    }    //    function flattenArray()  
     *    Convert a multi-dimensional array to a simple 1-dimensional array, but retain an element of indexing  
     *    @param    array    $array    Array to be flattened  
     *    @return    array    Flattened array  
        foreach ($array as $k1 =>  $value) {  
                foreach ($value as $k2 =>  $val) {  
                        foreach ($val as $k3 =>  $v) {  
                            $arrayValues[$k1. '.'. $k2. '.'. $k3] =  $v;  
                        $arrayValues[$k1. '.'. $k2] =  $val;  
                $arrayValues[$k1] =  $value;  
    }    //    function flattenArrayIndexed()  
     *    Convert an array to a single scalar value by extracting the first element  
     *    @param    mixed        $value        Array or scalar value  
    }    //    function flattenSingleValue()  
}    //    class PHPExcel_Calculation_Functions  
//    There are a few mathematical functions that aren't available on all versions of PHP for all platforms  
//    These functions aren't available in Windows implementations of PHP prior to version 5.3.0  
//    So we test if they do exist for this version of PHP/operating platform; and if not we create them  
        return 2 *  log(sqrt(($x +  1) /  2) +  sqrt(($x -  1) /  2));  
        return log($x +  sqrt(1 +  $x *  $x));  
        return (log(1 +  $x) -  log(1 -  $x)) /  2;  
        $regex =  array( '/%((?:[\^!\-]|\+|\(|\=.)*)([0-9]+)?(?:#([0-9]+))?',  
                         '(?:\.([0-9]+))?([in%])/'  
            trigger_error("No format specified or invalid format", E_USER_WARNING);  
        $flags =  array( 'fillchar'    =>  preg_match('/\=(.)/', $fmatch[1], $match) ?  $match[1] :  ' ',  
                        'usesignal'    =>  preg_match('/\+|\(/', $fmatch[1], $match) ?  $match[0] :  '+',  
        $width    =  trim($fmatch[2]) ? (int) $fmatch[2] :  0;  
        $left    =  trim($fmatch[3]) ? (int) $fmatch[3] :  0;  
        $right    =  trim($fmatch[4]) ? (int) $fmatch[4] :  $locale['int_frac_digits'];  
        $conversion =  $fmatch[5];  
        $letter =  $positive ?  'p' :  'n';  
        $prefix =  $suffix =  $cprefix =  $csuffix =  $signal =  '';  
            $signal =  $locale['negative_sign'];  
                case $locale['n_sign_posn'] ==  0 ||  $flags['usesignal'] ==  '(':   
                case $locale['n_sign_posn'] ==  1:   
                case $locale['n_sign_posn'] ==  2:   
                case $locale['n_sign_posn'] ==  3:   
                case $locale['n_sign_posn'] ==  4:   
        if (!$flags['nosimbol']) {  
            $currency .=  ($conversion ==  'i' ?  $locale['int_curr_symbol'] :  $locale['currency_symbol']);  
            $currency =  iconv('ISO-8859-1','UTF-8',$currency);  
        $space =  $locale["{ $letter}_sep_by_space"] ?  ' ' :  '';  
        $number =  number_format($number, $right, $locale['mon_decimal_point'], $flags['nogroup'] ?  '' :  $locale['mon_thousands_sep'] );  
        $number =  explode($locale['mon_decimal_point'], $number);  
        if ($left >  0 &&  $left >  $n) {  
                $number[0] .=  str_repeat($flags['fillchar'], $left -  $n);  
                $number[0] =  str_repeat($flags['fillchar'], $left -  $n) .  $number[0];  
        $number =  implode($locale['mon_decimal_point'], $number);  
        if ($locale["{ $letter}_cs_precedes"]) {  
            $number =  $prefix .  $currency .  $space .  $number .  $suffix;  
            $number =  $prefix .  $number .  $space .  $currency .  $suffix;  
            $number =  str_pad($number, $width, $flags['fillchar'], $flags['isleft'] ?  STR_PAD_RIGHT :  STR_PAD_LEFT);  
    }    //    function money_format()  
//    Strangely, PHP doesn't have a mb_str_replace multibyte function  
//    As we'll only ever use this function with UTF-8 characters, we can simply "hard-code" the character set  
            foreach($subject as $key =>  $val) {  
        foreach((array)  $search as $key =>  $s) {  
 
 
        
       |