PHPExcel_Calculation
[ class tree: PHPExcel_Calculation ] [ index: PHPExcel_Calculation ] [ all elements ]

Source for file Functions.php

Documentation is available at Functions.php

  1. <?php
  2.  
  3. /**
  4.  * PHPExcel
  5.  *
  6.  * Copyright (c) 2006 - 2009 PHPExcel
  7.  *
  8.  * This library is free software; you can redistribute it and/or
  9.  * modify it under the terms of the GNU Lesser General Public
  10.  * License as published by the Free Software Foundation; either
  11.  * version 2.1 of the License, or (at your option) any later version.
  12.  *
  13.  * This library is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16.  * Lesser General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU Lesser General Public
  19.  * License along with this library; if not, write to the Free Software
  20.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  21.  *
  22.  * @category   PHPExcel
  23.  * @package    PHPExcel_Calculation
  24.  * @copyright  Copyright (c) 2006 - 2009 PHPExcel (http://www.codeplex.com/PHPExcel)
  25.  * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
  26.  * @version    1.6.5, 2009-01-05
  27.  */
  28.  
  29.  
  30. define('EPS'2.22e-16);
  31. define('MAX_VALUE'1.2e308);
  32. define('LOG_GAMMA_X_MAX_VALUE'2.55e305);
  33. define('SQRT2PI'2.5066282746310005024157652848110452530069867406099);
  34. define('XMININ'2.23e-308);
  35. define('MAX_ITERATIONS'150);
  36. define('PRECISION'8.88E-016);
  37. define('EULER'2.71828182845904523536);
  38.  
  39. $savedPrecision ini_get('precision');
  40. if ($savedPrecision 15{
  41.     ini_set('precision',15);
  42. }
  43.  
  44.  
  45. /** PHPExcel_Cell */
  46. require_once 'PHPExcel/Cell.php';
  47.  
  48. /** PHPExcel_Cell_DataType */
  49. require_once 'PHPExcel/Cell/DataType.php';
  50.  
  51. /** PHPExcel_Shared_Date */
  52. require_once 'PHPExcel/Shared/Date.php';
  53.  
  54. /** PHPExcel_Shared_Date */
  55. require_once 'PHPExcel/Shared/JAMA/Matrix.php';
  56.  
  57.  
  58. /**
  59.  * PHPExcel_Calculation_Functions
  60.  *
  61.  * @category   PHPExcel
  62.  * @package    PHPExcel_Calculation
  63.  * @copyright  Copyright (c) 2006 - 2009 PHPExcel (http://www.codeplex.com/PHPExcel)
  64.  */
  65.  
  66.     /** constants */
  67.     const COMPATIBILITY_EXCEL        'Excel';
  68.     const COMPATIBILITY_GNUMERIC    'Gnumeric';
  69.     const COMPATIBILITY_OPENOFFICE    'OpenOfficeCalc';
  70.  
  71.     const RETURNDATE_PHP_NUMERIC 'P';
  72.     const RETURNDATE_PHP_OBJECT 'O';
  73.     const RETURNDATE_EXCEL 'E';
  74.  
  75.  
  76.     /**
  77.      * Compatibility mode to use for error checking and responses
  78.      *
  79.      * @var string 
  80.      */
  81.     private static $compatibilityMode    self::COMPATIBILITY_EXCEL;
  82.  
  83.     /**
  84.      * Data Type to use when returning date values
  85.      *
  86.      * @var integer 
  87.      */
  88.     private static $ReturnDateType    self::RETURNDATE_PHP_NUMERIC;
  89.  
  90.     /**
  91.      * List of error codes
  92.      *
  93.      * @var array 
  94.      */
  95.     private static $_errorCodes    array'null'                => '#NULL!',
  96.                                          'divisionbyzero'    => '#DIV/0!',
  97.                                          'value'            => '#VALUE!',
  98.                                          'reference'        => '#REF!',
  99.                                          'name'                => '#NAME?',
  100.                                          'num'                => '#NUM!',
  101.                                          'na'                => '#N/A',
  102.                                          'gettingdata'        => '#GETTING_DATA'
  103.                                        );
  104.  
  105.  
  106.     /**
  107.      * Set the Compatibility Mode
  108.      *
  109.      * @param     string        $compatibilityMode        Compatibility Mode
  110.      * @return     boolean    (Success or Failure)
  111.      */
  112.     public static function setCompatibilityMode($compatibilityMode{
  113.         if (($compatibilityMode == self::COMPATIBILITY_EXCEL||
  114.             ($compatibilityMode == self::COMPATIBILITY_GNUMERIC||
  115.             ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  116.             self::$compatibilityMode $compatibilityMode;
  117.             return True;
  118.         }
  119.         return False;
  120.     }
  121.  
  122.     /**
  123.      * Return the current Compatibility Mode
  124.      *
  125.      * @return     string        $compatibilityMode        Compatibility Mode
  126.      */
  127.     public static function getCompatibilityMode({
  128.         return self::$compatibilityMode;
  129.     }
  130.  
  131.     /**
  132.      * Set the Return Date Format (Excel, PHP Serialized or PHP Object)
  133.      *
  134.      * @param     integer    $returnDateType            Return Date Format
  135.      * @return     boolean                            Success or failure
  136.      */
  137.     public static function setReturnDateType($returnDateType{
  138.         if (($returnDateType == self::RETURNDATE_PHP_NUMERIC||
  139.             ($returnDateType == self::RETURNDATE_PHP_OBJECT||
  140.             ($returnDateType == self::RETURNDATE_EXCEL)) {
  141.             self::$ReturnDateType $returnDateType;
  142.             return True;
  143.         }
  144.         return False;
  145.     }    //    function setReturnDateType()
  146.  
  147.  
  148.     /**
  149.      * Return the Return Date Format (Excel, PHP Serialized or PHP Object)
  150.      *
  151.      * @return     integer    $returnDateType            Return Date Format
  152.      */
  153.     public static function getReturnDateType({
  154.         return self::$ReturnDateType;
  155.     }    //    function getReturnDateType()
  156.  
  157.  
  158.     /**
  159.      * DUMMY
  160.      *
  161.      * @return  string    #NAME?
  162.      */
  163.     public static function DUMMY({
  164.         return self::$_errorCodes['name'];
  165.     }
  166.  
  167.     /**
  168.      * NA
  169.      *
  170.      * @return  string    #N/A!
  171.      */
  172.     public static function NA({
  173.         return self::$_errorCodes['na'];
  174.     }
  175.  
  176.     /**
  177.      * LOGICAL_AND
  178.      *
  179.      * Returns boolean TRUE if all its arguments are TRUE; returns FALSE if one or more argument is FALSE.
  180.      *
  181.      *    Booleans arguments are treated as True or False as appropriate
  182.      *    Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
  183.      *    If any argument value is a string, or a Null, it is ignored
  184.      *
  185.      *    Quirk of Excel:
  186.      *        String values passed directly to the function rather than through a cell reference
  187.      *            e.g.=AND(1,"A",1)
  188.      *        will return a #VALUE! error, _not_ ignoring the string.
  189.      *        This behaviour is not replicated
  190.      *
  191.      * @param    array of mixed        Data Series
  192.      * @return  boolean 
  193.      */
  194.     public static function LOGICAL_AND({
  195.         // Return value
  196.         $returnValue True;
  197.  
  198.         // Loop through the arguments
  199.         $aArgs self::flattenArray(func_get_args());
  200.         $argCount 0;
  201.         foreach ($aArgs as $arg{
  202.             // Is it a boolean value?
  203.             if (is_bool($arg)) {
  204.                 $returnValue $returnValue && $arg;
  205.                 ++$argCount;
  206.             elseif ((is_numeric($arg)) && (!is_string($arg))) {
  207.                 $returnValue $returnValue && ($arg != 0);
  208.                 ++$argCount;
  209.             }
  210.         }
  211.  
  212.         // Return
  213.         if ($argCount == 0{
  214.             return self::$_errorCodes['value'];
  215.         }
  216.         return $returnValue;
  217.     }
  218.  
  219.     /**
  220.      * LOGICAL_OR
  221.      *
  222.      * Returns boolean TRUE if any argument is TRUE; returns FALSE if all arguments are FALSE.
  223.      *
  224.      *    Booleans arguments are treated as True or False as appropriate
  225.      *    Integer or floating point arguments are treated as True, except for 0 or 0.0 which are False
  226.      *    If any argument value is a string, or a Null, it is ignored
  227.      *
  228.      * @param    array of mixed        Data Series
  229.      * @return  boolean 
  230.      */
  231.     public static function LOGICAL_OR({
  232.         // Return value
  233.         $returnValue False;
  234.  
  235.         // Loop through the arguments
  236.         $aArgs self::flattenArray(func_get_args());
  237.         $argCount 0;
  238.         foreach ($aArgs as $arg{
  239.             // Is it a boolean value?
  240.             if (is_bool($arg)) {
  241.                 $returnValue $returnValue || $arg;
  242.                 ++$argCount;
  243.             elseif ((is_numeric($arg)) && (!is_string($arg))) {
  244.                 $returnValue $returnValue || ($arg != 0);
  245.                 ++$argCount;
  246.             }
  247.         }
  248.  
  249.         // Return
  250.         if ($argCount == 0{
  251.             return self::$_errorCodes['value'];
  252.         }
  253.         return $returnValue;
  254.     }
  255.  
  256.     /**
  257.      * LOGICAL_FALSE
  258.      *
  259.      * Returns FALSE.
  260.      *
  261.      * @return  boolean 
  262.      */
  263.     public static function LOGICAL_FALSE({
  264.         return False;
  265.     }
  266.  
  267.     /**
  268.      * LOGICAL_TRUE
  269.      *
  270.      * Returns TRUE.
  271.      *
  272.      * @return  boolean 
  273.      */
  274.     public static function LOGICAL_TRUE({
  275.         return True;
  276.     }
  277.  
  278.     /**
  279.      * ATAN2
  280.      *
  281.      * This function calculates the arc tangent of the two variables x and y. It is similar to
  282.      *        calculating the arc tangent of y / x, except that the signs of both arguments are used
  283.      *        to determine the quadrant of the result.
  284.      * Note that Excel reverses the arguments, so we need to reverse them here before calling the
  285.      *        standard PHP atan() function
  286.      *
  287.      * @param    float    $x        Number
  288.      * @param    float    $y        Number
  289.      * @return  float    Square Root of Number * Pi
  290.      */
  291.     public static function REVERSE_ATAN2($x$y{
  292.         $x    self::flattenSingleValue($x);
  293.         $y    self::flattenSingleValue($y);
  294.  
  295.         return atan2($y$x);
  296.     }
  297.  
  298.     /**
  299.      * SUM
  300.      *
  301.      * SUM computes the sum of all the values and cells referenced in the argument list.
  302.      *
  303.      * @param    array of mixed        Data Series
  304.      * @return  float 
  305.      */
  306.     public static function SUM({
  307.         // Return value
  308.         $returnValue 0;
  309.  
  310.         // Loop through the arguments
  311.         $aArgs self::flattenArray(func_get_args());
  312.         foreach ($aArgs as $arg{
  313.             // Is it a numeric value?
  314.             if ((is_numeric($arg)) && (!is_string($arg))) {
  315.                 $returnValue += $arg;
  316.             }
  317.         }
  318.  
  319.         // Return
  320.         return $returnValue;
  321.     }
  322.  
  323.     /**
  324.      * SUMSQ
  325.      *
  326.      * Returns the sum of the squares of the arguments
  327.      *
  328.      * @param    array of mixed        Data Series
  329.      * @return  float 
  330.      */
  331.     public static function SUMSQ({
  332.         // Return value
  333.         $returnValue 0;
  334.  
  335.         // Loop trough arguments
  336.         $aArgs self::flattenArray(func_get_args());
  337.         foreach ($aArgs as $arg{
  338.             // Is it a numeric value?
  339.             if ((is_numeric($arg)) && (!is_string($arg))) {
  340.                 $returnValue += pow($arg,2);
  341.             }
  342.         }
  343.  
  344.         // Return
  345.         return $returnValue;
  346.     }
  347.  
  348.     /**
  349.      * PRODUCT
  350.      *
  351.      * PRODUCT returns the product of all the values and cells referenced in the argument list.
  352.      *
  353.      * @param    array of mixed        Data Series
  354.      * @return  float 
  355.      */
  356.     public static function PRODUCT({
  357.         // Return value
  358.         $returnValue null;
  359.  
  360.         // Loop trough arguments
  361.         $aArgs self::flattenArray(func_get_args());
  362.         foreach ($aArgs as $arg{
  363.             // Is it a numeric value?
  364.             if ((is_numeric($arg)) && (!is_string($arg))) {
  365.                 if (is_null($returnValue)) {
  366.                     $returnValue $arg;
  367.                 else {
  368.                     $returnValue *= $arg;
  369.                 }
  370.             }
  371.         }
  372.  
  373.         // Return
  374.         if (is_null($returnValue)) {
  375.             return 0;
  376.         }
  377.         return $returnValue;
  378.     }
  379.  
  380.     /**
  381.      * QUOTIENT
  382.      *
  383.      * QUOTIENT function returns the integer portion of a division.numerator is the divided number
  384.      * and denominator is the divisor.
  385.      *
  386.      * @param    array of mixed        Data Series
  387.      * @return  float 
  388.      */
  389.     public static function QUOTIENT({
  390.         // Return value
  391.         $returnValue null;
  392.  
  393.         // Loop trough arguments
  394.         $aArgs self::flattenArray(func_get_args());
  395.         foreach ($aArgs as $arg{
  396.             // Is it a numeric value?
  397.             if ((is_numeric($arg)) && (!is_string($arg))) {
  398.                 if (is_null($returnValue)) {
  399.                     if (($returnValue == 0|| ($arg == 0)) {
  400.                         $returnValue 0;
  401.                     else {
  402.                         $returnValue $arg;
  403.                     }
  404.                 else {
  405.                     if (($returnValue == 0|| ($arg == 0)) {
  406.                         $returnValue 0;
  407.                     else {
  408.                         $returnValue /= $arg;
  409.                     }
  410.                 }
  411.             }
  412.         }
  413.  
  414.         // Return
  415.         return intval($returnValue);
  416.     }
  417.  
  418.     /**
  419.      * MIN
  420.      *
  421.      * MIN returns the value of the element of the values passed that has the smallest value,
  422.      * with negative numbers considered smaller than positive numbers.
  423.      *
  424.      * @param    array of mixed        Data Series
  425.      * @return  float 
  426.      */
  427.     public static function MIN({
  428.         // Return value
  429.         $returnValue null;
  430.  
  431.         // Loop trough arguments
  432.         $aArgs self::flattenArray(func_get_args());
  433.         foreach ($aArgs as $arg{
  434.             // Is it a numeric value?
  435.             if ((is_numeric($arg)) && (!is_string($arg))) {
  436.                 if ((is_null($returnValue)) || ($arg $returnValue)) {
  437.                     $returnValue $arg;
  438.                 }
  439.             }
  440.         }
  441.  
  442.         // Return
  443.         if(is_null($returnValue)) {
  444.             return 0;
  445.         }
  446.         return $returnValue;
  447.     }
  448.  
  449.     /**
  450.      * MINA
  451.      *
  452.      * Returns the smallest value in a list of arguments, including numbers, text, and logical values
  453.      *
  454.      * @param    array of mixed        Data Series
  455.      * @return  float 
  456.      */
  457.     public static function MINA({
  458.         // Return value
  459.         $returnValue null;
  460.  
  461.         // Loop through arguments
  462.         $aArgs self::flattenArray(func_get_args());
  463.         foreach ($aArgs as $arg{
  464.             // Is it a numeric value?
  465.             if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg&& ($arg != '')))) {
  466.                 if (is_bool($arg)) {
  467.                     $arg = (integer) $arg;
  468.                 elseif (is_string($arg)) {
  469.                     $arg 0;
  470.                 }
  471.                 if ((is_null($returnValue)) || ($arg $returnValue)) {
  472.                     $returnValue $arg;
  473.                 }
  474.             }
  475.         }
  476.  
  477.         // Return
  478.         if(is_null($returnValue)) {
  479.             return 0;
  480.         }
  481.         return $returnValue;
  482.     }
  483.  
  484.     /**
  485.      * SMALL
  486.      *
  487.      * Returns the nth smallest value in a data set. You can use this function to
  488.      * select a value based on its relative standing.
  489.      *
  490.      * @param    array of mixed        Data Series
  491.      * @param    float    Entry in the series to return
  492.      * @return    float 
  493.      */
  494.     public static function SMALL({
  495.         $aArgs self::flattenArray(func_get_args());
  496.  
  497.         // Calculate
  498.         $n array_pop($aArgs);
  499.  
  500.         if ((is_numeric($n)) && (!is_string($n))) {
  501.             $mArgs array();
  502.             foreach ($aArgs as $arg{
  503.                 // Is it a numeric value?
  504.                 if ((is_numeric($arg)) && (!is_string($arg))) {
  505.                     $mArgs[$arg;
  506.                 }
  507.             }
  508.             $count self::COUNT($mArgs);
  509.             $n floor(--$n);
  510.             if (($n 0|| ($n >= $count|| ($count == 0)) {
  511.                 return self::$_errorCodes['num'];
  512.             }
  513.             sort($mArgs);
  514.             return $mArgs[$n];
  515.         }
  516.         return self::$_errorCodes['value'];
  517.     }
  518.  
  519.     /**
  520.      * MAX
  521.      *
  522.      * MAX returns the value of the element of the values passed that has the highest value,
  523.      * with negative numbers considered smaller than positive numbers.
  524.      *
  525.      * @param    array of mixed        Data Series
  526.      * @return  float 
  527.      */
  528.     public static function MAX({
  529.         // Return value
  530.         $returnValue null;
  531.  
  532.         // Loop trough arguments
  533.         $aArgs self::flattenArray(func_get_args());
  534.         foreach ($aArgs as $arg{
  535.             // Is it a numeric value?
  536.             if ((is_numeric($arg)) && (!is_string($arg))) {
  537.                 if ((is_null($returnValue)) || ($arg $returnValue)) {
  538.                     $returnValue $arg;
  539.                 }
  540.             }
  541.         }
  542.  
  543.         // Return
  544.         if(is_null($returnValue)) {
  545.             return 0;
  546.         }
  547.         return $returnValue;
  548.     }
  549.  
  550.     /**
  551.      * MAXA
  552.      *
  553.      * Returns the greatest value in a list of arguments, including numbers, text, and logical values
  554.      *
  555.      * @param    array of mixed        Data Series
  556.      * @return  float 
  557.      */
  558.     public static function MAXA({
  559.         // Return value
  560.         $returnValue null;
  561.  
  562.         // Loop through arguments
  563.         $aArgs self::flattenArray(func_get_args());
  564.         foreach ($aArgs as $arg{
  565.             // Is it a numeric value?
  566.             if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg&& ($arg != '')))) {
  567.                 if (is_bool($arg)) {
  568.                     $arg = (integer) $arg;
  569.                 elseif (is_string($arg)) {
  570.                     $arg 0;
  571.                 }
  572.                 if ((is_null($returnValue)) || ($arg $returnValue)) {
  573.                     $returnValue $arg;
  574.                 }
  575.             }
  576.         }
  577.  
  578.         // Return
  579.         if(is_null($returnValue)) {
  580.             return 0;
  581.         }
  582.         return $returnValue;
  583.     }
  584.  
  585.     /**
  586.      * LARGE
  587.      *
  588.      * Returns the nth largest value in a data set. You can use this function to
  589.      * select a value based on its relative standing.
  590.      *
  591.      * @param    array of mixed        Data Series
  592.      * @param    float    Entry in the series to return
  593.      * @return    float 
  594.      *
  595.      */
  596.     public static function LARGE({
  597.         $aArgs self::flattenArray(func_get_args());
  598.  
  599.         // Calculate
  600.         $n floor(array_pop($aArgs));
  601.  
  602.         if ((is_numeric($n)) && (!is_string($n))) {
  603.             $mArgs array();
  604.             foreach ($aArgs as $arg{
  605.                 // Is it a numeric value?
  606.                 if ((is_numeric($arg)) && (!is_string($arg))) {
  607.                     $mArgs[$arg;
  608.                 }
  609.             }
  610.             $count self::COUNT($mArgs);
  611.             $n floor(--$n);
  612.             if (($n 0|| ($n >= $count|| ($count == 0)) {
  613.                 return self::$_errorCodes['num'];
  614.             }
  615.             rsort($mArgs);
  616.             return $mArgs[$n];
  617.         }
  618.         return self::$_errorCodes['value'];
  619.     }
  620.  
  621.     /**
  622.      * PERCENTILE
  623.      *
  624.      * Returns the nth percentile of values in a range..
  625.      *
  626.      * @param    array of mixed        Data Series
  627.      * @param    float    $entry        Entry in the series to return
  628.      * @return    float 
  629.      */
  630.     public static function PERCENTILE({
  631.         $aArgs self::flattenArray(func_get_args());
  632.  
  633.         // Calculate
  634.         $entry array_pop($aArgs);
  635.  
  636.         if ((is_numeric($entry)) && (!is_string($entry))) {
  637.             if (($entry 0|| ($entry 1)) {
  638.                 return self::$_errorCodes['num'];
  639.             }
  640.             $mArgs array();
  641.             foreach ($aArgs as $arg{
  642.                 // Is it a numeric value?
  643.                 if ((is_numeric($arg)) && (!is_string($arg))) {
  644.                     $mArgs[$arg;
  645.                 }
  646.             }
  647.             $mValueCount count($mArgs);
  648.             if ($mValueCount 0{
  649.                 sort($mArgs);
  650.                 $count self::COUNT($mArgs);
  651.                 $index $entry ($count-1);
  652.                 $iBase floor($index);
  653.                 if ($index == $iBase{
  654.                     return $mArgs[$index];
  655.                 else {
  656.                     $iNext $iBase 1;
  657.                     $iProportion $index $iBase;
  658.                     return $mArgs[$iBase(($mArgs[$iNext$mArgs[$iBase]$iProportion;
  659.                 }
  660.             }
  661.         }
  662.         return self::$_errorCodes['value'];
  663.     }
  664.  
  665.     /**
  666.      * QUARTILE
  667.      *
  668.      * Returns the quartile of a data set.
  669.      *
  670.      * @param    array of mixed        Data Series
  671.      * @param    float    $entry        Entry in the series to return
  672.      * @return    float 
  673.      */
  674.     public static function QUARTILE({
  675.         $aArgs self::flattenArray(func_get_args());
  676.  
  677.         // Calculate
  678.         $entry floor(array_pop($aArgs));
  679.  
  680.         if ((is_numeric($entry)) && (!is_string($entry))) {
  681.             $entry /= 4;
  682.             if (($entry 0|| ($entry 1)) {
  683.                 return self::$_errorCodes['num'];
  684.             }
  685.             return self::PERCENTILE($aArgs,$entry);
  686.         }
  687.         return self::$_errorCodes['value'];
  688.     }
  689.  
  690.     /**
  691.      * COUNT
  692.      *
  693.      * Counts the number of cells that contain numbers within the list of arguments
  694.      *
  695.      * @param    array of mixed        Data Series
  696.      * @return  int 
  697.      */
  698.     public static function COUNT({
  699.         // Return value
  700.         $returnValue 0;
  701.  
  702.         // Loop trough arguments
  703.         $aArgs self::flattenArray(func_get_args());
  704.         foreach ($aArgs as $arg{
  705.             if ((is_bool($arg)) && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  706.                 $arg = (int) $arg;
  707.             }
  708.             // Is it a numeric value?
  709.             if ((is_numeric($arg)) && (!is_string($arg))) {
  710.                 ++$returnValue;
  711.             }
  712.         }
  713.  
  714.         // Return
  715.         return $returnValue;
  716.     }
  717.  
  718.     /**
  719.      * COUNTBLANK
  720.      *
  721.      * Counts the number of empty cells within the list of arguments
  722.      *
  723.      * @param    array of mixed        Data Series
  724.      * @return  int 
  725.      */
  726.     public static function COUNTBLANK({
  727.         // Return value
  728.         $returnValue 0;
  729.  
  730.         // Loop trough arguments
  731.         $aArgs self::flattenArray(func_get_args());
  732.         foreach ($aArgs as $arg{
  733.             // Is it a blank cell?
  734.             if ((is_null($arg)) || ((is_string($arg)) && ($arg == ''))) {
  735.                 ++$returnValue;
  736.             }
  737.         }
  738.  
  739.         // Return
  740.         return $returnValue;
  741.     }
  742.  
  743.     /**
  744.      * COUNTA
  745.      *
  746.      * Counts the number of cells that are not empty within the list of arguments
  747.      *
  748.      * @param    array of mixed        Data Series
  749.      * @return  int 
  750.      */
  751.     public static function COUNTA({
  752.         // Return value
  753.         $returnValue 0;
  754.  
  755.         // Loop through arguments
  756.         $aArgs self::flattenArray(func_get_args());
  757.         foreach ($aArgs as $arg{
  758.             // Is it a numeric, boolean or string value?
  759.             if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg&& ($arg != '')))) {
  760.                 ++$returnValue;
  761.             }
  762.         }
  763.  
  764.         // Return
  765.         return $returnValue;
  766.     }
  767.  
  768.     /**
  769.      * AVERAGE
  770.      *
  771.      * Returns the average (arithmetic mean) of the arguments
  772.      *
  773.      * @param    array of mixed        Data Series
  774.      * @return  float 
  775.      */
  776.     public static function AVERAGE({
  777.         // Return value
  778.         $returnValue 0;
  779.  
  780.         // Loop through arguments
  781.         $aArgs self::flattenArray(func_get_args());
  782.         $aCount 0;
  783.         foreach ($aArgs as $arg{
  784.             if ((is_bool($arg))  && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  785.                 $arg = (integer) $arg;
  786.             }
  787.             // Is it a numeric value?
  788.             if ((is_numeric($arg)) && (!is_string($arg))) {
  789.                 if (is_null($returnValue)) {
  790.                     $returnValue $arg;
  791.                 else {
  792.                     $returnValue += $arg;
  793.                 }
  794.                 ++$aCount;
  795.             }
  796.         }
  797.  
  798.         // Return
  799.         if ($aCount 0{
  800.             return $returnValue $aCount;
  801.         else {
  802.             return self::$_errorCodes['divisionbyzero'];
  803.         }
  804.     }
  805.  
  806.     /**
  807.      * AVERAGEA
  808.      *
  809.      * Returns the average of its arguments, including numbers, text, and logical values
  810.      *
  811.      * @param    array of mixed        Data Series
  812.      * @return  float 
  813.      */
  814.     public static function AVERAGEA({
  815.         // Return value
  816.         $returnValue null;
  817.  
  818.         // Loop through arguments
  819.         $aArgs self::flattenArray(func_get_args());
  820.         $aCount 0;
  821.         foreach ($aArgs as $arg{
  822.             if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg&& ($arg != '')))) {
  823.                 if (is_bool($arg)) {
  824.                     $arg = (integer) $arg;
  825.                 elseif (is_string($arg)) {
  826.                     $arg 0;
  827.                 }
  828.                 if (is_null($returnValue)) {
  829.                     $returnValue $arg;
  830.                 else {
  831.                     $returnValue += $arg;
  832.                 }
  833.                 ++$aCount;
  834.             }
  835.         }
  836.  
  837.         // Return
  838.         if ($aCount 0{
  839.             return $returnValue $aCount;
  840.         else {
  841.             return self::$_errorCodes['divisionbyzero'];
  842.         }
  843.     }
  844.  
  845.     /**
  846.      * MEDIAN
  847.      *
  848.      * Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
  849.      *
  850.      * @param    array of mixed        Data Series
  851.      * @return  float 
  852.      */
  853.     public static function MEDIAN({
  854.         // Return value
  855.         $returnValue self::$_errorCodes['num'];
  856.  
  857.         $mArgs array();
  858.         // Loop through arguments
  859.         $aArgs self::flattenArray(func_get_args());
  860.         foreach ($aArgs as $arg{
  861.             // Is it a numeric value?
  862.             if ((is_numeric($arg)) && (!is_string($arg))) {
  863.                 $mArgs[$arg;
  864.             }
  865.         }
  866.  
  867.         $mValueCount count($mArgs);
  868.         if ($mValueCount 0{
  869.             sort($mArgs,SORT_NUMERIC);
  870.             $mValueCount $mValueCount 2;
  871.             if ($mValueCount == floor($mValueCount)) {
  872.                 $returnValue ($mArgs[$mValueCount--$mArgs[$mValueCount]2;
  873.             else {
  874.                 $mValueCount == floor($mValueCount);
  875.                 $returnValue $mArgs[$mValueCount];
  876.             }
  877.         }
  878.  
  879.         // Return
  880.         return $returnValue;
  881.     }
  882.  
  883.     //
  884.     //    Special variant of array_count_values that isn't limited to strings and integers,
  885.     //        but can work with floating point numbers as values
  886.     //
  887.     private static function modeCalc($data{
  888.         $frequencyArray array();
  889.         foreach($data as $datum{
  890.             $found False;
  891.             foreach($frequencyArray as $key => $value{
  892.                 if ((string)$value['value'== (string)$datum{
  893.                     ++$frequencyArray[$key]['frequency'];
  894.                     $found True;
  895.                     break;
  896.                 }
  897.             }
  898.             if (!$found{
  899.                 $frequencyArray[array('value'        => $datum,
  900.                                           'frequency'    =>    );
  901.             }
  902.         }
  903.  
  904.         foreach($frequencyArray as $key => $value{
  905.             $frequencyList[$key$value['frequency'];
  906.             $valueList[$key$value['value'];
  907.         }
  908.         array_multisort($frequencyListSORT_DESC$valueListSORT_ASCSORT_NUMERIC$frequencyArray);
  909.  
  910.         if ($frequencyArray[0]['frequency'== 1{
  911.             return self::NA();
  912.         }
  913.         return $frequencyArray[0]['value'];
  914.     }
  915.  
  916.     /**
  917.      * MODE
  918.      *
  919.      * Returns the most frequently occurring, or repetitive, value in an array or range of data
  920.      *
  921.      * @param    array of mixed        Data Series
  922.      * @return  float 
  923.      */
  924.     public static function MODE({
  925.         // Return value
  926.         $returnValue self::NA();
  927.  
  928.         // Loop through arguments
  929.         $aArgs self::flattenArray(func_get_args());
  930.  
  931.         $mArgs array();
  932.         foreach ($aArgs as $arg{
  933.             // Is it a numeric value?
  934.             if ((is_numeric($arg)) && (!is_string($arg))) {
  935.                 $mArgs[$arg;
  936.             }
  937.         }
  938.  
  939.         if (count($mArgs0{
  940.             return self::modeCalc($mArgs);
  941.         }
  942.  
  943.         // Return
  944.         return $returnValue;
  945.     }
  946.  
  947.     /**
  948.      * DEVSQ
  949.      *
  950.      * Returns the sum of squares of deviations of data points from their sample mean.
  951.      *
  952.      * @param    array of mixed        Data Series
  953.      * @return  float 
  954.      */
  955.     public static function DEVSQ({
  956.         // Return value
  957.         $returnValue null;
  958.  
  959.         $aMean self::AVERAGE(func_get_args());
  960.         if (!is_null($aMean)) {
  961.             $aArgs self::flattenArray(func_get_args());
  962.  
  963.             $aCount = -1;
  964.             foreach ($aArgs as $arg{
  965.                 // Is it a numeric value?
  966.                 if ((is_bool($arg))  && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  967.                     $arg = (int) $arg;
  968.                 }
  969.                 if ((is_numeric($arg)) && (!is_string($arg))) {
  970.                     if (is_null($returnValue)) {
  971.                         $returnValue pow(($arg $aMean),2);
  972.                     else {
  973.                         $returnValue += pow(($arg $aMean),2);
  974.                     }
  975.                     ++$aCount;
  976.                 }
  977.             }
  978.  
  979.             // Return
  980.             if (is_null($returnValue)) {
  981.                 return self::$_errorCodes['num'];
  982.             else {
  983.                 return $returnValue;
  984.             }
  985.         }
  986.         return self::NA();
  987.     }
  988.  
  989.     /**
  990.      * AVEDEV
  991.      *
  992.      * Returns the average of the absolute deviations of data points from their mean.
  993.      * AVEDEV is a measure of the variability in a data set.
  994.      *
  995.      * @param    array of mixed        Data Series
  996.      * @return  float 
  997.      */
  998.     public static function AVEDEV({
  999.         $aArgs self::flattenArray(func_get_args());
  1000.  
  1001.         // Return value
  1002.         $returnValue null;
  1003.  
  1004.         $aMean self::AVERAGE($aArgs);
  1005.         if ($aMean != self::$_errorCodes['divisionbyzero']{
  1006.             $aCount 0;
  1007.             foreach ($aArgs as $arg{
  1008.                 if ((is_bool($arg))  && (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE)) {
  1009.                     $arg = (integer) $arg;
  1010.                 }
  1011.                 // Is it a numeric value?
  1012.                 if ((is_numeric($arg)) && (!is_string($arg))) {
  1013.                     if (is_null($returnValue)) {
  1014.                         $returnValue abs($arg $aMean);
  1015.                     else {
  1016.                         $returnValue += abs($arg $aMean);
  1017.                     }
  1018.                     ++$aCount;
  1019.                 }
  1020.             }
  1021.  
  1022.             // Return
  1023.             return $returnValue $aCount ;
  1024.         }
  1025.         return self::$_errorCodes['num'];
  1026.     }
  1027.  
  1028.     /**
  1029.      * GEOMEAN
  1030.      *
  1031.      * Returns the geometric mean of an array or range of positive data. For example, you
  1032.      * can use GEOMEAN to calculate average growth rate given compound interest with
  1033.      * variable rates.
  1034.      *
  1035.      * @param    array of mixed        Data Series
  1036.      * @return  float 
  1037.      */
  1038.     public static function GEOMEAN({
  1039.         $aMean self::PRODUCT(func_get_args());
  1040.         if (is_numeric($aMean&& ($aMean 0)) {
  1041.             $aArgs self::flattenArray(func_get_args());
  1042.             $aCount self::COUNT($aArgs;
  1043.             if (self::MIN($aArgs0{
  1044.                 return pow($aMean($aCount));
  1045.             }
  1046.         }
  1047.         return self::$_errorCodes['num'];
  1048.     }
  1049.  
  1050.     /**
  1051.      * HARMEAN
  1052.      *
  1053.      * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
  1054.      * arithmetic mean of reciprocals.
  1055.      *
  1056.      * @param    array of mixed        Data Series
  1057.      * @return  float 
  1058.      */
  1059.     public static function HARMEAN({
  1060.         // Return value
  1061.         $returnValue self::NA();
  1062.  
  1063.         // Loop through arguments
  1064.         $aArgs self::flattenArray(func_get_args());
  1065.         if (self::MIN($aArgs0{
  1066.             return self::$_errorCodes['num'];
  1067.         }
  1068.         $aCount 0;
  1069.         foreach ($aArgs as $arg{
  1070.             // Is it a numeric value?
  1071.             if ((is_numeric($arg)) && (!is_string($arg))) {
  1072.                 if ($arg <= 0{
  1073.                     return self::$_errorCodes['num'];
  1074.                 }
  1075.                 if (is_null($returnValue)) {
  1076.                     $returnValue ($arg);
  1077.                 else {
  1078.                     $returnValue += ($arg);
  1079.                 }
  1080.                 ++$aCount;
  1081.             }
  1082.         }
  1083.  
  1084.         // Return
  1085.         if ($aCount 0{
  1086.             return ($returnValue $aCount);
  1087.         else {
  1088.             return $returnValue;
  1089.         }
  1090.     }
  1091.  
  1092.     /**
  1093.      * TRIMMEAN
  1094.      *
  1095.      * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
  1096.      * taken by excluding a percentage of data points from the top and bottom tails
  1097.      * of a data set.
  1098.      *
  1099.      * @param    array of mixed        Data Series
  1100.      * @param    float    Percentage to discard
  1101.      * @return    float 
  1102.      */
  1103.     public static function TRIMMEAN({
  1104.         $aArgs self::flattenArray(func_get_args());
  1105.  
  1106.         // Calculate
  1107.         $percent array_pop($aArgs);
  1108.  
  1109.         if ((is_numeric($percent)) && (!is_string($percent))) {
  1110.             if (($percent 0|| ($percent 1)) {
  1111.                 return self::$_errorCodes['num'];
  1112.             }
  1113.             $mArgs array();
  1114.             foreach ($aArgs as $arg{
  1115.                 // Is it a numeric value?
  1116.                 if ((is_numeric($arg)) && (!is_string($arg))) {
  1117.                     $mArgs[$arg;
  1118.                 }
  1119.             }
  1120.             $discard floor(self::COUNT($mArgs$percent 2);
  1121.             sort($mArgs);
  1122.             for ($i=0$i $discard++$i{
  1123.                 array_pop($mArgs);
  1124.                 array_shift($mArgs);
  1125.             }
  1126.             return self::AVERAGE($mArgs);
  1127.         }
  1128.         return self::$_errorCodes['value'];
  1129.     }
  1130.  
  1131.     /**
  1132.      * STDEV
  1133.      *
  1134.      * Estimates standard deviation based on a sample. The standard deviation is a measure of how
  1135.      * widely values are dispersed from the average value (the mean).
  1136.      *
  1137.      * @param    array of mixed        Data Series
  1138.      * @return  float 
  1139.      */
  1140.     public static function STDEV({
  1141.         // Return value
  1142.         $returnValue null;
  1143.  
  1144.         $aMean self::AVERAGE(func_get_args());
  1145.         if (!is_null($aMean)) {
  1146.             $aArgs self::flattenArray(func_get_args());
  1147.  
  1148.             $aCount = -1;
  1149.             foreach ($aArgs as $arg{
  1150.                 // Is it a numeric value?
  1151.                 if ((is_numeric($arg)) && (!is_string($arg))) {
  1152.                     if (is_null($returnValue)) {
  1153.                         $returnValue pow(($arg $aMean),2);
  1154.                     else {
  1155.                         $returnValue += pow(($arg $aMean),2);
  1156.                     }
  1157.                     ++$aCount;
  1158.                 }
  1159.             }
  1160.  
  1161.             // Return
  1162.             if (($aCount 0&& ($returnValue 0)) {
  1163.                 return sqrt($returnValue $aCount);
  1164.             }
  1165.         }
  1166.         return self::$_errorCodes['divisionbyzero'];
  1167.     }
  1168.  
  1169.     /**
  1170.      * STDEVA
  1171.      *
  1172.      * Estimates standard deviation based on a sample, including numbers, text, and logical values
  1173.      *
  1174.      * @param    array of mixed        Data Series
  1175.      * @return  float 
  1176.      */
  1177.     public static function STDEVA({
  1178.         // Return value
  1179.         $returnValue null;
  1180.  
  1181.         $aMean self::AVERAGEA(func_get_args());
  1182.         if (!is_null($aMean)) {
  1183.             $aArgs self::flattenArray(func_get_args());
  1184.  
  1185.             $aCount = -1;
  1186.             foreach ($aArgs as $arg{
  1187.                 // Is it a numeric value?
  1188.                 if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg($arg != '')))) {
  1189.                     if (is_bool($arg)) {
  1190.                         $arg = (integer) $arg;
  1191.                     elseif (is_string($arg)) {
  1192.                         $arg 0;
  1193.                     }
  1194.                     if (is_null($returnValue)) {
  1195.                         $returnValue pow(($arg $aMean),2);
  1196.                     else {
  1197.                         $returnValue += pow(($arg $aMean),2);
  1198.                     }
  1199.                     ++$aCount;
  1200.                 }
  1201.             }
  1202.  
  1203.             // Return
  1204.             if (($aCount 0&& ($returnValue 0)) {
  1205.                 return sqrt($returnValue $aCount);
  1206.             }
  1207.         }
  1208.         return self::$_errorCodes['divisionbyzero'];
  1209.     }
  1210.  
  1211.     /**
  1212.      * STDEVP
  1213.      *
  1214.      * Calculates standard deviation based on the entire population
  1215.      *
  1216.      * @param    array of mixed        Data Series
  1217.      * @return  float 
  1218.      */
  1219.     public static function STDEVP({
  1220.         // Return value
  1221.         $returnValue null;
  1222.  
  1223.         $aMean self::AVERAGE(func_get_args());
  1224.         if (!is_null($aMean)) {
  1225.             $aArgs self::flattenArray(func_get_args());
  1226.  
  1227.             $aCount 0;
  1228.             foreach ($aArgs as $arg{
  1229.                 // Is it a numeric value?
  1230.                 if ((is_numeric($arg)) && (!is_string($arg))) {
  1231.                     if (is_null($returnValue)) {
  1232.                         $returnValue pow(($arg $aMean),2);
  1233.                     else {
  1234.                         $returnValue += pow(($arg $aMean),2);
  1235.                     }
  1236.                     ++$aCount;
  1237.                 }
  1238.             }
  1239.  
  1240.             // Return
  1241.             if (($aCount 0&& ($returnValue 0)) {
  1242.                 return sqrt($returnValue $aCount);
  1243.             }
  1244.         }
  1245.         return self::$_errorCodes['divisionbyzero'];
  1246.     }
  1247.  
  1248.     /**
  1249.      * STDEVPA
  1250.      *
  1251.      * Calculates standard deviation based on the entire population, including numbers, text, and logical values
  1252.      *
  1253.      * @param    array of mixed        Data Series
  1254.      * @return  float 
  1255.      */
  1256.     public static function STDEVPA({
  1257.         // Return value
  1258.         $returnValue null;
  1259.  
  1260.         $aMean self::AVERAGEA(func_get_args());
  1261.         if (!is_null($aMean)) {
  1262.             $aArgs self::flattenArray(func_get_args());
  1263.  
  1264.             $aCount 0;
  1265.             foreach ($aArgs as $arg{
  1266.                 // Is it a numeric value?
  1267.                 if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg($arg != '')))) {
  1268.                     if (is_bool($arg)) {
  1269.                         $arg = (integer) $arg;
  1270.                     elseif (is_string($arg)) {
  1271.                         $arg 0;
  1272.                     }
  1273.                     if (is_null($returnValue)) {
  1274.                         $returnValue pow(($arg $aMean),2);
  1275.                     else {
  1276.                         $returnValue += pow(($arg $aMean),2);
  1277.                     }
  1278.                     ++$aCount;
  1279.                 }
  1280.             }
  1281.  
  1282.             // Return
  1283.             if (($aCount 0&& ($returnValue 0)) {
  1284.                 return sqrt($returnValue $aCount);
  1285.             }
  1286.         }
  1287.         return self::$_errorCodes['divisionbyzero'];
  1288.     }
  1289.  
  1290.     /**
  1291.      * VARFunc
  1292.      *
  1293.      * Estimates variance based on a sample.
  1294.      *
  1295.      * @param    array of mixed        Data Series
  1296.      * @return  float 
  1297.      */
  1298.     public static function VARFunc({
  1299.         // Return value
  1300.         $returnValue self::$_errorCodes['divisionbyzero'];
  1301.  
  1302.         $summerA $summerB 0;
  1303.  
  1304.         // Loop through arguments
  1305.         $aArgs self::flattenArray(func_get_args());
  1306.         $aCount 0;
  1307.         foreach ($aArgs as $arg{
  1308.             // Is it a numeric value?
  1309.             if ((is_numeric($arg)) && (!is_string($arg))) {
  1310.                 $summerA += ($arg $arg);
  1311.                 $summerB += $arg;
  1312.                 ++$aCount;
  1313.             }
  1314.         }
  1315.  
  1316.         // Return
  1317.         if ($aCount 1{
  1318.             $summerA $summerA $aCount;
  1319.             $summerB ($summerB $summerB);
  1320.             $returnValue ($summerA $summerB($aCount ($aCount 1));
  1321.         }
  1322.         return $returnValue;
  1323.     }
  1324.  
  1325.     /**
  1326.      * VARA
  1327.      *
  1328.      * Estimates variance based on a sample, including numbers, text, and logical values
  1329.      *
  1330.      * @param    array of mixed        Data Series
  1331.      * @return  float 
  1332.      */
  1333.     public static function VARA({
  1334.         // Return value
  1335.         $returnValue self::$_errorCodes['divisionbyzero'];
  1336.  
  1337.         $summerA $summerB 0;
  1338.  
  1339.         // Loop through arguments
  1340.         $aArgs self::flattenArray(func_get_args());
  1341.         $aCount 0;
  1342.         foreach ($aArgs as $arg{
  1343.             // Is it a numeric value?
  1344.                 if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg($arg != '')))) {
  1345.                 if (is_bool($arg)) {
  1346.                     $arg = (integer) $arg;
  1347.                 elseif (is_string($arg)) {
  1348.                     $arg 0;
  1349.                 }
  1350.                 $summerA += ($arg $arg);
  1351.                 $summerB += $arg;
  1352.                 ++$aCount;
  1353.             }
  1354.         }
  1355.  
  1356.         // Return
  1357.         if ($aCount 1{
  1358.             $summerA $summerA $aCount;
  1359.             $summerB ($summerB $summerB);
  1360.             $returnValue ($summerA $summerB($aCount ($aCount 1));
  1361.         }
  1362.         return $returnValue;
  1363.     }
  1364.  
  1365.     /**
  1366.      * VARP
  1367.      *
  1368.      * Calculates variance based on the entire population
  1369.      *
  1370.      * @param    array of mixed        Data Series
  1371.      * @return  float 
  1372.      */
  1373.     public static function VARP({
  1374.         // Return value
  1375.         $returnValue self::$_errorCodes['divisionbyzero'];
  1376.  
  1377.         $summerA $summerB 0;
  1378.  
  1379.         // Loop through arguments
  1380.         $aArgs self::flattenArray(func_get_args());
  1381.         $aCount 0;
  1382.         foreach ($aArgs as $arg{
  1383.             // Is it a numeric value?
  1384.             if ((is_numeric($arg)) && (!is_string($arg))) {
  1385.                 $summerA += ($arg $arg);
  1386.                 $summerB += $arg;
  1387.                 ++$aCount;
  1388.             }
  1389.         }
  1390.  
  1391.         // Return
  1392.         if ($aCount 0{
  1393.             $summerA $summerA $aCount;
  1394.             $summerB ($summerB $summerB);
  1395.             $returnValue ($summerA $summerB($aCount $aCount);
  1396.         }
  1397.         return $returnValue;
  1398.     }
  1399.  
  1400.     /**
  1401.      * VARPA
  1402.      *
  1403.      * Calculates variance based on the entire population, including numbers, text, and logical values
  1404.      *
  1405.      * @param    array of mixed        Data Series
  1406.      * @return  float 
  1407.      */
  1408.     public static function VARPA({
  1409.         // Return value
  1410.         $returnValue self::$_errorCodes['divisionbyzero'];
  1411.  
  1412.         $summerA $summerB 0;
  1413.  
  1414.         // Loop through arguments
  1415.         $aArgs self::flattenArray(func_get_args());
  1416.         $aCount 0;
  1417.         foreach ($aArgs as $arg{
  1418.             // Is it a numeric value?
  1419.             if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg($arg != '')))) {
  1420.                 if (is_bool($arg)) {
  1421.                     $arg = (integer) $arg;
  1422.                 elseif (is_string($arg)) {
  1423.                     $arg 0;
  1424.                 }
  1425.                 $summerA += ($arg $arg);
  1426.                 $summerB += $arg;
  1427.                 ++$aCount;
  1428.             }
  1429.         }
  1430.  
  1431.         // Return
  1432.         if ($aCount 0{
  1433.             $summerA $summerA $aCount;
  1434.             $summerB ($summerB $summerB);
  1435.             $returnValue ($summerA $summerB($aCount $aCount);
  1436.         }
  1437.         return $returnValue;
  1438.     }
  1439.  
  1440.     /**
  1441.      * SUBTOTAL
  1442.      *
  1443.      * Returns a subtotal in a list or database.
  1444.      *
  1445.      * @param    int        the number 1 to 11 that specifies which function to
  1446.      *                     use in calculating subtotals within a list.
  1447.      * @param    array of mixed        Data Series
  1448.      * @return    float 
  1449.      */
  1450.     public static function SUBTOTAL({
  1451.         $aArgs self::flattenArray(func_get_args());
  1452.  
  1453.         // Calculate
  1454.         $subtotal array_shift($aArgs);
  1455.  
  1456.         if ((is_numeric($subtotal)) && (!is_string($subtotal))) {
  1457.             switch($subtotal{
  1458.                 case 1    :
  1459.                     return self::AVERAGE($aArgs);
  1460.                     break;
  1461.                 case 2    :
  1462.                     return self::COUNT($aArgs);
  1463.                     break;
  1464.                 case 3    :
  1465.                     return self::COUNTA($aArgs);
  1466.                     break;
  1467.                 case 4    :
  1468.                     return self::MAX($aArgs);
  1469.                     break;
  1470.                 case 5    :
  1471.                     return self::MIN($aArgs);
  1472.                     break;
  1473.                 case 6    :
  1474.                     return self::PRODUCT($aArgs);
  1475.                     break;
  1476.                 case 7    :
  1477.                     return self::STDEV($aArgs);
  1478.                     break;
  1479.                 case 8    :
  1480.                     return self::STDEVP($aArgs);
  1481.                     break;
  1482.                 case 9    :
  1483.                     return self::SUM($aArgs);
  1484.                     break;
  1485.                 case 10    :
  1486.                     return self::VARFunc($aArgs);
  1487.                     break;
  1488.                 case 11    :
  1489.                     return self::VARP($aArgs);
  1490.                     break;
  1491.             }
  1492.         }
  1493.         return self::$_errorCodes['value'];
  1494.     }
  1495.  
  1496.     /**
  1497.      * SQRTPI
  1498.      *
  1499.      * Returns the square root of (number * pi).
  1500.      *
  1501.      * @param    float    $number        Number
  1502.      * @return  float    Square Root of Number * Pi
  1503.      */
  1504.     public static function SQRTPI($number{
  1505.         $number    self::flattenSingleValue($number);
  1506.  
  1507.         if (is_numeric($number)) {
  1508.             if ($number 0{
  1509.                 return self::$_errorCodes['num'];
  1510.             }
  1511.             return sqrt($number pi()) ;
  1512.         }
  1513.         return self::$_errorCodes['value'];
  1514.     }
  1515.  
  1516.     /**
  1517.      * FACT
  1518.      *
  1519.      * Returns the factorial of a number.
  1520.      *
  1521.      * @param    float    $factVal    Factorial Value
  1522.      * @return  int        Factorial
  1523.      */
  1524.     public static function FACT($factVal{
  1525.         $factVal    self::flattenSingleValue($factVal);
  1526.  
  1527.         if (is_numeric($factVal)) {
  1528.             if ($factVal 0{
  1529.                 return self::$_errorCodes['num'];
  1530.             }
  1531.             $factLoop floor($factVal);
  1532.             if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC{
  1533.                 if ($factVal $factLoop{
  1534.                     return self::$_errorCodes['num'];
  1535.                 }
  1536.             }
  1537.             $factorial 1;
  1538.             while ($factLoop 1{
  1539.                 $factorial *= $factLoop--;
  1540.             }
  1541.             return $factorial ;
  1542.         }
  1543.         return self::$_errorCodes['value'];
  1544.     }
  1545.  
  1546.     /**
  1547.      * FACTDOUBLE
  1548.      *
  1549.      * Returns the double factorial of a number.
  1550.      *
  1551.      * @param    float    $factVal    Factorial Value
  1552.      * @return  int        Double Factorial
  1553.      */
  1554.     public static function FACTDOUBLE($factVal{
  1555.         $factLoop    floor(self::flattenSingleValue($factVal));
  1556.  
  1557.         if (is_numeric($factLoop)) {
  1558.             if ($factVal 0{
  1559.                 return self::$_errorCodes['num'];
  1560.             }
  1561.             $factorial 1;
  1562.             while ($factLoop 1{
  1563.                 $factorial *= $factLoop--;
  1564.                 --$factLoop;
  1565.             }
  1566.             return $factorial ;
  1567.         }
  1568.         return self::$_errorCodes['value'];
  1569.     }
  1570.  
  1571.     /**
  1572.      * MULTINOMIAL
  1573.      *
  1574.      * Returns the ratio of the factorial of a sum of values to the product of factorials.
  1575.      *
  1576.      * @param    array of mixed        Data Series
  1577.      * @return  float 
  1578.      */
  1579.     public static function MULTINOMIAL({
  1580.  
  1581.         // Loop through arguments
  1582.         $aArgs self::flattenArray(func_get_args());
  1583.         $summer 0;
  1584.         $divisor 1;
  1585.         foreach ($aArgs as $arg{
  1586.             // Is it a numeric value?
  1587.             if (is_numeric($arg)) {
  1588.                 if ($arg 1{
  1589.                     return self::$_errorCodes['num'];
  1590.                 }
  1591.                 $summer += floor($arg);
  1592.                 $divisor *= self::FACT($arg);
  1593.             else {
  1594.                 return self::$_errorCodes['value'];
  1595.             }
  1596.         }
  1597.  
  1598.         // Return
  1599.         if ($summer 0{
  1600.             $summer self::FACT($summer);
  1601.             return $summer $divisor;
  1602.         }
  1603.         return 0;
  1604.     }
  1605.  
  1606.     /**
  1607.      * CEILING
  1608.      *
  1609.      * Returns number rounded up, away from zero, to the nearest multiple of significance.
  1610.      *
  1611.      * @param    float    $number            Number to round
  1612.      * @param    float    $significance    Significance
  1613.      * @return  float    Rounded Number
  1614.      */
  1615.     public static function CEILING($number,$significance=null{
  1616.         $number            self::flattenSingleValue($number);
  1617.         $significance    self::flattenSingleValue($significance);
  1618.  
  1619.         if ((is_null($significance)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
  1620.             $significance $number/abs($number);
  1621.         }
  1622.  
  1623.         if ((is_numeric($number)) && (is_numeric($significance))) {
  1624.             if (self::SIGN($number== self::SIGN($significance)) {
  1625.                 if ($significance == 0.0{
  1626.                     return 0;
  1627.                 }
  1628.                 return ceil($number $significance$significance;
  1629.             else {
  1630.                 return self::$_errorCodes['num'];
  1631.             }
  1632.         }
  1633.         return self::$_errorCodes['value'];
  1634.     }
  1635.  
  1636.     /**
  1637.      * EVEN
  1638.      *
  1639.      * Returns number rounded up to the nearest even integer.
  1640.      *
  1641.      * @param    float    $number            Number to round
  1642.      * @return  int        Rounded Number
  1643.      */
  1644.     public static function EVEN($number{
  1645.         $number    self::flattenSingleValue($number);
  1646.  
  1647.         if (is_numeric($number)) {
  1648.             $significance self::SIGN($number);
  1649.             return self::CEILING($number,$significance);
  1650.         }
  1651.         return self::$_errorCodes['value'];
  1652.     }
  1653.  
  1654.     /**
  1655.      * ODD
  1656.      *
  1657.      * Returns number rounded up to the nearest odd integer.
  1658.      *
  1659.      * @param    float    $number            Number to round
  1660.      * @return  int        Rounded Number
  1661.      */
  1662.     public static function ODD($number{
  1663.         $number    self::flattenSingleValue($number);
  1664.  
  1665.         if (is_numeric($number)) {
  1666.             $significance self::SIGN($number);
  1667.             if ($significance == 0{
  1668.                 return 1;
  1669.             }
  1670.             $result self::CEILING($number,$significance);
  1671.             if (self::IS_EVEN($result)) {
  1672.                 $result += $significance;
  1673.             }
  1674.             return $result;
  1675.         }
  1676.         return self::$_errorCodes['value'];
  1677.     }
  1678.  
  1679.     /**
  1680.      * ROUNDUP
  1681.      *
  1682.      * Rounds a number up to a specified number of decimal places
  1683.      *
  1684.      * @param    float    $number            Number to round
  1685.      * @param    int        $digits            Number of digits to which you want to round $number
  1686.      * @return  float    Rounded Number
  1687.      */
  1688.     public static function ROUNDUP($number,$digits{
  1689.         $number    self::flattenSingleValue($number);
  1690.         $digits    self::flattenSingleValue($digits);
  1691.  
  1692.         if (is_numeric($number)) {
  1693.             if ((is_numeric($digits)) && ($digits >= 0)) {
  1694.                 $significance pow(10,$digits);
  1695.                 return ceil($number $significance$significance;
  1696.             }
  1697.         }
  1698.         return self::$_errorCodes['value'];
  1699.     }
  1700.  
  1701.     /**
  1702.      * ROUNDDOWN
  1703.      *
  1704.      * Rounds a number down to a specified number of decimal places
  1705.      *
  1706.      * @param    float    $number            Number to round
  1707.      * @param    int        $digits            Number of digits to which you want to round $number
  1708.      * @return  float    Rounded Number
  1709.      */
  1710.     public static function ROUNDDOWN($number,$digits{
  1711.         $number    self::flattenSingleValue($number);
  1712.         $digits    self::flattenSingleValue($digits);
  1713.  
  1714.         if (is_numeric($number)) {
  1715.             if ((is_numeric($digits)) && ($digits >= 0)) {
  1716.                 $significance pow(10,$digits);
  1717.                 return floor($number $significance$significance;
  1718.             }
  1719.         }
  1720.         return self::$_errorCodes['value'];
  1721.     }
  1722.  
  1723.     /**
  1724.      * MROUND
  1725.      *
  1726.      * Rounds a number to the nearest multiple of a specified value
  1727.      *
  1728.      * @param    float    $number            Number to round
  1729.      * @param    int        $multiple        Multiple to which you want to round $number
  1730.      * @return  float    Rounded Number
  1731.      */
  1732.     public static function MROUND($number,$multiple{
  1733.         $number        self::flattenSingleValue($number);
  1734.         $multiple    self::flattenSingleValue($multiple);
  1735.  
  1736.         if ((is_numeric($number)) && (is_numeric($multiple))) {
  1737.             if ((self::SIGN($number)) == (self::SIGN($multiple))) {
  1738.                 $lowerVal floor($number $multiple$multiple;
  1739.                 $upperVal ceil($number $multiple$multiple;
  1740.                 $adjustUp abs($number $upperVal);
  1741.                 $adjustDown abs($number $lowerValPRECISION;
  1742.                 if ($adjustDown $adjustUp{
  1743.                     return $lowerVal;
  1744.                 }
  1745.                 return $upperVal;
  1746.             }
  1747.             return self::$_errorCodes['num'];
  1748.         }
  1749.         return self::$_errorCodes['value'];
  1750.     }
  1751.  
  1752.     /**
  1753.      * SIGN
  1754.      *
  1755.      * Determines the sign of a number. Returns 1 if the number is positive, zero (0)
  1756.      * if the number is 0, and -1 if the number is negative.
  1757.      *
  1758.      * @param    float    $number            Number to round
  1759.      * @return  int        sign value
  1760.      */
  1761.     public static function SIGN($number{
  1762.         $number    self::flattenSingleValue($number);
  1763.  
  1764.         if (is_numeric($number)) {
  1765.             if ($number == 0.0{
  1766.                 return 0;
  1767.             }
  1768.             return $number abs($number);
  1769.         }
  1770.         return self::$_errorCodes['value'];
  1771.     }
  1772.  
  1773.     /**
  1774.      * FLOOR
  1775.      *
  1776.      * Rounds number down, toward zero, to the nearest multiple of significance.
  1777.      *
  1778.      * @param    float    $number            Number to round
  1779.      * @param    float    $significance    Significance
  1780.      * @return  float    Rounded Number
  1781.      */
  1782.     public static function FLOOR($number,$significance=null{
  1783.         $number            self::flattenSingleValue($number);
  1784.         $significance    self::flattenSingleValue($significance);
  1785.  
  1786.         if ((is_null($significance)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
  1787.             $significance $number/abs($number);
  1788.         }
  1789.  
  1790.         if ((is_numeric($number)) && (is_numeric($significance))) {
  1791.             if ((float) $significance == 0.0{
  1792.                 return self::$_errorCodes['divisionbyzero'];
  1793.             }
  1794.             if (self::SIGN($number== self::SIGN($significance)) {
  1795.                 return floor($number $significance$significance;
  1796.             else {
  1797.                 return self::$_errorCodes['num'];
  1798.             }
  1799.         }
  1800.         return self::$_errorCodes['value'];
  1801.     }
  1802.  
  1803.     /**
  1804.      * PERMUT
  1805.      *
  1806.      * Returns the number of permutations for a given number of objects that can be
  1807.      * selected from number objects. A permutation is any set or subset of objects or
  1808.      * events where internal order is significant. Permutations are different from
  1809.      * combinations, for which the internal order is not significant. Use this function
  1810.      * for lottery-style probability calculations.
  1811.      *
  1812.      * @param    int        $numObjs    Number of different objects
  1813.      * @param    int        $numInSet    Number of objects in each permutation
  1814.      * @return  int        Number of permutations
  1815.      */
  1816.     public static function PERMUT($numObjs,$numInSet{
  1817.         $numObjs    self::flattenSingleValue($numObjs);
  1818.         $numInSet    self::flattenSingleValue($numInSet);
  1819.  
  1820.         if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
  1821.             if ($numObjs $numInSet{
  1822.                 return self::$_errorCodes['num'];
  1823.             }
  1824.             return self::FACT($numObjsself::FACT($numObjs $numInSet);
  1825.         }
  1826.         return self::$_errorCodes['value'];
  1827.     }
  1828.  
  1829.     /**
  1830.      * COMBIN
  1831.      *
  1832.      * Returns the number of combinations for a given number of items. Use COMBIN to
  1833.      * determine the total possible number of groups for a given number of items.
  1834.      *
  1835.      * @param    int        $numObjs    Number of different objects
  1836.      * @param    int        $numInSet    Number of objects in each combination
  1837.      * @return  int        Number of combinations
  1838.      */
  1839.     public static function COMBIN($numObjs,$numInSet{
  1840.         $numObjs    self::flattenSingleValue($numObjs);
  1841.         $numInSet    self::flattenSingleValue($numInSet);
  1842.  
  1843.         if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
  1844.             if ($numObjs $numInSet{
  1845.                 return self::$_errorCodes['num'];
  1846.             elseif ($numInSet 0{
  1847.                 return self::$_errorCodes['num'];
  1848.             }
  1849.             return (self::FACT($numObjsself::FACT($numObjs $numInSet)) self::FACT($numInSet);
  1850.         }
  1851.         return self::$_errorCodes['value'];
  1852.     }
  1853.  
  1854.     /**
  1855.      * SERIESSUM
  1856.      *
  1857.      * Returns the sum of a power series
  1858.      *
  1859.      * @param    float            $x    Input value to the power series
  1860.      * @param    float            $n    Initial power to which you want to raise $x
  1861.      * @param    float            $m    Step by which to increase $n for each term in the series
  1862.      * @param    array of mixed        Data Series
  1863.      * @return    float 
  1864.      */
  1865.     public static function SERIESSUM({
  1866.         // Return value
  1867.         $returnValue 0;
  1868.  
  1869.         // Loop trough arguments
  1870.         $aArgs self::flattenArray(func_get_args());
  1871.  
  1872.         $x array_shift($aArgs);
  1873.         $n array_shift($aArgs);
  1874.         $m array_shift($aArgs);
  1875.  
  1876.         if ((is_numeric($x)) && (is_numeric($n)) && (is_numeric($m))) {
  1877.             // Calculate
  1878.             $i 0;
  1879.             foreach($aArgs as $arg{
  1880.                 // Is it a numeric value?
  1881.                 if ((is_numeric($arg)) && (!is_string($arg))) {
  1882.                     $returnValue += $arg pow($x,$n ($m $i++));
  1883.                 else {
  1884.                     return self::$_errorCodes['value'];
  1885.                 }
  1886.             }
  1887.             // Return
  1888.             return $returnValue;
  1889.         }
  1890.         return self::$_errorCodes['value'];
  1891.     }
  1892.  
  1893.     /**
  1894.      * STANDARDIZE
  1895.      *
  1896.      * Returns a normalized value from a distribution characterized by mean and standard_dev.
  1897.      *
  1898.      * @param    float    $value        Value to normalize
  1899.      * @param    float    $mean        Mean Value
  1900.      * @param    float    $stdDev        Standard Deviation
  1901.      * @return  float    Standardized value
  1902.      */
  1903.     public static function STANDARDIZE($value,$mean,$stdDev{
  1904.         $value    self::flattenSingleValue($value);
  1905.         $mean    self::flattenSingleValue($mean);
  1906.         $stdDev    self::flattenSingleValue($stdDev);
  1907.  
  1908.         if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  1909.             if ($stdDev <= 0{
  1910.                 return self::$_errorCodes['num'];
  1911.             }
  1912.             return ($value $mean$stdDev ;
  1913.         }
  1914.         return self::$_errorCodes['value'];
  1915.     }
  1916.  
  1917.     //
  1918.     //    Private method to return an array of the factors of the input value
  1919.     //
  1920.     private static function factors($value{
  1921.         $startVal floor(sqrt($value));
  1922.  
  1923.         $factorArray array();
  1924.         for ($i $startVal$i 1--$i{
  1925.             if (($value $i== 0{
  1926.                 $factorArray array_merge($factorArray,self::factors($value $i));
  1927.                 $factorArray array_merge($factorArray,self::factors($i));
  1928.                 if ($i <= sqrt($value)) {
  1929.                     break;
  1930.                 }
  1931.             }
  1932.         }
  1933.         if (count($factorArray0{
  1934.             rsort($factorArray);
  1935.             return $factorArray;
  1936.         else {
  1937.             return array((integer) $value);
  1938.         }
  1939.     }
  1940.  
  1941.     /**
  1942.      * LCM
  1943.      *
  1944.      * Returns the lowest common multiplier of a series of numbers
  1945.      *
  1946.      * @param    $array    Values to calculate the Lowest Common Multiplier
  1947.      * @return  int        Lowest Common Multiplier
  1948.      */
  1949.     public static function LCM({
  1950.         $aArgs self::flattenArray(func_get_args());
  1951.  
  1952.         $returnValue 1;
  1953.         $allPoweredFactors array();
  1954.         foreach($aArgs as $value{
  1955.             if (!is_numeric($value)) {
  1956.                 return self::$_errorCodes['value'];
  1957.             }
  1958.             if ($value 1{
  1959.                 return self::$_errorCodes['num'];
  1960.             }
  1961.             $myFactors self::factors(floor($value));
  1962.             $myCountedFactors array_count_values($myFactors);
  1963.             $myPoweredFactors array();
  1964.             foreach($myCountedFactors as $myCountedFactor => $myCountedPower{
  1965.                 $myPoweredFactors[$myCountedFactorpow($myCountedFactor,$myCountedPower);
  1966.             }
  1967.             foreach($myPoweredFactors as $myPoweredValue => $myPoweredFactor{
  1968.                 if (array_key_exists($myPoweredValue,$allPoweredFactors)) {
  1969.                     if ($allPoweredFactors[$myPoweredValue$myPoweredFactor{
  1970.                         $allPoweredFactors[$myPoweredValue$myPoweredFactor;
  1971.                     }
  1972.                 else {
  1973.                     $allPoweredFactors[$myPoweredValue$myPoweredFactor;
  1974.                 }
  1975.             }
  1976.         }
  1977.         foreach($allPoweredFactors as $allPoweredFactor{
  1978.             $returnValue *= (integer) $allPoweredFactor;
  1979.         }
  1980.         return $returnValue;
  1981.     }
  1982.  
  1983.     /**
  1984.      * GCD
  1985.      *
  1986.      * Returns the greatest common divisor of a series of numbers
  1987.      *
  1988.      * @param    $array    Values to calculate the Greatest Common Divisor
  1989.      * @return  int        Greatest Common Divisor
  1990.      */
  1991.     public static function GCD({
  1992.         $aArgs self::flattenArray(func_get_args());
  1993.  
  1994.         $returnValue 1;
  1995.         $allPoweredFactors array();
  1996.         foreach($aArgs as $value{
  1997.             if ($value == 0{
  1998.                 return 0;
  1999.             }
  2000.             $myFactors self::factors($value);
  2001.             $myCountedFactors array_count_values($myFactors);
  2002.             $allValuesFactors[$myCountedFactors;
  2003.         }
  2004.         $allValuesCount count($allValuesFactors);
  2005.         $mergedArray $allValuesFactors[0];
  2006.         for ($i=1;$i $allValuesCount++$i{
  2007.             $mergedArray array_intersect_key($mergedArray,$allValuesFactors[$i]);
  2008.         }
  2009.         $mergedArrayValues count($mergedArray);
  2010.         if ($mergedArrayValues == 0{
  2011.             return $returnValue;
  2012.         elseif ($mergedArrayValues 1{
  2013.             foreach($mergedArray as $mergedKey => $mergedValue{
  2014.                 foreach($allValuesFactors as $highestPowerTest{
  2015.                     foreach($highestPowerTest as $testKey => $testValue{
  2016.                         if (($testKey == $mergedKey&& ($testValue $mergedValue)) {
  2017.                             $mergedArray[$mergedKey$testValue;
  2018.                             $mergedValue $testValue;
  2019.                         }
  2020.                     }
  2021.                 }
  2022.             }
  2023.  
  2024.             $returnValue 1;
  2025.             foreach($mergedArray as $key => $value{
  2026.                 $returnValue *= pow($key,$value);
  2027.             }
  2028.             return $returnValue;
  2029.         else {
  2030.             $keys array_keys($mergedArray);
  2031.             $key $keys[0];
  2032.             $value $mergedArray[$key];
  2033.             foreach($allValuesFactors as $testValue{
  2034.                 foreach($testValue as $mergedKey => $mergedValue{
  2035.                     if (($mergedKey == $key&& ($mergedValue $value)) {
  2036.                         $value $mergedValue;
  2037.                     }
  2038.                 }
  2039.             }
  2040.             return pow($key,$value);
  2041.         }
  2042.     }
  2043.  
  2044.     /**
  2045.      * BINOMDIST
  2046.      *
  2047.      * Returns the individual term binomial distribution probability. Use BINOMDIST in problems with
  2048.      * a fixed number of tests or trials, when the outcomes of any trial are only success or failure,
  2049.      * when trials are independent, and when the probability of success is constant throughout the
  2050.      * experiment. For example, BINOMDIST can calculate the probability that two of the next three
  2051.      * babies born are male.
  2052.      *
  2053.      * @param    float        $value            Number of successes in trials
  2054.      * @param    float        $trials            Number of trials
  2055.      * @param    float        $probability    Probability of success on each trial
  2056.      * @param    boolean        $cumulative 
  2057.      * @return  float 
  2058.      *
  2059.      * @todo    Cumulative distribution function
  2060.      *
  2061.      */
  2062.     public static function BINOMDIST($value$trials$probability$cumulative{
  2063.         $value            floor(self::flattenSingleValue($value));
  2064.         $trials            floor(self::flattenSingleValue($trials));
  2065.         $probability    self::flattenSingleValue($probability);
  2066.  
  2067.         if ((is_numeric($value)) && (is_numeric($trials)) && (is_numeric($probability))) {
  2068.             if (($value 0|| ($value $trials)) {
  2069.                 return self::$_errorCodes['num'];
  2070.             }
  2071.             if (($probability 0|| ($probability 1)) {
  2072.                 return self::$_errorCodes['num'];
  2073.             }
  2074.             if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  2075.                 if ($cumulative{
  2076.                     $summer 0;
  2077.                     for ($i 0$i <= $value++$i{
  2078.                         $summer += self::COMBIN($trials,$ipow($probability,$ipow($probability,$trials $i);
  2079.                     }
  2080.                     return $summer;
  2081.                 else {
  2082.                     return self::COMBIN($trials,$valuepow($probability,$valuepow($probability,$trials $value;
  2083.                 }
  2084.             }
  2085.         }
  2086.         return self::$_errorCodes['value'];
  2087.     }
  2088.  
  2089.     /**
  2090.      * NEGBINOMDIST
  2091.      *
  2092.      * Returns the negative binomial distribution. NEGBINOMDIST returns the probability that
  2093.      * there will be number_f failures before the number_s-th success, when the constant
  2094.      * probability of a success is probability_s. This function is similar to the binomial
  2095.      * distribution, except that the number of successes is fixed, and the number of trials is
  2096.      * variable. Like the binomial, trials are assumed to be independent.
  2097.      *
  2098.      * @param    float        $failures        Number of Failures
  2099.      * @param    float        $successes        Threshold number of Successes
  2100.      * @param    float        $probability    Probability of success on each trial
  2101.      * @return  float 
  2102.      *
  2103.      */
  2104.     public static function NEGBINOMDIST($failures$successes$probability{
  2105.         $failures        floor(self::flattenSingleValue($failures));
  2106.         $successes        floor(self::flattenSingleValue($successes));
  2107.         $probability    self::flattenSingleValue($probability);
  2108.  
  2109.         if ((is_numeric($failures)) && (is_numeric($successes)) && (is_numeric($probability))) {
  2110.             if (($failures 0|| ($successes 1)) {
  2111.                 return self::$_errorCodes['num'];
  2112.             }
  2113.             if (($probability 0|| ($probability 1)) {
  2114.                 return self::$_errorCodes['num'];
  2115.             }
  2116.             if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC{
  2117.                 if (($failures $successes 1<= 0{
  2118.                     return self::$_errorCodes['num'];
  2119.                 }
  2120.             }
  2121.             return (self::COMBIN($failures $successes 1,$successes 1)) (pow($probability,$successes)) (pow($probability,$failures)) ;
  2122.         }
  2123.         return self::$_errorCodes['value'];
  2124.     }
  2125.  
  2126.  
  2127.     /**
  2128.      * CRITBINOM
  2129.      *
  2130.      * Returns the smallest value for which the cumulative binomial distribution is greater
  2131.      * than or equal to a criterion value
  2132.      *
  2133.      * See http://support.microsoft.com/kb/828117/ for details of the algorithm used
  2134.      *
  2135.      * @param    float        $trials            number of Bernoulli trials
  2136.      * @param    float        $probability    probability of a success on each trial
  2137.      * @param    float        $alpha            criterion value
  2138.      * @return  int 
  2139.      *
  2140.      *    @todo    Warning. This implementation differs from the algorithm detailed on the MS
  2141.      *             web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess
  2142.      *             This eliminates a potential endless loop error, but may have an adverse affect on the
  2143.      *             accuracy of the function (although all my tests have so far returned correct results).
  2144.      *
  2145.      */
  2146.     public static function CRITBINOM($trials$probability$alpha{
  2147.         $trials            floor(self::flattenSingleValue($trials));
  2148.         $probability    self::flattenSingleValue($probability);
  2149.         $alpha            self::flattenSingleValue($alpha);
  2150.  
  2151.         if ((is_numeric($trials)) && (is_numeric($probability)) && (is_numeric($alpha))) {
  2152.             if ($trials 0{
  2153.                 return self::$_errorCodes['num'];
  2154.             }
  2155.             if (($probability 0|| ($probability 1)) {
  2156.                 return self::$_errorCodes['num'];
  2157.             }
  2158.             if (($alpha 0|| ($alpha 1)) {
  2159.                 return self::$_errorCodes['num'];
  2160.             }
  2161.             if ($alpha <= 0.5{
  2162.                 $t sqrt(log(pow($alpha,2)));
  2163.                 $trialsApprox ($t (2.515517 0.802853 $t 0.010328 $t $t(1.432788 $t 0.189269 $t $t 0.001308 $t $t $t));
  2164.             else {
  2165.                 $t sqrt(log(pow($alpha,2)));
  2166.                 $trialsApprox $t (2.515517 0.802853 $t 0.010328 $t $t(1.432788 $t 0.189269 $t $t 0.001308 $t $t $t);
  2167.             }
  2168.             $Guess floor($trials $probability $trialsApprox sqrt($trials $probability ($probability)));
  2169.             if ($Guess 0{
  2170.                 $Guess 0;
  2171.             elseif ($Guess $trials{
  2172.                 $Guess $trials;
  2173.             }
  2174.  
  2175.             $TotalUnscaledProbability $UnscaledPGuess $UnscaledCumPGuess 0.0;
  2176.             $EssentiallyZero 10e-12;
  2177.  
  2178.             $m floor($trials $probability);
  2179.             ++$TotalUnscaledProbability;
  2180.             if ($m == $Guess++$UnscaledPGuess}
  2181.             if ($m <= $Guess++$UnscaledCumPGuess}
  2182.  
  2183.             $PreviousValue 1;
  2184.             $Done False;
  2185.             $k $m 1;
  2186.             while ((!$Done&& ($k <= $trials)) {
  2187.                 $CurrentValue $PreviousValue ($trials $k 1$probability ($k ($probability));
  2188.                 $TotalUnscaledProbability += $CurrentValue;
  2189.                 if ($k == $Guess$UnscaledPGuess += $CurrentValue}
  2190.                 if ($k <= $Guess$UnscaledCumPGuess += $CurrentValue}
  2191.                 if ($CurrentValue <= $EssentiallyZero$Done True}
  2192.                 $PreviousValue $CurrentValue;
  2193.                 ++$k;
  2194.             }
  2195.  
  2196.             $PreviousValue 1;
  2197.             $Done False;
  2198.             $k $m 1;
  2199.             while ((!$Done&& ($k >= 0)) {
  2200.                 $CurrentValue $PreviousValue $k ($probability(($trials $k$probability);
  2201.                 $TotalUnscaledProbability += $CurrentValue;
  2202.                 if ($k == $Guess$UnscaledPGuess += $CurrentValue}
  2203.                 if ($k <= $Guess$UnscaledCumPGuess += $CurrentValue}
  2204.                 if (CurrentValue <= EssentiallyZero$Done True}
  2205.                 $PreviousValue $CurrentValue;
  2206.                 --$k;
  2207.             }
  2208.  
  2209.             $PGuess $UnscaledPGuess $TotalUnscaledProbability;
  2210.             $CumPGuess $UnscaledCumPGuess $TotalUnscaledProbability;
  2211.  
  2212. //            $CumPGuessMinus1 = $CumPGuess - $PGuess;
  2213.             $CumPGuessMinus1 $CumPGuess 1;
  2214.  
  2215.             while (True{
  2216.                 if (($CumPGuessMinus1 $alpha&& ($CumPGuess >= $alpha)) {
  2217.                     return $Guess;
  2218.                 elseif (($CumPGuessMinus1 $alpha&& ($CumPGuess $alpha)) {
  2219.                     $PGuessPlus1 $PGuess ($trials $Guess$probability $Guess ($probability);
  2220.                     $CumPGuessMinus1 $CumPGuess;
  2221.                     $CumPGuess $CumPGuess $PGuessPlus1;
  2222.                     $PGuess $PGuessPlus1;
  2223.                     ++$Guess;
  2224.                 elseif (($CumPGuessMinus1 >= $alpha&& ($CumPGuess >= $alpha)) {
  2225.                     $PGuessMinus1 $PGuess $Guess ($probability($trials $Guess 1$probability;
  2226.                     $CumPGuess $CumPGuessMinus1;
  2227.                     $CumPGuessMinus1 $CumPGuessMinus1 $PGuess;
  2228.                     $PGuess $PGuessMinus1;
  2229.                     --$Guess;
  2230.                 }
  2231.             }
  2232.         }
  2233.         return self::$_errorCodes['value'];
  2234.     }
  2235.  
  2236.     /**
  2237.      * CHIDIST
  2238.      *
  2239.      * Returns the one-tailed probability of the chi-squared distribution.
  2240.      *
  2241.      * @param    float        $value            Value for the function
  2242.      * @param    float        $degrees        degrees of freedom
  2243.      * @return  float 
  2244.      */
  2245.     public static function CHIDIST($value$degrees{
  2246.         $value        self::flattenSingleValue($value);
  2247.         $degrees    floor(self::flattenSingleValue($degrees));
  2248.  
  2249.         if ((is_numeric($value)) && (is_numeric($degrees))) {
  2250.             if ($degrees 1{
  2251.                 return self::$_errorCodes['num'];
  2252.             }
  2253.             if ($value 0{
  2254.                 if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC{
  2255.                     return 1;
  2256.                 }
  2257.                 return self::$_errorCodes['num'];
  2258.             }
  2259.             return (self::incompleteGamma($degrees/2,$value/2self::gamma($degrees/2));
  2260.         }
  2261.         return self::$_errorCodes['value'];
  2262.     }
  2263.  
  2264.     /**
  2265.      * CHIINV
  2266.      *
  2267.      * Returns the one-tailed probability of the chi-squared distribution.
  2268.      *
  2269.      * @param    float        $probability    Probability for the function
  2270.      * @param    float        $degrees        degrees of freedom
  2271.      * @return  float 
  2272.      */
  2273.     public static function CHIINV($probability$degrees{
  2274.         $probability    self::flattenSingleValue($probability);
  2275.         $degrees        floor(self::flattenSingleValue($degrees));
  2276.  
  2277.         if ((is_numeric($probability)) && (is_numeric($degrees))) {
  2278.             $xLo 100;
  2279.             $xHi 0;
  2280.             $maxIteration 100;
  2281.  
  2282.             $x $xNew 1;
  2283.             $dx    1;
  2284.             $i 0;
  2285.  
  2286.             while ((abs($dxPRECISION&& ($i++ < MAX_ITERATIONS)) {
  2287.                 // Apply Newton-Raphson step
  2288.                 $result self::CHIDIST($x$degrees);
  2289.                 $error $result $probability;
  2290.                 if ($error == 0.0{
  2291.                     $dx 0;
  2292.                 elseif ($error 0.0{
  2293.                     $xLo $x;
  2294.                 else {
  2295.                     $xHi $x;
  2296.                 }
  2297.                 // Avoid division by zero
  2298.                 if ($result != 0.0{
  2299.                     $dx $error $result;
  2300.                     $xNew $x $dx;
  2301.                 }
  2302.                 // If the NR fails to converge (which for example may be the
  2303.                 // case if the initial guess is too rough) we apply a bisection
  2304.                 // step to determine a more narrow interval around the root.
  2305.                 if (($xNew $xLo|| ($xNew $xHi|| ($result == 0.0)) {
  2306.                     $xNew ($xLo $xHi2;
  2307.                     $dx $xNew $x;
  2308.                 }
  2309.                 $x $xNew;
  2310.             }
  2311.             if ($i == MAX_ITERATIONS{
  2312.                 return self::$_errorCodes['na'];
  2313.             }
  2314.             return round($x,12);
  2315.         }
  2316.         return self::$_errorCodes['value'];
  2317.     }
  2318.  
  2319.     /**
  2320.      * EXPONDIST
  2321.      *
  2322.      * Returns the exponential distribution. Use EXPONDIST to model the time between events,
  2323.      * such as how long an automated bank teller takes to deliver cash. For example, you can
  2324.      * use EXPONDIST to determine the probability that the process takes at most 1 minute.
  2325.      *
  2326.      * @param    float        $value            Value of the function
  2327.      * @param    float        $lambda            The parameter value
  2328.      * @param    boolean        $cumulative 
  2329.      * @return  float 
  2330.      */
  2331.     public static function EXPONDIST($value$lambda$cumulative{
  2332.         $value    self::flattenSingleValue($value);
  2333.         $lambda    self::flattenSingleValue($lambda);
  2334.         $cumulative    self::flattenSingleValue($cumulative);
  2335.  
  2336.         if ((is_numeric($value)) && (is_numeric($lambda))) {
  2337.             if (($value 0|| ($lambda 0)) {
  2338.                 return self::$_errorCodes['num'];
  2339.             }
  2340.             if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  2341.                 if ($cumulative{
  2342.                     return exp(0-$value*$lambda);
  2343.                 else {
  2344.                     return $lambda exp(0-$value*$lambda);
  2345.                 }
  2346.             }
  2347.         }
  2348.         return self::$_errorCodes['value'];
  2349.     }
  2350.  
  2351.     /**
  2352.      * FISHER
  2353.      *
  2354.      * Returns the Fisher transformation at x. This transformation produces a function that
  2355.      * is normally distributed rather than skewed. Use this function to perform hypothesis
  2356.      * testing on the correlation coefficient.
  2357.      *
  2358.      * @param    float        $value 
  2359.      * @return  float 
  2360.      */
  2361.     public static function FISHER($value{
  2362.         $value    self::flattenSingleValue($value);
  2363.  
  2364.         if (is_numeric($value)) {
  2365.             if (($value <= -1|| ($lambda >= 1)) {
  2366.                 return self::$_errorCodes['num'];
  2367.             }
  2368.             return 0.5 log((1+$value)/(1-$value));
  2369.         }
  2370.         return self::$_errorCodes['value'];
  2371.     }
  2372.  
  2373.     /**
  2374.      * FISHERINV
  2375.      *
  2376.      * Returns the inverse of the Fisher transformation. Use this transformation when
  2377.      * analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
  2378.      * FISHERINV(y) = x.
  2379.      *
  2380.      * @param    float        $value 
  2381.      * @return  float 
  2382.      */
  2383.     public static function FISHERINV($value{
  2384.         $value    self::flattenSingleValue($value);
  2385.  
  2386.         if (is_numeric($value)) {
  2387.             return (exp($value1(exp($value1);
  2388.         }
  2389.         return self::$_errorCodes['value'];
  2390.     }
  2391.  
  2392.     // Function cache for logBeta
  2393.     private static $logBetaCache_p            0.0;
  2394.     private static $logBetaCache_q            0.0;
  2395.     private static $logBetaCache_result    0.0;
  2396.  
  2397.     /**
  2398.      * The natural logarithm of the beta function.
  2399.      * @param require p>0
  2400.      * @param require q>0
  2401.      * @return if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
  2402.      * @author Jaco van Kooten
  2403.      */
  2404.     private static function logBeta($p$q{
  2405.         if ($p != self::$logBetaCache_p || $q != self::$logBetaCache_q{
  2406.             self::$logBetaCache_p $p;
  2407.             self::$logBetaCache_q $q;
  2408.             if (($p <= 0.0|| ($q <= 0.0|| (($p $qLOG_GAMMA_X_MAX_VALUE)) {
  2409.                 self::$logBetaCache_result 0.0;
  2410.             else {
  2411.                 self::$logBetaCache_result self::logGamma($pself::logGamma($qself::logGamma($p $q);
  2412.             }
  2413.         }
  2414.         return self::$logBetaCache_result;
  2415.     }
  2416.  
  2417.     /**
  2418.      * Evaluates of continued fraction part of incomplete beta function.
  2419.      * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
  2420.      * @author Jaco van Kooten
  2421.      */
  2422.     private static function betaFraction($x$p$q{
  2423.         $c 1.0;
  2424.         $sum_pq  $p $q;
  2425.         $p_plus  $p 1.0;
  2426.         $p_minus $p 1.0;
  2427.         $h 1.0 $sum_pq $x $p_plus;
  2428.         if (abs($hXMININ{
  2429.             $h XMININ;
  2430.         }
  2431.         $h 1.0 $h;
  2432.         $frac  $h;
  2433.         $m     1;
  2434.         $delta 0.0;
  2435.         while ($m <= MAX_ITERATIONS && abs($delta-1.0PRECISION {
  2436.             $m2 $m;
  2437.             // even index for d
  2438.             $d $m ($q $m$x ( ($p_minus $m2($p $m2));
  2439.             $h 1.0 $d $h;
  2440.             if (abs($hXMININ{
  2441.                 $h XMININ;
  2442.             }
  2443.             $h 1.0 $h;
  2444.             $c 1.0 $d $c;
  2445.             if (abs($cXMININ{
  2446.                 $c XMININ;
  2447.             }
  2448.             $frac *= $h $c;
  2449.             // odd index for d
  2450.             $d = -($p $m($sum_pq $m$x (($p $m2($p_plus $m2));
  2451.             $h 1.0 $d $h;
  2452.             if (abs($hXMININ{
  2453.                 $h XMININ;
  2454.             }
  2455.             $h 1.0 $h;
  2456.             $c 1.0 $d $c;
  2457.             if (abs($cXMININ{
  2458.                 $c XMININ;
  2459.             }
  2460.             $delta $h $c;
  2461.             $frac *= $delta;
  2462.             ++$m;
  2463.         }
  2464.         return $frac;
  2465.     }
  2466.  
  2467.     /**
  2468.      * logGamma function
  2469.      *
  2470.      * @version 1.1
  2471.      * @author Jaco van Kooten
  2472.      *
  2473.      *  Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
  2474.      *
  2475.      *  The natural logarithm of the gamma function. <br />
  2476.      *  Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz <br />
  2477.      *  Applied Mathematics Division <br />
  2478.      *  Argonne National Laboratory <br />
  2479.      *  Argonne, IL 60439 <br />
  2480.      *  <p>
  2481.      *  References:
  2482.      *  <ol>
  2483.      *  <li>W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural
  2484.      *      Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.</li>
  2485.      *  <li>K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.</li>
  2486.      *  <li>Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.</li>
  2487.      *  </ol>
  2488.      *  </p>
  2489.      *  <p>
  2490.      *  From the original documentation:
  2491.      *  </p>
  2492.      *  <p>
  2493.      *  This routine calculates the LOG(GAMMA) function for a positive real argument X.
  2494.      *  Computation is based on an algorithm outlined in references 1 and 2.
  2495.      *  The program uses rational functions that theoretically approximate LOG(GAMMA)
  2496.      *  to at least 18 significant decimal digits.  The approximation for X > 12 is from
  2497.      *  reference 3, while approximations for X < 12.0 are similar to those in reference
  2498.      *  1, but are unpublished. The accuracy achieved depends on the arithmetic system,
  2499.      *  the compiler, the intrinsic functions, and proper selection of the
  2500.      *  machine-dependent constants.
  2501.      *  </p>
  2502.      *  <p>
  2503.      *  Error returns: <br />
  2504.      *  The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
  2505.      *  The computation is believed to be free of underflow and overflow.
  2506.      *  </p>
  2507.      * @return MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
  2508.      */
  2509.  
  2510.     // Function cache for logGamma
  2511.     private static $logGammaCache_result    0.0;
  2512.     private static $logGammaCache_x        0.0;
  2513.  
  2514.     private static function logGamma($x{
  2515.         // Log Gamma related constants
  2516.         static $lg_d1 = -0.5772156649015328605195174;
  2517.         static $lg_d2 0.4227843350984671393993777;
  2518.         static $lg_d4 1.791759469228055000094023;
  2519.  
  2520.         static $lg_p1 array(    4.945235359296727046734888,
  2521.                                 201.8112620856775083915565,
  2522.                                 2290.838373831346393026739,
  2523.                                 11319.67205903380828685045,
  2524.                                 28557.24635671635335736389,
  2525.                                 38484.96228443793359990269,
  2526.                                 26377.48787624195437963534,
  2527.                                 7225.813979700288197698961 );
  2528.         static $lg_p2 array(    4.974607845568932035012064,
  2529.                                 542.4138599891070494101986,
  2530.                                 15506.93864978364947665077,
  2531.                                 184793.2904445632425417223,
  2532.                                 1088204.76946882876749847,
  2533.                                 3338152.967987029735917223,
  2534.                                 5106661.678927352456275255,
  2535.                                 3074109.054850539556250927 );
  2536.         static $lg_p4 array(    14745.02166059939948905062,
  2537.                                 2426813.369486704502836312,
  2538.                                 121475557.4045093227939592,
  2539.                                 2663432449.630976949898078,
  2540.                                 29403789566.34553899906876,
  2541.                                 170266573776.5398868392998,
  2542.                                 492612579337.743088758812,
  2543.                                 560625185622.3951465078242 );
  2544.  
  2545.         static $lg_q1 array(    67.48212550303777196073036,
  2546.                                 1113.332393857199323513008,
  2547.                                 7738.757056935398733233834,
  2548.                                 27639.87074403340708898585,
  2549.                                 54993.10206226157329794414,
  2550.                                 61611.22180066002127833352,
  2551.                                 36351.27591501940507276287,
  2552.                                 8785.536302431013170870835 );
  2553.         static $lg_q2 array(    183.0328399370592604055942,
  2554.                                 7765.049321445005871323047,
  2555.                                 133190.3827966074194402448,
  2556.                                 1136705.821321969608938755,
  2557.                                 5267964.117437946917577538,
  2558.                                 13467014.54311101692290052,
  2559.                                 17827365.30353274213975932,
  2560.                                 9533095.591844353613395747 );
  2561.         static $lg_q4 array(    2690.530175870899333379843,
  2562.                                 639388.5654300092398984238,
  2563.                                 41355999.30241388052042842,
  2564.                                 1120872109.61614794137657,
  2565.                                 14886137286.78813811542398,
  2566.                                 101680358627.2438228077304,
  2567.                                 341747634550.7377132798597,
  2568.                                 446315818741.9713286462081 );
  2569.  
  2570.         static $lg_c  array(    -0.001910444077728,
  2571.                                 8.4171387781295e-4,
  2572.                                 -5.952379913043012e-4,
  2573.                                 7.93650793500350248e-4,
  2574.                                 -0.002777777777777681622553,
  2575.                                 0.08333333333333333331554247,
  2576.                                 0.0057083835261 );
  2577.  
  2578.     // Rough estimate of the fourth root of logGamma_xBig
  2579.     static $lg_frtbig 2.25e76;
  2580.     static $pnt68     0.6796875;
  2581.  
  2582.  
  2583.     if ($x == self::$logGammaCache_x{
  2584.         return self::$logGammaCache_result;
  2585.     }
  2586.     $y $x;
  2587.     if ($y 0.0 && $y <= LOG_GAMMA_X_MAX_VALUE{
  2588.         if ($y <= EPS{
  2589.             $res = -log(y);
  2590.         elseif ($y <= 1.5{
  2591.             // ---------------------
  2592.             //  EPS .LT. X .LE. 1.5
  2593.             // ---------------------
  2594.             if ($y $pnt68{
  2595.                 $corr = -log($y);
  2596.                 $xm1  $y;
  2597.             else {
  2598.                 $corr 0.0;
  2599.                 $xm1  $y 1.0;
  2600.             }
  2601.             if ($y <= 0.5 || $y >= $pnt68{
  2602.                 $xden 1.0;
  2603.                 $xnum 0.0;
  2604.                 for ($i 0$i 8++$i{
  2605.                     $xnum $xnum $xm1 $lg_p1[$i];
  2606.                     $xden $xden $xm1 $lg_q1[$i];
  2607.                 }
  2608.                 $res $corr $xm1 ($lg_d1 $xm1 ($xnum $xden));
  2609.             else {
  2610.                 $xm2  $y 1.0;
  2611.                 $xden 1.0;
  2612.                 $xnum 0.0;
  2613.                 for ($i 0$i 8++$i{
  2614.                     $xnum $xnum $xm2 $lg_p2[$i];
  2615.                     $xden $xden $xm2 $lg_q2[$i];
  2616.                 }
  2617.                 $res $corr $xm2 ($lg_d2 $xm2 ($xnum $xden));
  2618.             }
  2619.         elseif ($y <= 4.0{
  2620.             // ---------------------
  2621.             //  1.5 .LT. X .LE. 4.0
  2622.             // ---------------------
  2623.             $xm2  $y 2.0;
  2624.             $xden 1.0;
  2625.             $xnum 0.0;
  2626.             for ($i 0$i 8++$i{
  2627.                 $xnum $xnum $xm2 $lg_p2[$i];
  2628.                 $xden $xden $xm2 $lg_q2[$i];
  2629.             }
  2630.             $res $xm2 ($lg_d2 $xm2 ($xnum $xden));
  2631.         elseif ($y <= 12.0{
  2632.             // ----------------------
  2633.             //  4.0 .LT. X .LE. 12.0
  2634.             // ----------------------
  2635.             $xm4  $y 4.0;
  2636.             $xden = -1.0;
  2637.             $xnum 0.0;
  2638.             for ($i 0$i 8++$i{
  2639.                 $xnum $xnum $xm4 $lg_p4[$i];
  2640.                 $xden $xden $xm4 $lg_q4[$i];
  2641.             }
  2642.             $res $lg_d4 $xm4 ($xnum $xden);
  2643.         else {
  2644.             // ---------------------------------
  2645.             //  Evaluate for argument .GE. 12.0
  2646.             // ---------------------------------
  2647.             $res 0.0;
  2648.             if ($y <= $lg_frtbig{
  2649.                 $res $lg_c[6];
  2650.                 $ysq $y $y;
  2651.                 for ($i 0$i 6++$i)
  2652.                     $res $res $ysq $lg_c[$i];
  2653.                 }
  2654.                 $res  /= $y;
  2655.                 $corr log($y);
  2656.                 $res  $res log(SQRT2PI0.5 $corr;
  2657.                 $res  += $y ($corr 1.0);
  2658.             }
  2659.         else {
  2660.             // --------------------------
  2661.             //  Return for bad arguments
  2662.             // --------------------------
  2663.             $res MAX_VALUE;
  2664.         }
  2665.         // ------------------------------
  2666.         //  Final adjustments and return
  2667.         // ------------------------------
  2668.         self::$logGammaCache_x $x;
  2669.         self::$logGammaCache_result $res;
  2670.         return $res;
  2671.     }
  2672.  
  2673.     /**
  2674.      * Beta function.
  2675.      *
  2676.      * @author Jaco van Kooten
  2677.      *
  2678.      * @param require p>0
  2679.      * @param require q>0
  2680.      * @return if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
  2681.      */
  2682.     private static function beta($p$q{
  2683.         if ($p <= 0.0 || $q <= 0.0 || ($p $qLOG_GAMMA_X_MAX_VALUE{
  2684.             return 0.0;
  2685.         else {
  2686.             return exp(self::logBeta($p$q));
  2687.         }
  2688.     }
  2689.  
  2690.     /**
  2691.      * Incomplete beta function
  2692.      *
  2693.      * @author Jaco van Kooten
  2694.      * @author Paul Meagher
  2695.      *
  2696.      *  The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
  2697.      * @param require 0<=x<=1
  2698.      * @param require p>0
  2699.      * @param require q>0
  2700.      * @return if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
  2701.      */
  2702.     private static function incompleteBeta($x$p$q{
  2703.         if ($x <= 0.0{
  2704.             return 0.0;
  2705.         elseif ($x >= 1.0{
  2706.             return 1.0;
  2707.         elseif (($p <= 0.0|| ($q <= 0.0|| (($p $qLOG_GAMMA_X_MAX_VALUE)) {
  2708.             return 0.0;
  2709.         }
  2710.         $beta_gam exp((self::logBeta($p$q)) $p log($x$q log(1.0 $x));
  2711.         if ($x ($p 1.0($p $q 2.0)) {
  2712.             return $beta_gam self::betaFraction($x$p$q$p;
  2713.         else {
  2714.             return 1.0 ($beta_gam self::betaFraction($x$q$p$q);
  2715.         }
  2716.     }
  2717.  
  2718.     /**
  2719.      * BETADIST
  2720.      *
  2721.      * Returns the beta distribution.
  2722.      *
  2723.      * @param    float        $value            Value at which you want to evaluate the distribution
  2724.      * @param    float        $alpha            Parameter to the distribution
  2725.      * @param    float        $beta            Parameter to the distribution
  2726.      * @param    boolean        $cumulative 
  2727.      * @return  float 
  2728.      *
  2729.      */
  2730.     public static function BETADIST($value,$alpha,$beta,$rMin=0,$rMax=1{
  2731.         $value    self::flattenSingleValue($value);
  2732.         $alpha    self::flattenSingleValue($alpha);
  2733.         $beta    self::flattenSingleValue($beta);
  2734.         $rMin    self::flattenSingleValue($rMin);
  2735.         $rMax    self::flattenSingleValue($rMax);
  2736.  
  2737.         if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
  2738.             if (($value $rMin|| ($value $rMax|| ($alpha <= 0|| ($beta <= 0|| ($rMin == $rMax)) {
  2739.                 return self::$_errorCodes['num'];
  2740.             }
  2741.             if ($rMin $rMax{
  2742.                 $tmp $rMin;
  2743.                 $rMin $rMax;
  2744.                 $rMax $tmp;
  2745.             }
  2746.             $value -= $rMin;
  2747.             $value /= ($rMax $rMin);
  2748.             return self::incompleteBeta($value,$alpha,$beta);
  2749.         }
  2750.         return self::$_errorCodes['value'];
  2751.     }
  2752.  
  2753.     /**
  2754.      * BETAINV
  2755.      *
  2756.      * Returns the inverse of the beta distribution.
  2757.      *
  2758.      * @param    float        $probability    Probability at which you want to evaluate the distribution
  2759.      * @param    float        $alpha            Parameter to the distribution
  2760.      * @param    float        $beta            Parameter to the distribution
  2761.      * @param    boolean        $cumulative 
  2762.      * @return  float 
  2763.      *
  2764.      */
  2765.     public static function BETAINV($probability,$alpha,$beta,$rMin=0,$rMax=1{
  2766.         $probability    self::flattenSingleValue($probability);
  2767.         $alpha            self::flattenSingleValue($alpha);
  2768.         $beta            self::flattenSingleValue($beta);
  2769.         $rMin            self::flattenSingleValue($rMin);
  2770.         $rMax            self::flattenSingleValue($rMax);
  2771.  
  2772.         if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
  2773.             if (($alpha <= 0|| ($beta <= 0|| ($rMin == $rMax|| ($probability <= 0|| ($probability 1)) {
  2774.                 return self::$_errorCodes['num'];
  2775.             }
  2776.             if ($rMin $rMax{
  2777.                 $tmp $rMin;
  2778.                 $rMin $rMax;
  2779.                 $rMax $tmp;
  2780.             }
  2781.             $a 0;
  2782.             $b 2;
  2783.             $maxIteration 100;
  2784.  
  2785.             $i 0;
  2786.             while ((($b $aPRECISION&& ($i++ < MAX_ITERATIONS)) {
  2787.                 $guess ($a $b2;
  2788.                 $result self::BETADIST($guess$alpha$beta);
  2789.                 if (($result == $probability|| ($result == 0)) {
  2790.                     $b $a;
  2791.                 elseif ($result $probability{
  2792.                     $b $guess;
  2793.                 else {
  2794.                     $a $guess;
  2795.                 }
  2796.             }
  2797.             if ($i == MAX_ITERATIONS{
  2798.                 return self::$_errorCodes['na'];
  2799.             }
  2800.             return round($rMin $guess ($rMax $rMin),12);
  2801.         }
  2802.         return self::$_errorCodes['value'];
  2803.     }
  2804.  
  2805.     //
  2806.     //    Private implementation of the incomplete Gamma function
  2807.     //
  2808.     private static function incompleteGamma($a,$x{
  2809.         static $max 32;
  2810.         $summer 0;
  2811.         for ($n=0$n<=$max++$n{
  2812.             $divisor $a;
  2813.             for ($i=1$i<=$n++$i{
  2814.                 $divisor *= ($a $i);
  2815.             }
  2816.             $summer += (pow($x,$n$divisor);
  2817.         }
  2818.         return pow($x,$aexp(0-$x$summer;
  2819.     }
  2820.  
  2821.  
  2822.     //
  2823.     //    Private implementation of the Gamma function
  2824.     //
  2825.     private static function gamma($data{
  2826.         if ($data == 0.0return 0;
  2827.  
  2828.         static $p0 1.000000000190015;
  2829.         static $p array => 76.18009172947146,
  2830.                             => -86.50532032941677,
  2831.                             => 24.01409824083091,
  2832.                             => -1.231739572450155,
  2833.                             => 1.208650973866179e-3,
  2834.                             => -5.395239384953e-6
  2835.                           );
  2836.  
  2837.         $y $x $data;
  2838.         $tmp $x 5.5;
  2839.         $tmp -= ($x 0.5log($tmp);
  2840.  
  2841.         $summer $p0;
  2842.         for ($j=1;$j<=6;++$j{
  2843.             $summer += ($p[$j/ ++$y);
  2844.         }
  2845.         return exp($tmp log(2.5066282746310005 $summer $x));
  2846.     }
  2847.  
  2848.     /**
  2849.      * GAMMADIST
  2850.      *
  2851.      * Returns the gamma distribution.
  2852.      *
  2853.      * @param    float        $value            Value at which you want to evaluate the distribution
  2854.      * @param    float        $a                Parameter to the distribution
  2855.      * @param    float        $b                Parameter to the distribution
  2856.      * @param    boolean        $cumulative 
  2857.      * @return  float 
  2858.      *
  2859.      */
  2860.     public static function GAMMADIST($value,$a,$b,$cumulative{
  2861.         $value    self::flattenSingleValue($value);
  2862.         $a        self::flattenSingleValue($a);
  2863.         $b        self::flattenSingleValue($b);
  2864.  
  2865.         if ((is_numeric($value)) && (is_numeric($a)) && (is_numeric($b))) {
  2866.             if (($value 0|| ($a <= 0|| ($b <= 0)) {
  2867.                 return self::$_errorCodes['num'];
  2868.             }
  2869.             if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  2870.                 if ($cumulative{
  2871.                     return self::incompleteGamma($a,$value $bself::gamma($a);
  2872.                 else {
  2873.                     return ((pow($b,$aself::gamma($a))) pow($value,$a-1exp(0-($value $b));
  2874.                 }
  2875.             }
  2876.         }
  2877.         return self::$_errorCodes['value'];
  2878.     }
  2879.  
  2880.     /**
  2881.      * GAMMAINV
  2882.      *
  2883.      * Returns the inverse of the beta distribution.
  2884.      *
  2885.      * @param    float        $probability    Probability at which you want to evaluate the distribution
  2886.      * @param    float        $alpha            Parameter to the distribution
  2887.      * @param    float        $beta            Parameter to the distribution
  2888.      * @param    boolean        $cumulative 
  2889.      * @return  float 
  2890.      *
  2891.      */
  2892.     public static function GAMMAINV($probability,$alpha,$beta{
  2893.         $probability    self::flattenSingleValue($probability);
  2894.         $alpha            self::flattenSingleValue($alpha);
  2895.         $beta            self::flattenSingleValue($beta);
  2896.         $rMin            self::flattenSingleValue($rMin);
  2897.         $rMax            self::flattenSingleValue($rMax);
  2898.  
  2899.         if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta))) {
  2900.             if (($alpha <= 0|| ($beta <= 0|| ($probability <= 0|| ($probability 1)) {
  2901.                 return self::$_errorCodes['num'];
  2902.             }
  2903.             $xLo 0;
  2904.             $xHi 100;
  2905.             $maxIteration 100;
  2906.  
  2907.             $x $xNew 1;
  2908.             $dx    1;
  2909.             $i 0;
  2910.  
  2911.             while ((abs($dxPRECISION&& ($i++ < MAX_ITERATIONS)) {
  2912.                 // Apply Newton-Raphson step
  2913.                 $result self::GAMMADIST($x$alpha$betaTrue);
  2914.                 $error $result $probability;
  2915.                 if ($error == 0.0{
  2916.                     $dx 0;
  2917.                 elseif ($error 0.0{
  2918.                     $xLo $x;
  2919.                 else {
  2920.                     $xHi $x;
  2921.                 }
  2922.                 // Avoid division by zero
  2923.                 if ($result != 0.0{
  2924.                     $dx $error $result;
  2925.                     $xNew $x $dx;
  2926.                 }
  2927.                 // If the NR fails to converge (which for example may be the
  2928.                 // case if the initial guess is too rough) we apply a bisection
  2929.                 // step to determine a more narrow interval around the root.
  2930.                 if (($xNew $xLo|| ($xNew $xHi|| ($result == 0.0)) {
  2931.                     $xNew ($xLo $xHi2;
  2932.                     $dx $xNew $x;
  2933.                 }
  2934.                 $x $xNew;
  2935.             }
  2936.             if ($i == MAX_ITERATIONS{
  2937.                 return self::$_errorCodes['na'];
  2938.             }
  2939.             return round($x,12);
  2940.         }
  2941.         return self::$_errorCodes['value'];
  2942.     }
  2943.  
  2944.     /**
  2945.      * GAMMALN
  2946.      *
  2947.      * Returns the natural logarithm of the gamma function.
  2948.      *
  2949.      * @param    float        $value 
  2950.      * @return  float 
  2951.      */
  2952.     public static function GAMMALN($value{
  2953.         $value    self::flattenSingleValue($value);
  2954.  
  2955.         if (is_numeric($value)) {
  2956.             if ($value <= 0{
  2957.                 return self::$_errorCodes['num'];
  2958.             }
  2959.             return log(self::gamma($value));
  2960.         }
  2961.         return self::$_errorCodes['value'];
  2962.     }
  2963.  
  2964.     /**
  2965.      * NORMDIST
  2966.      *
  2967.      * Returns the normal distribution for the specified mean and standard deviation. This
  2968.      * function has a very wide range of applications in statistics, including hypothesis
  2969.      * testing.
  2970.      *
  2971.      * @param    float        $value 
  2972.      * @param    float        $mean        Mean Value
  2973.      * @param    float        $stdDev        Standard Deviation
  2974.      * @param    boolean        $cumulative 
  2975.      * @return  float 
  2976.      *
  2977.      */
  2978.     public static function NORMDIST($value$mean$stdDev$cumulative{
  2979.         $value    self::flattenSingleValue($value);
  2980.         $mean    self::flattenSingleValue($mean);
  2981.         $stdDev    self::flattenSingleValue($stdDev);
  2982.  
  2983.         if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  2984.             if ($stdDev 0{
  2985.                 return self::$_errorCodes['num'];
  2986.             }
  2987.             if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  2988.                 if ($cumulative{
  2989.                     return 0.5 (self::erfVal(($value $mean($stdDev sqrt(2))));
  2990.                 else {
  2991.                     return ((SQRT2PI $stdDev)) exp(0  (pow($value $mean,2(pow($stdDev,2))));
  2992.                 }
  2993.             }
  2994.         }
  2995.         return self::$_errorCodes['value'];
  2996.     }
  2997.  
  2998.     /**
  2999.      * NORMSDIST
  3000.      *
  3001.      * Returns the standard normal cumulative distribution function. The distribution has
  3002.      * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
  3003.      * table of standard normal curve areas.
  3004.      *
  3005.      * @param    float        $value 
  3006.      * @return  float 
  3007.      */
  3008.     public static function NORMSDIST($value{
  3009.         $value    self::flattenSingleValue($value);
  3010.  
  3011.         return self::NORMDIST($value01True);
  3012.     }
  3013.  
  3014.     /**
  3015.      * LOGNORMDIST
  3016.      *
  3017.      * Returns the cumulative lognormal distribution of x, where ln(x) is normally distributed
  3018.      * with parameters mean and standard_dev.
  3019.      *
  3020.      * @param    float        $value 
  3021.      * @return  float 
  3022.      */
  3023.     public static function LOGNORMDIST($value$mean$stdDev{
  3024.         $value    self::flattenSingleValue($value);
  3025.         $mean    self::flattenSingleValue($mean);
  3026.         $stdDev    self::flattenSingleValue($stdDev);
  3027.  
  3028.         if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  3029.             if (($value <= 0|| ($stdDev <= 0)) {
  3030.                 return self::$_errorCodes['num'];
  3031.             }
  3032.             return self::NORMSDIST((log($value$mean$stdDev);
  3033.         }
  3034.         return self::$_errorCodes['value'];
  3035.     }
  3036.  
  3037.     /***************************************************************************
  3038.      *                                inverse_ncdf.php
  3039.      *                            -------------------
  3040.      *   begin                : Friday, January 16, 2004
  3041.      *   copyright            : (C) 2004 Michael Nickerson
  3042.      *   email                : nickersonm@yahoo.com
  3043.      *
  3044.      ***************************************************************************/
  3045.     private static function inverse_ncdf($p{
  3046.         //    Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
  3047.         //    PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
  3048.         //    a guide.  http://home.online.no/~pjacklam/notes/invnorm/index.html
  3049.         //    I have not checked the accuracy of this implementation.  Be aware that PHP
  3050.         //    will truncate the coeficcients to 14 digits.
  3051.  
  3052.         //    You have permission to use and distribute this function freely for
  3053.         //    whatever purpose you want, but please show common courtesy and give credit
  3054.         //    where credit is due.
  3055.  
  3056.         //    Input paramater is $p - probability - where 0 < p < 1.
  3057.  
  3058.         //    Coefficients in rational approximations
  3059.         static $a array(    => -3.969683028665376e+01,
  3060.                             => 2.209460984245205e+02,
  3061.                             => -2.759285104469687e+02,
  3062.                             => 1.383577518672690e+02,
  3063.                             => -3.066479806614716e+01,
  3064.                             => 2.506628277459239e+00
  3065.                           );
  3066.  
  3067.         static $b array(    => -5.447609879822406e+01,
  3068.                             => 1.615858368580409e+02,
  3069.                             => -1.556989798598866e+02,
  3070.                             => 6.680131188771972e+01,
  3071.                             => -1.328068155288572e+01
  3072.                           );
  3073.  
  3074.         static $c array(    => -7.784894002430293e-03,
  3075.                             => -3.223964580411365e-01,
  3076.                             => -2.400758277161838e+00,
  3077.                             => -2.549732539343734e+00,
  3078.                             => 4.374664141464968e+00,
  3079.                             => 2.938163982698783e+00
  3080.                           );
  3081.  
  3082.         static $d array(    => 7.784695709041462e-03,
  3083.                             => 3.224671290700398e-01,
  3084.                             => 2.445134137142996e+00,
  3085.                             => 3.754408661907416e+00
  3086.                           );
  3087.  
  3088.         //    Define lower and upper region break-points.
  3089.         $p_low =  0.02425;            //Use lower region approx. below this
  3090.         $p_high $p_low;        //Use upper region approx. above this
  3091.  
  3092.         if ($p && $p $p_low{
  3093.             //    Rational approximation for lower region.
  3094.             $q sqrt(-log($p));
  3095.             return ((((($c[1$q $c[2]$q $c[3]$q $c[4]$q $c[5]$q $c[6]/
  3096.                     (((($d[1$q $d[2]$q $d[3]$q $d[4]$q 1);
  3097.         elseif ($p_low <= $p && $p <= $p_high{
  3098.             //    Rational approximation for central region.
  3099.             $q $p 0.5;
  3100.             $r $q $q;
  3101.             return ((((($a[1$r $a[2]$r $a[3]$r $a[4]$r $a[5]$r $a[6]$q /
  3102.                    ((((($b[1$r $b[2]$r $b[3]$r $b[4]$r $b[5]$r 1);
  3103.         elseif ($p_high $p && $p 1{
  3104.             //    Rational approximation for upper region.
  3105.             $q sqrt(-log($p));
  3106.             return -((((($c[1$q $c[2]$q $c[3]$q $c[4]$q $c[5]$q $c[6]/
  3107.                      (((($d[1$q $d[2]$q $d[3]$q $d[4]$q 1);
  3108.         }
  3109.         //    If 0 < p < 1, return a null value
  3110.         return self::$_errorCodes['null'];
  3111.     }
  3112.  
  3113.     private static function inverse_ncdf2($prob{
  3114.         //    Approximation of inverse standard normal CDF developed by
  3115.         //    B. Moro, "The Full Monte," Risk 8(2), Feb 1995, 57-58.
  3116.  
  3117.         $a1 2.50662823884;
  3118.         $a2 = -18.61500062529;
  3119.         $a3 41.39119773534;
  3120.         $a4 = -25.44106049637;
  3121.  
  3122.         $b1 = -8.4735109309;
  3123.         $b2 23.08336743743;
  3124.         $b3 = -21.06224101826;
  3125.         $b4 3.13082909833;
  3126.  
  3127.         $c1 0.337475482272615;
  3128.         $c2 0.976169019091719;
  3129.         $c3 0.160797971491821;
  3130.         $c4 2.76438810333863E-02;
  3131.         $c5 3.8405729373609E-03;
  3132.         $c6 3.951896511919E-04;
  3133.         $c7 3.21767881768E-05;
  3134.         $c8 2.888167364E-07;
  3135.         $c9 3.960315187E-07;
  3136.  
  3137.         $y $prob 0.5;
  3138.         if (abs($y0.42{
  3139.             $z pow($y,2);
  3140.             $z $y ((($a4 $z $a3$z $a2$z $a1(((($b4 $z $b3$z $b2$z $b1$z 1);
  3141.         else {
  3142.             if ($y 0{
  3143.                 $z log(-log($prob));
  3144.             else {
  3145.                 $z log(-log($prob));
  3146.             }
  3147.             $z $c1 $z ($c2 $z ($c3 $z ($c4 $z ($c5 $z ($c6 $z ($c7 $z ($c8 $z $c9)))))));
  3148.             if ($y 0{
  3149.                 $z = -$z;
  3150.             }
  3151.         }
  3152.         return $z;
  3153.     }
  3154.  
  3155.     private static function inverse_ncdf3($p{
  3156.         //    ALGORITHM AS241 APPL. STATIST. (1988) VOL. 37, NO. 3.
  3157.         //    Produces the normal deviate Z corresponding to a given lower
  3158.         //    tail area of P; Z is accurate to about 1 part in 10**16.
  3159.         //
  3160.         //    This is a PHP version of the original FORTRAN code that can
  3161.         //    be found at http://lib.stat.cmu.edu/apstat/
  3162.         $split1 0.425;
  3163.         $split2 5;
  3164.         $const1 0.180625;
  3165.         $const2 1.6;
  3166.  
  3167.         //    coefficients for p close to 0.5
  3168.         $a0 3.3871328727963666080;
  3169.         $a1 1.3314166789178437745E+2;
  3170.         $a2 1.9715909503065514427E+3;
  3171.         $a3 1.3731693765509461125E+4;
  3172.         $a4 4.5921953931549871457E+4;
  3173.         $a5 6.7265770927008700853E+4;
  3174.         $a6 3.3430575583588128105E+4;
  3175.         $a7 2.5090809287301226727E+3;
  3176.  
  3177.         $b1 4.2313330701600911252E+1;
  3178.         $b2 6.8718700749205790830E+2;
  3179.         $b3 5.3941960214247511077E+3;
  3180.         $b4 2.1213794301586595867E+4;
  3181.         $b5 3.9307895800092710610E+4;
  3182.         $b6 2.8729085735721942674E+4;
  3183.         $b7 5.2264952788528545610E+3;
  3184.  
  3185.         //    coefficients for p not close to 0, 0.5 or 1.
  3186.         $c0 1.42343711074968357734;
  3187.         $c1 4.63033784615654529590;
  3188.         $c2 5.76949722146069140550;
  3189.         $c3 3.64784832476320460504;
  3190.         $c4 1.27045825245236838258;
  3191.         $c5 2.41780725177450611770E-1;
  3192.         $c6 2.27238449892691845833E-2;
  3193.         $c7 7.74545014278341407640E-4;
  3194.  
  3195.         $d1 2.05319162663775882187;
  3196.         $d2 1.67638483018380384940;
  3197.         $d3 6.89767334985100004550E-1;
  3198.         $d4 1.48103976427480074590E-1;
  3199.         $d5 1.51986665636164571966E-2;
  3200.         $d6 5.47593808499534494600E-4;
  3201.         $d7 1.05075007164441684324E-9;
  3202.  
  3203.         //    coefficients for p near 0 or 1.
  3204.         $e0 6.65790464350110377720;
  3205.         $e1 5.46378491116411436990;
  3206.         $e2 1.78482653991729133580;
  3207.         $e3 2.96560571828504891230E-1;
  3208.         $e4 2.65321895265761230930E-2;
  3209.         $e5 1.24266094738807843860E-3;
  3210.         $e6 2.71155556874348757815E-5;
  3211.         $e7 2.01033439929228813265E-7;
  3212.  
  3213.         $f1 5.99832206555887937690E-1;
  3214.         $f2 1.36929880922735805310E-1;
  3215.         $f3 1.48753612908506148525E-2;
  3216.         $f4 7.86869131145613259100E-4;
  3217.         $f5 1.84631831751005468180E-5;
  3218.         $f6 1.42151175831644588870E-7;
  3219.         $f7 2.04426310338993978564E-15;
  3220.  
  3221.         $q $p 0.5;
  3222.  
  3223.         //    computation for p close to 0.5
  3224.         if (abs($q<= split1{
  3225.             $R $const1 $q $q;
  3226.             $z $q ((((((($a7 $R $a6$R $a5$R $a4$R $a3$R $a2$R $a1$R $a0/
  3227.                       ((((((($b7 $R $b6$R $b5$R $b4$R $b3$R $b2$R $b1$R 1);
  3228.         else {
  3229.             if ($q 0{
  3230.                 $R $p;
  3231.             else {
  3232.                 $R $p;
  3233.             }
  3234.             $R pow(-log($R),2);
  3235.  
  3236.             //    computation for p not close to 0, 0.5 or 1.
  3237.             If ($R <= $split2{
  3238.                 $R $R $const2;
  3239.                 $z ((((((($c7 $R $c6$R $c5$R $c4$R $c3$R $c2$R $c1$R $c0/
  3240.                      ((((((($d7 $R $d6$R $d5$R $d4$R $d3$R $d2$R $d1$R 1);
  3241.             else {
  3242.             //    computation for p near 0 or 1.
  3243.                 $R $R $split2;
  3244.                 $z ((((((($e7 $R $e6$R $e5$R $e4$R $e3$R $e2$R $e1$R $e0/
  3245.                      ((((((($f7 $R $f6$R $f5$R $f4$R $f3$R $f2$R $f1$R 1);
  3246.             }
  3247.             if ($q 0{
  3248.                 $z = -$z;
  3249.             }
  3250.         }
  3251.         return $z;
  3252.     }
  3253.  
  3254.     /**
  3255.      * NORMINV
  3256.      *
  3257.      * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
  3258.      *
  3259.      * @param    float        $value 
  3260.      * @param    float        $mean        Mean Value
  3261.      * @param    float        $stdDev        Standard Deviation
  3262.      * @return  float 
  3263.      *
  3264.      */
  3265.     public static function NORMINV($probability,$mean,$stdDev{
  3266.         $probability    self::flattenSingleValue($probability);
  3267.         $mean            self::flattenSingleValue($mean);
  3268.         $stdDev            self::flattenSingleValue($stdDev);
  3269.  
  3270.         if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  3271.             if (($probability 0|| ($probability 1)) {
  3272.                 return self::$_errorCodes['num'];
  3273.             }
  3274.             if ($stdDev 0{
  3275.                 return self::$_errorCodes['num'];
  3276.             }
  3277.             return (self::inverse_ncdf($probability$stdDev$mean;
  3278.         }
  3279.         return self::$_errorCodes['value'];
  3280.     }
  3281.  
  3282.     /**
  3283.      * NORMSINV
  3284.      *
  3285.      * Returns the inverse of the standard normal cumulative distribution
  3286.      *
  3287.      * @param    float        $value 
  3288.      * @return  float 
  3289.      */
  3290.     public static function NORMSINV($value{
  3291.         return self::NORMINV($value01);
  3292.     }
  3293.  
  3294.     /**
  3295.      * LOGINV
  3296.      *
  3297.      * Returns the inverse of the normal cumulative distribution
  3298.      *
  3299.      * @param    float        $value 
  3300.      * @return  float 
  3301.      *
  3302.      * @todo    Try implementing P J Acklam's refinement algorithm for greater
  3303.      *             accuracy if I can get my head round the mathematics
  3304.      *             (as described at) http://home.online.no/~pjacklam/notes/invnorm/
  3305.      */
  3306.     public static function LOGINV($probability$mean$stdDev{
  3307.         $probability    self::flattenSingleValue($probability);
  3308.         $mean            self::flattenSingleValue($mean);
  3309.         $stdDev            self::flattenSingleValue($stdDev);
  3310.  
  3311.         if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
  3312.             if (($probability 0|| ($probability 1|| ($stdDev <= 0)) {
  3313.                 return self::$_errorCodes['num'];
  3314.             }
  3315.             return exp($mean $stdDev self::NORMSINV($probability));
  3316.         }
  3317.         return self::$_errorCodes['value'];
  3318.     }
  3319.  
  3320.     /**
  3321.      * HYPGEOMDIST
  3322.      *
  3323.      * Returns the hypergeometric distribution. HYPGEOMDIST returns the probability of a given number of
  3324.      * sample successes, given the sample size, population successes, and population size.
  3325.      *
  3326.      * @param    float        $sampleSuccesses        Number of successes in the sample
  3327.      * @param    float        $sampleNumber            Size of the sample
  3328.      * @param    float        $populationSuccesses    Number of successes in the population
  3329.      * @param    float        $populationNumber        Population size
  3330.      * @return  float 
  3331.      *
  3332.      */
  3333.     public static function HYPGEOMDIST($sampleSuccesses$sampleNumber$populationSuccesses$populationNumber{
  3334.         $sampleSuccesses        floor(self::flattenSingleValue($sampleSuccesses));
  3335.         $sampleNumber            floor(self::flattenSingleValue($sampleNumber));
  3336.         $populationSuccesses    floor(self::flattenSingleValue($populationSuccesses));
  3337.         $populationNumber        floor(self::flattenSingleValue($populationNumber));
  3338.  
  3339.         if ((is_numeric($sampleSuccesses)) && (is_numeric($sampleNumber)) && (is_numeric($populationSuccesses)) && (is_numeric($populationNumber))) {
  3340.             if (($sampleSuccesses 0|| ($sampleSuccesses $sampleNumber|| ($sampleSuccesses $populationSuccesses)) {
  3341.                 return self::$_errorCodes['num'];
  3342.             }
  3343.             if (($sampleNumber <= 0|| ($sampleNumber $populationNumber)) {
  3344.                 return self::$_errorCodes['num'];
  3345.             }
  3346.             if (($populationSuccesses <= 0|| ($populationSuccesses $populationNumber)) {
  3347.                 return self::$_errorCodes['num'];
  3348.             }
  3349.             return self::COMBIN($populationSuccesses,$sampleSuccesses*
  3350.                    self::COMBIN($populationNumber $populationSuccesses,$sampleNumber $sampleSuccesses/
  3351.                    self::COMBIN($populationNumber,$sampleNumber);
  3352.         }
  3353.         return self::$_errorCodes['value'];
  3354.     }
  3355.  
  3356.     public static function hypGeom($sampleSuccesses$sampleNumber$populationSuccesses$populationNumber{
  3357.         return self::COMBIN($populationSuccesses,$sampleSuccesses*
  3358.                self::COMBIN($populationNumber $populationSuccesses,$sampleNumber $sampleSuccesses/
  3359.                self::COMBIN($populationNumber,$sampleNumber);
  3360.     }
  3361.  
  3362.     /**
  3363.      * TDIST
  3364.      *
  3365.      * Returns the probability of Student's T distribution.
  3366.      *
  3367.      * @param    float        $value            Value for the function
  3368.      * @param    float        $degrees        degrees of freedom
  3369.      * @param    float        $tails            number of tails (1 or 2)
  3370.      * @return  float 
  3371.      */
  3372.     public static function TDIST($value$degrees$tails{
  3373.         $value        self::flattenSingleValue($value);
  3374.         $degrees    floor(self::flattenSingleValue($degrees));
  3375.         $tails        floor(self::flattenSingleValue($tails));
  3376.  
  3377.         if ((is_numeric($value)) && (is_numeric($degrees)) && (is_numeric($tails))) {
  3378.             if (($value 0|| ($degrees 1|| ($tails 1|| ($tails 2)) {
  3379.                 return self::$_errorCodes['num'];
  3380.             }
  3381.             //    tdist, which finds the probability that corresponds to a given value
  3382.             //    of t with k degrees of freedom.  This algorithm is translated from a
  3383.             //    pascal function on p81 of "Statistical Computing in Pascal" by D
  3384.             //    Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
  3385.             //    London).  The above Pascal algorithm is itself a translation of the
  3386.             //    fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
  3387.             //    Laboratory as reported in (among other places) "Applied Statistics
  3388.             //    Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
  3389.             //    Horwood Ltd.; W. Sussex, England).
  3390. //            $ta = 2 / pi();
  3391.             $ta 0.636619772367581;
  3392.             $tterm $degrees;
  3393.             $ttheta atan2($value,sqrt($tterm));
  3394.             $tc cos($ttheta);
  3395.             $ts sin($ttheta);
  3396.             $tsum 0;
  3397.  
  3398.             if (($degrees 2== 1{
  3399.                 $ti 3;
  3400.                 $tterm $tc;
  3401.             else {
  3402.                 $ti 2;
  3403.                 $tterm 1;
  3404.             }
  3405.  
  3406.             $tsum $tterm;
  3407.             while ($ti $degrees{
  3408.                 $tterm *= $tc $tc ($ti 1$ti;
  3409.                 $tsum += $tterm;
  3410.                 $ti += 2;
  3411.             }
  3412.             $tsum *= $ts;
  3413.             if (($degrees 2== 1$tsum $ta ($tsum $ttheta)}
  3414.             $tValue 0.5 ($tsum);
  3415.             if ($tails == 1{
  3416.                 return abs($tValue);
  3417.             else {
  3418.                 return abs(($tValue$tValue);
  3419.             }
  3420.         }
  3421.         return self::$_errorCodes['value'];
  3422.     }
  3423.  
  3424.     /**
  3425.      * TINV
  3426.      *
  3427.      * Returns the one-tailed probability of the chi-squared distribution.
  3428.      *
  3429.      * @param    float        $probability    Probability for the function
  3430.      * @param    float        $degrees        degrees of freedom
  3431.      * @return  float 
  3432.      */
  3433.     public static function TINV($probability$degrees{
  3434.         $probability    self::flattenSingleValue($probability);
  3435.         $degrees        floor(self::flattenSingleValue($degrees));
  3436.  
  3437.         if ((is_numeric($probability)) && (is_numeric($degrees))) {
  3438.             $xLo 100;
  3439.             $xHi 0;
  3440.             $maxIteration 100;
  3441.  
  3442.             $x $xNew 1;
  3443.             $dx    1;
  3444.             $i 0;
  3445.  
  3446.             while ((abs($dxPRECISION&& ($i++ < MAX_ITERATIONS)) {
  3447.                 // Apply Newton-Raphson step
  3448.                 $result self::TDIST($x$degrees2);
  3449.                 $error $result $probability;
  3450.                 if ($error == 0.0{
  3451.                     $dx 0;
  3452.                 elseif ($error 0.0{
  3453.                     $xLo $x;
  3454.                 else {
  3455.                     $xHi $x;
  3456.                 }
  3457.                 // Avoid division by zero
  3458.                 if ($result != 0.0{
  3459.                     $dx $error $result;
  3460.                     $xNew $x $dx;
  3461.                 }
  3462.                 // If the NR fails to converge (which for example may be the
  3463.                 // case if the initial guess is too rough) we apply a bisection
  3464.                 // step to determine a more narrow interval around the root.
  3465.                 if (($xNew $xLo|| ($xNew $xHi|| ($result == 0.0)) {
  3466.                     $xNew ($xLo $xHi2;
  3467.                     $dx $xNew $x;
  3468.                 }
  3469.                 $x $xNew;
  3470.             }
  3471.             if ($i == MAX_ITERATIONS{
  3472.                 return self::$_errorCodes['na'];
  3473.             }
  3474.             return round($x,12);
  3475.         }
  3476.         return self::$_errorCodes['value'];
  3477.     }
  3478.  
  3479.     /**
  3480.      * CONFIDENCE
  3481.      *
  3482.      * Returns the confidence interval for a population mean
  3483.      *
  3484.      * @param    float        $alpha 
  3485.      * @param    float        $stdDev        Standard Deviation
  3486.      * @param    float        $size 
  3487.      * @return  float 
  3488.      *
  3489.      */
  3490.     public static function CONFIDENCE($alpha,$stdDev,$size{
  3491.         $alpha    self::flattenSingleValue($alpha);
  3492.         $stdDev    self::flattenSingleValue($stdDev);
  3493.         $size    floor(self::flattenSingleValue($size));
  3494.  
  3495.         if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
  3496.             if (($alpha <= 0|| ($alpha >= 1)) {
  3497.                 return self::$_errorCodes['num'];
  3498.             }
  3499.             if (($stdDev <= 0|| ($size 1)) {
  3500.                 return self::$_errorCodes['num'];
  3501.             }
  3502.             return self::NORMSINV($alpha 2$stdDev sqrt($size);
  3503.         }
  3504.         return self::$_errorCodes['value'];
  3505.     }
  3506.  
  3507.     /**
  3508.      * POISSON
  3509.      *
  3510.      * Returns the Poisson distribution. A common application of the Poisson distribution
  3511.      * is predicting the number of events over a specific time, such as the number of
  3512.      * cars arriving at a toll plaza in 1 minute.
  3513.      *
  3514.      * @param    float        $value 
  3515.      * @param    float        $mean        Mean Value
  3516.      * @param    boolean        $cumulative 
  3517.      * @return  float 
  3518.      *
  3519.      */
  3520.     public static function POISSON($value$mean$cumulative{
  3521.         $value    self::flattenSingleValue($value);
  3522.         $mean    self::flattenSingleValue($mean);
  3523.  
  3524.         if ((is_numeric($value)) && (is_numeric($mean))) {
  3525.             if (($value <= 0|| ($mean <= 0)) {
  3526.                 return self::$_errorCodes['num'];
  3527.             }
  3528.             if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  3529.                 if ($cumulative{
  3530.                     $summer 0;
  3531.                     for ($i 0$i <= floor($value)++$i{
  3532.                         $summer += pow($mean,$iself::FACT($i);
  3533.                     }
  3534.                     return exp(0-$mean$summer;
  3535.                 else {
  3536.                     return (exp(0-$meanpow($mean,$value)) self::FACT($value);
  3537.                 }
  3538.             }
  3539.         }
  3540.         return self::$_errorCodes['value'];
  3541.     }
  3542.  
  3543.     /**
  3544.      * WEIBULL
  3545.      *
  3546.      * Returns the Weibull distribution. Use this distribution in reliability
  3547.      * analysis, such as calculating a device's mean time to failure.
  3548.      *
  3549.      * @param    float        $value 
  3550.      * @param    float        $alpha        Alpha Parameter
  3551.      * @param    float        $beta        Beta Parameter
  3552.      * @param    boolean        $cumulative 
  3553.      * @return  float 
  3554.      *
  3555.      */
  3556.     public static function WEIBULL($value$alpha$beta$cumulative{
  3557.         $value    self::flattenSingleValue($value);
  3558.         $alpha    self::flattenSingleValue($alpha);
  3559.         $beta    self::flattenSingleValue($beta);
  3560.  
  3561.         if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta))) {
  3562.             if (($value 0|| ($alpha <= 0|| ($beta <= 0)) {
  3563.                 return self::$_errorCodes['num'];
  3564.             }
  3565.             if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
  3566.                 if ($cumulative{
  3567.                     return exp(pow($value $beta,$alpha));
  3568.                 else {
  3569.                     return ($alpha pow($beta,$alpha)) pow($value,$alpha 1exp(pow($value $beta,$alpha));
  3570.                 }
  3571.             }
  3572.         }
  3573.         return self::$_errorCodes['value'];
  3574.     }
  3575.  
  3576.     /**
  3577.      * SKEW
  3578.      *
  3579.      * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
  3580.      * of a distribution around its mean. Positive skewness indicates a distribution with an
  3581.      * asymmetric tail extending toward more positive values. Negative skewness indicates a
  3582.      * distribution with an asymmetric tail extending toward more negative values.
  3583.      *
  3584.      * @param    array    Data Series
  3585.      * @return  float 
  3586.      */
  3587.     public static function SKEW({
  3588.         $aArgs self::flattenArray(func_get_args());
  3589.         $mean self::AVERAGE($aArgs);
  3590.         $stdDev self::STDEV($aArgs);
  3591.  
  3592.         $count $summer 0;
  3593.         // Loop through arguments
  3594.         foreach ($aArgs as $arg{
  3595.             // Is it a numeric value?
  3596.             if ((is_numeric($arg)) && (!is_string($arg))) {
  3597.                 $summer += pow((($arg $mean$stdDev),3;
  3598.                 ++$count;
  3599.             }
  3600.         }
  3601.  
  3602.         // Return
  3603.         if ($count 2{
  3604.             return $summer ($count (($count-1($count-2)));
  3605.         }
  3606.         return self::$_errorCodes['divisionbyzero'];
  3607.     }
  3608.  
  3609.     /**
  3610.      * KURT
  3611.      *
  3612.      * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
  3613.      * or flatness of a distribution compared with the normal distribution. Positive
  3614.      * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
  3615.      * relatively flat distribution.
  3616.      *
  3617.      * @param    array    Data Series
  3618.      * @return  float 
  3619.      */
  3620.     public static function KURT({
  3621.         $aArgs self::flattenArray(func_get_args());
  3622.         $mean self::AVERAGE($aArgs);
  3623.         $stdDev self::STDEV($aArgs);
  3624.  
  3625.         if ($stdDev 0{
  3626.             $count $summer 0;
  3627.             // Loop through arguments
  3628.             foreach ($aArgs as $arg{
  3629.                 // Is it a numeric value?
  3630.                 if ((is_numeric($arg)) && (!is_string($arg))) {
  3631.                     $summer += pow((($arg $mean$stdDev),4;
  3632.                     ++$count;
  3633.                 }
  3634.             }
  3635.  
  3636.             // Return
  3637.             if ($count 3{
  3638.                 return $summer ($count ($count+1(($count-1($count-2($count-3))) (pow($count-1,2(($count-2($count-3)));
  3639.             }
  3640.         }
  3641.         return self::$_errorCodes['divisionbyzero'];
  3642.     }
  3643.  
  3644.     /**
  3645.      * RAND
  3646.      *
  3647.      * @param    int        $min    Minimal value
  3648.      * @param    int        $max    Maximal value
  3649.      * @return  int        Random number
  3650.      */
  3651.     public static function RAND($min 0$max 0{
  3652.         $min        self::flattenSingleValue($min);
  3653.         $max        self::flattenSingleValue($max);
  3654.  
  3655.         if ($min == && $max == 0{
  3656.             return (rand(0,10000000)) 10000000;
  3657.         else {
  3658.             return rand($min$max);
  3659.         }
  3660.     }
  3661.  
  3662.     /**
  3663.      * MOD
  3664.      *
  3665.      * @param    int        $a        Dividend
  3666.      * @param    int        $b        Divisor
  3667.      * @return  int        Remainder
  3668.      */
  3669.     public static function MOD($a 1$b 1{
  3670.         $a        self::flattenSingleValue($a);
  3671.         $b        self::flattenSingleValue($b);
  3672.  
  3673.         return $a $b;
  3674.     }
  3675.  
  3676.     /**
  3677.      * ASCIICODE
  3678.      *
  3679.      * @param    string    $character    Value
  3680.      * @return  int 
  3681.      */
  3682.     public static function ASCIICODE($characters{
  3683.         $characters    self::flattenSingleValue($characters);
  3684.         if (is_bool($characters)) {
  3685.             if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE{
  3686.                 $characters = (int) $characters;
  3687.             else {
  3688.                 if ($characters{
  3689.                     $characters 'True';
  3690.                 else {
  3691.                     $characters 'False';
  3692.                 }
  3693.             }
  3694.         }
  3695.  
  3696.         if (strlen($characters0{
  3697.             return ord(substr($characters01));
  3698.         }
  3699.         return self::$_errorCodes['value'];
  3700.     }
  3701.  
  3702.     /**
  3703.      * CONCATENATE
  3704.      *
  3705.      * @return  string 
  3706.      */
  3707.     public static function CONCATENATE({
  3708.         // Return value
  3709.         $returnValue '';
  3710.  
  3711.         // Loop trough arguments
  3712.         $aArgs self::flattenArray(func_get_args());
  3713.         foreach ($aArgs as $arg{
  3714.             if (is_bool($arg)) {
  3715.                 if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE{
  3716.                     $arg = (int) $arg;
  3717.                 else {
  3718.                     if ($arg{
  3719.                         $arg 'TRUE';
  3720.                     else {
  3721.                         $arg 'FALSE';
  3722.                     }
  3723.                 }
  3724.             }
  3725.             $returnValue .= $arg;
  3726.         }
  3727.  
  3728.         // Return
  3729.         return $returnValue;
  3730.     }
  3731.  
  3732.     /**
  3733.      * SEARCHSENSITIVE
  3734.      *
  3735.      * @param    string    $needle        The string to look for
  3736.      * @param    string    $haystack    The string in which to look
  3737.      * @param    int        $offset        Offset within $haystack
  3738.      * @return  string 
  3739.      */
  3740.     public static function SEARCHSENSITIVE($needle,$haystack,$offset=1{
  3741.         $needle        = (string) self::flattenSingleValue($needle);
  3742.         $haystack    = (string) self::flattenSingleValue($haystack);
  3743.         $offset        self::flattenSingleValue($offset);
  3744.  
  3745.         if (($offset 0&& (strlen($haystack$offset)) {
  3746.             $pos strpos($haystack$needle--$offset);
  3747.             if ($pos !== false{
  3748.                 return ++$pos;
  3749.             }
  3750.         }
  3751.         return self::$_errorCodes['value'];
  3752.     }
  3753.  
  3754.     /**
  3755.      * SEARCHINSENSITIVE
  3756.      *
  3757.      * @param    string    $needle        The string to look for
  3758.      * @param    string    $haystack    The string in which to look
  3759.      * @param    int        $offset        Offset within $haystack
  3760.      * @return  string 
  3761.      */
  3762.     public static function SEARCHINSENSITIVE($needle,$haystack,$offset=1{
  3763.         $needle        = (string) self::flattenSingleValue($needle);
  3764.         $haystack    = (string) self::flattenSingleValue($haystack);
  3765.         $offset        self::flattenSingleValue($offset);
  3766.  
  3767.         if (($offset 0&& (strlen($haystack$offset)) {
  3768.             $pos stripos($haystack$needle--$offset);
  3769.             if ($pos !== false{
  3770.                 return ++$pos;
  3771.             }
  3772.         }
  3773.         return self::$_errorCodes['value'];
  3774.     }
  3775.  
  3776.     /**
  3777.      * LEFT
  3778.      *
  3779.      * @param    string    $value    Value
  3780.      * @param    int        $chars    Number of characters
  3781.      * @return  string 
  3782.      */
  3783.     public static function LEFT($value ''$chars null{
  3784.         $value        self::flattenSingleValue($value);
  3785.         $chars        self::flattenSingleValue($chars);
  3786.  
  3787.         return substr($value0$chars);
  3788.     }
  3789.  
  3790.     /**
  3791.      * RIGHT
  3792.      *
  3793.      * @param    string    $value    Value
  3794.      * @param    int        $chars    Number of characters
  3795.      * @return  string 
  3796.      */
  3797.     public static function RIGHT($value ''$chars null{
  3798.         $value        self::flattenSingleValue($value);
  3799.         $chars        self::flattenSingleValue($chars);
  3800.  
  3801.         return substr($valuestrlen($value$chars);
  3802.     }
  3803.  
  3804.     /**
  3805.      * MID
  3806.      *
  3807.      * @param    string    $value    Value
  3808.      * @param    int        $start    Start character
  3809.      * @param    int        $chars    Number of characters
  3810.      * @return  string 
  3811.      */
  3812.     public static function MID($value ''$start 1$chars null{
  3813.         $value        self::flattenSingleValue($value);
  3814.         $start        self::flattenSingleValue($start);
  3815.         $chars        self::flattenSingleValue($chars);
  3816.  
  3817.         return substr($value--$start$chars);
  3818.     }
  3819.  
  3820.     /**
  3821.      * RETURNSTRING
  3822.      *
  3823.      * @param    mixed    $value    Value to check
  3824.      * @return  boolean 
  3825.      */
  3826.     public static function RETURNSTRING($testValue ''{
  3827.         $testValue    self::flattenSingleValue($testValue);
  3828.  
  3829.         if (is_string($testValue)) {
  3830.             return $testValue;
  3831.         }
  3832.         return Null;
  3833.     }
  3834.  
  3835.     /**
  3836.      * TRIMSPACES
  3837.      *
  3838.      * @param    mixed    $value    Value to check
  3839.      * @return  string 
  3840.      */
  3841.     public static function TRIMSPACES($stringValue ''{
  3842.         $stringValue    self::flattenSingleValue($stringValue);
  3843.  
  3844.         if (is_string($stringValue)) {
  3845.             return str_replace('  ',' ',trim($stringValue));
  3846.         }
  3847.         return Null;
  3848.     }
  3849.  
  3850.     private static $_invalidChars Null;
  3851.  
  3852.     /**
  3853.      * TRIMNONPRINTABLE
  3854.      *
  3855.      * @param    mixed    $value    Value to check
  3856.      * @return  string 
  3857.      */
  3858.     public static function TRIMNONPRINTABLE($stringValue ''{
  3859.         $stringValue    self::flattenSingleValue($stringValue);
  3860.  
  3861.         if (self::$_invalidChars == Null{
  3862.             self::$_invalidChars range(chr(0),chr(31));
  3863.         }
  3864.  
  3865.         if (is_string($stringValue)) {
  3866.             return str_replace(self::$_invalidChars,'',trim($stringValue,"\x00..\x1F"));
  3867.         }
  3868.         return Null;
  3869.     }
  3870.  
  3871.     /**
  3872.      * ERROR_TYPE
  3873.      *
  3874.      * @param    mixed    $value    Value to check
  3875.      * @return  boolean 
  3876.      */
  3877.     public static function ERROR_TYPE($value ''{
  3878.         $value    self::flattenSingleValue($value);
  3879.  
  3880.         $i == i;
  3881.         foreach(self::$_errorCodes as $errorCode{
  3882.             if ($value == $errorCode{
  3883.                 return $i;
  3884.             }
  3885.             ++$i;
  3886.         }
  3887.         return self::$_errorCodes['na'];
  3888.     }
  3889.  
  3890.     /**
  3891.      * IS_BLANK
  3892.      *
  3893.      * @param    mixed    $value    Value to check
  3894.      * @return  boolean 
  3895.      */
  3896.     public static function IS_BLANK($value ''{
  3897.         $value    self::flattenSingleValue($value);
  3898.  
  3899.         return (is_null($value|| (is_string($value&& ($value == '')));
  3900.     }
  3901.  
  3902.     /**
  3903.      * IS_ERR
  3904.      *
  3905.      * @param    mixed    $value    Value to check
  3906.      * @return  boolean 
  3907.      */
  3908.     public static function IS_ERR($value ''{
  3909.         $value        self::flattenSingleValue($value);
  3910.  
  3911.         return self::IS_ERROR($value&& (!self::IS_NA($value));
  3912.     }
  3913.  
  3914.     /**
  3915.      * IS_ERROR
  3916.      *
  3917.      * @param    mixed    $value    Value to check
  3918.      * @return  boolean 
  3919.      */
  3920.     public static function IS_ERROR($value ''{
  3921.         $value        self::flattenSingleValue($value);
  3922.  
  3923.         return in_array($valuearray_values(self::$_errorCodes));
  3924.     }
  3925.  
  3926.     /**
  3927.      * IS_NA
  3928.      *
  3929.      * @param    mixed    $value    Value to check
  3930.      * @return  boolean 
  3931.      */
  3932.     public static function IS_NA($value ''{
  3933.         $value        self::flattenSingleValue($value);
  3934.  
  3935.         return ($value == self::$_errorCodes['na']);
  3936.     }
  3937.  
  3938.     /**
  3939.      * IS_EVEN
  3940.      *
  3941.      * @param    mixed    $value    Value to check
  3942.      * @return  boolean 
  3943.      */
  3944.     public static function IS_EVEN($value 0{
  3945.         $value        self::flattenSingleValue($value);
  3946.  
  3947.         while (intval($value!= $value{
  3948.             $value *= 10;
  3949.         }
  3950.         return ($value == 0);
  3951.     }
  3952.  
  3953.     /**
  3954.      * IS_NUMBER
  3955.      *
  3956.      * @param    mixed    $value        Value to check
  3957.      * @return  boolean 
  3958.      */
  3959.     public static function IS_NUMBER($value 0{
  3960.         $value        self::flattenSingleValue($value);
  3961.  
  3962.         return is_numeric($value);
  3963.     }
  3964.  
  3965.     /**
  3966.      * IS_LOGICAL
  3967.      *
  3968.      * @param    mixed    $value        Value to check
  3969.      * @return  boolean 
  3970.      */
  3971.     public static function IS_LOGICAL($value true{
  3972.         $value        self::flattenSingleValue($value);
  3973.  
  3974.         return is_bool($value);
  3975.     }
  3976.  
  3977.     /**
  3978.      * IS_TEXT
  3979.      *
  3980.      * @param    mixed    $value        Value to check
  3981.      * @return  boolean 
  3982.      */
  3983.     public static function IS_TEXT($value ''{
  3984.         $value        self::flattenSingleValue($value);
  3985.  
  3986.         return is_string($value);
  3987.     }
  3988.  
  3989.     /**
  3990.      * STATEMENT_IF
  3991.      *
  3992.      * @param    mixed    $value        Value to check
  3993.      * @param    mixed    $truepart    Value when true
  3994.      * @param    mixed    $falsepart    Value when false
  3995.      * @return  mixed 
  3996.      */
  3997.     public static function STATEMENT_IF($value true$truepart ''$falsepart ''{
  3998.         $value        self::flattenSingleValue($value);
  3999.         $truepart    self::flattenSingleValue($truepart);
  4000.         $falsepart    self::flattenSingleValue($falsepart);
  4001.  
  4002.         return ($value $truepart $falsepart);
  4003.     }
  4004.  
  4005.     /**
  4006.      * STATEMENT_IFERROR
  4007.      *
  4008.      * @param    mixed    $value        Value to check , is also value when no error
  4009.      * @param    mixed    $errorpart    Value when error
  4010.      * @return  mixed 
  4011.      */
  4012.     public static function STATEMENT_IFERROR($value ''$errorpart ''{
  4013.         return self::STATEMENT_IF(self::IS_ERROR($value)$errorpart$value);
  4014.     }
  4015.  
  4016.     /**
  4017.      * VERSION
  4018.      *
  4019.      * @return  string    Version information
  4020.      */
  4021.     public static function VERSION({
  4022.         return 'PHPExcel 1.6.5, 2009-01-05';
  4023.     }
  4024.  
  4025.     /**
  4026.      * DATE
  4027.      *
  4028.      * @param    long    $year 
  4029.      * @param    long    $month 
  4030.      * @param    long    $day 
  4031.      * @return  mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4032.      *                         depending on the value of the ReturnDateType flag
  4033.      */
  4034.     public static function DATE($year 0$month 1$day 1{
  4035.         $year    = (integer) self::flattenSingleValue($year);
  4036.         $month    = (integer) self::flattenSingleValue($month);
  4037.         $day    = (integer) self::flattenSingleValue($day);
  4038.  
  4039.         $baseYear PHPExcel_Shared_Date::getExcelCalendar();
  4040.         // Validate parameters
  4041.         if ($year ($baseYear-1900)) {
  4042.             return self::$_errorCodes['num'];
  4043.         }
  4044.         if ((($baseYear-1900!= 0&& ($year $baseYear&& ($year >= 1900)) {
  4045.             return self::$_errorCodes['num'];
  4046.         }
  4047.  
  4048.         if (($year $baseYear&& ($year ($baseYear-1900))) {
  4049.             $year += 1900;
  4050.         }
  4051.  
  4052.         if ($month 1{
  4053.             //    Handle year/month adjustment if month < 1
  4054.             --$month;
  4055.             $year += ceil($month 121;
  4056.             $month 13 abs($month 12);
  4057.         elseif ($month 12{
  4058.             //    Handle year/month adjustment if month > 12
  4059.             $year += floor($month 12);
  4060.             $month ($month 12);
  4061.         }
  4062.  
  4063.         // Re-validate the year parameter after adjustments
  4064.         if (($year $baseYear|| ($year >= 10000)) {
  4065.             return self::$_errorCodes['num'];
  4066.         }
  4067.  
  4068.         // Execute function
  4069.         $excelDateValue PHPExcel_Shared_Date::FormattedPHPToExcel($year$month$day);
  4070.         switch (self::getReturnDateType()) {
  4071.             case self::RETURNDATE_EXCEL            return (float) $excelDateValue;
  4072.                                                   break;
  4073.             case self::RETURNDATE_PHP_NUMERIC    return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
  4074.                                                   break;
  4075.             case self::RETURNDATE_PHP_OBJECT    return PHPExcel_Shared_Date::ExcelToPHPObject($excelDateValue);
  4076.                                                   break;
  4077.         }
  4078.     }
  4079.  
  4080.     /**
  4081.      * TIME
  4082.      *
  4083.      * @param    long    $hour 
  4084.      * @param    long    $minute 
  4085.      * @param    long    $second 
  4086.      * @return  mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4087.      *                         depending on the value of the ReturnDateType flag
  4088.      */
  4089.     public static function TIME($hour 0$minute 0$second 0{
  4090.         $hour    self::flattenSingleValue($hour);
  4091.         $minute    self::flattenSingleValue($minute);
  4092.         $second    self::flattenSingleValue($second);
  4093.  
  4094.         if ($hour == ''$hour 0}
  4095.         if ($minute == ''$minute 0}
  4096.         if ($second == ''$second 0}
  4097.  
  4098.         if ((!is_numeric($hour)) || (!is_numeric($minute)) || (!is_numeric($second))) {
  4099.             return self::$_errorCodes['value'];
  4100.         }
  4101.         $hour    = (integer) $hour;
  4102.         $minute    = (integer) $minute;
  4103.         $second    = (integer) $second;
  4104.  
  4105.         if ($second 0{
  4106.             $minute += floor($second 60);
  4107.             $second 60 abs($second 60);
  4108.             if ($second == 60$second 0}
  4109.         elseif ($second >= 60{
  4110.             $minute += floor($second 60);
  4111.             $second $second 60;
  4112.         }
  4113.         if ($minute 0{
  4114.             $hour += floor($minute 60);
  4115.             $minute 60 abs($minute 60);
  4116.             if ($minute == 60$minute 0}
  4117.         elseif ($minute >= 60{
  4118.             $hour += floor($minute 60);
  4119.             $minute $minute 60;
  4120.         }
  4121.  
  4122.         if ($hour 23{
  4123.             $hour $hour 24;
  4124.         elseif ($hour 0{
  4125.             return self::$_errorCodes['num'];
  4126.         }
  4127.  
  4128.         // Execute function
  4129.         switch (self::getReturnDateType()) {
  4130.             case self::RETURNDATE_EXCEL            $date 0;
  4131.                                                   $calendar PHPExcel_Shared_Date::getExcelCalendar();
  4132.                                                   if ($calendar != PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900{
  4133.                                                      $date 1;
  4134.                                                   }
  4135.                                                   return (float) PHPExcel_Shared_Date::FormattedPHPToExcel($calendar1$date$hour$minute$second);
  4136.                                                   break;
  4137.             case self::RETURNDATE_PHP_NUMERIC    return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::FormattedPHPToExcel(197011$hour-1$minute$second));    // -2147468400; //    -2147472000 + 3600
  4138.                                                   break;
  4139.             case self::RETURNDATE_PHP_OBJECT    $dayAdjust 0;
  4140.                                                   if ($hour 0{
  4141.                                                      $dayAdjust floor($hour 24);
  4142.                                                      $hour 24 abs($hour 24);
  4143.                                                      if ($hour == 24$hour 0}
  4144.                                                   elseif ($hour >= 24{
  4145.                                                      $dayAdjust floor($hour 24);
  4146.                                                      $hour $hour 24;
  4147.                                                   }
  4148.                                                   $phpDateObject new DateTime('1900-01-01 '.$hour.':'.$minute.':'.$second);
  4149.                                                   if ($dayAdjust != 0{
  4150.                                                      $phpDateObject->modify($dayAdjust.' days');
  4151.                                                   }
  4152.                                                   return $phpDateObject;
  4153.                                                   break;
  4154.         }
  4155.     }
  4156.  
  4157.     /**
  4158.      * DATEVALUE
  4159.      *
  4160.      * @param    string    $dateValue 
  4161.      * @return  mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4162.      *                         depending on the value of the ReturnDateType flag
  4163.      */
  4164.     public static function DATEVALUE($dateValue 1{
  4165.         $dateValue    self::flattenSingleValue($dateValue);
  4166.  
  4167.         $PHPDateArray date_parse($dateValue);
  4168.         if (($PHPDateArray === False|| ($PHPDateArray['error_count'0)) {
  4169.             $testVal1 strtok($dateValue,'/- ');
  4170.             if ($testVal1 !== False{
  4171.                 $testVal2 strtok('/- ');
  4172.                 if ($testVal2 !== False{
  4173.                     $testVal3 strtok('/- ');
  4174.                     if ($testVal3 === False{
  4175.                         $testVal3 strftime('%Y');
  4176.                     }
  4177.                 else {
  4178.                     return self::$_errorCodes['value'];
  4179.                 }
  4180.             else {
  4181.                 return self::$_errorCodes['value'];
  4182.             }
  4183.             $PHPDateArray date_parse($testVal1.'-'.$testVal2.'-'.$testVal3);
  4184.             if (($PHPDateArray === False|| ($PHPDateArray['error_count'0)) {
  4185.                 $PHPDateArray date_parse($testVal2.'-'.$testVal1.'-'.$testVal3);
  4186.                 if (($PHPDateArray === False|| ($PHPDateArray['error_count'0)) {
  4187.                     return self::$_errorCodes['value'];
  4188.                 }
  4189.             }
  4190.         }
  4191.  
  4192.         if (($PHPDateArray !== False&& ($PHPDateArray['error_count'== 0)) {
  4193.             // Execute function
  4194.             if ($PHPDateArray['year'== '')    $PHPDateArray['year'strftime('%Y')}
  4195.             if ($PHPDateArray['month'== '')    $PHPDateArray['month'strftime('%m')}
  4196.             if ($PHPDateArray['day'== '')        $PHPDateArray['day'strftime('%d')}
  4197.             $excelDateValue floor(PHPExcel_Shared_Date::FormattedPHPToExcel($PHPDateArray['year'],$PHPDateArray['month'],$PHPDateArray['day'],$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']));
  4198.  
  4199.             switch (self::getReturnDateType()) {
  4200.                 case self::RETURNDATE_EXCEL            return (float) $excelDateValue;
  4201.                                                       break;
  4202.                 case self::RETURNDATE_PHP_NUMERIC    return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
  4203.                                                       break;
  4204.                 case self::RETURNDATE_PHP_OBJECT    return new DateTime($PHPDateArray['year'].'-'.$PHPDateArray['month'].'-'.$PHPDateArray['day'].' 00:00:00');
  4205.                                                       break;
  4206.             }
  4207.         }
  4208.         return self::$_errorCodes['value'];
  4209.     }
  4210.  
  4211.     /**
  4212.      * _getDateValue
  4213.      *
  4214.      * @param    string    $dateValue 
  4215.      * @return  mixed    Excel date/time serial value, or string if error
  4216.      */
  4217.     private static function _getDateValue($dateValue{
  4218.         if (!is_numeric($dateValue)) {
  4219.             if ((is_string($dateValue)) && (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC)) {
  4220.                 return self::$_errorCodes['value'];
  4221.             }
  4222.             if ((is_object($dateValue)) && ($dateValue instanceof PHPExcel_Shared_Date::$dateTimeObjectType)) {
  4223.                 $dateValue PHPExcel_Shared_Date::PHPToExcel($dateValue);
  4224.             else {
  4225.                 $saveReturnDateType self::getReturnDateType();
  4226.                 self::setReturnDateType(self::RETURNDATE_EXCEL);
  4227.                 $dateValue self::DATEVALUE($dateValue);
  4228.                 self::setReturnDateType($saveReturnDateType);
  4229.             }
  4230.         elseif (!is_float($dateValue)) {
  4231.             $dateValue PHPExcel_Shared_Date::PHPToExcel($dateValue);
  4232.         }
  4233.         return $dateValue;
  4234.     }
  4235.  
  4236.     /**
  4237.      * TIMEVALUE
  4238.      *
  4239.      * @param    string    $timeValue 
  4240.      * @return  mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4241.      *                         depending on the value of the ReturnDateType flag
  4242.      */
  4243.     public static function TIMEVALUE($timeValue{
  4244.         $timeValue    self::flattenSingleValue($timeValue);
  4245.  
  4246.         if ((($PHPDateArray date_parse($timeValue)) !== False&& ($PHPDateArray['error_count'== 0)) {
  4247.             if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE{
  4248.                 $excelDateValue PHPExcel_Shared_Date::FormattedPHPToExcel($PHPDateArray['year'],$PHPDateArray['month'],$PHPDateArray['day'],$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']);
  4249.             else {
  4250.                 $excelDateValue PHPExcel_Shared_Date::FormattedPHPToExcel(1900,1,1,$PHPDateArray['hour'],$PHPDateArray['minute'],$PHPDateArray['second']1;
  4251.             }
  4252.  
  4253.             switch (self::getReturnDateType()) {
  4254.                 case self::RETURNDATE_EXCEL            return (float) $excelDateValue;
  4255.                                                       break;
  4256.                 case self::RETURNDATE_PHP_NUMERIC    return (integer) $phpDateValue PHPExcel_Shared_Date::ExcelToPHP($excelDateValue+255693600;;
  4257.                                                       break;
  4258.                 case self::RETURNDATE_PHP_OBJECT    return new DateTime('1900-01-01 '.$PHPDateArray['hour'].':'.$PHPDateArray['minute'].':'.$PHPDateArray['second']);
  4259.                                                       break;
  4260.             }
  4261.         }
  4262.         return self::$_errorCodes['value'];
  4263.     }
  4264.  
  4265.     /**
  4266.      * _getTimeValue
  4267.      *
  4268.      * @param    string    $timeValue 
  4269.      * @return  mixed    Excel date/time serial value, or string if error
  4270.      */
  4271.     private static function _getTimeValue($timeValue{
  4272.         $saveReturnDateType self::getReturnDateType();
  4273.         self::setReturnDateType(self::RETURNDATE_EXCEL);
  4274.         $timeValue self::TIMEVALUE($timeValue);
  4275.         self::setReturnDateType($saveReturnDateType);
  4276.         return $timeValue;
  4277.     }
  4278.  
  4279.     /**
  4280.      * DATETIMENOW
  4281.      *
  4282.      * @return  mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4283.      *                         depending on the value of the ReturnDateType flag
  4284.      */
  4285.     public static function DATETIMENOW({
  4286.         $saveTimeZone date_default_timezone_get();
  4287.         date_default_timezone_set('UTC');
  4288.         $retValue False;
  4289.         switch (self::getReturnDateType()) {
  4290.             case self::RETURNDATE_EXCEL            $retValue = (float) PHPExcel_Shared_Date::PHPToExcel(time());
  4291.                                                   break;
  4292.             case self::RETURNDATE_PHP_NUMERIC    $retValue = (integer) time();
  4293.                                                   break;
  4294.             case self::RETURNDATE_PHP_OBJECT    $retValue new DateTime();
  4295.                                                   break;
  4296.         }
  4297.         date_default_timezone_set($saveTimeZone);
  4298.  
  4299.         return $retValue;
  4300.     }
  4301.  
  4302.     /**
  4303.      * DATENOW
  4304.      *
  4305.      * @return  mixed    Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  4306.      *                         depending on the value of the ReturnDateType flag
  4307.      */
  4308.     public static function DATENOW({
  4309.         $saveTimeZone date_default_timezone_get();
  4310.         date_default_timezone_set('UTC');
  4311.         $retValue False;
  4312.         $excelDateTime floor(PHPExcel_Shared_Date::PHPToExcel(time()));
  4313.         switch (self::getReturnDateType()) {
  4314.             case self::RETURNDATE_EXCEL            $retValue = (float) $excelDateTime;
  4315.                                                   break;
  4316.             case self::RETURNDATE_PHP_NUMERIC    $retValue = (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateTime3600;
  4317.                                                   break;
  4318.             case self::RETURNDATE_PHP_OBJECT    $retValue PHPExcel_Shared_Date::ExcelToPHPObject($excelDateTime);
  4319.                                                   break;
  4320.         }
  4321.         date_default_timezone_set($saveTimeZone);
  4322.  
  4323.         return $retValue;
  4324.     }
  4325.  
  4326.     private static function isLeapYear($year{
  4327.         return ((($year 4== 0&& (($year 100!= 0|| (($year 400== 0));
  4328.     }
  4329.  
  4330.     private static function dateDiff360($startDay$startMonth$startYear$endDay$endMonth$endYear$methodUS{
  4331.         if ($startDay == 31{
  4332.             --$startDay;
  4333.         elseif ($methodUS && ($startMonth == && ($startDay == 29 || ($startDay == 28 && !self::isLeapYear($startYear))))) {
  4334.             $startDay 30;
  4335.         }
  4336.         if ($endDay == 31{
  4337.             if ($methodUS && $startDay != 30{
  4338.                 $endDay 1;
  4339.                 if ($endMonth == 12{
  4340.                     ++$endYear;
  4341.                     $endMonth 1;
  4342.                 else {
  4343.                     ++$endMonth;
  4344.                 }
  4345.             else {
  4346.                 $endDay 30;
  4347.             }
  4348.         }
  4349.  
  4350.         return $endDay $endMonth 30 $endYear 360 $startDay $startMonth 30 $startYear 360;
  4351.     }
  4352.  
  4353.     /**
  4354.      * DAYS360
  4355.      *
  4356.      * @param    long    $startDate        Excel date serial value or a standard date string
  4357.      * @param    long    $endDate        Excel date serial value or a standard date string
  4358.      * @param    boolean    $method            US or European Method
  4359.      * @return  long    PHP date/time serial
  4360.      */
  4361.     public static function DAYS360($startDate 0$endDate 0$method false{
  4362.         $startDate    self::flattenSingleValue($startDate);
  4363.         $endDate    self::flattenSingleValue($endDate);
  4364.  
  4365.         if (is_string($startDate self::_getDateValue($startDate))) {
  4366.             return self::$_errorCodes['value'];
  4367.         }
  4368.         if (is_string($endDate self::_getDateValue($endDate))) {
  4369.             return self::$_errorCodes['value'];
  4370.         }
  4371.  
  4372.         // Execute function
  4373.         $PHPStartDateObject PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
  4374.         $startDay $PHPStartDateObject->format('j');
  4375.         $startMonth $PHPStartDateObject->format('n');
  4376.         $startYear $PHPStartDateObject->format('Y');
  4377.  
  4378.         $PHPEndDateObject PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  4379.         $endDay $PHPEndDateObject->format('j');
  4380.         $endMonth $PHPEndDateObject->format('n');
  4381.         $endYear $PHPEndDateObject->format('Y');
  4382.  
  4383.         return self::dateDiff360($startDay$startMonth$startYear$endDay$endMonth$endYear!$method);
  4384.     }
  4385.  
  4386.     /**
  4387.      * DATEDIF
  4388.      *
  4389.      * @param    long    $startDate        Excel date serial value or a standard date string
  4390.      * @param    long    $endDate        Excel date serial value or a standard date string
  4391.      * @param    string    $unit 
  4392.      * @return  long    Interval between the dates
  4393.      */
  4394.     public static function DATEDIF($startDate 0$endDate 0$unit 'D'{
  4395.         $startDate    self::flattenSingleValue($startDate);
  4396.         $endDate    self::flattenSingleValue($endDate);
  4397.         $unit        strtoupper(self::flattenSingleValue($unit));
  4398.  
  4399.         if (is_string($startDate self::_getDateValue($startDate))) {
  4400.             return self::$_errorCodes['value'];
  4401.         }
  4402.         if (is_string($endDate self::_getDateValue($endDate))) {
  4403.             return self::$_errorCodes['value'];
  4404.         }
  4405.  
  4406.         // Validate parameters
  4407.         if ($startDate >= $endDate{
  4408.             return self::$_errorCodes['num'];
  4409.         }
  4410.  
  4411.         // Execute function
  4412.         $difference $endDate $startDate;
  4413.  
  4414.         $PHPStartDateObject PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
  4415.         $startDays $PHPStartDateObject->format('j');
  4416.         $startMonths $PHPStartDateObject->format('n');
  4417.         $startYears $PHPStartDateObject->format('Y');
  4418.  
  4419.         $PHPEndDateObject PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  4420.         $endDays $PHPEndDateObject->format('j');
  4421.         $endMonths $PHPEndDateObject->format('n');
  4422.         $endYears $PHPEndDateObject->format('Y');
  4423.  
  4424.         $retVal self::$_errorCodes['num'];
  4425.         switch ($unit{
  4426.             case 'D':
  4427.                 $retVal intval($difference);
  4428.                 break;
  4429.             case 'M':
  4430.                 $retVal intval($endMonths $startMonths(intval($endYears $startYears12);
  4431.                 //    We're only interested in full months
  4432.                 if ($endDays $startDays{
  4433.                     --$retVal;
  4434.                 }
  4435.                 break;
  4436.             case 'Y':
  4437.                 $retVal intval($endYears $startYears);
  4438.                 //    We're only interested in full months
  4439.                 if ($endMonths $startMonths{
  4440.                     --$retVal;
  4441.                 elseif (($endMonths == $startMonths&& ($endDays $startDays)) {
  4442.                     --$retVal;
  4443.                 }
  4444.                 break;
  4445.             case 'MD':
  4446.                 if ($endDays $startDays{
  4447.                     $retVal $endDays;
  4448.                     $PHPEndDateObject->modify('-'.$endDays.' days');
  4449.                     $adjustDays $PHPEndDateObject->format('j');
  4450.                     if ($adjustDays $startDays{
  4451.                         $retVal += ($adjustDays $startDays);
  4452.                     }
  4453.                 else {
  4454.                     $retVal $endDays $startDays;
  4455.                 }
  4456.                 break;
  4457.             case 'YM':
  4458.                 $retVal abs(intval($endMonths $startMonths));
  4459.                 //    We're only interested in full months
  4460.                 if ($endDays $startDays{
  4461.                     --$retVal;
  4462.                 }
  4463.                 break;
  4464.             case 'YD':
  4465.                 $retVal intval($difference);
  4466.                 if ($endYears $startYears{
  4467.                     while ($endYears $startYears{
  4468.                         $PHPEndDateObject->modify('-1 year');
  4469.                         $endYears $PHPEndDateObject->format('Y');
  4470.                     }
  4471.                     $retVal abs($PHPEndDateObject->format('z'$PHPStartDateObject->format('z'));
  4472.                 }
  4473.                 break;
  4474.         }
  4475.         return $retVal;
  4476.     }
  4477.  
  4478.     /**
  4479.      * YEARFRAC
  4480.      *
  4481.      * @param    long    $startDate        Excel date serial value or a standard date string
  4482.      * @param    long    $endDate        Excel date serial value or a standard date string
  4483.      * @param    integer    $method            Method used for the calculation
  4484.      * @return  long    PHP date/time serial
  4485.      */
  4486.     public static function YEARFRAC($startDate 0$endDate 0$method 0{
  4487.         $startDate    self::flattenSingleValue($startDate);
  4488.         $endDate    self::flattenSingleValue($endDate);
  4489.         $method        self::flattenSingleValue($method);
  4490.  
  4491.         if (is_string($startDate self::_getDateValue($startDate))) {
  4492.             return self::$_errorCodes['value'];
  4493.         }
  4494.         if (is_string($endDate self::_getDateValue($endDate))) {
  4495.             return self::$_errorCodes['value'];
  4496.         }
  4497.  
  4498.         if ((is_numeric($method)) && (!is_string($method))) {
  4499.             switch($method{
  4500.                 case 0    :
  4501.                     return self::DAYS360($startDate,$endDate360;
  4502.                     break;
  4503.                 case 1    :
  4504.                     $startYear self::YEAR($startDate);
  4505.                     $endYear self::YEAR($endDate);
  4506.                     $leapDay 0;
  4507.                     if (self::isLeapYear($startYear|| self::isLeapYear($endYear)) {
  4508.                         $leapDay 1;
  4509.                     }
  4510.                     return self::DATEDIF($startDate,$endDate(365 $leapDay);
  4511.                     break;
  4512.                 case 2    :
  4513.                     return self::DATEDIF($startDate,$endDate360;
  4514.                     break;
  4515.                 case 3    :
  4516.                     return self::DATEDIF($startDate,$endDate365;
  4517.                     break;
  4518.                 case 4    :
  4519.                     return self::DAYS360($startDate,$endDate,True360;
  4520.                     break;
  4521.             }
  4522.         }
  4523.         return self::$_errorCodes['value'];
  4524.     }
  4525.  
  4526.     /**
  4527.      * NETWORKDAYS
  4528.      *
  4529.      * @param    mixed                Start date
  4530.      * @param    mixed                End date
  4531.      * @param    array of mixed        Optional Date Series
  4532.      * @return  long    Interval between the dates
  4533.      */
  4534.     public static function NETWORKDAYS($startDate,$endDate{
  4535.         //    Flush the mandatory start and end date that are referenced in the function definition
  4536.         $dateArgs self::flattenArray(func_get_args());
  4537.         array_shift($dateArgs);
  4538.         array_shift($dateArgs);
  4539.  
  4540.         //    Validate the start and end dates
  4541.         if (is_string($startDate $sDate self::_getDateValue($startDate))) {
  4542.             return self::$_errorCodes['value'];
  4543.         }
  4544.         if (is_string($endDate $eDate self::_getDateValue($endDate))) {
  4545.             return self::$_errorCodes['value'];
  4546.         }
  4547.  
  4548.         if ($sDate $eDate{
  4549.             $startDate $eDate;
  4550.             $endDate $sDate;
  4551.         }
  4552.  
  4553.         // Execute function
  4554.         $startDoW self::DAYOFWEEK($startDate,2);
  4555.         if ($startDoW 0$startDoW 0}
  4556.         $endDoW self::DAYOFWEEK($endDate,2);
  4557.         if ($endDoW >= 6$endDoW 0}
  4558.  
  4559.         $wholeWeekDays floor(($endDate $startDate75;
  4560.         $partWeekDays $endDoW $startDoW;
  4561.         if ($partWeekDays 5{
  4562.             $partWeekDays -= 5;
  4563.         }
  4564.  
  4565.         //    Test any extra holiday parameters
  4566.         $holidayCountedArray array();
  4567.         foreach ($dateArgs as $holidayDate{
  4568.             if (is_string($holidayDate self::_getDateValue($holidayDate))) {
  4569.                 return self::$_errorCodes['value'];
  4570.             }
  4571.             if (($holidayDate >= $startDate&& ($holidayDate <= $endDate)) {
  4572.                 if ((self::DAYOFWEEK($holidayDate,26&& (!in_array($holidayDate,$holidayCountedArray))) {
  4573.                     --$partWeekDays;
  4574.                     $holidayCountedArray[$holidayDate;
  4575.                 }
  4576.             }
  4577.         }
  4578.  
  4579.         if ($sDate $eDate{
  4580.             return ($wholeWeekDays $partWeekDays);
  4581.         }
  4582.         return $wholeWeekDays $partWeekDays;
  4583.     }
  4584.  
  4585.     /**
  4586.      * WORKDAY
  4587.      *
  4588.      * @param    mixed                Start date
  4589.      * @param    mixed                number of days for adjustment
  4590.      * @param    array of mixed        Optional Date Series
  4591.      * @return  long    Interval between the dates
  4592.      */
  4593.     public static function WORKDAY($startDate,$endDays{
  4594.         $dateArgs self::flattenArray(func_get_args());
  4595.  
  4596.         array_shift($dateArgs);
  4597.         array_shift($dateArgs);
  4598.  
  4599.         if (is_string($startDate self::_getDateValue($startDate))) {
  4600.             return self::$_errorCodes['value'];
  4601.         }
  4602.         if (!is_numeric($endDays)) {
  4603.             return self::$_errorCodes['value'];
  4604.         }
  4605.         $endDate = (float) $startDate (floor($endDays 57($endDays 5);
  4606.         if ($endDays 0{
  4607.             $endDate += 7;
  4608.         }
  4609.  
  4610.         $endDoW self::DAYOFWEEK($endDate,3);
  4611.         if ($endDoW >= 5{
  4612.             if ($endDays >= 0{
  4613.                 $endDate += ($endDoW);
  4614.             else {
  4615.                 $endDate -= ($endDoW 5);
  4616.             }
  4617.         }
  4618.  
  4619.         //    Test any extra holiday parameters
  4620.         if (count($dateArgs0{
  4621.             $holidayCountedArray $holidayDates array();
  4622.             foreach ($dateArgs as $holidayDate{
  4623.                 if (is_string($holidayDate self::_getDateValue($holidayDate))) {
  4624.                     return self::$_errorCodes['value'];
  4625.                 }
  4626.                 $holidayDates[$holidayDate;
  4627.             }
  4628.             if ($endDays >= 0{
  4629.                 sort($holidayDatesSORT_NUMERIC);
  4630.             else {
  4631.                 rsort($holidayDatesSORT_NUMERIC);
  4632.             }
  4633.             foreach ($holidayDates as $holidayDate{
  4634.                 if ($endDays >= 0{
  4635.                     if (($holidayDate >= $startDate&& ($holidayDate <= $endDate)) {
  4636.                         if ((self::DAYOFWEEK($holidayDate,26&& (!in_array($holidayDate,$holidayCountedArray))) {
  4637.                             ++$endDate;
  4638.                             $holidayCountedArray[$holidayDate;
  4639.                         }
  4640.                     }
  4641.                 else {
  4642.                     if (($holidayDate <= $startDate&& ($holidayDate >= $endDate)) {
  4643.                         if ((self::DAYOFWEEK($holidayDate,26&& (!in_array($holidayDate,$holidayCountedArray))) {
  4644.                             --$endDate;
  4645.                             $holidayCountedArray[$holidayDate;
  4646.                         }
  4647.                     }
  4648.                 }
  4649.                 $endDoW self::DAYOFWEEK($endDate,3);
  4650.                 if ($endDoW >= 5{
  4651.                     if ($endDays >= 0{
  4652.                         $endDate += ($endDoW);
  4653.                     else {
  4654.                         $endDate -= ($endDoW 5);
  4655.                     }
  4656.                 }
  4657.             }
  4658.         }
  4659.  
  4660.         switch (self::getReturnDateType()) {
  4661.             case self::RETURNDATE_EXCEL            return (float) $endDate;
  4662.                                                   break;
  4663.             case self::RETURNDATE_PHP_NUMERIC    return (integer) PHPExcel_Shared_Date::ExcelToPHP($endDate);
  4664.                                                   break;
  4665.             case self::RETURNDATE_PHP_OBJECT    return PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  4666.                                                   break;
  4667.         }
  4668.     }
  4669.  
  4670.     /**
  4671.      * DAYOFMONTH
  4672.      *
  4673.      * @param    long    $dateValue        Excel date serial value or a standard date string
  4674.      * @return  int        Day
  4675.      */
  4676.     public static function DAYOFMONTH($dateValue 1{
  4677.         $dateValue    self::flattenSingleValue($dateValue);
  4678.  
  4679.         if (is_string($dateValue self::_getDateValue($dateValue))) {
  4680.             return self::$_errorCodes['value'];
  4681.         }
  4682.  
  4683.         // Execute function
  4684.         $PHPDateObject PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  4685.  
  4686.         return $PHPDateObject->format('j');
  4687.     }
  4688.  
  4689.     /**
  4690.      * DAYOFWEEK
  4691.      *
  4692.      * @param    long    $dateValue        Excel date serial value or a standard date string
  4693.      * @return  int        Day
  4694.      */
  4695.     public static function DAYOFWEEK($dateValue 1$style 1{
  4696.         $dateValue    self::flattenSingleValue($dateValue);
  4697.         $style        floor(self::flattenSingleValue($style));
  4698.  
  4699.         if (is_string($dateValue self::_getDateValue($dateValue))) {
  4700.             return self::$_errorCodes['value'];
  4701.         }
  4702.  
  4703.         // Execute function
  4704.         $PHPDateObject PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  4705.         $DoW $PHPDateObject->format('w');
  4706.         $firstDay 1;
  4707.         switch ($style{
  4708.             case 1: ++$DoW;
  4709.                     break;
  4710.             case 2if ($DoW == 0$DoW 7}
  4711.                     break;
  4712.             case 3if ($DoW == 0$DoW 7}
  4713.                     $firstDay 0;
  4714.                     --$DoW;
  4715.                     break;
  4716.             default:
  4717.         }
  4718.         if (self::$compatibilityMode == self::COMPATIBILITY_EXCEL{
  4719.             //    Test for Excel's 1900 leap year, and introduce the error as required
  4720.             if (($PHPDateObject->format('Y'== 1900&& ($PHPDateObject->format('n'<= 2)) {
  4721.                 --$DoW;
  4722.                 if ($DoW $firstDay{
  4723.                     $DoW += 7;
  4724.                 }
  4725.             }
  4726.         }
  4727.  
  4728.         return $DoW;
  4729.     }
  4730.  
  4731.     /**
  4732.      * WEEKOFYEAR
  4733.      *
  4734.      * @param    long    $dateValue        Excel date serial value or a standard date string
  4735.      * @param    boolean    $method            Week begins on Sunday or Monday
  4736.      * @return  int        Week Number
  4737.      */
  4738.     public static function WEEKOFYEAR($dateValue 1$method 1{
  4739.         $dateValue    self::flattenSingleValue($dateValue);
  4740.         $method        floor(self::flattenSingleValue($method));
  4741.  
  4742.         if (!is_numeric($method)) {
  4743.             return self::$_errorCodes['value'];
  4744.         elseif (($method 1|| ($method 2)) {
  4745.             return self::$_errorCodes['num'];
  4746.         }
  4747.  
  4748.         if (is_string($dateValue self::_getDateValue($dateValue))) {
  4749.             return self::$_errorCodes['value'];
  4750.         }
  4751.  
  4752.         // Execute function
  4753.         $PHPDateObject PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  4754.         $dayOfYear $PHPDateObject->format('z');
  4755.         $dow $PHPDateObject->format('w');
  4756.         $PHPDateObject->modify('-'.$dayOfYear.' days');
  4757.         $dow $PHPDateObject->format('w');
  4758.         $daysInFirstWeek (($dow ($method)) 7);
  4759.         $dayOfYear -= $daysInFirstWeek;
  4760.         $weekOfYear ceil($dayOfYear 71;
  4761.  
  4762.         return $weekOfYear;
  4763.     }
  4764.  
  4765.     /**
  4766.      * MONTHOFYEAR
  4767.      *
  4768.      * @param    long    $dateValue        Excel date serial value or a standard date string
  4769.      * @return  int        Month
  4770.      */
  4771.     public static function MONTHOFYEAR($dateValue 1{
  4772.         $dateValue    self::flattenSingleValue($dateValue);
  4773.  
  4774.         if (is_string($dateValue self::_getDateValue($dateValue))) {
  4775.             return self::$_errorCodes['value'];
  4776.         }
  4777.  
  4778.         // Execute function
  4779.         $PHPDateObject PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  4780.  
  4781.         return $PHPDateObject->format('n');
  4782.     }
  4783.  
  4784.     /**
  4785.      * YEAR
  4786.      *
  4787.      * @param    long    $dateValue        Excel date serial value or a standard date string
  4788.      * @return  int        Year
  4789.      */
  4790.     public static function YEAR($dateValue 1{
  4791.         $dateValue    self::flattenSingleValue($dateValue);
  4792.  
  4793.         if (is_string($dateValue self::_getDateValue($dateValue))) {
  4794.             return self::$_errorCodes['value'];
  4795.         }
  4796.  
  4797.         // Execute function
  4798.         $PHPDateObject PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  4799.  
  4800.         return $PHPDateObject->format('Y');
  4801.     }
  4802.  
  4803.     /**
  4804.      * HOUROFDAY
  4805.      *
  4806.      * @param    mixed    $timeValue        Excel time serial value or a standard time string
  4807.      * @return  int        Hour
  4808.      */
  4809.     public static function HOUROFDAY($timeValue 0{
  4810.         $timeValue    self::flattenSingleValue($timeValue);
  4811.  
  4812.         if (!is_numeric($timeValue)) {
  4813.             if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC{
  4814.                 $testVal strtok($timeValue,'/-: ');
  4815.                 if (strlen($testValstrlen($timeValue)) {
  4816.                     return self::$_errorCodes['value'];
  4817.                 }
  4818.             }
  4819.             $timeValue self::_getTimeValue($timeValue);
  4820.             if (is_string($timeValue)) {
  4821.                 return self::$_errorCodes['value'];
  4822.             }
  4823.         }
  4824.         // Execute function
  4825.         if (is_real($timeValue)) {
  4826.             $timeValue PHPExcel_Shared_Date::ExcelToPHP($timeValue);
  4827.         }
  4828.         return date('G',$timeValue);
  4829.     }
  4830.  
  4831.     /**
  4832.      * MINUTEOFHOUR
  4833.      *
  4834.      * @param    long    $timeValue        Excel time serial value or a standard time string
  4835.      * @return  int        Minute
  4836.      */
  4837.     public static function MINUTEOFHOUR($timeValue 0{
  4838.         $timeValue $timeTester    self::flattenSingleValue($timeValue);
  4839.  
  4840.         if (!is_numeric($timeValue)) {
  4841.             if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC{
  4842.                 $testVal strtok($timeValue,'/-: ');
  4843.                 if (strlen($testValstrlen($timeValue)) {
  4844.                     return self::$_errorCodes['value'];
  4845.                 }
  4846.             }
  4847.             $timeValue self::_getTimeValue($timeValue);
  4848.             if (is_string($timeValue)) {
  4849.                 return self::$_errorCodes['value'];
  4850.             }
  4851.         }
  4852.         // Execute function
  4853.         if (is_real($timeValue)) {
  4854.             $timeValue PHPExcel_Shared_Date::ExcelToPHP($timeValue);
  4855.         }
  4856.         return (int) date('i',$timeValue);
  4857.     }
  4858.  
  4859.     /**
  4860.      * SECONDOFMINUTE
  4861.      *
  4862.      * @param    long    $timeValue        Excel time serial value or a standard time string
  4863.      * @return  int        Second
  4864.      */
  4865.     public static function SECONDOFMINUTE($timeValue 0{
  4866.         $timeValue    self::flattenSingleValue($timeValue);
  4867.  
  4868.         if (!is_numeric($timeValue)) {
  4869.             if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC{
  4870.                 $testVal strtok($timeValue,'/-: ');
  4871.                 if (strlen($testValstrlen($timeValue)) {
  4872.                     return self::$_errorCodes['value'];
  4873.                 }
  4874.             }
  4875.             $timeValue self::_getTimeValue($timeValue);
  4876.             if (is_string($timeValue)) {
  4877.                 return self::$_errorCodes['value'];
  4878.             }
  4879.         }
  4880.         // Execute function
  4881.         if (is_real($timeValue)) {
  4882.             $timeValue PHPExcel_Shared_Date::ExcelToPHP($timeValue);
  4883.         }
  4884.         return (int) date('s',$timeValue);
  4885.     }
  4886.  
  4887.     private static function adjustDateByMonths ($dateValue 0$adjustmentMonths 0{
  4888.         // Execute function
  4889.         $PHPDateObject PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  4890.         $oMonth = (int) $PHPDateObject->format('m');
  4891.         $oYear = (int) $PHPDateObject->format('Y');
  4892.  
  4893.         $adjustmentMonthsString = (string) $adjustmentMonths;
  4894.         if ($adjustmentMonths 0{
  4895.             $adjustmentMonthsString '+'.$adjustmentMonths;
  4896.         }
  4897.         if ($adjustmentMonths != 0{
  4898.             $PHPDateObject->modify($adjustmentMonthsString.' months');
  4899.         }
  4900.         $nMonth = (int) $PHPDateObject->format('m');
  4901.         $nYear = (int) $PHPDateObject->format('Y');
  4902.  
  4903.         $monthDiff ($nMonth $oMonth(($nYear $oYear12);
  4904.         if ($monthDiff != $adjustmentMonths{
  4905.             $adjustDays = (int) $PHPDateObject->format('d');
  4906.             $adjustDaysString '-'.$adjustDays.' days';
  4907.             $PHPDateObject->modify($adjustDaysString);
  4908.         }
  4909.         return $PHPDateObject;
  4910.     }
  4911.  
  4912.     /**
  4913.      * EDATE
  4914.      *
  4915.      * Returns the serial number that represents the date that is the indicated number of months before or after a specified date
  4916.      * (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.
  4917.      *
  4918.      * @param    long    $dateValue                Excel date serial value or a standard date string
  4919.      * @param    int        $adjustmentMonths        Number of months to adjust by
  4920.      * @return  long    Excel date serial value
  4921.      */
  4922.     public static function EDATE($dateValue 1$adjustmentMonths 0{
  4923.         $dateValue            self::flattenSingleValue($dateValue);
  4924.         $adjustmentMonths    floor(self::flattenSingleValue($adjustmentMonths));
  4925.  
  4926.         if (!is_numeric($adjustmentMonths)) {
  4927.             return self::$_errorCodes['value'];
  4928.         }
  4929.  
  4930.         if (is_string($dateValue self::_getDateValue($dateValue))) {
  4931.             return self::$_errorCodes['value'];
  4932.         }
  4933.  
  4934.         // Execute function
  4935.         $PHPDateObject self::adjustDateByMonths($dateValue,$adjustmentMonths);
  4936.  
  4937.         switch (self::getReturnDateType()) {
  4938.             case self::RETURNDATE_EXCEL            return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
  4939.                                                   break;
  4940.             case self::RETURNDATE_PHP_NUMERIC    return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
  4941.                                                   break;
  4942.             case self::RETURNDATE_PHP_OBJECT    return $PHPDateObject;
  4943.                                                   break;
  4944.         }
  4945.     }
  4946.  
  4947.     /**
  4948.      * EOMONTH
  4949.      *
  4950.      * Returns the serial number for the last day of the month that is the indicated number of months before or after start_date.
  4951.      * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
  4952.      *
  4953.      * @param    long    $dateValue            Excel date serial value or a standard date string
  4954.      * @param    int        $adjustmentMonths    Number of months to adjust by
  4955.      * @return  long    Excel date serial value
  4956.      */
  4957.     public static function EOMONTH($dateValue 1$adjustmentMonths 0{
  4958.         $dateValue            self::flattenSingleValue($dateValue);
  4959.         $adjustmentMonths    floor(self::flattenSingleValue($adjustmentMonths));
  4960.  
  4961.         if (!is_numeric($adjustmentMonths)) {
  4962.             return self::$_errorCodes['value'];
  4963.         }
  4964.  
  4965.         if (is_string($dateValue self::_getDateValue($dateValue))) {
  4966.             return self::$_errorCodes['value'];
  4967.         }
  4968.  
  4969.         // Execute function
  4970.         $PHPDateObject self::adjustDateByMonths($dateValue,$adjustmentMonths+1);
  4971.         $adjustDays = (int) $PHPDateObject->format('d');
  4972.         $adjustDaysString '-'.$adjustDays.' days';
  4973.         $PHPDateObject->modify($adjustDaysString);
  4974.  
  4975.         switch (self::getReturnDateType()) {
  4976.             case self::RETURNDATE_EXCEL            return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
  4977.                                                   break;
  4978.             case self::RETURNDATE_PHP_NUMERIC    return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
  4979.                                                   break;
  4980.             case self::RETURNDATE_PHP_OBJECT    return $PHPDateObject;
  4981.                                                   break;
  4982.         }
  4983.     }
  4984.  
  4985.     /**
  4986.      * TRUNC
  4987.      *
  4988.      * Truncates value to the number of fractional digits by number_digits.
  4989.      *
  4990.      * @param    float        $value 
  4991.      * @param    int            $number_digits 
  4992.      * @return  float        Truncated value
  4993.      */
  4994.     public static function TRUNC($value 0$number_digits 0{
  4995.         $value            self::flattenSingleValue($value);
  4996.         $number_digits    self::flattenSingleValue($number_digits);
  4997.  
  4998.         // Validate parameters
  4999.         if ($number_digits 0{
  5000.             return self::$_errorCodes['value'];
  5001.         }
  5002.  
  5003.         // Truncate
  5004.         if ($number_digits 0{
  5005.             $value $value pow(10$number_digits);
  5006.         }
  5007.         $value intval($value);
  5008.         if ($number_digits 0{
  5009.             $value $value pow(10$number_digits);
  5010.         }
  5011.  
  5012.         // Return
  5013.         return $value;
  5014.     }
  5015.  
  5016.     /**
  5017.      * POWER
  5018.      *
  5019.      * Computes x raised to the power y.
  5020.      *
  5021.      * @param    float        $x 
  5022.      * @param    float        $y 
  5023.      * @return  float 
  5024.      */
  5025.     public static function POWER($x 0$y 2{
  5026.         $x    self::flattenSingleValue($x);
  5027.         $y    self::flattenSingleValue($y);
  5028.  
  5029.         // Validate parameters
  5030.         if ($x 0{
  5031.             return self::$_errorCodes['num'];
  5032.         }
  5033.         if ($x == && $y <= 0{
  5034.             return self::$_errorCodes['divisionbyzero'];
  5035.         }
  5036.  
  5037.         // Return
  5038.         return pow($x$y);
  5039.     }
  5040.  
  5041.     /**
  5042.      * BINTODEC
  5043.      *
  5044.      * Return a binary value as Decimal.
  5045.      *
  5046.      * @param    string        $x 
  5047.      * @return  string 
  5048.      */
  5049.     public static function BINTODEC($x{
  5050.         $x    self::flattenSingleValue($x);
  5051.  
  5052.         if (is_bool($x)) {
  5053.             if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE{
  5054.                 $x = (int) $x;
  5055.             else {
  5056.                 return self::$_errorCodes['value'];
  5057.             }
  5058.         }
  5059.         if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC{
  5060.             $x floor($x);
  5061.         }
  5062.         $x = (string) $x;
  5063.         if (strlen($xpreg_match_all('/[01]/',$x,$out)) {
  5064.             return self::$_errorCodes['num'];
  5065.         }
  5066.         if (strlen($x10{
  5067.             return self::$_errorCodes['num'];
  5068.         elseif (strlen($x== 10{
  5069.             //    Two's Complement
  5070.             $x substr($x,-9);
  5071.             return '-'.(512-bindec($x));
  5072.         }
  5073.         return bindec($x);
  5074.     }
  5075.  
  5076.     /**
  5077.      * BINTOHEX
  5078.      *
  5079.      * Return a binary value as Hex.
  5080.      *
  5081.      * @param    string        $x 
  5082.      * @return  string 
  5083.      */
  5084.     public static function BINTOHEX($x{
  5085.         $x    floor(self::flattenSingleValue($x));
  5086.  
  5087.         if (is_bool($x)) {
  5088.             if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE{
  5089.                 $x = (int) $x;
  5090.             else {
  5091.                 return self::$_errorCodes['value'];
  5092.             }
  5093.         }
  5094.         if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC{
  5095.             $x floor($x);
  5096.         }
  5097.         $x = (string) $x;
  5098.         if (strlen($xpreg_match_all('/[01]/',$x,$out)) {
  5099.             return self::$_errorCodes['num'];
  5100.         }
  5101.         if (strlen($x10{
  5102.             return self::$_errorCodes['num'];
  5103.         elseif (strlen($x== 10{
  5104.             //    Two's Complement
  5105.             return str_repeat('F',8).substr(strtoupper(dechex(bindec(substr($x,-9)))),-2);
  5106.         }
  5107.         return strtoupper(dechex(bindec($x)));
  5108.     }
  5109.  
  5110.     /**
  5111.      * BINTOOCT
  5112.      *
  5113.      * Return a binary value as Octal.
  5114.      *
  5115.      * @param    string        $x 
  5116.      * @return  string 
  5117.      */
  5118.     public static function BINTOOCT($x{
  5119.         $x    floor(self::flattenSingleValue($x));
  5120.  
  5121.         if (is_bool($x)) {
  5122.             if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE{
  5123.                 $x = (int) $x;
  5124.             else {
  5125.                 return self::$_errorCodes['value'];
  5126.             }
  5127.         }
  5128.         if (self::$compatibilityMode == self::COMPATIBILITY_GNUMERIC{
  5129.             $x floor($x);
  5130.         }
  5131.         $x = (string) $x;
  5132.         if (strlen($xpreg_match_all('/[01]/',$x,$out)) {
  5133.             return self::$_errorCodes['num'];
  5134.         }
  5135.         if (strlen($x10{
  5136.             return self::$_errorCodes['num'];
  5137.         elseif (strlen($x== 10{
  5138.             //    Two's Complement
  5139.             return str_repeat('7',7).substr(strtoupper(dechex(bindec(substr($x,-9)))),-3);
  5140.         }
  5141.         return decoct(bindec($x));
  5142.     }
  5143.  
  5144.     /**
  5145.      * DECTOBIN
  5146.      *
  5147.      * Return an octal value as binary.
  5148.      *
  5149.      * @param    string        $x 
  5150.      * @return  string 
  5151.      */
  5152.     public static function DECTOBIN($x{
  5153.         $x    self::flattenSingleValue($x);
  5154.  
  5155.         if (is_bool($x)) {
  5156.             if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE{
  5157.                 $x = (int) $x;
  5158.             else {
  5159.                 return self::$_errorCodes['value'];
  5160.             }
  5161.         }
  5162.         $x = (string) $x;
  5163.         if (strlen($xpreg_match_all('/[-0123456789.]/',$x,$out)) {
  5164.             return self::$_errorCodes['value'];
  5165.         }
  5166.         $x = (string) floor($x);
  5167.         $r decbin($x);
  5168.         if (strlen($r== 32{
  5169.             //    Two's Complement
  5170.             $r substr($r,-10);
  5171.         elseif (strlen($r11{
  5172.             return self::$_errorCodes['num'];
  5173.         }
  5174.         return $r;
  5175.     }
  5176.  
  5177.     /**
  5178.      * DECTOOCT
  5179.      *
  5180.      * Return an octal value as binary.
  5181.      *
  5182.      * @param    string        $x 
  5183.      * @return  string 
  5184.      */
  5185.     public static function DECTOOCT($x{
  5186.         $x    self::flattenSingleValue($x);
  5187.  
  5188.         if (is_bool($x)) {
  5189.             if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE{
  5190.                 $x = (int) $x;
  5191.             else {
  5192.                 return self::$_errorCodes['value'];
  5193.             }
  5194.         }
  5195.         $x = (string) $x;
  5196.         if (strlen($xpreg_match_all('/[-0123456789.]/',$x,$out)) {
  5197.             return self::$_errorCodes['value'];
  5198.         }
  5199.         $x = (string) floor($x);
  5200.         $r decoct($x);
  5201.         if (strlen($r== 11{
  5202.             //    Two's Complement
  5203.             $r substr($r,-10);
  5204.         }
  5205.         return ($r);
  5206.     }
  5207.  
  5208.     /**
  5209.      * DECTOHEX
  5210.      *
  5211.      * Return an octal value as binary.
  5212.      *
  5213.      * @param    string        $x 
  5214.      * @return  string 
  5215.      */
  5216.     public static function DECTOHEX($x{
  5217.         $x    self::flattenSingleValue($x);
  5218.  
  5219.         if (is_bool($x)) {
  5220.             if (self::$compatibilityMode == self::COMPATIBILITY_OPENOFFICE{
  5221.                 $x = (int) $x;
  5222.             else {
  5223.                 return self::$_errorCodes['value'];
  5224.             }
  5225.         }
  5226.         $x = (string) $x;
  5227.         if (strlen($xpreg_match_all('/[-0123456789.]/',$x,$out)) {
  5228.             return self::$_errorCodes['value'];
  5229.         }
  5230.         $x = (string) floor($x);
  5231.         $r strtoupper(dechex($x));
  5232.         if (strlen($r== 8{
  5233.             //    Two's Complement
  5234.             $r 'FF'.$r;
  5235.         }
  5236.         return ($r);
  5237.     }
  5238.  
  5239.     /**
  5240.      * HEXTOBIN
  5241.      *
  5242.      * Return a hex value as binary.
  5243.      *
  5244.      * @param    string        $x 
  5245.      * @return  string 
  5246.      */
  5247.     public static function HEXTOBIN($x{
  5248.         $x    self::flattenSingleValue($x);
  5249.  
  5250.         if (is_bool($x)) {
  5251.             return self::$_errorCodes['value'];
  5252.         }
  5253.         $x = (string) $x;
  5254.         if (strlen($xpreg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
  5255.             return self::$_errorCodes['num'];
  5256.         }
  5257.         return decbin(hexdec($x));
  5258.     }
  5259.  
  5260.     /**
  5261.      * HEXTOOCT
  5262.      *
  5263.      * Return a hex value as octal.
  5264.      *
  5265.      * @param    string        $x 
  5266.      * @return  string 
  5267.      */
  5268.     public static function HEXTOOCT($x{
  5269.         $x    self::flattenSingleValue($x);
  5270.  
  5271.         if (is_bool($x)) {
  5272.             return self::$_errorCodes['value'];
  5273.         }
  5274.         $x = (string) $x;
  5275.         if (strlen($xpreg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
  5276.             return self::$_errorCodes['num'];
  5277.         }
  5278.         return decoct(hexdec($x));
  5279.     }
  5280.  
  5281.     /**
  5282.      * HEXTODEC
  5283.      *
  5284.      * Return a hex value as octal.
  5285.      *
  5286.      * @param    string        $x 
  5287.      * @return  string 
  5288.      */
  5289.     public static function HEXTODEC($x{
  5290.         $x    self::flattenSingleValue($x);
  5291.  
  5292.         if (is_bool($x)) {
  5293.             return self::$_errorCodes['value'];
  5294.         }
  5295.         $x = (string) $x;
  5296.         if (strlen($xpreg_match_all('/[0123456789ABCDEF]/',strtoupper($x),$out)) {
  5297.             return self::$_errorCodes['num'];
  5298.         }
  5299.         return hexdec($x);
  5300.     }
  5301.  
  5302.     /**
  5303.      * OCTTOBIN
  5304.      *
  5305.      * Return an octal value as binary.
  5306.      *
  5307.      * @param    string        $x 
  5308.      * @return  string 
  5309.      */
  5310.     public static function OCTTOBIN($x{
  5311.         $x    self::flattenSingleValue($x);
  5312.  
  5313.         if (is_bool($x)) {
  5314.             return self::$_errorCodes['value'];
  5315.         }
  5316.         $x = (string) $x;
  5317.         if (preg_match_all('/[01234567]/',$x,$out!= strlen($x)) {
  5318.             return self::$_errorCodes['num'];
  5319.         }
  5320.         return decbin(octdec($x));
  5321.     }
  5322.  
  5323.     /**
  5324.      * OCTTODEC
  5325.      *
  5326.      * Return an octal value as binary.
  5327.      *
  5328.      * @param    string        $x 
  5329.      * @return  string 
  5330.      */
  5331.     public static function OCTTODEC($x{
  5332.         $x    self::flattenSingleValue($x);
  5333.  
  5334.         if (is_bool($x)) {
  5335.             return self::$_errorCodes['value'];
  5336.         }
  5337.         $x = (string) $x;
  5338.         if (preg_match_all('/[01234567]/',$x,$out!= strlen($x)) {
  5339.             return self::$_errorCodes['num'];
  5340.         }
  5341.         return octdec($x);
  5342.     }
  5343.  
  5344.     /**
  5345.      * OCTTOHEX
  5346.      *
  5347.      * Return an octal value as hex.
  5348.      *
  5349.      * @param    string        $x 
  5350.      * @return  string 
  5351.      */
  5352.     public static function OCTTOHEX($x{
  5353.         $x    self::flattenSingleValue($x);
  5354.  
  5355.         if (is_bool($x)) {
  5356.             return self::$_errorCodes['value'];
  5357.         }
  5358.         $x = (string) $x;
  5359.         if (preg_match_all('/[01234567]/',$x,$out!= strlen($x)) {
  5360.             return self::$_errorCodes['num'];
  5361.         }
  5362.         return strtoupper(dechex(octdec($x)));
  5363.     }
  5364.  
  5365.     public function parseComplex($complexNumber{
  5366.         $workString $complexNumber;
  5367.  
  5368.         $realNumber $imaginary 0;
  5369.         //    Extract the suffix, if there is one
  5370.         $suffix substr($workString,-1);
  5371.         if (!is_numeric($suffix)) {
  5372.             $workString substr($workString,0,-1);
  5373.         else {
  5374.             $suffix '';
  5375.         }
  5376.  
  5377.         //    Split the input into its Real and Imaginary components
  5378.         $leadingSign (($workString{0== '+'|| ($workString{0== '-')) 0;
  5379.         $power '';
  5380.         $realNumber strtok($workString'+-');
  5381.         if (strtoupper(substr($realNumber,-1)) == 'E'{
  5382.             $power strtok('+-');
  5383.             ++$leadingSign;
  5384.         }
  5385.         $realNumber substr($workString,0,strlen($realNumber)+strlen($power)+$leadingSign);
  5386.  
  5387.         if ($suffix != ''{
  5388.             $imaginary substr($workString,strlen($realNumber));
  5389.  
  5390.             if (($imaginary == ''&& (($realNumber == ''|| ($realNumber == '+'|| ($realNumber == '-'))) {
  5391.                 $imaginary $realNumber.'1';
  5392.                 $realNumber '0';
  5393.             else if ($imaginary == ''{
  5394.                 $imaginary $realNumber;
  5395.                 $realNumber '0';
  5396.             elseif (($imaginary == '+'|| ($imaginary == '-')) {
  5397.                 $imaginary .= '1';
  5398.             }
  5399.         }
  5400.  
  5401.         $complexArray array'real'        => $realNumber,
  5402.                                'imaginary'    => $imaginary,
  5403.                                'suffix'        => $suffix
  5404.                              );
  5405.  
  5406.         return $complexArray;
  5407.     }
  5408.  
  5409.     /**
  5410.      * COMPLEX
  5411.      *
  5412.      * returns a complex number of the form x + yi or x + yj.
  5413.      *
  5414.      * @param    float        $realNumber 
  5415.      * @param    float        $imaginary 
  5416.      * @param    string        $suffix 
  5417.      * @return  string 
  5418.      */
  5419.     public static function COMPLEX($realNumber=0.0$imaginary=0.0$suffix='i'{
  5420.         $realNumber    self::flattenSingleValue($realNumber);
  5421.         $imaginary    self::flattenSingleValue($imaginary);
  5422.         $suffix        self::flattenSingleValue($suffix);
  5423.  
  5424.         if (((is_numeric($realNumber)) && (is_numeric($imaginary))) &&
  5425.             (($suffix == 'i'|| ($suffix == 'j'))) {
  5426.             if ($realNumber == 0.0{
  5427.                 if ($imaginary == 0.0{
  5428.                     return (string) '0';
  5429.                 elseif ($imaginary == 1.0{
  5430.                     return (string) $suffix;
  5431.                 elseif ($imaginary == -1.0{
  5432.                     return (string) '-'.$suffix;
  5433.                 }
  5434.                 return (string) $imaginary.$suffix;
  5435.             elseif ($imaginary == 0.0{
  5436.                 return (string) $realNumber;
  5437.             elseif ($imaginary == 1.0{
  5438.                 return (string) $realNumber.'+'.$suffix;
  5439.             elseif ($imaginary == -1.0{
  5440.                 return (string) $realNumber.'-'.$suffix;
  5441.             }
  5442.             if ($imaginary 0$imaginary = (string) '+'.$imaginary}
  5443.             return (string) $realNumber.$imaginary.$suffix;
  5444.         }
  5445.         return self::$_errorCodes['value'];
  5446.     }
  5447.  
  5448.     /**
  5449.      * IMAGINARY
  5450.      *
  5451.      * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
  5452.      *
  5453.      * @param    string        $complexNumber 
  5454.      * @return  real 
  5455.      */
  5456.     public static function IMAGINARY($complexNumber{
  5457.         $complexNumber    self::flattenSingleValue($complexNumber);
  5458.  
  5459.         $parsedComplex self::parseComplex($complexNumber);
  5460.         if (!is_array($parsedComplex)) {
  5461.             return $parsedComplex;
  5462.         }
  5463.         return $parsedComplex['imaginary'];
  5464.     }
  5465.  
  5466.     /**
  5467.      * IMREAL
  5468.      *
  5469.      * Returns the real coefficient of a complex number in x + yi or x + yj text format.
  5470.      *
  5471.      * @param    string        $complexNumber 
  5472.      * @return  real 
  5473.      */
  5474.     public static function IMREAL($complexNumber{
  5475.         $complexNumber    self::flattenSingleValue($complexNumber);
  5476.  
  5477.         $parsedComplex self::parseComplex($complexNumber);
  5478.         if (!is_array($parsedComplex)) {
  5479.             return $parsedComplex;
  5480.         }
  5481.         return $parsedComplex['real'];
  5482.     }
  5483.  
  5484.     /**
  5485.      * IMABS
  5486.      *
  5487.      * Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.
  5488.      *
  5489.      * @param    string        $complexNumber 
  5490.      * @return  real 
  5491.      */
  5492.     public static function IMABS($complexNumber{
  5493.         $complexNumber    self::flattenSingleValue($complexNumber);
  5494.  
  5495.         $parsedComplex self::parseComplex($complexNumber);
  5496.         if (!is_array($parsedComplex)) {
  5497.             return $parsedComplex;
  5498.         }
  5499.         return sqrt(($parsedComplex['real'$parsedComplex['real']($parsedComplex['imaginary'$parsedComplex['imaginary']));
  5500.     }
  5501.  
  5502.     /**
  5503.      * IMARGUMENT
  5504.      *
  5505.      * 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.
  5506.      *
  5507.      * @param    string        $complexNumber 
  5508.      * @return  string 
  5509.      */
  5510.     public static function IMARGUMENT($complexNumber{
  5511.         $complexNumber    self::flattenSingleValue($complexNumber);
  5512.  
  5513.         $parsedComplex self::parseComplex($complexNumber);
  5514.         if (!is_array($parsedComplex)) {
  5515.             return $parsedComplex;
  5516.         }
  5517.  
  5518.         if ($parsedComplex['real'== 0.0{
  5519.             if ($parsedComplex['imaginary'== 0.0{
  5520.                 return 0.0;
  5521.             elseif($parsedComplex['imaginary'0.0{
  5522.                 return pi(/ -2;
  5523.             else {
  5524.                 return pi(2;
  5525.             }
  5526.         elseif ($parsedComplex['real'0.0{
  5527.             return atan($parsedComplex['imaginary'$parsedComplex['real']);
  5528.         elseif ($parsedComplex['imaginary'0.0{
  5529.             return (pi(atan(abs($parsedComplex['imaginary']abs($parsedComplex['real'])));
  5530.         else {
  5531.             return pi(atan($parsedComplex['imaginary'abs($parsedComplex['real']));
  5532.         }
  5533.     }
  5534.  
  5535.     /**
  5536.      * IMCONJUGATE
  5537.      *
  5538.      * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
  5539.      *
  5540.      * @param    string        $complexNumber 
  5541.      * @return  string 
  5542.      */
  5543.     public static function IMCONJUGATE($complexNumber{
  5544.         $complexNumber    self::flattenSingleValue($complexNumber);
  5545.  
  5546.         $parsedComplex self::parseComplex($complexNumber);
  5547.  
  5548.         if (!is_array($parsedComplex)) {
  5549.             return $parsedComplex;
  5550.         }
  5551.  
  5552.         if ($parsedComplex['imaginary'== 0.0{
  5553.             return $parsedComplex['real'];
  5554.         else {
  5555.             return self::COMPLEX($parsedComplex['real']$parsedComplex['imaginary']$parsedComplex['suffix']);
  5556.         }
  5557.     }
  5558.  
  5559.     /**
  5560.      * IMCOS
  5561.      *
  5562.      * Returns the cosine of a complex number in x + yi or x + yj text format.
  5563.      *
  5564.      * @param    string        $complexNumber 
  5565.      * @return  string 
  5566.      */
  5567.     public static function IMCOS($complexNumber{
  5568.         $complexNumber    self::flattenSingleValue($complexNumber);
  5569.  
  5570.         $parsedComplex self::parseComplex($complexNumber);
  5571.         if (!is_array($parsedComplex)) {
  5572.             return $parsedComplex;
  5573.         }
  5574.  
  5575.         if ($parsedComplex['imaginary'== 0.0{
  5576.             return cos($parsedComplex['real']);
  5577.         else {
  5578.             return self::IMCONJUGATE(self::COMPLEX(cos($parsedComplex['real']cosh($parsedComplex['imaginary']),sin($parsedComplex['real']sinh($parsedComplex['imaginary']),$parsedComplex['suffix']));
  5579.         }
  5580.     }
  5581.  
  5582.     /**
  5583.      * IMSIN
  5584.      *
  5585.      * Returns the sine of a complex number in x + yi or x + yj text format.
  5586.      *
  5587.      * @param    string        $complexNumber 
  5588.      * @return  string 
  5589.      */
  5590.     public static function IMSIN($complexNumber{
  5591.         $complexNumber    self::flattenSingleValue($complexNumber);
  5592.  
  5593.         $parsedComplex self::parseComplex($complexNumber);
  5594.         if (!is_array($parsedComplex)) {
  5595.             return $parsedComplex;
  5596.         }
  5597.  
  5598.         if ($parsedComplex['imaginary'== 0.0{
  5599.             return sin($parsedComplex['real']);
  5600.         else {
  5601.             return self::COMPLEX(sin($parsedComplex['real']cosh($parsedComplex['imaginary']),cos($parsedComplex['real']sinh($parsedComplex['imaginary']),$parsedComplex['suffix']);
  5602.         }
  5603.     }
  5604.  
  5605.     /**
  5606.      * IMSQRT
  5607.      *
  5608.      * Returns the square root of a complex number in x + yi or x + yj text format.
  5609.      *
  5610.      * @param    string        $complexNumber 
  5611.      * @return  string 
  5612.      */
  5613.     public static function IMSQRT($complexNumber{
  5614.         $complexNumber    self::flattenSingleValue($complexNumber);
  5615.  
  5616.         $parsedComplex self::parseComplex($complexNumber);
  5617.         if (!is_array($parsedComplex)) {
  5618.             return $parsedComplex;
  5619.         }
  5620.  
  5621.         $theta self::IMARGUMENT($complexNumber);
  5622.         $d1 cos($theta 2);
  5623.         $d2 sin($theta 2);
  5624.         $r sqrt(sqrt(($parsedComplex['real'$parsedComplex['real']($parsedComplex['imaginary'$parsedComplex['imaginary'])));
  5625.  
  5626.         if ($parsedComplex['suffix'== ''{
  5627.             return self::COMPLEX($d1 $r,$d2 $r);
  5628.         else {
  5629.             return self::COMPLEX($d1 $r,$d2 $r,$parsedComplex['suffix']);
  5630.         }
  5631.     }
  5632.  
  5633.     /**
  5634.      * IMLN
  5635.      *
  5636.      * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
  5637.      *
  5638.      * @param    string        $complexNumber 
  5639.      * @return  string 
  5640.      */
  5641.     public static function IMLN($complexNumber{
  5642.         $complexNumber    self::flattenSingleValue($complexNumber);
  5643.  
  5644.         $parsedComplex self::parseComplex($complexNumber);
  5645.         if (!is_array($parsedComplex)) {
  5646.             return $parsedComplex;
  5647.         }
  5648.  
  5649.         if (($parsedComplex['real'== 0.0&& ($parsedComplex['imaginary'== 0.0)) {
  5650.             return self::$_errorCodes['num'];
  5651.         }
  5652.  
  5653.         $logR log(sqrt(($parsedComplex['real'$parsedComplex['real']($parsedComplex['imaginary'$parsedComplex['imaginary'])));
  5654.         $t self::IMARGUMENT($complexNumber);
  5655.  
  5656.         if ($parsedComplex['suffix'== ''{
  5657.             return self::COMPLEX($logR,$t);
  5658.         else {
  5659.             return self::COMPLEX($logR,$t,$parsedComplex['suffix']);
  5660.         }
  5661.     }
  5662.  
  5663.     /**
  5664.      * IMLOG10
  5665.      *
  5666.      * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
  5667.      *
  5668.      * @param    string        $complexNumber 
  5669.      * @return  string 
  5670.      */
  5671.     public static function IMLOG10($complexNumber{
  5672.         $complexNumber    self::flattenSingleValue($complexNumber);
  5673.  
  5674.         $parsedComplex self::parseComplex($complexNumber);
  5675.         if (!is_array($parsedComplex)) {
  5676.             return $parsedComplex;
  5677.         }
  5678.  
  5679.         if (($parsedComplex['real'== 0.0&& ($parsedComplex['imaginary'== 0.0)) {
  5680.             return self::$_errorCodes['num'];
  5681.         elseif (($parsedComplex['real'0.0&& ($parsedComplex['imaginary'== 0.0)) {
  5682.             return log10($parsedComplex['real']);
  5683.         }
  5684.  
  5685.         return self::IMPRODUCT(log10(EULER),self::IMLN($complexNumber));
  5686.     }
  5687.  
  5688.     /**
  5689.      * IMLOG2
  5690.      *
  5691.      * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
  5692.      *
  5693.      * @param    string        $complexNumber 
  5694.      * @return  string 
  5695.      */
  5696.     public static function IMLOG2($complexNumber{
  5697.         $complexNumber    self::flattenSingleValue($complexNumber);
  5698.  
  5699.         $parsedComplex self::parseComplex($complexNumber);
  5700.         if (!is_array($parsedComplex)) {
  5701.             return $parsedComplex;
  5702.         }
  5703.  
  5704.         if (($parsedComplex['real'== 0.0&& ($parsedComplex['imaginary'== 0.0)) {
  5705.             return self::$_errorCodes['num'];
  5706.         elseif (($parsedComplex['real'0.0&& ($parsedComplex['imaginary'== 0.0)) {
  5707.             return log($parsedComplex['real'],2);
  5708.         }
  5709.  
  5710.         return self::IMPRODUCT(log(EULER,2),self::IMLN($complexNumber));
  5711.     }
  5712.  
  5713.     /**
  5714.      * IMEXP
  5715.      *
  5716.      * Returns the exponential of a complex number in x + yi or x + yj text format.
  5717.      *
  5718.      * @param    string        $complexNumber 
  5719.      * @return  string 
  5720.      */
  5721.     public static function IMEXP($complexNumber{
  5722.         $complexNumber    self::flattenSingleValue($complexNumber);
  5723.  
  5724.         $parsedComplex self::parseComplex($complexNumber);
  5725.         if (!is_array($parsedComplex)) {
  5726.             return $parsedComplex;
  5727.         }
  5728.  
  5729.         if (($parsedComplex['real'== 0.0&& ($parsedComplex['imaginary'== 0.0)) {
  5730.             return '1';
  5731.         }
  5732.  
  5733.         $e exp($parsedComplex['real']);
  5734.         $eX $e cos($parsedComplex['imaginary']);
  5735.         $eY $e sin($parsedComplex['imaginary']);
  5736.  
  5737.         if ($parsedComplex['suffix'== ''{
  5738.             return self::COMPLEX($eX,$eY);
  5739.         else {
  5740.             return self::COMPLEX($eX,$eY,$parsedComplex['suffix']);
  5741.         }
  5742.     }
  5743.  
  5744.     /**
  5745.      * IMPOWER
  5746.      *
  5747.      * Returns a complex number in x + yi or x + yj text format raised to a power.
  5748.      *
  5749.      * @param    string        $complexNumber 
  5750.      * @return  string 
  5751.      */
  5752.     public static function IMPOWER($complexNumber,$realNumber{
  5753.         $complexNumber    self::flattenSingleValue($complexNumber);
  5754.         $realNumber        self::flattenSingleValue($realNumber);
  5755.  
  5756.         if (!is_numeric($realNumber)) {
  5757.             return self::$_errorCodes['value'];
  5758.         }
  5759.  
  5760.         $parsedComplex self::parseComplex($complexNumber);
  5761.         if (!is_array($parsedComplex)) {
  5762.             return $parsedComplex;
  5763.         }
  5764.  
  5765.         $r sqrt(($parsedComplex['real'$parsedComplex['real']($parsedComplex['imaginary'$parsedComplex['imaginary']));
  5766.         $rPower pow($r,$realNumber);
  5767.         $theta self::IMARGUMENT($complexNumber$realNumber;
  5768.         if ($parsedComplex['imaginary'== 0.0{
  5769.             return self::COMPLEX($rPower cos($theta),$rPower sin($theta),$parsedComplex['suffix']);
  5770.         else {
  5771.             return self::COMPLEX($rPower cos($theta),$rPower sin($theta),$parsedComplex['suffix']);
  5772.         }
  5773.     }
  5774.  
  5775.     /**
  5776.      * IMDIV
  5777.      *
  5778.      * Returns the quotient of two complex numbers in x + yi or x + yj text format.
  5779.      *
  5780.      * @param    string        $complexDividend 
  5781.      * @param    string        $complexDivisor 
  5782.      * @return  real 
  5783.      */
  5784.     public static function IMDIV($complexDividend,$complexDivisor{
  5785.         $complexDividend    self::flattenSingleValue($complexDividend);
  5786.         $complexDivisor    self::flattenSingleValue($complexDivisor);
  5787.  
  5788.         $parsedComplexDividend self::parseComplex($complexDividend);
  5789.         if (!is_array($parsedComplexDividend)) {
  5790.             return $parsedComplexDividend;
  5791.         }
  5792.  
  5793.         $parsedComplexDivisor self::parseComplex($complexDivisor);
  5794.         if (!is_array($parsedComplexDivisor)) {
  5795.             return $parsedComplexDividend;
  5796.         }
  5797.  
  5798.         if ($parsedComplexDividend['suffix'!= $parsedComplexDivisor['suffix']{
  5799.             return self::$_errorCodes['num'];
  5800.         }
  5801.  
  5802.         $d1 ($parsedComplexDividend['real'$parsedComplexDivisor['real']($parsedComplexDividend['imaginary'$parsedComplexDivisor['imaginary']);
  5803.         $d2 ($parsedComplexDividend['imaginary'$parsedComplexDivisor['real']($parsedComplexDividend['real'$parsedComplexDivisor['imaginary']);
  5804.         $d3 ($parsedComplexDivisor['real'$parsedComplexDivisor['real']($parsedComplexDivisor['imaginary'$parsedComplexDivisor['imaginary']);
  5805.  
  5806.         return $d1/$d3.$d2/$d3.$parsedComplexDivisor['suffix'];
  5807.     }
  5808.  
  5809.     /**
  5810.      * IMSUB
  5811.      *
  5812.      * Returns the difference of two complex numbers in x + yi or x + yj text format.
  5813.      *
  5814.      * @param    string        $complexNumber1 
  5815.      * @param    string        $complexNumber2 
  5816.      * @return  real 
  5817.      */
  5818.     public static function IMSUB($complexNumber1,$complexNumber2{
  5819.         $complexNumber1    self::flattenSingleValue($complexNumber1);
  5820.         $complexNumber2    self::flattenSingleValue($complexNumber2);
  5821.  
  5822.         $parsedComplex1 self::parseComplex($complexNumber1);
  5823.         if (!is_array($parsedComplex1)) {
  5824.             return $parsedComplex1;
  5825.         }
  5826.  
  5827.         $parsedComplex2 self::parseComplex($complexNumber2);
  5828.         if (!is_array($parsedComplex2)) {
  5829.             return $parsedComplex2;
  5830.         }
  5831.  
  5832.         if ($parsedComplex1['suffix'!= $parsedComplex2['suffix']{
  5833.             return self::$_errorCodes['num'];
  5834.         }
  5835.  
  5836.         $d1 $parsedComplex1['real'$parsedComplex2['real'];
  5837.         $d2 $parsedComplex1['imaginary'$parsedComplex2['imaginary'];
  5838.  
  5839.         return self::COMPLEX($d1,$d2,$parsedComplex1['suffix']);
  5840.     }
  5841.  
  5842.     /**
  5843.      * IMSUM
  5844.      *
  5845.      * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
  5846.      *
  5847.      * @param    array of mixed        Data Series
  5848.      * @return  real 
  5849.      */
  5850.     public static function IMSUM({
  5851.         // Return value
  5852.         $returnValue self::parseComplex('0');
  5853.         $activeSuffix '';
  5854.  
  5855.         // Loop through the arguments
  5856.         $aArgs self::flattenArray(func_get_args());
  5857.         foreach ($aArgs as $arg{
  5858.             $parsedComplex self::parseComplex($arg);
  5859.             if (!is_array($parsedComplex)) {
  5860.                 return $parsedComplex;
  5861.             }
  5862.  
  5863.             if ($activeSuffix == ''{
  5864.                 $activeSuffix $parsedComplex['suffix'];
  5865.             elseif ($activeSuffix != $parsedComplex['suffix']{
  5866.                 return self::$_errorCodes['num'];
  5867.             }
  5868.  
  5869.             $returnValue['real'+= $parsedComplex['real'];
  5870.             $returnValue['imaginary'+= $parsedComplex['imaginary'];
  5871.         }
  5872.  
  5873.         if ($returnValue['imaginary'== 0.0$activeSuffix ''}
  5874.         return self::COMPLEX($returnValue['real'],$returnValue['imaginary'],$activeSuffix);
  5875.     }
  5876.  
  5877.     /**
  5878.      * IMPRODUCT
  5879.      *
  5880.      * Returns the product of two or more complex numbers in x + yi or x + yj text format.
  5881.      *
  5882.      * @param    array of mixed        Data Series
  5883.      * @return  real 
  5884.      */
  5885.     public static function IMPRODUCT({
  5886.         // Return value
  5887.         $returnValue self::parseComplex('1');
  5888.         $activeSuffix '';
  5889.  
  5890.         // Loop through the arguments
  5891.         $aArgs self::flattenArray(func_get_args());
  5892.         foreach ($aArgs as $arg{
  5893.             $parsedComplex self::parseComplex($arg);
  5894.             if (!is_array($parsedComplex)) {
  5895.                 return $parsedComplex;
  5896.             }
  5897.             $workValue $returnValue;
  5898.             if (($parsedComplex['suffix'!= ''&& ($activeSuffix == '')) {
  5899.                 $activeSuffix $parsedComplex['suffix'];
  5900.             elseif (($parsedComplex['suffix'!= ''&& ($activeSuffix != $parsedComplex['suffix'])) {
  5901.                 return self::$_errorCodes['num'];
  5902.             }
  5903.             $returnValue['real'($workValue['real'$parsedComplex['real']($workValue['imaginary'$parsedComplex['imaginary']);
  5904.             $returnValue['imaginary'($workValue['real'$parsedComplex['imaginary']($workValue['imaginary'$parsedComplex['real']);
  5905.         }
  5906.  
  5907.         if ($returnValue['imaginary'== 0.0$activeSuffix ''}
  5908.         return self::COMPLEX($returnValue['real'],$returnValue['imaginary'],$activeSuffix);
  5909.     }
  5910.  
  5911.     /**
  5912.      * BESSELI
  5913.      *
  5914.      * Returns the modified Bessel function, which is equivalent to the Bessel function evaluated for purely imaginary arguments
  5915.      *
  5916.      * @param    float        $x 
  5917.      * @param    float        $n 
  5918.      * @return  int 
  5919.      */
  5920.     public static function BESSELI($x$n{
  5921.         $x    self::flattenSingleValue($x);
  5922.         $n    floor(self::flattenSingleValue($n));
  5923.  
  5924.         if ((is_numeric($x)) && (is_numeric($n))) {
  5925.             if ($n 0{
  5926.                 return self::$_errorCodes['num'];
  5927.             }
  5928.             $f_2_PI pi();
  5929.  
  5930.             if (abs($x<= 30{
  5931.                 $fTerm pow($x 2$nself::FACT($n);
  5932.                 $nK 1;
  5933.                 $fResult $fTerm;
  5934.                 $fSqrX pow($x,24;
  5935.                 do {
  5936.                     $fTerm *= $fSqrX;
  5937.                     $fTerm /= ($nK ($nK $n));
  5938.                     $fResult += $fTerm;
  5939.                 while ((abs($fTerm1e-10&& (++$nK 100));
  5940.             else {
  5941.                 $fXAbs abs($x);
  5942.                 $fResult exp($fXAbssqrt($f_2_PI $fXAbs);
  5943.                 if (($n && 1&& ($x 0)) {
  5944.                     $fResult = -$fResult;
  5945.                 }
  5946.             }
  5947.             return $fResult;
  5948.         }
  5949.         return self::$_errorCodes['value'];
  5950.     }
  5951.  
  5952.     /**
  5953.      * BESSELJ
  5954.      *
  5955.      * Returns the Bessel function
  5956.      *
  5957.      * @param    float        $x 
  5958.      * @param    float        $n 
  5959.      * @return  int 
  5960.      */
  5961.     public static function BESSELJ($x$n{
  5962.         $x    self::flattenSingleValue($x);
  5963.         $n    floor(self::flattenSingleValue($n));
  5964.  
  5965.         if ((is_numeric($x)) && (is_numeric($n))) {
  5966.             if ($n 0{
  5967.                 return self::$_errorCodes['num'];
  5968.             }
  5969.             $f_2_DIV_PI pi();
  5970.             $f_PI_DIV_2 pi(2;
  5971.             $f_PI_DIV_4 pi(4;
  5972.  
  5973.             $fResult 0;
  5974.             if (abs($x<= 30{
  5975.                 $fTerm pow($x 2$nself::FACT($n);
  5976.                 $nK 1;
  5977.                 $fResult $fTerm;
  5978.                 $fSqrX pow($x,2/ -4;
  5979.                 do {
  5980.                     $fTerm *= $fSqrX;
  5981.                     $fTerm /= ($nK ($nK $n));
  5982.                     $fResult += $fTerm;
  5983.                 while ((abs($fTerm1e-10&& (++$nK 100));
  5984.             else {
  5985.                 $fXAbs abs($x);
  5986.                 $fResult sqrt($f_2_DIV_PI $fXAbscos($fXAbs $n $f_PI_DIV_2 $f_PI_DIV_4);
  5987.                 if (($n && 1&& ($x 0)) {
  5988.                     $fResult = -$fResult;
  5989.                 }
  5990.             }
  5991.             return $fResult;
  5992.         }
  5993.         return self::$_errorCodes['value'];
  5994.     }
  5995.  
  5996.     private static function Besselk0($fNum{
  5997.         if ($fNum <= 2{
  5998.             $fNum2 $fNum 0.5;
  5999.             $y pow($fNum2,2);
  6000.             $fRet = -log($fNum2self::BESSELI($fNum0+
  6001.                     (-0.57721566 $y (0.42278420 $y (0.23069756 $y (0.3488590e-1 $y (0.262698e-2 $y *
  6002.                     (0.10750e-3 $y 0.74e-5))))));
  6003.         else {
  6004.             $y $fNum;
  6005.             $fRet exp(-$fNumsqrt($fNum*
  6006.                     (1.25331414 $y (-0.7832358e-1 $y (0.2189568e-1 $y (-0.1062446e-1 $y *
  6007.                     (0.587872e-2 $y (-0.251540e-2 $y 0.53208e-3))))));
  6008.         }
  6009.         return $fRet;
  6010.     }
  6011.  
  6012.     private static function Besselk1($fNum{
  6013.         if ($fNum <= 2{
  6014.             $fNum2 $fNum 0.5;
  6015.             $y pow($fNum2,2);
  6016.             $fRet log($fNum2self::BESSELI($fNum1+
  6017.                     ($y (0.15443144 $y (-0.67278579 $y (-0.18156897 $y (-0.1919402e-1 $y *
  6018.                     (-0.110404e-2 $y (-0.4686e-4))))))) $fNum;
  6019.         else {
  6020.             $y $fNum;
  6021.             $fRet exp(-$fNumsqrt($fNum*
  6022.                     (1.25331414 $y (0.23498619 $y (-0.3655620e-1 $y (0.1504268e-1 $y (-0.780353e-2 $y *
  6023.                     (0.325614e-2 $y (-0.68245e-3)))))));
  6024.         }
  6025.         return $fRet;
  6026.     }
  6027.  
  6028.     /**
  6029.      * BESSELK
  6030.      *
  6031.      * Returns the modified Bessel function, which is equivalent to the Bessel functions evaluated for purely imaginary arguments.
  6032.      *
  6033.      * @param    float        $x 
  6034.      * @param    float        $n 
  6035.      * @return  int 
  6036.      */
  6037.     public static function BESSELK($x$ord{
  6038.         $x    self::flattenSingleValue($x);
  6039.         $n    floor(self::flattenSingleValue($ord));
  6040.  
  6041.         if ((is_numeric($x)) && (is_numeric($ord))) {
  6042.             if ($ord 0{
  6043.                 return self::$_errorCodes['num'];
  6044.             }
  6045.  
  6046.             switch($ord{
  6047.                 case :    return self::Besselk0($x);
  6048.                             break;
  6049.                 case :    return self::Besselk1($x);
  6050.                             break;
  6051.                 default :    $fTox    $x;
  6052.                             $fBkm    self::Besselk0($x);
  6053.                             $fBk    self::Besselk1($x);
  6054.                             for ($n 1$n $ord++$n{
  6055.                                 $fBkp    $fBkm $n $fTox $fBk;
  6056.                                 $fBkm    $fBk;
  6057.                                 $fBk    $fBkp;
  6058.                             }
  6059.             }
  6060.             return $fBk;
  6061.         }
  6062.         return self::$_errorCodes['value'];
  6063.     }
  6064.  
  6065.     private static function Bessely0($fNum{
  6066.         if ($fNum 8{
  6067.             $y pow($fNum,2);
  6068.             $f1 = -2957821389.0 $y (7062834065.0 $y (-512359803.6 $y (10879881.29 $y (-86327.92757 $y 228.4622733))));
  6069.             $f2 40076544269.0 $y (745249964.8 $y (7189466.438 $y (47447.26470 $y (226.1030244 $y))));
  6070.             $fRet $f1 $f2 0.636619772 self::BESSELJ($fNum0log($fNum);
  6071.         else {
  6072.             $z $fNum;
  6073.             $y pow($z,2);
  6074.             $xx $fNum 0.785398164;
  6075.             $f1 $y (-0.1098628627e-2 $y (0.2734510407e-4 $y (-0.2073370639e-5 $y 0.2093887211e-6)));
  6076.             $f2 = -0.1562499995e-1 $y (0.1430488765e-3 $y (-0.6911147651e-5 $y (0.7621095161e-6 $y (-0.934945152e-7))));
  6077.             $fRet sqrt(0.636619772 $fNum(sin($xx$f1 $z cos($xx$f2);
  6078.         }
  6079.         return $fRet;
  6080.     }
  6081.  
  6082.     private static function Bessely1($fNum{
  6083.         if ($fNum 8{
  6084.             $y pow($fNum,2);
  6085.             $f1 $fNum (-0.4900604943e13 $y (0.1275274390e13 $y (-0.5153438139e11 $y (0.7349264551e9 $y *
  6086.                 (-0.4237922726e7 $y 0.8511937935e4)))));
  6087.             $f2 0.2499580570e14 $y (0.4244419664e12 $y (0.3733650367e10 $y (0.2245904002e8 $y *
  6088.                 (0.1020426050e6 $y (0.3549632885e3 $y)))));
  6089.             $fRet $f1 $f2 0.636619772 self::BESSELJ($fNum1log($fNum$fNum);
  6090.         else {
  6091.             $z $fNum;
  6092.             $y $z $z;
  6093.             $xx $fNum 2.356194491;
  6094.             $f1 $y (0.183105e-2 $y (-0.3516396496e-4 $y (0.2457520174e-5 $y (-0.240337019e6))));
  6095.             $f2 0.04687499995 $y (-0.2002690873e-3 $y (0.8449199096e-5 $y (-0.88228987e-6 $y 0.105787412e-6)));
  6096.             $fRet sqrt(0.636619772 $fNum(sin($xx$f1 $z cos($xx$f2);
  6097.             #i12430# ...but this seems to work much better.
  6098. //            $fRet = sqrt(0.636619772 / $fNum) * sin($fNum - 2.356194491);
  6099.         }
  6100.         return $fRet;
  6101.     }
  6102.  
  6103.     /**
  6104.      * BESSELY
  6105.      *
  6106.      * Returns the Bessel function, which is also called the Weber function or the Neumann function.
  6107.      *
  6108.      * @param    float        $x 
  6109.      * @param    float        $n 
  6110.      * @return  int 
  6111.      */
  6112.     public static function BESSELY($x$ord{
  6113.         $x    self::flattenSingleValue($x);
  6114.         $n    floor(self::flattenSingleValue($ord));
  6115.  
  6116.         if ((is_numeric($x)) && (is_numeric($ord))) {
  6117.             if ($ord 0{
  6118.                 return self::$_errorCodes['num'];
  6119.             }
  6120.  
  6121.             switch($ord{
  6122.                 case :    return self::Bessely0($x);
  6123.                             break;
  6124.                 case :    return self::Bessely1($x);
  6125.                             break;
  6126.                 default:    $fTox    $x;
  6127.                             $fBym    self::Bessely0($x);
  6128.                             $fBy    self::Bessely1($x);
  6129.                             for ($n 1$n $ord++$n{
  6130.                                 $fByp    $n $fTox $fBy $fBym;
  6131.                                 $fBym    $fBy;
  6132.                                 $fBy    $fByp;
  6133.                             }
  6134.             }
  6135.             return $fBy;
  6136.         }
  6137.         return self::$_errorCodes['value'];
  6138.     }
  6139.  
  6140.     /**
  6141.      * DELTA
  6142.      *
  6143.      * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
  6144.      *
  6145.      * @param    float        $a 
  6146.      * @param    float        $b 
  6147.      * @return  int 
  6148.      */
  6149.     public static function DELTA($a$b=0{
  6150.         $a    self::flattenSingleValue($a);
  6151.         $b    self::flattenSingleValue($b);
  6152.  
  6153.         return (int) ($a == $b);
  6154.     }
  6155.  
  6156.     /**
  6157.      * GESTEP
  6158.      *
  6159.      * Returns 1 if number = step; returns 0 (zero) otherwise
  6160.      *
  6161.      * @param    float        $number 
  6162.      * @param    float        $step 
  6163.      * @return  int 
  6164.      */
  6165.     public static function GESTEP($number$step=0{
  6166.         $number    self::flattenSingleValue($number);
  6167.         $step    self::flattenSingleValue($step);
  6168.  
  6169.         return (int) ($number >= $step);
  6170.     }
  6171.  
  6172.     //
  6173.     //    Private method to calculate the erf value
  6174.     //
  6175.     private static $two_sqrtpi 1.128379167095512574;
  6176.     private static $rel_error 1E-15;
  6177.  
  6178.     private static function erfVal($x{
  6179.         if (abs($x2.2{
  6180.             return self::erfcVal($x);
  6181.         }
  6182.         $sum $term $x;
  6183.         $xsqr pow($x,2);
  6184.         $j 1;
  6185.         do {
  6186.             $term *= $xsqr $j;
  6187.             $sum -= $term ($j 1);
  6188.             ++$j;
  6189.             $term *= $xsqr $j;
  6190.             $sum += $term ($j 1);
  6191.             ++$j;
  6192.             if ($sum == 0{
  6193.                 break;
  6194.             }
  6195.         while (abs($term $sumself::$rel_error);
  6196.         return  self::$two_sqrtpi $sum;
  6197.     }
  6198.  
  6199.     /**
  6200.      * ERF
  6201.      *
  6202.      * Returns the error function integrated between lower_limit and upper_limit
  6203.      *
  6204.      * @param    float        $lower    lower bound for integrating ERF
  6205.      * @param    float        $upper    upper bound for integrating ERF.
  6206.      *                                 If omitted, ERF integrates between zero and lower_limit
  6207.      * @return  int 
  6208.      */
  6209.     public static function ERF($lower$upper 0{
  6210.         $lower    self::flattenSingleValue($lower);
  6211.         $upper    self::flattenSingleValue($upper);
  6212.  
  6213.         if ((is_numeric($lower)) && (is_numeric($upper))) {
  6214.             if (($lower 0|| ($upper 0)) {
  6215.                 return self::$_errorCodes['num'];
  6216.             }
  6217.             if ($upper $lower{
  6218.                 return self::erfVal($upperself::erfVal($lower);
  6219.             else {
  6220.                 return self::erfVal($lowerself::erfVal($upper);
  6221.             }
  6222.         }
  6223.         return self::$_errorCodes['value'];
  6224.     }
  6225.  
  6226.     //
  6227.     //    Private method to calculate the erfc value
  6228.     //
  6229.     private static $one_sqrtpi 0.564189583547756287;
  6230.  
  6231.     private static function erfcVal($x{
  6232.         if (abs($x2.2{
  6233.             return self::erfVal($x);
  6234.         }
  6235.         if ($x 0{
  6236.             return self::erfc(-$x);
  6237.         }
  6238.         $a $n 1;
  6239.         $b $c $x;
  6240.         $d pow($x,20.5;
  6241.         $q1 $q2 $b $d;
  6242.         $t 0;
  6243.         do {
  6244.             $t $a $n $b $x;
  6245.             $a $b;
  6246.             $b $t;
  6247.             $t $c $n $d $x;
  6248.             $c $d;
  6249.             $d $t;
  6250.             $n += 0.5;
  6251.             $q1 $q2;
  6252.             $q2 $b $d;
  6253.         while ((abs($q1 $q2$q2self::$rel_error);
  6254.         return self::$one_sqrtpi exp(-$x $x$q2;
  6255.     }
  6256.  
  6257.     /**
  6258.      * ERFC
  6259.      *
  6260.      * Returns the complementary ERF function integrated between x and infinity
  6261.      *
  6262.      * @param    float        $x        The lower bound for integrating ERF
  6263.      * @return  int 
  6264.      */
  6265.     public static function ERFC($x{
  6266.         $x    self::flattenSingleValue($x);
  6267.  
  6268.         if (is_numeric($x)) {
  6269.             if ($x 0{
  6270.                 return self::$_errorCodes['num'];
  6271.             }
  6272.             return self::erfcVal($x);
  6273.         }
  6274.         return self::$_errorCodes['value'];
  6275.     }
  6276.  
  6277.     /**
  6278.      * EFFECT
  6279.      *
  6280.      * Returns the effective interest rate given the nominal rate and the number of compounding payments per year.
  6281.      *
  6282.      * @param    float    $nominal_rate      Nominal interest rate
  6283.      * @param    int        $npery               Number of compounding payments per year
  6284.      * @return    float 
  6285.      */
  6286.     public static function EFFECT($nominal_rate 0$npery 0{
  6287.         $nominal_rate    self::flattenSingleValue($$nominal_rate);
  6288.         $npery            = (int)self::flattenSingleValue($npery);
  6289.  
  6290.         // Validate parameters
  6291.         if ($$nominal_rate <= || $npery 1{
  6292.             return self::$_errorCodes['num'];
  6293.         }
  6294.  
  6295.         return pow(($nominal_rate $npery)$npery1;
  6296.     }
  6297.  
  6298.     /**
  6299.      * NOMINAL
  6300.      *
  6301.      * Returns the nominal interest rate given the effective rate and the number of compounding payments per year.
  6302.      *
  6303.      * @param    float    $effect_rate    Effective interest rate
  6304.      * @param    int        $npery            Number of compounding payments per year
  6305.      * @return    float 
  6306.      */
  6307.     public static function NOMINAL($effect_rate 0$npery 0{
  6308.         $effect_rate    self::flattenSingleValue($effect_rate);
  6309.         $npery            = (int)self::flattenSingleValue($npery);
  6310.  
  6311.         // Validate parameters
  6312.         if ($effect_rate <= || $npery 1{
  6313.             return self::$_errorCodes['num'];
  6314.         }
  6315.  
  6316.         // Calculate
  6317.         return $npery (pow($effect_rate 1$npery1);
  6318.     }
  6319.  
  6320.     /**
  6321.      * PV
  6322.      *
  6323.      * Returns the Present Value of a cash flow with constant payments and interest rate (annuities).
  6324.      *
  6325.      * @param    float    $rate    Interest rate per period
  6326.      * @param    int        $nper    Number of periods
  6327.      * @param    float    $pmt    Periodic payment (annuity)
  6328.      * @param    float    $fv        Future Value
  6329.      * @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  6330.      * @return    float 
  6331.      */
  6332.     public static function PV($rate 0$nper 0$pmt 0$fv 0$type 0{
  6333.         $rate    self::flattenSingleValue($rate);
  6334.         $nper    self::flattenSingleValue($nper);
  6335.         $pmt    self::flattenSingleValue($pmt);
  6336.         $fv        self::flattenSingleValue($fv);
  6337.         $type    self::flattenSingleValue($type);
  6338.  
  6339.         // Validate parameters
  6340.         if ($type != && $type != 1{
  6341.             return self::$_errorCodes['num'];
  6342.         }
  6343.  
  6344.         // Calculate
  6345.         if (!is_null($rate&& $rate != 0{
  6346.             return (-$pmt ($rate $type((pow($rate$nper1$rate$fvpow($rate$nper);
  6347.         else {
  6348.             return -$fv $pmt $nper;
  6349.         }
  6350.     }
  6351.  
  6352.     /**
  6353.      * FV
  6354.      *
  6355.      * Returns the Future Value of a cash flow with constant payments and interest rate (annuities).
  6356.      *
  6357.      * @param    float    $rate    Interest rate per period
  6358.      * @param    int        $nper    Number of periods
  6359.      * @param    float    $pmt    Periodic payment (annuity)
  6360.      * @param    float    $pv        Present Value
  6361.      * @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  6362.      * @return    float 
  6363.      */
  6364.     public static function FV($rate 0$nper 0$pmt 0$pv 0$type 0{
  6365.         $rate    self::flattenSingleValue($rate);
  6366.         $nper    self::flattenSingleValue($nper);
  6367.         $pmt    self::flattenSingleValue($pmt);
  6368.         $pv        self::flattenSingleValue($pv);
  6369.         $type    self::flattenSingleValue($type);
  6370.  
  6371.         // Validate parameters
  6372.         if ($type != && $type != 1{
  6373.             return self::$_errorCodes['num'];
  6374.         }
  6375.  
  6376.         // Calculate
  6377.         if (!is_null($rate&& $rate != 0{
  6378.             return -$pv pow($rate$nper$pmt ($rate $type(pow($rate$nper1$rate;
  6379.         else {
  6380.             return -$pv $pmt $nper;
  6381.         }
  6382.     }
  6383.  
  6384.     /**
  6385.      * PMT
  6386.      *
  6387.      * Returns the constant payment (annuity) for a cash flow with a constant interest rate.
  6388.      *
  6389.      * @param    float    $rate    Interest rate per period
  6390.      * @param    int        $nper    Number of periods
  6391.      * @param    float    $pv        Present Value
  6392.      * @param    float    $fv        Future Value
  6393.      * @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  6394.      * @return    float 
  6395.      */
  6396.     public static function PMT($rate 0$nper 0$pv 0$fv 0$type 0{
  6397.         $rate    self::flattenSingleValue($rate);
  6398.         $nper    self::flattenSingleValue($nper);
  6399.         $pv        self::flattenSingleValue($pv);
  6400.         $fv        self::flattenSingleValue($fv);
  6401.         $type    self::flattenSingleValue($type);
  6402.  
  6403.         // Validate parameters
  6404.         if ($type != && $type != 1{
  6405.             return self::$_errorCodes['num'];
  6406.         }
  6407.  
  6408.         // Calculate
  6409.         if (!is_null($rate&& $rate != 0{
  6410.             return (-$fv $pv pow($rate$nper)) ($rate $type((pow($rate$nper1$rate);
  6411.         else {
  6412.             return (-$pv $fv$nper;
  6413.         }
  6414.     }
  6415.  
  6416.     /**
  6417.      * NPER
  6418.      *
  6419.      * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate.
  6420.      *
  6421.      * @param    float    $rate    Interest rate per period
  6422.      * @param    int        $pmt    Periodic payment (annuity)
  6423.      * @param    float    $pv        Present Value
  6424.      * @param    float    $fv        Future Value
  6425.      * @param    int        $type    Payment type: 0 = at the end of each period, 1 = at the beginning of each period
  6426.      * @return    float 
  6427.      */
  6428.     public static function NPER($rate 0$pmt 0$pv 0$fv 0$type 0{
  6429.         $rate    self::flattenSingleValue($rate);
  6430.         $pmt    self::flattenSingleValue($pmt);
  6431.         $pv        self::flattenSingleValue($pv);
  6432.         $fv        self::flattenSingleValue($fv);
  6433.         $type    self::flattenSingleValue($type);
  6434.  
  6435.         // Validate parameters
  6436.         if ($type != && $type != 1{
  6437.             return self::$_errorCodes['num'];
  6438.         }
  6439.  
  6440.         // Calculate
  6441.         if (!is_null($rate&& $rate != 0{
  6442.             if ($pmt == && $pv == 0{
  6443.                 return self::$_errorCodes['num'];
  6444.             }
  6445.             return log(($pmt ($rate $type$rate $fv($pv $pmt ($rate $type$rate)) log($rate);
  6446.         else {
  6447.             if ($pmt == 0{
  6448.                 return self::$_errorCodes['num'];
  6449.             }
  6450.             return (-$pv -$fv$pmt;
  6451.         }
  6452.     }
  6453.  
  6454.     /**
  6455.      * NPV
  6456.      *
  6457.      * Returns the Net Present Value of a cash flow series given a discount rate.
  6458.      *
  6459.      * @param    float    Discount interest rate
  6460.      * @param    array    Cash flow series
  6461.      * @return    float 
  6462.      */
  6463.     public static function NPV({
  6464.         // Return value
  6465.         $returnValue 0;
  6466.  
  6467.         // Loop trough arguments
  6468.         $aArgs self::flattenArray(func_get_args());
  6469.  
  6470.         // Calculate
  6471.         $rate array_shift($aArgs);
  6472.         for ($i 1$i <= count($aArgs)++$i{
  6473.             // Is it a numeric value?
  6474.             if (is_numeric($aArgs[$i 1])) {
  6475.                 $returnValue += $aArgs[$i 1pow($rate$i);
  6476.             }
  6477.         }
  6478.  
  6479.         // Return
  6480.         return $returnValue;
  6481.     }
  6482.  
  6483.     /**
  6484.      * ACCRINT
  6485.      *
  6486.      * Computes the accrued interest for a security that pays periodic interest.
  6487.      *
  6488.      * @param    int        $issue 
  6489.      * @param    int        $firstInterest 
  6490.      * @param    int        $settlement 
  6491.      * @param    int        $rate 
  6492.      * @param    int        $par 
  6493.      * @param    int        $frequency 
  6494.      * @param    int        $basis 
  6495.      * @return  int        The accrued interest for a security that pays periodic interest.
  6496.      */
  6497.     /*
  6498.     public static function ACCRINT($issue = 0, $firstInterest = 0, $settlement = 0, $rate = 0, $par = 1000, $frequency = 1, $basis = 0) {
  6499.         $issue            = self::flattenSingleValue($issue);
  6500.         $firstInterest    = self::flattenSingleValue($firstInterest);
  6501.         $settlement    = self::flattenSingleValue($settlement);
  6502.         $rate            = self::flattenSingleValue($rate);
  6503.         $par            = self::flattenSingleValue($par);
  6504.         $frequency        = self::flattenSingleValue($frequency);
  6505.         $basis            = self::flattenSingleValue($basis);
  6506.  
  6507.         // Perform checks
  6508.         if ($issue >= $settlement || $rate <= 0 || $par <= 0 || !($frequency == 1 || $frequency == 2 || $frequency == 4) || $basis < 0 || $basis > 4) return self::$_errorCodes['num'];
  6509.  
  6510.         // Calculate value
  6511.         return $par * ($rate / $frequency) *
  6512.     }
  6513.     */
  6514.  
  6515.     /**
  6516.      * SLN
  6517.      *
  6518.      * Returns the straight-line depreciation of an asset for one period
  6519.      *
  6520.      * @param    cost        Initial cost of the asset
  6521.      * @param    salvage        Value at the end of the depreciation
  6522.      * @param    life        Number of periods over which the asset is depreciated
  6523.      * @return    float 
  6524.      */
  6525.     public static function SLN($cost$salvage$life{
  6526.         $cost        self::flattenSingleValue($cost);
  6527.         $salvage    self::flattenSingleValue($salvage);
  6528.         $life        self::flattenSingleValue($life);
  6529.  
  6530.         // Calculate
  6531.         if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life))) {
  6532.             if ($life 0{
  6533.                 return self::$_errorCodes['num'];
  6534.             }
  6535.             return ($cost $salvage$life;
  6536.         }
  6537.         return self::$_errorCodes['value'];
  6538.     }
  6539.  
  6540.     /**
  6541.      * CELL_ADDRESS
  6542.      *
  6543.      * Returns the straight-line depreciation of an asset for one period
  6544.      *
  6545.      * @param    row            Row number to use in the cell reference
  6546.      * @param    column        Column number to use in the cell reference
  6547.      * @param    relativity    Flag indicating the type of reference to return
  6548.      * @param    sheetText    Name of worksheet to use
  6549.      * @return    string 
  6550.      */
  6551.     public static function CELL_ADDRESS($row$column$relativity=1$referenceStyle=True$sheetText=''{
  6552.         $row        self::flattenSingleValue($row);
  6553.         $column        self::flattenSingleValue($column);
  6554.         $relativity    self::flattenSingleValue($relativity);
  6555.         $sheetText    self::flattenSingleValue($sheetText);
  6556.  
  6557.         if ($sheetText ''{
  6558.             if (strpos($sheetText,' '!== False$sheetText "'".$sheetText."'"}
  6559.             $sheetText .='!';
  6560.         }
  6561.         if (!$referenceStyle{
  6562.             if (($relativity == 2|| ($relativity == 4)) $column '['.$column.']'}
  6563.             if (($relativity == 3|| ($relativity == 4)) $row '['.$row.']'}
  6564.             return $sheetText.'R'.$row.'C'.$column;
  6565.         else {
  6566.             $rowRelative $columnRelative '$';
  6567.             $column PHPExcel_Cell::stringFromColumnIndex($column-1);
  6568.             if (($relativity == 2|| ($relativity == 4)) $columnRelative ''}
  6569.             if (($relativity == 3|| ($relativity == 4)) $rowRelative ''}
  6570.             return $sheetText.$columnRelative.$column.$rowRelative.$row;
  6571.         }
  6572.     }
  6573.  
  6574.  
  6575.     public static function COLUMN($cellAddress=Null{
  6576.         if (is_null($cellAddress|| $cellAddress === ''{
  6577.             return 0;
  6578.         }
  6579.  
  6580.         foreach($cellAddress as $columnKey => $value{
  6581.             return PHPExcel_Cell::columnIndexFromString($columnKey);
  6582.         }
  6583.     }    //    function COLUMN()
  6584.  
  6585.  
  6586.     public static function ROW($cellAddress=Null{
  6587.         if ($cellAddress === Null{
  6588.             return 0;
  6589.         }
  6590.  
  6591.         foreach($cellAddress as $columnKey => $rowValue{
  6592.             foreach($rowValue as $rowKey => $cellValue{
  6593.                 return $rowKey;
  6594.             }
  6595.         }
  6596.     }    //    function ROW()
  6597.  
  6598.  
  6599.     public static function OFFSET($cellAddress=Null,$rows=0,$columns=0,$height=null,$width=null{
  6600.         if ($cellAddress == Null{
  6601.             return 0;
  6602.         }
  6603.  
  6604.         foreach($cellAddress as $startColumnKey => $rowValue{
  6605.             $startColumnIndex PHPExcel_Cell::columnIndexFromString($startColumnKey);
  6606.             foreach($rowValue as $startRowKey => $cellValue{
  6607.                 break 2;
  6608.             }
  6609.         }
  6610.  
  6611.         foreach($cellAddress as $endColumnKey => $rowValue{
  6612.             foreach($rowValue as $endRowKey => $cellValue{
  6613.             }
  6614.         }
  6615.         $endColumnIndex PHPExcel_Cell::columnIndexFromString($endColumnKey);
  6616.  
  6617.         $startColumnIndex += --$columns;
  6618.         $startRowKey += $rows;
  6619.  
  6620.         if ($width == null{
  6621.             $endColumnIndex += $columns -1;
  6622.         else {
  6623.             $endColumnIndex $startColumnIndex $width;
  6624.         }
  6625.         if ($height == null{
  6626.             $endRowKey += $rows;
  6627.         else {
  6628.             $endRowKey $startRowKey $height -1;
  6629.         }
  6630.  
  6631.         if (($startColumnIndex 0|| ($startRowKey <= 0)) {
  6632.             return self::$_errorCodes['reference'];
  6633.         }
  6634.  
  6635.         $startColumnKey PHPExcel_Cell::stringFromColumnIndex($startColumnIndex);
  6636.         $endColumnKey PHPExcel_Cell::stringFromColumnIndex($endColumnIndex);
  6637.  
  6638.         $startCell $startColumnKey.$startRowKey;
  6639.         $endCell $endColumnKey.$endRowKey;
  6640.  
  6641.         if ($startCell == $endCell{
  6642.             return $startColumnKey.$startRowKey;
  6643.         else {
  6644.             return $startColumnKey.$startRowKey.':'.$endColumnKey.$endRowKey;
  6645.         }
  6646.     }    //    function COLUMN()
  6647.  
  6648.  
  6649.     public static function CHOOSE({
  6650.         $chooseArgs func_get_args();
  6651.         $chosenEntry self::flattenSingleValue(array_shift($chooseArgs));
  6652.         $entryCount count($chooseArgs1;
  6653.  
  6654.         if ((is_numeric($chosenEntry)) && (!is_bool($chosenEntry))) {
  6655.             --$chosenEntry;
  6656.         else {
  6657.             return self::$_errorCodes['value'];
  6658.         }
  6659.         $chosenEntry floor($chosenEntry);
  6660.         if (($chosenEntry <= 0|| ($chosenEntry $entryCount)) {
  6661.             return self::$_errorCodes['value'];
  6662.         }
  6663.  
  6664.         if (is_array($chooseArgs[$chosenEntry])) {
  6665.             return self::flattenArray($chooseArgs[$chosenEntry]);
  6666.         else {
  6667.             return $chooseArgs[$chosenEntry];
  6668.         }
  6669.     }
  6670.     /**
  6671.      * MATCH
  6672.      * The MATCH function searches for a specified item in a range of cells
  6673.      * @param    lookup_value    The value that you want to match in lookup_array
  6674.      * @param    lookup_array    The range of cells being searched
  6675.      * @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.
  6676.      * @return    integer        the relative position of the found item
  6677.      */
  6678.     public static function MATCH($lookup_value$lookup_array$match_type=1{
  6679.  
  6680.         // flatten the lookup_array
  6681.         $lookup_array self::flattenArray($lookup_array);
  6682.  
  6683.         // flatten lookup_value since it may be a cell reference to a value or the value itself
  6684.         $lookup_value self::flattenSingleValue($lookup_value);
  6685.  
  6686.         // MATCH is not case sensitive
  6687.         $lookup_value strtolower($lookup_value);
  6688.  
  6689.         /*
  6690.         echo "--------------------<br>looking for $lookup_value in <br>";
  6691.         print_r($lookup_array);
  6692.         echo "<br>";
  6693.         //return 1;
  6694.         /**/
  6695.  
  6696.         // **
  6697.         // check inputs
  6698.         // **
  6699.         // lookup_value type has to be number, text, or logical values
  6700.         if (!is_numeric($lookup_value&& !is_string($lookup_value&& !is_bool($lookup_value)){
  6701.             // error: lookup_array should contain only number, text, or logical values
  6702.             //echo "error: lookup_array should contain only number, text, or logical values<br>";
  6703.             return self::$_errorCodes['na'];
  6704.         }
  6705.  
  6706.         // match_type is 0, 1 or -1
  6707.         if ($match_type!==&& $match_type!==-&& $match_type!==1){
  6708.             // error: wrong value for match_type
  6709.             //echo "error: wrong value for match_type<br>";
  6710.             return self::$_errorCodes['na'];
  6711.         }
  6712.  
  6713.         // lookup_array should not be empty
  6714.         if (sizeof($lookup_array)<=0){
  6715.             // error: empty range
  6716.             //echo "error: empty range ".sizeof($lookup_array)."<br>";
  6717.             return self::$_errorCodes['na'];
  6718.         }
  6719.  
  6720.         // lookup_array should contain only number, text, or logical values
  6721.         for ($i=0;$i<sizeof($lookup_array);++$i){
  6722.             // check the type of the value
  6723.             if (!is_numeric($lookup_array[$i]&& !is_string($lookup_array[$i]&& !is_bool($lookup_array[$i])){
  6724.                 // error: lookup_array should contain only number, text, or logical values
  6725.                 //echo "error: lookup_array should contain only number, text, or logical values<br>";
  6726.                 return self::$_errorCodes['na'];
  6727.             }
  6728.             // convert tpo lowercase
  6729.             if (is_string($lookup_array[$i]))
  6730.                 $lookup_array[$istrtolower($lookup_array[$i]);
  6731.         }
  6732.  
  6733.         // if match_type is 1 or -1, the list has to be ordered
  6734.         if($match_type==|| $match_type==-1){
  6735.             // **
  6736.             // iniitialization
  6737.             // store the last value
  6738.             $iLastValue=$lookup_array[0];
  6739.             // **
  6740.             // loop on the cells
  6741.             for ($i=0;$i<sizeof($lookup_array);++$i){
  6742.                 // check ascending order
  6743.                 if(($match_type==&& $lookup_array[$i]<$iLastValue)
  6744.                     // OR check descending order
  6745.                     || ($match_type==-&& $lookup_array[$i]>$iLastValue)){
  6746.                     // error: list is not ordered correctly
  6747.                     //echo "error: list is not ordered correctly<br>";
  6748.                     return self::$_errorCodes['na'];
  6749.                 }
  6750.             }
  6751.         }
  6752.         // **
  6753.         // find the match
  6754.         // **
  6755.         // loop on the cells
  6756.         for ($i=0$i sizeof($lookup_array)++$i){
  6757.             // if match_type is 0 <=> find the first value that is exactly equal to lookup_value
  6758.             if ($match_type==&& $lookup_array[$i]==$lookup_value){
  6759.                 // this is the exact match
  6760.                 return $i+1;
  6761.             }
  6762.             // if match_type is -1 <=> find the smallest value that is greater than or equal to lookup_value
  6763.             if ($match_type==-&& $lookup_array[$i$lookup_value){
  6764.                 if ($i<1){
  6765.                     // 1st cell was allready smaller than the lookup_value
  6766.                     break;
  6767.                 }
  6768.                 else
  6769.                     // the previous cell was the match
  6770.                     return $i;
  6771.             }
  6772.             // if match_type is 1 <=> find the largest value that is less than or equal to lookup_value
  6773.             if ($match_type==&& $lookup_array[$i$lookup_value){
  6774.                 if ($i<1){
  6775.                     // 1st cell was allready bigger than the lookup_value
  6776.                     break;
  6777.                 }
  6778.                 else
  6779.                     // the previous cell was the match
  6780.                     return $i;
  6781.             }
  6782.         }
  6783.         // unsuccessful in finding a match, return #N/A error value
  6784.         //echo "unsuccessful in finding a match<br>";
  6785.         return self::$_errorCodes['na'];
  6786.     }
  6787.     /**
  6788.      * Uses an index to choose a value from a reference or array
  6789.      * implemented: Return the value of a specified cell or array of cells    Array form
  6790.      * not implemented: Return a reference to specified cells    Reference form
  6791.      *
  6792.      * @param    range_array    a range of cells or an array constant
  6793.      * @param    row_num        selects the row in array from which to return a value. If row_num is omitted, column_num is required.
  6794.      * @param    column_num    selects the column in array from which to return a value. If column_num is omitted, row_num is required.
  6795.      */
  6796.     public static function INDEX($range_array,$row_num=null,$column_num=null{
  6797.         // **
  6798.         // check inputs
  6799.         // **
  6800.         // at least one of row_num and column_num is required
  6801.         if ($row_num==null && $column_num==null){
  6802.             // error: row_num and column_num are both undefined
  6803.             //echo "error: row_num and column_num are both undefined<br>";
  6804.             return self::$_errorCodes['value'];
  6805.         }
  6806.         // default values for row_num and column_num
  6807.         if ($row_num==null){
  6808.             $row_num 1;
  6809.         }
  6810.         if ($column_num==null){
  6811.             $column_num 1;
  6812.         }
  6813.  
  6814.         /* debug
  6815.         print_r($range_array);
  6816.         echo "<br>$row_num , $column_num<br>";
  6817.         /**/
  6818.  
  6819.         // row_num and column_num may not have negative values
  6820.         if (($row_num!=null && $row_num 0|| ($column_num!=null && $column_num 0)) {
  6821.             // error: row_num or column_num has negative value
  6822.             //echo "error: row_num or column_num has negative value<br>";
  6823.             return self::$_errorCodes['value'];
  6824.         }
  6825.         // **
  6826.         // convert column and row numbers into array indeces
  6827.         // **
  6828.         // array is zero based
  6829.         --$column_num;
  6830.         --$row_num;
  6831.  
  6832.         // retrieve the columns
  6833.         $columnKeys array_keys($range_array);
  6834.  
  6835.         // retrieve the rows
  6836.         $rowKeys array_keys($range_array[$columnKeys[0]]);
  6837.  
  6838.         // test ranges
  6839.         if ($column_num >= sizeof($columnKeys)){
  6840.             // error: column_num is out of range
  6841.             //echo "error: column_num is out of range - $column_num > ".sizeof($columnKeys)."<br>";
  6842.             return self::$_errorCodes['reference'];
  6843.         }
  6844.         if ($row_num >= sizeof($rowKeys)){
  6845.             // error: row_num is out of range
  6846.             //echo "error: row_num is out of range - $row_num > ".sizeof($rowKeys)."<br>";
  6847.             return self::$_errorCodes['reference'];
  6848.         }
  6849.         // compute and return result
  6850.         return $range_array[$columnKeys[$column_num]][$rowKeys[$row_num]];
  6851.     }
  6852.  
  6853. /*    public static function INDEX($arrayValues,$rowNum = 0,$columnNum = 0) {
  6854.  
  6855.         if (($rowNum < 0) || ($columnNum < 0)) {
  6856.             return self::$_errorCodes['value'];
  6857.         }
  6858.  
  6859.         $columnKeys = array_keys($arrayValues);
  6860.         $rowKeys = array_keys($arrayValues[$columnKeys[0]]);
  6861.         if ($columnNum > count($columnKeys)) {
  6862.             return self::$_errorCodes['value'];
  6863.         } elseif ($columnNum == 0) {
  6864.             if ($rowNum == 0) {
  6865.                 return $arrayValues;
  6866.             }
  6867.             $rowNum = $rowKeys[--$rowNum];
  6868.             $returnArray = array();
  6869.             foreach($arrayValues as $arrayColumn) {
  6870.                 $returnArray[] = $arrayColumn[$rowNum];
  6871.             }
  6872.             return $returnArray;
  6873.         }
  6874.         $columnNum = $columnKeys[--$columnNum];
  6875.         if ($rowNum > count($rowKeys)) {
  6876.             return self::$_errorCodes['value'];
  6877.         } elseif ($rowNum == 0) {
  6878.             return $arrayValues[$columnNum];
  6879.         }
  6880.         $rowNum = $rowKeys[--$rowNum];
  6881.  
  6882.         return $arrayValues[$columnNum][$rowNum];
  6883.     }
  6884. */
  6885.     /**
  6886.      * SYD
  6887.      *
  6888.      * Returns the sum-of-years' digits depreciation of an asset for a specified period.
  6889.      *
  6890.      * @param    cost        Initial cost of the asset
  6891.      * @param    salvage        Value at the end of the depreciation
  6892.      * @param    life        Number of periods over which the asset is depreciated
  6893.      * @param    period        Period
  6894.      * @return    float 
  6895.      */
  6896.     public static function SYD($cost$salvage$life$period{
  6897.         $cost        self::flattenSingleValue($cost);
  6898.         $salvage    self::flattenSingleValue($salvage);
  6899.         $life        self::flattenSingleValue($life);
  6900.         $period        self::flattenSingleValue($period);
  6901.  
  6902.         // Calculate
  6903.         if ((is_numeric($cost)) && (is_numeric($salvage)) && (is_numeric($life)) && (is_numeric($period))) {
  6904.             if (($life 1|| ($salvage $life)) {
  6905.                 return self::$_errorCodes['num'];
  6906.             }
  6907.             return (($cost $salvage($life $period 12($life ($life 1));
  6908.         }
  6909.         return self::$_errorCodes['value'];
  6910.     }
  6911.  
  6912.     /**
  6913.      * TRANSPOSE
  6914.      *
  6915.      * @param    mixed    $value    Value to check
  6916.      * @return  boolean 
  6917.      *
  6918.      *  Unlike the Excel TRANSPOSE function, which will only work on a single row or column, this function will transpose a full matrix.
  6919.      */
  6920.     public static function TRANSPOSE($matrixData{
  6921.         $returnMatrix array();
  6922.         $column 0;
  6923.         foreach($matrixData as $matrixRow{
  6924.             $row 0;
  6925.             foreach($matrixRow as $matrixCell{
  6926.                 $returnMatrix[$column][$row$matrixCell;
  6927.                 ++$row;
  6928.             }
  6929.             ++$column;
  6930.         }
  6931.         return $returnMatrix;
  6932.     }
  6933.  
  6934.     /**
  6935.      * MMULT
  6936.      *
  6937.      * @param    mixed    $value    Value to check
  6938.      * @return  boolean 
  6939.      */
  6940.     public static function MMULT($matrixData1,$matrixData2{
  6941.         $matrixAData $matrixBData array();
  6942.         $rowA 0;
  6943.         foreach($matrixData1 as $matrixRow{
  6944.             $columnA 0;
  6945.             foreach($matrixRow as $matrixCell{
  6946.                 if ((is_string($matrixCell)) || ($matrixCell === null)) {
  6947.                     return self::$_errorCodes['value'];
  6948.                 }
  6949.                 $matrixAData[$columnA][$rowA$matrixCell;
  6950.                 ++$columnA;
  6951.             }
  6952.             ++$rowA;
  6953.         }
  6954.         $matrixA new Matrix($matrixAData);
  6955.         $rowB 0;
  6956.         foreach($matrixData2 as $matrixRow{
  6957.             $columnB 0;
  6958.             foreach($matrixRow as $matrixCell{
  6959.                 if ((is_string($matrixCell)) || ($matrixCell === null)) {
  6960.                     return self::$_errorCodes['value'];
  6961.                 }
  6962.                 $matrixBData[$columnB][$rowB$matrixCell;
  6963.                 ++$columnB;
  6964.             }
  6965.             ++$rowB;
  6966.         }
  6967.         $matrixB new Matrix($matrixBData);
  6968.  
  6969.         if (($rowA != $columnB|| ($rowB != $columnA)) {
  6970.             return self::$_errorCodes['value'];
  6971.         }
  6972.  
  6973.         return $matrixA->times($matrixB)->getArray();
  6974.     }
  6975.  
  6976.     /**
  6977.     * VLOOKUP
  6978.     * 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.
  6979.     * @param    lookup_value    The value that you want to match in lookup_array
  6980.     * @param    lookup_array    The range of cells being searched
  6981.     * @param    index_number    The column number in table_array from which the matching value must be returned. The first column is 1.
  6982.     * @param    not_exact_match    Determines if you are looking for an exact match based on lookup_value.
  6983.     * @return    mixed            The value of the found cell
  6984.     */
  6985.     public static function VLOOKUP($lookup_value$lookup_array$index_number$not_exact_match=true{
  6986.         // index_number must be greater than or equal to 1
  6987.         if ($index_number 1{
  6988.             return self::$_errorCodes['value'];
  6989.         }
  6990.  
  6991.         // index_number must be less than or equal to the number of columns in lookup_array
  6992.         if ($index_number count($lookup_array)) {
  6993.             return self::$_errorCodes['reference'];
  6994.         }
  6995.  
  6996.         // re-index lookup_array with numeric keys starting at 1
  6997.         array_unshift($lookup_arrayarray());
  6998.         $lookup_array array_slice(array_values($lookup_array)1count($lookup_array)true);
  6999.  
  7000.         // look for an exact match
  7001.         $row_number array_search($lookup_value$lookup_array[1]);
  7002.  
  7003.         // if an exact match is required, we have what we need to return an appropriate response
  7004.         if ($not_exact_match == false{
  7005.             if ($row_number === false{
  7006.                 return self::$_errorCodes['na'];
  7007.             else {
  7008.                 return $lookup_array[$index_number][$row_number];
  7009.             }
  7010.         }
  7011.  
  7012.         // TODO: The VLOOKUP spec in Excel states that, at this point, we should search for
  7013.         // the highest value that is less than lookup_value. However, documentation on how string
  7014.         // values should be treated here is sparse.
  7015.         return self::$_errorCodes['na'];
  7016.     }
  7017.  
  7018.     /**
  7019.     * LOOKUP
  7020.     * The LOOKUP function searches for value either from a one-row or one-column range or from an array.
  7021.     * @param    lookup_value    The value that you want to match in lookup_array
  7022.     * @param    lookup_vector    The range of cells being searched
  7023.     * @param    result_vector    The column from which the matching value must be returned
  7024.     * @return    mixed            The value of the found cell
  7025.     */
  7026.     public static function LOOKUP($lookup_value$lookup_vector$result_vector=null{
  7027.  
  7028.         // check for LOOKUP Syntax (view Excel documentation)
  7029.         ifis_null($result_vector) )
  7030.         {
  7031.         // TODO: Syntax 2 (array)
  7032.         else {
  7033.         // Syntax 1 (vector)
  7034.             // get key (column or row) of lookup_vector
  7035.             $kl key($lookup_vector);
  7036.             // check if lookup_value exists in lookup_vector
  7037.             ifin_array($lookup_value$lookup_vector[$kl]) )
  7038.             {
  7039.             // FOUND IT! Get key of lookup_vector
  7040.                 $k_res array_search($lookup_value$lookup_vector[$kl]);
  7041.             else {
  7042.             // value NOT FOUND
  7043.                 // Get the smallest value in lookup_vector
  7044.                 // The LOOKUP spec in Excel states --> IMPORTANT - The values in lookup_vector must be placed in ascending order!
  7045.                 $ksv key($lookup_vector[$kl]);
  7046.                 $smallest_value $lookup_vector[$kl][$ksv];
  7047.                 // If lookup_value is smaller than the smallest value in lookup_vector, LOOKUP gives the #N/A error value.
  7048.                 if$lookup_value $smallest_value )
  7049.                 {
  7050.                     return self::$_errorCodes['na'];
  7051.                 else {
  7052.                     // If LOOKUP can't find the lookup_value, it matches the largest value in lookup_vector that is less than or equal to lookup_value.
  7053.                     // IMPORTANT : In Excel Documentation is not clear what happen if lookup_value is text!
  7054.                     foreach$lookup_vector[$klAS $kk => $value )
  7055.                     {
  7056.                         if$lookup_value >= $value )
  7057.                         {
  7058.                             $k_res $kk;
  7059.                         }
  7060.                     }
  7061.                 }
  7062.             }
  7063.  
  7064.             // Returns a value from the same position in result_vector
  7065.             // get key (column or row) of result_vector
  7066.             $kr key($result_vector);
  7067.             ifisset($result_vector[$kr][$k_res]) )
  7068.             {
  7069.                 return $result_vector[$kr][$k_res];
  7070.             else {
  7071.                 // TODO: In Excel Documentation is not clear what happen here...
  7072.             }
  7073.         }
  7074.      }
  7075.  
  7076.  
  7077.     /**
  7078.      * Flatten multidemensional array
  7079.      *
  7080.      * @param    array    $array    Array to be flattened
  7081.      * @return  array    Flattened array
  7082.      */
  7083.     public static function flattenArray($array{
  7084.         if(!is_array $array ) ){
  7085.             $array array $array );
  7086.         }
  7087.  
  7088.         $arrayValues array();
  7089.  
  7090.         foreach ($array as $value{
  7091.             if (is_scalar($value)) {
  7092.                 $arrayValues[self::flattenSingleValue($value);
  7093.             elseif (is_array($value)) {
  7094.                 $arrayValues array_merge($arrayValuesself::flattenArray($value));
  7095.             else {
  7096.                 $arrayValues[$value;
  7097.             }
  7098.         }
  7099.  
  7100.         return $arrayValues;
  7101.     }
  7102.  
  7103.     /**
  7104.      * Convert an array with one element to a flat value
  7105.      *
  7106.      * @param    mixed        $value        Array or flat value
  7107.      * @return    mixed 
  7108.      */
  7109.     public static function flattenSingleValue($value ''{
  7110.         if (is_array($value)) {
  7111.             $value self::flattenSingleValue(array_pop($value));
  7112.         }
  7113.         return $value;
  7114.     }
  7115. }
  7116.  
  7117.  
  7118. //
  7119. //    There are a few mathematical functions that aren't available on all versions of PHP for all platforms
  7120. //    These functions aren't available in Windows implementations of PHP prior to version 5.3.0
  7121. //    So we test if they do exist for this version of PHP/operating platform; and if not we create them
  7122. //
  7123. if (!function_exists('acosh')) {
  7124.     function acosh($x{
  7125.         return log(sqrt(($x 12sqrt(($x 12));
  7126.     }
  7127. }
  7128.  
  7129. if (!function_exists('asinh')) {
  7130.     function asinh($x{
  7131.         return log($x sqrt($x $x));
  7132.     }
  7133. }
  7134.  
  7135. if (!function_exists('atanh')) {
  7136.     function atanh($x{
  7137.         return (log($xlog($x)) 2;
  7138.     }
  7139. }
  7140.  
  7141. ?>

Documentation generated on Mon, 05 Jan 2009 20:37:47 +0100 by phpDocumentor 1.4.1