/**
  * Append a row to the end of the data frame, where all row fields are string encoded.
  *
  * @param row
  */
 public void appendRow(String[] row) {
   ensureAllocatedColumns();
   for (int j = 0; j < row.length; j++) _coldata.get(j).append(row[j]);
   _numRows++;
 }
  /**
   * @param in
   * @param ixrange
   * @param brlen
   * @param bclen
   * @param rlen
   * @param clen
   * @param outlist
   * @throws DMLRuntimeException
   */
  public static void performShift(
      Pair<Long, FrameBlock> in,
      IndexRange ixrange,
      int brlenLeft,
      int clenLeft /*, int bclen*/,
      long rlen,
      long clen,
      ArrayList<Pair<Long, FrameBlock>> outlist)
      throws DMLRuntimeException {
    Long ix = in.getKey();
    FrameBlock fb = in.getValue();
    long start_lhs_globalRowIndex = ixrange.rowStart + (ix - 1);
    long start_lhs_globalColIndex = ixrange.colStart;
    long end_lhs_globalRowIndex = start_lhs_globalRowIndex + fb.getNumRows() - 1;
    long end_lhs_globalColIndex = ixrange.colEnd;

    long start_lhs_rowIndex = UtilFunctions.computeBlockIndex(start_lhs_globalRowIndex, brlenLeft);
    long end_lhs_rowIndex = UtilFunctions.computeBlockIndex(end_lhs_globalRowIndex, brlenLeft);

    for (long leftRowIndex = start_lhs_rowIndex; leftRowIndex <= end_lhs_rowIndex; leftRowIndex++) {

      // Calculate global index of right hand side block
      long lhs_rl = Math.max((leftRowIndex - 1) * brlenLeft + 1, start_lhs_globalRowIndex);
      long lhs_ru = Math.min(leftRowIndex * brlenLeft, end_lhs_globalRowIndex);
      long lhs_cl = start_lhs_globalColIndex;
      long lhs_cu = end_lhs_globalColIndex;

      int lhs_lrl = UtilFunctions.computeCellInBlock(lhs_rl, brlenLeft);
      int lhs_lru = UtilFunctions.computeCellInBlock(lhs_ru, brlenLeft);
      int lhs_lcl = (int) lhs_cl - 1;
      int lhs_lcu = (int) lhs_cu - 1;

      long rhs_rl = lhs_rl - (ixrange.rowStart - 1) - (ix - 1);
      long rhs_ru = rhs_rl + (lhs_ru - lhs_rl);
      long rhs_cl = lhs_cl - ixrange.colStart + 1;
      long rhs_cu = rhs_cl + (lhs_cu - lhs_cl);

      // local indices are 0 (zero) based.
      int rhs_lrl = (int) (UtilFunctions.computeCellInBlock(rhs_rl, fb.getNumRows()));
      int rhs_lru = (int) (UtilFunctions.computeCellInBlock(rhs_ru, fb.getNumRows()));
      int rhs_lcl = (int) rhs_cl - 1;
      int rhs_lcu = (int) rhs_cu - 1;

      FrameBlock slicedRHSBlk =
          fb.sliceOperations(rhs_lrl, rhs_lru, rhs_lcl, rhs_lcu, new FrameBlock());

      int lbclen = clenLeft;

      List<ValueType> schemaPartialLeft = Collections.nCopies(lhs_lcl, ValueType.STRING);
      List<ValueType> schemaRHS =
          UtilFunctions.getSubSchema(fb.getSchema(), rhs_lcl, rhs_lcl - lhs_lcl + lhs_lcu);
      List<ValueType> schema = new ArrayList<ValueType>(schemaPartialLeft);
      schema.addAll(schemaRHS);
      List<ValueType> schemaPartialRight =
          Collections.nCopies(lbclen - schema.size(), ValueType.STRING);
      schema.addAll(schemaPartialRight);
      FrameBlock resultBlock = new FrameBlock(schema);
      int iRHSRows =
          (int)
              (leftRowIndex <= rlen / brlenLeft
                  ? brlenLeft
                  : rlen - (rlen / brlenLeft) * brlenLeft);
      resultBlock.ensureAllocatedColumns(iRHSRows);

      resultBlock =
          resultBlock.leftIndexingOperations(
              slicedRHSBlk, lhs_lrl, lhs_lru, lhs_lcl, lhs_lcu, new FrameBlock());
      outlist.add(new Pair<Long, FrameBlock>((leftRowIndex - 1) * brlenLeft + 1, resultBlock));
    }
  }