File: System\Linq\MaxMin.cs
Web Access
Project: src\src\libraries\System.Linq\src\System.Linq.csproj (System.Linq)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
 
namespace System.Linq
{
    public static partial class Enumerable
    {
        private interface IMinMaxCalc<T> where T : struct, IBinaryInteger<T>
        {
            public static abstract bool Compare(T left, T right);
            public static abstract Vector128<T> Compare(Vector128<T> left, Vector128<T> right);
            public static abstract Vector256<T> Compare(Vector256<T> left, Vector256<T> right);
            public static abstract Vector512<T> Compare(Vector512<T> left, Vector512<T> right);
        }
 
        private static T MinMaxInteger<T, TMinMax>(this IEnumerable<T> source)
            where T : struct, IBinaryInteger<T>
            where TMinMax : IMinMaxCalc<T>
        {
            T value;
 
            if (source is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
            }
 
            if (source.TryGetSpan(out ReadOnlySpan<T> span))
            {
                if (span.IsEmpty)
                {
                    ThrowHelper.ThrowNoElementsException();
                }
 
                if (!Vector128.IsHardwareAccelerated || !Vector128<T>.IsSupported || span.Length < Vector128<T>.Count)
                {
                    value = span[0];
                    for (int i = 1; i < span.Length; i++)
                    {
                        if (TMinMax.Compare(span[i], value))
                        {
                            value = span[i];
                        }
                    }
                }
                else if (!Vector256.IsHardwareAccelerated || !Vector256<T>.IsSupported || span.Length < Vector256<T>.Count)
                {
                    ref T current = ref MemoryMarshal.GetReference(span);
                    ref T lastVectorStart = ref Unsafe.Add(ref current, span.Length - Vector128<T>.Count);
 
                    Vector128<T> best = Vector128.LoadUnsafe(ref current);
                    current = ref Unsafe.Add(ref current, Vector128<T>.Count);
 
                    while (Unsafe.IsAddressLessThan(ref current, ref lastVectorStart))
                    {
                        best = TMinMax.Compare(best, Vector128.LoadUnsafe(ref current));
                        current = ref Unsafe.Add(ref current, Vector128<T>.Count);
                    }
                    best = TMinMax.Compare(best, Vector128.LoadUnsafe(ref lastVectorStart));
 
                    value = best[0];
                    for (int i = 1; i < Vector128<T>.Count; i++)
                    {
                        if (TMinMax.Compare(best[i], value))
                        {
                            value = best[i];
                        }
                    }
                }
                else if (!Vector512.IsHardwareAccelerated || !Vector512<T>.IsSupported || span.Length < Vector512<T>.Count)
                {
                    ref T current = ref MemoryMarshal.GetReference(span);
                    ref T lastVectorStart = ref Unsafe.Add(ref current, span.Length - Vector256<T>.Count);
 
                    Vector256<T> best = Vector256.LoadUnsafe(ref current);
                    current = ref Unsafe.Add(ref current, Vector256<T>.Count);
 
                    while (Unsafe.IsAddressLessThan(ref current, ref lastVectorStart))
                    {
                        best = TMinMax.Compare(best, Vector256.LoadUnsafe(ref current));
                        current = ref Unsafe.Add(ref current, Vector256<T>.Count);
                    }
                    best = TMinMax.Compare(best, Vector256.LoadUnsafe(ref lastVectorStart));
 
                    value = best[0];
                    for (int i = 1; i < Vector256<T>.Count; i++)
                    {
                        if (TMinMax.Compare(best[i], value))
                        {
                            value = best[i];
                        }
                    }
                }
                else
                {
                    ref T current = ref MemoryMarshal.GetReference(span);
                    ref T lastVectorStart = ref Unsafe.Add(ref current, span.Length - Vector512<T>.Count);
 
                    Vector512<T> best = Vector512.LoadUnsafe(ref current);
                    current = ref Unsafe.Add(ref current, Vector512<T>.Count);
 
                    while (Unsafe.IsAddressLessThan(ref current, ref lastVectorStart))
                    {
                        best = TMinMax.Compare(best, Vector512.LoadUnsafe(ref current));
                        current = ref Unsafe.Add(ref current, Vector512<T>.Count);
                    }
                    best = TMinMax.Compare(best, Vector512.LoadUnsafe(ref lastVectorStart));
 
                    value = best[0];
                    for (int i = 1; i < Vector512<T>.Count; i++)
                    {
                        if (TMinMax.Compare(best[i], value))
                        {
                            value = best[i];
                        }
                    }
                }
            }
            else
            {
                using (IEnumerator<T> e = source.GetEnumerator())
                {
                    if (!e.MoveNext())
                    {
                        ThrowHelper.ThrowNoElementsException();
                    }
 
                    value = e.Current;
                    while (e.MoveNext())
                    {
                        T x = e.Current;
                        if (TMinMax.Compare(x, value))
                        {
                            value = x;
                        }
                    }
                }
            }
 
            return value;
        }
    }
}