@VisibleForTesting
    PlanNodeStatsEstimate calculateJoinComplementStats(
            Optional<RowExpression> filter,
            List<EquiJoinClause> criteria,
            PlanNodeStatsEstimate leftStats,
            PlanNodeStatsEstimate rightStats)
    {
        if (rightStats.getOutputRowCount() == 0) {
            // no left side rows are matched
            return leftStats;
        }

        if (criteria.isEmpty()) {
            // TODO: account for non-equi conditions
            if (filter.isPresent()) {
                return PlanNodeStatsEstimate.unknown();
            }

            return normalizer.normalize(leftStats.mapOutputRowCount(rowCount -> 0.0));
        }

        // TODO: add support for non-equality conditions (e.g: <=, !=, >)
        int numberOfFilterClauses = filter.map(expression -> extractConjuncts(expression).size()).orElse(0);

        // Heuristics: select the most selective criteria for join complement clause.
        // Principals behind this heuristics is the same as in computeInnerJoinStats:
        // select "driving join clause" that reduces matched rows the most.
        return criteria.stream()
                .map(drivingClause -> calculateJoinComplementStats(leftStats, rightStats, drivingClause, criteria.size() - 1 + numberOfFilterClauses))
                .filter(estimate -> !estimate.isOutputRowCountUnknown())
                .max(comparingDouble(PlanNodeStatsEstimate::getOutputRowCount))
                .map(estimate -> normalizer.normalize(estimate))
                .orElse(PlanNodeStatsEstimate.unknown());
    }
@Test
    public void testRightJoinComplementStats()
    {
        PlanNodeStatsEstimate expected = NORMALIZER.normalize(
                planNodeStats(
                        RIGHT_ROWS_COUNT * RIGHT_JOIN_COLUMN_NULLS,
                        variableStatistics(RIGHT_JOIN_COLUMN, NaN, NaN, 1.0, 0),
                        RIGHT_OTHER_COLUMN_STATS));
        PlanNodeStatsEstimate actual = JOIN_STATS_RULE.calculateJoinComplementStats(
                Optional.empty(),
                ImmutableList.of(new EquiJoinClause(RIGHT_JOIN_COLUMN, LEFT_JOIN_COLUMN)),
                RIGHT_STATS,
                LEFT_STATS);
        assertEquals(actual, expected);
    }