-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Expand file tree
/
Copy pathUnion.cs
More file actions
283 lines (241 loc) · 13.1 KB
/
Copy pathUnion.cs
File metadata and controls
283 lines (241 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using static System.Linq.Utilities;
namespace System.Linq
{
public static partial class Enumerable
{
public static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) => Union(first, second, comparer: null);
public static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource>? comparer)
{
if (first == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
}
if (second == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}
return first is UnionIterator<TSource> union && AreEqualityComparersEqual(comparer, union._comparer) ? union.Union(second) : new UnionIterator2<TSource>(first, second, comparer);
}
/// <summary>Produces the set union of two sequences according to a specified key selector function.</summary>
/// <typeparam name="TSource">The type of the elements of the input sequences.</typeparam>
/// <typeparam name="TKey">The type of key to identify elements by.</typeparam>
/// <param name="first">An <see cref="IEnumerable{T}" /> whose distinct elements form the first set for the union.</param>
/// <param name="second">An <see cref="IEnumerable{T}" /> whose distinct elements form the second set for the union.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <returns>An <see cref="IEnumerable{T}" /> that contains the elements from both input sequences, excluding duplicates.</returns>
/// <exception cref="ArgumentNullException"><paramref name="first" /> or <paramref name="second" /> is <see langword="null" />.</exception>
/// <remarks>
/// <para>This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its `GetEnumerator` method directly or by using `foreach` in Visual C# or `For Each` in Visual Basic.</para>
/// <para>The default equality comparer, <see cref="EqualityComparer{T}.Default" />, is used to compare values.</para>
/// <para>When the object returned by this method is enumerated, <see cref="O:Enumerable.UnionBy" /> enumerates <paramref name="first" /> and <paramref name="second" /> in that order and yields each element that has not already been yielded.</para>
/// </remarks>
public static IEnumerable<TSource> UnionBy<TSource, TKey>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector) => UnionBy(first, second, keySelector, null);
/// <summary>Produces the set union of two sequences according to a specified key selector function.</summary>
/// <typeparam name="TSource">The type of the elements of the input sequences.</typeparam>
/// <typeparam name="TKey">The type of key to identify elements by.</typeparam>
/// <param name="first">An <see cref="IEnumerable{T}" /> whose distinct elements form the first set for the union.</param>
/// <param name="second">An <see cref="IEnumerable{T}" /> whose distinct elements form the second set for the union.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <param name="comparer">The <see cref="IEqualityComparer{T}" /> to compare values.</param>
/// <returns>An <see cref="IEnumerable{T}" /> that contains the elements from both input sequences, excluding duplicates.</returns>
/// <exception cref="ArgumentNullException"><paramref name="first" /> or <paramref name="second" /> is <see langword="null" />.</exception>
/// <remarks>
/// <para>This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its `GetEnumerator` method directly or by using `foreach` in Visual C# or `For Each` in Visual Basic.</para>
/// <para>If <paramref name="comparer" /> is <see langword="null" />, the default equality comparer, <see cref="EqualityComparer{T}.Default" />, is used to compare values.</para>
/// <para>When the object returned by this method is enumerated, <see cref="O:Enumerable.UnionBy" /> enumerates <paramref name="first" /> and <paramref name="second" /> in that order and yields each element that has not already been yielded.</para>
/// </remarks>
public static IEnumerable<TSource> UnionBy<TSource, TKey>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
if (first is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
}
if (second is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}
if (keySelector is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector);
}
return UnionByIterator(first, second, keySelector, comparer);
}
private static IEnumerable<TSource> UnionByIterator<TSource, TKey>(IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
var set = new HashSet<TKey>(DefaultInternalSetCapacity, comparer);
foreach (TSource element in first)
{
if (set.Add(keySelector(element)))
{
yield return element;
}
}
foreach (TSource element in second)
{
if (set.Add(keySelector(element)))
{
yield return element;
}
}
}
/// <summary>
/// An iterator that yields distinct values from two or more <see cref="IEnumerable{TSource}"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source enumerables.</typeparam>
private abstract partial class UnionIterator<TSource> : Iterator<TSource>
{
internal readonly IEqualityComparer<TSource>? _comparer;
private IEnumerator<TSource>? _enumerator;
private HashSet<TSource>? _set;
protected UnionIterator(IEqualityComparer<TSource>? comparer)
{
_comparer = comparer;
}
public sealed override void Dispose()
{
if (_enumerator != null)
{
_enumerator.Dispose();
_enumerator = null;
_set = null;
}
base.Dispose();
}
internal abstract IEnumerable<TSource>? GetEnumerable(int index);
internal abstract UnionIterator<TSource> Union(IEnumerable<TSource> next);
private void SetEnumerator(IEnumerator<TSource> enumerator)
{
_enumerator?.Dispose();
_enumerator = enumerator;
}
private void StoreFirst()
{
Debug.Assert(_enumerator != null);
var set = new HashSet<TSource>(DefaultInternalSetCapacity, _comparer);
TSource element = _enumerator.Current;
set.Add(element);
_current = element;
_set = set;
}
private bool GetNext()
{
Debug.Assert(_enumerator != null);
Debug.Assert(_set != null);
HashSet<TSource> set = _set;
while (_enumerator.MoveNext())
{
TSource element = _enumerator.Current;
if (set.Add(element))
{
_current = element;
return true;
}
}
return false;
}
public sealed override bool MoveNext()
{
if (_state == 1)
{
for (IEnumerable<TSource>? enumerable = GetEnumerable(0); enumerable != null; enumerable = GetEnumerable(_state - 1))
{
IEnumerator<TSource> enumerator = enumerable.GetEnumerator();
SetEnumerator(enumerator);
++_state;
if (enumerator.MoveNext())
{
StoreFirst();
return true;
}
}
}
else if (_state > 0)
{
while (true)
{
if (GetNext())
{
return true;
}
IEnumerable<TSource>? enumerable = GetEnumerable(_state - 1);
if (enumerable == null)
{
break;
}
SetEnumerator(enumerable.GetEnumerator());
++_state;
}
}
Dispose();
return false;
}
}
/// <summary>
/// An iterator that yields distinct values from two <see cref="IEnumerable{TSource}"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source enumerables.</typeparam>
private sealed class UnionIterator2<TSource> : UnionIterator<TSource>
{
private readonly IEnumerable<TSource> _first;
private readonly IEnumerable<TSource> _second;
public UnionIterator2(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource>? comparer)
: base(comparer)
{
Debug.Assert(first != null);
Debug.Assert(second != null);
_first = first;
_second = second;
}
public override Iterator<TSource> Clone() => new UnionIterator2<TSource>(_first, _second, _comparer);
internal override IEnumerable<TSource>? GetEnumerable(int index)
{
Debug.Assert(index >= 0 && index <= 2);
return index switch
{
0 => _first,
1 => _second,
_ => null,
};
}
internal override UnionIterator<TSource> Union(IEnumerable<TSource> next)
{
var sources = new SingleLinkedNode<IEnumerable<TSource>>(_first).Add(_second).Add(next);
return new UnionIteratorN<TSource>(sources, 2, _comparer);
}
}
/// <summary>
/// An iterator that yields distinct values from three or more <see cref="IEnumerable{TSource}"/>.
/// </summary>
/// <typeparam name="TSource">The type of the source enumerables.</typeparam>
private sealed class UnionIteratorN<TSource> : UnionIterator<TSource>
{
private readonly SingleLinkedNode<IEnumerable<TSource>> _sources;
private readonly int _headIndex;
public UnionIteratorN(SingleLinkedNode<IEnumerable<TSource>> sources, int headIndex, IEqualityComparer<TSource>? comparer)
: base(comparer)
{
Debug.Assert(headIndex >= 2);
Debug.Assert(sources?.GetCount() == headIndex + 1);
_sources = sources;
_headIndex = headIndex;
}
public override Iterator<TSource> Clone() => new UnionIteratorN<TSource>(_sources, _headIndex, _comparer);
internal override IEnumerable<TSource>? GetEnumerable(int index) => index > _headIndex ? null : _sources.GetNode(_headIndex - index).Item;
internal override UnionIterator<TSource> Union(IEnumerable<TSource> next)
{
if (_headIndex == int.MaxValue - 2)
{
// In the unlikely case of this many unions, if we produced a UnionIteratorN
// with int.MaxValue then state would overflow before it matched it's index.
// So we use the naive approach of just having a left and right sequence.
return new UnionIterator2<TSource>(this, next, _comparer);
}
return new UnionIteratorN<TSource>(_sources.Add(next), _headIndex + 1, _comparer);
}
}
}
}