From 8e179addc367f5d208411173fc5d5ee768c18ab8 Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 6 Nov 2019 11:44:50 +0100 Subject: [PATCH 01/17] Add TestWindowDoNotExposeItsBuffer, WindowLeftDoNotExposeItsBuffer and WindowRightDoNotExposeItsBuffer. --- MoreLinq.Test/WindowLeftTest.cs | 17 +++++++++++++++++ MoreLinq.Test/WindowRightTest.cs | 17 +++++++++++++++++ MoreLinq.Test/WindowTest.cs | 16 ++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/MoreLinq.Test/WindowLeftTest.cs b/MoreLinq.Test/WindowLeftTest.cs index d85d8f11f..827cbecfe 100644 --- a/MoreLinq.Test/WindowLeftTest.cs +++ b/MoreLinq.Test/WindowLeftTest.cs @@ -12,6 +12,23 @@ public void WindowLeftIsLazy() new BreakingSequence().WindowLeft(1); } + /// + /// Verify that Window doesn't return it's internal buffer + /// + [Test] + public void WindowLeftDoNotExposeItsBuffer() + { + var sequence = Enumerable.Repeat(0, 3); + var expected = new[] { 0, 0, 0 }; + var actual = sequence.WindowLeft(2).Select(l => + { + if (l.Count > 1) + l[1] = 1; + return l[0]; + }).ToList(); + CollectionAssert.AreEqual(expected, actual); + } + [Test] public void WindowLeftWithNegativeWindowSize() { diff --git a/MoreLinq.Test/WindowRightTest.cs b/MoreLinq.Test/WindowRightTest.cs index 62951923c..e30eb6640 100644 --- a/MoreLinq.Test/WindowRightTest.cs +++ b/MoreLinq.Test/WindowRightTest.cs @@ -12,6 +12,23 @@ public void WindowRightIsLazy() new BreakingSequence().WindowRight(1); } + /// + /// Verify that Window doesn't return it's internal buffer + /// + [Test] + public void WindowRightDoNotExposeItsBuffer() + { + var sequence = Enumerable.Repeat(0, 3); + var expected = new[] { 0, 0, 0 }; + var actual = sequence.WindowRight(2).Select(l => + { + if (l.Count > 1) + l[1] = 1; + return l[0]; + }).ToList(); + CollectionAssert.AreEqual(expected, actual); + } + [Test] public void WindowRightWithNegativeWindowSize() { diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index 83e71e6a4..732ef9dea 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -17,6 +17,22 @@ public void TestWindowIsLazy() new BreakingSequence().Window(1); } + /// + /// Verify that Window doesn't return it's internal buffer + /// + [Test] + public void TestWindowDoNotExposeItsBuffer() + { + var sequence = Enumerable.Repeat(0, 3); + var expected = new[] {0, 0}; + var actual = sequence.Window(2).Select(l => + { + l[1] = 1; + return l[0]; + }).ToList(); + CollectionAssert.AreEqual(expected, actual); + } + /// /// Verify that a negative window size results in an exception /// From 7cc861506289ac0069960061febc5b04a5e4429d Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 6 Nov 2019 12:33:48 +0100 Subject: [PATCH 02/17] Add TestWindowOnKnownResults --- MoreLinq.Test/WindowTest.cs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index 732ef9dea..cb43ae2e8 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -1,3 +1,7 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework.Interfaces; + namespace MoreLinq.Test { using NUnit.Framework; @@ -41,7 +45,7 @@ public void TestWindowNegativeWindowSizeException() { var sequence = Enumerable.Repeat(1, 10); - AssertThrowsArgument.OutOfRangeException("size",() => + AssertThrowsArgument.OutOfRangeException("size", () => sequence.Window(-5)); } @@ -130,5 +134,24 @@ public void TestWindowWindowsImmutability() reader.ReadEnd(); } } + + static IEnumerable Seq(params T[] values) => values; + + public static readonly IEnumerable TestData = + from e in new[] + { + new {Source = Enumerable.Range(0, 4), Size = 1, Result = new[] {Seq(0), Seq(1), Seq(2), Seq(3)}}, + new {Source = Enumerable.Range(0, 4), Size = 2, Result = new[] {Seq(0, 1), Seq(1, 2), Seq(2, 3)}}, + new {Source = Enumerable.Range(0, 4), Size = 3, Result = new[] {Seq(0, 1, 2), Seq(1, 2, 3)}}, + new {Source = Enumerable.Range(0, 4), Size = 4, Result = new[] {Seq(0, 1, 2, 3)}} + } + select new TestCaseData(e.Source, e.Size).Returns(e.Result); + + [Test, TestCaseSource(nameof(TestData))] + public IEnumerable> TestWindowOnKnownResults(IEnumerable sequence, int sizes) + { + using var testingSequence = sequence.AsTestingSequence(); + return testingSequence.Window(sizes).ToList(); + } } } From 8765b1b7a8b628e54f285a5c1936ff898e742c76 Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 6 Nov 2019 13:43:19 +0100 Subject: [PATCH 03/17] Renamed tests Window*DoNotExposeItsBuffer => TestWindow*ReturnIsolatedList --- MoreLinq.Test/WindowLeftTest.cs | 16 +++++++--------- MoreLinq.Test/WindowRightTest.cs | 16 +++++++--------- MoreLinq.Test/WindowTest.cs | 16 +++++++--------- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/MoreLinq.Test/WindowLeftTest.cs b/MoreLinq.Test/WindowLeftTest.cs index 827cbecfe..fc7ab73c9 100644 --- a/MoreLinq.Test/WindowLeftTest.cs +++ b/MoreLinq.Test/WindowLeftTest.cs @@ -13,20 +13,18 @@ public void WindowLeftIsLazy() } /// - /// Verify that Window doesn't return it's internal buffer + /// Verify that elements returned by Window are isolated. + /// Modification on one window should not be visible from the next window. /// [Test] - public void WindowLeftDoNotExposeItsBuffer() + public void TestWindowLeftReturnIsolatedList() { var sequence = Enumerable.Repeat(0, 3); - var expected = new[] { 0, 0, 0 }; - var actual = sequence.WindowLeft(2).Select(l => + foreach (var window in sequence.WindowLeft(2)) { - if (l.Count > 1) - l[1] = 1; - return l[0]; - }).ToList(); - CollectionAssert.AreEqual(expected, actual); + window[window.Count - 1] = 1; // modify last item of the window + Assert.That(window[0], Is.EqualTo(0)); + } } [Test] diff --git a/MoreLinq.Test/WindowRightTest.cs b/MoreLinq.Test/WindowRightTest.cs index e30eb6640..206d04ac9 100644 --- a/MoreLinq.Test/WindowRightTest.cs +++ b/MoreLinq.Test/WindowRightTest.cs @@ -13,20 +13,18 @@ public void WindowRightIsLazy() } /// - /// Verify that Window doesn't return it's internal buffer + /// Verify that elements returned by Window are isolated. + /// Modification on one window should not be visible from the next window. /// [Test] - public void WindowRightDoNotExposeItsBuffer() + public void TestWindowRightReturnIsolatedList() { var sequence = Enumerable.Repeat(0, 3); - var expected = new[] { 0, 0, 0 }; - var actual = sequence.WindowRight(2).Select(l => + foreach (var window in sequence.WindowRight(2)) { - if (l.Count > 1) - l[1] = 1; - return l[0]; - }).ToList(); - CollectionAssert.AreEqual(expected, actual); + window[window.Count - 1] = 1; // modify last item of the window + Assert.That(window[0], Is.EqualTo(0)); + } } [Test] diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index cb43ae2e8..4a75b708a 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using NUnit.Framework.Interfaces; @@ -22,19 +21,18 @@ public void TestWindowIsLazy() } /// - /// Verify that Window doesn't return it's internal buffer + /// Verify that elements returned by Window are isolated. + /// Modification on one window should not be visible from the next window. /// [Test] - public void TestWindowDoNotExposeItsBuffer() + public void TestWindowReturnIsolatedList() { var sequence = Enumerable.Repeat(0, 3); - var expected = new[] {0, 0}; - var actual = sequence.Window(2).Select(l => + foreach (var window in sequence.Window(2)) { - l[1] = 1; - return l[0]; - }).ToList(); - CollectionAssert.AreEqual(expected, actual); + window[window.Count - 1] = 1; // modify last item of the window + Assert.That(window[0], Is.EqualTo(0)); + } } /// From 340858b6596217ad2a7426491045304f3ef4e45e Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 6 Nov 2019 13:58:56 +0100 Subject: [PATCH 04/17] Fix TestWindow*ReturnIsolatedList --- MoreLinq.Test/WindowLeftTest.cs | 3 ++- MoreLinq.Test/WindowRightTest.cs | 3 ++- MoreLinq.Test/WindowTest.cs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/MoreLinq.Test/WindowLeftTest.cs b/MoreLinq.Test/WindowLeftTest.cs index fc7ab73c9..c122fa881 100644 --- a/MoreLinq.Test/WindowLeftTest.cs +++ b/MoreLinq.Test/WindowLeftTest.cs @@ -22,7 +22,8 @@ public void TestWindowLeftReturnIsolatedList() var sequence = Enumerable.Repeat(0, 3); foreach (var window in sequence.WindowLeft(2)) { - window[window.Count - 1] = 1; // modify last item of the window + if (window.Count > 1) + window[1] = 1; Assert.That(window[0], Is.EqualTo(0)); } } diff --git a/MoreLinq.Test/WindowRightTest.cs b/MoreLinq.Test/WindowRightTest.cs index 206d04ac9..b8dd2260b 100644 --- a/MoreLinq.Test/WindowRightTest.cs +++ b/MoreLinq.Test/WindowRightTest.cs @@ -22,7 +22,8 @@ public void TestWindowRightReturnIsolatedList() var sequence = Enumerable.Repeat(0, 3); foreach (var window in sequence.WindowRight(2)) { - window[window.Count - 1] = 1; // modify last item of the window + if (window.Count > 1) + window[1] = 1; Assert.That(window[0], Is.EqualTo(0)); } } diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index 4a75b708a..3553ce95a 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -30,7 +30,7 @@ public void TestWindowReturnIsolatedList() var sequence = Enumerable.Repeat(0, 3); foreach (var window in sequence.Window(2)) { - window[window.Count - 1] = 1; // modify last item of the window + window[1] = 1; Assert.That(window[0], Is.EqualTo(0)); } } From e41a21d3a4a49e18d8f439d793a0c43b911b26b7 Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 6 Nov 2019 14:02:40 +0100 Subject: [PATCH 05/17] Window* methods returned elements that are not isolated from each other. --- MoreLinq/Window.cs | 21 ++++++++++++--------- MoreLinq/WindowLeft.cs | 9 +++++++-- MoreLinq/WindowRight.cs | 5 ++++- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/MoreLinq/Window.cs b/MoreLinq/Window.cs index c377f90d3..ed32f6b1d 100644 --- a/MoreLinq/Window.cs +++ b/MoreLinq/Window.cs @@ -52,19 +52,22 @@ public static IEnumerable> Window(this IEnumerable> WindowLeft(this IEnumerable(window.Skip(1)); yield return window; - window = new List(window.Skip(1)); + window = nextWindow; } while (window.Count > 0) { + // prepare next window before exposing data + var nextWindow = new List(window.Skip(1)); yield return window; - window = new List(window.Skip(1)); + window = nextWindow; } } } diff --git a/MoreLinq/WindowRight.cs b/MoreLinq/WindowRight.cs index 9f9d76a91..4172c3d6d 100644 --- a/MoreLinq/WindowRight.cs +++ b/MoreLinq/WindowRight.cs @@ -84,8 +84,11 @@ static IEnumerable> WindowRightWhile( foreach (var item in source) { window.Add(item); + + // prepare next window before exposing data + var nextWindow = new List(predicate(item, window.Count) ? window : window.Skip(1)); yield return window; - window = new List(predicate(item, window.Count) ? window : window.Skip(1)); + window = nextWindow; } } } From 2ca1b7ab5a22b3a3c1cb62e70a469250665da180 Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 6 Nov 2019 14:27:13 +0100 Subject: [PATCH 06/17] Add TestWindowLeftOnKnownResults and TestWindowRightOnKnownResults --- MoreLinq.Test/WindowLeftTest.cs | 22 ++++++++++++++++++++++ MoreLinq.Test/WindowRightTest.cs | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/MoreLinq.Test/WindowLeftTest.cs b/MoreLinq.Test/WindowLeftTest.cs index c122fa881..ad02c2ae6 100644 --- a/MoreLinq.Test/WindowLeftTest.cs +++ b/MoreLinq.Test/WindowLeftTest.cs @@ -1,3 +1,5 @@ +using NUnit.Framework.Interfaces; + namespace MoreLinq.Test { using System.Collections.Generic; @@ -92,5 +94,25 @@ public void WindowLeftWithWindowSizeSmallerThanSequence() reader.ReadEnd(); } } + + static IEnumerable Seq(params T[] values) => values; + + public static readonly IEnumerable TestData = + from e in new[] + { + new {Source = Enumerable.Range(0, 4), Size = 1, Result = new[] {Seq(0), Seq(1), Seq(2), Seq(3)}}, + new {Source = Enumerable.Range(0, 4), Size = 2, Result = new[] {Seq(0, 1), Seq(1, 2), Seq(2, 3), Seq(3)}}, + new {Source = Enumerable.Range(0, 4), Size = 3, Result = new[] {Seq(0, 1, 2), Seq(1, 2, 3), Seq(2, 3), Seq(3)}}, + new {Source = Enumerable.Range(0, 4), Size = 4, Result = new[] {Seq(0, 1, 2, 3), Seq(1, 2, 3), Seq(2, 3), Seq(3)}} + } + select new TestCaseData(e.Source, e.Size).Returns(e.Result); + + [Test, TestCaseSource(nameof(TestData))] + public IEnumerable> TestWindowLeftOnKnownResults(IEnumerable sequence, int sizes) + { + using var testingSequence = sequence.AsTestingSequence(); + var r = testingSequence.WindowLeft(sizes).ToList(); + return r; + } } } diff --git a/MoreLinq.Test/WindowRightTest.cs b/MoreLinq.Test/WindowRightTest.cs index b8dd2260b..8465e492d 100644 --- a/MoreLinq.Test/WindowRightTest.cs +++ b/MoreLinq.Test/WindowRightTest.cs @@ -1,3 +1,5 @@ +using NUnit.Framework.Interfaces; + namespace MoreLinq.Test { using System.Collections.Generic; @@ -92,5 +94,25 @@ public void WindowRightWithWindowSizeSmallerThanSequence() reader.ReadEnd(); } } + + static IEnumerable Seq(params T[] values) => values; + + public static readonly IEnumerable TestData = + from e in new[] + { + new {Source = Enumerable.Range(0, 4), Size = 1, Result = new[] {Seq(0), Seq(1), Seq(2), Seq(3)}}, + new {Source = Enumerable.Range(0, 4), Size = 2, Result = new[] {Seq(0), Seq(0, 1), Seq(1, 2), Seq(2, 3)}}, + new {Source = Enumerable.Range(0, 4), Size = 3, Result = new[] {Seq(0), Seq(0, 1), Seq(0, 1, 2), Seq(1, 2, 3)}}, + new {Source = Enumerable.Range(0, 4), Size = 4, Result = new[] {Seq(0), Seq(0, 1), Seq(0, 1, 2), Seq(0, 1, 2, 3)}} + } + select new TestCaseData(e.Source, e.Size).Returns(e.Result); + + [Test, TestCaseSource(nameof(TestData))] + public IEnumerable> TestWindowRightOnKnownResults(IEnumerable sequence, int sizes) + { + using var testingSequence = sequence.AsTestingSequence(); + var r = testingSequence.WindowRight(sizes).ToList(); + return r; + } } } From 207781b13d56d4718e32d7672503d542b9aaba19 Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 6 Nov 2019 17:50:01 +0100 Subject: [PATCH 07/17] Refactor Window(Left|Right) so they use the same implementation. --- MoreLinq/Window.cs | 134 ++++++++++++++++++++++++++++++---------- MoreLinq/WindowLeft.cs | 23 +------ MoreLinq/WindowRight.cs | 29 +-------- 3 files changed, 104 insertions(+), 82 deletions(-) diff --git a/MoreLinq/Window.cs b/MoreLinq/Window.cs index ed32f6b1d..b6cc6bdc7 100644 --- a/MoreLinq/Window.cs +++ b/MoreLinq/Window.cs @@ -15,6 +15,8 @@ // limitations under the License. #endregion +using System.ComponentModel; + namespace MoreLinq { using System; @@ -39,38 +41,7 @@ public static IEnumerable> Window(this IEnumerable> _() - { - using var iter = source.GetEnumerator(); - - // generate the first window of items - var window = new TSource[size]; - int i; - for (i = 0; i < size && iter.MoveNext(); i++) - window[i] = iter.Current; - - if (i < size) - yield break; - - for(;;) - { - if (!iter.MoveNext()) - { - // return the last window. - yield return window; - yield break; - } - - // generate the next window by shifting forward by one item - // and do that before exposing the data - var newWindow = new TSource[size]; - Array.Copy(window, 1, newWindow, 0, size - 1); - newWindow[size - 1] = iter.Current; - - yield return window; - window = newWindow; - } - } + return Window(source, size, false, false); } /// @@ -88,5 +59,104 @@ public static IEnumerable> Window(this IEnumerable> Windowed(this IEnumerable source, int size) => source.Window(size); + + private static IEnumerable> Window(IEnumerable source, int size, + bool hasPartialBegin, bool hasPartialEnd) + { + return _(); + + IEnumerable> _() + { + using var iter = source.GetEnumerator(); + + var hasNext = iter.MoveNext(); + + // early break + if (!hasNext) + yield break; + + // Store the window to be yield. + // In any cases we build the next window (if any) before yielding + // Loops do not have to yield the last window they created. + TSource[] window; + + // Warm-up + if (hasPartialBegin) + { + // build first partial window; + window = new[] {iter.Current}; + hasNext = iter.MoveNext(); + + // build other partial windows + while (window.Length < size && hasNext) + { + // Prepare next window, bigger than the previous one + var nextWindow = new TSource[window.Length + 1]; + Array.Copy(window, nextWindow, window.Length); + nextWindow[nextWindow.Length - 1] = iter.Current; + hasNext = iter.MoveNext(); + + // window ready to ship, we forget it immediately + yield return window; + window = nextWindow; + } + } + else + { + // build first window + window = new TSource[size]; + int i; + for (i = 0; i < size && hasNext; i++) + { + window[i] = iter.Current; + hasNext = iter.MoveNext(); + } + + // Ensure correct size on partial window cases + if (window.Length != i) + Array.Resize(ref window, i); + } + + // Main loop + if (window.Length == size) + { + // build windows of given size + while (hasNext) + { + // Prepare next window, same size as the previous one + var nextWindow = new TSource[size]; + Array.Copy(window, 1, nextWindow, 0, size - 1); + nextWindow[size - 1] = iter.Current; + hasNext = iter.MoveNext(); + + // window ready to ship, we forget it immediately + yield return window; + window = nextWindow; + } + } + + // Cool down + if (hasPartialEnd) + { + // build final partial windows + while (window.Length > 1) + { + // Prepare next window, smaller than the previous one + var nextWindow = new TSource[window.Length - 1]; + Array.Copy(window, 1, nextWindow, 0, nextWindow.Length); + + // window ready to ship, we forget it immediately + yield return window; + window = nextWindow; + } + } + + // No more windows to build, we can finally yield this one + if (hasPartialBegin || hasPartialEnd || window.Length == size) + { + yield return window; + } + } + } } } diff --git a/MoreLinq/WindowLeft.cs b/MoreLinq/WindowLeft.cs index fdcd72803..ce8b3ed65 100644 --- a/MoreLinq/WindowLeft.cs +++ b/MoreLinq/WindowLeft.cs @@ -63,28 +63,7 @@ public static IEnumerable> WindowLeft(this IEnumerable> _() - { - var window = new List(); - foreach (var item in source) - { - window.Add(item); - if (window.Count < size) - continue; - - // prepare next window before exposing data - var nextWindow = new List(window.Skip(1)); - yield return window; - window = nextWindow; - } - while (window.Count > 0) - { - // prepare next window before exposing data - var nextWindow = new List(window.Skip(1)); - yield return window; - window = nextWindow; - } - } + return Window(source, size, false, true); } } } diff --git a/MoreLinq/WindowRight.cs b/MoreLinq/WindowRight.cs index 4172c3d6d..fbde6da0b 100644 --- a/MoreLinq/WindowRight.cs +++ b/MoreLinq/WindowRight.cs @@ -63,34 +63,7 @@ public static IEnumerable> WindowRight(this IEnumerable< if (source == null) throw new ArgumentNullException(nameof(source)); if (size < 0) throw new ArgumentOutOfRangeException(nameof(size)); - return source.WindowRightWhile((_, i) => i < size); - } - - /// - /// Creates a right-aligned sliding window over the source sequence - /// with a predicate function determining the window range. - /// - - static IEnumerable> WindowRightWhile( - this IEnumerable source, - Func predicate) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); - - return _(); IEnumerable> _() - { - var window = new List(); - foreach (var item in source) - { - window.Add(item); - - // prepare next window before exposing data - var nextWindow = new List(predicate(item, window.Count) ? window : window.Skip(1)); - yield return window; - window = nextWindow; - } - } + return Window(source, size, true, false); } } } From f2864cebd70b0bc02791ba99e2fe8b1d7f57ba14 Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 6 Nov 2019 18:05:02 +0100 Subject: [PATCH 08/17] Removed useless internal method in Window implementation --- MoreLinq/Window.cs | 143 ++++++++++++++++++++++----------------------- 1 file changed, 69 insertions(+), 74 deletions(-) diff --git a/MoreLinq/Window.cs b/MoreLinq/Window.cs index b6cc6bdc7..52da90ac5 100644 --- a/MoreLinq/Window.cs +++ b/MoreLinq/Window.cs @@ -63,100 +63,95 @@ public static IEnumerable> Windowed(this IEnumerab private static IEnumerable> Window(IEnumerable source, int size, bool hasPartialBegin, bool hasPartialEnd) { - return _(); + using var iter = source.GetEnumerator(); - IEnumerable> _() - { - using var iter = source.GetEnumerator(); + var hasNext = iter.MoveNext(); - var hasNext = iter.MoveNext(); + // early break + if (!hasNext) + yield break; - // early break - if (!hasNext) - yield break; + // Store the window to be yield. + // In any cases we build the next window (if any) before yielding + // Loops do not have to yield the last window they created. + TSource[] window; - // Store the window to be yield. - // In any cases we build the next window (if any) before yielding - // Loops do not have to yield the last window they created. - TSource[] window; + // Warm-up + if (hasPartialBegin) + { + // build first partial window; + window = new[] {iter.Current}; + hasNext = iter.MoveNext(); - // Warm-up - if (hasPartialBegin) + // build other partial windows + while (window.Length < size && hasNext) { - // build first partial window; - window = new[] {iter.Current}; + // Prepare next window, bigger than the previous one + var nextWindow = new TSource[window.Length + 1]; + Array.Copy(window, nextWindow, window.Length); + nextWindow[nextWindow.Length - 1] = iter.Current; hasNext = iter.MoveNext(); - // build other partial windows - while (window.Length < size && hasNext) - { - // Prepare next window, bigger than the previous one - var nextWindow = new TSource[window.Length + 1]; - Array.Copy(window, nextWindow, window.Length); - nextWindow[nextWindow.Length - 1] = iter.Current; - hasNext = iter.MoveNext(); - - // window ready to ship, we forget it immediately - yield return window; - window = nextWindow; - } + // window ready to ship, we forget it immediately + yield return window; + window = nextWindow; } - else + } + else + { + // build first window + window = new TSource[size]; + int i; + for (i = 0; i < size && hasNext; i++) { - // build first window - window = new TSource[size]; - int i; - for (i = 0; i < size && hasNext; i++) - { - window[i] = iter.Current; - hasNext = iter.MoveNext(); - } - - // Ensure correct size on partial window cases - if (window.Length != i) - Array.Resize(ref window, i); + window[i] = iter.Current; + hasNext = iter.MoveNext(); } - // Main loop - if (window.Length == size) - { - // build windows of given size - while (hasNext) - { - // Prepare next window, same size as the previous one - var nextWindow = new TSource[size]; - Array.Copy(window, 1, nextWindow, 0, size - 1); - nextWindow[size - 1] = iter.Current; - hasNext = iter.MoveNext(); - - // window ready to ship, we forget it immediately - yield return window; - window = nextWindow; - } - } + // Ensure correct size on partial window cases + if (window.Length != i) + Array.Resize(ref window, i); + } - // Cool down - if (hasPartialEnd) + // Main loop + if (window.Length == size) + { + // build windows of given size + while (hasNext) { - // build final partial windows - while (window.Length > 1) - { - // Prepare next window, smaller than the previous one - var nextWindow = new TSource[window.Length - 1]; - Array.Copy(window, 1, nextWindow, 0, nextWindow.Length); - - // window ready to ship, we forget it immediately - yield return window; - window = nextWindow; - } + // Prepare next window, same size as the previous one + var nextWindow = new TSource[size]; + Array.Copy(window, 1, nextWindow, 0, size - 1); + nextWindow[size - 1] = iter.Current; + hasNext = iter.MoveNext(); + + // window ready to ship, we forget it immediately + yield return window; + window = nextWindow; } + } - // No more windows to build, we can finally yield this one - if (hasPartialBegin || hasPartialEnd || window.Length == size) + // Cool down + if (hasPartialEnd) + { + // build final partial windows + while (window.Length > 1) { + // Prepare next window, smaller than the previous one + var nextWindow = new TSource[window.Length - 1]; + Array.Copy(window, 1, nextWindow, 0, nextWindow.Length); + + // window ready to ship, we forget it immediately yield return window; + window = nextWindow; } } + + // No more windows to build, we can finally yield this one + if (hasPartialBegin || hasPartialEnd || window.Length == size) + { + yield return window; + } } } } From 7b4653a2d89c692037fd87628a01124ddf68fb5d Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 6 Nov 2019 18:17:19 +0100 Subject: [PATCH 09/17] Remove unused using directives. --- MoreLinq/Window.cs | 2 -- MoreLinq/WindowLeft.cs | 1 - MoreLinq/WindowRight.cs | 1 - 3 files changed, 4 deletions(-) diff --git a/MoreLinq/Window.cs b/MoreLinq/Window.cs index 52da90ac5..8b0cbad34 100644 --- a/MoreLinq/Window.cs +++ b/MoreLinq/Window.cs @@ -15,8 +15,6 @@ // limitations under the License. #endregion -using System.ComponentModel; - namespace MoreLinq { using System; diff --git a/MoreLinq/WindowLeft.cs b/MoreLinq/WindowLeft.cs index ce8b3ed65..589425cc9 100644 --- a/MoreLinq/WindowLeft.cs +++ b/MoreLinq/WindowLeft.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Linq; public static partial class MoreEnumerable { diff --git a/MoreLinq/WindowRight.cs b/MoreLinq/WindowRight.cs index fbde6da0b..8231a97d7 100644 --- a/MoreLinq/WindowRight.cs +++ b/MoreLinq/WindowRight.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Linq; public static partial class MoreEnumerable { From 6c0de36956e2163661efebc9f3ec69704f3b1e40 Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 6 Nov 2019 18:40:58 +0100 Subject: [PATCH 10/17] Added some test for Window(Left|Right): - ModifyWindowBeforeMoveNextDoNotAffectNextWindow - ModifyWindowAfterMoveNextDoNotAffectNextWindow - ModifyWindowBeforeMoveNextDoNotAffectPrevWindow --- MoreLinq.Test/WindowLeftTest.cs | 53 ++++++++++++++++++++++++-------- MoreLinq.Test/WindowRightTest.cs | 53 ++++++++++++++++++++++++-------- MoreLinq.Test/WindowTest.cs | 52 ++++++++++++++++++++++++------- 3 files changed, 123 insertions(+), 35 deletions(-) diff --git a/MoreLinq.Test/WindowLeftTest.cs b/MoreLinq.Test/WindowLeftTest.cs index c122fa881..bdc3909a7 100644 --- a/MoreLinq.Test/WindowLeftTest.cs +++ b/MoreLinq.Test/WindowLeftTest.cs @@ -12,20 +12,49 @@ public void WindowLeftIsLazy() new BreakingSequence().WindowLeft(1); } - /// - /// Verify that elements returned by Window are isolated. - /// Modification on one window should not be visible from the next window. - /// [Test] - public void TestWindowLeftReturnIsolatedList() + public void ModifyWindowBeforeMoveNextDoNotAffectNextWindow() { - var sequence = Enumerable.Repeat(0, 3); - foreach (var window in sequence.WindowLeft(2)) - { - if (window.Count > 1) - window[1] = 1; - Assert.That(window[0], Is.EqualTo(0)); - } + var sequence = Enumerable.Range(0, 3); + using var e = sequence.WindowLeft(2).GetEnumerator(); + + e.MoveNext(); + var window1 = e.Current; + window1[1] = -1; + e.MoveNext(); + var window2 = e.Current; + + Assert.That(window2[0], Is.EqualTo(1)); + } + + [Test] + public void ModifyWindowAfterMoveNextDoNotAffectNextWindow() + { + var sequence = Enumerable.Range(0, 3); + using var e = sequence.WindowLeft(2).GetEnumerator(); + + e.MoveNext(); + var window1 = e.Current; + e.MoveNext(); + window1[1] = -1; + var window2 = e.Current; + + Assert.That(window2[0], Is.EqualTo(1)); + } + + [Test] + public void ModifyWindowBeforeMoveNextDoNotAffectPrevWindow() + { + var sequence = Enumerable.Range(0, 3); + using var e = sequence.WindowLeft(2).GetEnumerator(); + + e.MoveNext(); + var window1 = e.Current; + e.MoveNext(); + var window2 = e.Current; + window2[0] = -1; + + Assert.That(window1[1], Is.EqualTo(1)); } [Test] diff --git a/MoreLinq.Test/WindowRightTest.cs b/MoreLinq.Test/WindowRightTest.cs index b8dd2260b..6e0aa734d 100644 --- a/MoreLinq.Test/WindowRightTest.cs +++ b/MoreLinq.Test/WindowRightTest.cs @@ -12,20 +12,49 @@ public void WindowRightIsLazy() new BreakingSequence().WindowRight(1); } - /// - /// Verify that elements returned by Window are isolated. - /// Modification on one window should not be visible from the next window. - /// [Test] - public void TestWindowRightReturnIsolatedList() + public void ModifyWindowBeforeMoveNextDoNotAffectNextWindow() { - var sequence = Enumerable.Repeat(0, 3); - foreach (var window in sequence.WindowRight(2)) - { - if (window.Count > 1) - window[1] = 1; - Assert.That(window[0], Is.EqualTo(0)); - } + var sequence = Enumerable.Range(0, 3); + using var e = sequence.WindowRight(2).GetEnumerator(); + + e.MoveNext(); + var window1 = e.Current; + window1[0] = -1; + e.MoveNext(); + var window2 = e.Current; + + Assert.That(window2[0], Is.EqualTo(0)); + } + + [Test] + public void ModifyWindowAfterMoveNextDoNotAffectNextWindow() + { + var sequence = Enumerable.Range(0, 3); + using var e = sequence.WindowRight(2).GetEnumerator(); + + e.MoveNext(); + var window1 = e.Current; + e.MoveNext(); + window1[0] = -1; + var window2 = e.Current; + + Assert.That(window2[0], Is.EqualTo(0)); + } + + [Test] + public void ModifyWindowBeforeMoveNextDoNotAffectPrevWindow() + { + var sequence = Enumerable.Range(0, 3); + using var e = sequence.WindowRight(2).GetEnumerator(); + + e.MoveNext(); + var window1 = e.Current; + e.MoveNext(); + var window2 = e.Current; + window2[0] = -1; + + Assert.That(window1[0], Is.EqualTo(0)); } [Test] diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index 3553ce95a..274f096ed 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -20,19 +20,49 @@ public void TestWindowIsLazy() new BreakingSequence().Window(1); } - /// - /// Verify that elements returned by Window are isolated. - /// Modification on one window should not be visible from the next window. - /// [Test] - public void TestWindowReturnIsolatedList() + public void ModifyWindowBeforeMoveNextDoNotAffectNextWindow() { - var sequence = Enumerable.Repeat(0, 3); - foreach (var window in sequence.Window(2)) - { - window[1] = 1; - Assert.That(window[0], Is.EqualTo(0)); - } + var sequence = Enumerable.Range(0, 3); + using var e = sequence.Window(2).GetEnumerator(); + + e.MoveNext(); + var window1 = e.Current; + window1[1] = -1; + e.MoveNext(); + var window2 = e.Current; + + Assert.That(window2[0], Is.EqualTo(1)); + } + + [Test] + public void ModifyWindowAfterMoveNextDoNotAffectNextWindow() + { + var sequence = Enumerable.Range(0, 3); + using var e = sequence.Window(2).GetEnumerator(); + + e.MoveNext(); + var window1 = e.Current; + e.MoveNext(); + window1[1] = -1; + var window2 = e.Current; + + Assert.That(window2[0], Is.EqualTo(1)); + } + + [Test] + public void ModifyWindowBeforeMoveNextDoNotAffectPrevWindow() + { + var sequence = Enumerable.Range(0, 3); + using var e = sequence.Window(2).GetEnumerator(); + + e.MoveNext(); + var window1 = e.Current; + e.MoveNext(); + var window2 = e.Current; + window2[0] = -1; + + Assert.That(window1[1], Is.EqualTo(1)); } /// From 66c29a38100cab5fc94ab4ace36904300b8c32a2 Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 6 Nov 2019 18:48:56 +0100 Subject: [PATCH 11/17] Rename ModifyWindowBeforeMoveNextDoNotAffectPrevWindow to ModifyWindowDoNotAffectPrevWindow --- MoreLinq.Test/WindowLeftTest.cs | 2 +- MoreLinq.Test/WindowRightTest.cs | 2 +- MoreLinq.Test/WindowTest.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MoreLinq.Test/WindowLeftTest.cs b/MoreLinq.Test/WindowLeftTest.cs index 9265cd31a..ae14934d6 100644 --- a/MoreLinq.Test/WindowLeftTest.cs +++ b/MoreLinq.Test/WindowLeftTest.cs @@ -45,7 +45,7 @@ public void ModifyWindowAfterMoveNextDoNotAffectNextWindow() } [Test] - public void ModifyWindowBeforeMoveNextDoNotAffectPrevWindow() + public void ModifyWindowDoNotAffectPrevWindow() { var sequence = Enumerable.Range(0, 3); using var e = sequence.WindowLeft(2).GetEnumerator(); diff --git a/MoreLinq.Test/WindowRightTest.cs b/MoreLinq.Test/WindowRightTest.cs index f92c5fbdb..87c995119 100644 --- a/MoreLinq.Test/WindowRightTest.cs +++ b/MoreLinq.Test/WindowRightTest.cs @@ -45,7 +45,7 @@ public void ModifyWindowAfterMoveNextDoNotAffectNextWindow() } [Test] - public void ModifyWindowBeforeMoveNextDoNotAffectPrevWindow() + public void ModifyWindowDoNotAffectPrevWindow() { var sequence = Enumerable.Range(0, 3); using var e = sequence.WindowRight(2).GetEnumerator(); diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index 274f096ed..83eecf9eb 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -51,7 +51,7 @@ public void ModifyWindowAfterMoveNextDoNotAffectNextWindow() } [Test] - public void ModifyWindowBeforeMoveNextDoNotAffectPrevWindow() + public void ModifyWindowDoNotAffectPrevWindow() { var sequence = Enumerable.Range(0, 3); using var e = sequence.Window(2).GetEnumerator(); From d221ceae0b6d7da98b0cab59e4dcb2bc8288138b Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 6 Nov 2019 18:48:56 +0100 Subject: [PATCH 12/17] Rename ModifyWindowBeforeMoveNextDoNotAffectPrevWindow to ModifyWindowDoNotAffectPrevWindow --- MoreLinq.Test/WindowLeftTest.cs | 2 +- MoreLinq.Test/WindowRightTest.cs | 2 +- MoreLinq.Test/WindowTest.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MoreLinq.Test/WindowLeftTest.cs b/MoreLinq.Test/WindowLeftTest.cs index 9265cd31a..ae14934d6 100644 --- a/MoreLinq.Test/WindowLeftTest.cs +++ b/MoreLinq.Test/WindowLeftTest.cs @@ -45,7 +45,7 @@ public void ModifyWindowAfterMoveNextDoNotAffectNextWindow() } [Test] - public void ModifyWindowBeforeMoveNextDoNotAffectPrevWindow() + public void ModifyWindowDoNotAffectPrevWindow() { var sequence = Enumerable.Range(0, 3); using var e = sequence.WindowLeft(2).GetEnumerator(); diff --git a/MoreLinq.Test/WindowRightTest.cs b/MoreLinq.Test/WindowRightTest.cs index f92c5fbdb..87c995119 100644 --- a/MoreLinq.Test/WindowRightTest.cs +++ b/MoreLinq.Test/WindowRightTest.cs @@ -45,7 +45,7 @@ public void ModifyWindowAfterMoveNextDoNotAffectNextWindow() } [Test] - public void ModifyWindowBeforeMoveNextDoNotAffectPrevWindow() + public void ModifyWindowDoNotAffectPrevWindow() { var sequence = Enumerable.Range(0, 3); using var e = sequence.WindowRight(2).GetEnumerator(); diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index 274f096ed..83eecf9eb 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -51,7 +51,7 @@ public void ModifyWindowAfterMoveNextDoNotAffectNextWindow() } [Test] - public void ModifyWindowBeforeMoveNextDoNotAffectPrevWindow() + public void ModifyWindowDoNotAffectPrevWindow() { var sequence = Enumerable.Range(0, 3); using var e = sequence.Window(2).GetEnumerator(); From 5598a8da2c0208a23eddfdfc7c257db68dd73400 Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 6 Nov 2019 19:08:58 +0100 Subject: [PATCH 13/17] Simplify Window implementation --- MoreLinq/Window.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/MoreLinq/Window.cs b/MoreLinq/Window.cs index ed32f6b1d..018f25b16 100644 --- a/MoreLinq/Window.cs +++ b/MoreLinq/Window.cs @@ -52,15 +52,8 @@ public static IEnumerable> Window(this IEnumerable> Window(this IEnumerable Date: Fri, 8 Nov 2019 19:39:11 +0100 Subject: [PATCH 14/17] Clean-up tests --- MoreLinq.Test/WindowLeftTest.cs | 38 ++++++++++++++----------------- MoreLinq.Test/WindowRightTest.cs | 38 ++++++++++++++----------------- MoreLinq.Test/WindowTest.cs | 39 +++++++++++++++----------------- 3 files changed, 52 insertions(+), 63 deletions(-) diff --git a/MoreLinq.Test/WindowLeftTest.cs b/MoreLinq.Test/WindowLeftTest.cs index ae14934d6..faa2da355 100644 --- a/MoreLinq.Test/WindowLeftTest.cs +++ b/MoreLinq.Test/WindowLeftTest.cs @@ -1,9 +1,25 @@ -using NUnit.Framework.Interfaces; +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. 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.Collections.Generic; using NUnit.Framework; + using NUnit.Framework.Interfaces; [TestFixture] public class WindowLeftTest @@ -123,25 +139,5 @@ public void WindowLeftWithWindowSizeSmallerThanSequence() reader.ReadEnd(); } } - - static IEnumerable Seq(params T[] values) => values; - - public static readonly IEnumerable TestData = - from e in new[] - { - new {Source = Enumerable.Range(0, 4), Size = 1, Result = new[] {Seq(0), Seq(1), Seq(2), Seq(3)}}, - new {Source = Enumerable.Range(0, 4), Size = 2, Result = new[] {Seq(0, 1), Seq(1, 2), Seq(2, 3), Seq(3)}}, - new {Source = Enumerable.Range(0, 4), Size = 3, Result = new[] {Seq(0, 1, 2), Seq(1, 2, 3), Seq(2, 3), Seq(3)}}, - new {Source = Enumerable.Range(0, 4), Size = 4, Result = new[] {Seq(0, 1, 2, 3), Seq(1, 2, 3), Seq(2, 3), Seq(3)}} - } - select new TestCaseData(e.Source, e.Size).Returns(e.Result); - - [Test, TestCaseSource(nameof(TestData))] - public IEnumerable> TestWindowLeftOnKnownResults(IEnumerable sequence, int sizes) - { - using var testingSequence = sequence.AsTestingSequence(); - var r = testingSequence.WindowLeft(sizes).ToList(); - return r; - } } } diff --git a/MoreLinq.Test/WindowRightTest.cs b/MoreLinq.Test/WindowRightTest.cs index 87c995119..2dfcfb1b9 100644 --- a/MoreLinq.Test/WindowRightTest.cs +++ b/MoreLinq.Test/WindowRightTest.cs @@ -1,9 +1,25 @@ -using NUnit.Framework.Interfaces; +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. 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.Collections.Generic; using NUnit.Framework; + using NUnit.Framework.Interfaces; [TestFixture] public class WindowRightTest @@ -123,25 +139,5 @@ public void WindowRightWithWindowSizeSmallerThanSequence() reader.ReadEnd(); } } - - static IEnumerable Seq(params T[] values) => values; - - public static readonly IEnumerable TestData = - from e in new[] - { - new {Source = Enumerable.Range(0, 4), Size = 1, Result = new[] {Seq(0), Seq(1), Seq(2), Seq(3)}}, - new {Source = Enumerable.Range(0, 4), Size = 2, Result = new[] {Seq(0), Seq(0, 1), Seq(1, 2), Seq(2, 3)}}, - new {Source = Enumerable.Range(0, 4), Size = 3, Result = new[] {Seq(0), Seq(0, 1), Seq(0, 1, 2), Seq(1, 2, 3)}}, - new {Source = Enumerable.Range(0, 4), Size = 4, Result = new[] {Seq(0), Seq(0, 1), Seq(0, 1, 2), Seq(0, 1, 2, 3)}} - } - select new TestCaseData(e.Source, e.Size).Returns(e.Result); - - [Test, TestCaseSource(nameof(TestData))] - public IEnumerable> TestWindowRightOnKnownResults(IEnumerable sequence, int sizes) - { - using var testingSequence = sequence.AsTestingSequence(); - var r = testingSequence.WindowRight(sizes).ToList(); - return r; - } } } diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index 83eecf9eb..71947291f 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -1,9 +1,25 @@ -using System.Collections.Generic; -using NUnit.Framework.Interfaces; +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. 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.Collections.Generic; using NUnit.Framework; + using NUnit.Framework.Interfaces; /// /// Verify the behavior of the Window operator @@ -162,24 +178,5 @@ public void TestWindowWindowsImmutability() reader.ReadEnd(); } } - - static IEnumerable Seq(params T[] values) => values; - - public static readonly IEnumerable TestData = - from e in new[] - { - new {Source = Enumerable.Range(0, 4), Size = 1, Result = new[] {Seq(0), Seq(1), Seq(2), Seq(3)}}, - new {Source = Enumerable.Range(0, 4), Size = 2, Result = new[] {Seq(0, 1), Seq(1, 2), Seq(2, 3)}}, - new {Source = Enumerable.Range(0, 4), Size = 3, Result = new[] {Seq(0, 1, 2), Seq(1, 2, 3)}}, - new {Source = Enumerable.Range(0, 4), Size = 4, Result = new[] {Seq(0, 1, 2, 3)}} - } - select new TestCaseData(e.Source, e.Size).Returns(e.Result); - - [Test, TestCaseSource(nameof(TestData))] - public IEnumerable> TestWindowOnKnownResults(IEnumerable sequence, int sizes) - { - using var testingSequence = sequence.AsTestingSequence(); - return testingSequence.Window(sizes).ToList(); - } } } From 31796593b74fab26503d47d41ccc85cae65c548d Mon Sep 17 00:00:00 2001 From: Orace Date: Fri, 8 Nov 2019 20:54:08 +0100 Subject: [PATCH 15/17] Niptick improvement in Window --- MoreLinq/Window.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/MoreLinq/Window.cs b/MoreLinq/Window.cs index 8b0cbad34..1f64b69d4 100644 --- a/MoreLinq/Window.cs +++ b/MoreLinq/Window.cs @@ -87,11 +87,12 @@ private static IEnumerable> Window(IEnumerable // Prepare next window, bigger than the previous one var nextWindow = new TSource[window.Length + 1]; Array.Copy(window, nextWindow, window.Length); - nextWindow[nextWindow.Length - 1] = iter.Current; - hasNext = iter.MoveNext(); // window ready to ship, we forget it immediately yield return window; + + nextWindow[nextWindow.Length - 1] = iter.Current; + hasNext = iter.MoveNext(); window = nextWindow; } } @@ -120,11 +121,12 @@ private static IEnumerable> Window(IEnumerable // Prepare next window, same size as the previous one var nextWindow = new TSource[size]; Array.Copy(window, 1, nextWindow, 0, size - 1); - nextWindow[size - 1] = iter.Current; - hasNext = iter.MoveNext(); // window ready to ship, we forget it immediately yield return window; + + nextWindow[size - 1] = iter.Current; + hasNext = iter.MoveNext(); window = nextWindow; } } From 0c9bd2b091600b6526ede4daaf13979ae25f9d09 Mon Sep 17 00:00:00 2001 From: Orace Date: Fri, 8 Nov 2019 23:13:24 +0100 Subject: [PATCH 16/17] Update MoreLinq/Window.cs Avoid an useless call to array.resize --- MoreLinq/Window.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/MoreLinq/Window.cs b/MoreLinq/Window.cs index 1f64b69d4..cc2f3e428 100644 --- a/MoreLinq/Window.cs +++ b/MoreLinq/Window.cs @@ -108,7 +108,13 @@ private static IEnumerable> Window(IEnumerable } // Ensure correct size on partial window cases - if (window.Length != i) + if (i != size) + { + if (hasPartialEnd) + Array.Resize(ref window, i); + else + yield break; + } Array.Resize(ref window, i); } From 1e468046db400659c6102ac9d635694554d52537 Mon Sep 17 00:00:00 2001 From: Orace Date: Fri, 8 Nov 2019 23:16:22 +0100 Subject: [PATCH 17/17] Update MoreLinq/Window.cs Removed useless line of code --- MoreLinq/Window.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MoreLinq/Window.cs b/MoreLinq/Window.cs index cc2f3e428..32fa9b7ae 100644 --- a/MoreLinq/Window.cs +++ b/MoreLinq/Window.cs @@ -115,7 +115,6 @@ private static IEnumerable> Window(IEnumerable else yield break; } - Array.Resize(ref window, i); } // Main loop