Index: src/org/apache/poi/ss/formula/eval/FunctionEval.java =================================================================== --- src/org/apache/poi/ss/formula/eval/FunctionEval.java (revision 1112) +++ src/org/apache/poi/ss/formula/eval/FunctionEval.java (revision 1113) @@ -211,10 +211,12 @@ retval[318] = AggregateFunction.DEVSQ; retval[321] = AggregateFunction.SUMSQ; - + retval[325] = AggregateFunction.LARGE; retval[326] = AggregateFunction.SMALL; - + + retval[328] = AggregateFunction.PERCENTILE; + retval[330] = new Mode(); retval[336] = TextFunction.CONCATENATE; Index: src/org/apache/poi/ss/formula/functions/AggregateFunction.java =================================================================== --- src/org/apache/poi/ss/formula/functions/AggregateFunction.java (revision 1112) +++ src/org/apache/poi/ss/formula/functions/AggregateFunction.java (revision 1113) @@ -67,6 +67,51 @@ return new NumberEval(result); } } + + private static final class Percentile extends Fixed2ArgFunction { + + protected Percentile() { + } + + public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, + ValueEval arg1) { + double dn; + try { + ValueEval ve1 = OperandResolver.getSingleValue(arg1, srcRowIndex, srcColumnIndex); + dn = OperandResolver.coerceValueToDouble(ve1); + } catch (EvaluationException e1) { + // all errors in the second arg translate to #VALUE! + return ErrorEval.VALUE_INVALID; + } + if (dn < 0 || dn > 1) { // has to be percentage + return ErrorEval.NUM_ERROR; + } + + double result; + try { + double[] ds = ValueCollector.collectValues(arg0); + int N = ds.length; + double n = (N - 1) * dn + 1; + if (n == 1d) { + result = StatsLib.kthSmallest(ds, 1); + } else if (n == N) { + result = StatsLib.kthLargest(ds, 1); + } else { + int k = (int) n; + double d = n - k; + result = StatsLib.kthSmallest(ds, k) + d + * (StatsLib.kthSmallest(ds, k + 1) - StatsLib.kthSmallest(ds, k)); + } + + NumericFunction.checkValue(result); + } catch (EvaluationException e) { + return e.getErrorEval(); + } + + return new NumberEval(result); + } + } + static final class ValueCollector extends MultiOperandNumericFunction { private static final ValueCollector instance = new ValueCollector(); public ValueCollector() { @@ -148,6 +193,9 @@ return values.length > 0 ? MathX.min(values) : 0; } }; + + public static final Function PERCENTILE = new Percentile(); + public static final Function PRODUCT = new AggregateFunction() { protected double evaluate(double[] values) { return MathX.product(values);