docs: private DataContainer crossJoin(DataContainer left, DataContainer right, Optional<BooleanExpression> joinCondition, JoinType joinType) {
          final List<DataRow> leftRows = left.getRows();
          final List<DataRow> rightRows = right.getRows();
          final Set<Integer> matchedIndexes = new HashSet<>();
  
          boolean isLeftJoin = joinType == JoinType.LEFT;
          boolean isRightJoin = joinType == JoinType.RIGHT;
          int leftColumnsCount = left.getColumnNames().size();
          int rightColumnsCount = right.getColumnNames().size();
  
          DataContainer dataContainer = mergeContainerColumns(left, right);
          crossProduct(leftRows, rightRows, DataRow::getValues, (indexesPair, newRowValues) -> {
              DataRow dataRow = new DataRow(dataContainer, newRowValues);
              if (!joinCondition.isPresent() || expressionEvaluator.evaluateAs(joinCondition.get(), dataRow, Boolean.class)) {
                  dataContainer.addRow(dataRow);
                  if (isLeftJoin) {
                      matchedIndexes.add(indexesPair.getFirst());
                  }
                  if (isRightJoin) {
                      matchedIndexes.add(indexesPair.getSecond());
                  }
              }
          });
  
          if (isLeftJoin || isRightJoin) {
              int rowsCount = isLeftJoin ? leftRows.size() : rightRows.size();
              for (int i = 0; i <= rowsCount - 1; i++) {
                  if (!matchedIndexes.contains(i)) {
                      DataRow rowWithNulls = new DataRow(
                              dataContainer,
                              rowWithNullsValues(leftRows, leftColumnsCount, rightRows, rightColumnsCount, i, isLeftJoin)
                      );
                      dataContainer.addRow(rowWithNulls);
                  }
              }
          }
          return dataContainer;
      }