/**
   * Create a DbIterator over group aggregate results.
   *
   * @return a DbIterator whose tuples are the pair (groupVal, aggregateVal) if using group, or a
   *     single (aggregateVal) if no grouping. The aggregateVal is determined by the type of
   *     aggregate specified in the constructor.
   */
  public DbIterator iterator() {
    /* TODO: write about iterator() call recalculates all the aggregates, which seems necessary given how the test case is formulated
    (merge one tuple, get iterator, expect the result to be updated). Note that recalculate is not necessary unless the source tuples
    change, but as a simplification here we implement it like this. */
    TupleDesc td;
    ArrayList<Tuple> tuples = new ArrayList<Tuple>();

    if (isGrouping()) {
      Type[] typeAr = new Type[2];
      typeAr[0] = this.gbFieldType;
      typeAr[1] = Type.INT_TYPE;
      td = new TupleDesc(typeAr);

      for (Map.Entry<Field, ArrayList<IntField>> entry : this.tupleStorage.entrySet()) {
        IntField aggregate = this.getAggregate(entry.getValue());
        Tuple tup = new Tuple(td);
        tup.setField(0, entry.getKey());
        tup.setField(1, aggregate);
        tuples.add(tup);
      }
    } else {
      Type[] typeAr = new Type[1];
      typeAr[0] = Type.INT_TYPE;
      td = new TupleDesc(typeAr);

      Tuple tup = new Tuple(td);
      tup.setField(0, this.getAggregate(this.tupleStorageNoGrouping));
      tuples.add(tup);
    }

    TupleIterator iterator = new TupleIterator(td, tuples);
    return iterator;
  }
  /** Suck up tuples from the source file. */
  private Tuple readNextTuple(DataInputStream dis, int slotId) throws NoSuchElementException {
    // if associated bit is not set, read forward to the next tuple, and
    // return null.
    if (!getSlot(slotId)) {
      for (int i = 0; i < td.getSize(); i++) {
        try {
          dis.readByte();
        } catch (IOException e) {
          throw new NoSuchElementException("error reading empty tuple");
        }
      }
      return null;
    }

    // read fields in the tuple
    Tuple t = new Tuple(td);
    RecordID rid = new RecordID(pid, slotId);
    t.setRecordID(rid);
    try {
      for (int j = 0; j < td.numFields(); j++) {
        Field f = td.getType(j).parse(dis);
        t.setField(j, f);
      }
    } catch (java.text.ParseException e) {
      // e.printStackTrace();
      throw new NoSuchElementException("parsing error!");
    }

    return t;
  }
  /** Unit test for Tuple.getField() and Tuple.setField() */
  @Test
  public void modifyFields() {
    TupleDesc td = Utility.getTupleDesc(2);

    Tuple tup = new Tuple(td);
    tup.setField(0, new IntField(-1));
    tup.setField(1, new IntField(0));

    assertEquals(new IntField(-1), tup.getField(0));
    assertEquals(new IntField(0), tup.getField(1));

    tup.setField(0, new IntField(1));
    tup.setField(1, new IntField(37));

    assertEquals(new IntField(1), tup.getField(0));
    assertEquals(new IntField(37), tup.getField(1));
  }
  @Test
  public void new_toStringTest() {
    int length = 10;
    String name = "td";
    Tuple t = new Tuple(Utility.getTupleDesc(length, name));
    for (int i = 0; i < length; i++) {
      t.setField(i, new TestField());
    }

    String tString = t.toString();

    // Last character should be \n.
    assertEquals("\n", tString.substring(tString.length() - 1));

    // Only last character is \n.
    assertFalse(tString.substring(0, tString.length() - 1).contains("\n"));

    // Split string on any white character.
    String[] tStringAr = tString.substring(0, tString.length() - 1).split("\\s+");
    assertEquals(length, tStringAr.length);
  }