-
Notifications
You must be signed in to change notification settings - Fork 419
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
297 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
#region License and Terms | ||
// MoreLINQ - Extensions to LINQ to Objects | ||
// Copyright (c) 2010 Leopold Bushkin. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
#endregion | ||
|
||
namespace MoreLinq.Test | ||
{ | ||
using System; | ||
using NUnit.Framework; | ||
|
||
/// <summary> | ||
/// Verify the behavior of the Interleave operator | ||
/// </summary> | ||
[TestFixture] | ||
public class EquiInterleaveTests | ||
{ | ||
/// <summary> | ||
/// Verify that EquiInterleave behaves in a lazy manner | ||
/// </summary> | ||
[Test] | ||
public void TestEquiInterleaveIsLazy() | ||
{ | ||
new BreakingSequence<int>().EquiInterleave(new BreakingSequence<int>()); | ||
} | ||
|
||
/// <summary> | ||
/// Verify that EquiInterleave disposes those enumerators that it managed | ||
/// to open successfully | ||
/// </summary> | ||
[Test] | ||
public void TestEquiInterleaveDisposesOnError() | ||
{ | ||
using (var sequenceA = TestingSequence.Of<int>()) | ||
{ | ||
Assert.Throws<InvalidOperationException>(() => // Expected and thrown by BreakingSequence | ||
sequenceA.EquiInterleave(new BreakingSequence<int>()).Consume()); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Verify that two balanced sequences will EquiInterleave all of their elements | ||
/// </summary> | ||
[Test] | ||
public void TestEquiInterleaveTwoBalancedSequences() | ||
{ | ||
const int count = 10; | ||
var sequenceA = Enumerable.Range(1, count); | ||
var sequenceB = Enumerable.Range(1, count); | ||
var result = sequenceA.EquiInterleave(sequenceB); | ||
|
||
Assert.That(result, Is.EqualTo(Enumerable.Range(1, count).Select(x => new[] { x, x }).SelectMany(z => z))); | ||
} | ||
|
||
/// <summary> | ||
/// Verify that EquiInterleave with two empty sequences results in an empty sequence | ||
/// </summary> | ||
[Test] | ||
public void TestEquiInterleaveTwoEmptySequences() | ||
{ | ||
var sequenceA = Enumerable.Empty<int>(); | ||
var sequenceB = Enumerable.Empty<int>(); | ||
var result = sequenceA.EquiInterleave(sequenceB); | ||
|
||
Assert.That(result, Is.EqualTo(Enumerable.Empty<int>())); | ||
} | ||
|
||
/// <summary> | ||
/// Verify that EquiInterleave throw on two unbalanced sequences | ||
/// </summary> | ||
[Test] | ||
public void TestEquiInterleaveThrowOnUnbalanced() | ||
{ | ||
void Code() | ||
{ | ||
var sequenceA = new[] { 0, 0, 0, 0, 0, 0 }; | ||
var sequenceB = new[] { 1, 1, 1, 1 }; | ||
sequenceA.EquiInterleave(sequenceB).Consume(); | ||
} | ||
|
||
Assert.Throws<InvalidOperationException>(Code); | ||
} | ||
|
||
/// <summary> | ||
/// Verify that EquiInterleave multiple empty sequences results in an empty sequence | ||
/// </summary> | ||
[Test] | ||
public void TestEquiInterleaveManyEmptySequences() | ||
{ | ||
var sequenceA = Enumerable.Empty<int>(); | ||
var sequenceB = Enumerable.Empty<int>(); | ||
var sequenceC = Enumerable.Empty<int>(); | ||
var sequenceD = Enumerable.Empty<int>(); | ||
var sequenceE = Enumerable.Empty<int>(); | ||
var result = sequenceA.EquiInterleave(sequenceB, sequenceC, sequenceD, sequenceE); | ||
|
||
Assert.That(result, Is.Empty); | ||
} | ||
|
||
/// <summary> | ||
/// Verify that EquiInterleave throw on multiple unbalanced sequences | ||
/// </summary> | ||
[Test] | ||
public void TestEquiInterleaveManyImbalanceStrategySkip() | ||
{ | ||
void Code() | ||
{ | ||
var sequenceA = new[] {1, 5, 8, 11, 14, 16,}; | ||
var sequenceB = new[] {2, 6, 9, 12,}; | ||
var sequenceC = new int[] { }; | ||
var sequenceD = new[] {3}; | ||
var sequenceE = new[] {4, 7, 10, 13, 15, 17,}; | ||
sequenceA.EquiInterleave(sequenceB, sequenceC, sequenceD, sequenceE).Consume(); | ||
} | ||
|
||
Assert.Throws<InvalidOperationException>(Code); | ||
} | ||
|
||
/// <summary> | ||
/// Verify that Interleave disposes of all iterators it creates. | ||
/// </summary> | ||
[Test] | ||
public void TestEquiInterleaveDisposesAllIterators() | ||
{ | ||
const int count = 10; | ||
|
||
using (var sequenceA = Enumerable.Range(1, count).AsTestingSequence()) | ||
using (var sequenceB = Enumerable.Range(1, count).AsTestingSequence()) | ||
using (var sequenceC = Enumerable.Range(1, count).AsTestingSequence()) | ||
using (var sequenceD = Enumerable.Range(1, count).AsTestingSequence()) | ||
{ | ||
sequenceA.EquiInterleave(sequenceB, sequenceC, sequenceD).Consume(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
#region License and Terms | ||
// MoreLINQ - Extensions to LINQ to Objects | ||
// Copyright (c) 2019 Pierre Lando. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
#endregion | ||
|
||
namespace MoreLinq | ||
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
public static partial class MoreEnumerable | ||
{ | ||
/// <summary> | ||
/// Interleaves the elements of two or more sequences into a single sequence. | ||
/// If the input sequences are of different lengths, an exception is thrown. | ||
/// </summary> | ||
/// <remarks> | ||
/// Interleave combines sequences by visiting each in turn, and returning the first element of each, followed | ||
/// by the second, then the third, and so on. So, for example:<br/> | ||
/// <code><![CDATA[ | ||
/// {1,1,1}.Interleave( {2,2,2}, {3,3,3} ) => { 1,2,3,1,2,3,1,2,3 } | ||
/// ]]></code> | ||
/// This operator behaves in a deferred and streaming manner.<br/> | ||
/// As soon as a sequence shorter than the other is detected, an exception is thrown.<br/> | ||
/// The sequences are interleaved in the order that they appear in the <paramref name="otherSequences"/> | ||
/// collection, with <paramref name="sequence"/> as the first sequence. | ||
/// </remarks> | ||
/// <typeparam name="T">The type of the elements of the source sequences</typeparam> | ||
/// <param name="sequence">The first sequence in the interleave group</param> | ||
/// <param name="otherSequences">The other sequences in the interleave group</param> | ||
/// <returns> | ||
/// A sequence of interleaved elements from all of the source sequences</returns> | ||
/// <exception cref="InvalidOperationException"> | ||
/// The source sequences are of different lengths.</exception> | ||
|
||
public static IEnumerable<T> EquiInterleave<T>(this IEnumerable<T> sequence, params IEnumerable<T>[] otherSequences) | ||
{ | ||
if (sequence == null) throw new ArgumentNullException(nameof(sequence)); | ||
if (otherSequences == null) throw new ArgumentNullException(nameof(otherSequences)); | ||
|
||
return EquiInterleave(otherSequences.Prepend(sequence)); | ||
} | ||
|
||
private static IEnumerable<T> EquiInterleave<T>(IEnumerable<IEnumerable<T>> sequences) | ||
{ | ||
var enumerators = new List<IEnumerator<T>>(); | ||
|
||
try | ||
{ | ||
foreach (var sequence in sequences) | ||
{ | ||
if (sequence == null) | ||
throw new ArgumentException("An item is null.", nameof(sequences)); | ||
enumerators.Add(sequence.GetEnumerator()); | ||
} | ||
|
||
if (enumerators.Count == 0) | ||
yield break; | ||
|
||
for (;;) | ||
{ | ||
var (isHomogeneous, hasNext) = enumerators.Select(e => e.MoveNext()).IsHomogeneous(); | ||
|
||
if (isHomogeneous == false) | ||
throw new InvalidOperationException("Input sequences are of different length."); | ||
|
||
if (!hasNext) | ||
break; | ||
|
||
foreach (var enumerator in enumerators) | ||
yield return enumerator.Current; | ||
} | ||
} | ||
finally | ||
{ | ||
foreach (var enumerator in enumerators) | ||
enumerator.Dispose(); | ||
} | ||
} | ||
|
||
private static (bool? isHomogeneous, T value) IsHomogeneous<T>(this IEnumerable<T> source) | ||
{ | ||
var comparer = EqualityComparer<T>.Default; | ||
using var e = source.GetEnumerator(); | ||
|
||
if (!e.MoveNext()) | ||
return (null, default); | ||
|
||
var first = e.Current; | ||
while (e.MoveNext()) | ||
{ | ||
if (!comparer.Equals(first, e.Current)) | ||
return (false, default); | ||
} | ||
|
||
return (true, first); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters