code: @VisibleForTesting
      PlanNodeStatsEstimate calculateJoinComplementStats(
              Optional<Expression> filter,
              List<JoinNode.EquiJoinClause> criteria,
              PlanNodeStatsEstimate leftStats,
              PlanNodeStatsEstimate rightStats,
              TypeProvider types)
      {
          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), types);
          }
  
          // 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, types))
                  .orElse(PlanNodeStatsEstimate.unknown());
      }
docs: Calculates statistics for unmatched left rows.