*/
public final class ValueEvalToNumericXlator {
- public static final int STRING_IS_PARSED = 0x0001;
- public static final int BOOL_IS_PARSED = 0x0002;
- public static final int BLANK_IS_PARSED = 0x0004; // => blanks are not ignored, converted to 0
+ public static final int BLANK_IS_PARSED = 0x0001; // => blanks are not ignored, converted to 0
- public static final int REF_BOOL_IS_PARSED = 0x0008;
+ public static final int REF_BOOL_IS_PARSED = 0x0002;
private final int flags;
}
if (eval instanceof BoolEval) {
- return ((flags & BOOL_IS_PARSED) > 0)
- ? (NumericValueEval) eval
- : xlateBlankEval();
+ return eval;
}
if (eval instanceof StringEval) {
* uses the relevant flags to decode the StringEval
* @param eval
*/
- private ValueEval xlateStringEval(StringEval eval) {
+ private static ValueEval xlateStringEval(StringEval eval) {
- if ((flags & STRING_IS_PARSED) > 0) {
- String s = eval.getStringValue();
- Double d = OperandResolver.parseDouble(s);
- if(d == null) {
- return ErrorEval.VALUE_INVALID;
- }
- return new NumberEval(d.doubleValue());
+ String s = eval.getStringValue();
+ Double d = OperandResolver.parseDouble(s);
+ if(d == null) {
+ return ErrorEval.VALUE_INVALID;
}
- // else strings are errors?
- return ErrorEval.VALUE_INVALID;
+ return new NumberEval(d.doubleValue());
}
}
*/
public abstract class AggregateFunction extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
- new ValueEvalToNumericXlator((short) (
- ValueEvalToNumericXlator.BOOL_IS_PARSED
- | ValueEvalToNumericXlator.STRING_IS_PARSED
- ));
+ new ValueEvalToNumericXlator(0);
protected ValueEval attemptXlateToNumeric(ValueEval ve) {
return DEFAULT_NUM_XLATOR.attemptXlateToNumeric(ve);
public final class Maxa extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (
- ValueEvalToNumericXlator.BOOL_IS_PARSED
- | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
- | ValueEvalToNumericXlator.STRING_IS_PARSED
+ ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
| ValueEvalToNumericXlator.BLANK_IS_PARSED
));
public final class Mina extends MultiOperandNumericFunction {
private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
new ValueEvalToNumericXlator((short) (
- ValueEvalToNumericXlator.BOOL_IS_PARSED
- | ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
- | ValueEvalToNumericXlator.STRING_IS_PARSED
- | ValueEvalToNumericXlator.BLANK_IS_PARSED
+ ValueEvalToNumericXlator.REF_BOOL_IS_PARSED
+ | ValueEvalToNumericXlator.BLANK_IS_PARSED
));
protected ValueEval attemptXlateToNumeric(ValueEval ve) {
package org.apache.poi.hssf.record.formula.functions;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+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.BoolEval;
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.EvaluationException;
+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.StringEval;
import org.apache.poi.hssf.record.formula.eval.ValueEval;
-import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
- *
+ *
*/
-public class Mode extends MultiOperandNumericFunction {
- private static final ValueEvalToNumericXlator DEFAULT_NUM_XLATOR =
- new ValueEvalToNumericXlator(0);
+public class Mode implements Function {
/**
- * 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.
+ * 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
*/
- protected ValueEval attemptXlateToNumeric(ValueEval ve) {
- return DEFAULT_NUM_XLATOR.attemptXlateToNumeric(ve);
+ public static double evaluate(double[] v) throws EvaluationException {
+ if (v.length < 2) {
+ throw new EvaluationException(ErrorEval.NA);
+ }
+
+ // very naive impl, may need to be optimized
+ 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];
+ }
+ }
+ if (maxc > 1) {
+ return maxv;
+ }
+ throw new EvaluationException(ErrorEval.NA);
+
}
- protected double evaluate(double[] values) throws EvaluationException {
- double d = StatsLib.mode(values);
- if (Double.isNaN(d)) {
- // TODO - StatsLib is returning NaN to denote 'no duplicate values'
- throw new EvaluationException(ErrorEval.NA);
+ public Eval evaluate(Eval[] args, int srcCellRow, short srcCellCol) {
+ double result;
+ try {
+ List temp = new ArrayList();
+ for (int i = 0; i < args.length; i++) {
+ collectValues(args[i], temp);
+ }
+ double[] values = new double[temp.size()];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = ((Double) temp.get(i)).doubleValue();
+ }
+ result = evaluate(values);
+ } catch (EvaluationException e) {
+ return e.getErrorEval();
+ }
+ return new NumberEval(result);
+ }
+
+ private static void collectValues(Eval arg, List temp) throws EvaluationException {
+ if (arg instanceof AreaEval) {
+ AreaEval ae = (AreaEval) arg;
+ int width = ae.getWidth();
+ int height = ae.getHeight();
+ for (int rrIx = 0; rrIx < height; rrIx++) {
+ for (int rcIx = 0; rcIx < width; rcIx++) {
+ ValueEval ve1 = ae.getRelativeValue(rrIx, rcIx);
+ collectValue(ve1, temp, false);
+ }
+ }
+ return;
+ }
+ if (arg instanceof RefEval) {
+ RefEval re = (RefEval) arg;
+ collectValue(re.getInnerValueEval(), temp, true);
+ return;
+ }
+ collectValue(arg, temp, true);
+
+ }
+
+ private static void collectValue(Eval arg, List temp, boolean mustBeNumber)
+ throws EvaluationException {
+ if (arg instanceof ErrorEval) {
+ throw new EvaluationException((ErrorEval) arg);
+ }
+ if (arg == BlankEval.INSTANCE || arg instanceof BoolEval || arg instanceof StringEval) {
+ if (mustBeNumber) {
+ throw EvaluationException.invalidValue();
+ }
+ return;
+ }
+ if (arg instanceof NumberEval) {
+ temp.add(new Double(((NumberEval) arg).getNumberValue()));
+ return;
}
- return d;
+ throw new RuntimeException("Unexpected value type (" + arg.getClass().getName() + ")");
}
}
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
- */
- 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;
*/
package org.apache.poi.hssf.record.formula.functions;
+import junit.framework.AssertionFailedError;
+
+import org.apache.poi.hssf.record.formula.eval.ErrorEval;
+import org.apache.poi.hssf.record.formula.eval.EvaluationException;
+
/**
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
}
public void testMode() {
- double[] v = null;
+ double[] v;
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);
+ confirmMode(v, null);
v = new double[] {1,1,1,1,1,1,1,1,1,1};
- d = StatsLib.mode(v);
- x = 1;
- assertEquals("mode ", x, d);
+ confirmMode(v, 1.0);
v = new double[] {0,0,0,0,0,0,0,0,0,0};
- d = StatsLib.mode(v);
- x = 0;
- assertEquals("mode ", x, d);
+ confirmMode(v, 0.0);
v = new double[] {1,2,1,2,1,2,1,2,1,2};
- d = StatsLib.mode(v);
- x = 1;
- assertEquals("mode ", x, d);
+ confirmMode(v, 1.0);
v = new double[] {123.12,33.3333,2d/3d,5.37828,0.999};
- d = StatsLib.mode(v);
- x = Double.NaN;
- assertEquals("mode ", x, d);
+ confirmMode(v, null);
v = new double[] {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10};
- d = StatsLib.mode(v);
- x = Double.NaN;
- assertEquals("mode ", x, d);
+ confirmMode(v, null);
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);
+ confirmMode(v, 1.0);
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);
+ confirmMode(v, 0.0);
+ }
+ private static void confirmMode(double[] v, double expectedResult) {
+ confirmMode(v, new Double(expectedResult));
+ }
+ private static void confirmMode(double[] v, Double expectedResult) {
+ double actual;
+ try {
+ actual = Mode.evaluate(v);
+ if (expectedResult == null) {
+ throw new AssertionFailedError("Expected N/A exception was not thrown");
+ }
+ } catch (EvaluationException e) {
+ if (expectedResult == null) {
+ assertEquals(ErrorEval.NA, e.getErrorEval());
+ return;
+ }
+ throw new RuntimeException(e);
+ }
+ assertEquals("mode", expectedResult.doubleValue(), actual);
}
+
public void testStddev() {
double[] v = null;