From 2095af5f9f5d3f5d12c84f43cdbd0da9c74fd55e Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 17 Jul 2019 15:46:11 +0200 Subject: [PATCH 01/58] Prepare for 3.3 --- MoreLinq/MoreLinq.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 0acd16531..bc309b0c7 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -112,7 +112,7 @@ $([System.Text.RegularExpressions.Regex]::Replace($(Copyright), `\s+`, ` `).Trim()) MoreLINQ en-US - 3.2.0 + 3.3.0 MoreLINQ Developers. net451;netstandard1.0;netstandard2.0 7.3 From 01a43b750ec8db9b1ddba8fc36617eea016f0b38 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 17 Jul 2019 15:56:44 +0200 Subject: [PATCH 02/58] Fix minor project file formatting inconsistencies --- MoreLinq/MoreLinq.csproj | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index bc309b0c7..8d5367670 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -149,7 +149,7 @@ - + TextTemplatingFileGenerator Aggregate.g.cs @@ -193,9 +193,7 @@ - - 4.4.0 - + From af548cf73a3a5e3f27b2f95861aa5c30fa9e25fc Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 17 Jul 2019 15:59:03 +0200 Subject: [PATCH 03/58] Update System.ValueTuple to 4.5.0 --- MoreLinq.Test/MoreLinq.Test.csproj | 2 +- MoreLinq/MoreLinq.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index be1b2e6b2..051534d8a 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -36,7 +36,7 @@ - + diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 8d5367670..b93e0eff3 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -193,7 +193,7 @@ - + From e1580c1bb032c518f963e14a58a71c8edc06b453 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 17 Jul 2019 16:33:31 +0200 Subject: [PATCH 04/58] Update all test project dependencies (#603) --- MoreLinq.Test/MoreLinq.Test.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 051534d8a..87d7883a7 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -28,17 +28,17 @@ - + runtime; build; native; contentfiles; analyzers all - - + + - - + + From 3d80847367a183cea3305f45a818b2c8891a8942 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 19 Jul 2019 15:46:45 +0200 Subject: [PATCH 05/58] Update to allow use of C# 8 (#604) --- .travis.yml | 7 +++---- MoreLinq.sln | 4 ++-- MoreLinq/MoreLinq.csproj | 2 +- global.json | 2 +- msbuild.cmd | 13 +++++-------- 5 files changed, 12 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 094d6c580..07d62e9e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,16 +5,16 @@ os: osx_image: xcode8.3 solution: MoreLinq.sln mono: 5.0.1 -dist: trusty +dist: xenial sudo: required -dotnet: 2.1.500 +dotnet: 2.2.203 env: - CONFIGURATION=Debug - CONFIGURATION=Release addons: apt: sources: - - sourceline: 'deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-trusty-prod trusty main' + - sourceline: 'deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main' key_url: 'https://packages.microsoft.com/keys/microsoft.asc' packages: - dotnet-hostfxr-1.0.1 @@ -22,7 +22,6 @@ addons: before_install: - dotnet --info - - msbuild /version - | if [ "$TRAVIS_OS_NAME" == "osx" ] || [ `uname` == "Darwin" ]; then # Handle too many files on OS X diff --git a/MoreLinq.sln b/MoreLinq.sln index d0e5a44bc..7d4944e58 100644 --- a/MoreLinq.sln +++ b/MoreLinq.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.0 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29102.190 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{835F8FFA-471F-4322-B721-A897F27872FA}" ProjectSection(SolutionItems) = preProject diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index b93e0eff3..c77bea0c7 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -115,7 +115,7 @@ 3.3.0 MoreLINQ Developers. net451;netstandard1.0;netstandard2.0 - 7.3 + 8 true portable true diff --git a/global.json b/global.json index c8f9e01ca..a9571de35 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "2.1.500" + "version": "2.2.200" } } diff --git a/msbuild.cmd b/msbuild.cmd index 3763315de..f24ead479 100644 --- a/msbuild.cmd +++ b/msbuild.cmd @@ -3,8 +3,8 @@ setlocal if "%PROCESSOR_ARCHITECTURE%"=="x86" set PROGRAMS=%ProgramFiles% if defined ProgramFiles(x86) set PROGRAMS=%ProgramFiles(x86)% for %%e in (Community Professional Enterprise) do ( - if exist "%PROGRAMS%\Microsoft Visual Studio\2017\%%e\MSBuild\15.0\Bin\MSBuild.exe" ( - set "MSBUILD=%PROGRAMS%\Microsoft Visual Studio\2017\%%e\MSBuild\15.0\Bin\MSBuild.exe" + if exist "%PROGRAMS%\Microsoft Visual Studio\2019\%%e\MSBuild\Current\Bin\MSBuild.exe" ( + set "MSBUILD=%PROGRAMS%\Microsoft Visual Studio\2019\%%e\MSBuild\Current\Bin\MSBuild.exe" ) ) if exist "%MSBUILD%" goto :build @@ -15,19 +15,16 @@ set MSBUILD_VERSION_MAJOR= set MSBUILD_VERSION_MINOR= for /f "delims=. tokens=1,2,3,4" %%m in ('msbuild /version /nologo') do ( set MSBUILD_VERSION_MAJOR=%%m - set MSBUILD_VERSION_MINOR=%%n ) if not defined MSBUILD_VERSION_MAJOR goto :nomsbuild -if not defined MSBUILD_VERSION_MINOR goto :nomsbuild -if %MSBUILD_VERSION_MAJOR% lss 15 goto :nomsbuild -if %MSBUILD_VERSION_MINOR% lss 1 goto :nomsbuild +if %MSBUILD_VERSION_MAJOR% lss 16 goto :nomsbuild :build "%MSBUILD%" %* goto :EOF :nomsbuild -echo>&2 Microsoft Build Engine 15.1 is required to build the solution. For -echo>&2 installation instructions, see: +echo>&2 Microsoft Build Engine 16.0 or a later version is required to build +echo>&2 the solution. For installation instructions, see: echo>&2 https://docs.microsoft.com/en-us/visualstudio/install/use-command-line-parameters-to-install-visual-studio echo>&2 At the very least, you will want to install the MSBuilt Tool workload echo>&2 that has the identifier "Microsoft.VisualStudio.Workload.MSBuildTools": From 00c1f27b23eb1cc4d78d7d3076961a69277d50f8 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 19 Jul 2019 16:55:45 +0200 Subject: [PATCH 06/58] Address readability to-do in LeftJoin --- MoreLinq/LeftJoin.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/MoreLinq/LeftJoin.cs b/MoreLinq/LeftJoin.cs index 648071726..d4af068fb 100644 --- a/MoreLinq/LeftJoin.cs +++ b/MoreLinq/LeftJoin.cs @@ -207,14 +207,12 @@ public static IEnumerable LeftJoin( if (firstSelector == null) throw new ArgumentNullException(nameof(firstSelector)); if (bothSelector == null) throw new ArgumentNullException(nameof(bothSelector)); - KeyValuePair Pair(TK k, TV v) => new KeyValuePair(k, v); - - return // TODO replace KeyValuePair<,> with (,) for clarity - from j in first.GroupJoin(second, firstKeySelector, secondKeySelector, - (f, ss) => Pair(f, from s in ss select Pair(true, s)), + return + from f in first.GroupJoin(second, firstKeySelector, secondKeySelector, + (f, ss) => (Value: f, Seconds: from s in ss select (HasValue: true, Value: s)), comparer) - from s in j.Value.DefaultIfEmpty() - select s.Key ? bothSelector(j.Key, s.Value) : firstSelector(j.Key); + from s in f.Seconds.DefaultIfEmpty() + select s.HasValue ? bothSelector(f.Value, s.Value) : firstSelector(f.Value); } } } From 9ef007c6913e41bf674cb9bece3be99c1df0af0a Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 19 Jul 2019 16:56:33 +0200 Subject: [PATCH 07/58] Use static local function where applicable --- MoreLinq/ToDataTable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/ToDataTable.cs b/MoreLinq/ToDataTable.cs index fa2719739..b9809f5a3 100644 --- a/MoreLinq/ToDataTable.cs +++ b/MoreLinq/ToDataTable.cs @@ -156,7 +156,7 @@ static IEnumerable PrepareMemberInfos(ICollection Date: Sat, 20 Jul 2019 19:15:20 +0200 Subject: [PATCH 08/58] Revert to C# 7.3 (#606) --- MoreLinq/MoreLinq.csproj | 2 +- MoreLinq/ToDataTable.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index c77bea0c7..b93e0eff3 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -115,7 +115,7 @@ 3.3.0 MoreLINQ Developers. net451;netstandard1.0;netstandard2.0 - 8 + 7.3 true portable true diff --git a/MoreLinq/ToDataTable.cs b/MoreLinq/ToDataTable.cs index b9809f5a3..fa2719739 100644 --- a/MoreLinq/ToDataTable.cs +++ b/MoreLinq/ToDataTable.cs @@ -156,7 +156,7 @@ static IEnumerable PrepareMemberInfos(ICollection Date: Mon, 22 Jul 2019 05:20:26 -0300 Subject: [PATCH 09/58] Fix Move that returned empty on subsequent iterations This is squashed merge of PR #607 that fixes #605. --- MoreLinq.Test/MoveTest.cs | 9 +++++++++ MoreLinq/Move.cs | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/MoreLinq.Test/MoveTest.cs b/MoreLinq.Test/MoveTest.cs index acb1454b9..150f958ec 100644 --- a/MoreLinq.Test/MoveTest.cs +++ b/MoreLinq.Test/MoveTest.cs @@ -102,6 +102,15 @@ public static IEnumerable MoveWithSequenceShorterThanToIndexSource() .Select(toIndex => new TestCaseData(length, 5, 2, toIndex)); } + [Test] + public void MoveIsRepeatable() + { + var source = Enumerable.Range(0, 10); + var result = source.Move(0, 5, 10); + + Assert.That(result.ToArray(), Is.EqualTo(result)); + } + [Test] public void MoveWithFromIndexEqualsToIndex() { diff --git a/MoreLinq/Move.cs b/MoreLinq/Move.cs index 88a6d9943..f95f022d4 100644 --- a/MoreLinq/Move.cs +++ b/MoreLinq/Move.cs @@ -57,15 +57,15 @@ public static IEnumerable Move(this IEnumerable source, int fromIndex, if (toIndex == fromIndex || count == 0) return source; - bool hasMore = true; - bool MoveNext(IEnumerator e) => hasMore && (hasMore = e.MoveNext()); - return toIndex < fromIndex ? _(toIndex, fromIndex - toIndex, count) : _(fromIndex, count, toIndex - fromIndex); IEnumerable _(int bufferStartIndex, int bufferSize, int bufferYieldIndex) { + bool hasMore = true; + bool MoveNext(IEnumerator e) => hasMore && (hasMore = e.MoveNext()); + using (var e = source.GetEnumerator()) { for (var i = 0; i < bufferStartIndex && MoveNext(e); i++) From b2c7ad35d13815a5dbc6a5ed26996b99b936ee30 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Fri, 23 Aug 2019 02:06:41 -0400 Subject: [PATCH 10/58] Fix Min/MaxBy documented exception list (#608) Remove InvalidOperationException from the documented list of exceptions thrown by MinBy and MaxBy; they don't throw it since they were changed in fb08d80739ff9ea710034b27a7906a829a45b527 (#328) to return all matching elements. --- MoreLinq/Extensions.g.cs | 4 ---- MoreLinq/MaxBy.cs | 2 -- MoreLinq/MinBy.cs | 2 -- 3 files changed, 8 deletions(-) diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 9b3f26915..bc072f509 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -3288,7 +3288,6 @@ public static partial class MaxByExtension /// Selector to use to pick the results to compare /// The maximal element, according to the projection. /// or is null - /// is empty public static IExtremaEnumerable MaxBy(this IEnumerable source, Func selector) @@ -3310,7 +3309,6 @@ public static IExtremaEnumerable MaxBy(this IEnumerable< /// The maximal element, according to the projection. /// , /// or is null - /// is empty public static IExtremaEnumerable MaxBy(this IEnumerable source, Func selector, IComparer comparer) @@ -3338,7 +3336,6 @@ public static partial class MinByExtension /// Selector to use to pick the results to compare /// The minimal element, according to the projection. /// or is null - /// is empty public static IExtremaEnumerable MinBy(this IEnumerable source, Func selector) @@ -3360,7 +3357,6 @@ public static IExtremaEnumerable MinBy(this IEnumerable< /// The minimal element, according to the projection. /// , /// or is null - /// is empty public static IExtremaEnumerable MinBy(this IEnumerable source, Func selector, IComparer comparer) diff --git a/MoreLinq/MaxBy.cs b/MoreLinq/MaxBy.cs index 24b00f669..588446444 100644 --- a/MoreLinq/MaxBy.cs +++ b/MoreLinq/MaxBy.cs @@ -182,7 +182,6 @@ public static T SingleOrDefault(this IExtremaEnumerable source) /// Selector to use to pick the results to compare /// The maximal element, according to the projection. /// or is null - /// is empty public static IExtremaEnumerable MaxBy(this IEnumerable source, Func selector) @@ -206,7 +205,6 @@ public static IExtremaEnumerable MaxBy(this IEnumerable< /// The maximal element, according to the projection. /// , /// or is null - /// is empty public static IExtremaEnumerable MaxBy(this IEnumerable source, Func selector, IComparer comparer) diff --git a/MoreLinq/MinBy.cs b/MoreLinq/MinBy.cs index 38a30937c..8dc488161 100644 --- a/MoreLinq/MinBy.cs +++ b/MoreLinq/MinBy.cs @@ -37,7 +37,6 @@ static partial class MoreEnumerable /// Selector to use to pick the results to compare /// The minimal element, according to the projection. /// or is null - /// is empty public static IExtremaEnumerable MinBy(this IEnumerable source, Func selector) @@ -61,7 +60,6 @@ public static IExtremaEnumerable MinBy(this IEnumerable< /// The minimal element, according to the projection. /// , /// or is null - /// is empty public static IExtremaEnumerable MinBy(this IEnumerable source, Func selector, IComparer comparer) From bb10cf50df32d267c7dd330d40b4f2f8724d6d69 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 24 Oct 2019 10:44:30 +0100 Subject: [PATCH 11/58] Upgrade to .NET Core SDK 3 (#618) --- .travis.yml | 15 ++++++++++++--- MoreLinq.Test/MoreLinq.Test.csproj | 2 +- appveyor.yml | 2 +- .../MoreLinq.ExtensionsGenerator.csproj | 2 +- global.json | 8 ++++---- test.cmd | 2 ++ test.sh | 2 +- 7 files changed, 22 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 07d62e9e9..e8c0b6b43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,12 @@ language: csharp os: - linux - osx -osx_image: xcode8.3 +osx_image: xcode9.4 solution: MoreLinq.sln mono: 5.0.1 dist: xenial sudo: required -dotnet: 2.2.203 +dotnet: 3.0.100 env: - CONFIGURATION=Debug - CONFIGURATION=Release @@ -19,9 +19,10 @@ addons: packages: - dotnet-hostfxr-1.0.1 - dotnet-sharedframework-microsoft.netcore.app-1.0.5 + - dotnet-runtime-2.0.9 + - dotnet-runtime-2.1 before_install: - - dotnet --info - | if [ "$TRAVIS_OS_NAME" == "osx" ] || [ `uname` == "Darwin" ]; then # Handle too many files on OS X @@ -29,7 +30,14 @@ before_install: # Install dotnet core 1 sdk wget --retry-connrefused --waitretry=1 -O /tmp/dn1.pkg 'https://download.microsoft.com/download/B/9/F/B9F1AF57-C14A-4670-9973-CDF47209B5BF/dotnet-dev-osx-x64.1.0.4.pkg' sudo installer -pkg /tmp/dn1.pkg -target / + # Install dotnet core 2.0 runtime + wget --retry-connrefused --waitretry=1 -O /tmp/dn2.pkg 'https://download.microsoft.com/download/3/a/3/3a3bda26-560d-4d8e-922e-6f6bc4553a84/dotnet-runtime-2.0.9-osx-x64.pkg' + sudo installer -pkg /tmp/dn2.pkg -target / + # Install dotnet core 2.1 runtime + wget --retry-connrefused --waitretry=1 -O /tmp/dn21.pkg 'https://download.visualstudio.microsoft.com/download/pr/9314da31-774c-4d2b-8743-998f2a21f5ab/bc918ca05ab6b650f2991b205c04f623/dotnet-runtime-2.1.13-osx-x64.pkg' + sudo installer -pkg /tmp/dn21.pkg -target / fi + - dotnet --info install: - dotnet restore @@ -51,4 +59,5 @@ script: - dotnet exec MoreLinq.Test/bin/$CONFIGURATION/netcoreapp1.0/MoreLinq.Test.dll - dotnet exec MoreLinq.Test/bin/$CONFIGURATION/netcoreapp2.0/MoreLinq.Test.dll - dotnet exec MoreLinq.Test/bin/$CONFIGURATION/netcoreapp2.1/MoreLinq.Test.dll + - dotnet exec MoreLinq.Test/bin/$CONFIGURATION/netcoreapp3.0/MoreLinq.Test.dll - mono MoreLinq.Test/bin/$CONFIGURATION/net451/MoreLinq.Test.exe diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 87d7883a7..164653621 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -2,7 +2,7 @@ MoreLinq.Test - netcoreapp1.0;netcoreapp2.0;netcoreapp2.1;net451 + netcoreapp1.0;netcoreapp2.0;netcoreapp2.1;netcoreapp3.0;net451 true portable MoreLinq.Test diff --git a/appveyor.yml b/appveyor.yml index c5f2e5d53..4c3ea3d1c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ version: '{build}' -image: Visual Studio 2017 +image: Visual Studio 2019 skip_commits: files: - '*.md' diff --git a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj index 6f5aad70f..c4546c814 100644 --- a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj +++ b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj @@ -1,7 +1,7 @@  Exe - netcoreapp2.1 + netcoreapp3.0 false diff --git a/global.json b/global.json index a9571de35..2223a05e3 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { - "sdk": { - "version": "2.2.200" - } -} + "sdk": { + "version": "3.0.100" + } +} \ No newline at end of file diff --git a/test.cmd b/test.cmd index 5d1b41be4..ab4250205 100644 --- a/test.cmd +++ b/test.cmd @@ -13,6 +13,8 @@ call build ^ && call :test netcoreapp2.0 Release ^ && call :test netcoreapp2.1 Debug ^ && call :test netcoreapp2.1 Release ^ + && call :test netcoreapp3.0 Debug ^ + && call :test netcoreapp3.0 Release ^ && call :test net451 Debug ^ && call :test net451 Release goto :EOF diff --git a/test.sh b/test.sh index 5844b6ffa..33048cf14 100755 --- a/test.sh +++ b/test.sh @@ -2,7 +2,7 @@ set -e cd "$(dirname "$0")" ./build.sh -for v in 1.0 2.0 2.1; do +for v in 1.0 2.0 2.1 3.0; do for c in Debug Release; do dotnet exec MoreLinq.Test/bin/$c/netcoreapp$v/MoreLinq.Test.dll done From 6af58704b4633063d52d60cf10025a15d49ba4bd Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 24 Oct 2019 13:20:23 +0100 Subject: [PATCH 12/58] Fix subscription disposal race in Subject This is squashed merge of PR #617 that fixes #616. --- MoreLinq.Test/AggregateTest.cs | 13 ++++++++ MoreLinq.Test/SubjectTest.cs | 12 +++++++ MoreLinq/Reactive/Subject.cs | 58 ++++++++++++++++++++++++++++++++-- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/MoreLinq.Test/AggregateTest.cs b/MoreLinq.Test/AggregateTest.cs index 1a801ab7a..55b531658 100644 --- a/MoreLinq.Test/AggregateTest.cs +++ b/MoreLinq.Test/AggregateTest.cs @@ -198,5 +198,18 @@ public void SevenUniqueAccumulatorComprehensions() new { Num = 9, Str = "9" }, new { Num = 10, Str = "10" }); } + + [Test(Description = "https://github.com/morelinq/MoreLINQ/issues/616")] + public void Issue616() + { + var (first, last) = + Enumerable.Range(1, 10) + .Aggregate(ds => ds.FirstAsync(), + ds => ds.LastAsync(), + ValueTuple.Create); + + Assert.That(first, Is.EqualTo(1)); + Assert.That(last, Is.EqualTo(10)); + } } } diff --git a/MoreLinq.Test/SubjectTest.cs b/MoreLinq.Test/SubjectTest.cs index b045d2224..cd571589d 100644 --- a/MoreLinq.Test/SubjectTest.cs +++ b/MoreLinq.Test/SubjectTest.cs @@ -210,5 +210,17 @@ public void ErrorsOnce() Assert.That(count, Is.EqualTo(1)); } + + [Test] + public void SafeToDisposeDuringOnNext() + { + var subject = new Subject(); + IDisposable subscription = null; + var action = new Action(() => subscription.Dispose()); + subscription = subject.Subscribe(_ => action()); + subject.OnNext(42); + action = BreakingAction.WithoutArguments; + subject.OnNext(42); + } } } diff --git a/MoreLinq/Reactive/Subject.cs b/MoreLinq/Reactive/Subject.cs index 066a8777c..d47aedaed 100644 --- a/MoreLinq/Reactive/Subject.cs +++ b/MoreLinq/Reactive/Subject.cs @@ -49,16 +49,68 @@ public IDisposable Subscribe(IObserver observer) } Observers.Add(observer); - return Delegate.Disposable(() => Observers.Remove(observer)); + + return Delegate.Disposable(() => + { + var observers = Observers; + + // Could do the following to find the index of the + // the observer: + // + // var i = observers.FindIndex(o => o == observer); + // + // but it would require a closure allocation. + + for (var i = 0; i < observers.Count; i++) + { + if (observers[i] == observer) + { + if (_shouldDeleteObserver) + observers[i] = null; + else + observers.RemoveAt(i); + break; + } + } + }); } + bool _shouldDeleteObserver; // delete (null) or remove an observer? + public void OnNext(T value) { if (!HasObservers) return; - foreach (var observer in Observers) - observer.OnNext(value); + var observers = Observers; + + // Set a flag around iteration to indicate that an observer that + // disposes their subscription should be marked for deletion + // instead of being removed from the list of observers. The actual + // removal is then deferred until after the iteration is complete. + + _shouldDeleteObserver = true; + + try + { + // DO NOT change the following loop into the for-each variant + // because an observer might dispose its subscription during + // the call to "OnNext" and List's enumerator will throw + // seeing that as a modification of the collection during + // enumeration. + + for (var i = 0; i < observers.Count; i++) + observers[i].OnNext(value); + } + finally + { + _shouldDeleteObserver = false; + + // Remove any observers that were marked for deletion during + // iteration. + + observers.RemoveAll(o => o == null); + } } public void OnError(Exception error) => From 7a58b00de4277b0607b9078db514fff64833c2f8 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 24 Oct 2019 16:14:32 +0200 Subject: [PATCH 13/58] Fix pack (bash) script; was still using MSBuild --- pack.cmd | 4 ++-- pack.sh | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pack.cmd b/pack.cmd index 623cdbb64..5b0523374 100644 --- a/pack.cmd +++ b/pack.cmd @@ -11,7 +11,7 @@ if not %errorlevel%==0 exit /b %errorlevel% set VERSION_SUFFIX= if not "%~1"=="" set VERSION_SUFFIX=/p:VersionSuffix=%1 call build ^ - && dotnet pack /p:Configuration=Release ^ - %VERSION_SUFFIX% ^ + && dotnet pack -c Release ^ + %VERSION_SUFFIX% ^ MoreLinq\MoreLinq.csproj goto :EOF diff --git a/pack.sh b/pack.sh index ae156a56a..615a36142 100755 --- a/pack.sh +++ b/pack.sh @@ -5,7 +5,6 @@ VERSION_SUFFIX= if [ ! -z "$1" ]; then VERSION_SUFFIX="--version-suffix $1"; fi ./build.sh if [ ! -d dist ]; then mkdir dist; fi -./msbuild.sh /v:m /t:Pack \ - /p:Configuration=Release \ - $VERSION_SUFFIX \ - MoreLinq/MoreLinq.csproj +dotnet pack -c Release \ + $VERSION_SUFFIX \ + MoreLinq/MoreLinq.csproj From 423865b0d9f11bc8f420ca75bec5c85f641952d9 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 24 Oct 2019 16:17:53 +0200 Subject: [PATCH 14/58] Skip (potentially duplicate) building when packing --- pack.cmd | 3 ++- pack.sh | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pack.cmd b/pack.cmd index 5b0523374..8f90f4521 100644 --- a/pack.cmd +++ b/pack.cmd @@ -11,7 +11,8 @@ if not %errorlevel%==0 exit /b %errorlevel% set VERSION_SUFFIX= if not "%~1"=="" set VERSION_SUFFIX=/p:VersionSuffix=%1 call build ^ - && dotnet pack -c Release ^ + && dotnet pack --no-build ^ + -c Release ^ %VERSION_SUFFIX% ^ MoreLinq\MoreLinq.csproj goto :EOF diff --git a/pack.sh b/pack.sh index 615a36142..af8db9bea 100755 --- a/pack.sh +++ b/pack.sh @@ -5,6 +5,7 @@ VERSION_SUFFIX= if [ ! -z "$1" ]; then VERSION_SUFFIX="--version-suffix $1"; fi ./build.sh if [ ! -d dist ]; then mkdir dist; fi -dotnet pack -c Release \ +dotnet pack --no-build \ + -c Release \ $VERSION_SUFFIX \ MoreLinq/MoreLinq.csproj From 478218ace49794dc018dfe03d92fafae8ea77968 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 24 Oct 2019 16:22:08 +0200 Subject: [PATCH 15/58] Packaging scripts don't need project name [ci skip] --- pack.cmd | 5 +---- pack.sh | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/pack.cmd b/pack.cmd index 8f90f4521..6aacf62fe 100644 --- a/pack.cmd +++ b/pack.cmd @@ -11,8 +11,5 @@ if not %errorlevel%==0 exit /b %errorlevel% set VERSION_SUFFIX= if not "%~1"=="" set VERSION_SUFFIX=/p:VersionSuffix=%1 call build ^ - && dotnet pack --no-build ^ - -c Release ^ - %VERSION_SUFFIX% ^ - MoreLinq\MoreLinq.csproj + && dotnet pack --no-build -c Release %VERSION_SUFFIX% goto :EOF diff --git a/pack.sh b/pack.sh index af8db9bea..94688e1bf 100755 --- a/pack.sh +++ b/pack.sh @@ -5,7 +5,4 @@ VERSION_SUFFIX= if [ ! -z "$1" ]; then VERSION_SUFFIX="--version-suffix $1"; fi ./build.sh if [ ! -d dist ]; then mkdir dist; fi -dotnet pack --no-build \ - -c Release \ - $VERSION_SUFFIX \ - MoreLinq/MoreLinq.csproj +dotnet pack --no-build -c Release $VERSION_SUFFIX From 6447d757225d705261319355ed9d9146d024d73e Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 25 Oct 2019 08:59:31 +0100 Subject: [PATCH 16/58] Remove redundant restore during CI [ci skip] --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e8c0b6b43..eeffc14ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,6 @@ before_install: - dotnet --info install: - - dotnet restore - npm install -g eclint before_script: From ce96355ae5ca0db6901e91fd722191ec2f4ff21b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 24 Oct 2019 16:32:27 +0200 Subject: [PATCH 17/58] Update dependencies of wrapper extensions generator --- bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj index c4546c814..6b76e17e1 100644 --- a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj +++ b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj @@ -5,7 +5,7 @@ false - - + + From 70b02d00864c1128e7a81193445433de10c63589 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 25 Oct 2019 09:32:03 +0200 Subject: [PATCH 18/58] Use T4 as local tool; closes #601 --- .config/dotnet-tools.json | 12 ++++++++++++ MoreLinq/MoreLinq.csproj | 1 - MoreLinq/ToDelimitedString.g.tt | 3 ++- build.cmd | 2 +- build.sh | 1 + 5 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 .config/dotnet-tools.json diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 000000000..dab16c723 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-t4": { + "version": "2.0.5", + "commands": [ + "t4" + ] + } + } +} \ No newline at end of file diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index b93e0eff3..5e9b4836e 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -238,7 +238,6 @@ - diff --git a/MoreLinq/ToDelimitedString.g.tt b/MoreLinq/ToDelimitedString.g.tt index 2ed981368..8f2c6a2d7 100644 --- a/MoreLinq/ToDelimitedString.g.tt +++ b/MoreLinq/ToDelimitedString.g.tt @@ -50,7 +50,8 @@ namespace MoreLinq select method.GetParameters() into parameters where parameters.Length == 1 select parameters.First().ParameterType into type - where type.IsValueType || type == typeof(string) + where !type.IsGenericType // e.g. ReadOnlySpan<> + && (type.IsValueType || type == typeof(string)) let res = new { Type = type, diff --git a/build.cmd b/build.cmd index 36522cb46..08e0f43bf 100644 --- a/build.cmd +++ b/build.cmd @@ -10,7 +10,7 @@ for %%i in (dotnet.exe) do set dotnet=%%~dpnx$PATH:i if "%dotnet%"=="" goto :nodotnet if "%1"=="docs" shift & goto :docs :build -dotnet restore ^ +dotnet restore && dotnet tool restore ^ && call :codegen MoreLinq\Extensions.g.cs -x "[/\\]ToDataTable\.cs$" -u System.Linq -u System.Collections MoreLinq ^ && call :codegen MoreLinq\Extensions.ToDataTable.g.cs -i "[/\\]ToDataTable\.cs$" -u System.Data -u System.Linq.Expressions MoreLinq ^ && for %%i in (debug release) do dotnet build -c %%i --no-restore %* || exit /b 1 diff --git a/build.sh b/build.sh index a1dadbd31..ea2bdda85 100755 --- a/build.sh +++ b/build.sh @@ -2,6 +2,7 @@ set -e cd "$(dirname "$0")" dotnet restore +dotnet tool restore codegen() { dest="$1" printf "Generating extensions wrappers (%s)..." "$1" From 682a90f0c15148a171b4561557e6b96e6b00b491 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 25 Oct 2019 10:17:19 +0200 Subject: [PATCH 19/58] Integrate T4 execution in build scripts --- build.cmd | 1 + build.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/build.cmd b/build.cmd index 08e0f43bf..cedd31964 100644 --- a/build.cmd +++ b/build.cmd @@ -13,6 +13,7 @@ if "%1"=="docs" shift & goto :docs dotnet restore && dotnet tool restore ^ && call :codegen MoreLinq\Extensions.g.cs -x "[/\\]ToDataTable\.cs$" -u System.Linq -u System.Collections MoreLinq ^ && call :codegen MoreLinq\Extensions.ToDataTable.g.cs -i "[/\\]ToDataTable\.cs$" -u System.Data -u System.Linq.Expressions MoreLinq ^ + && call MoreLinq\tt ^ && for %%i in (debug release) do dotnet build -c %%i --no-restore %* || exit /b 1 goto :EOF diff --git a/build.sh b/build.sh index ea2bdda85..c0ef870bd 100755 --- a/build.sh +++ b/build.sh @@ -12,6 +12,7 @@ codegen() { } codegen MoreLinq/Extensions.g.cs -x "[/\\\\]ToDataTable\.cs$" -u System.Linq -u System.Collections MoreLinq codegen MoreLinq/Extensions.ToDataTable.g.cs -i "[/\\\\]ToDataTable\.cs$" -u System.Data -u System.Linq.Expressions MoreLinq +MoreLinq/tt.sh for c in Debug Release; do dotnet build --no-restore -c $c done From 7d1fc72959de920d872ce3b867eceef4fbd3ea43 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 25 Oct 2019 10:26:26 +0200 Subject: [PATCH 20/58] Build just the configuration for *nix/macOS CI --- .travis.yml | 2 +- build.sh | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index eeffc14ad..5c58dc95b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,7 @@ script: echo "System.Linq import found, failing the build!" >&2 exit 1 fi - - ./build.sh /v:m /p:Configuration=$CONFIGURATION + - ./build.sh $CONFIGURATION - dotnet exec MoreLinq.Test/bin/$CONFIGURATION/netcoreapp1.0/MoreLinq.Test.dll - dotnet exec MoreLinq.Test/bin/$CONFIGURATION/netcoreapp2.0/MoreLinq.Test.dll - dotnet exec MoreLinq.Test/bin/$CONFIGURATION/netcoreapp2.1/MoreLinq.Test.dll diff --git a/build.sh b/build.sh index c0ef870bd..516932a61 100755 --- a/build.sh +++ b/build.sh @@ -13,6 +13,11 @@ codegen() { codegen MoreLinq/Extensions.g.cs -x "[/\\\\]ToDataTable\.cs$" -u System.Linq -u System.Collections MoreLinq codegen MoreLinq/Extensions.ToDataTable.g.cs -i "[/\\\\]ToDataTable\.cs$" -u System.Data -u System.Linq.Expressions MoreLinq MoreLinq/tt.sh -for c in Debug Release; do - dotnet build --no-restore -c $c +if [[ -z "$1" ]]; then + configs="Debug Release" +else + configs="$1" +fi +for c in $configs; do + echo dotnet build --no-restore -c $c done From 7de87bd5e0b46d2b9e4ffffb59440e1cc82baaf1 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 25 Oct 2019 10:31:15 +0200 Subject: [PATCH 21/58] Upgrade wrapper extensions generator to use C# 8 --- .../MoreLinq.ExtensionsGenerator.csproj | 1 + bld/ExtensionsGenerator/Program.cs | 49 ++++++++++--------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj index 6b76e17e1..b43ac7fd7 100644 --- a/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj +++ b/bld/ExtensionsGenerator/MoreLinq.ExtensionsGenerator.csproj @@ -2,6 +2,7 @@ Exe netcoreapp3.0 + 8 false diff --git a/bld/ExtensionsGenerator/Program.cs b/bld/ExtensionsGenerator/Program.cs index 5a5f6012c..6c7820e9b 100644 --- a/bld/ExtensionsGenerator/Program.cs +++ b/bld/ExtensionsGenerator/Program.cs @@ -41,9 +41,6 @@ static void Run(IEnumerable args) var usings = new List(); var noClassLead = false; - Exception MissingArgValue() => - new InvalidOperationException("Missing argument value."); - using (var arg = args.GetEnumerator()) { while (arg.MoveNext()) @@ -78,12 +75,16 @@ Exception MissingArgValue() => break; } } + + static Exception MissingArgValue() => + new InvalidOperationException("Missing argument value."); } - Func PredicateFromPattern(string pattern, bool @default) => - string.IsNullOrEmpty(pattern) - ? delegate { return @default; } - : new Func(new Regex(pattern).IsMatch); + static Func + PredicateFromPattern(string pattern, bool @default) => + string.IsNullOrEmpty(pattern) + ? delegate { return @default; } + : new Func(new Regex(pattern).IsMatch); var includePredicate = PredicateFromPattern(includePattern, true); var excludePredicate = PredicateFromPattern(excludePattern, false); @@ -320,22 +321,24 @@ public static TypeKey CreateTypeKey(TypeSyntax root, { return Walk(root ?? throw new ArgumentNullException(nameof(root))); - TypeKey Walk(TypeSyntax ts) => - ts is GenericNameSyntax gns - ? new GenericTypeKey(gns.Identifier.ToString(), - ImmutableList.CreateRange(gns.TypeArgumentList.Arguments.Select(Walk))) - : ts is IdentifierNameSyntax ins - ? abbreviator?.Invoke(ins.Identifier.ValueText) ?? new SimpleTypeKey(ins.ToString()) - : ts is PredefinedTypeSyntax pts - ? new SimpleTypeKey(pts.ToString()) - : ts is ArrayTypeSyntax ats - ? new ArrayTypeKey(Walk(ats.ElementType), - ImmutableList.CreateRange(from rs in ats.RankSpecifiers select rs.Rank)) - : ts is NullableTypeSyntax nts - ? new NullableTypeKey(Walk(nts.ElementType)) - : ts is TupleTypeSyntax tts - ? (TypeKey) new TupleTypeKey(ImmutableList.CreateRange(from te in tts.Elements select Walk(te.Type))) - : throw new NotSupportedException("Unhandled type: " + ts); + TypeKey Walk(TypeSyntax ts) => ts switch + { + PredefinedTypeSyntax pts => new SimpleTypeKey(pts.ToString()), + NullableTypeSyntax nts => new NullableTypeKey(Walk(nts.ElementType)), + IdentifierNameSyntax ins => abbreviator?.Invoke(ins.Identifier.ValueText) + ?? new SimpleTypeKey(ins.ToString()), + GenericNameSyntax gns => + new GenericTypeKey(gns.Identifier.ToString(), + ImmutableList.CreateRange(gns.TypeArgumentList.Arguments.Select(Walk))), + ArrayTypeSyntax ats => + new ArrayTypeKey(Walk(ats.ElementType), + ImmutableList.CreateRange(from rs in ats.RankSpecifiers + select rs.Rank)), + TupleTypeSyntax tts => + new TupleTypeKey(ImmutableList.CreateRange(from te in tts.Elements + select Walk(te.Type))), + _ => throw new NotSupportedException("Unhandled type: " + ts) + }; } static T Read(IEnumerator e, Func errorFactory = null) From b33bea8fc351a5cb07f7a3830e199ec36f9e2c7f Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 25 Oct 2019 10:32:08 +0200 Subject: [PATCH 22/58] Allow use of C# 8 in the entire project --- MoreLinq.Test/MoreLinq.Test.csproj | 2 +- MoreLinq/MoreLinq.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 164653621..852f38af2 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -15,7 +15,7 @@ library MoreLinq.Test.Program - 7.2 + 8 618 diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 5e9b4836e..4b149f227 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -115,7 +115,7 @@ 3.3.0 MoreLINQ Developers. net451;netstandard1.0;netstandard2.0 - 7.3 + 8 true portable true From 6efc67662a892b36c24e35f5528cd5c6ca94e473 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 25 Oct 2019 10:34:21 +0200 Subject: [PATCH 23/58] Use string interpolation for exception message --- MoreLinq/Random.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/MoreLinq/Random.cs b/MoreLinq/Random.cs index e9fb6f0d6..cc6385c26 100644 --- a/MoreLinq/Random.cs +++ b/MoreLinq/Random.cs @@ -151,9 +151,12 @@ public static IEnumerable Random(int minValue, int maxValue) public static IEnumerable Random(Random rand, int minValue, int maxValue) { if (rand == null) throw new ArgumentNullException(nameof(rand)); + if (minValue > maxValue) - throw new ArgumentOutOfRangeException( nameof(minValue), - string.Format("The argument minValue ({0}) is greater than maxValue ({1})", minValue, maxValue) ); + { + throw new ArgumentOutOfRangeException(nameof(minValue), + $"The argument minValue ({minValue}) is greater than maxValue ({maxValue})"); + } return RandomImpl(rand, r => r.Next(minValue, maxValue)); } From a929b845d3adbd9734343a3dd41d67da5a9b7700 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 25 Oct 2019 10:39:36 +0200 Subject: [PATCH 24/58] Format blocks consistently --- MoreLinq.Test/GroupAdjacentTest.cs | 6 ++++-- MoreLinq/FullGroupJoin.cs | 6 +++--- MoreLinq/PartialSort.cs | 6 ++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/MoreLinq.Test/GroupAdjacentTest.cs b/MoreLinq.Test/GroupAdjacentTest.cs index ef09817dc..a68f099bb 100644 --- a/MoreLinq.Test/GroupAdjacentTest.cs +++ b/MoreLinq.Test/GroupAdjacentTest.cs @@ -167,7 +167,8 @@ public void GroupAdjacentSourceSequenceResultSelector() var groupings = source.GroupAdjacent(e => e.Month, (key, group) => group.Sum(v => v.Value)); - using (var reader = groupings.Read()) { + using (var reader = groupings.Read()) + { AssertResult(reader, 123 + 456 + 789); AssertResult(reader, 987 + 654 + 321); AssertResult(reader, 789 + 456 + 123); @@ -197,7 +198,8 @@ public void GroupAdjacentSourceSequenceResultSelectorComparer() var groupings = source.GroupAdjacent(e => e.Month, (key, group) => group.Sum(v => v.Value), StringComparer.OrdinalIgnoreCase); - using (var reader = groupings.Read()) { + using (var reader = groupings.Read()) + { AssertResult(reader, 123 + 456 + 789); AssertResult(reader, 987 + 654 + 321); AssertResult(reader, 789 + 456 + 123); diff --git a/MoreLinq/FullGroupJoin.cs b/MoreLinq/FullGroupJoin.cs index b801e3672..3244ae938 100644 --- a/MoreLinq/FullGroupJoin.cs +++ b/MoreLinq/FullGroupJoin.cs @@ -154,11 +154,11 @@ public static IEnumerable FullGroupJoin var alookup = Lookup.CreateForJoin(first, firstKeySelector, comparer); var blookup = Lookup.CreateForJoin(second, secondKeySelector, comparer); - foreach (var a in alookup) { + foreach (var a in alookup) yield return resultSelector(a.Key, a, blookup[a.Key]); - } - foreach (var b in blookup) { + foreach (var b in blookup) + { if (alookup.Contains(b.Key)) continue; // We can skip the lookup because we are iterating over keys not found in the first sequence diff --git a/MoreLinq/PartialSort.cs b/MoreLinq/PartialSort.cs index 27df35d80..8c2a93aae 100644 --- a/MoreLinq/PartialSort.cs +++ b/MoreLinq/PartialSort.cs @@ -110,9 +110,8 @@ public static IEnumerable PartialSort(this IEnumerable source, int count, IComparer comparer, OrderByDirection direction) { comparer = comparer ?? Comparer.Default; - if (direction == OrderByDirection.Descending) { + if (direction == OrderByDirection.Descending) comparer = new ReverseComparer(comparer); - } return source.PartialSort(count, comparer); } @@ -215,9 +214,8 @@ public static IEnumerable PartialSortBy( OrderByDirection direction) { comparer = comparer ?? Comparer.Default; - if (direction == OrderByDirection.Descending) { + if (direction == OrderByDirection.Descending) comparer = new ReverseComparer(comparer); - } return source.PartialSortBy(count, keySelector, comparer); } From f9840abcbe2696c2019115baeebb368a7b843bd3 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 25 Oct 2019 11:29:37 +0200 Subject: [PATCH 25/58] Remove echo slip into bash build script --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 516932a61..226dd4559 100755 --- a/build.sh +++ b/build.sh @@ -19,5 +19,5 @@ else configs="$1" fi for c in $configs; do - echo dotnet build --no-restore -c $c + dotnet build --no-restore -c $c done From 3d44f73401dd9481f04d5e375b77a80a6e70e855 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 25 Oct 2019 10:41:38 +0100 Subject: [PATCH 26/58] Use null-coalescing assignments (#621) --- MoreLinq/EndsWith.cs | 2 +- MoreLinq/FillBackward.cs | 2 +- MoreLinq/FullGroupJoin.cs | 6 +++--- MoreLinq/MaxBy.cs | 6 +++--- MoreLinq/MinBy.cs | 2 +- MoreLinq/OrderedMerge.cs | 5 +++-- MoreLinq/PartialSort.cs | 4 ++-- MoreLinq/Partition.cs | 4 ++-- MoreLinq/Random.cs | 2 +- MoreLinq/Rank.cs | 5 +++-- MoreLinq/Reactive/Subject.cs | 2 +- MoreLinq/RunLengthEncode.cs | 5 +++-- MoreLinq/ScanBy.cs | 6 +++--- MoreLinq/SortedMerge.cs | 2 +- MoreLinq/Split.cs | 2 +- MoreLinq/StartsWith.cs | 2 +- MoreLinq/ToArrayByIndex.cs | 2 +- 17 files changed, 31 insertions(+), 28 deletions(-) diff --git a/MoreLinq/EndsWith.cs b/MoreLinq/EndsWith.cs index c4f8fff76..b3af2aa4b 100644 --- a/MoreLinq/EndsWith.cs +++ b/MoreLinq/EndsWith.cs @@ -71,7 +71,7 @@ public static bool EndsWith(this IEnumerable first, IEnumerable second, if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); - comparer = comparer ?? EqualityComparer.Default; + comparer ??= EqualityComparer.Default; List secondList; return second.TryGetCollectionCount() is int secondCount diff --git a/MoreLinq/FillBackward.cs b/MoreLinq/FillBackward.cs index 900265f02..1b4cc5514 100644 --- a/MoreLinq/FillBackward.cs +++ b/MoreLinq/FillBackward.cs @@ -114,7 +114,7 @@ static IEnumerable FillBackwardImpl(IEnumerable source, Func p var isBlank = predicate(item); if (isBlank) { - (blanks ?? (blanks = new List())).Add(item); + (blanks ??= new List()).Add(item); } else { diff --git a/MoreLinq/FullGroupJoin.cs b/MoreLinq/FullGroupJoin.cs index 3244ae938..c1a331057 100644 --- a/MoreLinq/FullGroupJoin.cs +++ b/MoreLinq/FullGroupJoin.cs @@ -147,10 +147,10 @@ public static IEnumerable FullGroupJoin if (secondKeySelector == null) throw new ArgumentNullException(nameof(secondKeySelector)); if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - return _(); IEnumerable _() - { - comparer = comparer ?? EqualityComparer.Default; + return _(comparer ?? EqualityComparer.Default); + IEnumerable _(IEqualityComparer comparer) + { var alookup = Lookup.CreateForJoin(first, firstKeySelector, comparer); var blookup = Lookup.CreateForJoin(second, secondKeySelector, comparer); diff --git a/MoreLinq/MaxBy.cs b/MoreLinq/MaxBy.cs index 588446444..74a0f49f9 100644 --- a/MoreLinq/MaxBy.cs +++ b/MoreLinq/MaxBy.cs @@ -212,7 +212,7 @@ public static IExtremaEnumerable MaxBy(this IEnumerable< if (source == null) throw new ArgumentNullException(nameof(source)); if (selector == null) throw new ArgumentNullException(nameof(selector)); - comparer = comparer ?? Comparer.Default; + comparer ??= Comparer.Default; return new ExtremaEnumerable(source, selector, (x, y) => comparer.Compare(x, y)); } @@ -254,7 +254,7 @@ sealed class FirstExtrema : Extrema, T> { protected override IEnumerable GetSomeEnumerable(List store) => store; protected override int Count(List store) => store?.Count ?? 0; - protected override void Push(ref List store, T item) => (store ?? (store = new List())).Add(item); + protected override void Push(ref List store, T item) => (store ??= new List()).Add(item); protected override bool TryPop(ref List store) => false; } @@ -262,7 +262,7 @@ sealed class LastExtrema : Extrema, T> { protected override IEnumerable GetSomeEnumerable(Queue store) => store; protected override int Count(Queue store) => store?.Count ?? 0; - protected override void Push(ref Queue store, T item) => (store ?? (store = new Queue())).Enqueue(item); + protected override void Push(ref Queue store, T item) => (store ??= new Queue()).Enqueue(item); protected override bool TryPop(ref Queue store) { store.Dequeue(); return true; } } } diff --git a/MoreLinq/MinBy.cs b/MoreLinq/MinBy.cs index 8dc488161..ed016132b 100644 --- a/MoreLinq/MinBy.cs +++ b/MoreLinq/MinBy.cs @@ -67,7 +67,7 @@ public static IExtremaEnumerable MinBy(this IEnumerable< if (source == null) throw new ArgumentNullException(nameof(source)); if (selector == null) throw new ArgumentNullException(nameof(selector)); - comparer = comparer ?? Comparer.Default; + comparer ??= Comparer.Default; return new ExtremaEnumerable(source, selector, (x, y) => -Math.Sign(comparer.Compare(x, y))); } } diff --git a/MoreLinq/OrderedMerge.cs b/MoreLinq/OrderedMerge.cs index 003a888c6..f0ece4b18 100644 --- a/MoreLinq/OrderedMerge.cs +++ b/MoreLinq/OrderedMerge.cs @@ -282,8 +282,9 @@ public static IEnumerable OrderedMerge( if (bothSelector == null) throw new ArgumentNullException(nameof(bothSelector)); if (secondSelector == null) throw new ArgumentNullException(nameof(secondSelector)); - comparer = comparer ?? Comparer.Default; - return _(); IEnumerable _() + return _(comparer ?? Comparer.Default); + + IEnumerable _(IComparer comparer) { using (var e1 = first.GetEnumerator()) using (var e2 = second.GetEnumerator()) diff --git a/MoreLinq/PartialSort.cs b/MoreLinq/PartialSort.cs index 8c2a93aae..eb09b545e 100644 --- a/MoreLinq/PartialSort.cs +++ b/MoreLinq/PartialSort.cs @@ -109,7 +109,7 @@ public static IEnumerable PartialSort(this IEnumerable source, public static IEnumerable PartialSort(this IEnumerable source, int count, IComparer comparer, OrderByDirection direction) { - comparer = comparer ?? Comparer.Default; + comparer ??= Comparer.Default; if (direction == OrderByDirection.Descending) comparer = new ReverseComparer(comparer); return source.PartialSort(count, comparer); @@ -213,7 +213,7 @@ public static IEnumerable PartialSortBy( IComparer comparer, OrderByDirection direction) { - comparer = comparer ?? Comparer.Default; + comparer ??= Comparer.Default; if (direction == OrderByDirection.Descending) comparer = new ReverseComparer(comparer); return source.PartialSortBy(count, keySelector, comparer); diff --git a/MoreLinq/Partition.cs b/MoreLinq/Partition.cs index eb7750c49..0cda564ab 100644 --- a/MoreLinq/Partition.cs +++ b/MoreLinq/Partition.cs @@ -305,7 +305,7 @@ static TResult PartitionImpl(IEnumerable.Default; + comparer ??= EqualityComparer.Default; List> etc = null; @@ -325,7 +325,7 @@ static TResult PartitionImpl(IEnumerable>(); + etc ??= new List>(); etc.Add(e); } else diff --git a/MoreLinq/Random.cs b/MoreLinq/Random.cs index cc6385c26..eaf66bca1 100644 --- a/MoreLinq/Random.cs +++ b/MoreLinq/Random.cs @@ -231,7 +231,7 @@ sealed class GlobalRandom : Random static int _seed = Environment.TickCount; [ThreadStatic] static Random _threadRandom; - static Random ThreadRandom => _threadRandom ?? (_threadRandom = new Random(Interlocked.Increment(ref _seed))); + static Random ThreadRandom => _threadRandom ??= new Random(Interlocked.Increment(ref _seed)); GlobalRandom() { } diff --git a/MoreLinq/Rank.cs b/MoreLinq/Rank.cs index d2c806ad0..b0d218276 100644 --- a/MoreLinq/Rank.cs +++ b/MoreLinq/Rank.cs @@ -77,8 +77,9 @@ public static IEnumerable RankBy(this IEnumerable s if (source == null) throw new ArgumentNullException(nameof(source)); if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - comparer = comparer ?? Comparer.Default; - return _(); IEnumerable _() + return _(comparer ?? Comparer.Default); + + IEnumerable _(IComparer comparer) { source = source.ToArray(); // avoid enumerating source twice diff --git a/MoreLinq/Reactive/Subject.cs b/MoreLinq/Reactive/Subject.cs index d47aedaed..cbb1a9764 100644 --- a/MoreLinq/Reactive/Subject.cs +++ b/MoreLinq/Reactive/Subject.cs @@ -28,7 +28,7 @@ sealed class Subject : IObservable, IObserver Exception _error; bool HasObservers => (_observers?.Count ?? 0) > 0; - List> Observers => _observers ?? (_observers = new List>()); + List> Observers => _observers ??= new List>(); bool IsMuted => _completed || _error != null; diff --git a/MoreLinq/RunLengthEncode.cs b/MoreLinq/RunLengthEncode.cs index cf9ee29fa..92482e647 100644 --- a/MoreLinq/RunLengthEncode.cs +++ b/MoreLinq/RunLengthEncode.cs @@ -50,8 +50,9 @@ public static IEnumerable> RunLengthEncode(this IEnumera if (sequence == null) throw new ArgumentNullException(nameof(sequence)); - comparer = comparer ?? EqualityComparer.Default; - return _(); IEnumerable> _() + return _(comparer ?? EqualityComparer.Default); + + IEnumerable> _(IEqualityComparer comparer) { // This implementation could also have been written using a foreach loop, // but it proved to be easier to deal with edge certain cases that occur diff --git a/MoreLinq/ScanBy.cs b/MoreLinq/ScanBy.cs index 551aa5043..2f5de2e03 100644 --- a/MoreLinq/ScanBy.cs +++ b/MoreLinq/ScanBy.cs @@ -85,10 +85,10 @@ public static IEnumerable> ScanBy> _() - { - comparer = comparer ?? EqualityComparer.Default; + return _(comparer ?? EqualityComparer.Default); + IEnumerable> _(IEqualityComparer comparer) + { var stateByKey = new Dictionary(comparer); var prevKey = (HasValue: false, Value: default(TKey)); var nullKeyState = (HasValue: false, Value: default(TState)); diff --git a/MoreLinq/SortedMerge.cs b/MoreLinq/SortedMerge.cs index 705528324..70bb0741a 100644 --- a/MoreLinq/SortedMerge.cs +++ b/MoreLinq/SortedMerge.cs @@ -73,7 +73,7 @@ public static IEnumerable SortedMerge(this IEnumerable.Default; + comparer ??= Comparer.Default; // define an precedence function based on the comparer and direction // this is a function that will return True if (b) should precede (a) diff --git a/MoreLinq/Split.cs b/MoreLinq/Split.cs index b4481661a..69b1c5f30 100644 --- a/MoreLinq/Split.cs +++ b/MoreLinq/Split.cs @@ -182,7 +182,7 @@ public static IEnumerable Split(this IEnumerable.Default; + comparer ??= EqualityComparer.Default; return Split(source, item => comparer.Equals(item, separator), count, resultSelector); } diff --git a/MoreLinq/StartsWith.cs b/MoreLinq/StartsWith.cs index e4dddc150..468d3e68c 100644 --- a/MoreLinq/StartsWith.cs +++ b/MoreLinq/StartsWith.cs @@ -80,7 +80,7 @@ public static bool StartsWith(this IEnumerable first, IEnumerable secon return false; } - comparer = comparer ?? EqualityComparer.Default; + comparer ??= EqualityComparer.Default; using (var firstIter = first.GetEnumerator()) { diff --git a/MoreLinq/ToArrayByIndex.cs b/MoreLinq/ToArrayByIndex.cs index 1ccf13d93..4bbad61ab 100644 --- a/MoreLinq/ToArrayByIndex.cs +++ b/MoreLinq/ToArrayByIndex.cs @@ -122,7 +122,7 @@ public static TResult[] ToArrayByIndex(this IEnumerable source, var lastIndex = -1; var indexed = (List>) null; - List> Indexed() => indexed ?? (indexed = new List>()); + List> Indexed() => indexed ??= new List>(); foreach (var e in source) { From cdc6f76f4bf348e860b9eaa072279ee2956d6de7 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 25 Oct 2019 11:45:13 +0100 Subject: [PATCH 27/58] Replace using statements with declarations This is a squashed merge of PR #622. --- MoreLinq/Backsert.cs | 34 +++++++++---------- MoreLinq/CountMethods.cs | 2 +- MoreLinq/EndsWith.cs | 4 +-- MoreLinq/Exclude.cs | 22 ++++++------- MoreLinq/FullJoin.cs | 21 ++++++------ MoreLinq/GroupAdjacent.cs | 41 ++++++++++++----------- MoreLinq/Insert.cs | 21 ++++++------ MoreLinq/Lag.cs | 43 ++++++++++++------------ MoreLinq/Lead.cs | 33 +++++++++---------- MoreLinq/MaxBy.cs | 45 +++++++++++++------------ MoreLinq/OrderedMerge.cs | 65 ++++++++++++++++++------------------- MoreLinq/Pairwise.cs | 19 +++++------ MoreLinq/Permutations.cs | 9 +++-- MoreLinq/PreScan.cs | 18 +++++----- MoreLinq/RunLengthEncode.cs | 35 ++++++++++---------- MoreLinq/Scan.cs | 21 ++++++------ MoreLinq/Segment.cs | 61 +++++++++++++++++----------------- MoreLinq/SkipUntil.cs | 16 ++++----- MoreLinq/SortedMerge.cs | 57 ++++++++++++++++---------------- MoreLinq/StartsWith.cs | 6 ++-- MoreLinq/Window.cs | 45 +++++++++++++------------ 21 files changed, 298 insertions(+), 320 deletions(-) diff --git a/MoreLinq/Backsert.cs b/MoreLinq/Backsert.cs index 0918a0938..928eef47a 100644 --- a/MoreLinq/Backsert.cs +++ b/MoreLinq/Backsert.cs @@ -67,29 +67,27 @@ public static IEnumerable Backsert(this IEnumerable first, IEnumerable< return _(); IEnumerable _() { - using (var e = first.CountDown(index, ValueTuple.Create) - .GetEnumerator()) + using var e = first.CountDown(index, ValueTuple.Create).GetEnumerator(); + + if (e.MoveNext()) { - if (e.MoveNext()) - { - var (_, countdown) = e.Current; - if (countdown is int n && n != index - 1) - throw new ArgumentOutOfRangeException(nameof(index), "Insertion index is greater than the length of the first sequence."); + var (_, countdown) = e.Current; + if (countdown is int n && n != index - 1) + throw new ArgumentOutOfRangeException(nameof(index), "Insertion index is greater than the length of the first sequence."); - do + do + { + T a; + (a, countdown) = e.Current; + if (countdown == index - 1) { - T a; - (a, countdown) = e.Current; - if (countdown == index - 1) - { - foreach (var b in second) - yield return b; - } - - yield return a; + foreach (var b in second) + yield return b; } - while (e.MoveNext()); + + yield return a; } + while (e.MoveNext()); } } } diff --git a/MoreLinq/CountMethods.cs b/MoreLinq/CountMethods.cs index 24bd2afff..b55ee8656 100644 --- a/MoreLinq/CountMethods.cs +++ b/MoreLinq/CountMethods.cs @@ -180,7 +180,7 @@ public static int CompareCount(this IEnumerable first, bool firstHasNext; bool secondHasNext; - using (var e1 = first.GetEnumerator()) + using var e1 = first.GetEnumerator(); using (var e2 = second.GetEnumerator()) { do diff --git a/MoreLinq/EndsWith.cs b/MoreLinq/EndsWith.cs index b3af2aa4b..ca1de4e66 100644 --- a/MoreLinq/EndsWith.cs +++ b/MoreLinq/EndsWith.cs @@ -82,8 +82,8 @@ public static bool EndsWith(this IEnumerable first, IEnumerable second, bool Impl(IEnumerable snd, int count) { - using (var firstIter = first.TakeLast(count).GetEnumerator()) - return snd.All(item => firstIter.MoveNext() && comparer.Equals(firstIter.Current, item)); + using var firstIter = first.TakeLast(count).GetEnumerator(); + return snd.All(item => firstIter.MoveNext() && comparer.Equals(firstIter.Current, item)); } } } diff --git a/MoreLinq/Exclude.cs b/MoreLinq/Exclude.cs index 2539194f3..ae65ca6a7 100644 --- a/MoreLinq/Exclude.cs +++ b/MoreLinq/Exclude.cs @@ -45,18 +45,16 @@ public static IEnumerable Exclude(this IEnumerable sequence, int startI { var index = -1; var endIndex = startIndex + count; - using (var iter = sequence.GetEnumerator()) - { - // yield the first part of the sequence - while (iter.MoveNext() && ++index < startIndex) - yield return iter.Current; - // skip the next part (up to count items) - while (++index < endIndex && iter.MoveNext()) - continue; - // yield the remainder of the sequence - while (iter.MoveNext()) - yield return iter.Current; - } + var iter = sequence.GetEnumerator(); + // yield the first part of the sequence + while (iter.MoveNext() && ++index < startIndex) + yield return iter.Current; + // skip the next part (up to count items) + while (++index < endIndex && iter.MoveNext()) + continue; + // yield the remainder of the sequence + while (iter.MoveNext()) + yield return iter.Current; } } } diff --git a/MoreLinq/FullJoin.cs b/MoreLinq/FullJoin.cs index eacc8d21a..5f3f782c2 100644 --- a/MoreLinq/FullJoin.cs +++ b/MoreLinq/FullJoin.cs @@ -239,18 +239,17 @@ public static IEnumerable FullJoin( var key = firstKeySelector(fe); firstKeys.Add(key); - using (var se = secondLookup[key].GetEnumerator()) + using var se = secondLookup[key].GetEnumerator(); + + if (se.MoveNext()) + { + do { yield return bothSelector(fe, se.Current); } + while (se.MoveNext()); + } + else { - if (se.MoveNext()) - { - do { yield return bothSelector(fe, se.Current); } - while (se.MoveNext()); - } - else - { - se.Dispose(); - yield return firstSelector(fe); - } + se.Dispose(); + yield return firstSelector(fe); } } diff --git a/MoreLinq/GroupAdjacent.cs b/MoreLinq/GroupAdjacent.cs index b24521998..3523bb690 100644 --- a/MoreLinq/GroupAdjacent.cs +++ b/MoreLinq/GroupAdjacent.cs @@ -271,31 +271,30 @@ static IEnumerable GroupAdjacentImpl( Debug.Assert(resultSelector != null); Debug.Assert(comparer != null); - using (var iterator = source.GetEnumerator()) - { - var group = default(TKey); - var members = (List) null; + using var iterator = source.GetEnumerator(); + + var group = default(TKey); + var members = (List) null; - while (iterator.MoveNext()) + while (iterator.MoveNext()) + { + var key = keySelector(iterator.Current); + var element = elementSelector(iterator.Current); + if (members != null && comparer.Equals(group, key)) { - var key = keySelector(iterator.Current); - var element = elementSelector(iterator.Current); - if (members != null && comparer.Equals(group, key)) - { - members.Add(element); - } - else - { - if (members != null) - yield return resultSelector(group, members); - group = key; - members = new List { element }; - } + members.Add(element); + } + else + { + if (members != null) + yield return resultSelector(group, members); + group = key; + members = new List { element }; } - - if (members != null) - yield return resultSelector(group, members); } + + if (members != null) + yield return resultSelector(group, members); } static IGrouping CreateGroupAdjacentGrouping(TKey key, IList members) diff --git a/MoreLinq/Insert.cs b/MoreLinq/Insert.cs index 098205e78..09181f47f 100644 --- a/MoreLinq/Insert.cs +++ b/MoreLinq/Insert.cs @@ -59,20 +59,19 @@ public static IEnumerable Insert(this IEnumerable first, IEnumerable { var i = -1; - using (var iter = first.GetEnumerator()) - { - while (++i < index && iter.MoveNext()) - yield return iter.Current; + using var iter = first.GetEnumerator(); - if (i < index) - throw new ArgumentOutOfRangeException(nameof(index), "Insertion index is greater than the length of the first sequence."); + while (++i < index && iter.MoveNext()) + yield return iter.Current; - foreach (var item in second) - yield return item; + if (i < index) + throw new ArgumentOutOfRangeException(nameof(index), "Insertion index is greater than the length of the first sequence."); - while (iter.MoveNext()) - yield return iter.Current; - } + foreach (var item in second) + yield return item; + + while (iter.MoveNext()) + yield return iter.Current; } } } diff --git a/MoreLinq/Lag.cs b/MoreLinq/Lag.cs index 0356dc3c1..51cdf5808 100644 --- a/MoreLinq/Lag.cs +++ b/MoreLinq/Lag.cs @@ -66,31 +66,30 @@ public static IEnumerable Lag(this IEnumerable _() { - using (var iter = source.GetEnumerator()) + using var iter = source.GetEnumerator(); + + var i = offset; + var lagQueue = new Queue(offset); + // until we progress far enough, the lagged value is defaultLagValue + var hasMore = true; + // NOTE: The if statement below takes advantage of short-circuit evaluation + // to ensure we don't advance the iterator when we reach the lag offset. + // Do not reorder the terms in the condition! + while (i-- > 0 && (hasMore = iter.MoveNext())) { - var i = offset; - var lagQueue = new Queue(offset); - // until we progress far enough, the lagged value is defaultLagValue - var hasMore = true; - // NOTE: The if statement below takes advantage of short-circuit evaluation - // to ensure we don't advance the iterator when we reach the lag offset. - // Do not reorder the terms in the condition! - while (i-- > 0 && (hasMore = iter.MoveNext())) - { - lagQueue.Enqueue(iter.Current); - // until we reach the lag offset, the lagged value is the defaultLagValue - yield return resultSelector(iter.Current, defaultLagValue); - } + lagQueue.Enqueue(iter.Current); + // until we reach the lag offset, the lagged value is the defaultLagValue + yield return resultSelector(iter.Current, defaultLagValue); + } - if (hasMore) // check that we didn't consume the sequence yet + if (hasMore) // check that we didn't consume the sequence yet + { + // now the lagged value is derived from the sequence + while (iter.MoveNext()) { - // now the lagged value is derived from the sequence - while (iter.MoveNext()) - { - var lagValue = lagQueue.Dequeue(); - yield return resultSelector(iter.Current, lagValue); - lagQueue.Enqueue(iter.Current); - } + var lagValue = lagQueue.Dequeue(); + yield return resultSelector(iter.Current, lagValue); + lagQueue.Enqueue(iter.Current); } } } diff --git a/MoreLinq/Lead.cs b/MoreLinq/Lead.cs index eaf9c0e2b..dc5a9c391 100644 --- a/MoreLinq/Lead.cs +++ b/MoreLinq/Lead.cs @@ -65,25 +65,24 @@ public static IEnumerable Lead(this IEnumerable _() { var leadQueue = new Queue(offset); - using (var iter = source.GetEnumerator()) + var iter = source.GetEnumerator(); + + bool hasMore; + // first, prefetch and populate the lead queue with the next step of + // items to be streamed out to the consumer of the sequence + while ((hasMore = iter.MoveNext()) && leadQueue.Count < offset) + leadQueue.Enqueue(iter.Current); + // next, while the source sequence has items, yield the result of + // the projection function applied to the top of queue and current item + while (hasMore) { - bool hasMore; - // first, prefetch and populate the lead queue with the next step of - // items to be streamed out to the consumer of the sequence - while ((hasMore = iter.MoveNext()) && leadQueue.Count < offset) - leadQueue.Enqueue(iter.Current); - // next, while the source sequence has items, yield the result of - // the projection function applied to the top of queue and current item - while (hasMore) - { - yield return resultSelector(leadQueue.Dequeue(), iter.Current); - leadQueue.Enqueue(iter.Current); - hasMore = iter.MoveNext(); - } - // yield the remaining values in the lead queue with the default lead value - while (leadQueue.Count > 0) - yield return resultSelector(leadQueue.Dequeue(), defaultLeadValue); + yield return resultSelector(leadQueue.Dequeue(), iter.Current); + leadQueue.Enqueue(iter.Current); + hasMore = iter.MoveNext(); } + // yield the remaining values in the lead queue with the default lead value + while (leadQueue.Count > 0) + yield return resultSelector(leadQueue.Dequeue(), defaultLeadValue); } } } diff --git a/MoreLinq/MaxBy.cs b/MoreLinq/MaxBy.cs index 74a0f49f9..f9f3ab3f9 100644 --- a/MoreLinq/MaxBy.cs +++ b/MoreLinq/MaxBy.cs @@ -308,34 +308,33 @@ static IEnumerable ExtremaBy( IEnumerable Extrema() { - using (var e = source.GetEnumerator()) - { - if (!e.MoveNext()) - return new List(); + using var e = source.GetEnumerator(); + + if (!e.MoveNext()) + return new List(); - var store = extrema.New(); - extrema.Add(ref store, limit, e.Current); - var extremaKey = selector(e.Current); + var store = extrema.New(); + extrema.Add(ref store, limit, e.Current); + var extremaKey = selector(e.Current); - while (e.MoveNext()) + while (e.MoveNext()) + { + var item = e.Current; + var key = selector(item); + var comparison = comparer(key, extremaKey); + if (comparison > 0) { - var item = e.Current; - var key = selector(item); - var comparison = comparer(key, extremaKey); - if (comparison > 0) - { - extrema.Restart(ref store); - extrema.Add(ref store, limit, item); - extremaKey = key; - } - else if (comparison == 0) - { - extrema.Add(ref store, limit, item); - } + extrema.Restart(ref store); + extrema.Add(ref store, limit, item); + extremaKey = key; + } + else if (comparison == 0) + { + extrema.Add(ref store, limit, item); } - - return extrema.GetEnumerable(store); } + + return extrema.GetEnumerable(store); } } diff --git a/MoreLinq/OrderedMerge.cs b/MoreLinq/OrderedMerge.cs index f0ece4b18..bce2a5ac3 100644 --- a/MoreLinq/OrderedMerge.cs +++ b/MoreLinq/OrderedMerge.cs @@ -286,50 +286,49 @@ public static IEnumerable OrderedMerge( IEnumerable _(IComparer comparer) { - using (var e1 = first.GetEnumerator()) - using (var e2 = second.GetEnumerator()) - { - var gotFirst = e1.MoveNext(); - var gotSecond = e2.MoveNext(); + using var e1 = first.GetEnumerator(); + using var e2 = second.GetEnumerator(); + + var gotFirst = e1.MoveNext(); + var gotSecond = e2.MoveNext(); - while (gotFirst || gotSecond) + while (gotFirst || gotSecond) + { + if (gotFirst && gotSecond) { - if (gotFirst && gotSecond) - { - var element1 = e1.Current; - var key1 = firstKeySelector(element1); - var element2 = e2.Current; - var key2 = secondKeySelector(element2); - var comparison = comparer.Compare(key1, key2); + var element1 = e1.Current; + var key1 = firstKeySelector(element1); + var element2 = e2.Current; + var key2 = secondKeySelector(element2); + var comparison = comparer.Compare(key1, key2); - if (comparison < 0) - { - yield return firstSelector(element1); - gotFirst = e1.MoveNext(); - } - else if (comparison > 0) - { - yield return secondSelector(element2); - gotSecond = e2.MoveNext(); - } - else - { - yield return bothSelector(element1, element2); - gotFirst = e1.MoveNext(); - gotSecond = e2.MoveNext(); - } + if (comparison < 0) + { + yield return firstSelector(element1); + gotFirst = e1.MoveNext(); } - else if (gotSecond) + else if (comparison > 0) { - yield return secondSelector(e2.Current); + yield return secondSelector(element2); gotSecond = e2.MoveNext(); } - else // (gotFirst) + else { - yield return firstSelector(e1.Current); + yield return bothSelector(element1, element2); gotFirst = e1.MoveNext(); + gotSecond = e2.MoveNext(); } } + else if (gotSecond) + { + yield return secondSelector(e2.Current); + gotSecond = e2.MoveNext(); + } + else // (gotFirst) + { + yield return firstSelector(e1.Current); + gotFirst = e1.MoveNext(); + } } } } diff --git a/MoreLinq/Pairwise.cs b/MoreLinq/Pairwise.cs index 64b42df0d..9e262e85d 100644 --- a/MoreLinq/Pairwise.cs +++ b/MoreLinq/Pairwise.cs @@ -55,17 +55,16 @@ public static IEnumerable Pairwise(this IEnumerable _() { - using (var e = source.GetEnumerator()) - { - if (!e.MoveNext()) - yield break; + using var e = source.GetEnumerator(); + + if (!e.MoveNext()) + yield break; - var previous = e.Current; - while (e.MoveNext()) - { - yield return resultSelector(previous, e.Current); - previous = e.Current; - } + var previous = e.Current; + while (e.MoveNext()) + { + yield return resultSelector(previous, e.Current); + previous = e.Current; } } } diff --git a/MoreLinq/Permutations.cs b/MoreLinq/Permutations.cs index 4482e41ed..afede3963 100644 --- a/MoreLinq/Permutations.cs +++ b/MoreLinq/Permutations.cs @@ -198,11 +198,10 @@ public static IEnumerable> Permutations(this IEnumerable sequence return _(); IEnumerable> _() { - using (var iter = new PermutationEnumerator(sequence)) - { - while (iter.MoveNext()) - yield return iter.Current; - } + using var iter = new PermutationEnumerator(sequence); + + while (iter.MoveNext()) + yield return iter.Current; } } } diff --git a/MoreLinq/PreScan.cs b/MoreLinq/PreScan.cs index 61292f23f..88aa8f57c 100644 --- a/MoreLinq/PreScan.cs +++ b/MoreLinq/PreScan.cs @@ -62,20 +62,18 @@ public static IEnumerable PreScan( return _(); IEnumerable _() { var aggregator = identity; + using var e = source.GetEnumerator(); - using (var e = source.GetEnumerator()) + if (e.MoveNext()) { - if (e.MoveNext()) + yield return aggregator; + var current = e.Current; + + while (e.MoveNext()) { + aggregator = transformation(aggregator, current); yield return aggregator; - var current = e.Current; - - while (e.MoveNext()) - { - aggregator = transformation(aggregator, current); - yield return aggregator; - current = e.Current; - } + current = e.Current; } } } diff --git a/MoreLinq/RunLengthEncode.cs b/MoreLinq/RunLengthEncode.cs index 92482e647..d60b9f131 100644 --- a/MoreLinq/RunLengthEncode.cs +++ b/MoreLinq/RunLengthEncode.cs @@ -58,29 +58,28 @@ IEnumerable> _(IEqualityComparer comparer) // but it proved to be easier to deal with edge certain cases that occur // (such as empty sequences) using an explicit iterator and a while loop. - using (var iter = sequence.GetEnumerator()) + using var iter = sequence.GetEnumerator(); + + if (iter.MoveNext()) { - if (iter.MoveNext()) - { - var prevItem = iter.Current; - var runCount = 1; + var prevItem = iter.Current; + var runCount = 1; - while (iter.MoveNext()) + while (iter.MoveNext()) + { + if (comparer.Equals(prevItem, iter.Current)) { - if (comparer.Equals(prevItem, iter.Current)) - { - ++runCount; - } - else - { - yield return new KeyValuePair(prevItem, runCount); - prevItem = iter.Current; - runCount = 1; - } + ++runCount; + } + else + { + yield return new KeyValuePair(prevItem, runCount); + prevItem = iter.Current; + runCount = 1; } - - yield return new KeyValuePair(prevItem, runCount); } + + yield return new KeyValuePair(prevItem, runCount); } } } diff --git a/MoreLinq/Scan.cs b/MoreLinq/Scan.cs index b05c99230..a14ffe599 100644 --- a/MoreLinq/Scan.cs +++ b/MoreLinq/Scan.cs @@ -93,20 +93,19 @@ static IEnumerable ScanImpl(IEnumerable source Func transformation, Func, (bool, TState)> seeder) { - using (var e = source.GetEnumerator()) - { - var (seeded, aggregator) = seeder(e); + using var e = source.GetEnumerator(); - if (!seeded) - yield break; + var (seeded, aggregator) = seeder(e); - yield return aggregator; + if (!seeded) + yield break; + + yield return aggregator; - while (e.MoveNext()) - { - aggregator = transformation(aggregator, e.Current); - yield return aggregator; - } + while (e.MoveNext()) + { + aggregator = transformation(aggregator, e.Current); + yield return aggregator; } } } diff --git a/MoreLinq/Segment.cs b/MoreLinq/Segment.cs index 3100d6085..198d8d076 100644 --- a/MoreLinq/Segment.cs +++ b/MoreLinq/Segment.cs @@ -77,44 +77,43 @@ public static IEnumerable> Segment(this IEnumerable source, return _(); IEnumerable> _() { var index = -1; - using (var iter = source.GetEnumerator()) + using var iter = source.GetEnumerator(); + + var segment = new List(); + var prevItem = default(T); + + // ensure that the first item is always part + // of the first segment. This is an intentional + // behavior. Segmentation always begins with + // the second element in the sequence. + if (iter.MoveNext()) + { + ++index; + segment.Add(iter.Current); + prevItem = iter.Current; + } + + while (iter.MoveNext()) { - var segment = new List(); - var prevItem = default(T); + ++index; + // check if the item represents the start of a new segment + var isNewSegment = newSegmentPredicate(iter.Current, prevItem, index); + prevItem = iter.Current; - // ensure that the first item is always part - // of the first segment. This is an intentional - // behavior. Segmentation always begins with - // the second element in the sequence. - if (iter.MoveNext()) + if (!isNewSegment) { - ++index; + // if not a new segment, append and continue segment.Add(iter.Current); - prevItem = iter.Current; + continue; } + yield return segment; // yield the completed segment - while (iter.MoveNext()) - { - ++index; - // check if the item represents the start of a new segment - var isNewSegment = newSegmentPredicate(iter.Current, prevItem, index); - prevItem = iter.Current; - - if (!isNewSegment) - { - // if not a new segment, append and continue - segment.Add(iter.Current); - continue; - } - yield return segment; // yield the completed segment - - // start a new segment... - segment = new List { iter.Current }; - } - // handle the case of the sequence ending before new segment is detected - if (segment.Count > 0) - yield return segment; + // start a new segment... + segment = new List { iter.Current }; } + // handle the case of the sequence ending before new segment is detected + if (segment.Count > 0) + yield return segment; } } } diff --git a/MoreLinq/SkipUntil.cs b/MoreLinq/SkipUntil.cs index be1e6b8b6..debbc04a8 100644 --- a/MoreLinq/SkipUntil.cs +++ b/MoreLinq/SkipUntil.cs @@ -59,16 +59,16 @@ public static IEnumerable SkipUntil(this IEnumerable return _(); IEnumerable _() { - using (var iterator = source.GetEnumerator()) + using var iterator = source.GetEnumerator(); + + while (iterator.MoveNext()) { - while (iterator.MoveNext()) - { - if (predicate(iterator.Current)) - break; - } - while (iterator.MoveNext()) - yield return iterator.Current; + if (predicate(iterator.Current)) + break; } + + while (iterator.MoveNext()) + yield return iterator.Current; } } } diff --git a/MoreLinq/SortedMerge.cs b/MoreLinq/SortedMerge.cs index 70bb0741a..7b68a2327 100644 --- a/MoreLinq/SortedMerge.cs +++ b/MoreLinq/SortedMerge.cs @@ -98,43 +98,42 @@ public static IEnumerable SortedMerge(this IEnumerable Impl(IEnumerable> sequences) { - using (var disposables = new DisposableGroup(sequences.Select(e => e.GetEnumerator()).Acquire())) + using var disposables = new DisposableGroup(sequences.Select(e => e.GetEnumerator()).Acquire()); + + var iterators = disposables.Iterators; + + // prime all of the iterators by advancing them to their first element (if any) + // NOTE: We start with the last index to simplify the removal of an iterator if + // it happens to be terminal (no items) before we start merging + for (var i = iterators.Count - 1; i >= 0; i--) { - var iterators = disposables.Iterators; + if (!iterators[i].MoveNext()) + disposables.Exclude(i); + } - // prime all of the iterators by advancing them to their first element (if any) - // NOTE: We start with the last index to simplify the removal of an iterator if - // it happens to be terminal (no items) before we start merging - for (var i = iterators.Count - 1; i >= 0; i--) - { - if (!iterators[i].MoveNext()) - disposables.Exclude(i); - } + // while all iterators have not yet been consumed... + while (iterators.Count > 0) + { + var nextIndex = 0; + var nextValue = disposables[0].Current; - // while all iterators have not yet been consumed... - while (iterators.Count > 0) + // find the next least element to return + for (var i = 1; i < iterators.Count; i++) { - var nextIndex = 0; - var nextValue = disposables[0].Current; - - // find the next least element to return - for (var i = 1; i < iterators.Count; i++) + var anotherElement = disposables[i].Current; + // determine which element follows based on ordering function + if (precedenceFunc(nextValue, anotherElement)) { - var anotherElement = disposables[i].Current; - // determine which element follows based on ordering function - if (precedenceFunc(nextValue, anotherElement)) - { - nextIndex = i; - nextValue = anotherElement; - } + nextIndex = i; + nextValue = anotherElement; } + } - yield return nextValue; // next value in precedence order + yield return nextValue; // next value in precedence order - // advance iterator that yielded element, excluding it when consumed - if (!iterators[nextIndex].MoveNext()) - disposables.Exclude(nextIndex); - } + // advance iterator that yielded element, excluding it when consumed + if (!iterators[nextIndex].MoveNext()) + disposables.Exclude(nextIndex); } } } diff --git a/MoreLinq/StartsWith.cs b/MoreLinq/StartsWith.cs index 468d3e68c..2bedebaf0 100644 --- a/MoreLinq/StartsWith.cs +++ b/MoreLinq/StartsWith.cs @@ -82,10 +82,8 @@ public static bool StartsWith(this IEnumerable first, IEnumerable secon comparer ??= EqualityComparer.Default; - using (var firstIter = first.GetEnumerator()) - { - return second.All(item => firstIter.MoveNext() && comparer.Equals(firstIter.Current, item)); - } + using var firstIter = first.GetEnumerator(); + return second.All(item => firstIter.MoveNext() && comparer.Equals(firstIter.Current, item)); } } } diff --git a/MoreLinq/Window.cs b/MoreLinq/Window.cs index 278379862..c377f90d3 100644 --- a/MoreLinq/Window.cs +++ b/MoreLinq/Window.cs @@ -41,32 +41,31 @@ 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; + 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; + if (i < size) + yield break; - // return the first window (whatever size it may be) - yield return window; + // return the first window (whatever size it may be) + yield return window; - // generate the next window by shifting forward by one item - while (iter.MoveNext()) - { - // NOTE: If we used a circular queue rather than a list, - // we could make this quite a bit more efficient. - // Sadly the BCL does not offer such a collection. - var newWindow = new TSource[size]; - Array.Copy(window, 1, newWindow, 0, size - 1); - newWindow[size - 1] = iter.Current; - yield return newWindow; - window = newWindow; - } + // generate the next window by shifting forward by one item + while (iter.MoveNext()) + { + // NOTE: If we used a circular queue rather than a list, + // we could make this quite a bit more efficient. + // Sadly the BCL does not offer such a collection. + var newWindow = new TSource[size]; + Array.Copy(window, 1, newWindow, 0, size - 1); + newWindow[size - 1] = iter.Current; + yield return newWindow; + window = newWindow; } } } From 183ee85f914882a4d3e34f13dee60100c3d61a5d Mon Sep 17 00:00:00 2001 From: James Webster Date: Tue, 29 Oct 2019 06:36:30 +1000 Subject: [PATCH 28/58] Add TrySingle: zero | (one, x) | many This is a squashed merge of PR #612 that closes #540. Co-Authored-By: Atif Aziz --- MoreLinq.Test/TrySingleTest.cs | 150 +++++++++++++++++++++++++++++++++ MoreLinq/Extensions.g.cs | 82 ++++++++++++++++++ MoreLinq/MoreLinq.csproj | 1 + MoreLinq/TrySingle.cs | 132 +++++++++++++++++++++++++++++ README.md | 8 ++ 5 files changed, 373 insertions(+) create mode 100644 MoreLinq.Test/TrySingleTest.cs create mode 100644 MoreLinq/TrySingle.cs diff --git a/MoreLinq.Test/TrySingleTest.cs b/MoreLinq.Test/TrySingleTest.cs new file mode 100644 index 000000000..675044c7e --- /dev/null +++ b/MoreLinq.Test/TrySingleTest.cs @@ -0,0 +1,150 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2019 James Webster. 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 + +using NUnit.Framework.Interfaces; + +namespace MoreLinq.Test +{ + using System; + using System.Collections; + using System.Collections.Generic; + using NUnit.Framework; + + [TestFixture] + public class TrySingleTest + { + [TestCase(SourceKind.Sequence)] + [TestCase(SourceKind.BreakingList)] + [TestCase(SourceKind.BreakingReadOnlyList)] + [TestCase(SourceKind.BreakingCollection)] + [TestCase(SourceKind.BreakingReadOnlyCollection)] + public void TrySingleWithEmptySource(SourceKind kind) + { + var source = new int?[0].ToSourceKind(kind); + + var (cardinality, value) = source.TrySingle("zero", "one", "many"); + + Assert.That(cardinality, Is.EqualTo("zero")); + Assert.That(value, Is.Null); + } + + [TestCase(SourceKind.Sequence)] + [TestCase(SourceKind.BreakingList)] + [TestCase(SourceKind.BreakingReadOnlyList)] + public void TrySingleWithSingleton(SourceKind kind) + { + var source = new int?[] { 10 }.ToSourceKind(kind); + + var (cardinality, value) = source.TrySingle("zero", "one", "many"); + + Assert.That(cardinality, Is.EqualTo("one")); + Assert.That(value, Is.EqualTo(10)); + } + + [TestCaseSource(nameof(SingletonCollectionTestCases))] + public void TrySingleWithSingletonCollection(IEnumerable source, T result) + { + var (cardinality, value) = source.TrySingle("zero", "one", "many"); + + Assert.That(cardinality, Is.EqualTo("one")); + Assert.That(value, Is.EqualTo(result)); + } + + static readonly ITestCaseData[] SingletonCollectionTestCases = + { + new TestCaseData(new BreakingSingleElementCollection(10), 10), + new TestCaseData(new BreakingSingleElementReadOnlyCollection(20), 20) + }; + + class BreakingSingleElementCollectionBase : IEnumerable + { + readonly T _element; + + protected BreakingSingleElementCollectionBase(T element) => _element = element; + + public int Count => 1; + + public IEnumerator GetEnumerator() + { + yield return _element; + throw new Exception($"{nameof(MoreEnumerable.TrySingle)} should not have attempted to consume a second element."); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + sealed class BreakingSingleElementCollection : + BreakingSingleElementCollectionBase, ICollection + { + public BreakingSingleElementCollection(T element) : base(element) {} + + public void Add(T item) => throw new NotImplementedException(); + public void Clear() => throw new NotImplementedException(); + public bool Contains(T item) => throw new NotImplementedException(); + public void CopyTo(T[] array, int arrayIndex) => throw new NotImplementedException(); + public bool Remove(T item) => throw new NotImplementedException(); + public bool IsReadOnly => true; + } + + sealed class BreakingSingleElementReadOnlyCollection : + BreakingSingleElementCollectionBase, IReadOnlyCollection + { + public BreakingSingleElementReadOnlyCollection(T element) : base(element) {} + } + + [TestCase(SourceKind.Sequence)] + [TestCase(SourceKind.BreakingList)] + [TestCase(SourceKind.BreakingReadOnlyList)] + [TestCase(SourceKind.BreakingCollection)] + [TestCase(SourceKind.BreakingReadOnlyCollection)] + public void TrySingleWithMoreThanOne(SourceKind kind) + { + var source = new int?[] { 10, 20 }.ToSourceKind(kind); + + var (cardinality, value) = source.TrySingle("zero", "one", "many"); + + Assert.That(cardinality, Is.EqualTo("many")); + Assert.That(value, Is.Null); + } + + [Test] + public void TrySingleDoesNotConsumeMoreThanTwoElementsFromTheSequence() + { + static IEnumerable TestSequence() + { + yield return 1; + yield return 2; + throw new Exception(nameof(MoreEnumerable.TrySingle) + " should not have attempted to consume a third element."); + } + + var (cardinality, value) = TestSequence().TrySingle("zero", "one", "many"); + + Assert.That(cardinality, Is.EqualTo("many")); + Assert.That(value, Is.EqualTo(0)); + } + + [TestCase(0, "zero")] + [TestCase(1, "one")] + [TestCase(2, "many")] + public void TrySingleEnumeratesOnceOnlyAndDisposes(int numberOfElements, string expectedCardinality) + { + using var seq = Enumerable.Range(1, numberOfElements).AsTestingSequence(); + var (cardinality, _) = seq.TrySingle("zero", "one", "many"); + Assert.That(cardinality, Is.EqualTo(expectedCardinality)); + } + } +} diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index bc072f509..9c0485718 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -6562,6 +6562,88 @@ public static IEnumerable> Transpose(this IEnumerableTrySingle extension. + + [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] + public static partial class TrySingleExtension + { + /// + /// Returns a tuple with the cardinality of the sequence and the + /// single element in the sequence if it contains exactly one element. + /// similar to . + /// + /// The source sequence. + /// + /// The value that is returned in the tuple if the sequence has zero + /// elements. + /// + /// The value that is returned in the tuple if the sequence has a + /// single element only. + /// + /// The value that is returned in the tuple if the sequence has two or + /// more elements. + /// + /// The type of the elements of . + /// + /// The type that expresses cardinality. + /// + /// A tuple containing the identified + /// and either the single value of in the sequence + /// or its default value. + /// + /// This operator uses immediate execution, but never consumes more + /// than two elements from the sequence. + /// + + public static (TCardinality Cardinality, T Value) + TrySingle(this IEnumerable source, + TCardinality zero, TCardinality one, TCardinality many) => MoreEnumerable. TrySingle(source, zero, one, many); + + /// + /// Returns a result projected from the the cardinality of the sequence + /// and the single element in the sequence if it contains exactly one + /// element. + /// + /// The source sequence. + /// + /// The value that is passed as the first argument to + /// if the sequence has zero + /// elements. + /// + /// The value that is passed as the first argument to + /// if the sequence has a + /// single element only. + /// + /// The value that is passed as the first argument to + /// if the sequence has two or + /// more elements. + /// + /// A function that receives the cardinality and, if the + /// sequence has just one element, the value of that element as + /// argument and projects a resulting value of type + /// . + /// + /// The type of the elements of . + /// + /// The type that expresses cardinality. + /// + /// The type of the result value returned by the + /// function. + /// + /// The value returned by . + /// + /// + /// This operator uses immediate execution, but never consumes more + /// than two elements from the sequence. + /// + + public static TResult TrySingle(this IEnumerable source, + TCardinality zero, TCardinality one, TCardinality many, + Func resultSelector) + => MoreEnumerable.TrySingle(source, zero, one, many, resultSelector); + + } + /// Window extension. [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 4b149f227..551c30fc1 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -101,6 +101,7 @@ - Transpose - TraverseBreadthFirst - TraverseDepthFirst + - TrySingle - Unfold - Window - WindowLeft diff --git a/MoreLinq/TrySingle.cs b/MoreLinq/TrySingle.cs new file mode 100644 index 000000000..24bfe35df --- /dev/null +++ b/MoreLinq/TrySingle.cs @@ -0,0 +1,132 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2019 James Webster. 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; + + partial class MoreEnumerable + { + /// + /// Returns a tuple with the cardinality of the sequence and the + /// single element in the sequence if it contains exactly one element. + /// similar to . + /// + /// The source sequence. + /// + /// The value that is returned in the tuple if the sequence has zero + /// elements. + /// + /// The value that is returned in the tuple if the sequence has a + /// single element only. + /// + /// The value that is returned in the tuple if the sequence has two or + /// more elements. + /// + /// The type of the elements of . + /// + /// The type that expresses cardinality. + /// + /// A tuple containing the identified + /// and either the single value of in the sequence + /// or its default value. + /// + /// This operator uses immediate execution, but never consumes more + /// than two elements from the sequence. + /// + + public static (TCardinality Cardinality, T Value) + TrySingle(this IEnumerable source, + TCardinality zero, TCardinality one, TCardinality many) => + TrySingle(source, zero, one, many, ValueTuple.Create); + + /// + /// Returns a result projected from the the cardinality of the sequence + /// and the single element in the sequence if it contains exactly one + /// element. + /// + /// The source sequence. + /// + /// The value that is passed as the first argument to + /// if the sequence has zero + /// elements. + /// + /// The value that is passed as the first argument to + /// if the sequence has a + /// single element only. + /// + /// The value that is passed as the first argument to + /// if the sequence has two or + /// more elements. + /// + /// A function that receives the cardinality and, if the + /// sequence has just one element, the value of that element as + /// argument and projects a resulting value of type + /// . + /// + /// The type of the elements of . + /// + /// The type that expresses cardinality. + /// + /// The type of the result value returned by the + /// function. + /// + /// The value returned by . + /// + /// + /// This operator uses immediate execution, but never consumes more + /// than two elements from the sequence. + /// + + public static TResult TrySingle(this IEnumerable source, + TCardinality zero, TCardinality one, TCardinality many, + Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + switch (source.TryGetCollectionCount()) + { + case 0: + return resultSelector(zero, default); + case 1: + { + var item = source switch + { + IReadOnlyList list => list[0], + IList list => list[0], + _ => source.First() + }; + return resultSelector(one, item); + } + case int _: + return resultSelector(many, default); + default: + { + using var e = source.GetEnumerator(); + if (!e.MoveNext()) + return resultSelector(zero, default); + var current = e.Current; + return !e.MoveNext() ? resultSelector(one, current) + : resultSelector(many, default); + } + } + } + } +} diff --git a/README.md b/README.md index 59694ffb3..d6923b81d 100644 --- a/README.md +++ b/README.md @@ -672,6 +672,14 @@ Traces the elements of a source sequence for diagnostics. This method has 3 overloads. +### TrySingle + +Returns the only element of a sequence that has just one element. If the +sequence has zero or multiple elements, then returns a user-defined value +that indicates the cardinality of the result sequence. + +This method has 2 overloads. + ### Unfold Returns a sequence generated by applying a state to the generator function, From 418806b4aadac31506b2b9435129a9e701805c62 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 29 Oct 2019 09:33:13 +0000 Subject: [PATCH 29/58] Optimize Batch for collection fitting a single bucket This is a squashed merge of PR #623. --- MoreLinq.Test/BatchTest.cs | 30 +++++++++++++ MoreLinq.Test/MoreLinq.Test.csproj | 1 + MoreLinq.Test/ReadOnlyCollection.cs | 41 +++++++++++++++++ MoreLinq/Batch.cs | 69 ++++++++++++++++++++--------- 4 files changed, 120 insertions(+), 21 deletions(-) create mode 100644 MoreLinq.Test/ReadOnlyCollection.cs diff --git a/MoreLinq.Test/BatchTest.cs b/MoreLinq.Test/BatchTest.cs index f3aa6ec4e..09cdadbae 100644 --- a/MoreLinq.Test/BatchTest.cs +++ b/MoreLinq.Test/BatchTest.cs @@ -87,5 +87,35 @@ public void BatchIsLazy() { new BreakingSequence().Batch(1); } + + [TestCase(SourceKind.BreakingCollection , 0)] + [TestCase(SourceKind.BreakingList , 0)] + [TestCase(SourceKind.BreakingReadOnlyList, 0)] + [TestCase(SourceKind.BreakingCollection , 1)] + [TestCase(SourceKind.BreakingList , 1)] + [TestCase(SourceKind.BreakingReadOnlyList, 1)] + [TestCase(SourceKind.BreakingCollection , 2)] + [TestCase(SourceKind.BreakingList , 2)] + [TestCase(SourceKind.BreakingReadOnlyList, 2)] + public void BatchCollectionSmallerThanSize(SourceKind kind, int oversize) + { + var xs = new[] { 1, 2, 3, 4, 5 }; + var result = xs.ToSourceKind(kind).Batch(xs.Length + oversize); + using var reader = result.Read(); + reader.Read().AssertSequenceEqual(1, 2, 3, 4, 5); + reader.ReadEnd(); + } + + [TestCase(0)] + [TestCase(1)] + [TestCase(2)] + public void BatchReadOnlyCollectionSmallerThanSize(int oversize) + { + var collection = ReadOnlyCollection.From(1, 2, 3, 4, 5); + var result = collection.Batch(collection.Count * 2); + using var reader = result.Read(); + reader.Read().AssertSequenceEqual(1, 2, 3, 4, 5); + reader.ReadEnd(); + } } } diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 852f38af2..1dc81cafe 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -62,6 +62,7 @@ + diff --git a/MoreLinq.Test/ReadOnlyCollection.cs b/MoreLinq.Test/ReadOnlyCollection.cs new file mode 100644 index 000000000..23da7f290 --- /dev/null +++ b/MoreLinq.Test/ReadOnlyCollection.cs @@ -0,0 +1,41 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2019 Atif Aziz. 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; + using System.Collections.Generic; + + static class ReadOnlyCollection + { + public static IReadOnlyCollection From(params T[] items) => + new ListCollection(items); + + sealed class ListCollection : IReadOnlyCollection + where TList : IList + { + readonly TList _list; + + public ListCollection(TList list) => _list = list; + + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public int Count => _list.Count; + } + } +} diff --git a/MoreLinq/Batch.cs b/MoreLinq/Batch.cs index 9dc95fa9b..e1d9a060b 100644 --- a/MoreLinq/Batch.cs +++ b/MoreLinq/Batch.cs @@ -58,37 +58,64 @@ public static IEnumerable Batch(this IEnumerable _() + switch (source) { - TSource[] bucket = null; - var count = 0; - - foreach (var item in source) + case ICollection collection when collection.Count <= size: + { + return _(); IEnumerable _() + { + var bucket = new TSource[collection.Count]; + collection.CopyTo(bucket, 0); + yield return resultSelector(bucket); + } + } + case IReadOnlyList list when list.Count <= size: { - if (bucket == null) + return _(); IEnumerable _() { - bucket = new TSource[size]; + var bucket = new TSource[list.Count]; + for (var i = 0; i < list.Count; i++) + bucket[i] = list[i]; + yield return resultSelector(bucket); } + } + case IReadOnlyCollection collection when collection.Count <= size: + { + return Batch(collection.Count); + } + default: + { + return Batch(size); + } - bucket[count++] = item; + IEnumerable Batch(int size) + { + TSource[] bucket = null; + var count = 0; - // The bucket is fully buffered before it's yielded - if (count != size) + foreach (var item in source) { - continue; - } + if (bucket == null) + bucket = new TSource[size]; - yield return resultSelector(bucket); + bucket[count++] = item; - bucket = null; - count = 0; - } + // The bucket is fully buffered before it's yielded + if (count != size) + continue; - // Return the last bucket with all remaining elements - if (bucket != null && count > 0) - { - Array.Resize(ref bucket, count); - yield return resultSelector(bucket); + yield return resultSelector(bucket); + + bucket = null; + count = 0; + } + + // Return the last bucket with all remaining elements + if (bucket != null && count > 0) + { + Array.Resize(ref bucket, count); + yield return resultSelector(bucket); + } } } } From bbc3a06c310e2d256227d874db7e2f3b65f359d8 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 29 Oct 2019 14:57:48 +0100 Subject: [PATCH 30/58] Fix Batch doc (only buckets are streamed) --- MoreLinq/Batch.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MoreLinq/Batch.cs b/MoreLinq/Batch.cs index e1d9a060b..da126754d 100644 --- a/MoreLinq/Batch.cs +++ b/MoreLinq/Batch.cs @@ -30,7 +30,8 @@ static partial class MoreEnumerable /// Size of buckets. /// A sequence of equally sized buckets containing elements of the source collection. /// - /// This operator uses deferred execution and streams its results (buckets and bucket content). + /// This operator uses deferred execution and streams its results + /// (buckets are streamed but their content buffered). /// public static IEnumerable> Batch(this IEnumerable source, int size) @@ -48,7 +49,8 @@ public static IEnumerable> Batch(this IEnumerable< /// The projection to apply to each bucket. /// A sequence of projections on equally sized buckets containing elements of the source collection. /// - /// This operator uses deferred execution and streams its results (buckets and bucket content). + /// This operator uses deferred execution and streams its results + /// (buckets are streamed but their content buffered). /// public static IEnumerable Batch(this IEnumerable source, int size, From b9a534885d52eb82c7307d62d150143214632c09 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 29 Oct 2019 15:33:51 +0100 Subject: [PATCH 31/58] Improve Batch documentation [ci skip] Adds following remarks: - The last bucket can be smaller than the given size. - Memory could be exhausted if size is set to a very large value in the hope of forcing just a single bucket (#619). --- MoreLinq/Batch.cs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/MoreLinq/Batch.cs b/MoreLinq/Batch.cs index da126754d..c6db41d82 100644 --- a/MoreLinq/Batch.cs +++ b/MoreLinq/Batch.cs @@ -30,8 +30,21 @@ static partial class MoreEnumerable /// Size of buckets. /// A sequence of equally sized buckets containing elements of the source collection. /// + /// /// This operator uses deferred execution and streams its results - /// (buckets are streamed but their content buffered). + /// (buckets are streamed but their content buffered). + /// + /// When more than one bucket is streamed, all buckets except the last + /// is guaranteed to have elements. The last + /// bucket may be smaller depending on the remaining elements in the + /// sequence. + /// + /// Each bucket is pre-allocated to elements. + /// If is set to a very large value, e.g. + /// to effectively disable batching by just + /// hoping for a single bucket, then it can lead to memory exhaustion + /// (). + /// /// public static IEnumerable> Batch(this IEnumerable source, int size) @@ -48,10 +61,21 @@ public static IEnumerable> Batch(this IEnumerable< /// Size of buckets. /// The projection to apply to each bucket. /// A sequence of projections on equally sized buckets containing elements of the source collection. - /// + /// /// This operator uses deferred execution and streams its results - /// (buckets are streamed but their content buffered). - /// + /// (buckets are streamed but their content buffered). + /// + /// + /// When more than one bucket is streamed, all buckets except the last + /// is guaranteed to have elements. The last + /// bucket may be smaller depending on the remaining elements in the + /// sequence. + /// Each bucket is pre-allocated to elements. + /// If is set to a very large value, e.g. + /// to effectively disable batching by just + /// hoping for a single bucket, then it can lead to memory exhaustion + /// (). + /// public static IEnumerable Batch(this IEnumerable source, int size, Func, TResult> resultSelector) From 07b66ea05fec60c1032f6b26b714ac58342bdeaf Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 2 Nov 2019 15:27:04 +0100 Subject: [PATCH 32/58] Sync Batch wrapper extension doc --- MoreLinq/Extensions.g.cs | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 9c0485718..0b1211058 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -650,7 +650,21 @@ public static partial class BatchExtension /// Size of buckets. /// A sequence of equally sized buckets containing elements of the source collection. /// - /// This operator uses deferred execution and streams its results (buckets and bucket content). + /// + /// This operator uses deferred execution and streams its results + /// (buckets are streamed but their content buffered). + /// + /// When more than one bucket is streamed, all buckets except the last + /// is guaranteed to have elements. The last + /// bucket may be smaller depending on the remaining elements in the + /// sequence. + /// + /// Each bucket is pre-allocated to elements. + /// If is set to a very large value, e.g. + /// to effectively disable batching by just + /// hoping for a single bucket, then it can lead to memory exhaustion + /// (). + /// /// public static IEnumerable> Batch(this IEnumerable source, int size) @@ -665,9 +679,21 @@ public static IEnumerable> Batch(this IEnumerable< /// Size of buckets. /// The projection to apply to each bucket. /// A sequence of projections on equally sized buckets containing elements of the source collection. - /// - /// This operator uses deferred execution and streams its results (buckets and bucket content). - /// + /// + /// This operator uses deferred execution and streams its results + /// (buckets are streamed but their content buffered). + /// + /// + /// When more than one bucket is streamed, all buckets except the last + /// is guaranteed to have elements. The last + /// bucket may be smaller depending on the remaining elements in the + /// sequence. + /// Each bucket is pre-allocated to elements. + /// If is set to a very large value, e.g. + /// to effectively disable batching by just + /// hoping for a single bucket, then it can lead to memory exhaustion + /// (). + /// public static IEnumerable Batch(this IEnumerable source, int size, Func, TResult> resultSelector) From ce57548c166c9ad59c9c5f6712bbc42bb358ca50 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 2 Nov 2019 16:04:51 +0100 Subject: [PATCH 33/58] Remove unused imports [ci skip] --- MoreLinq.Test/AssertCountTest.cs | 1 - MoreLinq.Test/BreakingReadOnlyCollection.cs | 1 - MoreLinq.Test/RepeatTest.cs | 1 - MoreLinq.Test/SegmentTest.cs | 1 - MoreLinq/Choose.cs | 1 - MoreLinq/ScanRight.cs | 1 - MoreLinq/TagFirstLast.cs | 1 - 7 files changed, 7 deletions(-) diff --git a/MoreLinq.Test/AssertCountTest.cs b/MoreLinq.Test/AssertCountTest.cs index d4c4b1501..2a05912f8 100644 --- a/MoreLinq.Test/AssertCountTest.cs +++ b/MoreLinq.Test/AssertCountTest.cs @@ -18,7 +18,6 @@ namespace MoreLinq.Test { using System; - using System.Collections.Generic; using NUnit.Framework; [TestFixture] diff --git a/MoreLinq.Test/BreakingReadOnlyCollection.cs b/MoreLinq.Test/BreakingReadOnlyCollection.cs index 862186ada..a05e36607 100644 --- a/MoreLinq.Test/BreakingReadOnlyCollection.cs +++ b/MoreLinq.Test/BreakingReadOnlyCollection.cs @@ -17,7 +17,6 @@ namespace MoreLinq.Test { - using System; using System.Collections.Generic; class BreakingReadOnlyCollection : BreakingSequence, IReadOnlyCollection diff --git a/MoreLinq.Test/RepeatTest.cs b/MoreLinq.Test/RepeatTest.cs index 12b1ce1bc..7a3023348 100644 --- a/MoreLinq.Test/RepeatTest.cs +++ b/MoreLinq.Test/RepeatTest.cs @@ -1,6 +1,5 @@ namespace MoreLinq.Test { - using System.Collections.Generic; using NUnit.Framework; /// diff --git a/MoreLinq.Test/SegmentTest.cs b/MoreLinq.Test/SegmentTest.cs index 273f5876a..bfde0301e 100644 --- a/MoreLinq.Test/SegmentTest.cs +++ b/MoreLinq.Test/SegmentTest.cs @@ -1,6 +1,5 @@ namespace MoreLinq.Test { - using System; using NUnit.Framework; /// diff --git a/MoreLinq/Choose.cs b/MoreLinq/Choose.cs index 52822e584..bffbc72d9 100644 --- a/MoreLinq/Choose.cs +++ b/MoreLinq/Choose.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Linq; static partial class MoreEnumerable { diff --git a/MoreLinq/ScanRight.cs b/MoreLinq/ScanRight.cs index d515b7fa3..648aaa470 100644 --- a/MoreLinq/ScanRight.cs +++ b/MoreLinq/ScanRight.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Linq; static partial class MoreEnumerable { diff --git a/MoreLinq/TagFirstLast.cs b/MoreLinq/TagFirstLast.cs index f22d3fbbf..e37367582 100644 --- a/MoreLinq/TagFirstLast.cs +++ b/MoreLinq/TagFirstLast.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Linq; partial class MoreEnumerable { From 6fc7657956380481f8e0f15fc4d1a537a94f4b44 Mon Sep 17 00:00:00 2001 From: Mitch Bodmer Date: Wed, 6 Nov 2019 13:43:55 -0800 Subject: [PATCH 34/58] Add Return: x -> [x] This is a squashed merge of PR #636 that closes #615. --- MoreLinq.Test/ReturnTest.cs | 163 ++++++++++++++++++++++++++++++++++++ MoreLinq/MoreLinq.csproj | 1 + MoreLinq/Return.cs | 70 ++++++++++++++++ README.md | 4 + 4 files changed, 238 insertions(+) create mode 100644 MoreLinq.Test/ReturnTest.cs create mode 100644 MoreLinq/Return.cs diff --git a/MoreLinq.Test/ReturnTest.cs b/MoreLinq.Test/ReturnTest.cs new file mode 100644 index 000000000..0cf3f3ee9 --- /dev/null +++ b/MoreLinq.Test/ReturnTest.cs @@ -0,0 +1,163 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2019 Mitch Bodmer. 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 System.Collections.Generic; + using NUnit.Framework; + using NUnit.Framework.Interfaces; + + class ReturnTest + { + static class SomeSingleton + { + public static readonly object Item = new object(); + public static readonly IEnumerable Sequence = MoreEnumerable.Return(Item); + public static IList List => (IList)Sequence; + public static ICollection Collection => (ICollection)Sequence; + } + + static class NullSingleton + { + public static readonly IEnumerable Sequence = MoreEnumerable.Return(null); + public static IList List => (IList)Sequence; + } + + [Test] + public void TestResultingSequenceContainsSingle() + { + Assert.That(SomeSingleton.Sequence.Count(), Is.EqualTo(1)); + } + + [Test] + public void TestResultingSequenceContainsTheItemProvided() + { + Assert.That(SomeSingleton.Sequence, Has.Member(SomeSingleton.Item)); + } + + [Test] + public void TestResultingListHasCountOne() + { + Assert.That(SomeSingleton.List.Count, Is.EqualTo(1)); + } + + [Test] + public void TestContainsReturnsTrueWhenTheResultingSequenceContainsTheItemProvided() + { + Assert.That(SomeSingleton.Sequence.Contains(SomeSingleton.Item), Is.True); + } + + [Test] + public void TestContainsDoesNotThrowWhenTheItemContainedIsNull() + { + Assert.That(() => SomeSingleton.Sequence.Contains(null), Throws.Nothing); + } + + [Test] + public void TestContainsDoesNotThrowWhenTheItemProvidedIsNull() + { + Assert.That(() => NullSingleton.Sequence.Contains(new object()), Throws.Nothing); + } + + [Test] + public void TestIndexOfDoesNotThrowWhenTheItemProvidedIsNull() + { + Assert.That(() => NullSingleton.List.IndexOf(new object()), Throws.Nothing); + } + + [Test] + public void TestIndexOfDoesNotThrowWhenTheItemContainedIsNull() + { + Assert.That(() => SomeSingleton.List.IndexOf(null), Throws.Nothing); + } + + [Test] + public void TestCopyToSetsTheValueAtTheIndexToTheItemContained() + { + var first = new object(); + var third = new object(); + + var array = new[] + { + first, + new object(), + third + }; + + SomeSingleton.List.CopyTo(array, 1); + + Assert.That(array[0], Is.EqualTo(first)); + Assert.That(array[1], Is.EqualTo(SomeSingleton.Item)); + Assert.That(array[2], Is.EqualTo(third)); + } + + [Test] + public void TestResultingCollectionIsReadOnly() + { + Assert.That(SomeSingleton.Collection.IsReadOnly, Is.True); + } + + [Test] + public void TestResultingCollectionHasCountOne() + { + Assert.That(SomeSingleton.Collection.Count, Is.EqualTo(1)); + } + + [Test] + public void TestIndexZeroContainsTheItemProvided() + { + Assert.That(SomeSingleton.List[0], Is.EqualTo(SomeSingleton.Item)); + } + + [Test] + public void TestIndexOfTheItemProvidedIsZero() + { + Assert.That(SomeSingleton.List.IndexOf(SomeSingleton.Item), Is.EqualTo(0)); + } + + [Test] + public void TestIndexOfAnItemNotContainedIsNegativeOne() + { + Assert.That(SomeSingleton.List.IndexOf(new object()), Is.EqualTo(-1)); + } + + static IEnumerable UnsupportedActions(string testName) => + from ma in new (string MethodName, Action Action)[] + { + ("Add" , () => SomeSingleton.List.Add(new object())), + ("Clear" , () => SomeSingleton.Collection.Clear()), + ("Remove" , () => SomeSingleton.Collection.Remove(SomeSingleton.Item)), + ("RemoveAt", () => SomeSingleton.List.RemoveAt(0)), + ("Insert" , () => SomeSingleton.List.Insert(0, new object())), + ("Index" , () => SomeSingleton.List[0] = new object()), + } + select new TestCaseData(ma.Action).SetName($"{testName}({ma.MethodName})"); + + [TestCaseSource(nameof(UnsupportedActions), new object[] { nameof(TestUnsupportedMethodShouldThrow) })] + public void TestUnsupportedMethodShouldThrow(Action unsupportedAction) + { + Assert.That(() => unsupportedAction(), Throws.InstanceOf()); + } + + [Test] + public void TestIndexingPastZeroShouldThrow() + { + Assert.That(() => SomeSingleton.List[1], Throws.InstanceOf()); + } + } +} diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 551c30fc1..dbe55aa50 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -72,6 +72,7 @@ - Rank - RankBy - Repeat + - Return - RightJoin - RunLengthEncode - Scan diff --git a/MoreLinq/Return.cs b/MoreLinq/Return.cs new file mode 100644 index 000000000..a09a688c8 --- /dev/null +++ b/MoreLinq/Return.cs @@ -0,0 +1,70 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2019 Mitch Bodmer. 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; + using System.Collections.Generic; + + partial class MoreEnumerable + { + /// + /// Returns a single-element sequence containing the item provided. + /// + /// The type of the item. + /// The item to return in a sequence. + /// A sequence containing only . + + public static IEnumerable Return(T item) => new SingleElementList(item); + + sealed class SingleElementList : IList, IReadOnlyList + { + readonly T _item; + + public SingleElementList(T item) => _item = item; + + public int Count => 1; + public bool IsReadOnly => true; + + public T this[int index] + { + get => index == 0 ? _item : throw new ArgumentOutOfRangeException(); + set => throw ReadOnlyException(); + } + + public IEnumerator GetEnumerator() { yield return _item; } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public int IndexOf(T item) => Contains(item) ? 0 : -1; + public bool Contains(T item) => EqualityComparer.Default.Equals(_item, item); + + public void CopyTo(T[] array, int arrayIndex) => array[arrayIndex] = _item; + + // Following methods are unsupported as this is a read-only list. + + public void Add(T item) => throw ReadOnlyException(); + public void Clear() => throw ReadOnlyException(); + public bool Remove(T item) => throw ReadOnlyException(); + public void Insert(int index, T item) => throw ReadOnlyException(); + public void RemoveAt(int index) => throw ReadOnlyException(); + + static NotSupportedException ReadOnlyException() => + new NotSupportedException("Single element list is immutable."); + } + } +} diff --git a/README.md b/README.md index d6923b81d..29661bedf 100644 --- a/README.md +++ b/README.md @@ -490,6 +490,10 @@ Repeats the sequence indefinitely or a specific number of times. This method has 2 overloads. +### Return + +Returns a single-element sequence containing the item provided. + ### RightJoin Performs a right outer join between two sequences. From 2539d3ce0307cd84d662959338d698674704a8ac Mon Sep 17 00:00:00 2001 From: Orace Date: Sat, 9 Nov 2019 15:53:40 +0100 Subject: [PATCH 35/58] Fix Exclude & Lead to dispose enumerator This is a squashed merge or PR #673 that closes #649. It fixes mistakes made in commit "Replace using statements with declarations" (hash cdc6f76f4bf348e860b9eaa072279ee2956d6de7) as part of PR #622. --- MoreLinq/Exclude.cs | 2 +- MoreLinq/Lead.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MoreLinq/Exclude.cs b/MoreLinq/Exclude.cs index ae65ca6a7..6572753ca 100644 --- a/MoreLinq/Exclude.cs +++ b/MoreLinq/Exclude.cs @@ -45,7 +45,7 @@ public static IEnumerable Exclude(this IEnumerable sequence, int startI { var index = -1; var endIndex = startIndex + count; - var iter = sequence.GetEnumerator(); + using var iter = sequence.GetEnumerator(); // yield the first part of the sequence while (iter.MoveNext() && ++index < startIndex) yield return iter.Current; diff --git a/MoreLinq/Lead.cs b/MoreLinq/Lead.cs index dc5a9c391..a354aea30 100644 --- a/MoreLinq/Lead.cs +++ b/MoreLinq/Lead.cs @@ -65,7 +65,7 @@ public static IEnumerable Lead(this IEnumerable _() { var leadQueue = new Queue(offset); - var iter = source.GetEnumerator(); + using var iter = source.GetEnumerator(); bool hasMore; // first, prefetch and populate the lead queue with the next step of From c3516e335230f2a598ea0ab8f570b3d1b45b0970 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sat, 9 Nov 2019 19:02:58 +0000 Subject: [PATCH 36/58] Drop targets for end-of-life .NET Core versions (#675) - .NET Core 2.0 end of support was 2018-10-01 - .NET Core 1.1 end of support was 2019-06-27 - .NET Core 1.0 end of support was 2019-06-27 --- .travis.yml | 11 ----------- MoreLinq.Test/MoreLinq.Test.csproj | 2 +- test.cmd | 4 ---- test.sh | 2 +- 4 files changed, 2 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5c58dc95b..7bafdbf6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,9 +17,6 @@ addons: - sourceline: 'deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-xenial-prod xenial main' key_url: 'https://packages.microsoft.com/keys/microsoft.asc' packages: - - dotnet-hostfxr-1.0.1 - - dotnet-sharedframework-microsoft.netcore.app-1.0.5 - - dotnet-runtime-2.0.9 - dotnet-runtime-2.1 before_install: @@ -27,12 +24,6 @@ before_install: if [ "$TRAVIS_OS_NAME" == "osx" ] || [ `uname` == "Darwin" ]; then # Handle too many files on OS X ulimit -n 4096 - # Install dotnet core 1 sdk - wget --retry-connrefused --waitretry=1 -O /tmp/dn1.pkg 'https://download.microsoft.com/download/B/9/F/B9F1AF57-C14A-4670-9973-CDF47209B5BF/dotnet-dev-osx-x64.1.0.4.pkg' - sudo installer -pkg /tmp/dn1.pkg -target / - # Install dotnet core 2.0 runtime - wget --retry-connrefused --waitretry=1 -O /tmp/dn2.pkg 'https://download.microsoft.com/download/3/a/3/3a3bda26-560d-4d8e-922e-6f6bc4553a84/dotnet-runtime-2.0.9-osx-x64.pkg' - sudo installer -pkg /tmp/dn2.pkg -target / # Install dotnet core 2.1 runtime wget --retry-connrefused --waitretry=1 -O /tmp/dn21.pkg 'https://download.visualstudio.microsoft.com/download/pr/9314da31-774c-4d2b-8743-998f2a21f5ab/bc918ca05ab6b650f2991b205c04f623/dotnet-runtime-2.1.13-osx-x64.pkg' sudo installer -pkg /tmp/dn21.pkg -target / @@ -55,8 +46,6 @@ script: exit 1 fi - ./build.sh $CONFIGURATION - - dotnet exec MoreLinq.Test/bin/$CONFIGURATION/netcoreapp1.0/MoreLinq.Test.dll - - dotnet exec MoreLinq.Test/bin/$CONFIGURATION/netcoreapp2.0/MoreLinq.Test.dll - dotnet exec MoreLinq.Test/bin/$CONFIGURATION/netcoreapp2.1/MoreLinq.Test.dll - dotnet exec MoreLinq.Test/bin/$CONFIGURATION/netcoreapp3.0/MoreLinq.Test.dll - mono MoreLinq.Test/bin/$CONFIGURATION/net451/MoreLinq.Test.exe diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 1dc81cafe..768af1e69 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -2,7 +2,7 @@ MoreLinq.Test - netcoreapp1.0;netcoreapp2.0;netcoreapp2.1;netcoreapp3.0;net451 + netcoreapp2.1;netcoreapp3.0;net451 true portable MoreLinq.Test diff --git a/test.cmd b/test.cmd index ab4250205..2fafd3c21 100644 --- a/test.cmd +++ b/test.cmd @@ -7,10 +7,6 @@ goto :EOF :main setlocal call build ^ - && call :test netcoreapp1.0 Debug ^ - && call :test netcoreapp1.0 Release ^ - && call :test netcoreapp2.0 Debug ^ - && call :test netcoreapp2.0 Release ^ && call :test netcoreapp2.1 Debug ^ && call :test netcoreapp2.1 Release ^ && call :test netcoreapp3.0 Debug ^ diff --git a/test.sh b/test.sh index 33048cf14..4729e73d4 100755 --- a/test.sh +++ b/test.sh @@ -2,7 +2,7 @@ set -e cd "$(dirname "$0")" ./build.sh -for v in 1.0 2.0 2.1 3.0; do +for v in 2.1 3.0; do for c in Debug Release; do dotnet exec MoreLinq.Test/bin/$c/netcoreapp$v/MoreLinq.Test.dll done From 82caa0fb7d3b39d8d994179b867dead47e5c270a Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 10 Nov 2019 11:38:54 +0100 Subject: [PATCH 37/58] Clean-up conditions predicated on .NET Core 1.0 target This is a clean-up of residuals (thanks to Pierre Lando (aka @Orace) for pointing them out) from addressing issue #674 and belonged with commit "Drop targets for end-of-life .NET Core versions" (hash c3516e335230f2a598ea0ab8f570b3d1b45b0970) that was merged from PR #675. --- MoreLinq.Test/MoreLinq.Test.csproj | 17 ----------------- MoreLinq.Test/NullArgumentTest.cs | 6 ------ 2 files changed, 23 deletions(-) diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index 768af1e69..ea2da2dde 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -9,8 +9,6 @@ Exe true false - $(PackageTargetFallback);dotnet5.4;portable-net451+win8 - 1.0.4 false library @@ -19,10 +17,6 @@ 618 - - $(DefineConstants);NO_ASYNC - - @@ -69,11 +63,6 @@ - - - - - @@ -84,12 +73,6 @@ - - - 4.3.0 - - - diff --git a/MoreLinq.Test/NullArgumentTest.cs b/MoreLinq.Test/NullArgumentTest.cs index fb1f8541f..dc3115a6d 100644 --- a/MoreLinq.Test/NullArgumentTest.cs +++ b/MoreLinq.Test/NullArgumentTest.cs @@ -120,9 +120,7 @@ static bool CanBeNull(ParameterInfo parameter) nameof(MoreEnumerable.From) + ".function1", nameof(MoreEnumerable.From) + ".function2", nameof(MoreEnumerable.From) + ".function3", - #if !NETCOREAPP1_0 nameof(MoreEnumerable.ToDataTable) + ".expressions", - #endif nameof(MoreEnumerable.Trace) + ".format" }; @@ -215,8 +213,6 @@ public System.Linq.IOrderedEnumerable CreateOrderedEnumerable(Func : Enumerable, Experimental.IAwaitQuery { @@ -224,8 +220,6 @@ public class AwaitQuery : Enumerable, public Experimental.IAwaitQuery WithOptions(Experimental.AwaitQueryOptions options) => this; } - #endif - public class Comparer : IComparer { public int Compare(T x, T y) => -1; From 934fb39dd9be3b91138faa08ac7f54fafc608553 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 10 Nov 2019 16:24:35 +0100 Subject: [PATCH 38/58] Fix error propagation in T4 batch script [ci skip] --- MoreLinq/tt.cmd | 1 + 1 file changed, 1 insertion(+) diff --git a/MoreLinq/tt.cmd b/MoreLinq/tt.cmd index cbb7ac281..11ac52522 100644 --- a/MoreLinq/tt.cmd +++ b/MoreLinq/tt.cmd @@ -6,3 +6,4 @@ for /f "tokens=*" %%f in ('dir /s /b *.tt') do ( ) :end popd +exit /b %ERRORLEVEL% From ae1a86396abf9ee46912027eed13232e50d5fe22 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Sun, 10 Nov 2019 19:02:14 +0100 Subject: [PATCH 39/58] Revert "Fix error propagation in T4 batch script" [ci skip] This reverts commit 934fb39dd9be3b91138faa08ac7f54fafc608553. --- MoreLinq/tt.cmd | 1 - 1 file changed, 1 deletion(-) diff --git a/MoreLinq/tt.cmd b/MoreLinq/tt.cmd index 11ac52522..cbb7ac281 100644 --- a/MoreLinq/tt.cmd +++ b/MoreLinq/tt.cmd @@ -6,4 +6,3 @@ for /f "tokens=*" %%f in ('dir /s /b *.tt') do ( ) :end popd -exit /b %ERRORLEVEL% From b6886ea9646d7da52bdceaa8d7b743d7bb24ca7b Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 12 Nov 2019 22:19:40 +0100 Subject: [PATCH 40/58] Add EditorConfig URL as header comment --- .editorconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.editorconfig b/.editorconfig index 43ffc671d..0f87ef210 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,3 +1,5 @@ +# http://editorconfig.org/ + root = true [*] From 53b62c9ca93e3616031c82ae41a14957f5410cd4 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 12 Nov 2019 22:21:53 +0100 Subject: [PATCH 41/58] Update indent size & style in EditorConfig --- .editorconfig | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 0f87ef210..5cf109a14 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,11 +3,18 @@ root = true [*] +indent_style = space insert_final_newline = true trim_trailing_whitespace = true -[*.xml] -indent_style = space +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +[*.{sln}] +indent_style = tab + +[*.{json,yml}] +indent_size = 2 [*.{cs,tt}] charset = utf-8 From 62de1cb7ee63333bc59ad744b69281b62c8fa06c Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 12 Nov 2019 22:23:38 +0100 Subject: [PATCH 42/58] Set max line len for C# & T4 sources (EditorConfig) --- .editorconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/.editorconfig b/.editorconfig index 5cf109a14..73b9b61f3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -20,3 +20,4 @@ indent_size = 2 charset = utf-8 indent_style = space indent_size = 4 +max_line_length = 100 From fd32a8ad5754aea1288c277dedb927053e1c8368 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 12 Nov 2019 22:24:22 +0100 Subject: [PATCH 43/58] Add C# styles to EditorConfig [ci skip] --- .editorconfig | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.editorconfig b/.editorconfig index 73b9b61f3..8078f30fa 100644 --- a/.editorconfig +++ b/.editorconfig @@ -21,3 +21,36 @@ charset = utf-8 indent_style = space indent_size = 4 max_line_length = 100 + +[*.cs] +# Prefer "var" everywhere +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion + +# Prefer method-like constructs to have a block body +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Suggest more modern language features when available +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion + +# Spacing +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_declaration_parameter_list_parentheses = false + +# Wrapping +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true From b94870c95ce5c54a3086fb7ebb28ac7fe116a4ad Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 13 Nov 2019 14:44:47 +0100 Subject: [PATCH 44/58] Fix copyright holders of test files (#686) This is a squashed merge of PR #686 that closes #671. --- MoreLinq.Test/AcquireTest.cs | 2 +- MoreLinq.Test/AssertCountTest.cs | 2 +- MoreLinq.Test/AssertTest.cs | 2 +- MoreLinq.Test/AssertThrowsArgument.cs | 17 +++++++++++++++++ MoreLinq.Test/AtLeastTest.cs | 2 +- MoreLinq.Test/BreakingCollection.cs | 2 +- MoreLinq.Test/BreakingList.cs | 17 +++++++++++++++++ MoreLinq.Test/BreakingReadOnlyCollection.cs | 2 +- MoreLinq.Test/BreakingReadOnlyList.cs | 17 +++++++++++++++++ MoreLinq.Test/CartesianTest.cs | 17 +++++++++++++++++ MoreLinq.Test/Combinatorics.cs | 16 ++++++++++++++++ MoreLinq.Test/Comparer.cs | 17 +++++++++++++++++ MoreLinq.Test/EndsWithTest.cs | 2 +- MoreLinq.Test/Enumerable.cs | 17 +++++++++++++++++ MoreLinq.Test/EqualityComparer.cs | 17 +++++++++++++++++ MoreLinq.Test/EquiZipTest.cs | 2 +- MoreLinq.Test/ExcludeTest.cs | 17 +++++++++++++++++ MoreLinq.Test/FallbackIfEmptyTest.cs | 2 +- MoreLinq.Test/FillBackwardTest.cs | 2 +- MoreLinq.Test/FillForwardTest.cs | 2 +- MoreLinq.Test/FoldTest.cs | 2 +- MoreLinq.Test/FullGroupJoinTest.cs | 2 +- MoreLinq.Test/FullJoinTest.cs | 2 +- MoreLinq.Test/GroupAdjacentTest.cs | 2 +- MoreLinq.Test/IndexTest.cs | 2 +- MoreLinq.Test/InterleaveTest.cs | 17 +++++++++++++++++ MoreLinq.Test/KeyValuePair.cs | 2 +- MoreLinq.Test/LagTest.cs | 17 +++++++++++++++++ MoreLinq.Test/LeadTest.cs | 17 +++++++++++++++++ MoreLinq.Test/LeftJoinTest.cs | 2 +- MoreLinq.Test/NullArgumentTest.cs | 2 +- MoreLinq.Test/OrderByTest.cs | 17 +++++++++++++++++ MoreLinq.Test/PairwiseTest.cs | 2 +- MoreLinq.Test/PartialSortByTest.cs | 2 +- MoreLinq.Test/PartialSortTest.cs | 2 +- MoreLinq.Test/PartitionTest.cs | 2 +- MoreLinq.Test/PermutationsTest.cs | 17 +++++++++++++++++ MoreLinq.Test/Program.cs | 17 +++++++++++++++++ MoreLinq.Test/RandomSubsetTest.cs | 17 +++++++++++++++++ MoreLinq.Test/RandomTest.cs | 17 +++++++++++++++++ MoreLinq.Test/RankTest.cs | 17 +++++++++++++++++ MoreLinq.Test/RepeatTest.cs | 17 +++++++++++++++++ MoreLinq.Test/RightJoinTest.cs | 2 +- MoreLinq.Test/RunLengthEncodeTest.cs | 17 +++++++++++++++++ MoreLinq.Test/SegmentTest.cs | 17 +++++++++++++++++ MoreLinq.Test/ShuffleTest.cs | 17 +++++++++++++++++ MoreLinq.Test/SliceTest.cs | 17 +++++++++++++++++ MoreLinq.Test/SortedMergeTest.cs | 17 +++++++++++++++++ MoreLinq.Test/StartsWithTest.cs | 2 +- MoreLinq.Test/SubsetTest.cs | 17 +++++++++++++++++ MoreLinq.Test/TagFirstLastTest.cs | 2 +- MoreLinq.Test/TakeLastTest.cs | 2 +- MoreLinq.Test/TestingSequence.cs | 2 +- MoreLinq.Test/ToDataTableTest.cs | 2 +- MoreLinq.Test/ToDictionaryTest.cs | 2 +- MoreLinq.Test/ToLookupTest.cs | 2 +- MoreLinq.Test/WindowLeftTest.cs | 17 +++++++++++++++++ MoreLinq.Test/WindowRightTest.cs | 17 +++++++++++++++++ MoreLinq.Test/WindowTest.cs | 17 +++++++++++++++++ 59 files changed, 506 insertions(+), 31 deletions(-) diff --git a/MoreLinq.Test/AcquireTest.cs b/MoreLinq.Test/AcquireTest.cs index 119d4c3e4..4e687078b 100644 --- a/MoreLinq.Test/AcquireTest.cs +++ b/MoreLinq.Test/AcquireTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2012 Atif Aziz. 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. diff --git a/MoreLinq.Test/AssertCountTest.cs b/MoreLinq.Test/AssertCountTest.cs index 2a05912f8..ded8d4f25 100644 --- a/MoreLinq.Test/AssertCountTest.cs +++ b/MoreLinq.Test/AssertCountTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2009 Atif Aziz. 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. diff --git a/MoreLinq.Test/AssertTest.cs b/MoreLinq.Test/AssertTest.cs index b43d53a92..2d8cdf6a7 100644 --- a/MoreLinq.Test/AssertTest.cs +++ b/MoreLinq.Test/AssertTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2013 Atif Aziz. 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. diff --git a/MoreLinq.Test/AssertThrowsArgument.cs b/MoreLinq.Test/AssertThrowsArgument.cs index 01aa281a1..ade934394 100644 --- a/MoreLinq.Test/AssertThrowsArgument.cs +++ b/MoreLinq.Test/AssertThrowsArgument.cs @@ -1,3 +1,20 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2017 George Vovos. 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; diff --git a/MoreLinq.Test/AtLeastTest.cs b/MoreLinq.Test/AtLeastTest.cs index 3399d2b20..691b7ae99 100644 --- a/MoreLinq.Test/AtLeastTest.cs +++ b/MoreLinq.Test/AtLeastTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2015 "sholland". 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. diff --git a/MoreLinq.Test/BreakingCollection.cs b/MoreLinq.Test/BreakingCollection.cs index 9a390fd6a..a628424cf 100644 --- a/MoreLinq.Test/BreakingCollection.cs +++ b/MoreLinq.Test/BreakingCollection.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2018 Jonas Nyrup. 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. diff --git a/MoreLinq.Test/BreakingList.cs b/MoreLinq.Test/BreakingList.cs index a9b72e22e..e2cb32936 100644 --- a/MoreLinq.Test/BreakingList.cs +++ b/MoreLinq.Test/BreakingList.cs @@ -1,3 +1,20 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2018 Avi Levin. 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; diff --git a/MoreLinq.Test/BreakingReadOnlyCollection.cs b/MoreLinq.Test/BreakingReadOnlyCollection.cs index a05e36607..5a13ed7b6 100644 --- a/MoreLinq.Test/BreakingReadOnlyCollection.cs +++ b/MoreLinq.Test/BreakingReadOnlyCollection.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2018 Leandro F. Vieira. 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. diff --git a/MoreLinq.Test/BreakingReadOnlyList.cs b/MoreLinq.Test/BreakingReadOnlyList.cs index 216238b91..5fb25c193 100644 --- a/MoreLinq.Test/BreakingReadOnlyList.cs +++ b/MoreLinq.Test/BreakingReadOnlyList.cs @@ -1,3 +1,20 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2018 Avi Levin. 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; diff --git a/MoreLinq.Test/CartesianTest.cs b/MoreLinq.Test/CartesianTest.cs index bdbcea332..a7eb0e5c8 100644 --- a/MoreLinq.Test/CartesianTest.cs +++ b/MoreLinq.Test/CartesianTest.cs @@ -1,3 +1,20 @@ +#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 NUnit.Framework; diff --git a/MoreLinq.Test/Combinatorics.cs b/MoreLinq.Test/Combinatorics.cs index aa43fae06..0a5d3b903 100644 --- a/MoreLinq.Test/Combinatorics.cs +++ b/MoreLinq.Test/Combinatorics.cs @@ -1,3 +1,19 @@ +#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 { diff --git a/MoreLinq.Test/Comparer.cs b/MoreLinq.Test/Comparer.cs index ea9688004..75983a3d2 100644 --- a/MoreLinq.Test/Comparer.cs +++ b/MoreLinq.Test/Comparer.cs @@ -1,3 +1,20 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2017 Atif Aziz. 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; diff --git a/MoreLinq.Test/EndsWithTest.cs b/MoreLinq.Test/EndsWithTest.cs index 89334b2a9..81dece5bf 100644 --- a/MoreLinq.Test/EndsWithTest.cs +++ b/MoreLinq.Test/EndsWithTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2016 Andreas Gullberg Larsen. 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. diff --git a/MoreLinq.Test/Enumerable.cs b/MoreLinq.Test/Enumerable.cs index a01f088b7..0d6f188d3 100644 --- a/MoreLinq.Test/Enumerable.cs +++ b/MoreLinq.Test/Enumerable.cs @@ -1,3 +1,20 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2017 Atif Aziz. 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; diff --git a/MoreLinq.Test/EqualityComparer.cs b/MoreLinq.Test/EqualityComparer.cs index 5eb9ba1a4..f377a819c 100644 --- a/MoreLinq.Test/EqualityComparer.cs +++ b/MoreLinq.Test/EqualityComparer.cs @@ -1,3 +1,20 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2017 Atif Aziz. 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; diff --git a/MoreLinq.Test/EquiZipTest.cs b/MoreLinq.Test/EquiZipTest.cs index 8ca05f1d5..b7f9dde57 100644 --- a/MoreLinq.Test/EquiZipTest.cs +++ b/MoreLinq.Test/EquiZipTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2009 Atif Aziz. 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. diff --git a/MoreLinq.Test/ExcludeTest.cs b/MoreLinq.Test/ExcludeTest.cs index fbe71da82..61cd5241f 100644 --- a/MoreLinq.Test/ExcludeTest.cs +++ b/MoreLinq.Test/ExcludeTest.cs @@ -1,3 +1,20 @@ +#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 NUnit.Framework; diff --git a/MoreLinq.Test/FallbackIfEmptyTest.cs b/MoreLinq.Test/FallbackIfEmptyTest.cs index f5b4cdc56..b8e6b7516 100644 --- a/MoreLinq.Test/FallbackIfEmptyTest.cs +++ b/MoreLinq.Test/FallbackIfEmptyTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2016 Atif Aziz. 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. diff --git a/MoreLinq.Test/FillBackwardTest.cs b/MoreLinq.Test/FillBackwardTest.cs index c6e713412..19129b27b 100644 --- a/MoreLinq.Test/FillBackwardTest.cs +++ b/MoreLinq.Test/FillBackwardTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2017 Atif Aziz. 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. diff --git a/MoreLinq.Test/FillForwardTest.cs b/MoreLinq.Test/FillForwardTest.cs index 2a05e611e..8eb004942 100644 --- a/MoreLinq.Test/FillForwardTest.cs +++ b/MoreLinq.Test/FillForwardTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2017 Atif Aziz. 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. diff --git a/MoreLinq.Test/FoldTest.cs b/MoreLinq.Test/FoldTest.cs index 7824b6873..0c1c7185a 100644 --- a/MoreLinq.Test/FoldTest.cs +++ b/MoreLinq.Test/FoldTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2013 Atif Aziz. 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. diff --git a/MoreLinq.Test/FullGroupJoinTest.cs b/MoreLinq.Test/FullGroupJoinTest.cs index 61759d37b..adae88d99 100644 --- a/MoreLinq.Test/FullGroupJoinTest.cs +++ b/MoreLinq.Test/FullGroupJoinTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2015 Jonathan Skeet. All rights reserved. +// Copyright (c) 2015 Felipe Sateler. 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. diff --git a/MoreLinq.Test/FullJoinTest.cs b/MoreLinq.Test/FullJoinTest.cs index c460a3264..88d22b6f2 100644 --- a/MoreLinq.Test/FullJoinTest.cs +++ b/MoreLinq.Test/FullJoinTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2015 Jonathan Skeet. All rights reserved. +// Copyright (c) 2017 Atif Aziz. 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. diff --git a/MoreLinq.Test/GroupAdjacentTest.cs b/MoreLinq.Test/GroupAdjacentTest.cs index a68f099bb..1c4edb323 100644 --- a/MoreLinq.Test/GroupAdjacentTest.cs +++ b/MoreLinq.Test/GroupAdjacentTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2012 Atif Aziz. 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. diff --git a/MoreLinq.Test/IndexTest.cs b/MoreLinq.Test/IndexTest.cs index ecc200e1d..6b57ee2c3 100644 --- a/MoreLinq.Test/IndexTest.cs +++ b/MoreLinq.Test/IndexTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2012 Atif Aziz. 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. diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index b95e50517..598c3bd05 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -1,3 +1,20 @@ +#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; diff --git a/MoreLinq.Test/KeyValuePair.cs b/MoreLinq.Test/KeyValuePair.cs index a1f79affd..ee9a44dc0 100644 --- a/MoreLinq.Test/KeyValuePair.cs +++ b/MoreLinq.Test/KeyValuePair.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2012 Atif Aziz. 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. diff --git a/MoreLinq.Test/LagTest.cs b/MoreLinq.Test/LagTest.cs index 01147e60d..dd864b9f9 100644 --- a/MoreLinq.Test/LagTest.cs +++ b/MoreLinq.Test/LagTest.cs @@ -1,3 +1,20 @@ +#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 NUnit.Framework; diff --git a/MoreLinq.Test/LeadTest.cs b/MoreLinq.Test/LeadTest.cs index c2913cfd3..4953a3afd 100644 --- a/MoreLinq.Test/LeadTest.cs +++ b/MoreLinq.Test/LeadTest.cs @@ -1,3 +1,20 @@ +#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 NUnit.Framework; diff --git a/MoreLinq.Test/LeftJoinTest.cs b/MoreLinq.Test/LeftJoinTest.cs index 8884839e2..9bd5d7bdc 100644 --- a/MoreLinq.Test/LeftJoinTest.cs +++ b/MoreLinq.Test/LeftJoinTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2015 Jonathan Skeet. All rights reserved. +// Copyright (c) 2017 Atif Aziz. 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. diff --git a/MoreLinq.Test/NullArgumentTest.cs b/MoreLinq.Test/NullArgumentTest.cs index dc3115a6d..56f3eb8fc 100644 --- a/MoreLinq.Test/NullArgumentTest.cs +++ b/MoreLinq.Test/NullArgumentTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2015 Julian Lettner. 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. diff --git a/MoreLinq.Test/OrderByTest.cs b/MoreLinq.Test/OrderByTest.cs index 07753d5cc..7cc944c25 100644 --- a/MoreLinq.Test/OrderByTest.cs +++ b/MoreLinq.Test/OrderByTest.cs @@ -1,3 +1,20 @@ +#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 NUnit.Framework; diff --git a/MoreLinq.Test/PairwiseTest.cs b/MoreLinq.Test/PairwiseTest.cs index 7d84355f4..d810ea9fa 100644 --- a/MoreLinq.Test/PairwiseTest.cs +++ b/MoreLinq.Test/PairwiseTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2012 Atif Aziz. 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. diff --git a/MoreLinq.Test/PartialSortByTest.cs b/MoreLinq.Test/PartialSortByTest.cs index 51a2b8a03..9c83dcd7c 100644 --- a/MoreLinq.Test/PartialSortByTest.cs +++ b/MoreLinq.Test/PartialSortByTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2016 Atif Aziz. 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. diff --git a/MoreLinq.Test/PartialSortTest.cs b/MoreLinq.Test/PartialSortTest.cs index 16a906f15..04257a737 100644 --- a/MoreLinq.Test/PartialSortTest.cs +++ b/MoreLinq.Test/PartialSortTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2016 Atif Aziz. 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. diff --git a/MoreLinq.Test/PartitionTest.cs b/MoreLinq.Test/PartitionTest.cs index 727dff238..7617f90ab 100644 --- a/MoreLinq.Test/PartitionTest.cs +++ b/MoreLinq.Test/PartitionTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// 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. diff --git a/MoreLinq.Test/PermutationsTest.cs b/MoreLinq.Test/PermutationsTest.cs index cbb3eef8a..fe2897556 100644 --- a/MoreLinq.Test/PermutationsTest.cs +++ b/MoreLinq.Test/PermutationsTest.cs @@ -1,3 +1,20 @@ +#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.Collections.Generic; diff --git a/MoreLinq.Test/Program.cs b/MoreLinq.Test/Program.cs index b37944246..4044ef621 100644 --- a/MoreLinq.Test/Program.cs +++ b/MoreLinq.Test/Program.cs @@ -1,3 +1,20 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2017 Atif Aziz. 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; diff --git a/MoreLinq.Test/RandomSubsetTest.cs b/MoreLinq.Test/RandomSubsetTest.cs index 2081ba60f..50988c862 100644 --- a/MoreLinq.Test/RandomSubsetTest.cs +++ b/MoreLinq.Test/RandomSubsetTest.cs @@ -1,3 +1,20 @@ +#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; diff --git a/MoreLinq.Test/RandomTest.cs b/MoreLinq.Test/RandomTest.cs index e90ff4eef..5f2bafdf0 100644 --- a/MoreLinq.Test/RandomTest.cs +++ b/MoreLinq.Test/RandomTest.cs @@ -1,3 +1,20 @@ +#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; diff --git a/MoreLinq.Test/RankTest.cs b/MoreLinq.Test/RankTest.cs index a3e586af4..de3852782 100644 --- a/MoreLinq.Test/RankTest.cs +++ b/MoreLinq.Test/RankTest.cs @@ -1,3 +1,20 @@ +#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; diff --git a/MoreLinq.Test/RepeatTest.cs b/MoreLinq.Test/RepeatTest.cs index 7a3023348..46a810e6b 100644 --- a/MoreLinq.Test/RepeatTest.cs +++ b/MoreLinq.Test/RepeatTest.cs @@ -1,3 +1,20 @@ +#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 NUnit.Framework; diff --git a/MoreLinq.Test/RightJoinTest.cs b/MoreLinq.Test/RightJoinTest.cs index 00177d6bf..9d7076077 100644 --- a/MoreLinq.Test/RightJoinTest.cs +++ b/MoreLinq.Test/RightJoinTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2015 Jonathan Skeet. All rights reserved. +// Copyright (c) 2017 Atif Aziz. 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. diff --git a/MoreLinq.Test/RunLengthEncodeTest.cs b/MoreLinq.Test/RunLengthEncodeTest.cs index 614a6bd32..9ef44e32c 100644 --- a/MoreLinq.Test/RunLengthEncodeTest.cs +++ b/MoreLinq.Test/RunLengthEncodeTest.cs @@ -1,3 +1,20 @@ +#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; diff --git a/MoreLinq.Test/SegmentTest.cs b/MoreLinq.Test/SegmentTest.cs index bfde0301e..8d740dc7c 100644 --- a/MoreLinq.Test/SegmentTest.cs +++ b/MoreLinq.Test/SegmentTest.cs @@ -1,3 +1,20 @@ +#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 NUnit.Framework; diff --git a/MoreLinq.Test/ShuffleTest.cs b/MoreLinq.Test/ShuffleTest.cs index 2bd04e3a1..cd9769d86 100644 --- a/MoreLinq.Test/ShuffleTest.cs +++ b/MoreLinq.Test/ShuffleTest.cs @@ -1,3 +1,20 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2018 Leandro F. Vieira (leandromoh). 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; diff --git a/MoreLinq.Test/SliceTest.cs b/MoreLinq.Test/SliceTest.cs index 984da8811..18e80eeea 100644 --- a/MoreLinq.Test/SliceTest.cs +++ b/MoreLinq.Test/SliceTest.cs @@ -1,3 +1,20 @@ +#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 NUnit.Framework; diff --git a/MoreLinq.Test/SortedMergeTest.cs b/MoreLinq.Test/SortedMergeTest.cs index 061f57949..bc21204be 100644 --- a/MoreLinq.Test/SortedMergeTest.cs +++ b/MoreLinq.Test/SortedMergeTest.cs @@ -1,3 +1,20 @@ +#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; diff --git a/MoreLinq.Test/StartsWithTest.cs b/MoreLinq.Test/StartsWithTest.cs index 88c22de17..c4df100b5 100644 --- a/MoreLinq.Test/StartsWithTest.cs +++ b/MoreLinq.Test/StartsWithTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2016 Andreas Gullberg Larsen (angularsen). 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. diff --git a/MoreLinq.Test/SubsetTest.cs b/MoreLinq.Test/SubsetTest.cs index cc4725a2d..439631eaa 100644 --- a/MoreLinq.Test/SubsetTest.cs +++ b/MoreLinq.Test/SubsetTest.cs @@ -1,3 +1,20 @@ +#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; diff --git a/MoreLinq.Test/TagFirstLastTest.cs b/MoreLinq.Test/TagFirstLastTest.cs index b3017b6f9..0dcf9928c 100644 --- a/MoreLinq.Test/TagFirstLastTest.cs +++ b/MoreLinq.Test/TagFirstLastTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2013 Atif Aziz. 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. diff --git a/MoreLinq.Test/TakeLastTest.cs b/MoreLinq.Test/TakeLastTest.cs index 4c9e35660..16c23e633 100644 --- a/MoreLinq.Test/TakeLastTest.cs +++ b/MoreLinq.Test/TakeLastTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2009 Atif Aziz. 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. diff --git a/MoreLinq.Test/TestingSequence.cs b/MoreLinq.Test/TestingSequence.cs index 3f9f56298..cbafe5f0e 100644 --- a/MoreLinq.Test/TestingSequence.cs +++ b/MoreLinq.Test/TestingSequence.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2009 Atif Aziz. 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. diff --git a/MoreLinq.Test/ToDataTableTest.cs b/MoreLinq.Test/ToDataTableTest.cs index 10bf432e9..cfc95a224 100644 --- a/MoreLinq.Test/ToDataTableTest.cs +++ b/MoreLinq.Test/ToDataTableTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2010 Johannes Rudolph. 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. diff --git a/MoreLinq.Test/ToDictionaryTest.cs b/MoreLinq.Test/ToDictionaryTest.cs index 6b8bf11ff..e4a1f8ad6 100644 --- a/MoreLinq.Test/ToDictionaryTest.cs +++ b/MoreLinq.Test/ToDictionaryTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2017 Atif Aziz. 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. diff --git a/MoreLinq.Test/ToLookupTest.cs b/MoreLinq.Test/ToLookupTest.cs index 6bcaa389f..c7403d0b5 100644 --- a/MoreLinq.Test/ToLookupTest.cs +++ b/MoreLinq.Test/ToLookupTest.cs @@ -1,6 +1,6 @@ #region License and Terms // MoreLINQ - Extensions to LINQ to Objects -// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// Copyright (c) 2017 Atif Aziz. 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. diff --git a/MoreLinq.Test/WindowLeftTest.cs b/MoreLinq.Test/WindowLeftTest.cs index d85d8f11f..a0cc4410a 100644 --- a/MoreLinq.Test/WindowLeftTest.cs +++ b/MoreLinq.Test/WindowLeftTest.cs @@ -1,3 +1,20 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2018 Atif Aziz. 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; diff --git a/MoreLinq.Test/WindowRightTest.cs b/MoreLinq.Test/WindowRightTest.cs index 62951923c..d62c4fc57 100644 --- a/MoreLinq.Test/WindowRightTest.cs +++ b/MoreLinq.Test/WindowRightTest.cs @@ -1,3 +1,20 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2018 Atif Aziz. 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; diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index 83e71e6a4..7ed406dfe 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -1,3 +1,20 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2018 Atif Aziz. 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 NUnit.Framework; From 13c8c8355de0b6ea428d6106f6f0a92be03a8955 Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 13 Nov 2019 17:11:32 +0100 Subject: [PATCH 45/58] Fix SkipUntil to not call MoveNext past end of sequence This is a squashed merge of PR #666 that closes #664. --- MoreLinq.Test/SkipUntilTest.cs | 22 ++++++++++++++++++++++ MoreLinq/SkipUntil.cs | 13 +++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/MoreLinq.Test/SkipUntilTest.cs b/MoreLinq.Test/SkipUntilTest.cs index 066ffe643..a1c541b7b 100644 --- a/MoreLinq.Test/SkipUntilTest.cs +++ b/MoreLinq.Test/SkipUntilTest.cs @@ -18,6 +18,8 @@ namespace MoreLinq.Test { using NUnit.Framework; + using NUnit.Framework.Interfaces; + using System.Collections.Generic; [TestFixture] public class SkipUntilTest @@ -57,5 +59,25 @@ public void SkipUntilEvaluatesPredicateLazily() var sequence = Enumerable.Range(-2, 5).SkipUntil(x => 1 / x == -1); sequence.AssertSequenceEqual(0, 1, 2); } + + public static readonly IEnumerable TestData = + from e in new[] + { + new { Source = new int[0] , Min = 0, Expected = new int[0] }, // empty sequence + new { Source = new[] { 0 }, Min = 0, Expected = new int[0] }, // one-item sequence, predicate succeed + new { Source = new[] { 0 }, Min = 1, Expected = new int[0] }, // one-item sequence, predicate don't succeed + new { Source = new[] { 1, 2, 3 }, Min = 0, Expected = new[] { 2, 3 } }, // predicate succeed on first item + new { Source = new[] { 1, 2, 3 }, Min = 1, Expected = new[] { 2, 3 } }, + new { Source = new[] { 1, 2, 3 }, Min = 2, Expected = new[] { 3 } }, + new { Source = new[] { 1, 2, 3 }, Min = 3, Expected = new int[0] }, // predicate succeed on last item + new { Source = new[] { 1, 2, 3 }, Min = 4, Expected = new int[0] } // predicate never succeed + } + select new TestCaseData(e.Source, e.Min).Returns(e.Expected); + + [Test, TestCaseSource(nameof(TestData))] + public int[] TestSkipUntil(int[] source, int min) + { + return source.AsTestingSequence().SkipUntil(v => v >= min).ToArray(); + } } } diff --git a/MoreLinq/SkipUntil.cs b/MoreLinq/SkipUntil.cs index debbc04a8..7a738c35c 100644 --- a/MoreLinq/SkipUntil.cs +++ b/MoreLinq/SkipUntil.cs @@ -59,16 +59,17 @@ public static IEnumerable SkipUntil(this IEnumerable return _(); IEnumerable _() { - using var iterator = source.GetEnumerator(); + using var enumerator = source.GetEnumerator(); - while (iterator.MoveNext()) + do { - if (predicate(iterator.Current)) - break; + if (!enumerator.MoveNext()) + yield break; } + while (!predicate(enumerator.Current)); - while (iterator.MoveNext()) - yield return iterator.Current; + while (enumerator.MoveNext()) + yield return enumerator.Current; } } } From 5ad0a6528a6d7708189518396026fd0c05df65c1 Mon Sep 17 00:00:00 2001 From: Orace Date: Thu, 14 Nov 2019 13:57:59 +0100 Subject: [PATCH 46/58] Fix Interleave overload count in README --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 29661bedf..65340dc87 100644 --- a/README.md +++ b/README.md @@ -342,8 +342,6 @@ Inserts the elements of a sequence into another sequence at a specified index. Interleaves the elements of two or more sequences into a single sequence, skipping sequences as they are consumed. -This method has 2 overloads. - ### Lag Produces a projection of a sequence by evaluating pairs of elements separated From c8e81a94e0b5520d2c75f90e7db2e540e55204e6 Mon Sep 17 00:00:00 2001 From: Orace Date: Fri, 15 Nov 2019 15:59:16 +0100 Subject: [PATCH 47/58] Refresh method inventory in project description (#710) --- MoreLinq/MoreLinq.csproj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index dbe55aa50..3ee4f8020 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -29,6 +29,7 @@ - DistinctBy - EndsWith - EquiZip + - Evaluate - Exactly - ExceptBy - Exclude @@ -45,13 +46,14 @@ - GenerateByIndex - GroupAdjacent - Index + - IndexBy - Insert - Interleave - Lag - Lead - LeftJoin - MaxBy - - Memoize + - Memoize (EXPERIMENTAL) - MinBy - Move - OrderBy @@ -76,6 +78,7 @@ - RightJoin - RunLengthEncode - Scan + - ScanBy - ScanRight - Segment - Sequence From 9f8cf6b54ca5b16b7c01d76d29dffc4bdc35511c Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 15 Nov 2019 18:03:23 +0100 Subject: [PATCH 48/58] Fix more overload counts in README Applies to: - ToDelimitedString - Unfold - PartialSort - PartialSortBy - GroupAdjacent - Aggregate - Cartesian - FallbackIfEmpty This is a squashed merge of PR #712 that: - closes #703 (PR) - closes #704 (PR) - closes #705 (PR) - closes #705 (PR) - closes #706 (PR) - closes #707 (PR) - closes #708 (PR) - closes #709 (PR) --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 65340dc87..a218b1613 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ acquired till that point are disposed. Applies multiple accumulators sequentially in a single pass over a sequence. -This method has 8 overloads. +This method has 7 overloads. ### AggregateRight @@ -156,7 +156,7 @@ Returns the Cartesian product of two or more sequences by combining each element from the sequences and applying a user-defined projection to the set. -This method has 8 overloads. +This method has 7 overloads. ### Choose @@ -245,6 +245,8 @@ Excludes elements from a sequence starting at a given index Returns the elements of a sequence and falls back to another if the original sequence is empty. +This method has 6 overloads. + ### FillBackward Returns a sequence with each null reference or value in the source replaced @@ -309,7 +311,7 @@ Returns a sequence of values based on indexes Groups the adjacent elements of a sequence according to a specified key selector function. -This method has 4 overloads. +This method has 6 overloads. ### ~~Incremental~~ @@ -420,10 +422,14 @@ which is only returned as the predecessor of the second element Combines `OrderBy` (where element is key) and `Take` in a single operation. +This method has 4 overloads. + ### PartialSortBy Combines `OrderBy` and `Take` in a single operation. +This method has 4 overloads. + ### Partition Partitions a sequence by a predicate, or a grouping by Boolean keys or up to 3 @@ -631,7 +637,7 @@ This method has 4 overloads. Creates a delimited string from a sequence of values. The delimiter used depends on the current culture of the executing thread. -This method has 30 overloads. +This method has 15 overloads. ### ToDictionary @@ -688,8 +694,6 @@ Returns a sequence generated by applying a state to the generator function, and from its result, determines if the sequence should have a next element and its value, and the next state in the recursive call. -This method has 2 overloads. - ### Window Processes a sequence into a series of subsequences representing a windowed From 43a14ca96a8297cecbaaca3498f4d9ec8139d07d Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 15 Nov 2019 18:08:28 +0100 Subject: [PATCH 49/58] Revert "Fix more overload counts in README" This reverts commit 9f8cf6b54ca5b16b7c01d76d29dffc4bdc35511c. This is to purely to credit Pierre Lando in a future revert of this change as GitHub seems to have messed that up. --- README.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a218b1613..65340dc87 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ acquired till that point are disposed. Applies multiple accumulators sequentially in a single pass over a sequence. -This method has 7 overloads. +This method has 8 overloads. ### AggregateRight @@ -156,7 +156,7 @@ Returns the Cartesian product of two or more sequences by combining each element from the sequences and applying a user-defined projection to the set. -This method has 7 overloads. +This method has 8 overloads. ### Choose @@ -245,8 +245,6 @@ Excludes elements from a sequence starting at a given index Returns the elements of a sequence and falls back to another if the original sequence is empty. -This method has 6 overloads. - ### FillBackward Returns a sequence with each null reference or value in the source replaced @@ -311,7 +309,7 @@ Returns a sequence of values based on indexes Groups the adjacent elements of a sequence according to a specified key selector function. -This method has 6 overloads. +This method has 4 overloads. ### ~~Incremental~~ @@ -422,14 +420,10 @@ which is only returned as the predecessor of the second element Combines `OrderBy` (where element is key) and `Take` in a single operation. -This method has 4 overloads. - ### PartialSortBy Combines `OrderBy` and `Take` in a single operation. -This method has 4 overloads. - ### Partition Partitions a sequence by a predicate, or a grouping by Boolean keys or up to 3 @@ -637,7 +631,7 @@ This method has 4 overloads. Creates a delimited string from a sequence of values. The delimiter used depends on the current culture of the executing thread. -This method has 15 overloads. +This method has 30 overloads. ### ToDictionary @@ -694,6 +688,8 @@ Returns a sequence generated by applying a state to the generator function, and from its result, determines if the sequence should have a next element and its value, and the next state in the recursive call. +This method has 2 overloads. + ### Window Processes a sequence into a series of subsequences representing a windowed From 84e26259e5bdfe79977d6f19ee28ab1b44f20011 Mon Sep 17 00:00:00 2001 From: Orace Date: Fri, 15 Nov 2019 18:11:23 +0100 Subject: [PATCH 50/58] Revert "Revert "Fix more overload counts in README"" This reverts commit 43a14ca96a8297cecbaaca3498f4d9ec8139d07d. This is to properly credit Pierre Lando for "Fix more overload counts in README" that is commit 9f8cf6b54ca5b16b7c01d76d29dffc4bdc35511c. --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 65340dc87..a218b1613 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ acquired till that point are disposed. Applies multiple accumulators sequentially in a single pass over a sequence. -This method has 8 overloads. +This method has 7 overloads. ### AggregateRight @@ -156,7 +156,7 @@ Returns the Cartesian product of two or more sequences by combining each element from the sequences and applying a user-defined projection to the set. -This method has 8 overloads. +This method has 7 overloads. ### Choose @@ -245,6 +245,8 @@ Excludes elements from a sequence starting at a given index Returns the elements of a sequence and falls back to another if the original sequence is empty. +This method has 6 overloads. + ### FillBackward Returns a sequence with each null reference or value in the source replaced @@ -309,7 +311,7 @@ Returns a sequence of values based on indexes Groups the adjacent elements of a sequence according to a specified key selector function. -This method has 4 overloads. +This method has 6 overloads. ### ~~Incremental~~ @@ -420,10 +422,14 @@ which is only returned as the predecessor of the second element Combines `OrderBy` (where element is key) and `Take` in a single operation. +This method has 4 overloads. + ### PartialSortBy Combines `OrderBy` and `Take` in a single operation. +This method has 4 overloads. + ### Partition Partitions a sequence by a predicate, or a grouping by Boolean keys or up to 3 @@ -631,7 +637,7 @@ This method has 4 overloads. Creates a delimited string from a sequence of values. The delimiter used depends on the current culture of the executing thread. -This method has 30 overloads. +This method has 15 overloads. ### ToDictionary @@ -688,8 +694,6 @@ Returns a sequence generated by applying a state to the generator function, and from its result, determines if the sequence should have a next element and its value, and the next state in the recursive call. -This method has 2 overloads. - ### Window Processes a sequence into a series of subsequences representing a windowed From bf150619f465f75ca24c517a95e5db1644bd492d Mon Sep 17 00:00:00 2001 From: Orace Date: Tue, 19 Nov 2019 20:16:32 +0100 Subject: [PATCH 51/58] Fix Segment to not move past sequence end This is a squashed merge of PR #691 that closes #687. --- MoreLinq.Test/SegmentTest.cs | 26 ++++++++++++++++++++++++ MoreLinq/Segment.cs | 38 ++++++++++++++++-------------------- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/MoreLinq.Test/SegmentTest.cs b/MoreLinq.Test/SegmentTest.cs index 8d740dc7c..a9b786568 100644 --- a/MoreLinq.Test/SegmentTest.cs +++ b/MoreLinq.Test/SegmentTest.cs @@ -17,6 +17,8 @@ namespace MoreLinq.Test { + using System.Collections.Generic; + using NUnit.Framework.Interfaces; using NUnit.Framework; /// @@ -146,5 +148,29 @@ public void VerifyCanSegmentByPrevious() Assert.AreEqual(sequence.Distinct().Count(), result.Count()); Assert.IsTrue(result.All(s => s.Count() == repCount)); } + + static IEnumerable Seq(params T[] values) => values; + + public static readonly IEnumerable TestData = + from e in new[] + { + // input sequence is empty + new { Source = Seq(), Expected = Seq>() }, + // input sequence contains only new segment start + new { Source = Seq(0, 3, 6), Expected = Seq(Seq(0), Seq(3), Seq(6)) }, + // input sequence do not contains new segment start + new { Source = Seq(1, 2, 4, 5), Expected = Seq(Seq(1, 2, 4, 5)) }, + // input sequence start with a segment start + new { Source = Seq(0, 1, 2, 3, 4, 5), Expected = Seq(Seq(0, 1, 2), Seq(3, 4, 5)) }, + // input sequence do not start with a segment start + new { Source = Seq(1, 2, 3, 4, 5), Expected = Seq(Seq(1, 2), Seq(3, 4, 5)) } + } + select new TestCaseData(e.Source).Returns(e.Expected); + + [Test, TestCaseSource(nameof(TestData))] + public IEnumerable> TestSegment(IEnumerable source) + { + return source.AsTestingSequence().Segment(v => v % 3 == 0); + } } } diff --git a/MoreLinq/Segment.cs b/MoreLinq/Segment.cs index 198d8d076..283773021 100644 --- a/MoreLinq/Segment.cs +++ b/MoreLinq/Segment.cs @@ -76,44 +76,40 @@ public static IEnumerable> Segment(this IEnumerable source, return _(); IEnumerable> _() { - var index = -1; using var iter = source.GetEnumerator(); - var segment = new List(); - var prevItem = default(T); + // early break + if (!iter.MoveNext()) + yield break; // ensure that the first item is always part // of the first segment. This is an intentional // behavior. Segmentation always begins with // the second element in the sequence. - if (iter.MoveNext()) - { - ++index; - segment.Add(iter.Current); - prevItem = iter.Current; - } + var prevItem = iter.Current; + var segment = new List { iter.Current }; - while (iter.MoveNext()) + for (var index = 1; iter.MoveNext(); index++) { - ++index; // check if the item represents the start of a new segment - var isNewSegment = newSegmentPredicate(iter.Current, prevItem, index); - prevItem = iter.Current; + if (newSegmentPredicate(iter.Current, prevItem, index)) + { + // yield the completed segment + yield return segment; - if (!isNewSegment) + // start a new segment + segment = new List { iter.Current }; + } + else { // if not a new segment, append and continue segment.Add(iter.Current); - continue; } - yield return segment; // yield the completed segment - // start a new segment... - segment = new List { iter.Current }; + prevItem = iter.Current; } - // handle the case of the sequence ending before new segment is detected - if (segment.Count > 0) - yield return segment; + + yield return segment; } } } From 50b842d7bab630cc63f5196d79ccedfa1a4e5699 Mon Sep 17 00:00:00 2001 From: Orace Date: Wed, 20 Nov 2019 16:23:29 +0100 Subject: [PATCH 52/58] Return isolated windows from Window* methods This is a squashed merge of PR #655 that closes #652. --- MoreLinq.Test/WindowLeftTest.cs | 45 ++++++++++++++++++++++++++++++ MoreLinq.Test/WindowRightTest.cs | 45 ++++++++++++++++++++++++++++++ MoreLinq.Test/WindowTest.cs | 47 +++++++++++++++++++++++++++++++- MoreLinq/Window.cs | 15 +++++----- MoreLinq/WindowLeft.cs | 9 ++++-- MoreLinq/WindowRight.cs | 5 +++- 6 files changed, 154 insertions(+), 12 deletions(-) diff --git a/MoreLinq.Test/WindowLeftTest.cs b/MoreLinq.Test/WindowLeftTest.cs index a0cc4410a..c63dc071c 100644 --- a/MoreLinq.Test/WindowLeftTest.cs +++ b/MoreLinq.Test/WindowLeftTest.cs @@ -29,6 +29,51 @@ public void WindowLeftIsLazy() new BreakingSequence().WindowLeft(1); } + [Test] + public void WindowModifiedBeforeMoveNextDoesNotAffectNextWindow() + { + 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 WindowModifiedAfterMoveNextDoesNotAffectNextWindow() + { + 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 WindowModifiedDoesNotAffectPreviousWindow() + { + 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] public void WindowLeftWithNegativeWindowSize() { diff --git a/MoreLinq.Test/WindowRightTest.cs b/MoreLinq.Test/WindowRightTest.cs index d62c4fc57..d5296e307 100644 --- a/MoreLinq.Test/WindowRightTest.cs +++ b/MoreLinq.Test/WindowRightTest.cs @@ -29,6 +29,51 @@ public void WindowRightIsLazy() new BreakingSequence().WindowRight(1); } + [Test] + public void WindowModifiedBeforeMoveNextDoesNotAffectNextWindow() + { + 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 WindowModifiedAfterMoveNextDoesNotAffectNextWindow() + { + 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 WindowModifiedDoesNotAffectPreviousWindow() + { + 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] public void WindowRightWithNegativeWindowSize() { diff --git a/MoreLinq.Test/WindowTest.cs b/MoreLinq.Test/WindowTest.cs index 7ed406dfe..2fc765be9 100644 --- a/MoreLinq.Test/WindowTest.cs +++ b/MoreLinq.Test/WindowTest.cs @@ -34,6 +34,51 @@ public void TestWindowIsLazy() new BreakingSequence().Window(1); } + [Test] + public void WindowModifiedBeforeMoveNextDoesNotAffectNextWindow() + { + 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 WindowModifiedAfterMoveNextDoesNotAffectNextWindow() + { + 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 WindowModifiedDoesNotAffectPreviousWindow() + { + 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)); + } + /// /// Verify that a negative window size results in an exception /// @@ -42,7 +87,7 @@ public void TestWindowNegativeWindowSizeException() { var sequence = Enumerable.Repeat(1, 10); - AssertThrowsArgument.OutOfRangeException("size",() => + AssertThrowsArgument.OutOfRangeException("size", () => sequence.Window(-5)); } diff --git a/MoreLinq/Window.cs b/MoreLinq/Window.cs index c377f90d3..a3c5375f6 100644 --- a/MoreLinq/Window.cs +++ b/MoreLinq/Window.cs @@ -52,21 +52,20 @@ 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 d883a75cba2075913b84746016cb758e93aed3fd Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 19 Nov 2019 20:26:23 +0100 Subject: [PATCH 53/58] Clean-up Segment vars & comments [ci skip] - Simpler variable names - Reuse current item variables - Layout comments for a terser layout --- MoreLinq/Segment.cs | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/MoreLinq/Segment.cs b/MoreLinq/Segment.cs index 283773021..42a9f9ce6 100644 --- a/MoreLinq/Segment.cs +++ b/MoreLinq/Segment.cs @@ -76,37 +76,33 @@ public static IEnumerable> Segment(this IEnumerable source, return _(); IEnumerable> _() { - using var iter = source.GetEnumerator(); + using var e = source.GetEnumerator(); - // early break - if (!iter.MoveNext()) + if (!e.MoveNext()) // break early (it's empty) yield break; - // ensure that the first item is always part - // of the first segment. This is an intentional - // behavior. Segmentation always begins with - // the second element in the sequence. - var prevItem = iter.Current; - var segment = new List { iter.Current }; + // Ensure that the first item is always part of the first + // segment. This is an intentional behavior. Segmentation always + // begins with the second element in the sequence. - for (var index = 1; iter.MoveNext(); index++) + var previous = e.Current; + var segment = new List { previous }; + + for (var index = 1; e.MoveNext(); index++) { - // check if the item represents the start of a new segment - if (newSegmentPredicate(iter.Current, prevItem, index)) - { - // yield the completed segment - yield return segment; + var current = e.Current; - // start a new segment - segment = new List { iter.Current }; + if (newSegmentPredicate(current, previous, index)) + { + yield return segment; // yield the completed segment + segment = new List { current }; // start a new segment } - else + else // not a new segment, append and continue { - // if not a new segment, append and continue - segment.Add(iter.Current); + segment.Add(current); } - prevItem = iter.Current; + previous = current; } yield return segment; From 9fc8486b9d8819690c9bc67fe41e74d4e1e2d765 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 22 Nov 2019 11:30:45 +0100 Subject: [PATCH 54/58] Use simple foreach loop in Iterleave's yielding loop --- MoreLinq/Interleave.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index cd19e19a6..c06a0129c 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -133,10 +133,8 @@ static IEnumerable Interleave(this IEnumerable sequence, ImbalancedInte if (shouldContinue) // only if all iterators could be advanced { // yield the values of each iterator's current position - for (var index = 0; index < iterCount; index++) - { - yield return iteratorList[index].Current; - } + foreach (var iterator in iteratorList) + yield return iterator.Current; } } } From 52f93f471b9e198675327d1c6b367ebaf6d7e82b Mon Sep 17 00:00:00 2001 From: Orace Date: Thu, 12 Dec 2019 11:25:56 +0100 Subject: [PATCH 55/58] Fix Interleave to prevent eager MoveNext calls This is a squashed merge of PR #696 that fixes #694. --- MoreLinq.Test/InterleaveTest.cs | 12 ++++ MoreLinq/Interleave.cs | 116 ++++++-------------------------- 2 files changed, 32 insertions(+), 96 deletions(-) diff --git a/MoreLinq.Test/InterleaveTest.cs b/MoreLinq.Test/InterleaveTest.cs index 598c3bd05..35c3bd863 100644 --- a/MoreLinq.Test/InterleaveTest.cs +++ b/MoreLinq.Test/InterleaveTest.cs @@ -35,6 +35,18 @@ public void TestInterleaveIsLazy() new BreakingSequence().Interleave(new BreakingSequence()); } + /// + /// Verify that interleaving do not call enumerators MoveNext method eagerly + /// + [Test] + public void TestInterleaveDoNoCallMoveNextEagerly() + { + var sequenceA = Enumerable.Range(1, 1); + var sequenceB = MoreEnumerable.From(() => throw new TestException()); + + sequenceA.Interleave(sequenceB).Take(1).Consume(); + } + /// /// Verify that interleaving disposes those enumerators that it managed /// to open successfully diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index c06a0129c..164fec136 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -19,7 +19,6 @@ namespace MoreLinq { using System; using System.Collections.Generic; - using System.Diagnostics; using System.Linq; public static partial class MoreEnumerable @@ -45,33 +44,6 @@ public static partial class MoreEnumerable /// A sequence of interleaved elements from all of the source sequences public static IEnumerable Interleave(this IEnumerable sequence, params IEnumerable[] otherSequences) - { - return Interleave(sequence, ImbalancedInterleaveStrategy.Skip, otherSequences); - } - - /// - /// Interleaves the elements of two or more sequences into a single sequence, applying the specified strategy when sequences are of unequal length - /// - /// - /// 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:
- /// { 1,2,3,1,2,3,1,2,3 } - /// ]]> - /// This operator behaves in a deferred and streaming manner.
- /// When sequences are of unequal length, this method will use the imbalance strategy specified to - /// decide how to continue interleaving the remaining sequences. See - /// for more information.
- /// The sequences are interleaved in the order that they appear in the - /// collection, with as the first sequence. - ///
- /// The type of the elements of the source sequences - /// The first sequence in the interleave group - /// Defines the behavior of the operator when sequences are of unequal length - /// The other sequences in the interleave group - /// A sequence of interleaved elements from all of the source sequences - - static IEnumerable Interleave(this IEnumerable sequence, ImbalancedInterleaveStrategy imbalanceStrategy, params IEnumerable[] otherSequences) { if (sequence == null) throw new ArgumentNullException(nameof(sequence)); if (otherSequences == null) throw new ArgumentNullException(nameof(otherSequences)); @@ -82,88 +54,40 @@ static IEnumerable Interleave(this IEnumerable sequence, ImbalancedInte { var sequences = new[] { sequence }.Concat(otherSequences); - // produce an iterator collection for all IEnumerable instancess passed to us - var iterators = sequences.Select(e => e.GetEnumerator()).Acquire(); - List> iteratorList = null; + // produce an enumerators collection for all IEnumerable instances passed to us + var enumerators = sequences.Select(e => e.GetEnumerator()).Acquire(); try { - iteratorList = new List>(iterators); - iterators = null; - var shouldContinue = true; - var consumedIterators = 0; - var iterCount = iteratorList.Count; - - while (shouldContinue) + var hasNext = true; + while (hasNext) { - // advance every iterator and verify a value exists to be yielded - for (var index = 0; index < iterCount; index++) + hasNext = false; + for (var i = 0; i < enumerators.Length; i++) { - if (!iteratorList[index].MoveNext()) - { - // check if all iterators have been consumed and we can terminate - // or if the imbalance strategy informs us that we MUST terminate - if (++consumedIterators == iterCount || imbalanceStrategy == ImbalancedInterleaveStrategy.Stop) - { - shouldContinue = false; - break; - } - - iteratorList[index].Dispose(); // dispose the iterator sice we no longer need it - - // otherwise, apply the imbalance strategy - switch (imbalanceStrategy) - { - case ImbalancedInterleaveStrategy.Pad: - var newIter = iteratorList[index] = Generate(default(T), x => default).GetEnumerator(); - newIter.MoveNext(); - break; - - case ImbalancedInterleaveStrategy.Skip: - iteratorList.RemoveAt(index); // no longer visit this particular iterator - --iterCount; // reduce the expected number of iterators to visit - --index; // decrement iterator index to compensate for index shifting - --consumedIterators; // decrement consumer iterator count to stay in balance - break; - } + var enumerator = enumerators[i]; + if (enumerator == null) + continue; + if (enumerator.MoveNext()) + { + hasNext = true; + yield return enumerator.Current; + } + else + { + enumerators[i] = null; + enumerator.Dispose(); } - } - - if (shouldContinue) // only if all iterators could be advanced - { - // yield the values of each iterator's current position - foreach (var iterator in iteratorList) - yield return iterator.Current; } } } finally { - Debug.Assert(iteratorList != null || iterators != null); - foreach (var iter in (iteratorList ?? (IList>) iterators)) - iter.Dispose(); + foreach (var enumerator in enumerators) + enumerator?.Dispose(); } } } - - /// - /// Defines the strategies available when Interleave is passed sequences of unequal length - /// - enum ImbalancedInterleaveStrategy - { - /// - /// Extends a sequence by padding its tail with default(T) - /// - Pad, - /// - /// Removes the sequence from the interleave set, and continues interleaving remaining sequences. - /// - Skip, - /// - /// Stops the interleave operation. - /// - Stop, - } } } From 31cb640338217d96362f7fa39b57961b1c940562 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Thu, 12 Dec 2019 11:27:56 +0100 Subject: [PATCH 56/58] Add code coverage reporting This is a squashed merge of PR #682 that closes #670. --- .config/dotnet-tools.json | 6 ++++++ .gitignore | 2 ++ .travis.yml | 10 ++++++---- MoreLinq.Test/MoreLinq.Test.csproj | 4 ++++ appveyor.yml | 6 ++++++ test.cmd | 5 ++++- test.sh | 23 ++++++++++++++++++----- 7 files changed, 46 insertions(+), 10 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index dab16c723..962d2ed9f 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -7,6 +7,12 @@ "commands": [ "t4" ] + }, + "dotnet-reportgenerator-globaltool": { + "version": "4.3.6", + "commands": [ + "reportgenerator" + ] } } } \ No newline at end of file diff --git a/.gitignore b/.gitignore index cc7e1a503..f9828f54d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +*.opencover.xml + ### VisualStudio ### ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. diff --git a/.travis.yml b/.travis.yml index 7bafdbf6b..08fa6e26d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,7 +45,9 @@ script: echo "System.Linq import found, failing the build!" >&2 exit 1 fi - - ./build.sh $CONFIGURATION - - dotnet exec MoreLinq.Test/bin/$CONFIGURATION/netcoreapp2.1/MoreLinq.Test.dll - - dotnet exec MoreLinq.Test/bin/$CONFIGURATION/netcoreapp3.0/MoreLinq.Test.dll - - mono MoreLinq.Test/bin/$CONFIGURATION/net451/MoreLinq.Test.exe + - ./test.sh $CONFIGURATION + - # revert to following post merge of PR codecov/codecov-bash#138 + - # curl -s https://codecov.io/bash > codecov + - curl -sSL https://raw.githubusercontent.com/codecov/codecov-bash/14662d32a4862918c31efafe4b450de1305a38e1/codecov > codecov + - chmod +x codecov + - ./codecov -f ./MoreLinq.Test/coverage.opencover.xml diff --git a/MoreLinq.Test/MoreLinq.Test.csproj b/MoreLinq.Test/MoreLinq.Test.csproj index ea2da2dde..4f9d06d1e 100644 --- a/MoreLinq.Test/MoreLinq.Test.csproj +++ b/MoreLinq.Test/MoreLinq.Test.csproj @@ -22,6 +22,10 @@ + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + runtime; build; native; contentfiles; analyzers diff --git a/appveyor.yml b/appveyor.yml index 4c3ea3d1c..67b83b8ad 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -46,8 +46,14 @@ build_script: } test_script: - cmd: test.cmd +- ps: dotnet reportgenerator -reports:MoreLinq.Test/coverage.opencover.xml -targetdir:tmp/cover -tag:(git show -q --pretty=%H) +- ps: Get-ChildItem tmp/cover | Compress-Archive -DestinationPath coverage-report.zip +- cmd: | + cd tmp\cover + tar -cvz -f ../../coverage-report.tar.gz * artifacts: - path: dist\*.nupkg +- path: coverage-report.* deploy: - provider: NuGet server: https://www.myget.org/F/morelinq/api/v2/package diff --git a/test.cmd b/test.cmd index 2fafd3c21..7cc660244 100644 --- a/test.cmd +++ b/test.cmd @@ -18,9 +18,12 @@ goto :EOF :test setlocal echo Testing %1 (%2)... +if %2==Debug set COVERAGE_ARGS=-p:CollectCoverage=true ^ + -p:CoverletOutputFormat=opencover ^ + -p:Exclude=\"[NUnit*]*,[MoreLinq]MoreLinq.Extensions.*,[MoreLinq]MoreLinq.Experimental.*\" if %1==net451 ( MoreLinq.Test\bin\%2\net451\MoreLinq.Test.exe ) else ( - dotnet exec MoreLinq.Test\bin\%2\%1\MoreLinq.Test.dll + dotnet test --no-build MoreLinq.Test -c %2 %COVERAGE_ARGS% ) goto :EOF diff --git a/test.sh b/test.sh index 4729e73d4..263c1fea0 100755 --- a/test.sh +++ b/test.sh @@ -1,16 +1,29 @@ #!/usr/bin/env bash set -e cd "$(dirname "$0")" -./build.sh +./build.sh $c +if [[ -z "$1" ]]; then + configs="Debug Release" +else + configs="$1" +fi for v in 2.1 3.0; do - for c in Debug Release; do - dotnet exec MoreLinq.Test/bin/$c/netcoreapp$v/MoreLinq.Test.dll + for c in $configs; do + if [[ "$c" == "Debug" ]]; then + coverage_args="-p:CollectCoverage=true + -p:CoverletOutputFormat=opencover + -p:Exclude=\"[NUnit*]*,[MoreLinq]MoreLinq.Extensions.*,[MoreLinq]MoreLinq.Experimental.*\"" + else + unset coverage_args + fi + dotnet test --no-build -c $c -f netcoreapp$v MoreLinq.Test $coverage_args done done if [[ -z `which mono 2>/dev/null` ]]; then echo>&2 NOTE! Mono does not appear to be installed so unit tests echo>&2 against the Mono runtime will be skipped. else - mono MoreLinq.Test/bin/Debug/net451/MoreLinq.Test.exe - mono MoreLinq.Test/bin/Release/net451/MoreLinq.Test.exe + for c in $configs; do + mono MoreLinq.Test/bin/$c/net451/MoreLinq.Test.exe + done fi From 349a8e6e5fc1ce0eec2f4a741eb005492b1bceaf Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 13 Dec 2019 09:24:54 +0100 Subject: [PATCH 57/58] Prepare for 3.3.1 --- MoreLinq/MoreLinq.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MoreLinq/MoreLinq.csproj b/MoreLinq/MoreLinq.csproj index 3ee4f8020..aaa0380e4 100644 --- a/MoreLinq/MoreLinq.csproj +++ b/MoreLinq/MoreLinq.csproj @@ -117,7 +117,7 @@ $([System.Text.RegularExpressions.Regex]::Replace($(Copyright), `\s+`, ` `).Trim()) MoreLINQ en-US - 3.3.0 + 3.3.1 MoreLINQ Developers. net451;netstandard1.0;netstandard2.0 8 From b77df70598ab84c28cd43dcf74594024b6d575e1 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Fri, 13 Dec 2019 09:40:17 +0100 Subject: [PATCH 58/58] Move TrySingle to Experimental namespace --- MoreLinq.Test/TrySingleTest.cs | 8 +-- MoreLinq/{ => Experimental}/TrySingle.cs | 4 +- MoreLinq/Extensions.g.cs | 82 ------------------------ MoreLinq/MoreEnumerable.cs | 2 +- README.md | 16 ++--- 5 files changed, 15 insertions(+), 97 deletions(-) rename MoreLinq/{ => Experimental}/TrySingle.cs (98%) diff --git a/MoreLinq.Test/TrySingleTest.cs b/MoreLinq.Test/TrySingleTest.cs index 675044c7e..4fe51a3b2 100644 --- a/MoreLinq.Test/TrySingleTest.cs +++ b/MoreLinq.Test/TrySingleTest.cs @@ -15,14 +15,14 @@ // limitations under the License. #endregion -using NUnit.Framework.Interfaces; - namespace MoreLinq.Test { using System; using System.Collections; using System.Collections.Generic; using NUnit.Framework; + using NUnit.Framework.Interfaces; + using Experimental; [TestFixture] public class TrySingleTest @@ -81,7 +81,7 @@ class BreakingSingleElementCollectionBase : IEnumerable public IEnumerator GetEnumerator() { yield return _element; - throw new Exception($"{nameof(MoreEnumerable.TrySingle)} should not have attempted to consume a second element."); + throw new Exception($"{nameof(ExperimentalEnumerable.TrySingle)} should not have attempted to consume a second element."); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -128,7 +128,7 @@ static IEnumerable TestSequence() { yield return 1; yield return 2; - throw new Exception(nameof(MoreEnumerable.TrySingle) + " should not have attempted to consume a third element."); + throw new Exception(nameof(ExperimentalEnumerable.TrySingle) + " should not have attempted to consume a third element."); } var (cardinality, value) = TestSequence().TrySingle("zero", "one", "many"); diff --git a/MoreLinq/TrySingle.cs b/MoreLinq/Experimental/TrySingle.cs similarity index 98% rename from MoreLinq/TrySingle.cs rename to MoreLinq/Experimental/TrySingle.cs index 24bfe35df..6aa591164 100644 --- a/MoreLinq/TrySingle.cs +++ b/MoreLinq/Experimental/TrySingle.cs @@ -15,13 +15,13 @@ // limitations under the License. #endregion -namespace MoreLinq +namespace MoreLinq.Experimental { using System; using System.Collections.Generic; using System.Linq; - partial class MoreEnumerable + partial class ExperimentalEnumerable { /// /// Returns a tuple with the cardinality of the sequence and the diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index 0b1211058..5e45fc65f 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -6588,88 +6588,6 @@ public static IEnumerable> Transpose(this IEnumerableTrySingle extension. - - [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] - public static partial class TrySingleExtension - { - /// - /// Returns a tuple with the cardinality of the sequence and the - /// single element in the sequence if it contains exactly one element. - /// similar to . - /// - /// The source sequence. - /// - /// The value that is returned in the tuple if the sequence has zero - /// elements. - /// - /// The value that is returned in the tuple if the sequence has a - /// single element only. - /// - /// The value that is returned in the tuple if the sequence has two or - /// more elements. - /// - /// The type of the elements of . - /// - /// The type that expresses cardinality. - /// - /// A tuple containing the identified - /// and either the single value of in the sequence - /// or its default value. - /// - /// This operator uses immediate execution, but never consumes more - /// than two elements from the sequence. - /// - - public static (TCardinality Cardinality, T Value) - TrySingle(this IEnumerable source, - TCardinality zero, TCardinality one, TCardinality many) => MoreEnumerable. TrySingle(source, zero, one, many); - - /// - /// Returns a result projected from the the cardinality of the sequence - /// and the single element in the sequence if it contains exactly one - /// element. - /// - /// The source sequence. - /// - /// The value that is passed as the first argument to - /// if the sequence has zero - /// elements. - /// - /// The value that is passed as the first argument to - /// if the sequence has a - /// single element only. - /// - /// The value that is passed as the first argument to - /// if the sequence has two or - /// more elements. - /// - /// A function that receives the cardinality and, if the - /// sequence has just one element, the value of that element as - /// argument and projects a resulting value of type - /// . - /// - /// The type of the elements of . - /// - /// The type that expresses cardinality. - /// - /// The type of the result value returned by the - /// function. - /// - /// The value returned by . - /// - /// - /// This operator uses immediate execution, but never consumes more - /// than two elements from the sequence. - /// - - public static TResult TrySingle(this IEnumerable source, - TCardinality zero, TCardinality one, TCardinality many, - Func resultSelector) - => MoreEnumerable.TrySingle(source, zero, one, many, resultSelector); - - } - /// Window extension. [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] diff --git a/MoreLinq/MoreEnumerable.cs b/MoreLinq/MoreEnumerable.cs index 09831c851..118911f81 100644 --- a/MoreLinq/MoreEnumerable.cs +++ b/MoreLinq/MoreEnumerable.cs @@ -27,7 +27,7 @@ namespace MoreLinq public static partial class MoreEnumerable { - static int? TryGetCollectionCount(this IEnumerable source) + internal static int? TryGetCollectionCount(this IEnumerable source) { if (source == null) throw new ArgumentNullException(nameof(source)); diff --git a/README.md b/README.md index a218b1613..03631fdef 100644 --- a/README.md +++ b/README.md @@ -680,14 +680,6 @@ Traces the elements of a source sequence for diagnostics. This method has 3 overloads. -### TrySingle - -Returns the only element of a sequence that has just one element. If the -sequence has zero or multiple elements, then returns a user-defined value -that indicates the cardinality of the result sequence. - -This method has 2 overloads. - ### Unfold Returns a sequence generated by applying a state to the generator function, @@ -770,6 +762,14 @@ Creates a sequence that lazily caches the source as it is iterated for the first time, reusing the cache thereafter for future re-iterations. If the source is already cached or buffered then it is returned verbatim. +### TrySingle + +Returns the only element of a sequence that has just one element. If the +sequence has zero or multiple elements, then returns a user-defined value +that indicates the cardinality of the result sequence. + +This method has 2 overloads. + [#122]: https://github.com/morelinq/MoreLINQ/issues/122 [dict]: https://docs.microsoft.com/en-us/dotnet/api/System.Collections.Generic.Dictionary-2