public ValueEval evaluate(ValueEval[] args, int srcCellRow, int srcCellCol) {

    int maxN = args.length;

    if (maxN < 1) {
      return ErrorEval.VALUE_INVALID;
    }
    ValueEval firstArg = args[0];
    try {
      if (firstArg instanceof NumericValueEval) {
        return evaluateSingleProduct(args);
      }
      if (firstArg instanceof RefEval) {
        return evaluateSingleProduct(args);
      }
      if (firstArg instanceof TwoDEval) {
        TwoDEval ae = (TwoDEval) firstArg;
        if (ae.isRow() && ae.isColumn()) {
          return evaluateSingleProduct(args);
        }
        return evaluateAreaSumProduct(args);
      }
    } catch (EvaluationException e) {
      return e.getErrorEval();
    }
    throw new RuntimeException(
        "Invalid arg type for SUMPRODUCT: (" + firstArg.getClass().getName() + ")");
  }
  /**
   * Determines a <code>double</code> value for the specified <code>ValueEval</code>.
   *
   * @param isScalarProduct <code>false</code> for SUMPRODUCTs over area refs.
   * @throws EvaluationException if <code>ve</code> represents an error value.
   *     <p>Note - string values and empty cells are interpreted differently depending on <code>
   *     isScalarProduct</code>. For scalar products, if any term is blank or a string, the error
   *     (#VALUE!) is raised. For area (sum)products, if any term is blank or a string, the result
   *     is zero.
   */
  private static double getProductTerm(ValueEval ve, boolean isScalarProduct)
      throws EvaluationException {

    if (ve instanceof BlankEval || ve == null) {
      // TODO - shouldn't BlankEval.INSTANCE be used always instead of null?
      // null seems to occur when the blank cell is part of an area ref (but not reliably)
      if (isScalarProduct) {
        throw new EvaluationException(ErrorEval.VALUE_INVALID);
      }
      return 0;
    }

    if (ve instanceof ErrorEval) {
      throw new EvaluationException((ErrorEval) ve);
    }
    if (ve instanceof StringEval) {
      if (isScalarProduct) {
        throw new EvaluationException(ErrorEval.VALUE_INVALID);
      }
      // Note for area SUMPRODUCTs, string values are interpreted as zero
      // even if they would parse as valid numeric values
      return 0;
    }
    if (ve instanceof NumericValueEval) {
      NumericValueEval nve = (NumericValueEval) ve;
      return nve.getNumberValue();
    }
    throw new RuntimeException("Unexpected value eval class (" + ve.getClass().getName() + ")");
  }