3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
8 // AggregationMinMaxHelpers.cs
10 // <OWNER>Microsoft</OWNER>
12 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
14 using System.Collections.Generic;
15 using System.Linq.Parallel;
16 using System.Diagnostics.Contracts;
20 internal static class AggregationMinMaxHelpers<T>
23 //-----------------------------------------------------------------------------------
24 // Helper method to find the minimum or maximum element in the source.
27 private static T Reduce(IEnumerable<T> source, int sign)
29 Contract.Assert(source != null);
30 Contract.Assert(sign == -1 || sign == 1);
32 Func<Pair<bool, T>, T, Pair<bool, T>> intermediateReduce = MakeIntermediateReduceFunction(sign);
33 Func<Pair<bool, T>, Pair<bool, T>, Pair<bool, T>> finalReduce = MakeFinalReduceFunction(sign);
34 Func<Pair<bool, T>, T> resultSelector = MakeResultSelectorFunction();
36 AssociativeAggregationOperator<T, Pair<bool, T>, T> aggregation =
37 new AssociativeAggregationOperator<T, Pair<bool, T>, T>(source, new Pair<bool, T>(false, default(T)), null,
38 true, intermediateReduce, finalReduce, resultSelector, default(T) != null, QueryAggregationOptions.AssociativeCommutative);
40 return aggregation.Aggregate();
43 //-----------------------------------------------------------------------------------
44 // Helper method to find the minimum element in the source.
47 internal static T ReduceMin(IEnumerable<T> source)
49 return Reduce(source, -1);
52 //-----------------------------------------------------------------------------------
53 // Helper method to find the maximum element in the source.
56 internal static T ReduceMax(IEnumerable<T> source)
58 return Reduce(source, 1);
61 //-----------------------------------------------------------------------------------
62 // These methods are used to generate delegates to perform the comparisons.
65 private static Func<Pair<bool, T>, T, Pair<bool, T>> MakeIntermediateReduceFunction(int sign)
67 Comparer<T> comparer = Util.GetDefaultComparer<T>();
69 // Note that we capture the 'sign' argument and 'comparer' local, and therefore the C#
70 // compiler will transform this into an instance-based delegate, incurring an extra (hidden)
72 return delegate(Pair<bool, T> accumulator, T element)
74 // If this is the first element, or the sign of the result of comparing the element with
75 // the existing accumulated result is equal to the sign requested by the function factory,
76 // we will return a new pair that contains the current element as the best item. We will
77 // ignore null elements (for reference and nullable types) in the input stream.
78 if ((default(T) != null || element != null) &&
79 (!accumulator.First || Util.Sign(comparer.Compare(element, accumulator.Second)) == sign))
81 return new Pair<bool, T>(true, element);
84 // Otherwise, just return the current accumulator result.
89 private static Func<Pair<bool, T>, Pair<bool, T>, Pair<bool, T>> MakeFinalReduceFunction(int sign)
91 Comparer<T> comparer = Util.GetDefaultComparer<T>();
93 // Note that we capture the 'sign' argument and 'comparer' local, and therefore the C#
94 // compiler will transform this into an instance-based delegate, incurring an extra (hidden)
96 return delegate(Pair<bool, T> accumulator, Pair<bool, T> element)
98 // If the intermediate reduction is empty, we will ignore it. Otherwise, if this is the
99 // first element, or the sign of the result of comparing the element with the existing
100 // accumulated result is equal to the sign requested by the function factory, we will
101 // return a new pair that contains the current element as the best item.
103 (!accumulator.First || Util.Sign(comparer.Compare(element.Second, accumulator.Second)) == sign))
105 Contract.Assert(default(T) != null || element.Second != null, "nulls unexpected in final reduce");
106 return new Pair<bool, T>(true, element.Second);
109 // Otherwise, just return the current accumulator result.
114 private static Func<Pair<bool, T>, T> MakeResultSelectorFunction()
116 // If we saw at least one element in the source stream, the right pair element will contain
117 // the element we're looking for -- so we return that. In the case of non-nullable value
118 // types, the aggregation API will have thrown an exception before calling us for
119 // empty sequences. Else, we will just return the element, which may be null for other types.
120 return delegate(Pair<bool, T> accumulator)
122 Contract.Assert(accumulator.First || default(T) == null,
123 "for non-null types we expect an exception to be thrown before getting here");
124 return accumulator.Second;