@NotNull
  private static MergeConflictType getLeftToRightDiffType(@NotNull Predicate<? super ThreeSide> emptiness,
                                                          @NotNull BiPredicate<? super ThreeSide, ? super ThreeSide> equality) {
    boolean isLeftEmpty = emptiness.test(ThreeSide.LEFT);
    boolean isBaseEmpty = emptiness.test(ThreeSide.BASE);
    boolean isRightEmpty = emptiness.test(ThreeSide.RIGHT);
    assert !isLeftEmpty || !isBaseEmpty || !isRightEmpty;

    if (isBaseEmpty) {
      if (isLeftEmpty) { // --=
        return new MergeConflictType(MergeConflictType.Type.INSERTED, false, true);
      }
      else if (isRightEmpty) { // =--
        return new MergeConflictType(MergeConflictType.Type.DELETED, true, false);
      }
      else { // =-=
        return new MergeConflictType(MergeConflictType.Type.MODIFIED, true, true);
      }
    }
    else {
      //noinspection IfStatementWithIdenticalBranches
      if (isLeftEmpty && isRightEmpty) { // -=-
        return new MergeConflictType(MergeConflictType.Type.MODIFIED, true, true);
      }
      else { // -==, ==-, ===
        boolean unchangedLeft = equality.test(ThreeSide.BASE, ThreeSide.LEFT);
        boolean unchangedRight = equality.test(ThreeSide.BASE, ThreeSide.RIGHT);
        assert !unchangedLeft || !unchangedRight;

        if (unchangedLeft) {
          return new MergeConflictType(isRightEmpty ? MergeConflictType.Type.DELETED : MergeConflictType.Type.MODIFIED, false, true);
        }
        if (unchangedRight) {
          return new MergeConflictType(isLeftEmpty ? MergeConflictType.Type.INSERTED : MergeConflictType.Type.MODIFIED, true, false);
        }

        return new MergeConflictType(MergeConflictType.Type.MODIFIED, true, true);
      }
    }
  }
@NotNull
  private static MergeConflictType getLeftToRightDiffType(@NotNull Predicate<? super ThreeSide> emptiness,
                                                          @NotNull BiPredicate<? super ThreeSide, ? super ThreeSide> equality)