private int removeFromAssigns(
     AbstractLogicalOperator op, Set<LogicalVariable> toRemove, IOptimizationContext context)
     throws AlgebricksException {
   switch (op.getOperatorTag()) {
     case ASSIGN:
       {
         AssignOperator assign = (AssignOperator) op;
         if (removeUnusedVarsAndExprs(toRemove, assign.getVariables(), assign.getExpressions())) {
           context.computeAndSetTypeEnvironmentForOperator(assign);
         }
         return assign.getVariables().size();
       }
     case AGGREGATE:
       {
         AggregateOperator agg = (AggregateOperator) op;
         if (removeUnusedVarsAndExprs(toRemove, agg.getVariables(), agg.getExpressions())) {
           context.computeAndSetTypeEnvironmentForOperator(agg);
         }
         return agg.getVariables().size();
       }
     case UNNEST:
       {
         UnnestOperator uOp = (UnnestOperator) op;
         LogicalVariable pVar = uOp.getPositionalVariable();
         if (pVar != null && toRemove.contains(pVar)) {
           uOp.setPositionalVariable(null);
         }
         break;
       }
     case UNIONALL:
       {
         UnionAllOperator unionOp = (UnionAllOperator) op;
         if (removeUnusedVarsFromUnionAll(unionOp, toRemove)) {
           context.computeAndSetTypeEnvironmentForOperator(unionOp);
         }
         return unionOp.getVariableMappings().size();
       }
   }
   return -1;
 }
  @Override
  public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
      throws AlgebricksException {
    AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue();

    if (op.getOperatorTag() != LogicalOperatorTag.INNERJOIN) {
      return false;
    }
    AbstractBinaryJoinOperator join = (AbstractBinaryJoinOperator) op;

    ILogicalExpression expr = join.getCondition().getValue();
    if (expr.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
      return false;
    }
    AbstractFunctionCallExpression fexp = (AbstractFunctionCallExpression) expr;
    FunctionIdentifier fi = fexp.getFunctionIdentifier();
    if (!fi.equals(AlgebricksBuiltinFunctions.AND)) {
      return false;
    }
    List<Mutable<ILogicalExpression>> eqVarVarComps = new ArrayList<Mutable<ILogicalExpression>>();
    List<Mutable<ILogicalExpression>> otherPredicates =
        new ArrayList<Mutable<ILogicalExpression>>();
    for (Mutable<ILogicalExpression> arg : fexp.getArguments()) {
      if (isEqVarVar(arg.getValue())) {
        eqVarVarComps.add(arg);
      } else {
        otherPredicates.add(arg);
      }
    }
    if (eqVarVarComps.isEmpty() || otherPredicates.isEmpty()) {
      return false;
    }
    // pull up
    ILogicalExpression pulledCond = makeCondition(otherPredicates, context);
    SelectOperator select =
        new SelectOperator(new MutableObject<ILogicalExpression>(pulledCond), false, null);
    ILogicalExpression newJoinCond = makeCondition(eqVarVarComps, context);
    join.getCondition().setValue(newJoinCond);
    select.getInputs().add(new MutableObject<ILogicalOperator>(join));
    opRef.setValue(select);
    context.computeAndSetTypeEnvironmentForOperator(select);
    return true;
  }
  @Override
  public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
      throws AlgebricksException {

    AbstractLogicalOperator unnest = (AbstractLogicalOperator) opRef.getValue();
    if (unnest.getOperatorTag() != LogicalOperatorTag.UNNEST) {
      return false;
    }
    UnnestOperator unnestOpRef = (UnnestOperator) opRef.getValue();
    Mutable<ILogicalOperator> unionOp = unnest.getInputs().get(0);

    AbstractLogicalOperator unionAbstractOp = (AbstractLogicalOperator) unionOp.getValue();
    if (unionAbstractOp.getOperatorTag() != LogicalOperatorTag.UNIONALL) {
      return false;
    }

    LogicalVariable unnestVar1 = context.newVar();
    UnnestOperator unnest1 =
        new UnnestOperator(
            unnestVar1,
            new MutableObject<ILogicalExpression>(
                unnestOpRef.getExpressionRef().getValue().cloneExpression()));
    LogicalVariable unnestVar2 = context.newVar();
    UnnestOperator unnest2 =
        new UnnestOperator(
            unnestVar2,
            new MutableObject<ILogicalExpression>(
                unnestOpRef.getExpressionRef().getValue().cloneExpression()));

    // Getting the two topmost branched and adding them as an input to the unnests:
    Mutable<ILogicalOperator> branch1 = unionAbstractOp.getInputs().get(0);
    ILogicalOperator agg1 = branch1.getValue();
    List<LogicalVariable> agg1_var = new ArrayList<LogicalVariable>();
    VariableUtilities.getLiveVariables(agg1, agg1_var);
    Mutable<ILogicalOperator> branch2 = unionAbstractOp.getInputs().get(1);
    ILogicalOperator agg2 = branch2.getValue();
    List<LogicalVariable> agg2_var = new ArrayList<LogicalVariable>();
    VariableUtilities.getLiveVariables(agg2, agg2_var);

    // Modifying the unnest so it has the right variable
    List<LogicalVariable> var_unnest_1 = new ArrayList<LogicalVariable>();
    unnest1.getExpressionRef().getValue().getUsedVariables(var_unnest_1);
    unnest1.getExpressionRef().getValue().substituteVar(var_unnest_1.get(0), agg1_var.get(0));

    List<LogicalVariable> var_unnest2 = new ArrayList<LogicalVariable>();
    unnest2.getExpressionRef().getValue().getUsedVariables(var_unnest2);
    unnest2.getExpressionRef().getValue().substituteVar(var_unnest2.get(0), agg2_var.get(0));

    unnest1.getInputs().add(branch1);
    unnest2.getInputs().add(branch2);
    context.computeAndSetTypeEnvironmentForOperator(unnest1);
    context.computeAndSetTypeEnvironmentForOperator(unnest2);

    // creating a new union operator with the updated logical variables
    List<Triple<LogicalVariable, LogicalVariable, LogicalVariable>> varMap =
        new ArrayList<Triple<LogicalVariable, LogicalVariable, LogicalVariable>>(1);
    Triple<LogicalVariable, LogicalVariable, LogicalVariable> union_triple_vars =
        new Triple<LogicalVariable, LogicalVariable, LogicalVariable>(
            unnestVar1, unnestVar2, unnestOpRef.getVariables().get(0));
    varMap.add(union_triple_vars);
    UnionAllOperator unionOpFinal = new UnionAllOperator(varMap);

    unionOpFinal.getInputs().add(new MutableObject<ILogicalOperator>(unnest1));
    unionOpFinal.getInputs().add(new MutableObject<ILogicalOperator>(unnest2));

    context.computeAndSetTypeEnvironmentForOperator(unionOpFinal);

    opRef.setValue(unionOpFinal);
    return true;
  }
  @Override
  public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
      throws AlgebricksException {

    if (!opRef.getValue().getOperatorTag().equals(LogicalOperatorTag.ASSIGN)) {
      return false;
    }

    AssignOperator assignUnion = (AssignOperator) opRef.getValue();

    if (assignUnion.getExpressions().get(0).getValue().getExpressionTag()
        != LogicalExpressionTag.FUNCTION_CALL) return false;

    AbstractFunctionCallExpression u =
        (AbstractFunctionCallExpression) assignUnion.getExpressions().get(0).getValue();
    if (!AsterixBuiltinFunctions.UNION.equals(u.getFunctionIdentifier())) {
      return false;
    }

    // Retrieving the logical variables for the union from the two aggregates which are inputs to
    // the join
    Mutable<ILogicalOperator> join = assignUnion.getInputs().get(0);

    LogicalOperatorTag tag1 = join.getValue().getOperatorTag();
    if (tag1 != LogicalOperatorTag.INNERJOIN && tag1 != LogicalOperatorTag.LEFTOUTERJOIN) {
      return false;
    }
    AbstractBinaryJoinOperator join1 = (AbstractBinaryJoinOperator) join.getValue();
    ILogicalExpression cond1 = join1.getCondition().getValue();
    // don't try to push a product down
    if (!OperatorPropertiesUtil.isAlwaysTrueCond(cond1)) {
      return false;
    }

    List<Mutable<ILogicalOperator>> joinInputs = join.getValue().getInputs();

    Mutable<ILogicalOperator> left_branch = joinInputs.get(0);
    Mutable<ILogicalOperator> right_branch = joinInputs.get(1);

    List<LogicalVariable> input1Var = new ArrayList<LogicalVariable>();
    VariableUtilities.getProducedVariables(left_branch.getValue(), input1Var);

    List<LogicalVariable> input2Var = new ArrayList<LogicalVariable>();
    VariableUtilities.getProducedVariables(right_branch.getValue(), input2Var);

    List<Triple<LogicalVariable, LogicalVariable, LogicalVariable>> varMap =
        new ArrayList<Triple<LogicalVariable, LogicalVariable, LogicalVariable>>(1);
    Triple<LogicalVariable, LogicalVariable, LogicalVariable> triple =
        new Triple<LogicalVariable, LogicalVariable, LogicalVariable>(
            input1Var.get(0), input2Var.get(0), assignUnion.getVariables().get(0));
    varMap.add(triple);
    UnionAllOperator unionOp = new UnionAllOperator(varMap);

    unionOp.getInputs().add(left_branch);
    unionOp.getInputs().add(right_branch);

    context.computeAndSetTypeEnvironmentForOperator(unionOp);

    opRef.setValue(unionOp);

    return true;
  }