protected static boolean aggressiveDedup(ReduceSinkOperator cRS, ReduceSinkOperator pRS,
          ReduceSinkDeduplicateProcCtx dedupCtx) throws SemanticException {
    assert cRS.getNumParent() == 1;

    ReduceSinkDesc cConf = cRS.getConf();
    ReduceSinkDesc pConf = pRS.getConf();
    List<ExprNodeDesc> cKeys = cConf.getKeyCols();
    List<ExprNodeDesc> pKeys = pConf.getKeyCols();

    if (!checkSelectSingleBranchOnly(cRS, pRS)) {
      return false;
    }

    // If child keys are null or empty, we bail out
    if (cKeys == null || cKeys.isEmpty()) {
      return false;
    }
    // If parent keys are null or empty, we bail out
    if (pKeys == null || pKeys.isEmpty()) {
      return false;
    }

    // Backtrack key columns of cRS to pRS
    // If we cannot backtrack any of the columns, bail out
    List<ExprNodeDesc> cKeysInParentRS = ExprNodeDescUtils.backtrack(cKeys, cRS, pRS);
    for (int i = 0; i < cKeysInParentRS.size(); i++) {
      ExprNodeDesc pexpr = cKeysInParentRS.get(i);
      if (pexpr == null) {
        // We cannot backtrack the expression, we bail out
        return false;
      }
    }
    cRS.getConf().setKeyCols(cKeysInParentRS);

    // Backtrack partition columns of cRS to pRS
    // If we cannot backtrack any of the columns, bail out
    List<ExprNodeDesc> cPartitionInParentRS = ExprNodeDescUtils.backtrack(
            cConf.getPartitionCols(), cRS, pRS);
    for (int i = 0; i < cPartitionInParentRS.size(); i++) {
      ExprNodeDesc pexpr = cPartitionInParentRS.get(i);
      if (pexpr == null) {
        // We cannot backtrack the expression, we bail out
        return false;
      }
    }
    cRS.getConf().setPartitionCols(cPartitionInParentRS);

    // Backtrack value columns of cRS to pRS
    // If we cannot backtrack any of the columns, bail out
    List<ExprNodeDesc> cValueInParentRS = ExprNodeDescUtils.backtrack(
            cConf.getValueCols(), cRS, pRS);
    for (int i = 0; i < cValueInParentRS.size(); i++) {
      ExprNodeDesc pexpr = cValueInParentRS.get(i);
      if (pexpr == null) {
        // We cannot backtrack the expression, we bail out
        return false;
      }
    }
    cRS.getConf().setValueCols(cValueInParentRS);

    // Backtrack bucket columns of cRS to pRS (if any)
    // If we cannot backtrack any of the columns, bail out
    if (cConf.getBucketCols() != null) {
      List<ExprNodeDesc> cBucketInParentRS = ExprNodeDescUtils.backtrack(
              cConf.getBucketCols(), cRS, pRS);
      for (int i = 0; i < cBucketInParentRS.size(); i++) {
        ExprNodeDesc pexpr = cBucketInParentRS.get(i);
        if (pexpr == null) {
          // We cannot backtrack the expression, we bail out
          return false;
        }
      }
      cRS.getConf().setBucketCols(cBucketInParentRS);
    }

    // Update column expression map
    for (Entry<String, ExprNodeDesc> e : cRS.getColumnExprMap().entrySet()) {
      e.setValue(ExprNodeDescUtils.backtrack(e.getValue(), cRS, pRS));
    }

    // Replace pRS with cRS and remove operator sequence from pRS to cRS
    // Recall that the sequence must be pRS-SEL*-cRS
    Operator<? extends OperatorDesc> parent = cRS.getParentOperators().get(0);
    while (parent != pRS) {
      dedupCtx.addRemovedOperator(parent);
      parent = parent.getParentOperators().get(0);
    }
    dedupCtx.addRemovedOperator(pRS);
    cRS.getParentOperators().clear();
    for (Operator<? extends OperatorDesc> op : pRS.getParentOperators()) {
      op.replaceChild(pRS, cRS);
      cRS.getParentOperators().add(op);
    }
    pRS.getParentOperators().clear();
    pRS.getChildOperators().clear();

    return true;
  }
If number of reducers for RS is -1, the RS can have any number of reducers.
It's generally true except for order-by or forced bucketing cases.
if both of num-reducers are not -1, those number should be the same.
ql/src/java/org/apache/hadoop/hive/ql/optimizer/correlation/ReduceSinkDeDuplicationUtils.java