Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add generic version of Sequence for any number type #893

Merged
merged 8 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 13 additions & 14 deletions MoreLinq.Test/NullArgumentTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,19 @@ static MethodInfo InstantiateMethod(MethodInfo definition)
{
if (!definition.IsGenericMethodDefinition) return definition;

var typeArguments = definition.GetGenericArguments().Select(t => InstantiateType(t.GetTypeInfo())).ToArray();
return definition.MakeGenericMethod(typeArguments);
}

static Type InstantiateType(TypeInfo typeParameter)
{
var constraints = typeParameter.GetGenericParameterConstraints();

return constraints.Length switch
{
0 => typeof(int),
1 => constraints.Single(),
_ => throw new NotImplementedException("NullArgumentTest.InstantiateType")
};
var typeArguments =
from t in definition.GetGenericArguments()
select t.GetGenericParameterConstraints() switch
{
{ Length: 0 } => typeof(int),
#if NET7_0_OR_GREATER
var constraints when constraints.Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(System.Numerics.INumber<>)) => typeof(int),
#endif
{ Length: 1 } constraints => constraints.Single(),
_ => throw new NotImplementedException("NullArgumentTest.InstantiateType")
};

return definition.MakeGenericMethod(typeArguments.ToArray());
}

static bool IsReferenceType(ParameterInfo parameter) =>
Expand Down
6 changes: 5 additions & 1 deletion MoreLinq/MoreLinq.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,12 @@
<DefineConstants>$(DefineConstants);MORELINQ</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.1' Or '$(TargetFramework)' == 'net6.0' ">
<DefineConstants>$(DefineConstants);NO_STATIC_ABSTRACTS</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<DefineConstants>$(DefineConstants);NO_ASYNC_STREAMS;NO_BUFFERS</DefineConstants>
<DefineConstants>$(DefineConstants);NO_STATIC_ABSTRACTS;NO_ASYNC_STREAMS;NO_BUFFERS</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' != 'netstandard2.0' And '$(TargetFramework)' != 'net6.0'">
Expand Down
3 changes: 3 additions & 0 deletions MoreLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
#nullable enable
static MoreLinq.MoreEnumerable.Sequence<T>(T start) -> System.Collections.Generic.IEnumerable<T>!
static MoreLinq.MoreEnumerable.Sequence<T>(T start, T stop) -> System.Collections.Generic.IEnumerable<T>!
static MoreLinq.MoreEnumerable.Sequence<T>(T start, T stop, T step) -> System.Collections.Generic.IEnumerable<T>!
100 changes: 95 additions & 5 deletions MoreLinq/Sequence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,90 @@
// limitations under the License.
#endregion

#if !NO_STATIC_ABSTRACTS

namespace MoreLinq
{
using System;
using System.Collections.Generic;
using System.Numerics;

static partial class MoreEnumerable
{
/// <summary>
/// Generates a sequence of numbers starting with the given value and in steps of 1.
/// </summary>
/// <typeparam name="T">
/// A type that represents a number and defines its minimum and maximum representable value.
/// </typeparam>
/// <param name="start">The value of the first number in the sequence.</param>
/// <returns>
/// A sequence of sequential numbers starting with <paramref name="start"/> and up to the
/// maximum representable value, in increments of 1.</returns>
/// <remarks>
/// This operator uses deferred execution and streams its results.
/// </remarks>

public static IEnumerable<T> Sequence<T>(T start)
where T : INumber<T>, IMinMaxValue<T> =>
Sequence(start, T.MaxValue, T.One);

Check warning on line 44 in MoreLinq/Sequence.cs

View check run for this annotation

Codecov / codecov/patch

MoreLinq/Sequence.cs#L44

Added line #L44 was not covered by tests

/// <summary>
/// Generates a sequence of numbers within the (inclusive) specified range.
/// If sequence is ascending the step is +1, otherwise -1.
/// </summary>
/// <typeparam name="T">A type that represents a number.</typeparam>
/// <param name="start">The value of the first number in the sequence.</param>
/// <param name="stop">The value of the last number in the sequence.</param>
/// <returns>A sequence of sequential numbers.</returns>
/// <remarks>
/// This operator uses deferred execution and streams its results.
/// </remarks>

public static IEnumerable<T> Sequence<T>(T start, T stop)
where T : INumber<T> =>
Sequence(start, stop, stop < start ? -T.One : T.One);

/// <summary>
/// Generates a sequence of numbers within the (inclusive) specified range. An additional
/// parameter specifies the steps in which the numbers of the sequence increase or decrease.
/// </summary>
/// <typeparam name="T">A type that represents a number.</typeparam>
/// <param name="start">The value of the first number in the sequence.</param>
/// <param name="stop">The value of the last number in the sequence.</param>
/// <param name="step">The step to define the next number.</param>
/// <returns>A sequence of sequential numbers.</returns>
/// <remarks>
/// <para>
/// This operator uses deferred execution and streams its results.</para>
/// <para>
/// When <paramref name="step"/> is equal to zero, this operator returns an infinite
/// sequence where all elements are equal to <paramref name="start"/>.</para>
/// </remarks>

public static IEnumerable<T> Sequence<T>(T start, T stop, T step)
where T : INumber<T>
{
var current = start;

while (step >= T.Zero ? stop >= current : stop <= current)
{
yield return current;
try
{
current = checked(current + step);
}
catch (OverflowException)
{
yield break;
}
}
}
}
}

#endif // !NO_STATIC_ABSTRACTS

namespace MoreLinq
{
using System.Collections.Generic;
Expand All @@ -38,10 +122,12 @@
/// The <c>result</c> variable will contain <c>{ 6, 5, 4, 3, 2, 1, 0 }</c>.
/// </example>

public static IEnumerable<int> Sequence(int start, int stop)
{
return Sequence(start, stop, start < stop ? 1 : -1);
}
public static IEnumerable<int> Sequence(int start, int stop) =>
#if !NO_STATIC_ABSTRACTS
Sequence<int>(start, stop);
#else
Sequence(start, stop, start < stop ? 1 : -1);
#endif

/// <summary>
/// Generates a sequence of integral numbers within the (inclusive) specified range.
Expand All @@ -53,7 +139,7 @@
/// <returns>An <see cref="IEnumerable{Int32}"/> that contains a range of sequential integral numbers.</returns>
/// <remarks>
/// When <paramref name="step"/> is equal to zero, this operator returns an
/// infinite sequence where all elements are equals to <paramref name="start"/>.
/// infinite sequence where all elements are equal to <paramref name="start"/>.
/// This operator uses deferred execution and streams its results.
/// </remarks>
/// <example>
Expand All @@ -65,6 +151,9 @@

public static IEnumerable<int> Sequence(int start, int stop, int step)
{
#if !NO_STATIC_ABSTRACTS
return Sequence<int>(start, stop, step);
#else
long current = start;

while (step >= 0 ? stop >= current
Expand All @@ -73,6 +162,7 @@
yield return (int)current;
current += step;
}
#endif
}
}
}
Loading