diff --git a/Documentation/src/enumerable-extensions/cartesian-product.md b/Documentation/src/enumerable-extensions/cartesian-product.md index 96b7528f..4d909744 100644 --- a/Documentation/src/enumerable-extensions/cartesian-product.md +++ b/Documentation/src/enumerable-extensions/cartesian-product.md @@ -1,38 +1,61 @@ ## CartesianProduct -In mathematics, specifically set theory, the Cartesian product of two sets A and B, denoted AΓ—B, is the set of all ordered pairs (a, b) where a is in A and b is in B. +In mathematics, specifically set theory, the Cartesian product of two sets A and B, denoted AΓ—B, +is the set of all ordered pairs (a, b) where a ∈ A and b ∈ B. -The CartesianProduct extension function returns all possible pairs of two given IEnumerables. - -There are two overloads, one which lets you choose +In other words: The Cartesian product produces all possible pairs of two given `IEnumerable`s. ![cartesian-product with marbles](cartesian-product.svg "Marble me!") +### Recipe +The Cartesian product can be easily implemented ad-hoc using LINQ's built-in `SelectMany` extension function: + +```cs +using System; +using System.Linq; + +// Version A: Get each pair as a tuple +var result = sequenceA.SelectMany(_ => sequenceB, ValueTuple.Create); + +// Version B: Transform each pair using a selector +var result = sequenceA.SelectMany(_ => sequenceB, (a, b) => ...); + +// Version C: Using LINQs declarative query syntax +var result = + from a in sequenceA + from b in sequenceB + select ...; +``` + + ### Examples Two sequences as input: -``` +``` smiles = [πŸ˜€, 😐, πŸ™„] fruits = [πŸ‰, 🍌, πŸ‡, πŸ“] -``` +``` -The cartesian products of smiles and fruits: +The Cartesian products of smiles and fruits: -``` -smiles Γ— fruits => [[πŸ˜€, πŸ‰], [πŸ˜€, 🍌], [πŸ˜€, πŸ‡], [πŸ˜€, πŸ“], - [😐, πŸ‰], [😐, 🍌], [😐, πŸ‡], [😐, πŸ“], +``` +smiles Γ— fruits => [[πŸ˜€, πŸ‰], [πŸ˜€, 🍌], [πŸ˜€, πŸ‡], [πŸ˜€, πŸ“], + [😐, πŸ‰], [😐, 🍌], [😐, πŸ‡], [😐, πŸ“], [πŸ™„, πŸ‰], [πŸ™„, 🍌], [πŸ™„, πŸ‡], [πŸ™„, πŸ“]] ``` -In this C# example you see how all Playing cards are in fact a cartesian product of a suit and a value. +In this C# example you see how all playing cards are in fact a Cartesian products of a suit and a value. -This example uses the overload with our own selector, because we just want a sequence of strings. +This example uses the overload with a selector, because we just want a sequence of strings. ```cs -var suits = ImmutableList.Create("β™ ", "♣", "β™₯", "♦"); -var values = - ImmutableList.Create("2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A"); +using System; +using System.Linq; +using Funcky; + +var suits = Sequence.Return("β™ ", "♣", "β™₯", "♦"); +var values = Sequence.Return("2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A"); -var allCards = suits.CartesianProduct(values, (suit, value) => $"{value}{suit}"); -``` +var deck = suits.SelectMany(_ => values, (suit, value) => $"{value}{suit}"); +``` diff --git a/Funcky.Async.Test/Extensions/AsyncEnumerableExtensions/ChunkTest.cs b/Funcky.Async.Test/Extensions/AsyncEnumerableExtensions/ChunkTest.cs index dedef74a..72f85444 100644 --- a/Funcky.Async.Test/Extensions/AsyncEnumerableExtensions/ChunkTest.cs +++ b/Funcky.Async.Test/Extensions/AsyncEnumerableExtensions/ChunkTest.cs @@ -42,29 +42,29 @@ public async Task GivenAnEnumerableWeChanChunkItIntoAnEnumerableOfEnumerablesAsy await AsyncAssert.Collection( chunked, - a => + chunk => { Assert.Collection( - a, - aa => Assert.Equal(1, aa), - ab => Assert.Equal(2, ab), - ac => Assert.Equal(3, ac)); + chunk, + value => Assert.Equal(1, value), + value => Assert.Equal(2, value), + value => Assert.Equal(3, value)); }, - b => + chunk => { Assert.Collection( - b, - ba => Assert.Equal(4, ba), - bb => Assert.Equal(5, bb), - bc => Assert.Equal(6, bc)); + chunk, + value => Assert.Equal(4, value), + value => Assert.Equal(5, value), + value => Assert.Equal(6, value)); }, - c => + chunk => { Assert.Collection( - c, - ca => Assert.Equal(7, ca), - cb => Assert.Equal(8, cb), - cc => Assert.Equal(9, cc)); + chunk, + value => Assert.Equal(7, value), + value => Assert.Equal(8, value), + value => Assert.Equal(9, value)); }); } @@ -79,18 +79,9 @@ public async Task GivenAnEnumerableNotAMultipleOfSizeWeHaveASmallerLastSlice() await AsyncAssert.Collection( chunked, - a => - { - Assert.Equal(chunkSize, a.Count); - }, - b => - { - Assert.Equal(chunkSize, b.Count); - }, - c => - { - Assert.Equal(count % chunkSize, c.Count); - }); + chunk => Assert.Equal(chunkSize, chunk.Count), + chunk => Assert.Equal(chunkSize, chunk.Count), + chunk => Assert.Equal(count % chunkSize, chunk.Count)); } [Theory] diff --git a/Funcky.Async.Test/Extensions/AsyncEnumerableExtensions/SlidingWindowTest.cs b/Funcky.Async.Test/Extensions/AsyncEnumerableExtensions/SlidingWindowTest.cs index 4feee190..fe79ebb3 100644 --- a/Funcky.Async.Test/Extensions/AsyncEnumerableExtensions/SlidingWindowTest.cs +++ b/Funcky.Async.Test/Extensions/AsyncEnumerableExtensions/SlidingWindowTest.cs @@ -41,7 +41,7 @@ public async Task GivenASourceSequenceShorterThanTheSlidingWindowWidthReturnsAnE } [Fact] - public async Task SlidingWindowReturnsTheCorrectAmountOfWindowsAllOfEvenSizeAsync() + public async Task SlidingWindowReturnsTheCorrectAmountOfWindowsAllOfTheSameSizeAsync() { const int width = 5; var source = AsyncEnumerable.Range(0, 10); @@ -74,9 +74,9 @@ public async Task SlidingWindowReturnsASequenceOfConsecutiveWindowsAsync() await AsyncAssert.Collection( source.SlidingWindow(width), - window1 => { Assert.Equal(Enumerable.Range(0, width), window1); }, - window2 => { Assert.Equal(Enumerable.Range(1, width), window2); }, - window3 => { Assert.Equal(Enumerable.Range(2, width), window3); }); + window => Assert.Equal(Enumerable.Range(0, width), window), + window => Assert.Equal(Enumerable.Range(1, width), window), + window => Assert.Equal(Enumerable.Range(2, width), window)); } [Fact] diff --git a/Funcky.Async/Extensions/AsyncEnumerableExtensions/Chunk.cs b/Funcky.Async/Extensions/AsyncEnumerableExtensions/Chunk.cs index c001000d..fcd2ceda 100644 --- a/Funcky.Async/Extensions/AsyncEnumerableExtensions/Chunk.cs +++ b/Funcky.Async/Extensions/AsyncEnumerableExtensions/Chunk.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; +using Funcky.Internal.Validators; namespace Funcky.Extensions; @@ -13,7 +14,7 @@ public static partial class AsyncEnumerableExtensions /// A sequence of equally sized sequences containing elements of the source collection in the same order. [Pure] public static IAsyncEnumerable> Chunk(this IAsyncEnumerable source, int size) - => ChunkEnumerable(source, ValidateChunkSize(size)); + => ChunkEnumerable(source, ChunkSizeValidator.Validate(size)); /// /// Chunks the source sequence into equally sized chunks. The last chunk can be smaller. @@ -26,7 +27,7 @@ public static IAsyncEnumerable> Chunk(this IAsyn /// A sequence of results based on equally sized chunks. [Pure] public static IAsyncEnumerable Chunk(this IAsyncEnumerable source, int size, Func, TResult> resultSelector) - => ChunkEnumerable(source, ValidateChunkSize(size)) + => ChunkEnumerable(source, ChunkSizeValidator.Validate(size)) .Select(resultSelector); /// @@ -40,7 +41,7 @@ public static IAsyncEnumerable Chunk(this IAsyncEnume /// A sequence of results based on equally sized chunks. [Pure] public static IAsyncEnumerable ChunkAwait(this IAsyncEnumerable source, int size, Func, ValueTask> resultSelector) - => ChunkEnumerable(source, ValidateChunkSize(size)) + => ChunkEnumerable(source, ChunkSizeValidator.Validate(size)) .SelectAwait(resultSelector); /// @@ -54,14 +55,9 @@ public static IAsyncEnumerable ChunkAwait(this IAsync /// A sequence of results based on equally sized chunks. [Pure] public static IAsyncEnumerable ChunkAwaitWithCancellation(this IAsyncEnumerable source, int size, Func, CancellationToken, ValueTask> resultSelector) - => ChunkEnumerable(source, ValidateChunkSize(size)) + => ChunkEnumerable(source, ChunkSizeValidator.Validate(size)) .SelectAwaitWithCancellation(resultSelector); - private static int ValidateChunkSize(int size) - => size > 0 - ? size - : throw new ArgumentOutOfRangeException(nameof(size), size, "Size must be bigger than 0"); - private static async IAsyncEnumerable> ChunkEnumerable(IAsyncEnumerable source, int size, [EnumeratorCancellation] CancellationToken cancellationToken = default) { var asyncEnumerator = source.GetAsyncEnumerator(cancellationToken); diff --git a/Funcky.Async/Extensions/AsyncEnumerableExtensions/SlidingWindow.cs b/Funcky.Async/Extensions/AsyncEnumerableExtensions/SlidingWindow.cs index 1f62d305..2a4e7989 100644 --- a/Funcky.Async/Extensions/AsyncEnumerableExtensions/SlidingWindow.cs +++ b/Funcky.Async/Extensions/AsyncEnumerableExtensions/SlidingWindow.cs @@ -1,5 +1,6 @@ using System.Runtime.CompilerServices; using Funcky.Internal; +using Funcky.Internal.Validators; namespace Funcky.Extensions; @@ -19,7 +20,7 @@ public static partial class AsyncEnumerableExtensions /// Returns a sequence of equally sized window sequences. [Pure] public static IAsyncEnumerable> SlidingWindow(this IAsyncEnumerable source, int width) - => SlidingWindowEnumerable(source, ValidateWindowWidth(width)); + => SlidingWindowEnumerable(source, WindowWidthValidator.Validate(width)); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static async IAsyncEnumerable> SlidingWindowEnumerable( @@ -36,10 +37,4 @@ private static async IAsyncEnumerable> SlidingWindowEnume } } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int ValidateWindowWidth(int width) - => width > 0 - ? width - : throw new ArgumentOutOfRangeException(nameof(width), width, "The width of the window must be bigger than 0"); } diff --git a/Funcky.Async/Funcky.Async.csproj b/Funcky.Async/Funcky.Async.csproj index f508cd7a..da8da973 100644 --- a/Funcky.Async/Funcky.Async.csproj +++ b/Funcky.Async/Funcky.Async.csproj @@ -35,6 +35,8 @@ + + diff --git a/Funcky.Test/Extensions/EnumerableExtensions/ChunkTest.cs b/Funcky.Test/Extensions/EnumerableExtensions/ChunkTest.cs index 7f3ffd2b..90f273cc 100644 --- a/Funcky.Test/Extensions/EnumerableExtensions/ChunkTest.cs +++ b/Funcky.Test/Extensions/EnumerableExtensions/ChunkTest.cs @@ -31,11 +31,11 @@ public void GivenAnSingleElementListWeGetEnumerableWithOneElement() Assert.Collection( chunked, - a => + chunk => { Assert.Collection( - a, - aa => Assert.Equal(1, aa)); + chunk, + value => Assert.Equal(1, value)); }); } @@ -48,29 +48,29 @@ public void GivenAnEnumerableWeChanChunkItIntoAnEnumerableOfEnumerables() Assert.Collection( chunked, - a => + chunk => { Assert.Collection( - a, - aa => Assert.Equal(1, aa), - ab => Assert.Equal(2, ab), - ac => Assert.Equal(3, ac)); + chunk, + value => Assert.Equal(1, value), + value => Assert.Equal(2, value), + value => Assert.Equal(3, value)); }, - b => + chunk => { Assert.Collection( - b, - ba => Assert.Equal(4, ba), - bb => Assert.Equal(5, bb), - bc => Assert.Equal(6, bc)); + chunk, + value => Assert.Equal(4, value), + value => Assert.Equal(5, value), + value => Assert.Equal(6, value)); }, - c => + chunk => { Assert.Collection( - c, - ca => Assert.Equal(7, ca), - cb => Assert.Equal(8, cb), - cc => Assert.Equal(9, cc)); + chunk, + value => Assert.Equal(7, value), + value => Assert.Equal(8, value), + value => Assert.Equal(9, value)); }); } @@ -84,18 +84,9 @@ public void GivenAnEnumerableNotAMultipleOfSizeWeHaveASmallerLastSlice() Assert.Collection( chunked, - a => - { - Assert.Equal(chunkSize, a.Count); - }, - b => - { - Assert.Equal(chunkSize, b.Count); - }, - c => - { - Assert.Equal(numbers.Count % chunkSize, c.Count); - }); + chunk => Assert.Equal(chunkSize, chunk.Count), + chunk => Assert.Equal(chunkSize, chunk.Count), + chunk => Assert.Equal(numbers.Count % chunkSize, chunk.Count)); } [Theory] diff --git a/Funcky.Test/Extensions/EnumerableExtensions/SlidingWindowTest.cs b/Funcky.Test/Extensions/EnumerableExtensions/SlidingWindowTest.cs index d5aedda0..6d528635 100644 --- a/Funcky.Test/Extensions/EnumerableExtensions/SlidingWindowTest.cs +++ b/Funcky.Test/Extensions/EnumerableExtensions/SlidingWindowTest.cs @@ -40,7 +40,7 @@ public void GivenASourceSequenceShorterThanTheSlidingWindowWidthReturnsAnEmptySe } [Fact] - public void SlidingWindowReturnsTheCorrectAmountOfWindowsAllOfEvenSize() + public void SlidingWindowReturnsTheCorrectAmountOfWindowsAllOfTheSameSize() { const int width = 5; var source = Enumerable.Range(0, 10); @@ -70,8 +70,8 @@ public void SlidingWindowReturnsASequenceOfConsecutiveWindows() Assert.Collection( source.SlidingWindow(width), - window1 => { Assert.Equal(Enumerable.Range(0, width), window1); }, - window2 => { Assert.Equal(Enumerable.Range(1, width), window2); }, - window3 => { Assert.Equal(Enumerable.Range(2, width), window3); }); + window => Assert.Equal(Enumerable.Range(0, width), window), + window => Assert.Equal(Enumerable.Range(1, width), window), + window => Assert.Equal(Enumerable.Range(2, width), window)); } } diff --git a/Funcky.Test/Extensions/StringExtensions/ChunkOnStringTest.cs b/Funcky.Test/Extensions/StringExtensions/ChunkOnStringTest.cs new file mode 100644 index 00000000..e17531ac --- /dev/null +++ b/Funcky.Test/Extensions/StringExtensions/ChunkOnStringTest.cs @@ -0,0 +1,54 @@ +using FsCheck; +using FsCheck.Xunit; + +namespace Funcky.Test.Extensions.StringExtensions; + +public class ChunkOnStringTest +{ + [Property] + public Property ChunkingAnEmptyStringWillAlwaysYieldAndEmptySequence(PositiveInt width) + => string.Empty + .Chunk(width.Get) + .None() + .ToProperty(); + + [Fact] + public void GivenAnSingleElementListWeGetEnumerableWithOneElement() + { + var value = "a"; + + var chunked = value.Chunk(3); + + Assert.Collection( + chunked, + chunk => Assert.Equal("a", chunk)); + } + + [Fact] + public void GivenAnStringWhichIsNotAMultipleOfSizeWeHaveASmallerLastString() + { + var value = "abcdefghij"; + + const int chunkSize = 4; + var chunked = value.Chunk(chunkSize); + + Assert.Collection( + chunked, + chunk => Assert.Equal(chunkSize, chunk.Length), + chunk => Assert.Equal(chunkSize, chunk.Length), + chunk => Assert.Equal(value.Length % chunkSize, chunk.Length)); + } + + [Fact] + public void AChunkOfAStringReturnsAListOfConsecutivePartialStrings() + { + const int width = 3; + var source = "epsilon"; + + Assert.Collection( + source.Chunk(width), + chunk => { Assert.Equal("eps", chunk); }, + chunk => { Assert.Equal("ilo", chunk); }, + chunk => { Assert.Equal("n", chunk); }); + } +} diff --git a/Funcky.Test/Extensions/StringExtensions/SlidingWindowOnStringTest.cs b/Funcky.Test/Extensions/StringExtensions/SlidingWindowOnStringTest.cs new file mode 100644 index 00000000..36bbbd70 --- /dev/null +++ b/Funcky.Test/Extensions/StringExtensions/SlidingWindowOnStringTest.cs @@ -0,0 +1,57 @@ +using FsCheck; +using FsCheck.Xunit; + +namespace Funcky.Test.Extensions.StringExtensions; + +public class SlidingWindowOnStringTest +{ + [Property] + public Property ASlidingWindowFromAnEmptyStringWillAlwaysYieldAnEmptySequence(PositiveInt width) + => string.Empty + .SlidingWindow(width.Get) + .None() + .ToProperty(); + + [Fact] + public void SlidingWindowReturnsAListOfOverlappingPartialStrings() + { + const int width = 4; + var source = "epsilon"; + + Assert.Collection( + source.SlidingWindow(width), + window => Assert.Equal("epsi", window), + window => Assert.Equal("psil", window), + window => Assert.Equal("silo", window), + window => Assert.Equal("ilon", window)); + } + + [Fact] + public void GivenASourceStringEqualInLengthToTheSlidingWindowWidthReturnsASequenceWithOneElement() + { + var source = "gamma"; + + Assert.Single(source.SlidingWindow(5)); + Assert.Equal(source, source.SlidingWindow(5).Single()); + } + + [Fact] + public void GivenASourceSequenceShorterThanTheSlidingWindowWidthReturnsAnEmptySequence() + { + var source = "gamma"; + + Assert.Empty(source.SlidingWindow(10)); + } + + [Theory] + [InlineData(int.MinValue)] + [InlineData(-42)] + [InlineData(-1)] + [InlineData(0)] + public void SlidingWindowThrowsOnNonPositiveWidth(int width) + { + var source = "Just a simple test!"; + + Assert.Throws(() => source.SlidingWindow(width)); + } +} diff --git a/Funcky/Extensions/EnumerableExtensions/Chunk.cs b/Funcky/Extensions/EnumerableExtensions/Chunk.cs index fcbd0c5e..85eac260 100644 --- a/Funcky/Extensions/EnumerableExtensions/Chunk.cs +++ b/Funcky/Extensions/EnumerableExtensions/Chunk.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using Funcky.Internal.Validators; namespace Funcky.Extensions; @@ -18,7 +19,7 @@ public static IEnumerable> Chunk(IEnumerable> Chunk(this IEnumerable source, int size) #endif - => ChunkEnumerable(source, ValidateChunkSize(size)); + => ChunkEnumerable(source, ChunkSizeValidator.Validate(size)); /// /// Chunks the source sequence into equally sized chunks. The last chunk can be smaller. @@ -31,14 +32,9 @@ public static IEnumerable> Chunk(this IEnumerabl /// A sequence of results based on equally sized chunks. [Pure] public static IEnumerable Chunk(this IEnumerable source, int size, Func, TResult> resultSelector) - => ChunkEnumerable(source, ValidateChunkSize(size)) + => ChunkEnumerable(source, ChunkSizeValidator.Validate(size)) .Select(resultSelector); - private static int ValidateChunkSize(int size) - => size > 0 - ? size - : throw new ArgumentOutOfRangeException(nameof(size), size, "Size must be bigger than 0"); - private static IEnumerable> ChunkEnumerable(IEnumerable source, int size) { using var sourceEnumerator = source.GetEnumerator(); diff --git a/Funcky/Extensions/EnumerableExtensions/SlidingWindow.cs b/Funcky/Extensions/EnumerableExtensions/SlidingWindow.cs index 2cc44c45..8140a966 100644 --- a/Funcky/Extensions/EnumerableExtensions/SlidingWindow.cs +++ b/Funcky/Extensions/EnumerableExtensions/SlidingWindow.cs @@ -1,5 +1,6 @@ using System.Runtime.CompilerServices; using Funcky.Internal; +using Funcky.Internal.Validators; namespace Funcky.Extensions; @@ -19,7 +20,7 @@ public static partial class EnumerableExtensions /// Returns a sequence of equally sized window sequences. [Pure] public static IEnumerable> SlidingWindow(this IEnumerable source, int width) - => SlidingWindowEnumerable(source, ValidateWindowWidth(width)); + => SlidingWindowEnumerable(source, WindowWidthValidator.Validate(width)); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static IEnumerable> SlidingWindowEnumerable(IEnumerable source, int width) @@ -33,10 +34,4 @@ private static IEnumerable> SlidingWindowEnumerable width > 0 - ? width - : throw new ArgumentOutOfRangeException(nameof(width), width, "The width of the window must be larger than 0"); } diff --git a/Funcky/Extensions/StringExtensions/Chunk.cs b/Funcky/Extensions/StringExtensions/Chunk.cs new file mode 100644 index 00000000..f3404f61 --- /dev/null +++ b/Funcky/Extensions/StringExtensions/Chunk.cs @@ -0,0 +1,41 @@ +using System.Runtime.CompilerServices; +using Funcky.Internal.Validators; + +namespace Funcky.Extensions; + +public static partial class StringExtensions +{ + private const int LastElement = 1; + + /// + /// Chunks the source string into equally sized chunks. The last chunk can be smaller. + /// + /// The source string. + /// The desired size of the chunks in characters. + /// A sequence of equally sized substrings containing consecutive substrings of the source string in the same order. + public static IEnumerable Chunk(this string source, int size) + => ChunkString(source, ChunkSizeValidator.Validate(size)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static IEnumerable ChunkString(string source, int size) + { + var count = DivisionWithCeiling(source.Length, size); + + // optimization: we do not emit the last chunk, because it might be smaller than size. + var index = 0; + for (; index < count - LastElement; ++index) + { + yield return source.Substring(index * size, size); + } + + // If there is anything left to emit, we will emit the last chunk. + if (source.Length > 0) + { + yield return source.Substring(index * size); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int DivisionWithCeiling(int dividend, int divisior) + => (dividend + divisior - 1) / divisior; +} diff --git a/Funcky/Extensions/StringExtensions/SlidingWindow.cs b/Funcky/Extensions/StringExtensions/SlidingWindow.cs new file mode 100644 index 00000000..0c0730c6 --- /dev/null +++ b/Funcky/Extensions/StringExtensions/SlidingWindow.cs @@ -0,0 +1,28 @@ +using System.Runtime.CompilerServices; +using Funcky.Internal.Validators; + +namespace Funcky.Extensions; + +public static partial class StringExtensions +{ + /// + /// SlidingWindow returns a sequence of sliding window strings of the given width. + /// The nth string will start with the nth character of the source sequence. + /// + /// The source string. + /// The width of the sliding window in characters. + /// Returns a sequence of equally sized substrings. + public static IEnumerable SlidingWindow(this string source, int width) + => StringSlidingWindow(source, WindowWidthValidator.Validate(width)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static IEnumerable StringSlidingWindow(string source, int width) + { + var numberOfWindows = source.Length - width + 1; + + for (var index = 0; index < numberOfWindows; ++index) + { + yield return source.Substring(index, width); + } + } +} diff --git a/Funcky/Internal/Validators/ChunkSizeValidator.cs b/Funcky/Internal/Validators/ChunkSizeValidator.cs new file mode 100644 index 00000000..c3ce35da --- /dev/null +++ b/Funcky/Internal/Validators/ChunkSizeValidator.cs @@ -0,0 +1,12 @@ +ο»Ώusing System.Runtime.CompilerServices; + +namespace Funcky.Internal.Validators; + +internal static class ChunkSizeValidator +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Validate(int size) + => size > 0 + ? size + : throw new ArgumentOutOfRangeException(nameof(size), size, "Size must be bigger than 0"); +} diff --git a/Funcky/Internal/Validators/WindowWidthValidator.cs b/Funcky/Internal/Validators/WindowWidthValidator.cs new file mode 100644 index 00000000..94885e4a --- /dev/null +++ b/Funcky/Internal/Validators/WindowWidthValidator.cs @@ -0,0 +1,12 @@ +using System.Runtime.CompilerServices; + +namespace Funcky.Internal.Validators; + +internal static class WindowWidthValidator +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Validate(int width) + => width > 0 + ? width + : throw new ArgumentOutOfRangeException(nameof(width), width, "The width of the window must be larger than 0"); +} diff --git a/Funcky/PublicAPI.Unshipped.txt b/Funcky/PublicAPI.Unshipped.txt index 00f48e94..5a25287e 100644 --- a/Funcky/PublicAPI.Unshipped.txt +++ b/Funcky/PublicAPI.Unshipped.txt @@ -14,3 +14,5 @@ static Funcky.Extensions.ParseExtensions.ParseNumberOrNone(this string! static Funcky.Extensions.ParseExtensions.ParseNumberOrNone(this System.ReadOnlySpan value, System.Globalization.NumberStyles style, System.IFormatProvider? provider) -> Funcky.Monads.Option static Funcky.Extensions.ParseExtensions.ParseOrNone(this string? value, System.IFormatProvider? provider) -> Funcky.Monads.Option static Funcky.Extensions.ParseExtensions.ParseOrNone(this System.ReadOnlySpan value, System.IFormatProvider? provider) -> Funcky.Monads.Option +static Funcky.Extensions.StringExtensions.Chunk(this string! source, int size) -> System.Collections.Generic.IEnumerable! +static Funcky.Extensions.StringExtensions.SlidingWindow(this string! source, int width) -> System.Collections.Generic.IEnumerable!