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() + ")");
  }
 private static void throwFirstError(TwoDEval areaEval) throws EvaluationException {
   int height = areaEval.getHeight();
   int width = areaEval.getWidth();
   for (int rrIx = 0; rrIx < height; rrIx++) {
     for (int rcIx = 0; rcIx < width; rcIx++) {
       ValueEval ve = areaEval.getValue(rrIx, rcIx);
       if (ve instanceof ErrorEval) {
         throw new EvaluationException((ErrorEval) ve);
       }
     }
   }
 }
 private static boolean areasAllSameSize(TwoDEval[] args, int height, int width) {
   for (int i = 0; i < args.length; i++) {
     TwoDEval areaEval = args[i];
     // check that height and width match
     if (areaEval.getHeight() != height) {
       return false;
     }
     if (areaEval.getWidth() != width) {
       return false;
     }
   }
   return true;
 }
  private static ValueEval evaluateAreaSumProduct(ValueEval[] evalArgs) throws EvaluationException {
    int maxN = evalArgs.length;
    TwoDEval[] args = new TwoDEval[maxN];
    try {
      System.arraycopy(evalArgs, 0, args, 0, maxN);
    } catch (ArrayStoreException e) {
      // one of the other args was not an AreaRef
      return ErrorEval.VALUE_INVALID;
    }

    TwoDEval firstArg = args[0];

    int height = firstArg.getHeight();
    int width = firstArg.getWidth(); // TODO - junit

    // first check dimensions
    if (!areasAllSameSize(args, height, width)) {
      // normally this results in #VALUE!,
      // but errors in individual cells take precedence
      for (int i = 1; i < args.length; i++) {
        throwFirstError(args[i]);
      }
      return ErrorEval.VALUE_INVALID;
    }

    double acc = 0;

    for (int rrIx = 0; rrIx < height; rrIx++) {
      for (int rcIx = 0; rcIx < width; rcIx++) {
        double term = 1D;
        for (int n = 0; n < maxN; n++) {
          double val = getProductTerm(args[n].getValue(rrIx, rcIx), false);
          term *= val;
        }
        acc += term;
      }
    }

    return new NumberEval(acc);
  }