private CauseItem @NotNull [] findRelationCause(RelationType relationType,
                                                  MemoryStateChange leftChange, DfaValue leftValue,
                                                  MemoryStateChange rightChange, DfaValue rightValue) {
    ProgressManager.checkCanceled();
    FactDefinition<DfaNullability> leftNullability = leftChange.findFact(leftValue, FactExtractor.nullability());
    FactDefinition<DfaNullability> rightNullability = rightChange.findFact(rightValue, FactExtractor.nullability());
    if ((leftNullability.myFact == DfaNullability.NULL && rightNullability.myFact == DfaNullability.NOT_NULL) ||
        (rightNullability.myFact == DfaNullability.NULL && leftNullability.myFact == DfaNullability.NOT_NULL)) {
      return new CauseItem[]{
        findNullabilityCause(leftChange, leftNullability.myFact),
        findNullabilityCause(rightChange, rightNullability.myFact)};
    }

    FactDefinition<LongRangeSet> leftRange = leftChange.findFact(leftValue, FactExtractor.range());
    FactDefinition<LongRangeSet> rightRange = rightChange.findFact(rightValue, FactExtractor.range());
    LongRangeSet fromRelation = rightRange.myFact.fromRelation(relationType.getNegated());
    if (fromRelation != null && !fromRelation.intersects(leftRange.myFact)) {
      while (leftRange.myOldFact != null && !fromRelation.intersects(leftRange.myOldFact) &&
             leftRange.myChange != null && !(leftRange.myChange.myInstruction instanceof AssignInstruction)) {
        MemoryStateChange previous = leftRange.myChange.getPrevious();
        if (previous == null) break;
        leftRange = previous.findFact(leftValue, FactExtractor.range());
      }
      return new CauseItem[]{
        findRangeCause(leftChange, leftValue, leftRange.myFact, JavaAnalysisBundle.message("dfa.find.cause.left.operand.range.template")),
        findRangeCause(rightChange, rightValue, rightRange.myFact,
                       JavaAnalysisBundle.message("dfa.find.cause.right.operand.range.template"))};
    }
    if (leftValue instanceof DfaVariableValue) {
      if (leftValue == rightValue) {
        PsiExpression leftExpression = leftChange.getExpression();
        PsiExpression rightExpression = rightChange.getExpression();
        if (leftExpression instanceof PsiMethodCallExpression) {
          CauseItem cause = fromCallContract(leftChange, (PsiMethodCallExpression)leftExpression, rightExpression);
          if (cause != null) {
            return new CauseItem[]{cause};
          }
        }
        if (rightExpression instanceof PsiMethodCallExpression) {
          CauseItem cause = fromCallContract(rightChange, (PsiMethodCallExpression)rightExpression, leftExpression);
          if (cause != null) {
            return new CauseItem[]{cause};
          }
        }
      }
      Relation relation = new Relation(relationType, rightValue);
      MemoryStateChange change = findRelationAddedChange(leftChange, (DfaVariableValue)leftValue, relation);
      if (change != null) {
        CauseItem cause = findRelationCause(change, (DfaVariableValue)leftValue, relation, rightChange);
        if (cause != null) {
          Instruction instruction = change.myInstruction;
          if (instruction instanceof AssignInstruction) {
            PsiExpression expression = ((AssignInstruction)instruction).getRExpression();
            MemoryStateChange assignmentChange = change.findExpressionPush(expression);
            if (assignmentChange != null) {
              DfaValue target = change.myTopOfStack;
              if (target == rightValue) {
                return ArrayUtil.prepend(cause, findRelationCause(relationType, leftChange, assignmentChange));
              }
            }
          }
        }
        return new CauseItem[]{cause};
      }
    }
    if (rightValue instanceof DfaVariableValue) {
      Relation relation = new Relation(
        Objects.requireNonNull(relationType.getFlipped()), leftValue);
      MemoryStateChange change = findRelationAddedChange(rightChange, (DfaVariableValue)rightValue, relation);
      if (change != null) {
        return new CauseItem[]{findRelationCause(change, (DfaVariableValue)rightValue, relation, leftChange)};
      }
    }
    if (relationType == RelationType.NE) {
      SpecialField leftField = SpecialField.fromQualifierType(leftValue.getDfType());
      SpecialField rightField = SpecialField.fromQualifierType(rightValue.getDfType());
      if (leftField != null && leftField == rightField) {
        DfaValue leftSpecial = leftField.createValue(getFactory(), leftValue);
        DfaValue rightSpecial = rightField.createValue(getFactory(), rightValue);
        CauseItem[] specialCause = findRelationCause(relationType, leftChange, leftSpecial, rightChange, rightSpecial);
        if (specialCause.length > 0) {
          CauseItem item = new CauseItem(JavaAnalysisBundle.message("dfa.find.cause.values.cannot.be.equal.because",
                                                                    leftValue + "." + leftField + " != " + rightValue + "." + rightField),
                                         (PsiElement)null);
          item.addChildren(specialCause);
          return new CauseItem[]{item};
        }
      }
    }
    return new CauseItem[0];
  }
private CauseItem @NotNull [] findRelationCause(RelationType relationType,
                                                  MemoryStateChange leftChange, DfaValue leftValue,
                                                  MemoryStateChange rightChange, DfaValue rightValue)