]> source.dussan.org Git - poi.git/commitdiff
Amol's formula eval additions. Missed these files, sorry
authorAvik Sengupta <avik@apache.org>
Tue, 14 Jun 2005 17:41:33 +0000 (17:41 +0000)
committerAvik Sengupta <avik@apache.org>
Tue, 14 Jun 2005 17:41:33 +0000 (17:41 +0000)
git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@353717 13f79535-47bb-0310-9956-ffa450edef68

src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/FinanceLib.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/MathX.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/StatsLib.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/TextFunction.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AbstractNumericTestCase.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestFinanceLib.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestMathX.java [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestStatsLib.java [new file with mode: 0644]

diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/FinanceLib.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/FinanceLib.java
new file mode 100644 (file)
index 0000000..90378c8
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Created on May 21, 2005
+ *
+ */
+package org.apache.poi.hssf.record.formula.functions;
+
+/**
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+ * 
+ * 
+ * This class is a functon library for common fiscal functions.
+ * <b>Glossary of terms/abbreviations:</b>
+ * <br/>
+ * <ul>
+ * <li><em>FV:</em> Future Value</li>
+ * <li><em>PV:</em> Present Value</li>
+ * <li><em>NPV:</em> Net Present Value</li>
+ * <li><em>PMT:</em> (Periodic) Payment</li>
+ * 
+ * </ul>
+ * For more info on the terms/abbreviations please use the references below 
+ * (hyperlinks are subject to change):
+ * </br>Online References:
+ * <ol>
+ * <li>GNU Emacs Calc 2.02 Manual: http://theory.uwinnipeg.ca/gnu/calc/calc_203.html</li>
+ * <li>Yahoo Financial Glossary: http://biz.yahoo.com/f/g/nn.html#y</li>
+ * </ol>
+ */
+public class FinanceLib {
+    
+    // constants for default values
+    
+    
+    
+    private FinanceLib() {}
+    
+    /**
+     * Future value of an amount given the number of payments, rate, amount
+     * of individual payment, present value and boolean value indicating whether
+     * payments are due at the beginning of period 
+     * (false => payments are due at end of period) 
+     * @param r rate
+     * @param n num of periods
+     * @param y pmt per period
+     * @param f future value
+     * @param t type (true=pmt at end of period, false=pmt at begining of period)
+     * @return
+     */
+    public static double fv(double r, int n, double y, double p, boolean t) {
+        double r1 = r + 1;
+        return ((1-Math.pow(r1, n)) * (t ? r1 : 1) * y ) / r  
+                  - 
+               p*Math.pow(r1, n);
+    }
+    
+    /**
+     * Present value of an amount given the number of future payments, rate, amount
+     * of individual payment, future value and boolean value indicating whether
+     * payments are due at the beginning of period 
+     * (false => payments are due at end of period) 
+     * @param r
+     * @param n
+     * @param y
+     * @param f
+     * @param t
+     * @return
+     */
+    public static double pv(double r, int n, double y, double f, boolean t) {
+        double r1 = r + 1;
+        return (( ( 1 - Math.pow(r1, n) ) / r ) * (t ? r1 : 1)  * y - f)
+                  /
+               Math.pow(r1, n);
+    }
+    
+    /**
+     * calculates the Net Present Value of a principal amount
+     * given the discount rate and a sequence of cash flows 
+     * (supplied as an array). If the amounts are income the value should 
+     * be positive, else if they are payments and not income, the 
+     * value should be negative.
+     * @param r
+     * @param cfs cashflow amounts
+     * @return
+     */
+    public static double npv(double r, double[] cfs) {
+        double npv = 0;
+        double r1 = r + 1;
+        double trate = r1;
+        for (int i=0, iSize=cfs.length; i<iSize; i++) {
+            npv += cfs[i] / trate;
+            trate *= r1;
+        }
+        return npv;
+    }
+    
+    /**
+     * 
+     * @param r
+     * @param n
+     * @param p
+     * @param f
+     * @param t
+     * @return
+     */
+    public static double pmt(double r, int n, double p, double f, boolean t) {
+        double r1 = r + 1;
+        return ( f + p * Math.pow(r1, n) ) * r 
+                  / 
+               ((t ? r1 : 1) * (1 - Math.pow(r1, n)));
+    }
+    
+    /**
+     * 
+     * @param r
+     * @param n
+     * @param p
+     * @param f
+     * @param t
+     * @return
+     */
+    public static int nper(double r, double y, double p, double f, boolean t) {
+        double r1 = r + 1;
+        double ryr = (t ? r1 : 1) * y / r;
+        double a1 = ((ryr-f) < 0) ? Math.log(f-ryr) : Math.log(ryr-f);
+        double a2 = ((ryr-f) < 0) ? Math.log(-p-ryr) : Math.log(p+ryr);
+        double a3 = Math.log(r1);
+        double dval = ( a1 - a2 ) / a3;
+        
+        return (int) Math.round(dval);
+    }
+    
+
+}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/MathX.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/MathX.java
new file mode 100644 (file)
index 0000000..d897b71
--- /dev/null
@@ -0,0 +1,571 @@
+/*
+ * Created on May 19, 2005
+ *
+ */
+package org.apache.poi.hssf.record.formula.functions;
+
+
+
+/**
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+ * This class is an extension to the standard math library
+ * provided by java.lang.Math class. It follows the Math class
+ * in that it has a private constructor and all static methods.
+ */
+public final class MathX {
+
+    
+    private MathX() {}
+    
+    
+    /**
+     * Returns a value rounded to p digits after decimal.
+     * If p is negative, then the number is rounded to
+     * places to the left of the decimal point. eg. 
+     * 10.23 rounded to -1 will give: 10. If p is zero,
+     * the returned value is rounded to the nearest integral
+     * value.
+     * <p>If n is negative, the resulting value is obtained
+     * as the round value of absolute value of n multiplied
+     * by the sign value of n (@see MathX.sign(double d)). 
+     * Thus, -0.6666666 rounded to p=0 will give -1 not 0.
+     * <p>If n is NaN, returned value is NaN.
+     * @param n
+     * @param p
+     * @return
+     */
+    public static double round(double n, int p) {
+        double retval;
+        
+        if (Double.isNaN(n) || Double.isInfinite(n)) {
+            retval = Double.NaN;
+        }
+        else {
+            if (p != 0) {
+                double temp = Math.pow(10, p);
+                retval = Math.round(n*temp)/temp;
+            }
+            else {
+                retval = Math.round(n);
+            }
+        }
+        
+        return retval;
+    }
+
+    /**
+     * Returns a value rounded-up to p digits after decimal.
+     * If p is negative, then the number is rounded to
+     * places to the left of the decimal point. eg. 
+     * 10.23 rounded to -1 will give: 20. If p is zero,
+     * the returned value is rounded to the nearest integral
+     * value.
+     * <p>If n is negative, the resulting value is obtained
+     * as the round-up value of absolute value of n multiplied
+     * by the sign value of n (@see MathX.sign(double d)). 
+     * Thus, -0.2 rounded-up to p=0 will give -1 not 0.
+     * <p>If n is NaN, returned value is NaN.
+     * @param n
+     * @param p
+     * @return
+     */
+    public static double roundUp(double n, int p) {
+        double retval;
+        
+        if (Double.isNaN(n) || Double.isInfinite(n)) {
+            retval = Double.NaN;
+        }
+        else {
+            if (p != 0) {
+                double temp = Math.pow(10, p);
+                double nat = Math.abs(n*temp);
+                
+                retval = sign(n) * 
+                    ((nat == (long) nat)
+                            ? nat / temp
+                            : Math.round(nat + 0.5) / temp);
+            }
+            else {
+                double na = Math.abs(n);
+                retval = sign(n) * 
+                    ((na == (long) na)
+                        ? na
+                        : (long) na + 1);
+            }
+        }
+        
+        return retval;
+    }
+
+    /**
+     * Returns a value rounded to p digits after decimal.
+     * If p is negative, then the number is rounded to
+     * places to the left of the decimal point. eg. 
+     * 10.23 rounded to -1 will give: 10. If p is zero,
+     * the returned value is rounded to the nearest integral
+     * value.
+     * <p>If n is negative, the resulting value is obtained
+     * as the round-up value of absolute value of n multiplied
+     * by the sign value of n (@see MathX.sign(double d)). 
+     * Thus, -0.8 rounded-down to p=0 will give 0 not -1.
+     * <p>If n is NaN, returned value is NaN.
+     * @param n
+     * @param p
+     * @return
+     */
+    public static double roundDown(double n, int p) {
+        double retval;
+        
+        if (Double.isNaN(n) || Double.isInfinite(n)) {
+            retval = Double.NaN;
+        }
+        else {
+            if (p != 0) {
+                double temp = Math.pow(10, p);
+                retval = sign(n) * Math.round((Math.abs(n)*temp) - 0.5)/temp;
+            }
+            else {
+                retval = (long) n;
+            }
+        }
+        
+        return retval;
+    }
+    
+    
+    /**
+     * If d < 0, returns short -1
+     * <br/>
+     * If d > 0, returns short 1
+     * <br/>
+     * If d == 0, returns short 0 
+     * <p> If d is NaN, then 1 will be returned. It is the responsibility
+     * of caller to check for d isNaN if some other value is desired.
+     * @param d
+     * @return
+     */
+    public static short sign(double d) {
+        return (short) ((d == 0)
+                ? 0
+                : (d < 0)
+                        ? -1
+                        : 1);
+    }
+   
+    /**
+     * average of all values
+     * @param values
+     * @return
+     */
+    public static double average(double[] values) {
+        double ave = 0;
+        double sum = 0;
+        for (int i=0, iSize=values.length; i<iSize; i++) {
+            sum += values[i];
+        }
+        ave = sum / values.length;
+        return ave;
+    }
+    
+    
+    /**
+     * sum of all values
+     * @param values
+     * @return
+     */
+    public static double sum(double[] values) {
+        double sum = 0;
+        for (int i=0, iSize=values.length; i<iSize; i++) {
+            sum += values[i];
+        }
+        return sum;
+    }
+    
+    /**
+     * sum of squares of all values
+     * @param values
+     * @return
+     */
+    public static double sumsq(double[] values) {
+        double sumsq = 0;
+        for (int i=0, iSize=values.length; i<iSize; i++) {
+            sumsq += values[i]*values[i];
+        }
+        return sumsq;
+    }
+    
+    
+    /**
+     * product of all values
+     * @param values
+     * @return
+     */
+    public static double product(double[] values) {
+        double product = 0;
+        if (values!=null && values.length > 0) {
+            product = 1;
+            for (int i=0, iSize=values.length; i<iSize; i++) {
+                product *= values[i];
+            }
+        }
+        return product;
+    }
+    
+    /**
+     * min of all values. If supplied array is zero length,
+     * Double.POSITIVE_INFINITY is returned.
+     * @param values
+     * @return
+     */
+    public static double min(double[] values) {
+        double min = Double.POSITIVE_INFINITY;
+        for (int i=0, iSize=values.length; i<iSize; i++) {
+            min = Math.min(min, values[i]);
+        }
+        return min;
+    }
+
+    /**
+     * min of all values. If supplied array is zero length,
+     * Double.NEGATIVE_INFINITY is returned.
+     * @param values
+     * @return
+     */
+    public static double max(double[] values) {
+        double max = Double.NEGATIVE_INFINITY;
+        for (int i=0, iSize=values.length; i<iSize; i++) {
+            max = Math.max(max, values[i]);
+        }
+        return max;
+    }
+
+    /**
+     * Note: this function is different from java.lang.Math.floor(..).
+     * <p>
+     * When n and s are "valid" arguments, the returned value is: Math.floor(n/s) * s;
+     * <br/>
+     * n and s are invalid if any of following conditions are true:
+     * <ul>
+     * <li>s is zero</li>
+     * <li>n is negative and s is positive</li>
+     * <li>n is positive and s is negative</li>
+     * </ul>
+     * In all such cases, Double.NaN is returned.
+     * @param n
+     * @param s
+     * @return
+     */
+    public static double floor(double n, double s) {
+        double f;
+        
+        if ((n<0 && s>0) || (n>0 && s<0) || (s==0 && n!=0)) {
+            f = Double.NaN;
+        }
+        else {
+            f = (n==0 || s==0) ? 0 : Math.floor(n/s) * s;
+        }
+        
+        return f;
+    }
+    
+    /**
+     * Note: this function is different from java.lang.Math.ceil(..).
+     * <p>
+     * When n and s are "valid" arguments, the returned value is: Math.ceiling(n/s) * s;
+     * <br/>
+     * n and s are invalid if any of following conditions are true:
+     * <ul>
+     * <li>s is zero</li>
+     * <li>n is negative and s is positive</li>
+     * <li>n is positive and s is negative</li>
+     * </ul>
+     * In all such cases, Double.NaN is returned.
+     * @param n
+     * @param s
+     * @return
+     */
+    public static double ceiling(double n, double s) {
+        double c;
+        
+        if ((n<0 && s>0) || (n>0 && s<0)) {
+            c = Double.NaN;
+        }
+        else {
+            c = (n == 0 || s == 0) ? 0 : Math.ceil(n/s) * s;
+        }
+        
+        return c;
+    }
+    
+    /**
+     * <br/> for all n >= 1; factorial n = n * (n-1) * (n-2) * ... * 1 
+     * <br/> else if n == 0; factorial n = 1
+     * <br/> else if n < 0; factorial n = Double.NaN
+     * <br/> Loss of precision can occur if n is large enough.
+     * If n is large so that the resulting value would be greater 
+     * than Double.MAX_VALUE; Double.POSITIVE_INFINITY is returned.
+     * If n < 0, Double.NaN is returned. 
+     * @param n
+     * @return
+     */
+    public static double factorial(int n) {
+        double d = 1;
+        
+        if (n >= 0) {
+            if (n <= 170) {
+                for (int i=1; i<=n; i++) {
+                    d *= i;
+                }
+            }
+            else {
+                d = Double.POSITIVE_INFINITY;
+            }
+        }
+        else {
+            d = Double.NaN;
+        }
+        return d;
+    }
+    
+
+    /**
+     * returns the remainder resulting from operation:
+     * n / d. 
+     * <br/> The result has the sign of the divisor.
+     * <br/> Examples:
+     * <ul>
+     * <li>mod(3.4, 2) = 1.4</li>
+     * <li>mod(-3.4, 2) = 0.6</li>
+     * <li>mod(-3.4, -2) = -1.4</li>
+     * <li>mod(3.4, -2) = -0.6</li>
+     * </ul>
+     * If d == 0, result is NaN
+     * @param n
+     * @param d
+     * @return
+     */
+    public static double mod(double n, double d) {
+        double result = 0;
+        
+        if (d == 0) {
+            result = Double.NaN;
+        }
+        else if (sign(n) == sign(d)) {
+            double t = Math.abs(n / d);
+            t = t - (long) t;
+            result = sign(d) * Math.abs(t * d);
+        }
+        else {
+            double t = Math.abs(n / d);
+            t = t - (long) t;
+            t = Math.ceil(t) - t;
+            result = sign(d) * Math.abs(t * d);
+        }
+        
+        return result;
+    }
+    
+    
+    /**
+     * inverse hyperbolic cosine
+     * @param d
+     * @return
+     */
+    public static double acosh(double d) {
+        return Math.log(Math.sqrt(Math.pow(d, 2) - 1) + d);
+    }
+    
+    /**
+     * inverse hyperbolic sine
+     * @param d
+     * @return
+     */
+    public static double asinh(double d) {
+        double d2 = d*d;
+        return Math.log(Math.sqrt(d*d + 1) + d);
+    }
+    
+    /**
+     * inverse hyperbolic tangent
+     * @param d
+     * @return
+     */
+    public static double atanh(double d) {
+        return Math.log((1 + d)/(1 - d)) / 2;
+    }
+    
+    /**
+     * hyperbolic cosine
+     * @param d
+     * @return
+     */
+    public static double cosh(double d) {
+        double ePowX = Math.pow(Math.E, d);
+        double ePowNegX = Math.pow(Math.E, -d);
+        d = (ePowX + ePowNegX) / 2;
+        return d;
+    }
+    
+    /**
+     * hyperbolic sine
+     * @param d
+     * @return
+     */
+    public static double sinh(double d) {
+        double ePowX = Math.pow(Math.E, d);
+        double ePowNegX = Math.pow(Math.E, -d);
+        d = (ePowX - ePowNegX) / 2;
+        return d;
+    }
+    
+    /**
+     * hyperbolic tangent
+     * @param d
+     * @return
+     */
+    public static double tanh(double d) {
+        double ePowX = Math.pow(Math.E, d);
+        double ePowNegX = Math.pow(Math.E, -d);
+        d = (ePowX - ePowNegX) / (ePowX + ePowNegX);
+        return d;
+    }
+    
+    /**
+     * returns the sum of product of corresponding double value in each
+     * subarray. It is the responsibility of the caller to ensure that
+     * all the subarrays are of equal length. If the subarrays are
+     * not of equal length, the return value can be unpredictable.
+     * @param arrays
+     * @return
+     */
+    public static double sumproduct(double[][] arrays) {
+        double d = 0;
+        
+        try {
+            int narr = arrays.length;
+            int arrlen = arrays[0].length; 
+            
+            for (int j=0; j<arrlen; j++) {
+                double t = 1;
+                for (int i=0; i<narr; i++) {
+                    t *= arrays[i][j];
+                }
+                d += t;
+            }            
+        }
+        catch (ArrayIndexOutOfBoundsException ae) {
+            d = Double.NaN;
+        }
+        
+        return d;
+    }
+    
+    /**
+     * returns the sum of difference of squares of corresponding double 
+     * value in each subarray: ie. sigma (xarr[i]^2-yarr[i]^2) 
+     * <br/>
+     * It is the responsibility of the caller 
+     * to ensure that the two subarrays are of equal length. If the 
+     * subarrays are not of equal length, the return value can be 
+     * unpredictable.
+     * @param arrays
+     * @return
+     */
+    public static double sumx2my2(double[] xarr, double[] yarr) {
+        double d = 0;
+        
+        try {
+            for (int i=0, iSize=xarr.length; i<iSize; i++) {
+                d += (xarr[i] + yarr[i]) * (xarr[i] - yarr[i]);
+            }            
+        }
+        catch (ArrayIndexOutOfBoundsException ae) {
+            d = Double.NaN;
+        }
+        
+        return d;
+    }
+    
+    /**
+     * returns the sum of sum of squares of corresponding double 
+     * value in each subarray: ie. sigma (xarr[i]^2 + yarr[i]^2) 
+     * <br/>
+     * It is the responsibility of the caller 
+     * to ensure that the two subarrays are of equal length. If the 
+     * subarrays are not of equal length, the return value can be 
+     * unpredictable.
+     * @param arrays
+     * @return
+     */
+    public static double sumx2py2(double[] xarr, double[] yarr) {
+        double d = 0;
+        
+        try {
+            for (int i=0, iSize=xarr.length; i<iSize; i++) {
+                d += (xarr[i] * xarr[i]) + (yarr[i] * yarr[i]);
+            }            
+        }
+        catch (ArrayIndexOutOfBoundsException ae) {
+            d = Double.NaN;
+        }
+        
+        return d;
+    }
+    
+
+    /**
+     * returns the sum of squares of difference of corresponding double 
+     * value in each subarray: ie. sigma ( (xarr[i]-yarr[i])^2 ) 
+     * <br/>
+     * It is the responsibility of the caller 
+     * to ensure that the two subarrays are of equal length. If the 
+     * subarrays are not of equal length, the return value can be 
+     * unpredictable.
+     * @param arrays
+     * @return
+     */
+    public static double sumxmy2(double[] xarr, double[] yarr) {
+        double d = 0;
+        
+        try {
+            for (int i=0, iSize=xarr.length; i<iSize; i++) {
+                double t = (xarr[i] - yarr[i]);
+                d += t * t;
+            }            
+        }
+        catch (ArrayIndexOutOfBoundsException ae) {
+            d = Double.NaN;
+        }
+        
+        return d;
+    }
+    
+    /**
+     * returns the total number of combinations possible when
+     * k items are chosen out of total of n items. If the number
+     * is too large, loss of precision may occur (since returned
+     * value is double). If the returned value is larger than
+     * Double.MAX_VALUE, Double.POSITIVE_INFINITY is returned.
+     * If either of the parameters is negative, Double.NaN is returned.
+     * @param n
+     * @param k
+     * @return
+     */
+    public static double nChooseK(int n, int k) {
+        double d = 1;
+        if (n<0 || k<0 || n<k) {
+            d= Double.NaN;
+        }
+        else {
+            int minnk = Math.min(n-k, k);
+            int maxnk = Math.max(n-k, k);
+            for (int i=maxnk; i<n; i++) {
+                d *= i+1;
+            }
+            d /= factorial(minnk);
+        }
+        
+        return d;
+    }
+    
+}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/MultiOperandNumericFunction.java
new file mode 100644 (file)
index 0000000..a3b4775
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Created on May 22, 2005
+ *
+ */
+package org.apache.poi.hssf.record.formula.functions;
+
+import org.apache.poi.hssf.record.formula.eval.AreaEval;
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.NumericValueEval;
+import org.apache.poi.hssf.record.formula.eval.Ref2DEval;
+import org.apache.poi.hssf.record.formula.eval.RefEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
+
+/**
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+ * This is the super class for all excel function evaluator
+ * classes that take variable number of operands, and
+ * where the order of operands does not matter
+ */
+public abstract class MultiOperandNumericFunction extends NumericFunction {
+
+    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
+        new ValueEvalToNumericXlator((short) (
+                  ValueEvalToNumericXlator.BOOL_IS_PARSED  
+                | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
+                | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
+              //| ValueEvalToNumericXlator.STRING_IS_PARSED  
+                | ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
+                | ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
+              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
+              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
+              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
+              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE  
+                ));
+    
+    private static final int DEFAULT_MAX_NUM_OPERANDS = 30;
+
+    /**
+     * this is the default impl for the factory method getXlator
+     * of the super class NumericFunction. Subclasses can override this method
+     * if they desire to return a different ValueEvalToNumericXlator instance
+     * than the default.
+     */
+    protected ValueEvalToNumericXlator getXlator() {
+        return DEFAULT_NUM_XLATOR;
+    }
+    
+    /**
+     * Maximum number of operands accepted by this function.
+     * Subclasses may override to change default value.
+     * @return
+     */
+    protected int getMaxNumOperands() {
+        return DEFAULT_MAX_NUM_OPERANDS;
+    }
+    
+    /**
+     * Returns a double array that contains values for the numeric cells
+     * from among the list of operands. Blanks and Blank equivalent cells
+     * are ignored. Error operands or cells containing operands of type
+     * that are considered invalid and would result in #VALUE! error in 
+     * excel cause this function to return null.
+     * 
+     * @param operands
+     * @param srcRow
+     * @param srcCol
+     * @return
+     */
+    protected double[] getNumberArray(Eval[] operands, int srcRow, short srcCol) {
+        double[] retval = new double[30];
+        int count = 0;
+        
+        outer: do { // goto simulator loop
+            if (operands.length > getMaxNumOperands()) {
+                break outer;
+            }
+            else {
+                for (int i=0, iSize=operands.length; i<iSize; i++) {
+                    double[] temp = getNumberArray(operands[i], srcRow, srcCol);
+                    if (temp == null) {
+                        retval = null; // error occurred.
+                        break;
+                    }
+                    retval = putInArray(retval, count, temp);
+                    count += temp.length;
+                }
+            }
+        } while (false); // end goto simulator loop
+        
+        if (retval != null) {
+            double[] temp = retval;
+            retval = new double[count];
+            System.arraycopy(temp, 0, retval, 0, count);
+        }
+        
+        return retval;
+    }
+    
+    /**
+     * Same as getNumberArray(Eval[], int, short) except that this
+     * takes Eval instead of Eval[].
+     * @param operand
+     * @param srcRow
+     * @param srcCol
+     * @return
+     */
+    protected double[] getNumberArray(Eval operand, int srcRow, short srcCol) {
+        double[] retval;
+        int count = 0;
+        
+        if (operand instanceof AreaEval) {
+            AreaEval ae = (AreaEval) operand;
+            ValueEval[] values = ae.getValues();
+            retval = new double[values.length];
+            for (int j=0, jSize=values.length; j<jSize; j++) {
+                /*
+                 * TODO: For an AreaEval, we are constructing a RefEval
+                 * per element.
+                 * For now this is a tempfix solution since this may
+                 * require a more generic fix at the level of
+                 * HSSFFormulaEvaluator where we store an array
+                 * of RefEvals as the "values" array. 
+                 */
+                RefEval re = (values[j] instanceof RefEval)
+                        ? new Ref2DEval(null, ((RefEval) values[j]).getInnerValueEval(), true)
+                        : new Ref2DEval(null, values[j], false);
+                ValueEval ve = singleOperandEvaluate(re, srcRow, srcCol);
+                
+                if (ve instanceof NumericValueEval) {
+                    NumericValueEval nve = (NumericValueEval) ve;
+                    retval = putInArray(retval, count++, nve.getNumberValue());
+                }
+                else if (ve instanceof BlankEval) {} // ignore operand
+                else {
+                    retval = null; // null => indicate to calling subclass that error occurred
+                    break;
+                }
+            }
+        }
+        else { // for ValueEvals other than AreaEval
+            retval = new double[1];
+            ValueEval ve = singleOperandEvaluate(operand, srcRow, srcCol);
+            
+            if (ve instanceof NumericValueEval) {
+                NumericValueEval nve = (NumericValueEval) ve;
+                retval = putInArray(retval, count++, nve.getNumberValue());
+            }
+            else if (ve instanceof BlankEval) {} // ignore operand
+            else {
+                retval = null; // null => indicate to calling subclass that error occurred
+            }
+        }
+        
+        if (retval != null && retval.length >= 1) {
+            double[] temp = retval;
+            retval = new double[count];
+            System.arraycopy(temp, 0, retval, 0, count);
+        }
+        
+        return retval;
+    }
+    
+    /**
+     * puts d at position pos in array arr. If pos is greater than arr, the 
+     * array is dynamically resized (using a simple doubling rule).
+     * @param arr
+     * @param pos
+     * @param d
+     * @return
+     */
+    private static double[] putInArray(double[] arr, int pos, double d) {
+        double[] tarr = arr;
+        while (pos >= arr.length) {
+            arr = new double[arr.length << 1];
+        }
+        if (tarr.length != arr.length) {
+            System.arraycopy(tarr, 0, arr, 0, tarr.length);
+        }
+        arr[pos] = d;
+        return arr;
+    }
+    
+    private static double[] putInArray(double[] arr, int pos, double[] d) {
+        double[] tarr = arr;
+        while (pos+d.length >= arr.length) {
+            arr = new double[arr.length << 1];
+        }
+        if (tarr.length != arr.length) {
+            System.arraycopy(tarr, 0, arr, 0, tarr.length);
+        }
+        for (int i=0, iSize=d.length; i<iSize; i++) {
+            arr[pos+i] = d[i];
+        }
+        return arr;
+    }
+    
+    protected static boolean areSubArraysConsistent(double[][] values) {
+        boolean retval = false;
+        
+        outer: do {
+            if (values != null && values.length > 0) {
+                if (values[0] == null)
+                    break outer;
+                int len = values[0].length;
+                for (int i=1, iSize=values.length; i<iSize; i++) {
+                    if (values[i] == null)
+                        break outer;
+                    int tlen = values[i].length;
+                    if (len != tlen) {
+                        break outer;
+                    }
+                }
+            }
+            retval = true;
+        } while (false);
+        return retval;
+    }
+    
+}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/StatsLib.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/StatsLib.java
new file mode 100644 (file)
index 0000000..f2ac347
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Created on May 30, 2005
+ *
+ */
+package org.apache.poi.hssf.record.formula.functions;
+
+import java.util.Arrays;
+
+/**
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+ *
+ * Library for common statistics functions
+ */
+public class StatsLib {
+
+    private StatsLib() {}
+
+
+    /**
+     * returns the mean of deviations from mean.
+     * @param v
+     * @return
+     */
+    public static double avedev(double[] v) {
+        double r = 0;
+        double m = 0;
+        double s = 0;
+        for (int i=0, iSize=v.length; i<iSize; i++) {
+            s += v[i];
+        }
+        m = s / v.length;
+        s = 0;
+        for (int i=0, iSize=v.length; i<iSize; i++) {
+            s += Math.abs(v[i]-m);
+        }
+        r = s / v.length;
+        return r;
+    }
+    
+    public static double stdev(double[] v) {
+        double r = Double.NaN;
+        if (v!=null && v.length > 1) {
+            r = Math.sqrt( devsq(v) / (v.length - 1) );
+        }
+        return r;
+    }
+    
+    /**
+     * if v is zero length or contains no duplicates, return value
+     * is Double.NaN. Else returns the value that occurs most times
+     * and if there is a tie, returns the first such value. 
+     * @param v
+     * @return
+     */
+    public static double mode(double[] v) {
+        double r = Double.NaN;
+        
+        // very naive impl, may need to be optimized
+        if (v!=null && v.length > 1) {
+            int[] counts = new int[v.length]; 
+            Arrays.fill(counts, 1);
+            for (int i=0, iSize=v.length; i<iSize; i++) {
+                for (int j=i+1, jSize=v.length; j<jSize; j++) {
+                    if (v[i] == v[j]) counts[i]++;
+                }
+            }
+            double maxv = 0;
+            int maxc = 0;
+            for (int i=0, iSize=counts.length; i<iSize; i++) {
+                if (counts[i] > maxc) {
+                    maxv = v[i];
+                    maxc = counts[i];
+                }
+            }
+            r = (maxc > 1) ? maxv : Double.NaN; // "no-dups" check
+        }
+        return r;
+    }
+    
+    public static double median(double[] v) {
+        double r = Double.NaN;
+        
+        if (v!=null && v.length >= 1) {
+            int n = v.length;
+            Arrays.sort(v);
+            r = (n % 2 == 0)
+                ? (v[n / 2] + v[n / 2 - 1]) / 2
+                : v[n / 2];
+        }
+        
+        return r;
+    }
+    
+    
+    public static double devsq(double[] v) {
+        double r = Double.NaN;
+        if (v!=null && v.length >= 1) {
+            double m = 0;
+            double s = 0;
+            int n = v.length;
+            for (int i=0; i<n; i++) {
+                s += v[i];
+            }
+            m = s / n;
+            s = 0;
+            for (int i=0; i<n; i++) {
+                s += (v[i]- m) * (v[i] - m);
+            }
+            
+            r = (n == 1)
+                    ? 0
+                    : s;
+        }
+        return r;
+    }
+    
+    /**
+     * returns the kth largest element in the array. Duplicates
+     * are considered as distinct values. Hence, eg.
+     * for array {1,2,4,3,3} & k=2, returned value is 3.
+     * <br/>
+     * k <= 0 & k >= v.length and null or empty arrays
+     * will result in return value Double.NaN
+     * @param v
+     * @param k
+     * @return
+     */
+    public static double kthLargest(double[] v, int k) {
+        double r = Double.NaN;
+        k--; // since arrays are 0-based
+        if (v!=null && v.length > k && k >= 0) {
+            Arrays.sort(v);
+            r = v[v.length-k-1];
+        }
+        return r;
+    }
+
+    /**
+     * returns the kth smallest element in the array. Duplicates
+     * are considered as distinct values. Hence, eg.
+     * for array {1,1,2,4,3,3} & k=2, returned value is 1.
+     * <br/>
+     * k <= 0 & k >= v.length or null array or empty array
+     * will result in return value Double.NaN
+     * @param v
+     * @param k
+     * @return
+     */
+    public static double kthSmallest(double[] v, int k) {
+        double r = Double.NaN;
+        k--; // since arrays are 0-based
+        if (v!=null && v.length > k && k >= 0) {
+            Arrays.sort(v);
+            r = v[k];
+        }
+        return r;
+    }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/TextFunction.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/TextFunction.java
new file mode 100644 (file)
index 0000000..860b74c
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Created on May 22, 2005
+ *
+ */
+package org.apache.poi.hssf.record.formula.functions;
+
+import org.apache.poi.hssf.record.formula.eval.AreaEval;
+import org.apache.poi.hssf.record.formula.eval.BlankEval;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.RefEval;
+import org.apache.poi.hssf.record.formula.eval.StringValueEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+
+/**
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+ *
+ */
+public abstract class TextFunction implements Function {
+    
+    protected static final String EMPTY_STRING = "";
+    
+    protected ValueEval singleOperandEvaluate(Eval eval, int srcRow, short srcCol) {
+        ValueEval retval;
+        if (eval instanceof AreaEval) {
+            AreaEval ae = (AreaEval) eval;
+            if (ae.contains(srcRow, srcCol)) { // circular ref!
+                retval = ErrorEval.CIRCULAR_REF_ERROR;
+            }
+            else if (ae.isRow()) {
+                if (ae.containsColumn(srcCol)) {
+                    ValueEval ve = ae.getValueAt(ae.getFirstRow(), srcCol);
+                    retval = attemptXlateToText(ve);
+                }
+                else {
+                    retval = ErrorEval.VALUE_INVALID;
+                }
+            }
+            else if (ae.isColumn()) {
+                if (ae.containsRow(srcRow)) {
+                    ValueEval ve = ae.getValueAt(srcRow, ae.getFirstColumn());
+                    retval = attemptXlateToText(ve);
+                }
+                else {
+                    retval = ErrorEval.VALUE_INVALID;
+                }
+            }
+            else {
+                retval = ErrorEval.VALUE_INVALID;
+            }
+        }
+        else {
+            retval = attemptXlateToText((ValueEval) eval);
+        }
+        return retval;
+    }
+
+    
+    /**
+     * converts from Different ValueEval types to StringEval.
+     * Note: AreaEvals are not handled, if arg is an AreaEval,
+     * the returned value is ErrorEval.VALUE_INVALID
+     * @param ve
+     * @return
+     */
+    protected ValueEval attemptXlateToText(ValueEval ve) {
+        ValueEval retval;
+        if (ve instanceof StringValueEval) {
+            retval = ve;
+        }
+        else if (ve instanceof RefEval) {
+            RefEval re = (RefEval) ve;
+            ValueEval ive = re.getInnerValueEval();
+            if (ive instanceof StringValueEval) {
+                retval = ive;
+            }
+            else if (ive instanceof BlankEval) {
+                retval = ive;
+            }
+            else {
+                retval = ErrorEval.VALUE_INVALID;
+            }
+        }
+        else if (ve instanceof BlankEval) {
+            retval = ve;
+        }
+        else {
+            retval = ErrorEval.VALUE_INVALID;
+        }
+        return retval;
+    }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java b/src/scratchpad/src/org/apache/poi/hssf/record/formula/functions/XYNumericFunction.java
new file mode 100644 (file)
index 0000000..6ab874a
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Created on May 29, 2005
+ *
+ */
+package org.apache.poi.hssf.record.formula.functions;
+
+import org.apache.poi.hssf.record.formula.eval.AreaEval;
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.Eval;
+import org.apache.poi.hssf.record.formula.eval.NumberEval;
+import org.apache.poi.hssf.record.formula.eval.RefEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEval;
+import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
+
+/**
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+ *
+ */
+public abstract class XYNumericFunction extends NumericFunction {
+    protected static final int X = 0;
+    protected static final int Y = 1;
+
+    private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
+        new ValueEvalToNumericXlator((short) (
+                  ValueEvalToNumericXlator.BOOL_IS_PARSED  
+                | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED  
+                | ValueEvalToNumericXlator.EVALUATED_REF_BOOL_IS_PARSED  
+              //| ValueEvalToNumericXlator.STRING_IS_PARSED  
+                | ValueEvalToNumericXlator.REF_STRING_IS_PARSED  
+                | ValueEvalToNumericXlator.EVALUATED_REF_STRING_IS_PARSED  
+              //| ValueEvalToNumericXlator.STRING_TO_BOOL_IS_PARSED  
+              //| ValueEvalToNumericXlator.REF_STRING_TO_BOOL_IS_PARSED  
+              //| ValueEvalToNumericXlator.STRING_IS_INVALID_VALUE  
+              //| ValueEvalToNumericXlator.REF_STRING_IS_INVALID_VALUE  
+                ));
+
+    /**
+     * this is the default impl for the factory method getXlator
+     * of the super class NumericFunction. Subclasses can override this method
+     * if they desire to return a different ValueEvalToNumericXlator instance
+     * than the default.
+     */
+    protected ValueEvalToNumericXlator getXlator() {
+        return DEFAULT_NUM_XLATOR;
+    }
+    
+    protected int getMaxNumOperands() {
+        return 30;
+    }
+
+    /**
+     * Returns a double array that contains values for the numeric cells
+     * from among the list of operands. Blanks and Blank equivalent cells
+     * are ignored. Error operands or cells containing operands of type
+     * that are considered invalid and would result in #VALUE! error in 
+     * excel cause this function to return null.
+     * 
+     * @param operands
+     * @param srcRow
+     * @param srcCol
+     * @return
+     */
+    protected double[][] getNumberArray(Eval[] xops, Eval[] yops, int srcRow, short srcCol) {
+        double[][] retval = new double[2][30];
+        int count = 0;
+        
+        if (xops.length > getMaxNumOperands() 
+                || yops.length > getMaxNumOperands()
+                || xops.length != yops.length) {
+            retval = null;
+        }
+        else {
+            
+            for (int i=0, iSize=xops.length; i<iSize; i++) {
+                Eval xEval = xops[i];
+                Eval yEval = yops[i];
+                
+                if (isNumberEval(xEval) && isNumberEval(yEval)) {
+                    retval[X] = ensureCapacity(retval[X], count);
+                    retval[Y] = ensureCapacity(retval[Y], count);
+                    retval[X][count] = getDoubleValue(xEval);
+                    retval[Y][count] = getDoubleValue(yEval);
+                    if (Double.isNaN(retval[X][count]) || Double.isNaN(retval[Y][count])) {
+                        retval = null;
+                        break;
+                    }
+                    count++;
+                }
+            }
+        }
+        
+        if (retval != null) {
+            double[][] temp = retval;
+            retval[X] = trimToSize(retval[X], count);
+            retval[Y] = trimToSize(retval[Y], count);
+        }
+        
+        return retval;
+    }
+    
+    protected double[][] getValues(Eval[] operands, int srcCellRow, short srcCellCol) {
+        double[][] retval = null;
+        
+        outer: do {
+            if (operands.length == 2) {
+                Eval[] xEvals = new Eval[1];
+                Eval[] yEvals = new Eval[1];
+                if (operands[X] instanceof AreaEval) {
+                    AreaEval ae = (AreaEval) operands[0];
+                    xEvals = ae.getValues();
+                }
+                else if (operands[X] instanceof ErrorEval) {
+                    break outer;
+                }
+                else {
+                    xEvals[0] = operands[X];
+                }
+                
+                if (operands[Y] instanceof AreaEval) {
+                    AreaEval ae = (AreaEval) operands[Y];
+                    yEvals = ae.getValues();
+                }
+                else if (operands[Y] instanceof ErrorEval) {
+                    break outer;
+                }
+                else {
+                    yEvals[0] = operands[Y];
+                }
+                
+                retval = getNumberArray(xEvals, yEvals, srcCellRow, srcCellCol);
+            }
+        } while (false);
+        
+        return retval;
+    }
+    
+
+    protected static double[] ensureCapacity(double[] arr, int pos) {
+        double[] temp = arr;
+        while (pos >= arr.length) {
+            arr = new double[arr.length << 2];
+        }
+        if (temp.length != arr.length)
+            System.arraycopy(temp, 0, arr, 0, temp.length);
+        return arr;
+    }
+    
+    protected static double[] trimToSize(double[] arr, int len) {
+        double[] tarr = arr;
+        if (arr.length > len) {
+            tarr = new double[len];
+            System.arraycopy(arr, 0, tarr, 0, len);
+        }
+        return tarr;
+    }
+    
+    protected static boolean isNumberEval(Eval eval) {
+        boolean retval = false;
+        
+        if (eval instanceof NumberEval) {
+            retval = true;
+        }
+        else if (eval instanceof RefEval) {
+            RefEval re = (RefEval) eval;
+            ValueEval ve = re.getInnerValueEval();
+            retval = (ve instanceof NumberEval);
+        }
+        
+        return retval;
+    }
+    
+    protected static double getDoubleValue(Eval eval) {
+        double retval = 0;
+        if (eval instanceof NumberEval) {
+            NumberEval ne = (NumberEval) eval;
+            retval = ne.getNumberValue();
+        }
+        else if (eval instanceof RefEval) {
+            RefEval re = (RefEval) eval;
+            ValueEval ve = re.getInnerValueEval();
+                retval = (ve instanceof NumberEval)
+                    ? ((NumberEval) ve).getNumberValue()
+                    : Double.NaN;
+        }
+        else if (eval instanceof ErrorEval) {
+            retval = Double.NaN;
+        }
+        return retval;
+    }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AbstractNumericTestCase.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/AbstractNumericTestCase.java
new file mode 100644 (file)
index 0000000..76cc19f
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Created on May 29, 2005
+ *
+ */
+package org.apache.poi.hssf.record.formula.functions;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+ *
+ */
+public abstract class AbstractNumericTestCase extends TestCase {
+
+    public static final double POS_ZERO = 1E-4;
+    public static final double DIFF_TOLERANCE_FACTOR = 1E-8;
+
+    public void setUp() {
+    }
+
+    public void tearDown() {
+    }
+
+    /**
+     * Why doesnt JUnit have a method like this for doubles? 
+     * The current impl (3.8.1) of Junit has a retar*** method
+     * for comparing doubles. DO NOT use that.
+     * TODO: This class should really be in an abstract super class
+     * to avoid code duplication across this project.
+     * @param message
+     * @param baseval
+     * @param checkval
+     */
+    public static void assertEquals(String message, double baseval, double checkval, double almostZero, double diffToleranceFactor) {
+        double posZero = Math.abs(almostZero);
+        double negZero = -1 * posZero;
+        if (Double.isNaN(baseval)) {
+            assertTrue(message+": Expected " + baseval + " but was " + checkval
+                    , Double.isNaN(baseval));
+        }
+        else if (Double.isInfinite(baseval)) {
+            assertTrue(message+": Expected " + baseval + " but was " + checkval
+                    , Double.isInfinite(baseval) && ((baseval<0) == (checkval<0)));
+        }
+        else {
+            assertTrue(message+": Expected " + baseval + " but was " + checkval
+                ,baseval != 0
+                    ? Math.abs(baseval - checkval) <= Math.abs(diffToleranceFactor * baseval)
+                    : checkval < posZero && checkval > negZero);
+        }
+    }
+
+    public static void assertEquals(String msg, double baseval, double checkval) {
+        assertEquals(msg, baseval, checkval, POS_ZERO, DIFF_TOLERANCE_FACTOR);
+    }
+
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestFinanceLib.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestFinanceLib.java
new file mode 100644 (file)
index 0000000..b8bffc2
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Created on May 23, 2005
+ *
+ */
+package org.apache.poi.hssf.record.formula.functions;
+
+
+/**
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+ *
+ */
+public class TestFinanceLib extends AbstractNumericTestCase {
+
+    public void testFv() {
+        double f, r, y, p, x;
+        int n;
+        boolean t = false;
+        
+        r = 1; n = 10; y = 100; p = 10000; t = false;
+        f = FinanceLib.fv(r, n, y, p, t);
+        x = -10342300;
+        assertEquals("fv ", x, f);
+        
+        r = 1; n = 10; y = 100; p = 10000; t = true;
+        f = FinanceLib.fv(r, n, y, p, t);
+        x = -10444600;
+        assertEquals("fv ", x, f);
+        
+        r = 2; n = 12; y = 120; p = 12000; t = false;
+        f = FinanceLib.fv(r, n, y, p, t);
+        x = -6409178400d;
+        assertEquals("fv ", x, f);
+        
+        r = 2; n = 12; y = 120; p = 12000; t = true;
+        f = FinanceLib.fv(r, n, y, p, t);
+        x = -6472951200d;
+        assertEquals("fv ", x, f);
+        
+        // cross tests with pv
+        r = 2.95; n = 13; y = 13000; p = -4406.78544294496; t = false;
+        f = FinanceLib.fv(r, n, y, p, t);
+        x = 333891.230010986; // as returned by excel
+        assertEquals("fv ", x, f);
+        
+        r = 2.95; n = 13; y = 13000; p = -17406.7852148156; t = true;
+        f = FinanceLib.fv(r, n, y, p, t);
+        x = 333891.230102539; // as returned by excel
+        assertEquals("fv ", x, f);
+        
+    }
+    public void testNpv() {
+        double r, v[], npv, x;
+        
+        r = 1; v = new double[]{100, 200, 300, 400};
+        npv = FinanceLib.npv(r, v);
+        x = 162.5;
+        assertEquals("npv ", x, npv);
+        
+        r = 2.5; v = new double[]{1000, 666.66666, 333.33, 12.2768416};
+        npv = FinanceLib.npv(r, v);
+        x = 347.99232604144827;
+        assertEquals("npv ", x, npv);
+        
+        r = 12.33333; v = new double[]{1000, 0, -900, -7777.5765};
+        npv = FinanceLib.npv(r, v);
+        x = 74.3742433377061;
+        assertEquals("npv ", x, npv);
+        
+        r = 0.05; v = new double[]{200000, 300000.55, 400000, 1000000, 6000000, 7000000, -300000};
+        npv = FinanceLib.npv(r, v);
+        x = 11342283.4233124;
+        assertEquals("npv ", x, npv);
+    }
+    public void testPmt() {
+        double f, r, y, p, x;
+        int n;
+        boolean t = false;
+        
+        // cross check with pv
+        r = 1; n = 10; p = -109.66796875; f = 10000; t = false;
+        y = FinanceLib.pmt(r, n, p, f, t);
+        x = 100;
+        assertEquals("pmt ", x, y);    
+        
+        r = 1; n = 10; p = -209.5703125; f = 10000; t = true;
+        y = FinanceLib.pmt(r, n, p, f, t);
+        x = 100;
+        assertEquals("pmt ", x, y);
+        
+        // cross check with fv
+        r = 2; n = 12; f = -6409178400d; p = 12000; t = false;
+        y = FinanceLib.pmt(r, n, p, f, t);
+        x = 120;
+        assertEquals("pmt ", x, y);    
+        
+        r = 2; n = 12; f = -6472951200d; p = 12000; t = true;
+        y = FinanceLib.pmt(r, n, p, f, t);
+        x = 120;
+        assertEquals("pmt ", x, y);
+    }
+    
+    public void testPv() {
+        double f, r, y, p, x;
+        int n;
+        boolean t = false;
+        
+        r = 1; n = 10; y = 100; f = 10000; t = false;
+        p = FinanceLib.pv(r, n, y, f, t);
+        x = -109.66796875;
+        assertEquals("pv ", x, p);    
+        
+        r = 1; n = 10; y = 100; f = 10000; t = true;
+        p = FinanceLib.pv(r, n, y, f, t);
+        x = -209.5703125;
+        assertEquals("pv ", x, p);    
+        
+        r = 2.95; n = 13; y = 13000; f = 333891.23; t = false;
+        p = FinanceLib.pv(r, n, y, f, t);
+        x = -4406.78544294496;
+        assertEquals("pv ", x, p);
+        
+        r = 2.95; n = 13; y = 13000; f = 333891.23; t = true;
+        p = FinanceLib.pv(r, n, y, f, t);
+        x = -17406.7852148156;
+        assertEquals("pv ", x, p);
+        
+        // cross tests with fv
+        r = 2; n = 12; y = 120; f = -6409178400d; t = false;
+        p = FinanceLib.pv(r, n, y, f, t);
+        x = 12000;
+        assertEquals("pv ", x, p);
+        
+        r = 2; n = 12; y = 120; f = -6472951200d; t = true;
+        p = FinanceLib.pv(r, n, y, f, t);
+        x = 12000; 
+        assertEquals("pv ", x, p);
+        
+    }
+    
+    public void testNper() {
+        double f, r, y, p, x;
+        int n;
+        boolean t = false;
+        
+        // cross check with pv
+        r = 1; y = 100; p = -109.66796875; f = 10000; t = false;
+        n = FinanceLib.nper(r, y, p, f, t);
+        x = 10;
+        assertEquals("nper ", x, n);    
+        
+        r = 1; y = 100; p = -209.5703125; f = 10000; t = true;
+        n = FinanceLib.nper(r, y, p, f, t);
+        x = 10;
+        assertEquals("nper ", x, n);
+        
+        // cross check with fv
+        r = 2; y = 120; f = -6409178400d; p = 12000; t = false;
+        n = FinanceLib.nper(r, y, p, f, t);
+        x = 12;
+        assertEquals("nper ", x, n);    
+        
+        r = 2; y = 120; f = -6472951200d; p = 12000; t = true;
+        n = FinanceLib.nper(r, y, p, f, t);
+        x = 12; 
+        assertEquals("nper ", x, n);
+    }
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestMathX.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestMathX.java
new file mode 100644 (file)
index 0000000..baf8234
--- /dev/null
@@ -0,0 +1,893 @@
+/*
+ * Created on May 23, 2005
+ *
+ */
+package org.apache.poi.hssf.record.formula.functions;
+
+
+/**
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+ *  
+ */
+public class TestMathX extends AbstractNumericTestCase {
+
+    public void testAcosh() {
+        double d = 0;
+
+        d = MathX.acosh(0);
+        assertTrue("Acosh 0 is NaN", Double.isNaN(d));
+
+        d = MathX.acosh(1);
+        assertEquals("Acosh 1 ", 0, d);
+
+        d = MathX.acosh(-1);
+        assertTrue("Acosh -1 is NaN", Double.isNaN(d));
+
+        d = MathX.acosh(100);
+        assertEquals("Acosh 100 ", 5.298292366d, d);
+
+        d = MathX.acosh(101.001);
+        assertEquals("Acosh 101.001 ", 5.308253091d, d);
+
+        d = MathX.acosh(200000);
+        assertEquals("Acosh 200000 ", 12.89921983d, d);
+
+    }
+
+    public void testAsinh() {
+        double d = 0;
+
+        d = MathX.asinh(0);
+        assertEquals("asinh 0", d, 0);
+
+        d = MathX.asinh(1);
+        assertEquals("asinh 1 ", 0.881373587, d);
+
+        d = MathX.asinh(-1);
+        assertEquals("asinh -1 ", -0.881373587, d);
+
+        d = MathX.asinh(-100);
+        assertEquals("asinh -100 ", -5.298342366, d);
+
+        d = MathX.asinh(100);
+        assertEquals("asinh 100 ", 5.298342366, d);
+
+        d = MathX.asinh(200000);
+        assertEquals("asinh 200000", 12.899219826096400, d);
+
+        d = MathX.asinh(-200000);
+        assertEquals("asinh -200000 ", -12.899223853137, d);
+
+    }
+
+    public void testAtanh() {
+        double d = 0;
+        d = MathX.atanh(0);
+        assertEquals("atanh 0", d, 0);
+
+        d = MathX.atanh(1);
+        assertEquals("atanh 1 ", Double.POSITIVE_INFINITY, d);
+
+        d = MathX.atanh(-1);
+        assertEquals("atanh -1 ", Double.NEGATIVE_INFINITY, d);
+
+        d = MathX.atanh(-100);
+        assertEquals("atanh -100 ", Double.NaN, d);
+
+        d = MathX.atanh(100);
+        assertEquals("atanh 100 ", Double.NaN, d);
+
+        d = MathX.atanh(200000);
+        assertEquals("atanh 200000", Double.NaN, d);
+
+        d = MathX.atanh(-200000);
+        assertEquals("atanh -200000 ", Double.NaN, d);
+
+        d = MathX.atanh(0.1);
+        assertEquals("atanh 0.1", 0.100335348, d);
+
+        d = MathX.atanh(-0.1);
+        assertEquals("atanh -0.1 ", -0.100335348, d);
+
+    }
+
+    public void testCosh() {
+        double d = 0;
+        d = MathX.cosh(0);
+        assertEquals("cosh 0", 1, d);
+
+        d = MathX.cosh(1);
+        assertEquals("cosh 1 ", 1.543080635, d);
+
+        d = MathX.cosh(-1);
+        assertEquals("cosh -1 ", 1.543080635, d);
+
+        d = MathX.cosh(-100);
+        assertEquals("cosh -100 ", 1.344058570908070E+43, d);
+
+        d = MathX.cosh(100);
+        assertEquals("cosh 100 ", 1.344058570908070E+43, d);
+
+        d = MathX.cosh(15);
+        assertEquals("cosh 15", 1634508.686, d);
+
+        d = MathX.cosh(-15);
+        assertEquals("cosh -15 ", 1634508.686, d);
+
+        d = MathX.cosh(0.1);
+        assertEquals("cosh 0.1", 1.005004168, d);
+
+        d = MathX.cosh(-0.1);
+        assertEquals("cosh -0.1 ", 1.005004168, d);
+
+    }
+
+    public void testTanh() {
+        double d = 0;
+        d = MathX.tanh(0);
+        assertEquals("tanh 0", 0, d);
+
+        d = MathX.tanh(1);
+        assertEquals("tanh 1 ", 0.761594156, d);
+
+        d = MathX.tanh(-1);
+        assertEquals("tanh -1 ", -0.761594156, d);
+
+        d = MathX.tanh(-100);
+        assertEquals("tanh -100 ", -1, d);
+
+        d = MathX.tanh(100);
+        assertEquals("tanh 100 ", 1, d);
+
+        d = MathX.tanh(15);
+        assertEquals("tanh 15", 1, d);
+
+        d = MathX.tanh(-15);
+        assertEquals("tanh -15 ", -1, d);
+
+        d = MathX.tanh(0.1);
+        assertEquals("tanh 0.1", 0.099667995, d);
+
+        d = MathX.tanh(-0.1);
+        assertEquals("tanh -0.1 ", -0.099667995, d);
+
+    }
+
+    public void testMax() {
+        double[] d = new double[100];
+        d[0] = 1.1;     d[1] = 2.1;     d[2] = 3.1;     d[3] = 4.1; 
+        d[4] = 5.1;     d[5] = 6.1;     d[6] = 7.1;     d[7] = 8.1;
+        d[8] = 9.1;     d[9] = 10.1;    d[10] = 11.1;   d[11] = 12.1;
+        d[12] = 13.1;   d[13] = 14.1;   d[14] = 15.1;   d[15] = 16.1;
+        d[16] = 17.1;   d[17] = 18.1;   d[18] = 19.1;   d[19] = 20.1; 
+        
+        double m = MathX.max(d);
+        assertEquals("Max ", 20.1, m);
+        
+        d = new double[1000];
+        m = MathX.max(d);
+        assertEquals("Max ", 0, m);
+        
+        d[0] = -1.1;     d[1] = 2.1;     d[2] = -3.1;     d[3] = 4.1; 
+        d[4] = -5.1;     d[5] = 6.1;     d[6] = -7.1;     d[7] = 8.1;
+        d[8] = -9.1;     d[9] = 10.1;    d[10] = -11.1;   d[11] = 12.1;
+        d[12] = -13.1;   d[13] = 14.1;   d[14] = -15.1;   d[15] = 16.1;
+        d[16] = -17.1;   d[17] = 18.1;   d[18] = -19.1;   d[19] = 20.1; 
+        m = MathX.max(d);
+        assertEquals("Max ", 20.1, m);
+        
+        d = new double[20];
+        d[0] = -1.1;     d[1] = -2.1;     d[2] = -3.1;     d[3] = -4.1; 
+        d[4] = -5.1;     d[5] = -6.1;     d[6] = -7.1;     d[7] = -8.1;
+        d[8] = -9.1;     d[9] = -10.1;    d[10] = -11.1;   d[11] = -12.1;
+        d[12] = -13.1;   d[13] = -14.1;   d[14] = -15.1;   d[15] = -16.1;
+        d[16] = -17.1;   d[17] = -18.1;   d[18] = -19.1;   d[19] = -20.1; 
+        m = MathX.max(d);
+        assertEquals("Max ", -1.1, m);
+        
+    }
+
+    public void testMin() {
+        double[] d = new double[100];
+        d[0] = 1.1;     d[1] = 2.1;     d[2] = 3.1;     d[3] = 4.1; 
+        d[4] = 5.1;     d[5] = 6.1;     d[6] = 7.1;     d[7] = 8.1;
+        d[8] = 9.1;     d[9] = 10.1;    d[10] = 11.1;   d[11] = 12.1;
+        d[12] = 13.1;   d[13] = 14.1;   d[14] = 15.1;   d[15] = 16.1;
+        d[16] = 17.1;   d[17] = 18.1;   d[18] = 19.1;   d[19] = 20.1; 
+        
+        double m = MathX.min(d);
+        assertEquals("Min ", 0, m);
+        
+        d = new double[20];
+        d[0] = 1.1;     d[1] = 2.1;     d[2] = 3.1;     d[3] = 4.1; 
+        d[4] = 5.1;     d[5] = 6.1;     d[6] = 7.1;     d[7] = 8.1;
+        d[8] = 9.1;     d[9] = 10.1;    d[10] = 11.1;   d[11] = 12.1;
+        d[12] = 13.1;   d[13] = 14.1;   d[14] = 15.1;   d[15] = 16.1;
+        d[16] = 17.1;   d[17] = 18.1;   d[18] = 19.1;   d[19] = 20.1; 
+        
+        m = MathX.min(d);
+        assertEquals("Min ", 1.1, m);
+        
+        d = new double[1000];
+        m = MathX.min(d);
+        assertEquals("Min ", 0, m);
+        
+        d[0] = -1.1;     d[1] = 2.1;     d[2] = -3.1;     d[3] = 4.1; 
+        d[4] = -5.1;     d[5] = 6.1;     d[6] = -7.1;     d[7] = 8.1;
+        d[8] = -9.1;     d[9] = 10.1;    d[10] = -11.1;   d[11] = 12.1;
+        d[12] = -13.1;   d[13] = 14.1;   d[14] = -15.1;   d[15] = 16.1;
+        d[16] = -17.1;   d[17] = 18.1;   d[18] = -19.1;   d[19] = 20.1; 
+        m = MathX.min(d);
+        assertEquals("Min ", -19.1, m);
+        
+        d = new double[20];
+        d[0] = -1.1;     d[1] = -2.1;     d[2] = -3.1;     d[3] = -4.1; 
+        d[4] = -5.1;     d[5] = -6.1;     d[6] = -7.1;     d[7] = -8.1;
+        d[8] = -9.1;     d[9] = -10.1;    d[10] = -11.1;   d[11] = -12.1;
+        d[12] = -13.1;   d[13] = -14.1;   d[14] = -15.1;   d[15] = -16.1;
+        d[16] = -17.1;   d[17] = -18.1;   d[18] = -19.1;   d[19] = -20.1; 
+        m = MathX.min(d);
+        assertEquals("Min ", -20.1, m);
+    }
+
+    public void testProduct() {
+        double[] d = new double[100];
+        d[0] = 1.1;     d[1] = 2.1;     d[2] = 3.1;     d[3] = 4.1; 
+        d[4] = 5.1;     d[5] = 6.1;     d[6] = 7.1;     d[7] = 8.1;
+        d[8] = 9.1;     d[9] = 10.1;    d[10] = 11.1;   d[11] = 12.1;
+        d[12] = 13.1;   d[13] = 14.1;   d[14] = 15.1;   d[15] = 16.1;
+        d[16] = 17.1;   d[17] = 18.1;   d[18] = 19.1;   d[19] = 20.1; 
+        
+        double m = MathX.min(d);
+        assertEquals("Min ", 0, m);
+        
+        d = new double[20];
+        d[0] = 1.1;     d[1] = 2.1;     d[2] = 3.1;     d[3] = 4.1; 
+        d[4] = 5.1;     d[5] = 6.1;     d[6] = 7.1;     d[7] = 8.1;
+        d[8] = 9.1;     d[9] = 10.1;    d[10] = 11.1;   d[11] = 12.1;
+        d[12] = 13.1;   d[13] = 14.1;   d[14] = 15.1;   d[15] = 16.1;
+        d[16] = 17.1;   d[17] = 18.1;   d[18] = 19.1;   d[19] = 20.1; 
+        
+        m = MathX.min(d);
+        assertEquals("Min ", 1.1, m);
+        
+        d = new double[1000];
+        m = MathX.min(d);
+        assertEquals("Min ", 0, m);
+        
+        d[0] = -1.1;     d[1] = 2.1;     d[2] = -3.1;     d[3] = 4.1; 
+        d[4] = -5.1;     d[5] = 6.1;     d[6] = -7.1;     d[7] = 8.1;
+        d[8] = -9.1;     d[9] = 10.1;    d[10] = -11.1;   d[11] = 12.1;
+        d[12] = -13.1;   d[13] = 14.1;   d[14] = -15.1;   d[15] = 16.1;
+        d[16] = -17.1;   d[17] = 18.1;   d[18] = -19.1;   d[19] = 20.1; 
+        m = MathX.min(d);
+        assertEquals("Min ", -19.1, m);
+        
+        d = new double[20];
+        d[0] = -1.1;     d[1] = -2.1;     d[2] = -3.1;     d[3] = -4.1; 
+        d[4] = -5.1;     d[5] = -6.1;     d[6] = -7.1;     d[7] = -8.1;
+        d[8] = -9.1;     d[9] = -10.1;    d[10] = -11.1;   d[11] = -12.1;
+        d[12] = -13.1;   d[13] = -14.1;   d[14] = -15.1;   d[15] = -16.1;
+        d[16] = -17.1;   d[17] = -18.1;   d[18] = -19.1;   d[19] = -20.1; 
+        m = MathX.min(d);
+        assertEquals("Min ", -20.1, m);
+    }
+
+    public void testMod() {
+    }
+
+    public void testNChooseK() {
+        int n=100;
+        int k=50;
+        double d = MathX.nChooseK(n, k);
+        assertEquals("NChooseK ", 1.00891344545564E29, d);
+        
+        n = -1; k = 1;
+        d = MathX.nChooseK(n, k);
+        assertEquals("NChooseK ", Double.NaN, d);
+        
+        n = 1; k = -1;
+        d = MathX.nChooseK(n, k);
+        assertEquals("NChooseK ", Double.NaN, d);
+        
+        n = 0; k = 1;
+        d = MathX.nChooseK(n, k);
+        assertEquals("NChooseK ", Double.NaN, d);
+        
+        n = 1; k = 0;
+        d = MathX.nChooseK(n, k);
+        assertEquals("NChooseK ", 1, d);
+        
+        n = 10; k = 9;
+        d = MathX.nChooseK(n, k);
+        assertEquals("NChooseK ", 10, d);
+        
+        n = 10; k = 10;
+        d = MathX.nChooseK(n, k);
+        assertEquals("NChooseK ", 1, d);
+        
+        n = 10; k = 1;
+        d = MathX.nChooseK(n, k);
+        assertEquals("NChooseK ", 10, d);
+        
+        n = 1000; k = 1;
+        d = MathX.nChooseK(n, k);
+        assertEquals("NChooseK ", 1000, d); // awesome ;)
+        
+        n = 1000; k = 2;
+        d = MathX.nChooseK(n, k);
+        assertEquals("NChooseK ", 499500, d); // awesome ;)
+        
+        n = 13; k = 7;
+        d = MathX.nChooseK(n, k);
+        assertEquals("NChooseK ", 1716, d);
+        
+    }
+
+    public void testSign() {
+        final short minus = -1;
+        final short zero = 0;
+        final short plus = 1;
+        double d = 0;
+        
+        
+        assertEquals("Sign ", minus, MathX.sign(minus));
+        assertEquals("Sign ", plus, MathX.sign(plus));
+        assertEquals("Sign ", zero, MathX.sign(zero));
+        
+        d = 0;
+        assertEquals("Sign ", zero, MathX.sign(d));
+        
+        d = -1.000001;
+        assertEquals("Sign ", minus, MathX.sign(d));
+        
+        d = -.000001;
+        assertEquals("Sign ", minus, MathX.sign(d));
+        
+        d = -1E-200;
+        assertEquals("Sign ", minus, MathX.sign(d));
+        
+        d = Double.NEGATIVE_INFINITY;
+        assertEquals("Sign ", minus, MathX.sign(d));
+        
+        d = -200.11;
+        assertEquals("Sign ", minus, MathX.sign(d));
+        
+        d = -2000000000000.11;
+        assertEquals("Sign ", minus, MathX.sign(d));
+        
+        d = 1.000001;
+        assertEquals("Sign ", plus, MathX.sign(d));
+        
+        d = .000001;
+        assertEquals("Sign ", plus, MathX.sign(d));
+        
+        d = 1E-200;
+        assertEquals("Sign ", plus, MathX.sign(d));
+        
+        d = Double.POSITIVE_INFINITY;
+        assertEquals("Sign ", plus, MathX.sign(d));
+        
+        d = 200.11;
+        assertEquals("Sign ", plus, MathX.sign(d));
+        
+        d = 2000000000000.11;
+        assertEquals("Sign ", plus, MathX.sign(d));
+        
+    }
+
+    public void testSinh() {
+        double d = 0;
+        d = MathX.sinh(0);
+        assertEquals("sinh 0", 0, d);
+
+        d = MathX.sinh(1);
+        assertEquals("sinh 1 ", 1.175201194, d);
+
+        d = MathX.sinh(-1);
+        assertEquals("sinh -1 ", -1.175201194, d);
+
+        d = MathX.sinh(-100);
+        assertEquals("sinh -100 ", -1.344058570908070E+43, d);
+
+        d = MathX.sinh(100);
+        assertEquals("sinh 100 ", 1.344058570908070E+43, d);
+
+        d = MathX.sinh(15);
+        assertEquals("sinh 15", 1634508.686, d);
+
+        d = MathX.sinh(-15);
+        assertEquals("sinh -15 ", -1634508.686, d);
+
+        d = MathX.sinh(0.1);
+        assertEquals("sinh 0.1", 0.10016675, d);
+
+        d = MathX.sinh(-0.1);
+        assertEquals("sinh -0.1 ", -0.10016675, d);
+
+    }
+
+    public void testSum() {
+        double[] d = new double[100];
+        d[0] = 1.1;     d[1] = 2.1;     d[2] = 3.1;     d[3] = 4.1; 
+        d[4] = 5.1;     d[5] = 6.1;     d[6] = 7.1;     d[7] = 8.1;
+        d[8] = 9.1;     d[9] = 10.1;    d[10] = 11.1;   d[11] = 12.1;
+        d[12] = 13.1;   d[13] = 14.1;   d[14] = 15.1;   d[15] = 16.1;
+        d[16] = 17.1;   d[17] = 18.1;   d[18] = 19.1;   d[19] = 20.1; 
+        
+        double s = MathX.sum(d);
+        assertEquals("Sum ", 212, s);
+        
+        d = new double[1000];
+        s = MathX.sum(d);
+        assertEquals("Sum ", 0, s);
+        
+        d[0] = -1.1;     d[1] = 2.1;     d[2] = -3.1;     d[3] = 4.1; 
+        d[4] = -5.1;     d[5] = 6.1;     d[6] = -7.1;     d[7] = 8.1;
+        d[8] = -9.1;     d[9] = 10.1;    d[10] = -11.1;   d[11] = 12.1;
+        d[12] = -13.1;   d[13] = 14.1;   d[14] = -15.1;   d[15] = 16.1;
+        d[16] = -17.1;   d[17] = 18.1;   d[18] = -19.1;   d[19] = 20.1; 
+        s = MathX.sum(d);
+        assertEquals("Sum ", 10, s);
+        
+        d[0] = -1.1;     d[1] = -2.1;     d[2] = -3.1;     d[3] = -4.1; 
+        d[4] = -5.1;     d[5] = -6.1;     d[6] = -7.1;     d[7] = -8.1;
+        d[8] = -9.1;     d[9] = -10.1;    d[10] = -11.1;   d[11] = -12.1;
+        d[12] = -13.1;   d[13] = -14.1;   d[14] = -15.1;   d[15] = -16.1;
+        d[16] = -17.1;   d[17] = -18.1;   d[18] = -19.1;   d[19] = -20.1; 
+        s = MathX.sum(d);
+        assertEquals("Sum ", -212, s);
+        
+    }
+
+    public void testSumproduct() {
+        double d = 0;
+        double[][] darr = new double[][]
+               {{0   ,0.11   ,23.23},
+                {1  ,0.22   ,46.46},
+                {2  ,0.33   ,69.69},
+                {3  ,0.44   ,92.92},
+                {4  ,0.55   ,116.15},
+                {5  ,0.66   ,139.38},
+                {6  ,0.77   ,162.61},
+                {7  ,0.88   ,185.84},
+                {8  ,0.99   ,209.07},
+                {9  ,1.1    ,232.3},
+                {10 ,1.21   ,255.53}};
+        d = MathX.sumproduct(darr);
+        assertEquals("Sumproduct ", 4.243234425E+22, d);
+        darr = new double[][]
+               {{0  ,0.11   ,23.23},
+                {0  ,0.22   ,46.46},
+                {0  ,0.33   ,69.69},
+                {0  ,0.44   ,92.92},
+                {0  ,0.55   ,116.15},
+                {0  ,0.66   ,139.38},
+                {0  ,0.77   ,162.61},
+                {0  ,0.88   ,185.84},
+                {0  ,0.99   ,209.07},
+                {0  ,1.1    ,232.3},
+                {0  ,1.21   ,255.53}};
+        d = MathX.sumproduct(darr);
+        assertEquals("Sumproduct ", 4.243234425E+22, d);
+        
+        darr = new double[][]
+               {{0, 0, 0, 0, 0, 0, 0, 0},
+                {0.11, 0.22, 0.33, 0.44, 0.55, 0.66, 0.77, 0.88},
+                {23.23, 46.46, 69.69, 92.92, 116.15, 139.38, 162.61, 185.84}};
+        d = MathX.sumproduct(darr);
+        assertEquals("Sumproduct ", 0, d);
+
+        darr = new double[][]
+               {{0, 1, 2, 3, 4, 5, 6, 7},
+                {0.11, 0.22, 0.33, 0.44, 0.55, 0.66, 0.77, 0.88},
+                {23.23, 46.46, 69.69, 92.92, 116.15, 139.38, 162.61, 185.84}};
+        d = MathX.sumproduct(darr);
+        assertEquals("Sumproduct ", 2790.3876, d);
+
+        
+    }
+
+    public void testSumsq() {
+        double[] d = new double[100];
+        d[0] = 1.1;     d[1] = 2.1;     d[2] = 3.1;     d[3] = 4.1; 
+        d[4] = 5.1;     d[5] = 6.1;     d[6] = 7.1;     d[7] = 8.1;
+        d[8] = 9.1;     d[9] = 10.1;    d[10] = 11.1;   d[11] = 12.1;
+        d[12] = 13.1;   d[13] = 14.1;   d[14] = 15.1;   d[15] = 16.1;
+        d[16] = 17.1;   d[17] = 18.1;   d[18] = 19.1;   d[19] = 20.1; 
+        
+        double s = MathX.sumsq(d);
+        assertEquals("Sumsq ", 2912.2, s);
+        
+        d = new double[1000];
+        s = MathX.sumsq(d);
+        assertEquals("Sumsq ", 0, s);
+        
+        d[0] = -1.1;     d[1] = 2.1;     d[2] = -3.1;     d[3] = 4.1; 
+        d[4] = -5.1;     d[5] = 6.1;     d[6] = -7.1;     d[7] = 8.1;
+        d[8] = -9.1;     d[9] = 10.1;    d[10] = -11.1;   d[11] = 12.1;
+        d[12] = -13.1;   d[13] = 14.1;   d[14] = -15.1;   d[15] = 16.1;
+        d[16] = -17.1;   d[17] = 18.1;   d[18] = -19.1;   d[19] = 20.1; 
+        s = MathX.sumsq(d);
+        assertEquals("Sumsq ", 2912.2, s);
+        
+        d[0] = -1.1;     d[1] = -2.1;     d[2] = -3.1;     d[3] = -4.1; 
+        d[4] = -5.1;     d[5] = -6.1;     d[6] = -7.1;     d[7] = -8.1;
+        d[8] = -9.1;     d[9] = -10.1;    d[10] = -11.1;   d[11] = -12.1;
+        d[12] = -13.1;   d[13] = -14.1;   d[14] = -15.1;   d[15] = -16.1;
+        d[16] = -17.1;   d[17] = -18.1;   d[18] = -19.1;   d[19] = -20.1; 
+        s = MathX.sumsq(d);
+        assertEquals("Sumsq ", 2912.2, s);
+    }
+
+    public void testFactorial() {
+        int n = 0;
+        double s = 0;
+        
+        n = 0;
+        s = MathX.factorial(n);
+        assertEquals("Factorial ", 1, s);
+        
+        n = 1;
+        s = MathX.factorial(n);
+        assertEquals("Factorial ", 1, s);
+        
+        n = 10;
+        s = MathX.factorial(n);
+        assertEquals("Factorial ", 3628800, s);
+        
+        n = 99;
+        s = MathX.factorial(n);
+        assertEquals("Factorial ", 9.33262154439E+155, s);
+        
+        n = -1;
+        s = MathX.factorial(n);
+        assertEquals("Factorial ", Double.NaN, s);
+        
+        n = Integer.MAX_VALUE;
+        s = MathX.factorial(n);
+        assertEquals("Factorial ", Double.POSITIVE_INFINITY, s);
+    }
+
+    public void testSumx2my2() {
+        double d = 0;
+        double[] xarr = null;
+        double[] yarr = null;
+        
+        xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+        d = MathX.sumx2my2(xarr, yarr);
+        assertEquals("sumx2my2 ", 100, d);
+        
+        xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10};
+        yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+        d = MathX.sumx2my2(xarr, yarr);
+        assertEquals("sumx2my2 ", 100, d);
+        
+        xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+        yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        d = MathX.sumx2my2(xarr, yarr);
+        assertEquals("sumx2my2 ", -100, d);
+        
+        xarr = new double[]{10};
+        yarr = new double[]{9};
+        d = MathX.sumx2my2(xarr, yarr);
+        assertEquals("sumx2my2 ", 19, d);
+        
+        xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        d = MathX.sumx2my2(xarr, yarr);
+        assertEquals("sumx2my2 ", 0, d);
+        
+    }
+
+    public void testSumx2py2() {
+        double d = 0;
+        double[] xarr = null;
+        double[] yarr = null;
+        
+        xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+        d = MathX.sumx2py2(xarr, yarr);
+        assertEquals("sumx2py2 ", 670, d);
+        
+        xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10};
+        yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+        d = MathX.sumx2py2(xarr, yarr);
+        assertEquals("sumx2py2 ", 670, d);
+        
+        xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+        yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        d = MathX.sumx2py2(xarr, yarr);
+        assertEquals("sumx2py2 ", 670, d);
+        
+        xarr = new double[]{10};
+        yarr = new double[]{9};
+        d = MathX.sumx2py2(xarr, yarr);
+        assertEquals("sumx2py2 ", 181, d);
+        
+        xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        d = MathX.sumx2py2(xarr, yarr);
+        assertEquals("sumx2py2 ", 770, d);
+    }
+
+    public void testSumxmy2() {
+        double d = 0;
+        double[] xarr = null;
+        double[] yarr = null;
+        
+        xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+        d = MathX.sumxmy2(xarr, yarr);
+        assertEquals("sumxmy2 ", 10, d);
+        
+        xarr = new double[]{-1, -2, -3, -4, -5, -6, -7, -8, -9, -10};
+        yarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+        d = MathX.sumxmy2(xarr, yarr);
+        assertEquals("sumxmy2 ", 1330, d);
+        
+        xarr = new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+        yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        d = MathX.sumxmy2(xarr, yarr);
+        assertEquals("sumxmy2 ", 10, d);
+        
+        xarr = new double[]{10};
+        yarr = new double[]{9};
+        d = MathX.sumxmy2(xarr, yarr);
+        assertEquals("sumxmy2 ", 1, d);
+        
+        xarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        yarr = new double[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+        d = MathX.sumxmy2(xarr, yarr);
+        assertEquals("sumxmy2 ", 0, d);
+    }
+
+    public void testRound() {
+        double d = 0;
+        int p = 0;
+        
+        d = 0; p = 0;
+        assertEquals("round ", 0, MathX.round(d, p));
+        
+        d = 10; p = 0;
+        assertEquals("round ", 10, MathX.round(d, p));
+        
+        d = 123.23; p = 0;
+        assertEquals("round ", 123, MathX.round(d, p));
+        
+        d = -123.23; p = 0;
+        assertEquals("round ", -123, MathX.round(d, p));
+        
+        d = 123.12; p = 2;
+        assertEquals("round ", 123.12, MathX.round(d, p));
+        
+        d = 88.123459; p = 5;
+        assertEquals("round ", 88.12346, MathX.round(d, p));
+        
+        d = 0; p = 2;
+        assertEquals("round ", 0, MathX.round(d, p));
+        
+        d = 0; p = -1;
+        assertEquals("round ", 0, MathX.round(d, p));
+        
+        d = 0.01; p = -1;
+        assertEquals("round ", 0, MathX.round(d, p));
+
+        d = 123.12; p = -2;
+        assertEquals("round ", 100, MathX.round(d, p));
+        
+        d = 88.123459; p = -3;
+        assertEquals("round ", 0, MathX.round(d, p));
+        
+        d = 49.00000001; p = -1;
+        assertEquals("round ", 50, MathX.round(d, p));
+        
+        d = 149.999999; p = -2;
+        assertEquals("round ", 100, MathX.round(d, p));
+        
+        d = 150.0; p = -2;
+        assertEquals("round ", 200, MathX.round(d, p));
+    }
+
+    public void testRoundDown() {
+        double d = 0;
+        int p = 0;
+        
+        d = 0; p = 0;
+        assertEquals("roundDown ", 0, MathX.roundDown(d, p));
+        
+        d = 10; p = 0;
+        assertEquals("roundDown ", 10, MathX.roundDown(d, p));
+        
+        d = 123.99; p = 0;
+        assertEquals("roundDown ", 123, MathX.roundDown(d, p));
+        
+        d = -123.99; p = 0;
+        assertEquals("roundDown ", -123, MathX.roundDown(d, p));
+        
+        d = 123.99; p = 2;
+        assertEquals("roundDown ", 123.99, MathX.roundDown(d, p));
+        
+        d = 88.123459; p = 5;
+        assertEquals("roundDown ", 88.12345, MathX.roundDown(d, p));
+        
+        d = 0; p = 2;
+        assertEquals("roundDown ", 0, MathX.roundDown(d, p));
+        
+        d = 0; p = -1;
+        assertEquals("roundDown ", 0, MathX.roundDown(d, p));
+        
+        d = 0.01; p = -1;
+        assertEquals("roundDown ", 0, MathX.roundDown(d, p));
+
+        d = 199.12; p = -2;
+        assertEquals("roundDown ", 100, MathX.roundDown(d, p));
+        
+        d = 88.123459; p = -3;
+        assertEquals("roundDown ", 0, MathX.roundDown(d, p));
+        
+        d = 99.00000001; p = -1;
+        assertEquals("roundDown ", 90, MathX.roundDown(d, p));
+        
+        d = 100.00001; p = -2;
+        assertEquals("roundDown ", 100, MathX.roundDown(d, p));
+        
+        d = 150.0; p = -2;
+        assertEquals("roundDown ", 100, MathX.roundDown(d, p));
+    }
+
+    public void testRoundUp() {
+        double d = 0;
+        int p = 0;
+        
+        d = 0; p = 0;
+        assertEquals("roundUp ", 0, MathX.roundUp(d, p));
+        
+        d = 10; p = 0;
+        assertEquals("roundUp ", 10, MathX.roundUp(d, p));
+        
+        d = 123.23; p = 0;
+        assertEquals("roundUp ", 124, MathX.roundUp(d, p));
+        
+        d = -123.23; p = 0;
+        assertEquals("roundUp ", -124, MathX.roundUp(d, p));
+        
+        d = 123.12; p = 2;
+        assertEquals("roundUp ", 123.12, MathX.roundUp(d, p));
+        
+        d = 88.123459; p = 5;
+        assertEquals("roundUp ", 88.12346, MathX.roundUp(d, p));
+        
+        d = 0; p = 2;
+        assertEquals("roundUp ", 0, MathX.roundUp(d, p));
+        
+        d = 0; p = -1;
+        assertEquals("roundUp ", 0, MathX.roundUp(d, p));
+        
+        d = 0.01; p = -1;
+        assertEquals("roundUp ", 10, MathX.roundUp(d, p));
+
+        d = 123.12; p = -2;
+        assertEquals("roundUp ", 200, MathX.roundUp(d, p));
+        
+        d = 88.123459; p = -3;
+        assertEquals("roundUp ", 1000, MathX.roundUp(d, p));
+        
+        d = 49.00000001; p = -1;
+        assertEquals("roundUp ", 50, MathX.roundUp(d, p));
+        
+        d = 149.999999; p = -2;
+        assertEquals("roundUp ", 200, MathX.roundUp(d, p));
+        
+        d = 150.0; p = -2;
+        assertEquals("roundUp ", 200, MathX.roundUp(d, p));
+    }
+
+    public void testCeiling() {
+        double d = 0;
+        double s = 0;
+        
+        d = 0; s = 0;
+        assertEquals("ceiling ", 0, MathX.ceiling(d, s));
+        
+        d = 1; s = 0;
+        assertEquals("ceiling ", 0, MathX.ceiling(d, s));
+        
+        d = 0; s = 1;
+        assertEquals("ceiling ", 0, MathX.ceiling(d, s));
+        
+        d = -1; s = 0;
+        assertEquals("ceiling ", 0, MathX.ceiling(d, s));
+        
+        d = 0; s = -1;
+        assertEquals("ceiling ", 0, MathX.ceiling(d, s));
+        
+        d = 10; s = 1.11;
+        assertEquals("ceiling ", 11.1, MathX.ceiling(d, s));
+        
+        d = 11.12333; s = 0.03499;
+        assertEquals("ceiling ", 11.12682, MathX.ceiling(d, s));
+        
+        d = -11.12333; s = 0.03499;
+        assertEquals("ceiling ", Double.NaN, MathX.ceiling(d, s));
+        
+        d = 11.12333; s = -0.03499;
+        assertEquals("ceiling ", Double.NaN, MathX.ceiling(d, s));
+        
+        d = -11.12333; s = -0.03499;
+        assertEquals("ceiling ", -11.12682, MathX.ceiling(d, s));
+        
+        d = 100; s = 0.001;
+        assertEquals("ceiling ", 100, MathX.ceiling(d, s));
+        
+        d = -0.001; s = -9.99;
+        assertEquals("ceiling ", -9.99, MathX.ceiling(d, s));
+        
+        d = 4.42; s = 0.05;
+        assertEquals("ceiling ", 4.45, MathX.ceiling(d, s));
+        
+        d = 0.05; s = 4.42;
+        assertEquals("ceiling ", 4.42, MathX.ceiling(d, s));
+        
+        d = 0.6666; s = 3.33;
+        assertEquals("ceiling ", 3.33, MathX.ceiling(d, s));
+        
+        d = 2d/3; s = 3.33;
+        assertEquals("ceiling ", 3.33, MathX.ceiling(d, s));
+    }
+
+    public void testFloor() {
+        double d = 0;
+        double s = 0;
+        
+        d = 0; s = 0;
+        assertEquals("floor ", 0, MathX.floor(d, s));
+        
+        d = 1; s = 0;
+        assertEquals("floor ", Double.NaN, MathX.floor(d, s));
+        
+        d = 0; s = 1;
+        assertEquals("floor ", 0, MathX.floor(d, s));
+        
+        d = -1; s = 0;
+        assertEquals("floor ", Double.NaN, MathX.floor(d, s));
+        
+        d = 0; s = -1;
+        assertEquals("floor ", 0, MathX.floor(d, s));
+        
+        d = 10; s = 1.11;
+        assertEquals("floor ", 9.99, MathX.floor(d, s));
+        
+        d = 11.12333; s = 0.03499;
+        assertEquals("floor ", 11.09183, MathX.floor(d, s));
+        
+        d = -11.12333; s = 0.03499;
+        assertEquals("floor ", Double.NaN, MathX.floor(d, s));
+        
+        d = 11.12333; s = -0.03499;
+        assertEquals("floor ", Double.NaN, MathX.floor(d, s));
+        
+        d = -11.12333; s = -0.03499;
+        assertEquals("floor ", -11.09183, MathX.floor(d, s));
+        
+        d = 100; s = 0.001;
+        assertEquals("floor ", 100, MathX.floor(d, s));
+        
+        d = -0.001; s = -9.99;
+        assertEquals("floor ", 0, MathX.floor(d, s));
+        
+        d = 4.42; s = 0.05;
+        assertEquals("floor ", 4.4, MathX.floor(d, s));
+        
+        d = 0.05; s = 4.42;
+        assertEquals("floor ", 0, MathX.floor(d, s));
+        
+        d = 0.6666; s = 3.33;
+        assertEquals("floor ", 0, MathX.floor(d, s));
+        
+        d = 2d/3; s = 3.33;
+        assertEquals("floor ", 0, MathX.floor(d, s));
+    }
+
+}
diff --git a/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestStatsLib.java b/src/scratchpad/testcases/org/apache/poi/hssf/record/formula/functions/TestStatsLib.java
new file mode 100644 (file)
index 0000000..15ee280
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Created on May 30, 2005
+ *
+ */
+package org.apache.poi.hssf.record.formula.functions;
+
+
+/**
+ * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
+ *
+ */
+public class TestStatsLib extends AbstractNumericTestCase {
+
+    public void testDevsq() {
+        double[] v = null;
+        double d, x = 0;
+        
+        v = new double[] {1,2,3,4,5,6,7,8,9,10};
+        d = StatsLib.devsq(v);
+        x = 82.5;
+        assertEquals("devsq ", x, d);
+        
+        v = new double[] {1,1,1,1,1,1,1,1,1,1};
+        d = StatsLib.devsq(v);
+        x = 0;
+        assertEquals("devsq ", x, d);
+        
+        v = new double[] {0,0,0,0,0,0,0,0,0,0};
+        d = StatsLib.devsq(v);
+        x = 0;
+        assertEquals("devsq ", x, d);
+        
+        v = new double[] {1,2,1,2,1,2,1,2,1,2};
+        d = StatsLib.devsq(v);
+        x = 2.5;
+        assertEquals("devsq ", x, d);
+        
+        v = new double[] {123.12,33.3333,2d/3d,5.37828,0.999};
+        d = StatsLib.devsq(v);
+        x = 10953.7416965767;
+        assertEquals("devsq ", x, d);
+        
+        v = new double[] {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10};
+        d = StatsLib.devsq(v);
+        x = 82.5;
+        assertEquals("devsq ", x, d);
+    }
+
+    public void testKthLargest() {
+        double[] v = null;
+        double d, x = 0;
+        
+        v = new double[] {1,2,3,4,5,6,7,8,9,10};
+        d = StatsLib.kthLargest(v, 3);
+        x = 8;
+        assertEquals("kthLargest ", x, d);
+        
+        v = new double[] {1,1,1,1,1,1,1,1,1,1};
+        d = StatsLib.kthLargest(v, 3);
+        x = 1;
+        assertEquals("kthLargest ", x, d);
+        
+        v = new double[] {0,0,0,0,0,0,0,0,0,0};
+        d = StatsLib.kthLargest(v, 3);
+        x = 0;
+        assertEquals("kthLargest ", x, d);
+        
+        v = new double[] {1,2,1,2,1,2,1,2,1,2};
+        d = StatsLib.kthLargest(v, 3);
+        x = 2;
+        assertEquals("kthLargest ", x, d);
+        
+        v = new double[] {123.12,33.3333,2d/3d,5.37828,0.999};
+        d = StatsLib.kthLargest(v, 3);
+        x = 5.37828;
+        assertEquals("kthLargest ", x, d);
+        
+        v = new double[] {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10};
+        d = StatsLib.kthLargest(v, 3);
+        x = -3;
+        assertEquals("kthLargest ", x, d);
+    }
+
+    public void testKthSmallest() {
+    }
+
+    public void testAvedev() {
+        double[] v = null;
+        double d, x = 0;
+        
+        v = new double[] {1,2,3,4,5,6,7,8,9,10};
+        d = StatsLib.avedev(v);
+        x = 2.5;
+        assertEquals("avedev ", x, d);
+        
+        v = new double[] {1,1,1,1,1,1,1,1,1,1};
+        d = StatsLib.avedev(v);
+        x = 0;
+        assertEquals("avedev ", x, d);
+        
+        v = new double[] {0,0,0,0,0,0,0,0,0,0};
+        d = StatsLib.avedev(v);
+        x = 0;
+        assertEquals("avedev ", x, d);
+        
+        v = new double[] {1,2,1,2,1,2,1,2,1,2};
+        d = StatsLib.avedev(v);
+        x = 0.5;
+        assertEquals("avedev ", x, d);
+        
+        v = new double[] {123.12,33.3333,2d/3d,5.37828,0.999};
+        d = StatsLib.avedev(v);
+        x = 36.42176053333;
+        assertEquals("avedev ", x, d);
+        
+        v = new double[] {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10};
+        d = StatsLib.avedev(v);
+        x = 2.5;
+        assertEquals("avedev ", x, d);
+    }
+
+    public void testMedian() {
+        double[] v = null;
+        double d, x = 0;
+        
+        v = new double[] {1,2,3,4,5,6,7,8,9,10};
+        d = StatsLib.median(v);
+        x = 5.5;
+        assertEquals("median ", x, d);
+        
+        v = new double[] {1,1,1,1,1,1,1,1,1,1};
+        d = StatsLib.median(v);
+        x = 1;
+        assertEquals("median ", x, d);
+        
+        v = new double[] {0,0,0,0,0,0,0,0,0,0};
+        d = StatsLib.median(v);
+        x = 0;
+        assertEquals("median ", x, d);
+        
+        v = new double[] {1,2,1,2,1,2,1,2,1,2};
+        d = StatsLib.median(v);
+        x = 1.5;
+        assertEquals("median ", x, d);
+        
+        v = new double[] {123.12,33.3333,2d/3d,5.37828,0.999};
+        d = StatsLib.median(v);
+        x = 5.37828;
+        assertEquals("median ", x, d);
+        
+        v = new double[] {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10};
+        d = StatsLib.median(v);
+        x = -5.5;
+        assertEquals("median ", x, d);
+        
+        v = new double[] {-2,-3,-4,-5,-6,-7,-8,-9,-10};
+        d = StatsLib.median(v);
+        x = -6;
+        assertEquals("median ", x, d);
+        
+        v = new double[] {1,2,3,4,5,6,7,8,9};
+        d = StatsLib.median(v);
+        x = 5;
+        assertEquals("median ", x, d);
+    }
+
+    public void testMode() {
+        double[] v = null;
+        double d, x = 0;
+        
+        v = new double[] {1,2,3,4,5,6,7,8,9,10};
+        d = StatsLib.mode(v);
+        x = Double.NaN;
+        assertEquals("mode ", x, d);
+        
+        v = new double[] {1,1,1,1,1,1,1,1,1,1};
+        d = StatsLib.mode(v);
+        x = 1;
+        assertEquals("mode ", x, d);
+        
+        v = new double[] {0,0,0,0,0,0,0,0,0,0};
+        d = StatsLib.mode(v);
+        x = 0;
+        assertEquals("mode ", x, d);
+        
+        v = new double[] {1,2,1,2,1,2,1,2,1,2};
+        d = StatsLib.mode(v);
+        x = 1;
+        assertEquals("mode ", x, d);
+        
+        v = new double[] {123.12,33.3333,2d/3d,5.37828,0.999};
+        d = StatsLib.mode(v);
+        x = Double.NaN;
+        assertEquals("mode ", x, d);
+        
+        v = new double[] {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10};
+        d = StatsLib.mode(v);
+        x = Double.NaN;
+        assertEquals("mode ", x, d);
+        
+        v = new double[] {1,2,3,4,1,1,1,1,0,0,0,0,0};
+        d = StatsLib.mode(v);
+        x = 1;
+        assertEquals("mode ", x, d);
+        
+        v = new double[] {0,1,2,3,4,1,1,1,0,0,0,0,1};
+        d = StatsLib.mode(v);
+        x = 0;
+        assertEquals("mode ", x, d);
+    }
+
+    public void testStddev() {
+        double[] v = null;
+        double d, x = 0;
+        
+        v = new double[] {1,2,3,4,5,6,7,8,9,10};
+        d = StatsLib.stdev(v);
+        x = 3.02765035410;
+        assertEquals("stdev ", x, d);
+        
+        v = new double[] {1,1,1,1,1,1,1,1,1,1};
+        d = StatsLib.stdev(v);
+        x = 0;
+        assertEquals("stdev ", x, d);
+        
+        v = new double[] {0,0,0,0,0,0,0,0,0,0};
+        d = StatsLib.stdev(v);
+        x = 0;
+        assertEquals("stdev ", x, d);
+        
+        v = new double[] {1,2,1,2,1,2,1,2,1,2};
+        d = StatsLib.stdev(v);
+        x = 0.52704627669;
+        assertEquals("stdev ", x, d);
+        
+        v = new double[] {123.12,33.3333,2d/3d,5.37828,0.999};
+        d = StatsLib.stdev(v);
+        x = 52.33006233652;
+        assertEquals("stdev ", x, d);
+        
+        v = new double[] {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10};
+        d = StatsLib.stdev(v);
+        x = 3.02765035410;
+        assertEquals("stdev ", x, d);
+    }
+}