@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
 public List<List<LeafNodePlan>> split(List<LeafNodePlan> initialPlans) {
   // We determine the maximum number of possible merge joins!
   final HashMap<Item, LinkedList<LeafNodePlan>> mergeJoins =
       new HashMap<Item, LinkedList<LeafNodePlan>>();
   for (final LeafNodePlan tp : initialPlans) {
     for (final Variable var : tp.getVariables()) {
       // just the group the leaf nodes with common variables together (this group can be surely
       // joined only with merge joins)
       LinkedList<LeafNodePlan> lltp = mergeJoins.get(var);
       if (lltp == null) {
         lltp = new LinkedList<LeafNodePlan>();
       }
       lltp.add(tp);
       mergeJoins.put(var, lltp);
     }
   }
   // determine the group the most possible merge joins
   int maxMergeJoins = 0;
   for (final LinkedList<LeafNodePlan> lltp : mergeJoins.values()) {
     if (lltp.size() > maxMergeJoins) {
       maxMergeJoins = lltp.size();
     }
   }
   // avoid the trivial cases (only few merge joins or the whole leaf nodes form a star-shaped
   // join)
   if (maxMergeJoins > 2 && maxMergeJoins < initialPlans.size()) {
     final LinkedList<LeafNodePlan> otherSubgraph = new LinkedList<LeafNodePlan>();
     for (final LinkedList<LeafNodePlan> lltp : mergeJoins.values()) {
       if (lltp.size() == maxMergeJoins) {
         // determine the other leaf nodes, which are not contained in the subgraph with maximum
         // number of merge joins!
         for (LeafNodePlan lfp : initialPlans) {
           if (!lltp.contains(lfp)) {
             otherSubgraph.add(lfp);
           }
         }
         // now check if otherSubGraph contains a cartesian product, which would be unacceptable!
         List<List<LeafNodePlan>> cartesianProduct2 =
             SplitCartesianProduct.cartesianProducts(otherSubgraph);
         if (cartesianProduct2.size() <= 1) {
           // no cartesian product detected => acceptable splitting!
           List<List<LeafNodePlan>> result = new LinkedList<List<LeafNodePlan>>();
           result.add(lltp);
           result.add(otherSubgraph);
           return result;
         }
       }
     }
   }
   // this strategy did not work!
   List<List<LeafNodePlan>> result = new LinkedList<List<LeafNodePlan>>();
   result.add(initialPlans);
   return result;
 }