public PlanNodeStatsEstimate capStats(PlanNodeStatsEstimate stats, PlanNodeStatsEstimate cap)
    {
        if (stats.isOutputRowCountUnknown() || cap.isOutputRowCountUnknown()) {
            return PlanNodeStatsEstimate.unknown();
        }

        PlanNodeStatsEstimate.Builder result = PlanNodeStatsEstimate.builder();
        double cappedRowCount = min(stats.getOutputRowCount(), cap.getOutputRowCount());
        result.setOutputRowCount(cappedRowCount);

        stats.getVariablesWithKnownStatistics().forEach(symbol -> {
            VariableStatsEstimate symbolStats = stats.getVariableStatistics(symbol);
            VariableStatsEstimate capSymbolStats = cap.getVariableStatistics(symbol);

            VariableStatsEstimate.Builder newSymbolStats = VariableStatsEstimate.builder();

            // for simplicity keep the average row size the same as in the input
            // in most cases the average row size doesn't change after applying filters
            newSymbolStats.setAverageRowSize(symbolStats.getAverageRowSize());
            newSymbolStats.setDistinctValuesCount(min(symbolStats.getDistinctValuesCount(), capSymbolStats.getDistinctValuesCount()));
            double newLow = max(symbolStats.getLowValue(), capSymbolStats.getLowValue());
            double newHigh = min(symbolStats.getHighValue(), capSymbolStats.getHighValue());
            newSymbolStats.setLowValue(newLow);
            newSymbolStats.setHighValue(newHigh);

            double numberOfNulls = stats.getOutputRowCount() * symbolStats.getNullsFraction();
            double capNumberOfNulls = cap.getOutputRowCount() * capSymbolStats.getNullsFraction();
            double cappedNumberOfNulls = min(numberOfNulls, capNumberOfNulls);
            double cappedNullsFraction = cappedRowCount == 0 ? 1 : cappedNumberOfNulls / cappedRowCount;
            newSymbolStats.setNullsFraction(cappedNullsFraction);
            if (shouldUseHistograms) {
                newSymbolStats.setHistogram(symbolStats.getHistogram().map(symbolHistogram -> addConjunction(symbolHistogram, new StatisticRange(newLow, newHigh, 0))));
            }

            result.addVariableStatistics(symbol, newSymbolStats.build());
        });

        return result.build();
    }
@Test
    public void testCapRowCount()
    {
        PlanNodeStatsEstimate unknownRowCount = statistics(NaN, NaN, NaN, NaN, NON_EMPTY_RANGE);
        PlanNodeStatsEstimate first = statistics(20, NaN, NaN, NaN, NON_EMPTY_RANGE);
        PlanNodeStatsEstimate second = statistics(10, NaN, NaN, NaN, NON_EMPTY_RANGE);

        assertEquals(calculator.capStats(unknownRowCount, unknownRowCount).getOutputRowCount(), NaN);
        assertEquals(calculator.capStats(first, unknownRowCount).getOutputRowCount(), NaN);
        assertEquals(calculator.capStats(unknownRowCount, second).getOutputRowCount(), NaN);
        assertEquals(calculator.capStats(first, second).getOutputRowCount(), 10.0);
        assertEquals(calculator.capStats(second, first).getOutputRowCount(), 10.0);
    }