public void operateOnPartition(
      PartitionDefinition definition, RowIterator inputIterator, RowEmitter outputEmitter) {
    errorHandler.enterOperateOnPartition(definition, inputIterator, outputEmitter);

    try {
      // Collect input rows for observed and expected values
      ArrayList<Double> expectedList = new ArrayList<Double>();
      ArrayList<Long> observedList = new ArrayList<Long>();

      while (inputIterator.advanceToNextRow()) {
        errorHandler.enterOperateOnRow(inputIterator, outputEmitter);
        if (inputIterator.isNullAt(observedArgumentIdx)
            || inputIterator.isNullAt(expectedArgumentIdx))
          throw new IllegalArgumentException("observed and expected values cannot be null");

        expectedList.add(inputIterator.getDoubleAt(expectedArgumentIdx));
        observedList.add(inputIterator.getLongAt(observedArgumentIdx));
        errorHandler.exitOperateOnRow();
      }

      double[] expected = new double[expectedList.size()];
      for (int i = 0; i < expected.length; i++) expected[i] = expectedList.get(i);

      long[] observed = new long[observedList.size()];
      for (int i = 0; i < observed.length; i++) observed[i] = observedList.get(i);

      // Run test
      double pValue = chiSquareTest.chiSquareTest(expected, observed);

      // Emit result
      accumulator.emit(inputIterator, outputEmitter);
      outputEmitter.addDouble(pValue);
      outputEmitter.emitRow();
    } catch (IllegalArgumentException e) {
      errorHandler.catchException(e);
      return; // End this partition and go to next if stopOnError is set to false (otherwise
      // exception is thrown)
    }

    errorHandler.exitOperateOnPartition();
  }
 /* (non-Javadoc)
  * @see com.asterdata.ncluster.sqlmr.Drainable#drainOutputRows(com.asterdata.ncluster.sqlmr.data.RowEmitter)
  *
  * Send last logging information
  */
 public void drainOutputRows(RowEmitter outputEmitter) {
   errorHandler.drainOutputRows();
 }