Skip to content

Commit

Permalink
Merge 259c31f into 66f7cab
Browse files Browse the repository at this point in the history
  • Loading branch information
atifaziz authored Oct 24, 2023
2 parents 66f7cab + 259c31f commit 7908cda
Show file tree
Hide file tree
Showing 16 changed files with 1,700 additions and 43 deletions.
64 changes: 47 additions & 17 deletions MoreLinq.Test/PairwiseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,60 @@ namespace MoreLinq.Test
using NUnit.Framework;

[TestFixture]
public class PairwiseTest
public static class PairwiseTest
{
[Test]
public void PairwiseIsLazy()
public class ReturningTuples
{
_ = new BreakingSequence<object>().Pairwise(BreakingFunc.Of<object, object, int>());
}
[Test]
public void PairwiseIsLazy()
{
_ = new BreakingSequence<object>().Pairwise();
}

[TestCase(0)]
[TestCase(1)]
public void PairwiseWithSequenceShorterThanTwo(int count)
{
var source = Enumerable.Range(0, count);
var result = source.Pairwise(BreakingFunc.Of<int, int, int>());
[TestCase(0)]
[TestCase(1)]
public void PairwiseWithSequenceShorterThanTwo(int count)
{
var source = Enumerable.Range(0, count);
var result = source.Pairwise();

Assert.That(result, Is.Empty);
}

Assert.That(result, Is.Empty);
[Test]
public void PairwiseWideSourceSequence()
{
using var source = new[] { "a", "b", "c", "d" }.AsTestingSequence();
var result = source.Pairwise();
result.AssertSequenceEqual(("a", "b"), ("b", "c"), ("c", "d"));
}
}

[Test]
public void PairwiseWideSourceSequence()
public class ReturningSomeResults
{
using var source = new[] { "a", "b", "c", "d" }.AsTestingSequence();
var result = source.Pairwise((x, y) => x + y);
result.AssertSequenceEqual("ab", "bc", "cd");
[Test]
public void PairwiseIsLazy()
{
_ = new BreakingSequence<object>().Pairwise(BreakingFunc.Of<object, object, int>());
}

[TestCase(0)]
[TestCase(1)]
public void PairwiseWithSequenceShorterThanTwo(int count)
{
var source = Enumerable.Range(0, count);
var result = source.Pairwise(BreakingFunc.Of<int, int, int>());

Assert.That(result, Is.Empty);
}

[Test]
public void PairwiseWideSourceSequence()
{
using var source = new[] { "a", "b", "c", "d" }.AsTestingSequence();
var result = source.Pairwise((x, y) => x + y);
result.AssertSequenceEqual("ab", "bc", "cd");
}
}
}
}
252 changes: 252 additions & 0 deletions MoreLinq/Cartesian.g.cs

Large diffs are not rendered by default.

70 changes: 50 additions & 20 deletions MoreLinq/Cartesian.g.tt
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,20 @@ namespace MoreLinq
{
new[]
{
new { Ordinal = "first" , Arity = "one" },
new { Ordinal = "second" , Arity = "two" },
new { Ordinal = "third" , Arity = "three" },
new { Ordinal = "fourth" , Arity = "four" },
new { Ordinal = "fifth" , Arity = "five" },
new { Ordinal = "sixth" , Arity = "six" },
new { Ordinal = "seventh", Arity = "seven" },
new { Ordinal = "eighth" , Arity = "eight" },
new { Ordinal = "First" , Arity = "one" },
new { Ordinal = "Second" , Arity = "two" },
new { Ordinal = "Third" , Arity = "three" },
new { Ordinal = "Fourth" , Arity = "four" },
new { Ordinal = "Fifth" , Arity = "five" },
new { Ordinal = "Sixth" , Arity = "six" },
new { Ordinal = "Seventh", Arity = "seven" },
new { Ordinal = "Eighth" , Arity = "eight" },
}
}
select args.Select((a, i) => new
{
a.Ordinal,
OrdinalLower = a.Ordinal.ToLowerInvariant(),
a.Arity,
Count = i + 1,
Number = (i + 1).ToString(CultureInfo.InvariantCulture),
Expand All @@ -66,9 +67,8 @@ namespace MoreLinq
select new
{
a.Arity,
Arguments = args.Take(a.Count)
.Select(aa => new { aa.Number, aa.Ordinal })
.ToList(),
Arguments = args.Take(a.Count).ToList(),
TypeParams = string.Join(", ", from aa in args.Take(a.Count) select $"T{aa.Number}")
};

foreach (var o in overloads)
Expand All @@ -81,12 +81,12 @@ namespace MoreLinq
/// </summary>
<# foreach (var arg in o.Arguments) { #>
/// <typeparam name="T<#= arg.Number #>">
/// The type of the elements of <paramref name="<#= arg.Ordinal #>"/>.</typeparam>
/// The type of the elements of <paramref name="<#= arg.OrdinalLower #>"/>.</typeparam>
<# } #>
/// <typeparam name="TResult">
/// The type of the elements of the result sequence.</typeparam>
<# foreach (var arg in o.Arguments) {#>
/// <param name="<#= arg.Ordinal #>">The <#= arg.Ordinal #> sequence of elements.</param>
/// <param name="<#= arg.OrdinalLower #>">The <#= arg.OrdinalLower #> sequence of elements.</param>
<# } #>
/// <param name="resultSelector">A projection function that combines
/// elements from all of the sequences.</param>
Expand All @@ -102,37 +102,67 @@ namespace MoreLinq
/// This method uses deferred execution and stream its results.</para>
/// </remarks>

public static IEnumerable<TResult> Cartesian<<#= string.Join(", ", from x in o.Arguments select "T" + x.Number) #>, TResult>(
public static IEnumerable<TResult> Cartesian<<#= o.TypeParams #>, TResult>(
this <#
foreach (var arg in o.Arguments) { #>
IEnumerable<T<#= arg.Number #>> <#= arg.Ordinal #>,
IEnumerable<T<#= arg.Number #>> <#= arg.OrdinalLower #>,
<#
} #>
Func<<#= string.Join(", ", from x in o.Arguments select "T" + x.Number) #>, TResult> resultSelector)
Func<<#= o.TypeParams #>, TResult> resultSelector)
{
<# foreach (var arg in o.Arguments) { #>
if (<#= arg.Ordinal #> == null) throw new ArgumentNullException(nameof(<#= arg.Ordinal #>));
if (<#= arg.OrdinalLower #> == null) throw new ArgumentNullException(nameof(<#= arg.OrdinalLower #>));
<# } #>
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

return _(); IEnumerable<TResult> _()
{
<# foreach (var arg in o.Arguments.Skip(1)) { #>
IEnumerable<T<#= arg.Number #>> <#= arg.Ordinal #>Memo;
IEnumerable<T<#= arg.Number #>> <#= arg.OrdinalLower #>Memo;
<# } #>

<# foreach (var arg in o.Arguments.Skip(1)) { #>
using ((<#= arg.Ordinal #>Memo = <#= arg.Ordinal #>.Memoize()) as IDisposable)
using ((<#= arg.OrdinalLower #>Memo = <#= arg.OrdinalLower #>.Memoize()) as IDisposable)
<# } #>
{
foreach (var item1 in first)
<# foreach (var arg in o.Arguments.Skip(1)) { #>
foreach (var item<#= arg.Number #> in <#= arg.Ordinal #>Memo)
foreach (var item<#= arg.Number #> in <#= arg.OrdinalLower #>Memo)
<# } #>
yield return resultSelector(<#= string.Join(", ", from x in o.Arguments select "item" + x.Number) #>);
}
}
}

/// <summary>
/// Returns the Cartesian product of <#= o.Arity #> sequences by enumerating tuples
/// of all possible combinations of one item from each sequence.
/// </summary>
<# foreach (var arg in o.Arguments) { #>
/// <typeparam name="T<#= arg.Number #>">The type of the elements of <paramref name="<#= arg.OrdinalLower #>"/>.</typeparam>
<# } #>
<# foreach (var arg in o.Arguments) {#>
/// <param name="<#= arg.OrdinalLower #>">The <#= arg.OrdinalLower #> sequence of elements.</param>
<# } #>
/// <returns>A sequence of tuples.</returns>
/// <remarks>
/// <para>
/// The method returns items in the same order as a nested foreach
/// loop, but all sequences except for <paramref name="first"/> are
/// cached when iterated over. The cache is then re-used for any
/// subsequent iterations.</para>
/// <para>
/// This method uses deferred execution and stream its results.</para>
/// </remarks>

public static IEnumerable<(<#= string.Join(", ", from a in o.Arguments select $"T{a.Number} {a.Ordinal}") #>)>
Cartesian<<#= o.TypeParams #>>(
this <#= string.Join($",{Environment.NewLine} ",
from arg in o.Arguments
select $"IEnumerable<T{arg.Number}> {arg.OrdinalLower}") #>)
{
return Cartesian(<#= string.Join(", ", from a in o.Arguments select a.OrdinalLower) #>, ValueTuple.Create);
}
<# } #>
}
}
26 changes: 26 additions & 0 deletions MoreLinq/CountDown.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,31 @@ IEnumerable<TResult> IterateSequence()
yield return resultSelector(queue.Dequeue(), queue.Count);
}
}

/// <summary>
/// Provides a countdown counter for a given count of elements at the
/// tail of the sequence where zero always represents the last element,
/// one represents the second-last element, two represents the
/// third-last element and so on.
/// </summary>
/// <typeparam name="T">
/// The type of elements of <paramref name="source"/></typeparam>
/// <param name="source">The source sequence.</param>
/// <param name="count">Count of tail elements of <paramref name="source"/> to count down.</param>
/// <returns>
/// A sequence of tuple with an element from <paramref name="source"/> and its countdown.
/// For elements before the last <paramref name="count"/>, the countdown value is <c>null</c>.
/// </returns>
/// <remarks>
/// This method uses deferred execution semantics and streams its
/// results. At most, <paramref name="count"/> elements of the source
/// sequence may be buffered at any one time unless
/// <paramref name="source"/> is a collection or a list.
/// </remarks>

public static IEnumerable<(T Item, int? CountDown)> CountDown<T>(this IEnumerable<T> source, int count)
{
return source.CountDown(count, ValueTuple.Create);
}

Check warning on line 122 in MoreLinq/CountDown.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/CountDown.cs#L122

Added line #L122 was not covered by tests
}
}
112 changes: 112 additions & 0 deletions MoreLinq/EquiZip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,118 @@ public static IEnumerable<TResult> EquiZip<T1, T2, T3, T4, TResult>(
return EquiZipImpl(first, second, third, fourth, resultSelector);
}

/// <summary>
/// Returns tuples, where each tuple contains the N-th
/// element from each of the argument sequences. An exception is thrown
/// if the input sequences are of different lengths.
/// </summary>
/// <typeparam name="T1">Type of elements in first sequence</typeparam>
/// <typeparam name="T2">Type of elements in second sequence</typeparam>
/// <param name="first">The first sequence.</param>
/// <param name="second">The second sequence.</param>
/// <returns>
/// A sequence of tuples that contains elements of the two input sequences.
/// </returns>
/// <exception cref="InvalidOperationException">
/// The input sequences are of different lengths.
/// </exception>
/// <example>
/// <code><![CDATA[
/// var numbers = new[] { 1, 2, 3, 4 };
/// var letters = new[] { "A", "B", "C", "D" };
/// var zipped = numbers.EquiZip(letters);
/// ]]></code>
/// The <c>zipped</c> variable, when iterated over, will yield the tuples : (1, A),
/// (2, B), (3, C), (4, D) in turn.
/// </example>
/// <remarks>
/// This operator uses deferred execution and streams its results.
/// </remarks>

public static IEnumerable<(T1, T2)> EquiZip<T1, T2>(this IEnumerable<T1> first, IEnumerable<T2> second)
{
return first.EquiZip(second, ValueTuple.Create);
}

Check warning on line 215 in MoreLinq/EquiZip.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/EquiZip.cs#L215

Added line #L215 was not covered by tests

/// <summary>
/// Returns tuples, where each tuple contains the N-th
/// element from each of the argument sequences. An exception is thrown
/// if the input sequences are of different lengths.
/// </summary>
/// <typeparam name="T1">Type of elements in first sequence</typeparam>
/// <typeparam name="T2">Type of elements in second sequence</typeparam>
/// <typeparam name="T3">Type of elements in third sequence</typeparam>
/// <param name="first">The first sequence.</param>
/// <param name="second">The second sequence.</param>
/// <param name="third">The third sequence.</param>
/// <returns>
/// A sequence of tuples that contains elements of the three input sequences.
/// </returns>
/// <exception cref="InvalidOperationException">
/// The input sequences are of different lengths.
/// </exception>
/// <example>
/// <code><![CDATA[
/// var numbers = new[] { 1, 2, 3, 4 };
/// var letters = new[] { "A", "B", "C", "D" };
/// var chars = new[] { 'a', 'b', 'c', 'd' };
/// var zipped = numbers.EquiZip(letters, chars);
/// ]]></code>
/// The <c>zipped</c> variable, when iterated over, will yield the tuples : (1, A, a),
/// (2, B, b), (3, C, c), (4, D, d) in turn.
/// </example>
/// <remarks>
/// This operator uses deferred execution and streams its results.
/// </remarks>

public static IEnumerable<(T1, T2, T3)> EquiZip<T1, T2, T3>(
this IEnumerable<T1> first,
IEnumerable<T2> second, IEnumerable<T3> third)
{
return first.EquiZip(second, third, ValueTuple.Create);
}

Check warning on line 253 in MoreLinq/EquiZip.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/EquiZip.cs#L253

Added line #L253 was not covered by tests

/// <summary>
/// Returns tuples, where each tuple contains the N-th
/// element from each of the argument sequences. An exception is thrown
/// if the input sequences are of different lengths.
/// </summary>
/// <typeparam name="T1">Type of elements in first sequence</typeparam>
/// <typeparam name="T2">Type of elements in second sequence</typeparam>
/// <typeparam name="T3">Type of elements in third sequence</typeparam>
/// <typeparam name="T4">Type of elements in fourth sequence</typeparam>
/// <param name="first">The first sequence.</param>
/// <param name="second">The second sequence.</param>
/// <param name="third">The third sequence.</param>
/// <param name="fourth">The fourth sequence.</param>
/// <returns>
/// A sequence of tuples that contains elements of the four input sequences.
/// </returns>
/// <exception cref="InvalidOperationException">
/// The input sequences are of different lengths.
/// </exception>
/// <example>
/// <code><![CDATA[
/// var numbers = new[] { 1, 2, 3, 4 };
/// var letters = new[] { "A", "B", "C", "D" };
/// var chars = new[] { 'a', 'b', 'c', 'd' };
/// var flags = new[] { true, false, true, false };
/// var zipped = numbers.EquiZip(letters, chars, flags);
/// ]]></code>
/// The <c>zipped</c> variable, when iterated over, will yield the tuples : (1, A, a, True),
/// (2, B, b, False), (3, C, c, True), (4, D, d, False) in turn.
/// </example>
/// <remarks>
/// This operator uses deferred execution and streams its results.
/// </remarks>

public static IEnumerable<(T1, T2, T3, T4)> EquiZip<T1, T2, T3, T4>(
this IEnumerable<T1> first,
IEnumerable<T2> second, IEnumerable<T3> third, IEnumerable<T4> fourth)
{
return first.EquiZip(second, third, fourth, ValueTuple.Create);
}

Check warning on line 294 in MoreLinq/EquiZip.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/EquiZip.cs#L294

Added line #L294 was not covered by tests

static IEnumerable<TResult> EquiZipImpl<T1, T2, T3, T4, TResult>(
IEnumerable<T1> s1,
IEnumerable<T2> s2,
Expand Down
Loading

0 comments on commit 7908cda

Please sign in to comment.