private void WrapPartitionedStreamHelper <TLeftKey, TRightKey>(
            PartitionedStream <Pair <TLeftInput, TKey>, TLeftKey> leftHashStream,
            HashLookupBuilder <IEnumerable <TRightInput>, TRightKey, TKey>[] rightLookupBuilders,
            IComparer <TRightKey>?rightKeyComparer, IPartitionedStreamRecipient <TOutput> outputRecipient,
            int partitionCount, CancellationToken cancellationToken)
        {
            if (RightChild.OutputOrdered && LeftChild.OutputOrdered)
            {
                PairOutputKeyBuilder <TLeftKey, TRightKey> outputKeyBuilder  = new PairOutputKeyBuilder <TLeftKey, TRightKey>();
                IComparer <Pair <TLeftKey, TRightKey> >    outputKeyComparer = new PairComparer <TLeftKey, TRightKey>(leftHashStream.KeyComparer, rightKeyComparer);

                WrapPartitionedStreamHelper <TLeftKey, TRightKey, Pair <TLeftKey, TRightKey> >(leftHashStream, rightLookupBuilders,
                                                                                               outputKeyBuilder, outputKeyComparer, outputRecipient, partitionCount, cancellationToken);
            }
            else
            {
                LeftKeyOutputKeyBuilder <TLeftKey, TRightKey> outputKeyBuilder = new LeftKeyOutputKeyBuilder <TLeftKey, TRightKey>();

                WrapPartitionedStreamHelper <TLeftKey, TRightKey, TLeftKey>(leftHashStream, rightLookupBuilders,
                                                                            outputKeyBuilder, leftHashStream.KeyComparer, outputRecipient, partitionCount, cancellationToken);
            }
        }
        //---------------------------------------------------------------------------------------
        // This is a helper method. WrapPartitionedStream decides what type TLeftKey is going
        // to be, and then call this method with that key as a generic parameter.
        //

        private void WrapPartitionedStreamHelper <TLeftKey, TRightKey>(
            PartitionedStream <Pair <TLeftInput, TKey>, TLeftKey> leftHashStream, PartitionedStream <TRightInput, TRightKey> rightPartitionedStream,
            IPartitionedStreamRecipient <TOutput> outputRecipient, CancellationToken cancellationToken)
        {
            if (RightChild.OutputOrdered && LeftChild.OutputOrdered)
            {
                PairOutputKeyBuilder <TLeftKey, TRightKey> outputKeyBuilder  = new PairOutputKeyBuilder <TLeftKey, TRightKey>();
                IComparer <Pair <TLeftKey, TRightKey> >    outputKeyComparer =
                    new PairComparer <TLeftKey, TRightKey>(leftHashStream.KeyComparer, rightPartitionedStream.KeyComparer);

                WrapPartitionedStreamHelper <TLeftKey, TRightKey, Pair <TLeftKey, TRightKey> >(leftHashStream,
                                                                                               ExchangeUtilities.HashRepartitionOrdered(rightPartitionedStream, _rightKeySelector, _keyComparer, null, cancellationToken),
                                                                                               outputKeyBuilder, outputKeyComparer, outputRecipient, cancellationToken);
            }
            else
            {
                LeftKeyOutputKeyBuilder <TLeftKey, int> outputKeyBuilder = new LeftKeyOutputKeyBuilder <TLeftKey, int>();

                WrapPartitionedStreamHelper <TLeftKey, int, TLeftKey>(leftHashStream,
                                                                      ExchangeUtilities.HashRepartition(rightPartitionedStream, _rightKeySelector, _keyComparer, null, cancellationToken),
                                                                      outputKeyBuilder, leftHashStream.KeyComparer, outputRecipient, cancellationToken);
            }
        }