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);
  }