docs: private Set<String> doResolveFeatures(Collection<String> rootFeatures, Set<String> preResolved, SelectionContext selectionContext) {
          // first pass; process the roots until we have selected all the candidates for multiple versions;
          // need to reset the initial black list count so we can recalculate it during the first pass
          selectionContext.resetInitialBlackListCount();
          Set<String> result = processCurrentPermutation(rootFeatures, preResolved, selectionContext);
  
          // if the first pass resulted in no conflicts return the results (optimistic)
          if (selectionContext.getResult().getConflicts().isEmpty()) {
              selectionContext.selectCurrentPermutation();
              return result;
          }
  
          // oh oh, we have conflicts;
          // NOTE, if the current solution has more conflicts than the initial count (black list)
          // then that means one of the toleration (postponed) choices we made introduced an
          // addition conflict.  That implies that a better solution may be available.
          // As long as there are more conflicts than the number of initial root conflicts
          // and there is a different permutation to try do another pass
          while (selectionContext.currentHasMoreThanInitialBlackListCount() && selectionContext.popPermutation()) {
              result = processCurrentPermutation(rootFeatures, preResolved, selectionContext);
          }
  
          // Return the best solution found
          selectionContext.restoreBestSolution();
          return selectionContext.getResult().getResolvedFeatures();
      }