You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by fa...@apache.org on 2017/09/13 23:54:36 UTC

svn commit: r1808297 - in /poi/trunk: ./ sonar/main/ src/java/org/apache/poi/hssf/usermodel/ src/java/org/apache/poi/ss/formula/ src/java/org/apache/poi/ss/formula/eval/ src/java/org/apache/poi/ss/formula/eval/forked/ src/java/org/apache/poi/ss/formula...

Author: fanningpj
Date: Wed Sep 13 23:54:36 2017
New Revision: 1808297

URL: http://svn.apache.org/viewvc?rev=1808297&view=rev
Log:
Numeric Array Formula and Matrix Function [from Bob95132] This closes #69

Added:
    poi/trunk/src/java/org/apache/poi/ss/formula/CacheAreaEval.java   (with props)
    poi/trunk/src/java/org/apache/poi/ss/formula/functions/ArrayFunction.java   (with props)
    poi/trunk/src/java/org/apache/poi/ss/formula/functions/MatrixFunction.java   (with props)
    poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestMatrixFormulasFromXMLSpreadsheet.java   (with props)
    poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestMatrixFormulasFromBinarySpreadsheet.java   (with props)
    poi/trunk/test-data/spreadsheet/MatrixFormulaEvalTestData.xls   (with props)
    poi/trunk/test-data/spreadsheet/MatrixFormulaEvalTestData.xlsx   (with props)
Modified:
    poi/trunk/.classpath
    poi/trunk/build.gradle
    poi/trunk/build.xml
    poi/trunk/sonar/main/pom.xml
    poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationCell.java
    poi/trunk/src/java/org/apache/poi/ss/formula/EvaluationCell.java
    poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java
    poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java
    poi/trunk/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java
    poi/trunk/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java
    poi/trunk/src/java/org/apache/poi/ss/formula/eval/OperandResolver.java
    poi/trunk/src/java/org/apache/poi/ss/formula/eval/TwoOperandNumericOperation.java
    poi/trunk/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationCell.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFEvaluationCell.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationCell.java
    poi/trunk/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java

Modified: poi/trunk/.classpath
URL: http://svn.apache.org/viewvc/poi/trunk/.classpath?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/.classpath (original)
+++ poi/trunk/.classpath Wed Sep 13 23:54:36 2017
@@ -34,5 +34,6 @@
 	<classpathentry exported="true" kind="lib" path="lib/commons-codec-1.10.jar"/>
 	<classpathentry exported="true" kind="lib" path="lib/commons-logging-1.2.jar"/>
 	<classpathentry exported="true" kind="lib" path="lib/commons-collections4-4.1.jar"/>
+	<classpathentry kind="lib" path="lib/commons-math3-3.6.1.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>

Modified: poi/trunk/build.gradle
URL: http://svn.apache.org/viewvc/poi/trunk/build.gradle?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/build.gradle (original)
+++ poi/trunk/build.gradle Wed Sep 13 23:54:36 2017
@@ -73,7 +73,7 @@ subprojects {
     // See https://github.com/melix/japicmp-gradle-plugin 
 	apply plugin: 'me.champeau.gradle.japicmp'
         
-	version = '3.16-beta3'
+	version = '3.18-beta1'
 	ext {
 		japicmpversion = '3.15'
 	}
@@ -150,7 +150,8 @@ project('main') {
 		compile 'commons-codec:commons-codec:1.10'
 		compile 'commons-logging:commons-logging:1.2'
 		compile 'org.apache.commons:commons-collections4:4.1'
-		
+		compile 'org.apache.commons:commons-math3:3.6.1'
+
 		testCompile 'junit:junit:4.12'
 	}
 	
@@ -196,6 +197,7 @@ project('ooxml') {
 	dependencies {
 		compile 'org.apache.xmlbeans:xmlbeans:2.6.0'
 		compile 'org.apache.commons:commons-collections4:4.1'
+		compile 'org.apache.commons:commons-math3:3.6.1'
 		compile 'org.apache.santuario:xmlsec:2.0.6'
 		compile 'org.bouncycastle:bcpkix-jdk15on:1.54'
 		compile 'com.github.virtuald:curvesapi:1.04'

Modified: poi/trunk/build.xml
URL: http://svn.apache.org/viewvc/poi/trunk/build.xml?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/build.xml (original)
+++ poi/trunk/build.xml Wed Sep 13 23:54:36 2017
@@ -178,6 +178,9 @@ under the License.
     <property name="main.commons-collections4.jar" location="${main.lib}/commons-collections4-4.1.jar"/>
     <property name="main.commons-collections4.url"
               value="${repository.m2}/maven2/org/apache/commons/commons-collections4/4.1/commons-collections4-4.1.jar"/>
+    <property name="main.commons-math3.jar" location="${main.lib}/commons-math3-3.6.1.jar"/>
+    <property name="main.commons-math3.url"
+              value="${repository.m2}/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar"/>
 
     <!-- xml signature libs -->
     <property name="dsig.xmlsec.jar" location="${compile.lib}/xmlsec-2.0.6.jar"/>
@@ -326,6 +329,7 @@ under the License.
         <pathelement location="${main.commons-logging.jar}"/>
         <pathelement location="${main.commons-codec.jar}"/>
         <pathelement location="${main.commons-collections4.jar}"/>
+        <pathelement location="${main.commons-math3.jar}"/>
     </path>
 
     <!-- some libraries should only be required for compiling/running tests -->
@@ -631,6 +635,7 @@ under the License.
                     <available file="${dsig.xmlsec.jar}"/>
                     <available file="${dsig.sl4j-api.jar}"/>
                     <available file="${main.commons-collections4.jar}"/>
+                    <available file="${main.commons-math3.jar}"/>
                 </and>
                 <isset property="disconnected"/>
             </or>
@@ -652,6 +657,7 @@ under the License.
         <downloadfile src="${asm.url}" dest="${asm.jar}"/>
         <downloadfile src="${jacoco.url}" dest="${jacoco.zip}"/>
         <downloadfile src="${main.commons-collections4.url}" dest="${main.commons-collections4.jar}"/>
+        <downloadfile src="${main.commons-math3.url}" dest="${main.commons-math3.jar}"/>
         <unzip src="${jacoco.zip}" dest=".">
             <patternset>
                 <include name="lib/*.jar"/>
@@ -2044,6 +2050,7 @@ under the License.
                     <include name="commons-codec-*.jar"/>
                     <include name="commons-logging-*.jar"/>
                     <include name="commons-collections4-*.jar"/>
+                    <include name="commons-math3-*.jar"/>
                     <include name="junit-*.jar"/>
                     <include name="log4j-*.jar"/>
                 </fileset>
@@ -2306,6 +2313,7 @@ under the License.
             <auxClasspath path="${ooxml.curvesapi.jar}" />
             <auxClasspath path="${ooxml.xmlbeans26.jar}" />
             <auxClasspath path="${main.commons-collections4.jar}" />
+            <auxClasspath path="${main.commons-math3.jar}" />
             <auxClasspath path="${main.commons-codec.jar}" />
             <auxClasspath path="${main.commons-logging.jar}" />
             <auxClasspath path="${main.junit.jar}" />

Modified: poi/trunk/sonar/main/pom.xml
URL: http://svn.apache.org/viewvc/poi/trunk/sonar/main/pom.xml?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/sonar/main/pom.xml (original)
+++ poi/trunk/sonar/main/pom.xml Wed Sep 13 23:54:36 2017
@@ -116,6 +116,11 @@
             <version>4.1</version>
         </dependency>
         <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-math3</artifactId>
+            <version>3.6.1</version>
+        </dependency>
+        <dependency>
             <groupId>commons-codec</groupId>
             <artifactId>commons-codec</artifactId>
             <version>1.10</version>

Modified: poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationCell.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationCell.java?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationCell.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationCell.java Wed Sep 13 23:54:36 2017
@@ -20,6 +20,8 @@ package org.apache.poi.hssf.usermodel;
 import org.apache.poi.ss.formula.EvaluationCell;
 import org.apache.poi.ss.formula.EvaluationSheet;
 import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.util.CellRangeAddress;
+
 /**
  * HSSF wrapper for a cell under evaluation
  */
@@ -93,6 +95,17 @@ final class HSSFEvaluationCell implement
 	public String getStringCellValue() {
 		return _cell.getRichStringCellValue().getString();
 	}
+	
+	@Override
+	public CellRangeAddress getArrayFormulaRange() {
+		return _cell.getArrayFormulaRange();
+	}
+	
+	@Override
+	public boolean isPartOfArrayFormulaGroup() {
+		return _cell.isPartOfArrayFormulaGroup();
+	}
+	
 	/**
 	 * Will return {@link CellType} in a future version of POI.
 	 * For forwards compatibility, do not hard-code cell type literals in your code.

Added: poi/trunk/src/java/org/apache/poi/ss/formula/CacheAreaEval.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/CacheAreaEval.java?rev=1808297&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/CacheAreaEval.java (added)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/CacheAreaEval.java Wed Sep 13 23:54:36 2017
@@ -0,0 +1,131 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.formula;
+
+import org.apache.poi.ss.formula.TwoDEval;
+import org.apache.poi.ss.formula.eval.AreaEval;
+import org.apache.poi.ss.formula.eval.AreaEvalBase;
+import org.apache.poi.ss.formula.eval.BlankEval;
+import org.apache.poi.ss.formula.eval.ValueEval;
+import org.apache.poi.ss.formula.ptg.AreaI;
+import org.apache.poi.ss.formula.ptg.AreaI.OffsetArea;
+import org.apache.poi.ss.util.CellReference;
+
+/**
+ * @author Robert Hulbert
+ * Provides holding structure for temporary values in arrays during the evaluation process.
+ * As such, Row/Column references do not actually correspond to data in the file.
+ */
+
+public final class CacheAreaEval extends AreaEvalBase {
+    
+    /* Value Containter */
+    private final ValueEval[] _values;
+    
+    public CacheAreaEval(AreaI ptg, ValueEval[] values) {
+        super(ptg);
+        _values = values;
+    }
+    
+    public CacheAreaEval(int firstRow, int firstColumn, int lastRow, int lastColumn, ValueEval[] values) {
+        super(firstRow, firstColumn, lastRow, lastColumn);
+        _values = values;
+    }
+    
+    public ValueEval getRelativeValue(int relativeRowIndex, int relativeColumnIndex) {
+        return getRelativeValue(-1, relativeRowIndex, relativeColumnIndex);
+    }
+    
+    public ValueEval getRelativeValue(int sheetIndex, int relativeRowIndex, int relativeColumnIndex) {
+        int oneDimensionalIndex = relativeRowIndex * getWidth() + relativeColumnIndex;
+        return _values[oneDimensionalIndex];
+    }
+
+    public AreaEval offset(int relFirstRowIx, int relLastRowIx,
+            int relFirstColIx, int relLastColIx) {
+        
+        AreaI area = new OffsetArea(getFirstRow(), getFirstColumn(),
+                relFirstRowIx, relLastRowIx, relFirstColIx, relLastColIx);
+        
+        int height = area.getLastRow() - area.getFirstRow() + 1;
+        int width = area.getLastColumn() - area.getFirstColumn() + 1;
+        
+        ValueEval newVals[] = new ValueEval[height * width];
+        
+        int startRow = area.getFirstRow() - getFirstRow();
+        int startCol = area.getFirstColumn() - getFirstColumn();
+        
+        for (int j = 0; j < height; j++) {
+            for (int i = 0; i < width; i++) {
+                ValueEval temp;
+                
+                /* CacheAreaEval is only temporary value representation, does not equal sheet selection
+                 * so any attempts going beyond the selection results in BlankEval
+                 */
+                if (startRow + j > getLastRow() || startCol + i > getLastColumn()) {
+                    temp = BlankEval.instance;
+                }
+                else {
+                    temp = _values[(startRow + j) * getWidth() + (startCol + i)];
+                }
+                newVals[j * width + i] = temp;     
+            }
+        }
+
+        return new CacheAreaEval(area, newVals);
+    }
+
+    public TwoDEval getRow(int rowIndex) {
+        if (rowIndex >= getHeight()) {
+            throw new IllegalArgumentException("Invalid rowIndex " + rowIndex
+                    + ".  Allowable range is (0.." + getHeight() + ").");
+        }
+        int absRowIndex = getFirstRow() + rowIndex;
+        ValueEval[] values = new ValueEval[getWidth()];
+        
+        for (int i = 0; i < values.length; i++) {
+            values[i] = getRelativeValue(rowIndex, i);
+        }
+        return new CacheAreaEval(absRowIndex, getFirstColumn() , absRowIndex, getLastColumn(), values);
+    }
+
+    public TwoDEval getColumn(int columnIndex) {
+        if (columnIndex >= getWidth()) {
+            throw new IllegalArgumentException("Invalid columnIndex " + columnIndex
+                    + ".  Allowable range is (0.." + getWidth() + ").");
+        }
+        int absColIndex = getFirstColumn() + columnIndex;
+        ValueEval[] values = new ValueEval[getHeight()];
+        
+        for (int i = 0; i < values.length; i++) {
+            values[i] = getRelativeValue(i, columnIndex);
+        }
+        
+        return new CacheAreaEval(getFirstRow(), absColIndex, getLastRow(), absColIndex, values);
+    }
+    
+    public String toString() {
+        CellReference crA = new CellReference(getFirstRow(), getFirstColumn());
+        CellReference crB = new CellReference(getLastRow(), getLastColumn());
+        return getClass().getName() + "[" +
+                crA.formatAsString() +
+                ':' +
+                crB.formatAsString() +
+                "]";
+    }
+}

Propchange: poi/trunk/src/java/org/apache/poi/ss/formula/CacheAreaEval.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/EvaluationCell.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/EvaluationCell.java?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/EvaluationCell.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/EvaluationCell.java Wed Sep 13 23:54:36 2017
@@ -18,6 +18,7 @@
 package org.apache.poi.ss.formula;
 
 import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.util.CellRangeAddress;
 
 /**
  * Abstracts a cell for the purpose of formula evaluation.  This interface represents both formula
@@ -56,6 +57,8 @@ public interface EvaluationCell {
 	String getStringCellValue();
 	boolean getBooleanCellValue();
 	int getErrorCellValue();
+	CellRangeAddress getArrayFormulaRange();
+	boolean isPartOfArrayFormulaGroup();
 
 	/**
 	 * Will return {@link CellType} in a future version of POI.

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluationContext.java Wed Sep 13 23:54:36 2017
@@ -22,11 +22,15 @@ import org.apache.poi.ss.formula.Collabo
 import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalName;
 import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
 import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheetRange;
+import org.apache.poi.ss.formula.constant.ErrorConstant;
 import org.apache.poi.ss.formula.eval.AreaEval;
+import org.apache.poi.ss.formula.eval.BoolEval;
 import org.apache.poi.ss.formula.eval.ErrorEval;
 import org.apache.poi.ss.formula.eval.ExternalNameEval;
 import org.apache.poi.ss.formula.eval.FunctionNameEval;
+import org.apache.poi.ss.formula.eval.NumberEval;
 import org.apache.poi.ss.formula.eval.RefEval;
+import org.apache.poi.ss.formula.eval.StringEval;
 import org.apache.poi.ss.formula.eval.ValueEval;
 import org.apache.poi.ss.formula.functions.FreeRefFunction;
 import org.apache.poi.ss.formula.ptg.Area3DPtg;
@@ -338,6 +342,42 @@ public final class OperationEvaluationCo
         return new LazyAreaEval(aptg.getFirstRow(), aptg.getFirstColumn(),
                 aptg.getLastRow(), aptg.getLastColumn(), sre);
     }
+
+    public ValueEval getAreaValueEval(int firstRowIndex, int firstColumnIndex,
+            int lastRowIndex, int lastColumnIndex, Object[][] tokens) {
+        
+        ValueEval values[] = new ValueEval[tokens.length * tokens[0].length];
+        
+        int index = 0;
+        for (int jdx = 0; jdx < tokens.length; jdx++) {
+            for (int idx = 0; idx < tokens[0].length; idx++) {
+                values[index++] = convertObjectEval(tokens[jdx][idx]);
+            }
+        }
+        
+        return new CacheAreaEval(firstRowIndex, firstColumnIndex, lastRowIndex,
+                                 lastColumnIndex, values);
+    }
+    
+    private ValueEval convertObjectEval(Object token) {
+        if (token == null) {
+            throw new RuntimeException("Array item cannot be null");
+        }
+        if (token instanceof String) {
+            return new StringEval((String)token);
+        }
+        if (token instanceof Double) {
+            return new NumberEval(((Double)token).doubleValue());
+        }
+        if (token instanceof Boolean) {
+            return BoolEval.valueOf(((Boolean)token).booleanValue());
+        }
+        if (token instanceof ErrorConstant) {
+            return ErrorEval.valueOf(((ErrorConstant)token).getErrorCode());
+        }
+        throw new IllegalArgumentException("Unexpected constant class (" + token.getClass().getName() + ")");            
+    }
+    
     
     public ValueEval getNameXEval(NameXPtg nameXPtg) {
         // Is the name actually on our workbook?

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/OperationEvaluatorFactory.java Wed Sep 13 23:54:36 2017
@@ -52,6 +52,7 @@ import org.apache.poi.ss.formula.eval.Un
 import org.apache.poi.ss.formula.eval.UnaryPlusEval;
 import org.apache.poi.ss.formula.eval.ValueEval;
 import org.apache.poi.ss.formula.function.FunctionMetadataRegistry;
+import org.apache.poi.ss.formula.functions.ArrayFunction;
 import org.apache.poi.ss.formula.functions.Function;
 import org.apache.poi.ss.formula.functions.Indirect;
 
@@ -116,6 +117,12 @@ final class OperationEvaluatorFactory {
 		Function result = _instancesByPtgClass.get(ptg);
 
 		if (result != null) {
+			EvaluationSheet evalSheet = ec.getWorkbook().getSheet(ec.getSheetIndex());
+		    EvaluationCell evalCell = evalSheet.getCell(ec.getRowIndex(), ec.getColumnIndex());
+		    
+		    if (evalCell.isPartOfArrayFormulaGroup() && result instanceof ArrayFunction)
+		        return ((ArrayFunction) result).evaluateArray(args, ec.getRowIndex(), ec.getColumnIndex());
+		                
 			return  result.evaluate(args, ec.getRowIndex(), (short) ec.getColumnIndex());
 		}
 

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java Wed Sep 13 23:54:36 2017
@@ -530,14 +530,15 @@ public final class WorkbookEvaluator {
             throw new IllegalStateException("evaluation stack not empty");
         }
         
-        // "unwrap" result to just the value relevant for the source cell if needed
         ValueEval result;
+        
         if (ec.isSingleValue()) {
-            result = dereferenceResult(value, ec.getRowIndex(), ec.getColumnIndex());
-        } else {
+            result = dereferenceResult(value, ec);
+        }
+        else {
             result = value;
         }
-        
+
         if (dbgEvaluationOutputIndent > 0) {
             EVAL_LOG.log(POILogger.INFO, dbgIndentStr + "finshed eval of "
                             + new CellReference(ec.getRowIndex(), ec.getColumnIndex()).formatAsString()
@@ -573,6 +574,38 @@ public final class WorkbookEvaluator {
         }
         return index-startIndex;
     }
+    
+    /**
+     * Dereferences a single value from any AreaEval or RefEval evaluation
+     * result. If the supplied evaluationResult is just a plain value, it is
+     * returned as-is.
+     *
+     * @return a {@link NumberEval}, {@link StringEval}, {@link BoolEval}, or
+     *         {@link ErrorEval}. Never <code>null</code>. {@link BlankEval} is
+     *         converted to {@link NumberEval#ZERO}
+     */
+    private static ValueEval dereferenceResult(ValueEval evaluationResult, OperationEvaluationContext ec) {
+        ValueEval value;
+
+        if (ec == null) {
+            throw new IllegalArgumentException("OperationEvaluationContext ec is null");
+        }
+        if (ec.getWorkbook() == null) {
+            throw new IllegalArgumentException("OperationEvaluationContext ec.getWorkbook() is null");
+        }
+
+        EvaluationSheet evalSheet = ec.getWorkbook().getSheet(ec.getSheetIndex());
+        EvaluationCell evalCell = evalSheet.getCell(ec.getRowIndex(), ec.getColumnIndex());
+ 
+        if (evalCell.isPartOfArrayFormulaGroup() && evaluationResult instanceof AreaEval) {
+            value = OperandResolver.getElementFromArray((AreaEval) evaluationResult, evalCell);
+        }
+        else {
+            value = dereferenceResult(evaluationResult, ec.getRowIndex(), ec.getColumnIndex());
+        }
+        
+        return value;
+    }
 
     /**
      * Dereferences a single value from any AreaEval or RefEval evaluation
@@ -666,6 +699,11 @@ public final class WorkbookEvaluator {
            AreaPtg aptg = (AreaPtg) ptg;
            return ec.getAreaEval(aptg.getFirstRow(), aptg.getFirstColumn(), aptg.getLastRow(), aptg.getLastColumn());
         }
+        
+        if (ptg instanceof ArrayPtg) {
+           ArrayPtg aptg = (ArrayPtg) ptg;
+           return ec.getAreaValueEval(0, 0, aptg.getRowCount() - 1, aptg.getColumnCount() - 1, aptg.getTokenArrayValues());
+        }
 
         if (ptg instanceof UnknownPtg) {
             // POI uses UnknownPtg when the encoded Ptg array seems to be corrupted.

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/eval/FunctionEval.java Wed Sep 13 23:54:36 2017
@@ -145,6 +145,7 @@ public final class FunctionEval {
 
         retval[82] = TextFunction.SEARCH;
         // 83: TRANSPOSE
+        retval[83] = MatrixFunction.TRANSPOSE;
 
         // 86: TYPE
 
@@ -182,6 +183,10 @@ public final class FunctionEval {
         retval[FunctionID.INDIRECT] = null; // Indirect.evaluate has different signature
 
         retval[162] = TextFunction.CLEAN;
+        
+        retval[163] = MatrixFunction.MDETERM;
+        retval[164] = MatrixFunction.MINVERSE;
+        retval[165] = MatrixFunction.MMULT;
 
         retval[167] = new IPMT();
         retval[168] = new PPMT();

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/eval/OperandResolver.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/eval/OperandResolver.java?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/eval/OperandResolver.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/eval/OperandResolver.java Wed Sep 13 23:54:36 2017
@@ -17,6 +17,9 @@
 
 package org.apache.poi.ss.formula.eval;
 
+import org.apache.poi.ss.formula.EvaluationCell;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.formula.eval.ErrorEval;
 import java.util.regex.Pattern;
 
 /**
@@ -70,6 +73,40 @@ public final class OperandResolver {
         }
         return result;
     }
+    
+    /**
+     * Retrieves a single value from an area evaluation utilizing the 2D indices of the cell
+     * within its own area reference to index the value in the area evaluation.
+     *
+     * @param ae area reference after evaluation
+     * @param cell the source cell of the formula that contains its 2D indices
+     * @return a <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt> or <tt>BlankEval</tt>. or <tt>ErrorEval<tt>
+     * Never <code>null</code>.
+     */
+
+    public static ValueEval getElementFromArray(AreaEval ae, EvaluationCell cell) {
+        CellRangeAddress range =  cell.getArrayFormulaRange();
+        int relativeRowIndex = cell.getRowIndex() - range.getFirstRow();
+        int relativeColIndex = cell.getColumnIndex() - range.getFirstColumn();
+        //System.out.println("Row: " + relativeRowIndex + " Col: " + relativeColIndex);
+        
+        if (ae.isColumn()) {
+            if (ae.isRow()) {
+                return ae.getRelativeValue(0, 0);
+            }
+            else if(relativeRowIndex < ae.getHeight()) {
+                return ae.getRelativeValue(relativeRowIndex, 0);
+            }
+        }
+        else if (!ae.isRow() && relativeRowIndex < ae.getHeight() && relativeColIndex < ae.getWidth()) {
+            return ae.getRelativeValue(relativeRowIndex, relativeColIndex);
+        }
+        else if (ae.isRow() && relativeColIndex < ae.getWidth()) {
+            return ae.getRelativeValue(0, relativeColIndex);
+        }
+        
+        return ErrorEval.NA;
+    }
 
     /**
      * Implements (some perhaps not well known) Excel functionality to select a single cell from an

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/eval/TwoOperandNumericOperation.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/eval/TwoOperandNumericOperation.java?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/eval/TwoOperandNumericOperation.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/eval/TwoOperandNumericOperation.java Wed Sep 13 23:54:36 2017
@@ -17,18 +17,29 @@
 
 package org.apache.poi.ss.formula.eval;
 
+import org.apache.poi.ss.formula.functions.ArrayFunction;
 import org.apache.poi.ss.formula.functions.Fixed2ArgFunction;
 import org.apache.poi.ss.formula.functions.Function;
+import org.apache.poi.ss.formula.functions.MatrixFunction.MutableValueCollector;
+import org.apache.poi.ss.formula.functions.MatrixFunction.TwoArrayArg;
 
 /**
  * @author Josh Micich
  */
-public abstract class TwoOperandNumericOperation extends Fixed2ArgFunction {
+public abstract class TwoOperandNumericOperation extends Fixed2ArgFunction implements ArrayFunction {
 
 	protected final double singleOperandEvaluate(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException {
 		ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
 		return OperandResolver.coerceValueToDouble(ve);
 	}
+	
+	public ValueEval evaluateArray(ValueEval args[], int srcRowIndex, int srcColumnIndex) {
+	    if (args.length != 2) {
+	        return ErrorEval.VALUE_INVALID;
+	    }
+	    return new ArrayEval().evaluate(srcRowIndex, srcColumnIndex, args[0], args[1]);
+	}
+	
 	public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
 		double result;
 		try {
@@ -52,6 +63,29 @@ public abstract class TwoOperandNumericO
 
 	protected abstract double evaluate(double d0, double d1) throws EvaluationException;
 
+	private final class ArrayEval extends TwoArrayArg {
+	    private final MutableValueCollector instance = new MutableValueCollector(false, true);
+        
+        protected double[] collectValues(ValueEval arg) throws EvaluationException {
+            return instance.collectValues(arg);
+        }
+	    
+	    protected double[][] evaluate(double[][] d1, double[][] d2) throws IllegalArgumentException, EvaluationException {
+	        int width = (d1[0].length < d2[0].length) ? d1[0].length : d2[0].length;
+	        int height = (d1.length < d2.length) ? d1.length : d2.length;
+	        
+	        double result[][] = new double[height][width];
+	        
+	        for (int j = 0; j < height; j++) {
+	            for (int i = 0; i < width; i++) {
+	                result[j][i] = TwoOperandNumericOperation.this.evaluate(d1[j][i], d2[j][i]);
+	            }
+	        }
+	        
+	        return result;
+	    }
+	}
+	
 	public static final Function AddEval = new TwoOperandNumericOperation() {
 		protected double evaluate(double d0, double d1) {
 			return d0+d1;

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationCell.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationCell.java?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationCell.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/eval/forked/ForkedEvaluationCell.java Wed Sep 13 23:54:36 2017
@@ -27,6 +27,8 @@ import org.apache.poi.ss.formula.eval.St
 import org.apache.poi.ss.formula.eval.ValueEval;
 import org.apache.poi.ss.usermodel.Cell;
 import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.util.CellRangeAddress;
+
 
 /**
  * Represents a cell being used for forked evaluation that has had a value set different from the
@@ -154,6 +156,16 @@ final class ForkedEvaluationCell impleme
 	public int getColumnIndex() {
 		return _masterCell.getColumnIndex();
 	}
+	
+	@Override
+	public CellRangeAddress getArrayFormulaRange() {
+		return _masterCell.getArrayFormulaRange();
+	}
+	
+	@Override
+	public boolean isPartOfArrayFormulaGroup() {
+		return _masterCell.isPartOfArrayFormulaGroup();
+	}
 	/**
 	 * Will return {@link CellType} in a future version of POI.
 	 * For forwards compatibility, do not hard-code cell type literals in your code.

Added: poi/trunk/src/java/org/apache/poi/ss/formula/functions/ArrayFunction.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/functions/ArrayFunction.java?rev=1808297&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/functions/ArrayFunction.java (added)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/functions/ArrayFunction.java Wed Sep 13 23:54:36 2017
@@ -0,0 +1,44 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.formula.functions;
+
+import org.apache.poi.ss.formula.eval.BlankEval;
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.eval.MissingArgEval;
+import org.apache.poi.ss.formula.eval.ValueEval;
+
+/**
+ * @author Robert Hulbert
+ * Common Interface for any excel built-in function that has implemented array formula functionality.
+ */
+
+public interface ArrayFunction {
+    
+    /**
+     * @param args the evaluated function arguments.  Empty values are represented with
+     * {@link BlankEval} or {@link MissingArgEval}, never <code>null</code>.
+     * @param srcRowIndex row index of the cell containing the formula under evaluation
+     * @param srcColumnIndex column index of the cell containing the formula under evaluation
+     * @return The evaluated result, possibly an {@link ErrorEval}, never <code>null</code>.
+     * <b>Note</b> - Excel uses the error code <i>#NUM!</i> instead of IEEE <i>NaN</i>, so when
+     * numeric functions evaluate to {@link Double#NaN} be sure to translate the result to {@link
+     * ErrorEval#NUM_ERROR}.
+     */
+
+    ValueEval evaluateArray(ValueEval[] args, int srcRowIndex, int srcColumnIndex);
+}

Propchange: poi/trunk/src/java/org/apache/poi/ss/formula/functions/ArrayFunction.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: poi/trunk/src/java/org/apache/poi/ss/formula/functions/MatrixFunction.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/functions/MatrixFunction.java?rev=1808297&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/functions/MatrixFunction.java (added)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/functions/MatrixFunction.java Wed Sep 13 23:54:36 2017
@@ -0,0 +1,339 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.ss.formula.functions;
+
+import org.apache.poi.ss.formula.CacheAreaEval;
+import org.apache.poi.ss.formula.eval.AreaEval;
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.eval.EvaluationException;
+import org.apache.poi.ss.formula.eval.NumberEval;
+import org.apache.poi.ss.formula.eval.OperandResolver;
+import org.apache.poi.ss.formula.eval.ValueEval;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.LUDecomposition;
+import org.apache.commons.math3.linear.MatrixUtils;
+
+/**
+ * @author Robert Hulbert
+ */
+public abstract class MatrixFunction implements Function{
+    
+    public static final void checkValues(double[] results) throws EvaluationException {
+        for (int idx = 0; idx < results.length; idx++) {
+            if (Double.isNaN(results[idx]) || Double.isInfinite(results[idx])) {
+                throw new EvaluationException(ErrorEval.NUM_ERROR);
+            }
+        }
+    }
+    
+    protected final double singleOperandEvaluate(ValueEval arg, int srcCellRow, int srcCellCol) throws EvaluationException {
+        ValueEval ve = OperandResolver.getSingleValue(arg, srcCellRow, srcCellCol);
+        return OperandResolver.coerceValueToDouble(ve);
+    }
+    
+    /* converts 1D array to 2D array for calculations */
+    private static double[][] fillDoubleArray(double[] vector, int rows, int cols) throws EvaluationException {
+        int i = 0, j = 0;
+        
+        if (rows < 1 || cols < 1 || vector.length < 1) {
+            throw new EvaluationException(ErrorEval.VALUE_INVALID);
+        }
+        
+        double[][] matrix = new double[rows][cols];
+        
+        for (int idx = 0; idx < vector.length; idx++) {
+            if (j < matrix.length) {
+                if (i == matrix[0].length) {
+                    i = 0;
+                    j++;
+                }
+                matrix[j][i++] = vector[idx];
+            } 
+        }
+        
+        return matrix;
+    }
+    
+    /* retrieves 1D array from 2D array after calculations */
+    private static double[] extractDoubleArray(double[][] matrix) throws EvaluationException {
+        int idx = 0;
+        
+        if (matrix == null || matrix.length < 1 || matrix[0].length < 1) {
+            throw new EvaluationException(ErrorEval.VALUE_INVALID);
+        }
+        
+        double[] vector = new double[matrix.length * matrix[0].length];
+        
+        for (int j = 0; j < matrix.length; j++) {
+            for (int i = 0; i < matrix[0].length; i++) {
+                vector[idx++] = matrix[j][i];
+            }
+        }
+        return vector;
+    }
+    
+    public static abstract class OneArrayArg extends Fixed1ArgFunction {
+        protected OneArrayArg() {
+            //no fields to initialize
+        }
+        
+        @Override
+        public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0) {
+            if (arg0 instanceof AreaEval) {
+                double result[] = null, resultArray[][];
+                int width = 1, height = 1;
+                
+                try {
+                    double values[] = collectValues(arg0);
+                    double array[][] = fillDoubleArray(values,((AreaEval) arg0).getHeight(),((AreaEval) arg0).getWidth());
+                    resultArray = evaluate(array);
+                    width = resultArray[0].length;
+                    height = resultArray.length;
+                    result = extractDoubleArray(resultArray);
+                    
+                    checkValues(result);
+                }
+                catch(EvaluationException e){
+                    return e.getErrorEval();
+                }
+                
+                ValueEval vals[] = new ValueEval[result.length];
+                
+                for (int idx = 0; idx < result.length; idx++) {
+                    vals[idx] = new NumberEval(result[idx]);
+                }
+                                
+                if (result.length == 1) {
+                    return vals[0];
+                }
+                else {
+                    /* find a better solution */
+                    return new CacheAreaEval(((AreaEval) arg0).getFirstRow(), ((AreaEval) arg0).getFirstColumn(), 
+                                            ((AreaEval) arg0).getFirstRow() + height - 1, 
+                                            ((AreaEval) arg0).getFirstColumn() + width - 1, vals);
+                }
+            }
+            else {
+                double result[][] = null;
+                try {
+                    double value = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
+                    double temp[][] = {{value}};
+                    result = evaluate(temp);
+                    NumericFunction.checkValue(result[0][0]);
+                }
+                catch (EvaluationException e) {
+                    return e.getErrorEval();
+                }
+                
+                return new NumberEval(result[0][0]);
+            }
+        }
+        
+        protected abstract double[][] evaluate(double[][] d1) throws EvaluationException;
+        protected abstract double[] collectValues(ValueEval arg) throws EvaluationException;
+    }
+    
+    public static abstract class TwoArrayArg extends Fixed2ArgFunction {
+        protected TwoArrayArg() {
+            //no fields to initialize
+        }
+        
+        @Override
+        public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
+            double result[];
+            int width = 1, height = 1;
+
+            try {
+                double array0[][], array1[][], resultArray[][];
+                
+                if (arg0 instanceof AreaEval) {
+                    try {
+                        double values[] = collectValues(arg0);
+                        array0 = fillDoubleArray(values, ((AreaEval) arg0).getHeight(), ((AreaEval) arg0).getWidth());
+                    }
+                    catch(EvaluationException e) {
+                        return e.getErrorEval();
+                    }
+                }
+                else {
+                    try {
+                        double value = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
+                        array0 = new double[][] {{value}};
+                    }
+                    catch (EvaluationException e) {
+                        return e.getErrorEval();
+                    }
+                }
+                    
+                if (arg1 instanceof AreaEval) {
+                   try {
+                      double values[] = collectValues(arg1);
+                      array1 = fillDoubleArray(values, ((AreaEval) arg1).getHeight(),((AreaEval) arg1).getWidth());
+                   }
+                   catch (EvaluationException e) {
+                      return e.getErrorEval();
+                   }
+                }
+                else {
+                    try {
+                        double value = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
+                        array1 = new double[][] {{value}};
+                    }
+                    catch (EvaluationException e) {
+                        return e.getErrorEval();
+                    }
+                }
+             
+                resultArray = evaluate(array0, array1);
+                width = resultArray[0].length;
+                height = resultArray.length;
+                result = extractDoubleArray(resultArray);
+                checkValues(result);
+            }
+            catch (EvaluationException e) {
+                return e.getErrorEval();
+            }
+            catch (IllegalArgumentException e) {
+                return ErrorEval.VALUE_INVALID;
+            }
+                
+                
+            ValueEval vals[] = new ValueEval[result.length];
+            
+            for (int idx = 0; idx < result.length; idx++) {
+                vals[idx] = new NumberEval(result[idx]);
+            }
+            
+            if (result.length == 1)
+                return vals[0];
+            else {
+                return new CacheAreaEval(((AreaEval) arg0).getFirstRow(), ((AreaEval) arg0).getFirstColumn(), 
+                        ((AreaEval) arg0).getFirstRow() + height - 1, 
+                        ((AreaEval) arg0).getFirstColumn() + width - 1, vals);
+            }
+      
+        }
+        
+        protected abstract double[][] evaluate(double[][] d1, double[][] d2) throws EvaluationException;
+        protected abstract double[] collectValues(ValueEval arg) throws EvaluationException;
+
+    }
+    
+    public static final class MutableValueCollector extends MultiOperandNumericFunction {
+        public MutableValueCollector(boolean isReferenceBoolCounted, boolean isBlankCounted) {
+            super(isReferenceBoolCounted, isBlankCounted);
+        }
+        public double[] collectValues(ValueEval...operands) throws EvaluationException {
+            return getNumberArray(operands);
+        }
+        protected double evaluate(double[] values) {
+            throw new IllegalStateException("should not be called");
+        }
+    }
+    
+    public static final Function MINVERSE = new OneArrayArg() {
+        private final MutableValueCollector instance = new MutableValueCollector(false, false);
+        
+        protected double[] collectValues(ValueEval arg) throws EvaluationException {
+            double[] values = instance.collectValues(arg);
+            
+            /* handle case where MDETERM is operating on an array that that is not completely filled*/
+            if (arg instanceof AreaEval && values.length == 1)
+                throw new EvaluationException(ErrorEval.VALUE_INVALID);
+            
+            return values;
+        }
+        
+        protected double[][] evaluate(double[][] d1) throws EvaluationException {
+            if (d1.length != d1[0].length) {
+                throw new EvaluationException(ErrorEval.VALUE_INVALID);
+            }
+            
+            Array2DRowRealMatrix temp = new Array2DRowRealMatrix(d1);
+            return MatrixUtils.inverse(temp).getData();
+        }
+    };
+    
+    public static final Function TRANSPOSE = new OneArrayArg() {
+        private final MutableValueCollector instance = new MutableValueCollector(false, true);
+        
+        protected double[] collectValues(ValueEval arg) throws EvaluationException {
+            return instance.collectValues(arg);
+        }
+        
+        protected double[][] evaluate(double[][] d1) throws EvaluationException {
+            
+            Array2DRowRealMatrix temp = new Array2DRowRealMatrix(d1);
+            return temp.transpose().getData();
+        }
+    };
+    
+    public static final Function MDETERM = new OneArrayArg() {
+        private final MutableValueCollector instance = new MutableValueCollector(false, false);
+        
+        protected double[] collectValues(ValueEval arg) throws EvaluationException {
+            double[] values = instance.collectValues(arg);
+            
+            /* handle case where MDETERM is operating on an array that that is not completely filled*/
+            if (arg instanceof AreaEval && values.length == 1)
+                throw new EvaluationException(ErrorEval.VALUE_INVALID);
+            
+            return instance.collectValues(arg);
+        }
+        
+        protected double[][] evaluate(double[][] d1) throws EvaluationException {
+            if (d1.length != d1[0].length) {
+                throw new EvaluationException(ErrorEval.VALUE_INVALID);
+            }
+            
+            double result[][] = new double[1][1];
+            Array2DRowRealMatrix temp = new Array2DRowRealMatrix(d1);
+            result[0][0] = (new LUDecomposition(temp)).getDeterminant();
+            return result;
+        }
+    };
+    
+    public static final Function MMULT = new TwoArrayArg() {
+        private final MutableValueCollector instance = new MutableValueCollector(false, false);
+        
+        protected double[] collectValues(ValueEval arg) throws EvaluationException {
+            double values[] = instance.collectValues(arg);
+            
+            /* handle case where MMULT is operating on an array that is not completely filled*/
+            if (arg instanceof AreaEval && values.length == 1)
+                throw new EvaluationException(ErrorEval.VALUE_INVALID);
+            
+            return values;
+        }
+        
+        protected double[][] evaluate(double[][] d1, double[][] d2) throws EvaluationException{
+            Array2DRowRealMatrix first = new Array2DRowRealMatrix(d1);
+            Array2DRowRealMatrix second = new Array2DRowRealMatrix(d2);
+            
+            try {
+                MatrixUtils.checkMultiplicationCompatible(first, second);
+            }
+            catch (DimensionMismatchException e) {
+                throw new EvaluationException(ErrorEval.VALUE_INVALID);
+            }
+                
+            return first.multiply(second).getData();    
+        }
+    };
+}

Propchange: poi/trunk/src/java/org/apache/poi/ss/formula/functions/MatrixFunction.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFEvaluationCell.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFEvaluationCell.java?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFEvaluationCell.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFEvaluationCell.java Wed Sep 13 23:54:36 2017
@@ -20,6 +20,7 @@ package org.apache.poi.xssf.streaming;
 import org.apache.poi.ss.formula.EvaluationCell;
 import org.apache.poi.ss.formula.EvaluationSheet;
 import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.util.Internal;
 
 /**
@@ -97,6 +98,17 @@ final class SXSSFEvaluationCell implemen
     public String getStringCellValue() {
         return _cell.getRichStringCellValue().getString();
     }
+    
+    @Override
+	public CellRangeAddress getArrayFormulaRange() {
+		return _cell.getArrayFormulaRange();
+	}
+	
+	@Override
+	public boolean isPartOfArrayFormulaGroup() {
+		return _cell.isPartOfArrayFormulaGroup();
+	}
+	
     /**
      * Will return {@link CellType} in a future version of POI.
      * For forwards compatibility, do not hard-code cell type literals in your code.

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java Wed Sep 13 23:54:36 2017
@@ -483,9 +483,12 @@ public final class XSSFCell implements C
         }
 
         CTCellFormula f = _cell.getF();
-        if (isPartOfArrayFormulaGroup() && f == null) {
-            XSSFCell cell = getSheet().getFirstCellInArrayFormula(this);
-            return cell.getCellFormula(fpb);
+        if (isPartOfArrayFormulaGroup()) {
+            /* In an excel generated array formula, the formula property might be set, but the string is empty in slave cells */
+            if (f == null || f.getStringValue().isEmpty()) {
+                XSSFCell cell = getSheet().getFirstCellInArrayFormula(this);
+                return cell.getCellFormula(fpb);
+            }
         }
         if (f.getT() == STCellFormulaType.SHARED) {
             return convertSharedFormula((int)f.getSi(), fpb == null ? XSSFEvaluationWorkbook.create(getSheet().getWorkbook()) : fpb);

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationCell.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationCell.java?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationCell.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFEvaluationCell.java Wed Sep 13 23:54:36 2017
@@ -20,6 +20,7 @@ package org.apache.poi.xssf.usermodel;
 import org.apache.poi.ss.formula.EvaluationCell;
 import org.apache.poi.ss.formula.EvaluationSheet;
 import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.util.Internal;
 
 /**
@@ -97,6 +98,17 @@ final class XSSFEvaluationCell implement
 	public String getStringCellValue() {
 		return _cell.getRichStringCellValue().getString();
 	}
+	
+	@Override
+	public CellRangeAddress getArrayFormulaRange() {
+		return _cell.getArrayFormulaRange();
+	}
+	
+	@Override
+	public boolean isPartOfArrayFormulaGroup() {
+		return _cell.isPartOfArrayFormulaGroup();
+	}
+	
 	/**
 	 * Will return {@link CellType} in a future version of POI.
 	 * For forwards compatibility, do not hard-code cell type literals in your code.

Added: poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestMatrixFormulasFromXMLSpreadsheet.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestMatrixFormulasFromXMLSpreadsheet.java?rev=1808297&view=auto
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestMatrixFormulasFromXMLSpreadsheet.java (added)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestMatrixFormulasFromXMLSpreadsheet.java Wed Sep 13 23:54:36 2017
@@ -0,0 +1,226 @@
+package org.apache.poi.xssf.usermodel;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+
+
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.functions.TestMathX;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.CellValue;
+import org.apache.poi.ss.usermodel.FormulaEvaluator;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.util.LocaleUtil;
+import org.apache.poi.xssf.XSSFTestDataSamples;
+import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import junit.framework.AssertionFailedError;
+
+@RunWith(Parameterized.class)
+public final class TestMatrixFormulasFromXMLSpreadsheet {
+    
+    private static XSSFWorkbook workbook;
+    private static Sheet sheet;
+    private static FormulaEvaluator evaluator;
+    private static Locale userLocale;
+    
+    /*
+     * Unlike TestFormulaFromSpreadsheet which this class is modified from, there is no
+     * differentiation between operators and functions, if more functionality is implemented with
+     * array formulas then it might be worth it to separate operators from functions
+     * 
+     * Also, output matrices are statically 3x3, if larger matrices wanted to be tested
+     * then adding matrix size parameter would be useful and parsing would be based off that.
+     */
+    
+    private static interface Navigator {
+        /**
+         * Name of the test spreadsheet (found in the standard test data folder)
+         */
+        String FILENAME = "MatrixFormulaEvalTestData.xlsx";
+        /**
+         * Row (zero-based) in the spreadsheet where operations start
+         */
+        int START_OPERATORS_ROW_INDEX = 1;
+        /**
+         * Column (zero-based) in the spreadsheet where operations start
+         */
+        int START_OPERATORS_COL_INDEX = 0;
+        /**
+         * Column (zero-based) in the spreadsheet where evaluations start
+         */
+        int START_RESULT_COL_INDEX = 7;
+        /**
+         * Column separation in the spreadsheet between evaluations and expected results
+         */
+        int COL_OFF_EXPECTED_RESULT = 3;
+        /**
+         * Row separation in the spreadsheet between operations
+         */
+        int ROW_OFF_NEXT_OP = 4;
+        /**
+         * Used to indicate when there are no more operations left
+         */
+        String END_OF_TESTS = "<END>";
+        
+    }
+    
+    /* Parameters for test case */
+    @Parameter(0)
+    public String targetFunctionName;
+    @Parameter(1)
+    public int formulasRowIdx;
+    
+    @AfterClass
+    public static void closeResource() throws Exception {
+        LocaleUtil.setUserLocale(userLocale);
+        workbook.close();
+    }
+    
+    /* generating parameter instances */
+    @Parameters(name="{0}")
+    public static Collection<Object[]> data() throws Exception {
+        // Function "Text" uses custom-formats which are locale specific
+        // can't set the locale on a per-testrun execution, as some settings have been
+        // already set, when we would try to change the locale by then
+        userLocale = LocaleUtil.getUserLocale();
+        LocaleUtil.setUserLocale(Locale.ROOT);
+        
+        workbook = XSSFTestDataSamples.openSampleWorkbook(Navigator.FILENAME);
+        sheet = workbook.getSheetAt(0);
+        evaluator = new XSSFFormulaEvaluator(workbook);
+        
+        List<Object[]> data = new ArrayList<Object[]>();
+        
+        processFunctionGroup(data, Navigator.START_OPERATORS_ROW_INDEX, null);
+        
+        return data;
+    }
+    
+    /**
+     * @param startRowIndex row index in the spreadsheet where the first function/operator is found
+     * @param testFocusFunctionName name of a single function/operator to test alone.
+     * Typically pass <code>null</code> to test all functions
+     */
+    private static void processFunctionGroup(List<Object[]> data, int startRowIndex, String testFocusFunctionName) {
+        for (int rowIndex = startRowIndex; true; rowIndex += Navigator.ROW_OFF_NEXT_OP) {
+            Row r = sheet.getRow(rowIndex);
+            String targetFunctionName = getTargetFunctionName(r);
+            assertNotNull("Test spreadsheet cell empty on row ("
+                    + (rowIndex) + "). Expected function name or '"
+                    + Navigator.END_OF_TESTS + "'", targetFunctionName);
+            if(targetFunctionName.equals(Navigator.END_OF_TESTS)) {
+                // found end of functions list
+                break;
+            }
+            if(testFocusFunctionName == null || targetFunctionName.equalsIgnoreCase(testFocusFunctionName)) {
+                data.add(new Object[]{targetFunctionName, rowIndex});
+            }
+        }
+    }
+    
+    @Test
+    public void processFunctionRow() {
+
+       int endColNum = Navigator.START_RESULT_COL_INDEX + Navigator.COL_OFF_EXPECTED_RESULT;
+
+       for (int rowNum = formulasRowIdx; rowNum < formulasRowIdx + Navigator.ROW_OFF_NEXT_OP - 1; rowNum++) {
+           for (int colNum = Navigator.START_RESULT_COL_INDEX; colNum < endColNum; colNum++) {
+               Row r = sheet.getRow(rowNum);
+               
+               /* mainly to escape row failures on MDETERM which only returns a scalar */
+               if (r == null) {
+                   continue;
+               }
+               
+               Cell c = sheet.getRow(rowNum).getCell(colNum);
+               
+               if (c == null || c.getCellTypeEnum() != CellType.FORMULA) {
+                   continue;
+               }
+    
+               CellValue actValue = evaluator.evaluate(c);
+               Cell expValue = sheet.getRow(rowNum).getCell(colNum + Navigator.COL_OFF_EXPECTED_RESULT);
+    
+               String msg = String.format(Locale.ROOT, "Function '%s': Formula: %s @ %d:%d"
+                       , targetFunctionName, c.getCellFormula(), rowNum, colNum);
+    
+               assertNotNull(msg + " - Bad setup data expected value is null", expValue);
+               assertNotNull(msg + " - actual value was null", actValue);
+    
+               final CellType cellType = expValue.getCellTypeEnum();
+               switch (cellType) {
+                   case BLANK:
+                       assertEquals(msg, CellType.BLANK, actValue.getCellTypeEnum());
+                       break;
+                   case BOOLEAN:
+                       assertEquals(msg, CellType.BOOLEAN, actValue.getCellTypeEnum());
+                       assertEquals(msg, expValue.getBooleanCellValue(), actValue.getBooleanValue());
+                       break;
+                   case ERROR:
+                       assertEquals(msg, CellType.ERROR, actValue.getCellTypeEnum());
+                       assertEquals(msg, ErrorEval.getText(expValue.getErrorCellValue()), ErrorEval.getText(actValue.getErrorValue()));
+                       break;
+                   case FORMULA: // will never be used, since we will call method after formula evaluation
+                       fail("Cannot expect formula as result of formula evaluation: " + msg);
+                   case NUMERIC:
+                       assertEquals(msg, CellType.NUMERIC, actValue.getCellTypeEnum());
+                       TestMathX.assertEquals(msg, expValue.getNumericCellValue(), actValue.getNumberValue(), TestMathX.POS_ZERO, TestMathX.DIFF_TOLERANCE_FACTOR);
+                       break;
+                   case STRING:
+                       assertEquals(msg, CellType.STRING, actValue.getCellTypeEnum());
+                       assertEquals(msg, expValue.getRichStringCellValue().getString(), actValue.getStringValue());
+                       break;
+                   default:
+                       fail("Unexpected cell type: " + cellType);
+               }
+           }
+       }
+   }
+    
+    /**
+     * @return <code>null</code> if cell is missing, empty or blank
+     */
+    private static String getTargetFunctionName(Row r) {
+        if(r == null) {
+            System.err.println("Warning - given null row, can't figure out function name");
+            return null;
+        }
+        Cell cell = r.getCell(Navigator.START_OPERATORS_COL_INDEX);
+        System.err.println(String.valueOf(Navigator.START_OPERATORS_COL_INDEX));
+        if(cell == null) {
+            System.err.println("Warning - Row " + r.getRowNum() + " has no cell " + Navigator.START_OPERATORS_COL_INDEX + ", can't figure out function name");
+            return null;
+        }
+        if(cell.getCellTypeEnum() == CellType.BLANK) {
+            return null;
+        }
+        if(cell.getCellTypeEnum() == CellType.STRING) {
+            return cell.getRichStringCellValue().getString();
+        }
+
+        throw new AssertionFailedError("Bad cell type for 'function name' column: ("
+                + cell.getCellTypeEnum() + ") row (" + (r.getRowNum() +1) + ")");
+    }
+    
+    
+    
+    
+    
+
+}

Propchange: poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestMatrixFormulasFromXMLSpreadsheet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestMatrixFormulasFromBinarySpreadsheet.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestMatrixFormulasFromBinarySpreadsheet.java?rev=1808297&view=auto
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestMatrixFormulasFromBinarySpreadsheet.java (added)
+++ poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestMatrixFormulasFromBinarySpreadsheet.java Wed Sep 13 23:54:36 2017
@@ -0,0 +1,223 @@
+package org.apache.poi.hssf.usermodel;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.functions.TestMathX;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.CellValue;
+import org.apache.poi.ss.usermodel.FormulaEvaluator;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.util.LocaleUtil;
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import junit.framework.AssertionFailedError;
+
+@RunWith(Parameterized.class)
+public final class TestMatrixFormulasFromBinarySpreadsheet {
+    
+    private static HSSFWorkbook workbook;
+    private static Sheet sheet;
+    private static FormulaEvaluator evaluator;
+    private static Locale userLocale;
+    
+    /*
+     * Unlike TestFormulaFromSpreadsheet which this class is modified from, there is no
+     * differentiation between operators and functions, if more functionality is implemented with
+     * array formulas then it might be worth it to separate operators from functions
+     * 
+     * Also, output matrices are statically 3x3, if larger matrices wanted to be tested
+     * then adding matrix size parameter would be useful and parsing would be based off that.
+     */
+    
+    private static interface Navigator {
+        /**
+         * Name of the test spreadsheet (found in the standard test data folder)
+         */
+        String FILENAME = "MatrixFormulaEvalTestData.xls";
+        /**
+         * Row (zero-based) in the spreadsheet where operations start
+         */
+        int START_OPERATORS_ROW_INDEX = 1;
+        /**
+         * Column (zero-based) in the spreadsheet where operations start
+         */
+        int START_OPERATORS_COL_INDEX = 0;
+        /**
+         * Column (zero-based) in the spreadsheet where evaluations start
+         */
+        int START_RESULT_COL_INDEX = 7;
+        /**
+         * Column separation in the spreadsheet between evaluations and expected results
+         */
+        int COL_OFF_EXPECTED_RESULT = 3;
+        /**
+         * Row separation in the spreadsheet between operations
+         */
+        int ROW_OFF_NEXT_OP = 4;
+        /**
+         * Used to indicate when there are no more operations left
+         */
+        String END_OF_TESTS = "<END>";
+        
+    }
+    
+    /* Parameters for test case */
+    @Parameter(0)
+    public String targetFunctionName;
+    @Parameter(1)
+    public int formulasRowIdx;
+    
+    @AfterClass
+    public static void closeResource() throws Exception {
+        LocaleUtil.setUserLocale(userLocale);
+        workbook.close();
+    }
+    
+    /* generating parameter instances */
+    @Parameters(name="{0}")
+    public static Collection<Object[]> data() throws Exception {
+        // Function "Text" uses custom-formats which are locale specific
+        // can't set the locale on a per-testrun execution, as some settings have been
+        // already set, when we would try to change the locale by then
+        userLocale = LocaleUtil.getUserLocale();
+        LocaleUtil.setUserLocale(Locale.ROOT);
+        
+        workbook = HSSFTestDataSamples.openSampleWorkbook(Navigator.FILENAME);
+        sheet = workbook.getSheetAt(0);
+        evaluator = new HSSFFormulaEvaluator(workbook);
+        
+        List<Object[]> data = new ArrayList<Object[]>();
+        
+        processFunctionGroup(data, Navigator.START_OPERATORS_ROW_INDEX, null);
+        
+        return data;
+    }
+    
+    /**
+     * @param startRowIndex row index in the spreadsheet where the first function/operator is found
+     * @param testFocusFunctionName name of a single function/operator to test alone.
+     * Typically pass <code>null</code> to test all functions
+     */
+    private static void processFunctionGroup(List<Object[]> data, int startRowIndex, String testFocusFunctionName) {
+        for (int rowIndex = startRowIndex; true; rowIndex += Navigator.ROW_OFF_NEXT_OP) {
+            Row r = sheet.getRow(rowIndex);
+            String targetFunctionName = getTargetFunctionName(r);
+            assertNotNull("Test spreadsheet cell empty on row ("
+                    + (rowIndex) + "). Expected function name or '"
+                    + Navigator.END_OF_TESTS + "'", targetFunctionName);
+            if(targetFunctionName.equals(Navigator.END_OF_TESTS)) {
+                // found end of functions list
+                break;
+            }
+            if(testFocusFunctionName == null || targetFunctionName.equalsIgnoreCase(testFocusFunctionName)) {
+                data.add(new Object[]{targetFunctionName, rowIndex});
+            }
+        }
+    }
+    
+    @Test
+    public void processFunctionRow() {
+
+       int endColNum = Navigator.START_RESULT_COL_INDEX + Navigator.COL_OFF_EXPECTED_RESULT;
+
+       for (int rowNum = formulasRowIdx; rowNum < formulasRowIdx + Navigator.ROW_OFF_NEXT_OP - 1; rowNum++) {
+           for (int colNum = Navigator.START_RESULT_COL_INDEX; colNum < endColNum; colNum++) {
+               Row r = sheet.getRow(rowNum);
+               
+               /* mainly to escape row failures on MDETERM which only returns a scalar */
+               if (r == null) {
+                   continue;
+               }
+               
+               Cell c = sheet.getRow(rowNum).getCell(colNum);
+               
+               if (c == null || c.getCellTypeEnum() != CellType.FORMULA) {
+                   continue;
+               }
+    
+               CellValue actValue = evaluator.evaluate(c);
+               Cell expValue = sheet.getRow(rowNum).getCell(colNum + Navigator.COL_OFF_EXPECTED_RESULT);
+    
+               String msg = String.format(Locale.ROOT, "Function '%s': Formula: %s @ %d:%d"
+                       , targetFunctionName, c.getCellFormula(), rowNum, colNum);
+    
+               assertNotNull(msg + " - Bad setup data expected value is null", expValue);
+               assertNotNull(msg + " - actual value was null", actValue);
+    
+               final CellType cellType = expValue.getCellTypeEnum();
+               switch (cellType) {
+                   case BLANK:
+                       assertEquals(msg, CellType.BLANK, actValue.getCellTypeEnum());
+                       break;
+                   case BOOLEAN:
+                       assertEquals(msg, CellType.BOOLEAN, actValue.getCellTypeEnum());
+                       assertEquals(msg, expValue.getBooleanCellValue(), actValue.getBooleanValue());
+                       break;
+                   case ERROR:
+                       assertEquals(msg, CellType.ERROR, actValue.getCellTypeEnum());
+                       assertEquals(msg, ErrorEval.getText(expValue.getErrorCellValue()), ErrorEval.getText(actValue.getErrorValue()));
+                       break;
+                   case FORMULA: // will never be used, since we will call method after formula evaluation
+                       fail("Cannot expect formula as result of formula evaluation: " + msg);
+                   case NUMERIC:
+                       assertEquals(msg, CellType.NUMERIC, actValue.getCellTypeEnum());
+                       TestMathX.assertEquals(msg, expValue.getNumericCellValue(), actValue.getNumberValue(), TestMathX.POS_ZERO, TestMathX.DIFF_TOLERANCE_FACTOR);
+                       break;
+                   case STRING:
+                       assertEquals(msg, CellType.STRING, actValue.getCellTypeEnum());
+                       assertEquals(msg, expValue.getRichStringCellValue().getString(), actValue.getStringValue());
+                       break;
+                   default:
+                       fail("Unexpected cell type: " + cellType);
+               }
+           }
+       }
+   }
+    
+    /**
+     * @return <code>null</code> if cell is missing, empty or blank
+     */
+    private static String getTargetFunctionName(Row r) {
+        if(r == null) {
+            System.err.println("Warning - given null row, can't figure out function name");
+            return null;
+        }
+        Cell cell = r.getCell(Navigator.START_OPERATORS_COL_INDEX);
+        System.err.println(String.valueOf(Navigator.START_OPERATORS_COL_INDEX));
+        if(cell == null) {
+            System.err.println("Warning - Row " + r.getRowNum() + " has no cell " + Navigator.START_OPERATORS_COL_INDEX + ", can't figure out function name");
+            return null;
+        }
+        if(cell.getCellTypeEnum() == CellType.BLANK) {
+            return null;
+        }
+        if(cell.getCellTypeEnum() == CellType.STRING) {
+            return cell.getRichStringCellValue().getString();
+        }
+
+        throw new AssertionFailedError("Bad cell type for 'function name' column: ("
+                + cell.getCellTypeEnum() + ") row (" + (r.getRowNum() +1) + ")");
+    }
+    
+    
+    
+    
+    
+
+}

Propchange: poi/trunk/src/testcases/org/apache/poi/hssf/usermodel/TestMatrixFormulasFromBinarySpreadsheet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java?rev=1808297&r1=1808296&r2=1808297&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/ss/formula/TestWorkbookEvaluator.java Wed Sep 13 23:54:36 2017
@@ -27,6 +27,7 @@ import java.io.IOException;
 
 import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
 import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
@@ -58,7 +59,10 @@ public class TestWorkbookEvaluator {
     private static final double EPSILON = 0.0000001;
 
     private static ValueEval evaluateFormula(Ptg[] ptgs) {
-        OperationEvaluationContext ec = new OperationEvaluationContext(null, null, 0, 0, 0, null);
+        HSSFWorkbook wb = new HSSFWorkbook();
+        wb.createSheet().createRow(0).createCell(0);
+        EvaluationWorkbook ewb = HSSFEvaluationWorkbook.create(wb);
+        OperationEvaluationContext ec = new OperationEvaluationContext(null, ewb, 0, 0, 0, null);
         return new WorkbookEvaluator(null, null, null).evaluateFormula(ec, ptgs);
     }
 

Added: poi/trunk/test-data/spreadsheet/MatrixFormulaEvalTestData.xls
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/MatrixFormulaEvalTestData.xls?rev=1808297&view=auto
==============================================================================
Binary file - no diff available.

Propchange: poi/trunk/test-data/spreadsheet/MatrixFormulaEvalTestData.xls
------------------------------------------------------------------------------
    svn:mime-type = application/vnd.ms-excel

Added: poi/trunk/test-data/spreadsheet/MatrixFormulaEvalTestData.xlsx
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/spreadsheet/MatrixFormulaEvalTestData.xlsx?rev=1808297&view=auto
==============================================================================
Binary file - no diff available.

Propchange: poi/trunk/test-data/spreadsheet/MatrixFormulaEvalTestData.xlsx
------------------------------------------------------------------------------
--- svn:mime-type (added)
+++ svn:mime-type Wed Sep 13 23:54:36 2017
@@ -0,0 +1 @@
+application/vnd.openxmlformats-officedocument.spreadsheetml.sheet



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org