863788f3ab2dc3094b20c1db0ff97ca674b81e7f
[platform/upstream/dotnet/runtime.git] /
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
6 //
7 // IntMinMaxAggregationOperator.cs
8 //
9 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
10
11 using System.Collections.Generic;
12 using System.Diagnostics;
13 using System.Threading;
14
15 namespace System.Linq.Parallel
16 {
17     /// <summary>
18     /// An inlined min/max aggregation and its enumerator, for ints.
19     /// </summary>
20     internal sealed class IntMinMaxAggregationOperator : InlinedAggregationOperator<int, int, int>
21     {
22         private readonly int _sign; // The sign (-1 for min, 1 for max).
23
24         //---------------------------------------------------------------------------------------
25         // Constructs a new instance of a min/max associative operator.
26         //
27
28         internal IntMinMaxAggregationOperator(IEnumerable<int> child, int sign) : base(child)
29         {
30             Debug.Assert(sign == -1 || sign == 1, "invalid sign");
31             _sign = sign;
32         }
33
34         //---------------------------------------------------------------------------------------
35         // Executes the entire query tree, and aggregates the intermediate results into the
36         // final result based on the binary operators and final reduction.
37         //
38         // Return Value:
39         //     The single result of aggregation.
40         //
41
42         protected override int InternalAggregate(ref Exception singularExceptionToThrow)
43         {
44             // Because the final reduction is typically much cheaper than the intermediate
45             // reductions over the individual partitions, and because each parallel partition
46             // will do a lot of work to produce a single output element, we prefer to turn off
47             // pipelining, and process the final reductions serially.
48             using (IEnumerator<int> enumerator = GetEnumerator(ParallelMergeOptions.FullyBuffered, true))
49             {
50                 // Throw an error for empty results.
51                 if (!enumerator.MoveNext())
52                 {
53                     singularExceptionToThrow = new InvalidOperationException(SR.NoElements);
54                     return default(int);
55                 }
56
57                 int best = enumerator.Current;
58
59                 // Based on the sign, do either a min or max reduction.
60                 if (_sign == -1)
61                 {
62                     while (enumerator.MoveNext())
63                     {
64                         int current = enumerator.Current;
65                         if (current < best)
66                         {
67                             best = current;
68                         }
69                     }
70                 }
71                 else
72                 {
73                     while (enumerator.MoveNext())
74                     {
75                         int current = enumerator.Current;
76                         if (current > best)
77                         {
78                             best = current;
79                         }
80                     }
81                 }
82
83                 return best;
84             }
85         }
86
87         //---------------------------------------------------------------------------------------
88         // Creates an enumerator that is used internally for the final aggregation step.
89         //
90
91         protected override QueryOperatorEnumerator<int, int> CreateEnumerator<TKey>(
92             int index, int count, QueryOperatorEnumerator<int, TKey> source, object sharedData, CancellationToken cancellationToken)
93         {
94             return new IntMinMaxAggregationOperatorEnumerator<TKey>(source, index, _sign, cancellationToken);
95         }
96
97         //---------------------------------------------------------------------------------------
98         // This enumerator type encapsulates the intermediary aggregation over the underlying
99         // (possibly partitioned) data source.
100         //
101
102         private class IntMinMaxAggregationOperatorEnumerator<TKey> : InlinedAggregationOperatorEnumerator<int>
103         {
104             private readonly QueryOperatorEnumerator<int, TKey> _source; // The source data.
105             private readonly int _sign; // The sign for comparisons (-1 means min, 1 means max).
106
107             //---------------------------------------------------------------------------------------
108             // Instantiates a new aggregation operator.
109             //
110
111             internal IntMinMaxAggregationOperatorEnumerator(QueryOperatorEnumerator<int, TKey> source, int partitionIndex, int sign,
112                 CancellationToken cancellationToken) :
113                 base(partitionIndex, cancellationToken)
114             {
115                 Debug.Assert(source != null);
116                 _source = source;
117                 _sign = sign;
118             }
119
120             //---------------------------------------------------------------------------------------
121             // Tallies up the min/max of the underlying data source, walking the entire thing the first
122             // time MoveNext is called on this object.
123             //
124
125             protected override bool MoveNextCore(ref int currentElement)
126             {
127                 // Based on the sign, do either a min or max reduction.
128                 QueryOperatorEnumerator<int, TKey> source = _source;
129                 TKey keyUnused = default(TKey);
130
131                 if (source.MoveNext(ref currentElement, ref keyUnused))
132                 {
133                     int i = 0;
134                     // We just scroll through the enumerator and find the min or max.
135                     if (_sign == -1)
136                     {
137                         int elem = default(int);
138                         while (source.MoveNext(ref elem, ref keyUnused))
139                         {
140                             if ((i++ & CancellationState.POLL_INTERVAL) == 0)
141                                 CancellationState.ThrowIfCanceled(_cancellationToken);
142
143                             if (elem < currentElement)
144                             {
145                                 currentElement = elem;
146                             }
147                         }
148                     }
149                     else
150                     {
151                         int elem = default(int);
152                         while (source.MoveNext(ref elem, ref keyUnused))
153                         {
154                             if ((i++ & CancellationState.POLL_INTERVAL) == 0)
155                                 CancellationState.ThrowIfCanceled(_cancellationToken);
156
157                             if (elem > currentElement)
158                             {
159                                 currentElement = elem;
160                             }
161                         }
162                     }
163
164                     // The sum has been calculated. Now just return.
165                     return true;
166                 }
167
168                 return false;
169             }
170
171             //---------------------------------------------------------------------------------------
172             // Dispose of resources associated with the underlying enumerator.
173             //
174
175             protected override void Dispose(bool disposing)
176             {
177                 Debug.Assert(_source != null);
178                 _source.Dispose();
179             }
180         }
181     }
182 }