Skip to content

Commit

Permalink
Merge pull request #773 from polyadic/usability-of-repeat-range
Browse files Browse the repository at this point in the history
  • Loading branch information
bash authored Jan 14, 2025
2 parents 6df18de + ce7a408 commit b0e5591
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 2 deletions.
47 changes: 47 additions & 0 deletions Funcky.Test/Sequence/CycleMaterializedTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using FsCheck;
using FsCheck.Xunit;
using Funcky.Test.TestUtils;

namespace Funcky.Test;

public sealed class CycleMaterializedTest
{
[Fact]
public void IsEnumeratedLazily()
{
var doNotEnumerate = new FailOnEnumerateReadOnlyCollection<object>(Count: 1);
_ = Sequence.CycleMaterialized(doNotEnumerate);
}

[Fact]
public void CyclingAnEmptySetThrowsAnException()
=> Assert.Throws<InvalidOperationException>(CycleEmptySequence);

[Property]
public Property CanProduceArbitraryManyItems(NonEmptySet<int> sequence, PositiveInt arbitraryElements)
{
var cycleRange = Sequence.CycleMaterialized(sequence.Get.Materialize());

return (cycleRange.Take(arbitraryElements.Get).Count() == arbitraryElements.Get)
.ToProperty();
}

[Property]
public Property RepeatsTheElementsArbitraryManyTimes(NonEmptySet<int> sequence, PositiveInt arbitraryElements)
{
var cycleRange = Sequence.CycleMaterialized(sequence.Get.Materialize());

return cycleRange
.IsSequenceRepeating(sequence.Get)
.NTimes(arbitraryElements.Get)
.ToProperty();
}

private static void CycleEmptySequence()
{
var cycledRange = Sequence.CycleMaterialized(Array.Empty<int>());
using var enumerator = cycledRange.GetEnumerator();

enumerator.MoveNext();
}
}
4 changes: 2 additions & 2 deletions Funcky.Test/Sequence/CycleRangeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public void CycleRangeIsEnumeratedLazily()
}

[Fact]
public void CyclingAnEmptySetThrowsAnArgumentException()
=> Assert.Throws<InvalidOperationException>(CycleEmptySequence);
public void CyclingAnEmptySetThrowsAnException()
=> Assert.Throws<InvalidOperationException>(CycleEmptySequence);

[Property]
public Property CycleRangeCanProduceArbitraryManyItems(NonEmptySet<int> sequence, PositiveInt arbitraryElements)
Expand Down
43 changes: 43 additions & 0 deletions Funcky.Test/Sequence/RepeatMaterializedTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using FsCheck;
using FsCheck.Xunit;
using Funcky.Test.TestUtils;

namespace Funcky.Test;

public sealed class RepeatMaterializedTest
{
[Fact]
public void IsEnumeratedLazily()
{
var doNotEnumerate = new FailOnEnumerateReadOnlyCollection<object>(Count: 0);
_ = Sequence.RepeatMaterialized(doNotEnumerate, 2);
}

[Property]
public Property ARepeatedEmptySequenceIsStillEmpty(NonNegativeInt count)
{
var repeated = Sequence.RepeatMaterialized(Array.Empty<object>(), count.Get);
return (!repeated.Any()).ToProperty();
}

[Property]
public Property TheLengthOfTheGeneratedSequenceIsCorrect(List<int> list, NonNegativeInt count)
{
var repeatRange = Sequence.RepeatMaterialized(list, count.Get);

var materialized = repeatRange.ToList();

return (materialized.Count == list.Count * count.Get).ToProperty();
}

[Property]
public Property TheSequenceRepeatsTheGivenNumberOfTimes(List<int> list, NonNegativeInt count)
{
var repeatRange = Sequence.RepeatMaterialized(list, count.Get);

return repeatRange
.IsSequenceRepeating(list)
.NTimes(count.Get)
.ToProperty();
}
}
11 changes: 11 additions & 0 deletions Funcky.Test/TestUtils/FailOnEnumerateReadOnlyCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections;
using Xunit.Sdk;

namespace Funcky.Test.TestUtils;

internal record FailOnEnumerateReadOnlyCollection<T>(int Count) : IReadOnlyCollection<T>
{
public IEnumerator<T> GetEnumerator() => throw new XunitException("Should not be enumerated");

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
2 changes: 2 additions & 0 deletions Funcky/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ static Funcky.DownCast<TResult>.From<TItem>(Funcky.Monads.Result<TItem!> result)
static Funcky.DownCast<TResult>.From<TLeft, TRight>(Funcky.Monads.Either<TLeft, TRight!> either, System.Func<TLeft>! failedCast) -> Funcky.Monads.Either<TLeft, TResult!>
static Funcky.Extensions.ParseExtensions.ParseTypeNameOrNone(this System.ReadOnlySpan<char> candidate, System.Reflection.Metadata.TypeNameParseOptions? options = null) -> Funcky.Monads.Option<System.Reflection.Metadata.TypeName!>
static Funcky.Extensions.ParseExtensions.ParseAssemblyNameInfoOrNone(this System.ReadOnlySpan<char> candidate) -> Funcky.Monads.Option<System.Reflection.Metadata.AssemblyNameInfo!>
static Funcky.Sequence.CycleMaterialized<TSource>(System.Collections.Generic.IReadOnlyCollection<TSource>! source) -> System.Collections.Generic.IEnumerable<TSource>!
static Funcky.Sequence.RepeatMaterialized<TSource>(System.Collections.Generic.IReadOnlyCollection<TSource>! source, int count) -> System.Collections.Generic.IEnumerable<TSource>!
17 changes: 17 additions & 0 deletions Funcky/Sequence/Sequence.CycleMaterialized.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Funcky;

public static partial class Sequence
{
/// <summary>
/// Generates a sequence that contains the same sequence of elements over and over again as an endless generator.
/// </summary>
/// <typeparam name="TSource">Type of the elements to be cycled.</typeparam>
/// <param name="source">The sequence of elements which are cycled. Throws an exception if the sequence is empty.</param>
/// <returns>Returns an infinite IEnumerable repeating the same sequence of elements.</returns>
/// <remarks>Use <see cref="CycleRange{TSource}"/> if you need to cycle a lazy sequence.</remarks>
[Pure]
public static IEnumerable<TSource> CycleMaterialized<TSource>(IReadOnlyCollection<TSource> source)
=> source.Count > 0
? Cycle(source).SelectMany(Identity)
: throw new InvalidOperationException("you cannot cycle an empty enumerable");
}
1 change: 1 addition & 0 deletions Funcky/Sequence/Sequence.CycleRange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public static partial class Sequence
/// <typeparam name="TSource">Type of the elements to be cycled.</typeparam>
/// <param name="source">The sequence of elements which are cycled. Throws an exception if the sequence is empty.</param>
/// <returns>Returns an infinite IEnumerable repeating the same sequence of elements.</returns>
/// <remarks>Use <see cref="CycleMaterialized{TSource}"/> if you need to cycle an already materialized sequence.</remarks>
[Pure]
public static IBuffer<TSource> CycleRange<TSource>(IEnumerable<TSource> source)
=> CycleBuffer.Create(source);
Expand Down
16 changes: 16 additions & 0 deletions Funcky/Sequence/Sequence.RepeatMaterialized.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Funcky;

public static partial class Sequence
{
/// <summary>
/// Generates a sequence that contains the same sequence of elements the given number of times.
/// </summary>
/// <typeparam name="TSource">Type of the elements to be repeated.</typeparam>
/// <param name="source">The sequence of elements to be repeated.</param>
/// <param name="count">The number of times to repeat the value in the generated sequence.</param>
/// <returns>Returns an infinite IEnumerable cycling through the same elements.</returns>
/// <remarks>Use <see cref="RepeatRange{TSource}"/> if you need to cycle a lazy sequence.</remarks>
[Pure]
public static IEnumerable<TSource> RepeatMaterialized<TSource>(IReadOnlyCollection<TSource> source, int count)
=> Enumerable.Repeat(source, count).SelectMany(Identity);
}
1 change: 1 addition & 0 deletions Funcky/Sequence/Sequence.RepeatRange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public static partial class Sequence
/// <param name="source">The sequence of elements to be repeated.</param>
/// <param name="count">The number of times to repeat the value in the generated sequence.</param>
/// <returns>Returns an infinite IEnumerable cycling through the same elements.</returns>
/// <remarks>Use <see cref="RepeatMaterialized{TSource}"/> if you need to cycle an already materialized sequence.</remarks>
[Pure]
public static IBuffer<TSource> RepeatRange<TSource>(IEnumerable<TSource> source, int count)
=> CycleBuffer.Create(source, count);
Expand Down

0 comments on commit b0e5591

Please sign in to comment.