/// <summary>
        /// Map + single-input Reduce
        /// </summary>
        internal static StreamProperties <TKey, TOutput> MapReduce <TKey, TMapInputLeft, TMapInputRight, TReduceInput, TReduceKey, TBind, TOutput>(
            this StreamProperties <TKey, TMapInputLeft> leftStream,
            StreamProperties <TKey, TMapInputRight> rightStream,
            Func <IStreamable <TKey, TMapInputLeft>, IStreamable <TKey, TMapInputRight>, IStreamable <TKey, TReduceInput> > mapper,
            Expression <Func <TReduceInput, TReduceKey> > keySelector,
            Func <IStreamable <CompoundGroupKey <TKey, TReduceKey>, TReduceInput>, IStreamable <CompoundGroupKey <TKey, TReduceKey>, TBind> > reducer,
            Expression <Func <TReduceKey, TBind, TOutput> > resultSelector)
        {
            bool mc  = leftStream.IsMulticore;
            var  map = leftStream.ToMulticore(true).Derive(a => mapper(a, null));

            if (reducer != null)
            {
                // We need to test the reducer function to see what it does to properties.
                var group = map.GroupNested(keySelector);

                if (group.CanSpray(keySelector))
                {
                    var reduce = group.Derive(reducer).Ungroup(resultSelector);
                    if (Config.MapArity > 1)
                    {
                        reduce = reduce.Union(reduce);
                    }
                    var returnValue = reduce.ToMulticore(mc);
                    return(returnValue);
                }
                else
                {
                    if (Config.MapArity > 1)
                    {
                        group = group.Union(group);
                    }

                    var reduce = group.Derive(reducer).Ungroup(resultSelector);

                    if (Config.ReduceArity > 1)
                    {
                        reduce = reduce.Union(reduce);
                    }

                    var returnValue = reduce.ToMulticore(mc);
                    return(returnValue);
                }
            }
            else
            {
                var result = leftStream.Derive(a => mapper(a, null)) as StreamProperties <TKey, TOutput>;
                if (Config.MapArity > 1)
                {
                    result = result.Union(result);
                }
                return(result.ToMulticore(mc));
            }
        }
        Map2Reduce <TMapInputLeft1, TMapInputRight1, TMapInputLeft2, TMapInputRight2, TReduceKey, TReduceInput1, TReduceInput2, TBind, TOutput>(
            this StreamProperties <Empty, TMapInputLeft1> leftStream1,
            StreamProperties <Empty, TMapInputRight1> rightStream1,
            StreamProperties <Empty, TMapInputLeft2> leftStream2,
            StreamProperties <Empty, TMapInputRight2> rightStream2,

            Func <IStreamable <Empty, TMapInputLeft1>, IStreamable <Empty, TMapInputRight1>, IStreamable <Empty, TReduceInput1> > mapper1,
            Func <IStreamable <Empty, TMapInputLeft2>, IStreamable <Empty, TMapInputRight2>, IStreamable <Empty, TReduceInput2> > mapper2,

            Func <IStreamable <TReduceKey, TReduceInput1>,
                  IStreamable <TReduceKey, TReduceInput2>,
                  IStreamable <TReduceKey, TBind> > reducer,

            Expression <Func <TReduceInput1, TReduceKey> > keySelector1,
            Expression <Func <TReduceInput2, TReduceKey> > keySelector2,

            Expression <Func <TReduceKey, TBind, TOutput> > resultSelector,

            OperationalHint reduceOptions)
        {
            var map1 = leftStream1.Derive(a => mapper1(a, null));
            var map2 = leftStream2.Derive(a => mapper2(a, null));

            var group1 = map1.Group(keySelector1);
            var group2 = map2.Group(keySelector2);

            if (leftStream1.CanSpray(leftStream2, keySelector1, keySelector2) &&
                group1.CanSpray(group2, keySelector1, keySelector2))
            {
                var reduce = group1.Derive(group2, reducer).Ungroup(resultSelector);
                if (Config.MapArity > 1)
                {
                    reduce = reduce.Union(reduce);
                }
                return(reduce);
            }
            else
            {
                if (Config.MapArity > 1)
                {
                    group1 = group1.Union(group1);
                    group2 = group2.Union(group2);
                }

                var reduce = group1.Derive(group2, reducer).Ungroup(resultSelector);

                if (Config.ReduceArity > 1)
                {
                    reduce = reduce.Union(reduce);
                }

                return(reduce);
            }
        }