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

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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 @@ -90,20 +90,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
10 changes: 7 additions & 3 deletions MoreLinq/MoreLinq.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
<NeutralLanguage>en-US</NeutralLanguage>
<VersionPrefix>3.3.2</VersionPrefix>
<Authors>MoreLINQ Developers.</Authors>
<TargetFrameworks>net451;netstandard1.0;netstandard2.0;netstandard2.1;net6.0</TargetFrameworks>
<TargetFrameworks>net451;netstandard1.0;netstandard2.0;netstandard2.1;net6.0;net7.0</TargetFrameworks>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to include .net7 should be done on a separate PR.

<Nullable>enable</Nullable>
<!-- Parallel build is disabled to avoid file locking issues during T4 code generation.
See also: https://github.com/dotnet/msbuild/issues/2781 -->
Expand Down Expand Up @@ -192,12 +192,16 @@
<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' Or '$(TargetFramework)' == 'net451' ">
<DefineConstants>$(DefineConstants);NO_BUFFERS</DefineConstants>
<DefineConstants>$(DefineConstants);NO_STATIC_ABSTRACTS;NO_BUFFERS</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard1.0'">
<DefineConstants>$(DefineConstants);NO_BUFFERS;NO_SERIALIZATION_ATTRIBUTES;NO_EXCEPTION_SERIALIZATION;NO_TRACING;NO_COM;NO_ASYNC</DefineConstants>
<DefineConstants>$(DefineConstants);NO_STATIC_ABSTRACTS;NO_BUFFERS;NO_SERIALIZATION_ATTRIBUTES;NO_EXCEPTION_SERIALIZATION;NO_TRACING;NO_COM;NO_ASYNC</DefineConstants>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.0'">
Expand Down
98 changes: 94 additions & 4 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 within the 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 integers 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 equals 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 @@ -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
}
}
}