public EqualityPartition generateEqualitiesPartitionedBy(Set<Symbol> scope) {
    ImmutableSet.Builder<Expression> scopeEqualities = ImmutableSet.builder();
    ImmutableSet.Builder<Expression> scopeComplementEqualities = ImmutableSet.builder();
    ImmutableSet.Builder<Expression> scopeStraddlingEqualities = ImmutableSet.builder();

    for (Collection<Expression> equalitySet : equalitySets.asMap().values()) {
      Set<Expression> scopeExpressions = new LinkedHashSet<>();
      Set<Expression> scopeComplementExpressions = new LinkedHashSet<>();
      Set<Expression> scopeStraddlingExpressions = new LinkedHashSet<>();

      // Try to push each non-derived expression into one side of the scope
      equalitySet.stream()
          .filter(candidate -> !derivedExpressions.contains(candidate))
          .forEach(
              candidate -> {
                Expression scopeRewritten = rewrite(candidate, scope::contains, false);
                if (scopeRewritten != null) {
                  scopeExpressions.add(scopeRewritten);
                }
                Expression scopeComplementRewritten =
                    rewrite(candidate, symbol -> !scope.contains(symbol), false);
                if (scopeComplementRewritten != null) {
                  scopeComplementExpressions.add(scopeComplementRewritten);
                }
                if (scopeRewritten == null && scopeComplementRewritten == null) {
                  scopeStraddlingExpressions.add(candidate);
                }
              });
      // Compile the equality expressions on each side of the scope
      Expression matchingCanonical = getCanonical(scopeExpressions.stream());
      if (scopeExpressions.size() >= 2) {
        scopeExpressions.stream()
            .filter(expression -> !expression.equals(matchingCanonical))
            .map(
                expression ->
                    new ComparisonExpression(
                        ComparisonExpression.Operator.EQUAL, matchingCanonical, expression))
            .forEach(scopeEqualities::add);
      }
      Expression complementCanonical = getCanonical(scopeComplementExpressions.stream());
      if (scopeComplementExpressions.size() >= 2) {
        scopeComplementExpressions.stream()
            .filter(expression -> !expression.equals(complementCanonical))
            .map(
                expression ->
                    new ComparisonExpression(
                        ComparisonExpression.Operator.EQUAL, complementCanonical, expression))
            .forEach(scopeComplementEqualities::add);
      }

      // Compile single equality between matching and complement scope.
      // Only consider expressions that don't have derived expression in other scope.
      // Otherwise, redundant equality would be generated.
      Optional<Expression> matchingConnecting =
          scopeExpressions.stream()
              .filter(
                  expression ->
                      SymbolsExtractor.extractAll(expression).isEmpty()
                          || rewrite(expression, symbol -> !scope.contains(symbol), false) == null)
              .min(canonicalComparator);
      Optional<Expression> complementConnecting =
          scopeComplementExpressions.stream()
              .filter(
                  expression ->
                      SymbolsExtractor.extractAll(expression).isEmpty()
                          || rewrite(expression, scope::contains, false) == null)
              .min(canonicalComparator);
      if (matchingConnecting.isPresent()
          && complementConnecting.isPresent()
          && !matchingConnecting.equals(complementConnecting)) {
        scopeStraddlingEqualities.add(
            new ComparisonExpression(
                ComparisonExpression.Operator.EQUAL,
                matchingConnecting.get(),
                complementConnecting.get()));
      }

      // Compile the scope straddling equality expressions.
      // scopeStraddlingExpressions couldn't be pushed to either side,
      // therefore there needs to be an equality generated with
      // one of the scopes (either matching or complement).
      List<Expression> straddlingExpressions = new ArrayList<>();
      if (matchingCanonical != null) {
        straddlingExpressions.add(matchingCanonical);
      } else if (complementCanonical != null) {
        straddlingExpressions.add(complementCanonical);
      }
      straddlingExpressions.addAll(scopeStraddlingExpressions);
      Expression connectingCanonical = getCanonical(straddlingExpressions.stream());
      if (connectingCanonical != null) {
        straddlingExpressions.stream()
            .filter(expression -> !expression.equals(connectingCanonical))
            .map(
                expression ->
                    new ComparisonExpression(
                        ComparisonExpression.Operator.EQUAL, connectingCanonical, expression))
            .forEach(scopeStraddlingEqualities::add);
      }
    }

    return new EqualityPartition(
        scopeEqualities.build(),
        scopeComplementEqualities.build(),
        scopeStraddlingEqualities.build());
  }
Dumps the inference equalities as equality expressions that are partitioned by the symbolScope.
All stored equalities are returned in a compact set and will be classified into three groups as
determined by the symbol scope:

<ol>
  <li>equalities that fit entirely within the symbol scope
  <li>equalities that fit entirely outside of the symbol scope
  <li>equalities that straddle the symbol scope
</ol>

<pre>
Example:
  Stored Equalities:
    a = b = c
    d = e = f = g

  Symbol Scope:
    a, b, d, e

  Output EqualityPartition:
    Scope Equalities:
      a = b
      d = e
    Complement Scope Equalities
      f = g
    Scope Straddling Equalities
      a = c
      d = f
</pre>
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/EqualityInference.java