// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace System.Linq
{
public static partial class AsyncEnumerable
{
///
/// Determines whether two sequences are equal by comparing the elements pairwise.
///
/// The type of the elements in the source sequence.
/// First async-enumerable sequence to compare.
/// Second async-enumerable sequence to compare.
/// The optional cancellation token to be used for cancelling the sequence at any time.
/// An async-enumerable sequence that contains a single element which indicates whether both sequences are of equal length and their corresponding elements are equal according to the default equality comparer for their type.
/// or is null.
/// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.
public static ValueTask SequenceEqualAsync(this IAsyncEnumerable first, IAsyncEnumerable second, CancellationToken cancellationToken = default) =>
SequenceEqualAsync(first, second, comparer: null, cancellationToken);
///
/// Determines whether two sequences are equal by comparing the elements pairwise using a specified equality comparer.
///
/// The type of the elements in the source sequence.
/// First async-enumerable sequence to compare.
/// Second async-enumerable sequence to compare.
/// Comparer used to compare elements of both sequences.
/// The optional cancellation token to be used for cancelling the sequence at any time.
/// An async-enumerable sequence that contains a single element which indicates whether both sequences are of equal length and their corresponding elements are equal according to the specified equality comparer.
/// or or is null.
/// The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.
public static ValueTask SequenceEqualAsync(this IAsyncEnumerable first, IAsyncEnumerable second, IEqualityComparer? comparer, CancellationToken cancellationToken = default)
{
if (first == null)
throw Error.ArgumentNull(nameof(first));
if (second == null)
throw Error.ArgumentNull(nameof(second));
comparer ??= EqualityComparer.Default;
if (first is ICollection firstCol && second is ICollection secondCol)
{
if (firstCol.Count != secondCol.Count)
{
return new ValueTask(false);
}
if (firstCol is IList firstList && secondCol is IList secondList)
{
var count = firstCol.Count;
for (var i = 0; i < count; i++)
{
if (!comparer.Equals(firstList[i], secondList[i]))
{
return new ValueTask(false);
}
}
return new ValueTask(true);
}
}
return Core(first, second, comparer, cancellationToken);
static async ValueTask Core(IAsyncEnumerable first, IAsyncEnumerable second, IEqualityComparer comparer, CancellationToken cancellationToken)
{
await using var e1 = first.GetConfiguredAsyncEnumerator(cancellationToken, false);
await using var e2 = second.GetConfiguredAsyncEnumerator(cancellationToken, false);
while (await e1.MoveNextAsync())
{
if (!(await e2.MoveNextAsync() && comparer.Equals(e1.Current, e2.Current)))
{
return false;
}
}
return !await e2.MoveNextAsync();
}
}
}
}