private void reduce() {
    if (myConstraints.isEmpty()) {
      return;
    }

    if (myCurrentBinding.isCyclic()) {
      reduceCyclicVariables();
    }

    final Map<PsiTypeVariable, Constraint> myTypeVarConstraints = new HashMap<>();
    final Map<PsiTypeVariable, Constraint> myVarTypeConstraints = new HashMap<>();

    for (final Constraint constr : myConstraints) {
      final PsiType left = constr.getLeft();
      final PsiType right = constr.getRight();

      switch ((left instanceof PsiTypeVariable ? 0 : 1) + (right instanceof PsiTypeVariable ? 0 : 2)) {
        case 0 -> { }
        case 1 -> {
          final Constraint c = myTypeVarConstraints.get(right);

          if (c == null) {
            final Constraint d = myVarTypeConstraints.get(right);

            if (d != null) {
              reduceInterval(constr, d);
              return;
            }

            myTypeVarConstraints.put((PsiTypeVariable)right, constr);
          }
          else {
            reduceTypeVar(constr, c);
            return;
          }
        }
        case 2 -> {
          final Constraint c = myVarTypeConstraints.get(left);

          if (c == null) {
            final Constraint d = myTypeVarConstraints.get(left);

            if (d != null) {
              reduceInterval(d, constr);
              return;
            }

            myVarTypeConstraints.put((PsiTypeVariable)left, constr);
          }
          else {
            reduceVarType(constr, c);
            return;
          }
        }
        case 3 -> {
          reduceTypeType(constr);
          return;
        }
      }
    }

    //T1 < a < b ... < T2
    {
      for (final Constraint constr : myConstraints) {
        final PsiType left = constr.getLeft();
        final PsiType right = constr.getRight();

        if (!(left instanceof PsiTypeVariable) && right instanceof PsiTypeVariable) {
          Set<PsiTypeVariable> bound = new PsiTypeVarCollector().getSet(left);

          if (bound.contains(right)) {
            myConstraints.remove(constr);
            mySons = new ResolverTree[]{applyRule(myBindingFactory.create(((PsiTypeVariable)right), Bottom.BOTTOM))};

            return;
          }

          final PsiManager manager = PsiManager.getInstance(myProject);
          final PsiType leftType = left instanceof PsiWildcardType ? ((PsiWildcardType)left).getBound() : left;
          final PsiType[] types = getTypeRange(PsiType.getJavaLangObject(manager, GlobalSearchScope.allScope(myProject)), leftType);

          mySons = new ResolverTree[types.length];

          if (types.length > 0) {
            myConstraints.remove(constr);
          }

          for (int i = 0; i < types.length; i++) {
            final PsiType type = types[i];
            mySons[i] = applyRule(myBindingFactory.create(((PsiTypeVariable)right), type));
          }

          return;
        }
      }
    }

    //T1 < a < b < ...
    {
      Set<PsiTypeVariable> haveLeftBound = new HashSet<>();

      Constraint target = null;
      Set<PsiTypeVariable> boundVariables = new HashSet<>();

      for (final Constraint constr : myConstraints) {
        final PsiType leftType = constr.getLeft();
        final PsiType rightType = constr.getRight();

        if (leftType instanceof PsiTypeVariable) {
          boundVariables.add((PsiTypeVariable)leftType);

          if (rightType instanceof PsiTypeVariable) {
            boundVariables.add((PsiTypeVariable)rightType);
            haveLeftBound.add(((PsiTypeVariable)rightType));
          }
          else if (!Util.bindsTypeVariables(rightType)) {
            target = constr;
          }
        }
      }

      if (target == null) {
        if (mySettings.exhaustive()) {
          for (final Constraint constr : myConstraints) {
            final PsiType left = constr.getLeft();
            final PsiType right = constr.getRight();

            PsiType[] range = null;
            PsiTypeVariable var = null;

            if (left instanceof PsiTypeVariable && !(right instanceof PsiTypeVariable)) {
              range = getTypeRange(PsiType.getJavaLangObject(PsiManager.getInstance(myProject), GlobalSearchScope.allScope(myProject)),
                                   right);
              var = (PsiTypeVariable)left;
            }

            if (range == null && right instanceof PsiTypeVariable && !(left instanceof PsiTypeVariable)) {
              range = new PsiType[]{right};
              var = (PsiTypeVariable)right;
            }

            if (range != null) {
              mySons = new ResolverTree[range.length];

              for (int i = 0; i < range.length; i++) {
                mySons[i] = applyRule(myBindingFactory.create(var, range[i]));
              }

              return;
            }
          }
        }

        Binding binding = myBindingFactory.create();

        for (final PsiTypeVariable var : myBindingFactory.getBoundVariables()) {
          if (!myCurrentBinding.binds(var) && !boundVariables.contains(var)) {
            binding = binding.compose(myBindingFactory.create(var, Bottom.BOTTOM));
          }
        }

        if (!binding.nonEmpty()) {
          myConstraints.clear();
        }

        mySons = new ResolverTree[]{applyRule(binding)};
      }
      else {
        final PsiType type = target.getRight();
        final PsiTypeVariable var = (PsiTypeVariable)target.getLeft();

        final Binding binding =
          (haveLeftBound.contains(var) || type instanceof PsiWildcardType) || !mySettings.cookToWildcards()
          ? myBindingFactory.create(var, type)
          : myBindingFactory.create(var, PsiWildcardType.createExtends(PsiManager.getInstance(myProject), type));

        myConstraints.remove(target);

        mySons = new ResolverTree[]{applyRule(binding)};
      }
    }
  }
private void reduce()