docs: private DictionaryCompressionProjection selectDictionaryColumnToConvert(int totalNonDictionaryBytes, int stripeRowCount)
      {
          checkState(!directConversionCandidates.isEmpty());
  
          int totalNonDictionaryBytesPerRow = totalNonDictionaryBytes / stripeRowCount;
  
          // rawBytes = sum of the length of every row value (without dictionary encoding)
          // dictionaryBytes = sum of the length of every entry in the dictionary
          // indexBytes = bytes used encode the dictionary index (e.g., 2 byte for dictionary less than 65536 entries)
  
          long totalDictionaryRawBytes = 0;
          long totalDictionaryBytes = 0;
          long totalDictionaryIndexBytes = 0;
  
          long totalDictionaryRawBytesPerRow = 0;
          long totalDictionaryBytesPerNewRow = 0;
          long totalDictionaryIndexBytesPerRow = 0;
  
          for (DictionaryColumnManager column : allWriters) {
              if (!column.isDirectEncoded()) {
                  totalDictionaryRawBytes += column.getRawBytes();
                  totalDictionaryBytes += column.getDictionaryBytes();
                  totalDictionaryIndexBytes += column.getIndexBytes();
  
                  totalDictionaryRawBytesPerRow += column.getRawBytesPerRow();
                  totalDictionaryBytesPerNewRow += column.getDictionaryBytesPerFutureRow();
                  totalDictionaryIndexBytesPerRow += column.getIndexBytesPerRow();
              }
          }
  
          long totalUncompressedBytesPerRow = totalNonDictionaryBytesPerRow + totalDictionaryRawBytesPerRow;
  
          DictionaryCompressionProjection maxProjectedCompression = null;
          for (DictionaryColumnManager column : directConversionCandidates) {
              // determine the size of the currently written stripe if we were convert this column to direct
              long currentRawBytes = totalNonDictionaryBytes + column.getRawBytes();
              long currentDictionaryBytes = totalDictionaryBytes - column.getDictionaryBytes();
              long currentIndexBytes = totalDictionaryIndexBytes - column.getIndexBytes();
              long currentTotalBytes = currentRawBytes + currentDictionaryBytes + currentIndexBytes;
  
              // estimate the size of each new row if we were convert this column to direct
              double rawBytesPerFutureRow = totalNonDictionaryBytesPerRow + column.getRawBytesPerRow();
              double dictionaryBytesPerFutureRow = totalDictionaryBytesPerNewRow - column.getDictionaryBytesPerFutureRow();
              double indexBytesPerFutureRow = totalDictionaryIndexBytesPerRow - column.getIndexBytesPerRow();
              double totalBytesPerFutureRow = rawBytesPerFutureRow + dictionaryBytesPerFutureRow + indexBytesPerFutureRow;
  
              // estimate how many rows until we hit a limit and flush the stripe if we convert this column to direct
              long rowsToDictionaryMemoryLimit = (long) ((dictionaryMemoryMaxBytesLow - currentDictionaryBytes) / dictionaryBytesPerFutureRow);
              long rowsToStripeMemoryLimit = (long) ((stripeMaxBytes - currentTotalBytes) / totalBytesPerFutureRow);
              long rowsToStripeRowLimit = stripeMaxRowCount - stripeRowCount;
              long rowsToLimit = Longs.min(rowsToDictionaryMemoryLimit, rowsToStripeMemoryLimit, rowsToStripeRowLimit);
  
              // predict the compression ratio at that limit if we were convert this column to direct
              long predictedUncompressedSizeAtLimit = totalNonDictionaryBytes + totalDictionaryRawBytes + (totalUncompressedBytesPerRow * rowsToLimit);
              long predictedCompressedSizeAtLimit = (long) (currentTotalBytes + (totalBytesPerFutureRow * rowsToLimit));
              double predictedCompressionRatioAtLimit = 1.0 * predictedUncompressedSizeAtLimit / predictedCompressedSizeAtLimit;
  
              // convert the column that creates the best compression ratio
              if (maxProjectedCompression == null || maxProjectedCompression.getPredictedFileCompressionRatio() < predictedCompressionRatioAtLimit) {
                  maxProjectedCompression = new DictionaryCompressionProjection(column, predictedCompressionRatioAtLimit);
              }
          }
          return maxProjectedCompression;
      }