Skip to content

Commit

Permalink
Add {Cycle,Repeat}Materialized
Browse files Browse the repository at this point in the history
  • Loading branch information
bash committed Jan 14, 2025
1 parent 1c40968 commit 59caf20
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 3 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 FailOnEnumerateCollection<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 FailOnEnumerateCollection<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();
}
}
2 changes: 1 addition & 1 deletion Funcky.Test/TestUtils/FailOnEnumerateCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Funcky.Test.TestUtils;

internal record FailOnEnumerateCollection<T>(int Count) : ICollection<T>
internal record FailOnEnumerateCollection<T>(int Count) : ICollection<T>, IReadOnlyCollection<T>
{
public bool IsReadOnly => true;

Expand Down
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>!
11 changes: 11 additions & 0 deletions Funcky/Sequence/Sequence.CycleMaterialized.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Funcky;

public static partial class Sequence
{
/// <inheritdoc cref="CycleRange{TSource}(IEnumerable{TSource})"/>
[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");
}
9 changes: 9 additions & 0 deletions Funcky/Sequence/Sequence.RepeatMaterialized.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Funcky;

public static partial class Sequence
{
/// <inheritdoc cref="RepeatRange{TSource}(IEnumerable{TSource},int)"/>
[Pure]
public static IEnumerable<TSource> RepeatMaterialized<TSource>(IReadOnlyCollection<TSource> source, int count)
=> Enumerable.Repeat(source, count).SelectMany(Identity);
}

0 comments on commit 59caf20

Please sign in to comment.