diff --git a/Source/SuperLinq/Pad.cs b/Source/SuperLinq/Pad.cs index d7245a83..3122bba8 100644 --- a/Source/SuperLinq/Pad.cs +++ b/Source/SuperLinq/Pad.cs @@ -94,36 +94,71 @@ public static IEnumerable Pad(this IEnumerable source Guard.IsGreaterThanOrEqualTo(width, 0); if (source is IList list) - return new PadIterator(list, width, paddingSelector); + return new PadListIterator(list, width, paddingSelector); - return Core(source, width, paddingSelector); + if (source.TryGetCollectionCount() is int) + return new PadCollectionIterator(source, width, paddingSelector); - static IEnumerable Core( - IEnumerable source, int width, - Func paddingSelector) + return PadCore(source, width, paddingSelector); + } + + private static IEnumerable PadCore( + IEnumerable source, int width, + Func paddingSelector) + { + var count = 0; + foreach (var item in source) + { + yield return item; + count++; + } + + while (count < width) + { + yield return paddingSelector(count); + count++; + } + } + + private sealed class PadCollectionIterator : CollectionIterator + { + private readonly IEnumerable _source; + private readonly int _width; + private readonly Func _paddingSelector; + + public PadCollectionIterator( + IEnumerable source, int width, + Func paddingSelector) { - var count = 0; - foreach (var item in source) - { - yield return item; - count++; - } - - while (count < width) - { - yield return paddingSelector(count); - count++; - } + _source = source; + _width = width; + _paddingSelector = paddingSelector; + } + + public override int Count => Math.Max(_source.GetCollectionCount(), _width); + + protected override IEnumerable GetEnumerable() => + PadCore(_source, _width, _paddingSelector); + + public override void CopyTo(T[] array, int arrayIndex) + { + Guard.IsNotNull(array); + Guard.IsBetweenOrEqualTo(arrayIndex, 0, array.Length - Count); + + var cnt = _source.CopyTo(array, arrayIndex); + + for (var i = cnt; i < _width; i++) + array[arrayIndex + i] = _paddingSelector(i); } } - private sealed class PadIterator : ListIterator + private sealed class PadListIterator : ListIterator { private readonly IList _source; private readonly int _width; private readonly Func _paddingSelector; - public PadIterator(IList source, int width, Func paddingSelector) + public PadListIterator(IList source, int width, Func paddingSelector) { _source = source; _width = width; @@ -146,7 +181,7 @@ protected override IEnumerable GetEnumerable() public override void CopyTo(T[] array, int arrayIndex) { Guard.IsNotNull(array); - Guard.IsGreaterThanOrEqualTo(arrayIndex, 0); + Guard.IsBetweenOrEqualTo(arrayIndex, 0, array.Length - Count); _source.CopyTo(array, arrayIndex); diff --git a/Tests/SuperLinq.Test/PadTest.cs b/Tests/SuperLinq.Test/PadTest.cs index 000f94f5..9f16c907 100644 --- a/Tests/SuperLinq.Test/PadTest.cs +++ b/Tests/SuperLinq.Test/PadTest.cs @@ -17,214 +17,118 @@ public void PadIsLazy() _ = new BreakingSequence().Pad(0, BreakingFunc.Of()); } - public class ValueTypeElements + public static IEnumerable GetIntSequences() => + Seq(123, 456, 789) + .GetAllSequences() + .Select(x => new object[] { x, }); + + [Theory] + [MemberData(nameof(GetIntSequences))] + public void PadWideSourceSequence(IDisposableEnumerable seq) { - public static IEnumerable GetIntSequences() + using (seq) { - var seq = Seq(123, 456, 789); - yield return new object[] { seq.AsTestingSequence(maxEnumerations: 2), false, }; - yield return new object[] { seq.AsBreakingList(), false, }; - yield return new object[] { seq.AsBreakingList(), true, }; + var result = seq.Pad(2); + result.AssertSequenceEqual( + Seq(123, 456, 789), + testCollectionEnumerable: true); } + } - [Theory] - [MemberData(nameof(GetIntSequences))] - public void PadWideSourceSequence(IDisposableEnumerable seq, bool select) - { - using (seq) - { - var result = seq.Pad(2); - if (select) result = result.Select(SuperEnumerable.Identity); - result.AssertSequenceEqual(123, 456, 789); - } - } - - [Fact] - public void PadWideListBehavior() - { - using var seq = Enumerable.Range(0, 10_000).AsBreakingList(); - - var result = seq.Pad(5_000, x => x % 1_000); - Assert.Equal(10_000, result.Count()); - Assert.Equal(1_200, result.ElementAt(1_200)); - Assert.Equal(8_800, result.ElementAt(^1_200)); - - _ = Assert.Throws( - "index", - () => result.ElementAt(10_001)); - } - - [Theory] - [MemberData(nameof(GetIntSequences))] - public void PadEqualSourceSequence(IDisposableEnumerable seq, bool select) - { - using (seq) - { - var result = seq.Pad(3); - if (select) result = result.Select(SuperEnumerable.Identity); - result.AssertSequenceEqual(123, 456, 789); - } - } - - [Theory] - [MemberData(nameof(GetIntSequences))] - public void PadNarrowSourceSequenceWithDefaultPadding(IDisposableEnumerable seq, bool select) - { - using (seq) - { - var result = seq.Pad(5); - if (select) result = result.Select(SuperEnumerable.Identity); - result.AssertSequenceEqual(123, 456, 789, 0, 0); - } - } - - [Theory] - [MemberData(nameof(GetIntSequences))] - public void PadNarrowSourceSequenceWithNonDefaultPadding(IDisposableEnumerable seq, bool select) - { - using (seq) - { - var result = seq.Pad(5, -1); - if (select) result = result.Select(SuperEnumerable.Identity); - result.AssertSequenceEqual(123, 456, 789, -1, -1); - } - } - - [Fact] - public void PadNarrowListBehavior() - { - using var seq = Enumerable.Range(0, 10_000).AsBreakingList(); - - var result = seq.Pad(40_000, x => x % 1_000); - Assert.Equal(40_000, result.Count()); - Assert.Equal(1_200, result.ElementAt(1_200)); - Assert.Equal(200, result.ElementAt(11_200)); - Assert.Equal(800, result.ElementAt(^1_200)); + [Fact] + public void PadWideListBehavior() + { + using var seq = Enumerable.Range(0, 10_000).AsBreakingList(); - _ = Assert.Throws( - "index", - () => result.ElementAt(40_001)); - } + var result = seq.Pad(5_000, x => x % 1_000); + Assert.Equal(10_000, result.Count()); + Assert.Equal(1_200, result.ElementAt(1_200)); + Assert.Equal(8_800, result.ElementAt(^1_200)); - public static IEnumerable GetCharSequences() - { - var seq = "hello".AsEnumerable(); - yield return new object[] { seq.AsTestingSequence(maxEnumerations: 2), false, }; - yield return new object[] { seq.AsBreakingList(), false, }; - yield return new object[] { seq.AsBreakingList(), true, }; - } + _ = Assert.Throws( + "index", + () => result.ElementAt(10_001)); + } - [Theory] - [MemberData(nameof(GetCharSequences))] - public void PadNarrowSourceSequenceWithDynamicPadding(IDisposableEnumerable seq, bool select) + [Theory] + [MemberData(nameof(GetIntSequences))] + public void PadEqualSourceSequence(IDisposableEnumerable seq) + { + using (seq) { - using (seq) - { - var result = seq.Pad(15, i => i % 2 == 0 ? '+' : '-'); - if (select) result = result.Select(SuperEnumerable.Identity); - result.AssertSequenceEqual("hello-+-+-+-+-+".ToCharArray()); - } + var result = seq.Pad(3); + result.AssertSequenceEqual( + Seq(123, 456, 789), + testCollectionEnumerable: true); } } - public class ReferenceTypeElements + [Theory] + [MemberData(nameof(GetIntSequences))] + public void PadNarrowSourceSequenceWithDefaultPadding(IDisposableEnumerable seq) { - public static IEnumerable GetStringSequences() + using (seq) { - var seq = Seq("foo", "bar", "baz"); - yield return new object[] { seq.AsTestingSequence(maxEnumerations: 2), false, }; - yield return new object[] { seq.AsBreakingList(), false, }; - yield return new object[] { seq.AsBreakingList(), true, }; + var result = seq.Pad(5); + result.AssertSequenceEqual( + Seq(123, 456, 789, 0, 0), + testCollectionEnumerable: true); } + } - [Theory] - [MemberData(nameof(GetStringSequences))] - public void PadWideSourceSequence(IDisposableEnumerable seq, bool select) + [Theory] + [MemberData(nameof(GetIntSequences))] + public void PadNarrowSourceSequenceWithNonDefaultPadding(IDisposableEnumerable seq) + { + using (seq) { - using (seq) - { - var result = seq.Pad(2); - if (select) result = result.Select(SuperEnumerable.Identity); - result.AssertSequenceEqual("foo", "bar", "baz"); - } + var result = seq.Pad(5, -1); + result.AssertSequenceEqual( + Seq(123, 456, 789, -1, -1), + testCollectionEnumerable: true); } + } - [Fact] - public void PadWideListBehavior() - { - using var seq = Seq("foo", "bar", "baz").AsBreakingList(); - - var result = seq.Pad(2, x => $"Extra{x}"); - Assert.Equal(3, result.Count()); - Assert.Equal("bar", result.ElementAt(1)); - Assert.Equal("baz", result.ElementAt(^1)); + [Fact] + public void PadNarrowCollectionBehavior() + { + using var seq = Enumerable.Range(0, 10_000).AsBreakingCollection(); - _ = Assert.Throws( - "index", - () => result.ElementAt(3)); - } + var result = seq.Pad(40_000, x => x % 1_000); + Assert.Equal(40_000, result.Count()); + } - [Theory] - [MemberData(nameof(GetStringSequences))] - public void PadEqualSourceSequence(IDisposableEnumerable seq, bool select) - { - using (seq) - { - var result = seq.Pad(3); - if (select) result = result.Select(SuperEnumerable.Identity); - result.AssertSequenceEqual("foo", "bar", "baz"); - } - } + [Fact] + public void PadNarrowListBehavior() + { + using var seq = Enumerable.Range(0, 10_000).AsBreakingList(); - [Theory] - [MemberData(nameof(GetStringSequences))] - public void PadNarrowSourceSequenceWithDefaultPadding(IDisposableEnumerable seq, bool select) - { - using (seq) - { - var result = seq.Pad(5); - if (select) result = result.Select(SuperEnumerable.Identity); - result.AssertSequenceEqual("foo", "bar", "baz", null, null); - } - } + var result = seq.Pad(40_000, x => x % 1_000); + Assert.Equal(40_000, result.Count()); + Assert.Equal(1_200, result.ElementAt(1_200)); + Assert.Equal(200, result.ElementAt(11_200)); + Assert.Equal(800, result.ElementAt(^1_200)); - [Theory] - [MemberData(nameof(GetStringSequences))] - public void PadNarrowSourceSequenceWithNonDefaultPadding(IDisposableEnumerable seq, bool select) - { - using (seq) - { - var result = seq.Pad(5, string.Empty); - if (select) result = result.Select(SuperEnumerable.Identity); - result.AssertSequenceEqual("foo", "bar", "baz", string.Empty, string.Empty); - } - } + _ = Assert.Throws( + "index", + () => result.ElementAt(40_001)); + } - [Theory] - [MemberData(nameof(GetStringSequences))] - public void PadNarrowSourceSequenceWithDynamicPadding(IDisposableEnumerable seq, bool select) - { - using (seq) - { - var result = seq.Pad(5, x => $"Extra{x}"); - if (select) result = result.Select(SuperEnumerable.Identity); - result.AssertSequenceEqual("foo", "bar", "baz", "Extra3", "Extra4"); - } - } + public static IEnumerable GetCharSequences() => + "hello".AsEnumerable() + .GetAllSequences() + .Select(x => new object[] { x, }); - [Fact] - public void PadNarrowListBehavior() + [Theory] + [MemberData(nameof(GetCharSequences))] + public void PadNarrowSourceSequenceWithDynamicPadding(IDisposableEnumerable seq) + { + using (seq) { - using var seq = Seq("foo", "bar", "baz").AsBreakingList(); - - var result = seq.Pad(5, x => $"Extra{x}"); - Assert.Equal(5, result.Count()); - Assert.Equal("bar", result.ElementAt(1)); - Assert.Equal("Extra4", result.ElementAt(^1)); - - _ = Assert.Throws( - "index", - () => result.ElementAt(5)); + var result = seq.Pad(15, i => i % 2 == 0 ? '+' : '-'); + result.AssertSequenceEqual( + "hello-+-+-+-+-+", + testCollectionEnumerable: true); } } }