@Override
 protected BasicIndexScan getIndex(
     final LeafNodePlan plan,
     final BasicIndexScan indexScan,
     final Collection<Variable> sortCriterium,
     final Map<Variable, Literal> minima,
     final Map<Variable, Literal> maxima) {
   BasicIndexScan index1 =
       new MemoryIndexScan(
           (OperatorIDTuple) null,
           plan.getTriplePatterns(),
           indexScan.getGraphConstraint(),
           indexScan.getRoot());
   index1.setIntersectionVariables(plan.getVariables());
   index1.setUnionVariables(plan.getVariables());
   return index1;
 }
  @Override
  protected BasicOperator generateJoin(
      final InnerNodePlan inp,
      final Root root,
      final BasicOperator left,
      final BasicOperator right,
      final Collection<Variable> sortCriterium,
      final Map<TriplePattern, Map<Variable, VarBucket>> selectivity) {
    // left-deep-tree or right-deep-tree?
    if (left instanceof BasicIndexScan && right instanceof BasicIndexScan) {
      if (((BasicIndexScan) right).getTriplePattern().size() == 1
          || ((BasicIndexScan) left).getTriplePattern().size() == 1) {
        final Collection<TriplePattern> ctp;
        if (inp.getLeft().getCost() < inp.getRight().getCost()) {
          ctp = ((BasicIndexScan) left).getTriplePattern();
          ctp.addAll(((BasicIndexScan) right).getTriplePattern());
        } else {
          ctp = ((BasicIndexScan) right).getTriplePattern();
          ctp.addAll(((BasicIndexScan) left).getTriplePattern());
        }
        ((BasicIndexScan) left).setTriplePatterns(ctp);
        root.remove((BasicIndexScan) right);
        return left;
      }
    }
    Join join = new Join();
    join.setEstimatedCardinality(inp.getCardinality());

    // TODO check if necessary (or is it just necessary for RDF3X???)!

    if (!(inp.getLeft() instanceof InnerNodePlan
                && ((InnerNodePlan) inp.getLeft()).getJoinType() == JoinType.DEFAULT)
            && (inp.getRight() instanceof InnerNodePlan
                && ((InnerNodePlan) inp.getRight()).getJoinType() == JoinType.DEFAULT)
        || (inp.getLeft() instanceof LeafNodePlan && inp.getRight() instanceof InnerNodePlan)) {
      moveToLeft(inp.getRight().getTriplePatterns(), root);
    } else if (!(inp.getRight() instanceof InnerNodePlan
                && ((InnerNodePlan) inp.getRight()).getJoinType() == JoinType.DEFAULT)
            && (inp.getLeft() instanceof InnerNodePlan
                && ((InnerNodePlan) inp.getLeft()).getJoinType() == JoinType.DEFAULT)
        || (inp.getRight() instanceof LeafNodePlan && inp.getLeft() instanceof InnerNodePlan)) {
      moveToLeft(inp.getLeft().getTriplePatterns(), root);
    } else if (inp.getLeft().getCost() > inp.getRight().getCost()) {
      System.out.println(
          "Card. of joins with estimated lower cost vs. est. higher cost:"
              + inp.getRight().getCardinality()
              + "<->"
              + inp.getLeft().getCardinality());
      System.out.println(
          "Cost of joins with estimated lower cost vs. est. higher cost:"
              + inp.getRight().getCost()
              + "<->"
              + inp.getLeft().getCost());
      moveToLeft(inp.getRight().getTriplePatterns(), root);
    } else {
      System.out.println(
          "Card. of joins with estimated lower cost vs. est. higher cost:"
              + inp.getLeft().getCardinality()
              + "<->"
              + inp.getRight().getCardinality());
      System.out.println(
          "Cost of joins with estimated lower cost vs. est. higher cost:"
              + inp.getLeft().getCost()
              + "<->"
              + inp.getRight().getCost());
      moveToLeft(inp.getLeft().getTriplePatterns(), root);
    }

    join.setIntersectionVariables(inp.getJoinPartner());
    join.setUnionVariables(inp.getVariables());
    left.setSucceedingOperator(new OperatorIDTuple(join, 0));
    right.setSucceedingOperator(new OperatorIDTuple(join, 1));
    join.addPrecedingOperator(left);
    join.addPrecedingOperator(right);
    return join;
  }