Skip to content

Commit

Permalink
Add generic version of "Sequence" for any number type
Browse files Browse the repository at this point in the history
This is a squashed merge of PR #893 that closes #892.
  • Loading branch information
atifaziz authored Sep 23, 2024
1 parent e52d278 commit 03a160c
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 20 deletions.
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);

/// <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 @@ static partial class MoreEnumerable
/// 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 @@ public static IEnumerable<int> Sequence(int start, int stop)
/// <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)

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 @@ public static IEnumerable<int> Sequence(int start, int stop, int step)
yield return (int)current;
current += step;
}
#endif
}
}
}

0 comments on commit 03a160c

Please sign in to comment.