]> source.dussan.org Git - poi.git/commitdiff
fixed special cases of MODE function
authorJosh Micich <josh@apache.org>
Wed, 10 Sep 2008 23:37:22 +0000 (23:37 +0000)
committerJosh Micich <josh@apache.org>
Wed, 10 Sep 2008 23:37:22 +0000 (23:37 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@694065 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/hssf/record/formula/eval/ValueEvalToNumericXlator.java
src/java/org/apache/poi/hssf/record/formula/functions/AggregateFunction.java
src/java/org/apache/poi/hssf/record/formula/functions/Maxa.java
src/java/org/apache/poi/hssf/record/formula/functions/Mina.java
src/java/org/apache/poi/hssf/record/formula/functions/Mode.java
src/java/org/apache/poi/hssf/record/formula/functions/StatsLib.java
src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls
src/testcases/org/apache/poi/hssf/record/formula/functions/TestStatsLib.java

index 37f0b9b4b1ea1f6bb25e2dd1cce1b588359bcd58..bb04e411b127486efd36f3e3ccf8781bbf7a782d 100644 (file)
@@ -23,11 +23,9 @@ package org.apache.poi.hssf.record.formula.eval;
  */
 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;
     
@@ -59,9 +57,7 @@ public final class ValueEvalToNumericXlator {
         }
         
         if (eval instanceof BoolEval) {
-            return ((flags & BOOL_IS_PARSED) > 0)
-                ? (NumericValueEval) eval
-                : xlateBlankEval();
+            return eval;
         } 
         
         if (eval instanceof StringEval) {
@@ -135,17 +131,13 @@ public final class ValueEvalToNumericXlator {
      * 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());
     }
 }
index f04e64070bc9a4b47c4fae94ee44397bbb854ff6..0758686228250ce3bcad374e25a0d4d4792dbc6e 100644 (file)
@@ -28,10 +28,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
  */
 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);
index 7ae722889ed80cc2b6e1d263f0a55bbc91999cb4..95c4c8c911916454e3e17b51ccd197887af796c7 100644 (file)
@@ -27,9 +27,7 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
 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
                 ));
     
index b3965eefd4788997a97713952832658ba321ac73..73449363108189ae79a65fecd118880ff7b1a3e0 100644 (file)
@@ -27,10 +27,8 @@ import org.apache.poi.hssf.record.formula.eval.ValueEvalToNumericXlator;
 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) {
index 64f017e8e2a54af52a9df1e3bc90b53cdd8b225b..c236ccee62cc72ab4fd2cf904ecbc9d71d2281c0 100644 (file)
 
 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 &lt; amolweb at ya hoo dot com &gt;
- *
+ * 
  */
-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() + ")");
        }
 }
index 8ebccfd9508115083532532ff33dfb695377045c..e78f38caaa0b770ade45ce0daa6857d9377e6405 100644 (file)
@@ -60,36 +60,6 @@ public final class StatsLib {
         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;
index acf9c8b166c6232dcf06d900cd4db20613b26621..f046382eccd46b3855e3edc51fc1652731ee0f1e 100644 (file)
Binary files a/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls and b/src/testcases/org/apache/poi/hssf/data/FormulaEvalTestData.xls differ
index 237366baf0c64d420dea0d865a257cc13ff7b777..3562a67789c9c41634235323603ac4f538953dde 100644 (file)
  */
 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 &lt; amolweb at ya hoo dot com &gt;
@@ -181,49 +186,53 @@ public class TestStatsLib extends AbstractNumericTestCase {
     }
 
     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;