@NotNull
  private static PsiSubstitutor replaceVariables(Collection<InferenceVariable> inferenceVariables) {
    final List<InferenceVariable> targetVars = new ArrayList<InferenceVariable>();
    PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
    final InferenceVariable[] oldVars =
        inferenceVariables.toArray(new InferenceVariable[inferenceVariables.size()]);
    for (InferenceVariable variable : oldVars) {
      final InferenceVariable newVariable =
          new InferenceVariable(
              variable.getCallContext(), variable.getParameter(), variable.getName());
      substitutor =
          substitutor.put(
              variable,
              JavaPsiFacade.getElementFactory(variable.getProject()).createType(newVariable));
      targetVars.add(newVariable);
      if (variable.isThrownBound()) {
        newVariable.setThrownBound();
      }
    }

    for (int i = 0; i < targetVars.size(); i++) {
      InferenceVariable var = targetVars.get(i);
      for (InferenceBound boundType : InferenceBound.values()) {
        for (PsiType bound : oldVars[i].getBounds(boundType)) {
          var.addBound(substitutor.substitute(bound), boundType, null);
        }
      }
    }
    return substitutor;
  }
 /** a = b imply every bound of a matches a bound of b and vice versa */
 private boolean eqCrossVariables(InferenceVariable inferenceVariable, List<PsiType> eqBounds) {
   boolean needFurtherIncorporation = false;
   for (PsiType eqBound : eqBounds) {
     final InferenceVariable inferenceVar = mySession.getInferenceVariable(eqBound);
     if (inferenceVar != null) {
       for (InferenceBound inferenceBound : InferenceBound.values()) {
         for (PsiType bound : inferenceVariable.getBounds(inferenceBound)) {
           if (mySession.getInferenceVariable(bound) != inferenceVar) {
             needFurtherIncorporation |= inferenceVar.addBound(bound, inferenceBound);
           }
         }
         for (PsiType bound : inferenceVar.getBounds(inferenceBound)) {
           if (mySession.getInferenceVariable(bound) != inferenceVariable) {
             needFurtherIncorporation |= inferenceVariable.addBound(bound, inferenceBound);
           }
         }
       }
     }
   }
   return needFurtherIncorporation;
 }