presto-main/src/main/java/io/prestosql/sql/planner/EqualityInference.java/EqualityInference/generateEqualitiesPartitionedBy
class EqualityInference:    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
            List<Expression> candidates = equalitySet.stream()
                    .filter(candidate -> !derivedExpressions.contains(candidate))
                    .collect(Collectors.toList());

            for (Expression candidate : candidates) {
                Expression scopeRewritten = rewrite(candidate, scope::contains, false);
                if (scopeRewritten != null) {
                    scopeExpressions.add(scopeRewritten);
                }
                Expression scopeComplementRewritten = rewrite(candidate, expression -> !scope.contains(expression), 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);
            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);
            if (scopeComplementExpressions.size() >= 2) {
                scopeComplementExpressions.stream()
                        .filter(expression -> !expression.equals(complementCanonical))
                        .map(expression -> new ComparisonExpression(ComparisonExpression.Operator.EQUAL, complementCanonical, expression))
                        .forEach(scopeComplementEqualities::add);
            }

            // Compile the scope straddling equality expressions
            List<Expression> connectingExpressions = new ArrayList<>();
            connectingExpressions.add(matchingCanonical);
            connectingExpressions.add(complementCanonical);
            connectingExpressions.addAll(scopeStraddlingExpressions);
            connectingExpressions = connectingExpressions.stream()
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            Expression connectingCanonical = getCanonical(connectingExpressions);
            if (connectingCanonical != null) {
                connectingExpressions.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());
    }