docs: protected void prunePlanAlternatives(List<PlanNode> plans) {
  		if (plans.isEmpty()) {
  			throw new CompilerException("No plan meeting the requirements could be created @ " + this + ". Most likely reason: Too restrictive plan hints.");
  		}
  		// shortcut for the simple case
  		if (plans.size() == 1) {
  			return;
  		}
  		
  		// we can only compare plan candidates that made equal choices
  		// at the branching points. for each choice at a branching point,
  		// we need to keep the cheapest (wrt. interesting properties).
  		// if we do not keep candidates for each branch choice, we might not
  		// find branch compatible candidates when joining the branches back.
  		
  		// for pruning, we are quasi AFTER the node, so in the presence of
  		// branches, we need form the per-branch-choice groups by the choice
  		// they made at the latest un-joined branching node. Note that this is
  		// different from the check for branch compatibility of candidates, as
  		// this happens on the input sub-plans and hence BEFORE the node (therefore
  		// it is relevant to find the latest (partially) joined branch point.
  		
  		if (this.openBranches == null || this.openBranches.isEmpty()) {
  			prunePlanAlternativesWithCommonBranching(plans);
  		} else {
  			// partition the candidates into groups that made the same sub-plan candidate
  			// choice at the latest unclosed branch point
  			
  			final OptimizerNode[] branchDeterminers = new OptimizerNode[this.openBranches.size()];
  			
  			for (int i = 0; i < branchDeterminers.length; i++) {
  				branchDeterminers[i] = this.openBranches.get(this.openBranches.size() - 1 - i).getBranchingNode();
  			}
  			
  			// this sorter sorts by the candidate choice at the branch point
  			Comparator<PlanNode> sorter = new Comparator<PlanNode>() {
  				
  				@Override
  				public int compare(PlanNode o1, PlanNode o2) {
  					for (OptimizerNode branchDeterminer : branchDeterminers) {
  						PlanNode n1 = o1.getCandidateAtBranchPoint(branchDeterminer);
  						PlanNode n2 = o2.getCandidateAtBranchPoint(branchDeterminer);
  						int hash1 = System.identityHashCode(n1);
  						int hash2 = System.identityHashCode(n2);
  
  						if (hash1 != hash2) {
  							return hash1 - hash2;
  						}
  					}
  					return 0;
  				}
  			};
  			Collections.sort(plans, sorter);
  			
  			List<PlanNode> result = new ArrayList<PlanNode>();
  			List<PlanNode> turn = new ArrayList<PlanNode>();
  			
  			final PlanNode[] determinerChoice = new PlanNode[branchDeterminers.length];
  
  			while (!plans.isEmpty()) {
  				// take one as the determiner
  				turn.clear();
  				PlanNode determiner = plans.remove(plans.size() - 1);
  				turn.add(determiner);
  				
  				for (int i = 0; i < determinerChoice.length; i++) {
  					determinerChoice[i] = determiner.getCandidateAtBranchPoint(branchDeterminers[i]);
  				}
  
  				// go backwards through the plans and find all that are equal
  				boolean stillEqual = true;
  				for (int k = plans.size() - 1; k >= 0 && stillEqual; k--) {
  					PlanNode toCheck = plans.get(k);
  					
  					for (int i = 0; i < branchDeterminers.length; i++) {
  						PlanNode checkerChoice = toCheck.getCandidateAtBranchPoint(branchDeterminers[i]);
  					
  						if (checkerChoice != determinerChoice[i]) {
  							// not the same anymore
  							stillEqual = false;
  							break;
  						}
  					}
  					
  					if (stillEqual) {
  						// the same
  						plans.remove(k);
  						turn.add(toCheck);
  					}
  				}
  
  				// now that we have only plans with the same branch alternatives, prune!
  				if (turn.size() > 1) {
  					prunePlanAlternativesWithCommonBranching(turn);
  				}
  				result.addAll(turn);
  			}
  
  			// after all turns are complete
  			plans.clear();
  			plans.addAll(result);
  		}
  	}