Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
baff8f3
Add MemoryExtensions.Min/Max
manandre May 17, 2026
6ef9e87
Add specific exception message when span is empty
manandre May 18, 2026
a1023cc
Reject null comparer
manandre May 18, 2026
d214de8
Update documentation
manandre May 18, 2026
f5107bd
Remove cache file
manandre May 18, 2026
93c617a
Apply Copilot suggestions
manandre May 18, 2026
b4fb827
Align implementation on enumerable logic
manandre May 19, 2026
8d32597
Add fast paths
manandre May 19, 2026
69566d1
Add tests for integer types
manandre May 19, 2026
c2c8dfa
Add tests for NaN case
manandre May 19, 2026
55b1cf7
Small fixes
manandre May 19, 2026
0acc339
Reduce code duplication
manandre May 19, 2026
9c71813
Add test
manandre May 19, 2026
9835263
Move new code to a dedicated file
manandre May 20, 2026
9193d7e
Rework span cast for integer fast-path
manandre May 20, 2026
6d34978
Small fix
manandre May 20, 2026
9172c33
Cleanup
manandre May 20, 2026
b9d0ba1
Remove the duplicate logic in System.Linq
manandre May 20, 2026
6182e81
Extra cleanup
manandre May 20, 2026
bd6d541
Add extra tests
manandre May 20, 2026
2eabf0e
Another improvement for System.Linq
manandre May 20, 2026
e04c30e
Reduce comparer checks
manandre May 20, 2026
cc58f4a
More improvement for System.Linq
manandre May 21, 2026
906f624
Refactor IMinMaxDirection implementation for improved clarity and per…
manandre Jun 23, 2026
3073acc
Fix char type handling in MinMaxInteger methods for correct comparisons
manandre Jun 23, 2026
a4e042c
Remove cache file
manandre Jun 23, 2026
0367a7f
Update documentation for comparer parameter in MinMax methods to clar…
manandre Jun 23, 2026
6118075
Fix loop initialization in IMinMaxDirection for correct span iteration
manandre Jun 23, 2026
bd9c3b6
Reorder Max and Min methods for ReadOnlySpan<T> in MemoryExtensions
manandre Jun 23, 2026
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
1 change: 0 additions & 1 deletion src/libraries/System.Linq/src/System.Linq.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
<Compile Include="System\Linq\Lookup.cs" />
<Compile Include="System\Linq\Lookup.SpeedOpt.cs" />
<Compile Include="System\Linq\Max.cs" />
<Compile Include="System\Linq\MaxMin.cs" />
<Compile Include="System\Linq\Min.cs" />
<Compile Include="System\Linq\OfType.cs" />
<Compile Include="System\Linq\OfType.SpeedOpt.cs" />
Expand Down
33 changes: 7 additions & 26 deletions src/libraries/System.Linq/src/System/Linq/Max.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,14 @@

using System.Collections.Generic;
using System.Numerics;
using System.Runtime.Intrinsics;

namespace System.Linq
{
public static partial class Enumerable
{
public static int Max(this IEnumerable<int> source) => MinMaxInteger<int, MaxCalc<int>>(source);
public static int Max(this IEnumerable<int> source) => Max(source, comparer: null);

public static long Max(this IEnumerable<long> source) => MinMaxInteger<long, MaxCalc<long>>(source);

private readonly struct MaxCalc<T> : IMinMaxCalc<T> where T : struct, IBinaryInteger<T>
{
public static bool Compare(T left, T right) => left > right;
public static Vector128<T> Compare(Vector128<T> left, Vector128<T> right) => Vector128.Max(left, right);
public static Vector256<T> Compare(Vector256<T> left, Vector256<T> right) => Vector256.Max(left, right);
public static Vector512<T> Compare(Vector512<T> left, Vector512<T> right) => Vector512.Max(left, right);
}
public static long Max(this IEnumerable<long> source) => Max(source, comparer: null);
Comment thread
MihaZupan marked this conversation as resolved.

public static int? Max(this IEnumerable<int?> source) => MaxInteger(source);

Expand Down Expand Up @@ -317,22 +308,12 @@ public static decimal Max(this IEnumerable<decimal> source)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}

comparer ??= Comparer<TSource>.Default;
if (source.TryGetSpan(out ReadOnlySpan<TSource> span))
{
return span.Max(comparer);
}

// TODO https://github.com/dotnet/csharplang/discussions/6308: Update this to use generic constraint bridging if/when available.
if (typeof(TSource) == typeof(byte) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<byte, MaxCalc<byte>>((IEnumerable<byte>)source);
if (typeof(TSource) == typeof(sbyte) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<sbyte, MaxCalc<sbyte>>((IEnumerable<sbyte>)source);
if (typeof(TSource) == typeof(ushort) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<ushort, MaxCalc<ushort>>((IEnumerable<ushort>)source);
if (typeof(TSource) == typeof(short) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<short, MaxCalc<short>>((IEnumerable<short>)source);
if (typeof(TSource) == typeof(char) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<char, MaxCalc<char>>((IEnumerable<char>)source);
if (typeof(TSource) == typeof(uint) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<uint, MaxCalc<uint>>((IEnumerable<uint>)source);
if (typeof(TSource) == typeof(int) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<int, MaxCalc<int>>((IEnumerable<int>)source);
if (typeof(TSource) == typeof(ulong) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<ulong, MaxCalc<ulong>>((IEnumerable<ulong>)source);
if (typeof(TSource) == typeof(long) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<long, MaxCalc<long>>((IEnumerable<long>)source);
if (typeof(TSource) == typeof(nuint) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<nuint, MaxCalc<nuint>>((IEnumerable<nuint>)source);
if (typeof(TSource) == typeof(nint) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<nint, MaxCalc<nint>>((IEnumerable<nint>)source);
if (typeof(TSource) == typeof(Int128) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<Int128, MaxCalc<Int128>>((IEnumerable<Int128>)source);
if (typeof(TSource) == typeof(UInt128) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<UInt128, MaxCalc<UInt128>>((IEnumerable<UInt128>)source);
comparer ??= Comparer<TSource>.Default;

TSource? value = default;
using IEnumerator<TSource> e = source.GetEnumerator();
Expand Down
170 changes: 0 additions & 170 deletions src/libraries/System.Linq/src/System/Linq/MaxMin.cs

This file was deleted.

33 changes: 7 additions & 26 deletions src/libraries/System.Linq/src/System/Linq/Min.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,14 @@

using System.Collections.Generic;
using System.Numerics;
using System.Runtime.Intrinsics;

namespace System.Linq
{
public static partial class Enumerable
{
public static int Min(this IEnumerable<int> source) => MinMaxInteger<int, MinCalc<int>>(source);
public static int Min(this IEnumerable<int> source) => Min(source, comparer: null);

public static long Min(this IEnumerable<long> source) => MinMaxInteger<long, MinCalc<long>>(source);

private readonly struct MinCalc<T> : IMinMaxCalc<T> where T : struct, IBinaryInteger<T>
{
public static bool Compare(T left, T right) => left < right;
public static Vector128<T> Compare(Vector128<T> left, Vector128<T> right) => Vector128.Min(left, right);
public static Vector256<T> Compare(Vector256<T> left, Vector256<T> right) => Vector256.Min(left, right);
public static Vector512<T> Compare(Vector512<T> left, Vector512<T> right) => Vector512.Min(left, right);
}
public static long Min(this IEnumerable<long> source) => Min(source, comparer: null);
Comment thread
MihaZupan marked this conversation as resolved.

public static int? Min(this IEnumerable<int?> source) => MinInteger(source);

Expand Down Expand Up @@ -296,22 +287,12 @@ public static decimal Min(this IEnumerable<decimal> source)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}

comparer ??= Comparer<TSource>.Default;
if (source.TryGetSpan(out ReadOnlySpan<TSource> span))
{
return span.Min(comparer);
}

// TODO https://github.com/dotnet/csharplang/discussions/6308: Update this to use generic constraint bridging if/when available.
if (typeof(TSource) == typeof(byte) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<byte, MinCalc<byte>>((IEnumerable<byte>)source);
if (typeof(TSource) == typeof(sbyte) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<sbyte, MinCalc<sbyte>>((IEnumerable<sbyte>)source);
if (typeof(TSource) == typeof(ushort) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<ushort, MinCalc<ushort>>((IEnumerable<ushort>)source);
if (typeof(TSource) == typeof(short) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<short, MinCalc<short>>((IEnumerable<short>)source);
if (typeof(TSource) == typeof(char) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<char, MinCalc<char>>((IEnumerable<char>)source);
if (typeof(TSource) == typeof(uint) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<uint, MinCalc<uint>>((IEnumerable<uint>)source);
if (typeof(TSource) == typeof(int) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<int, MinCalc<int>>((IEnumerable<int>)source);
if (typeof(TSource) == typeof(ulong) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<ulong, MinCalc<ulong>>((IEnumerable<ulong>)source);
if (typeof(TSource) == typeof(long) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<long, MinCalc<long>>((IEnumerable<long>)source);
if (typeof(TSource) == typeof(nuint) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<nuint, MinCalc<nuint>>((IEnumerable<nuint>)source);
if (typeof(TSource) == typeof(nint) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<nint, MinCalc<nint>>((IEnumerable<nint>)source);
if (typeof(TSource) == typeof(Int128) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<Int128, MinCalc<Int128>>((IEnumerable<Int128>)source);
if (typeof(TSource) == typeof(UInt128) && comparer == Comparer<TSource>.Default) return (TSource)(object)MinMaxInteger<UInt128, MinCalc<UInt128>>((IEnumerable<UInt128>)source);
comparer ??= Comparer<TSource>.Default;

TSource? value = default;
using IEnumerator<TSource> e = source.GetEnumerator();
Expand Down
4 changes: 4 additions & 0 deletions src/libraries/System.Memory/ref/System.Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,10 @@ public static void CopyTo<T>(this T[]? source, System.Span<T> destination) { }
[System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)]
public static int LastIndexOfAnyInRange<T>(this System.Span<T> span, T lowInclusive, T highInclusive) where T : System.IComparable<T> { throw null; }
public static int LastIndexOfAnyWhiteSpace(this System.ReadOnlySpan<char> span) { throw null; }
public static T? Max<T>(this System.ReadOnlySpan<T> span) { throw null; }
public static T? Max<T>(this System.ReadOnlySpan<T> span, System.Collections.Generic.IComparer<T>? comparer) { throw null; }
public static T? Min<T>(this System.ReadOnlySpan<T> span) { throw null; }
public static T? Min<T>(this System.ReadOnlySpan<T> span, System.Collections.Generic.IComparer<T>? comparer) { throw null; }
public static bool Overlaps<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> other) { throw null; }
public static bool Overlaps<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> other, out int elementOffset) { throw null; }
[System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)]
Expand Down
Loading
Loading