diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 503d190..62da409 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,28 +1,28 @@ # Contributing to SpanExtensions.Net - + ### Did you catch a bug? **!!!Important!!!** -If the bug poses a threat to the security of dependent applications, please refer to our [Security Policy](SECURITY.md). +If the bug poses a threat to the security of dependent applications, please refer to our [Security Policy](SECURITY.md). There are two options available: -1. You can open an issue describing exactly: - - What is the bug and what did you expect to happen? - - How may it be reproduced (It is often helpful to include images or alike). - - Include the Version of SpanExtensions.Net affected. -2. Even better, you can fix the issue yourself by submitting a pull reqest. +1. You can open an issue describing exactly: + - What the bug is and what you expected to happen? + - How it may be reproduced (It is often helpful to include images or alike). + - hich versions of SpanExtensions.Net affected. +2. Even better, fix any issue yourself by submitting a pull request. ### Do you have a feature request? Tell us all about it (here)[]. Please consider however, that features should generally be implementable via Extension Methods, otherwise they are unlikely to be accepted. -### Did you notice a mistake or an error in the documentation? +### Did you notice a mistake or an error in the documentation? -If you found a spelling mistake or an ambigous or unclear sentence or phrase in the XML documentation in C# source files or in any of the documentation for this repository (for example: Readme.md), do one of the following: +If you found a spelling mistake or an ambigous or unclear sentence or phrase in the XML documentation in C# source files or in any of the documentation for this repository (for example: Readme.md), do one of the following: -[Open an **Issue**](https://github.com/draconware-dev/SpanExtensions.Net/issues/new/choose) with the tag `documentation`. It is recommended to follow the [example template](https://github.com/draconware-dev/SpanExtensions.Net/issues/new?assignees=&labels=documentation&projects=&template=documentation-report.md&title=Documentation+Error). +[Open an **Issue**](https://github.com/draconware-dev/SpanExtensions.Net/issues/new/choose) with the tag `documentation`. It is recommended to follow the [example template](https://github.com/draconware-dev/SpanExtensions.Net/issues/new?assignees=&labels=documentation&projects=&template=documentation-report.md&title=Documentation+Error). **OR** -[Open a **Pull Request**](https://github.com/draconware-dev/SpanExtensions.Net/compare) linked to an **Issue** tagged `documentation`, as grammatical errors are usually easier to fix, than they are to describe. In this case it is not necessary to describe the error in detail, a brief report with reasoning should do, unless it is not evident from the Pull Request, what actually was changed or why it was changed. +[Open a **Pull Request**](https://github.com/draconware-dev/SpanExtensions.Net/compare) linked to an **Issue** tagged `documentation`, as grammatical errors are usually easier to fix, than they are to describe. In this case it is not necessary to describe the error in detail, a brief report with reasoning should do, unless it is not evident from the Pull Request, what actually was changed or why it was changed. diff --git a/.github/README.md b/.github/README.md index 014f0cb..ca4ae12 100644 --- a/.github/README.md +++ b/.github/README.md @@ -5,84 +5,17 @@ [![GitHub License](https://img.shields.io/github/license/draconware-dev/SpanExtensions.Net)](https://github.com/draconware-dev/SpanExtensions.Net/blob/main/LICENSE) ## About -**`ReadonlySpan`** and **`Span`** are great Types in _C#_, but unfortunately working with them can sometimes be sort of a hassle and some use cases seem straight up impossible, even though they are not. -**SpanExtensions.Net** aims to help developers use `ReadonlySpan` and `Span` more **productively**, **efficiently** and **safely** and write overall more **performant** Programs. - -Never again switch back to using `string` instead of `ReadonlySpan`, just because the method you seek is not supported. - -**SpanExtensions.Net** provides alternatives for many missing Extension Methods for **`ReadonlySpan`** and **`Span`**, ranging from `string.Split()` over `Enumerable.Skip()` and `Enumerable.Take()` to an improved `ReadOnlySpan.IndexOf()`. +Never again switch back to using `string` instead of `ReadonlySpan`, just because the method you seek is not supported. -## Methods -The following **Extension Methods** are contained: - -#### String Methods made available for **`ReadonlySpan`** and **`Span`**: - -- `(ReadOnly-)Span.Split(T delimiter)` -- `(ReadOnly-)Span.Split(T delimiter, int count)` -- `(ReadOnly-)Span.Split(T delimiter, StringSplitOptions options)` -- `(ReadOnly-)Span.Split(T delimiter, StringSplitOptions options, int count)` -- `(ReadOnly-)Span.Split(ReadOnlySpan delimiters)` -- `(ReadOnly-)Span.Split(ReadOnlySpan delimiters, int count)` -- `(ReadOnly-)Span.Split(ReadOnlySpan delimiters, StringSplitOptions options)` -- `(ReadOnly-)Span.Split(ReadOnlySpan delimiters, StringSplitOptions options, int count)` -- `(ReadOnly-)Span.SplitAny(ReadOnlySpan delimiters)` -- `(ReadOnly-)Span.SplitAny(ReadOnlySpan delimiters, int count)` -- `(ReadOnly-)Span.SplitAny(ReadOnlySpan delimiters, StringSplitOptions options)` -- `(ReadOnly-)Span.SplitAny(ReadOnlySpan delimiters, StringSplitOptions options, int count)` -- `(ReadOnly-)Span.Remove(int startIndex)` -- `Span.Replace(T oldT, T newT)` - -#### Linq Methods made available for **`ReadonlySpan`** and **`Span`**: - -- `(ReadOnly-)Span.All(Predicate predicate)` -- `(ReadOnly-)Span.Any(Predicate predicate)` -- `(ReadOnly-)Span.Average()` -- `(ReadOnly-)Span.Sum()` -- `(ReadOnly-)Span.Skip(int count)` -- `(ReadOnly-)Span.Take(int count)` -- `(ReadOnly-)Span.SkipLast(int count)` -- `(ReadOnly-)Span.Takelast(int count)` -- `(ReadOnly-)Span.SkipWhile(Predicate condition)` -- `(ReadOnly-)Span.TakeWhile(Predicate condition)` -- `(Readonly-)Span.First()` -- `(Readonly-)Span.First(Predicate predicate)` -- `(Readonly-)Span.FirstOrDefault()` -- `(Readonly-)Span.FirstOrDefault(Predicate predicate)` -- `(Readonly-)Span.FirstOrDefault(T defaultValue)` -- `(Readonly-)Span.FirstOrDefault(Predicate predicate, T defaultValue)` -- `(Readonly-)Span.Last()` -- `(Readonly-)Span.Last(Predicate predicate)` -- `(Readonly-)Span.LastOrDefault()` -- `(Readonly-)Span.LastOrDefault(Predicate predicate)` -- `(Readonly-)Span.LastOrDefault(T defaultValue)` -- `(Readonly-)Span.LastOrDefault(Predicate predicate, T defaultValue)` -- `(Readonly-)Span.Single()` -- `(Readonly-)Span.Single(Predicate predicate)` -- `(Readonly-)Span.SingleOrDefault()` -- `(Readonly-)Span.SingleOrDefault(Predicate predicate)` -- `(Readonly-)Span.SingleOrDefault(T defaultValue)` -- `(Readonly-)Span.SingleOrDefault(Predicate predicate, T defaultValue)` -- `(Readonly-)Span.ElementAt(int index)` -- `(Readonly-)Span.ElementAt(Index index)` -- `(Readonly-)Span.ElementAtOrDefault(int index)` -- `(Readonly-)Span.ElementAtOrDefault(Index index)` -- `(Readonly-)Span.ElementAtOrDefault(int index, T defaultValue)` -- `(Readonly-)Span.ElementAtOrDefault(Index index, T defaultValue)` -- `(Readonly-)Span.Min()` -- `(Readonly-)Span.Min(Func selector)` -- `(Readonly-)Span.MinBy(Func keySelector)` -- `(Readonly-)Span.MinBy(Func keySelector, IComparer comparer)` -- `(Readonly-)Span.Max()` -- `(Readonly-)Span.Max(Func selector)` -- `(Readonly-)Span.MaxBy(Func keySelector)` -- `(Readonly-)Span.MaxBy(Func keySelector, IComparer comparer)` +**SpanExtensions.Net** aims to help developers use `ReadonlySpan` and `Span` more **productively**, **efficiently** and **safely** and write overall more **performant** Programs. ## Contributing Thank you for your interest in contributing to this project - Please see [Contributing](CONTRIBUTING.md)! + ## License -Copyright (c) draconware-dev. All rights reserved. +Copyright (c) draconware-dev. All rights reserved. Licensed under the [MIT](../LICENSE) License. diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 9cf7c9c..d960c75 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -2,4 +2,4 @@ ## Reporting a Vulnerability -If you find a security vulnerability please message **draconware@gmail.com** about the vulnerability and do not, **under no circumstances**, open a public issue. +If you find a security vulnerability please message **draconware@gmail.com** about the vulnerability and do not, **under no circumstances**, open a public issue. diff --git a/Changelog.md b/Changelog.md index a426767..7c4583d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,7 +3,30 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), -and this project adheres not (yet) to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) from v2.0 onwards. + +## [2.0.0] - 2025-9-28 + +### Fixed + +- an empty array not being treated as whitespace by range-based Split methods (https://github.com/draconware-dev/SpanExtensions.Net/pull/27) +- the SearchValues-overload of range-based Split methods. + +### Changed + +- vectorized `Min()` and `Max()`. +- transitioned to semantic versioning. + +## [1.5.1] - 2024-12-14 + +### Fixed + +- incorrect ranges returned by the range-based Split method for versions prior to .Net 9. + +### Changed + +- moved MemoryExtensions containing range-based Split method for versions prior to .Net 9 from `System` to `SpanExtensions`. +- grammatical issues in some documentation comments. ## [1.5.1] - 2024-12-14 @@ -22,49 +45,49 @@ and this project adheres not (yet) to [Semantic Versioning](https://semver.org/s - implementations of the newly introduced Span.Split methods from .Net 9 for any version prior to .Net 9 to maintain backwards-compatibility across .Net versions. -### Changed +### Changed - Split extension methods to refer to new split implementations compatible to the ones in .Net 9 and made .Net 9 split methods the default from that version onwards. The original split methods are still accessible as static methods. - original Split methods are no longer available without passing span as a parameter. ## [1.4.2] - 2024-10-29 -### Added +### Added - `(Readonly-)Span.Count(...)` overloads to all versions before .Net 8 matching these introduced in .Net 8. -### Changed +### Changed -- blocked compilation on .Net 9 due to known incompatibilities, which are to be resolved in version 1.5. +- blocked compilation on .Net 9 due to known incompatibilities, which are to be resolved in version 1.5. ## [1.4.1] - 2024-9-9 -### Fixed +### Fixed - a collision between the `Span.Replace` method provided by SpanExtensions and the one newly provided by .Net 8. ## [1.4] - 2024-9-3 -### Added +### Added - `(Readonly-)Span.Count()` - `(Readonly-)Span.Count(Predicate predicate)` -### Changed +### Changed - `Split` to throw an `ArgumentException` instead of an `InvalidCountExceedingBehaviourException` -### Removed +### Removed - `InvalidCountExceedingBehaviourException` -### Fixed +### Fixed -- various issues with `Split` (https://github.com/draconware-dev/SpanExtensions.Net/pull/11) +- various issues with `Split` (https://github.com/draconware-dev/SpanExtensions.Net/pull/11) ## [1.3] - 2024-3-19 -### Added +### Added - Compatibility with **.Net 8** - `(Readonly-)Span.First()` @@ -102,42 +125,42 @@ and this project adheres not (yet) to [Semantic Versioning](https://semver.org/s - nuget badge to README (https://github.com/draconware-dev/SpanExtensions.Net/pull/12) - `CountExceedingBehaviour`, which is passed to Split, defining how to properly handle its remaining elements. -### Changed +### Changed - documentation comments to better reflect the dotnet style (https://github.com/draconware-dev/SpanExtensions.Net/pull/8) - swapped order of `count` and `stringSplitOptions arguments` in `Split` methods. - renamed argument `span` in `Split` methods to `source`. -### Fixed +### Fixed - empty spans being ignored if they are the last element to be returned from `Split` and are therefore not returned. (https://github.com/draconware-dev/SpanExtensions.Net/pull/10) ## [1.2.1] - 2024-1-25 -### Fixed +### Fixed - Ambiguous Extension Methods (https://github.com/draconware-dev/SpanExtensions.Net/issues/6) -- Correctness of some documentation comments +- Correctness of some documentation comments ## [1.2.0] - 2023-12-28 -### Added +### Added - Missing documentation comments -### Fixed +### Fixed - Grammatical issues in some documentation comments -### Changed +### Changed -- moved custom Enumerators into `SpanExtensions.Enumerators` +- moved custom Enumerators into `SpanExtensions.Enumerators` - declared every `GetEnumerator` method in a ref struct as `readonly` -- renamed the source ReadOnlySpan in 10 out of 12 custom Enumerators from *span* to *source* +- renamed the source ReadOnlySpan in 10 out of 12 custom Enumerators from _span_ to _source_ ## [1.1.0] - 2023-11-4 -### Added +### Added - Compatibility with **.Net 6** - Compatibility with **.Net 5** @@ -149,7 +172,7 @@ and this project adheres not (yet) to [Semantic Versioning](https://semver.org/s - Missing documentation comments - Changelog -### Fixed +### Fixed - Grammatical issues in some documentation comments - Broken link to the repository on nuget ([#3](https://github.com/draconware-dev/SpanExtensions.Net/pull/3)) diff --git a/LICENSE b/LICENSE index 128eeaf..81f048e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 draconware-dev +Copyright (c) 2025 draconware-dev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/SpanExtensions.sln b/SpanExtensions.sln index b9f383d..3059f5c 100644 --- a/SpanExtensions.sln +++ b/SpanExtensions.sln @@ -15,16 +15,16 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {75DE5AFD-663E-415D-9B95-6BC513BD4A07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {75DE5AFD-663E-415D-9B95-6BC513BD4A07}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75DE5AFD-663E-415D-9B95-6BC513BD4A07}.Debug|Any CPU.ActiveCfg = Release|Any CPU + {75DE5AFD-663E-415D-9B95-6BC513BD4A07}.Debug|Any CPU.Build.0 = Release|Any CPU {75DE5AFD-663E-415D-9B95-6BC513BD4A07}.Release|Any CPU.ActiveCfg = Release|Any CPU {75DE5AFD-663E-415D-9B95-6BC513BD4A07}.Release|Any CPU.Build.0 = Release|Any CPU {B48A0293-A7FF-4E39-8F8D-57B6F824D70F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B48A0293-A7FF-4E39-8F8D-57B6F824D70F}.Debug|Any CPU.Build.0 = Debug|Any CPU {B48A0293-A7FF-4E39-8F8D-57B6F824D70F}.Release|Any CPU.ActiveCfg = Release|Any CPU {B48A0293-A7FF-4E39-8F8D-57B6F824D70F}.Release|Any CPU.Build.0 = Release|Any CPU - {CE74F420-1038-4E51-AC31-00DA964CC4F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CE74F420-1038-4E51-AC31-00DA964CC4F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE74F420-1038-4E51-AC31-00DA964CC4F5}.Debug|Any CPU.ActiveCfg = Release|Any CPU + {CE74F420-1038-4E51-AC31-00DA964CC4F5}.Debug|Any CPU.Build.0 = Release|Any CPU {CE74F420-1038-4E51-AC31-00DA964CC4F5}.Release|Any CPU.ActiveCfg = Release|Any CPU {CE74F420-1038-4E51-AC31-00DA964CC4F5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection diff --git a/src/Enumerators/System/SpanSplitEnumerator.cs b/src/Enumerators/System/SpanSplitEnumerator.cs index d4a4f68..eede70c 100644 --- a/src/Enumerators/System/SpanSplitEnumerator.cs +++ b/src/Enumerators/System/SpanSplitEnumerator.cs @@ -1,7 +1,5 @@ using System; using System.Buffers; -using System.Diagnostics; -using System.Runtime.CompilerServices; #if !NET9_0_OR_GREATER @@ -62,7 +60,7 @@ internal SpanSplitEnumerator(ReadOnlySpan source, SearchValues searchValue Delimiter = default!; SearchValues = searchValues; DelimiterSpan = default; - mode = SpanSplitEnumeratorMode.Delimiter; + mode = SpanSplitEnumeratorMode.SearchValues; currentStartIndex = 0; currentEndIndex = 0; nextStartIndex = 0; @@ -118,19 +116,17 @@ public bool MoveNext() } currentStartIndex = nextStartIndex; - + if(index < 0) { currentEndIndex = Span.Length; nextStartIndex = Span.Length; - mode = (SpanSplitEnumeratorMode)(-1); return true; } currentEndIndex = currentStartIndex + index; nextStartIndex = currentEndIndex + length; - return true; } } diff --git a/src/ExceptionHelpers.cs b/src/ExceptionHelpers.cs index a484ee5..3327e09 100644 --- a/src/ExceptionHelpers.cs +++ b/src/ExceptionHelpers.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using SpanExtensions; @@ -37,6 +36,7 @@ internal static void ThrowIfGreaterThanOrEqual(T value, T other, ThrowGreaterThanOrEqual(value, other, paramName); } } + internal static void ThrowIfLessThan(T value, T other, #if NET8_0_OR_GREATER [CallerArgumentExpression(nameof(value))] @@ -139,11 +139,24 @@ internal static void ThrowIfNegative(int value, } #endif + internal static void ThrowIfNull([NotNull] object? value, +#if NET8_0_OR_GREATER + [CallerArgumentExpression(nameof(value))] +#endif + string? paramName = null) + { + if(value is null) + { + ThrowNull(value, paramName); + } + } + [DoesNotReturn] static void ThrowGreaterThanOrEqual(T value, T other, string? paramName) { throw new ArgumentOutOfRangeException(paramName, value, $"{paramName} ('{value}') must be less than '{other}'. (Parameter '{paramName}')"); } + [DoesNotReturn] static void ThrowLessThan(T value, T other, string? paramName) { @@ -155,4 +168,10 @@ static void ThrowNegative(T value, string? paramName) { throw new ArgumentOutOfRangeException(paramName, value, $"{paramName} ('{value}') must be a non-negative value. (Parameter '{paramName}')"); } + + [DoesNotReturn] + static void ThrowNull(object? value, string? paramName) + { + throw new ArgumentNullException(paramName); + } } \ No newline at end of file diff --git a/src/Extensions/ReadOnlySpan/Linq/Max.cs b/src/Extensions/ReadOnlySpan/Linq/Max.cs index bf91b55..b9ef6ee 100644 --- a/src/Extensions/ReadOnlySpan/Linq/Max.cs +++ b/src/Extensions/ReadOnlySpan/Linq/Max.cs @@ -1,5 +1,12 @@ using System; using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#if NETCOREAPP3_0_OR_GREATER +using System.Runtime.Intrinsics; +#endif + namespace SpanExtensions { @@ -7,25 +14,1235 @@ public static partial class ReadOnlySpanExtensions { #if NET7_0_OR_GREATER +#if NET8_0_OR_GREATER + + /// + /// Returns the maximum value in . + /// + /// The type of elements in . + /// A to determine the maximum value of. + /// The maximum value in . + public static T Max(this ReadOnlySpan source) where T : IComparable + { + if(Vector512.IsHardwareAccelerated && Vector512.IsSupported && source.Length > Vector512.Count) + { + ref T current = ref MemoryMarshal.GetReference(source); + ref T secondToLast = ref Unsafe.Add(ref current, source.Length - Vector512.Count); + + Vector512 maxVector = Vector512.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector512.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector512.Max(maxVector, Vector512.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector512.Count); + } + + maxVector = Vector512.Max(maxVector, Vector512.LoadUnsafe(ref secondToLast)); + + T result = maxVector[0]; + + for(int i = 1; i < Vector512.Count; i++) + { + T currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref T current = ref MemoryMarshal.GetReference(source); + ref T secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 maxVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref secondToLast)); + + T result = maxVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + T currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count * 2) + { + ref T current = ref MemoryMarshal.GetReference(source); + ref T secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 maxVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref secondToLast)); + + T result = maxVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + T currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count * 4) + { + ref T current = ref MemoryMarshal.GetReference(source); + ref T secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 maxVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref secondToLast)); + + T result = maxVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + T currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + T max = source[0]; + + for(int i = 1; i < source.Length; i++) + { + T current = source[i]; + + if(current.CompareTo(max) > 0) + { + max = current; + } + } + + return max; + } +#else + /// + /// Returns the maximum value in . + /// + /// The type of elements in . + /// A to determine the maximum value of. + /// The maximum value in . + public static T Max(this ReadOnlySpan source) where T : IComparable + { + T max = source[0]; + + for(int i = 1; i < source.Length; i++) + { + T current = source[i]; + + if(current.CompareTo(max) > 0) + { + max = current; + } + } + + return max; + } + + /// + /// Returns the maximum value in . + /// + /// A to determine the maximum value of. + /// The maximum value in . + public static byte Max(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref byte current = ref MemoryMarshal.GetReference(source); + ref byte secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 maxVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref secondToLast)); + + byte result = maxVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + byte currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref byte current = ref MemoryMarshal.GetReference(source); + ref byte secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 maxVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref secondToLast)); + + byte result = maxVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + byte currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref byte current = ref MemoryMarshal.GetReference(source); + ref byte secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 maxVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref secondToLast)); + + byte result = maxVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + byte currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + byte max = source[0]; + + for(int i = 1; i < source.Length; i++) + { + byte current = source[i]; + + if(current.CompareTo(max) > 0) + { + max = current; + } + } + + return max; + } + + /// + /// Returns the maximum value in . + /// + /// A to determine the maximum value of. + /// The maximum value in . + public static ushort Max(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref ushort current = ref MemoryMarshal.GetReference(source); + ref ushort secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 maxVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref secondToLast)); + + ushort result = maxVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + ushort currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref ushort current = ref MemoryMarshal.GetReference(source); + ref ushort secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 maxVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref secondToLast)); + + ushort result = maxVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + ushort currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref ushort current = ref MemoryMarshal.GetReference(source); + ref ushort secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 maxVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref secondToLast)); + + ushort result = maxVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + ushort currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + ushort max = source[0]; + + for(int i = 1; i < source.Length; i++) + { + ushort current = source[i]; + + if(current.CompareTo(max) > 0) + { + max = current; + } + } + + return max; + } + + /// + /// Returns the maximum value in . + /// + /// A to determine the maximum value of. + /// The maximum value in . + public static uint Max(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref uint current = ref MemoryMarshal.GetReference(source); + ref uint secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 maxVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref secondToLast)); + + uint result = maxVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + uint currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref uint current = ref MemoryMarshal.GetReference(source); + ref uint secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 maxVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref secondToLast)); + + uint result = maxVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + uint currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref uint current = ref MemoryMarshal.GetReference(source); + ref uint secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 maxVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref secondToLast)); + + uint result = maxVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + uint currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + uint max = source[0]; + + for(int i = 1; i < source.Length; i++) + { + uint current = source[i]; + + if(current.CompareTo(max) > 0) + { + max = current; + } + } + + return max; + } + + /// + /// Returns the maximum value in . + /// + /// A to determine the maximum value of. + /// The maximum value in . + public static ulong Max(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref ulong current = ref MemoryMarshal.GetReference(source); + ref ulong secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 maxVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref secondToLast)); + + ulong result = maxVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + ulong currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref ulong current = ref MemoryMarshal.GetReference(source); + ref ulong secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 maxVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref secondToLast)); + + ulong result = maxVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + ulong currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + ulong max = source[0]; + + for(int i = 1; i < source.Length; i++) + { + ulong current = source[i]; + + if(current.CompareTo(max) > 0) + { + max = current; + } + } + + return max; + } + + /// + /// Returns the maximum value in . + /// + /// A to determine the maximum value of. + /// The maximum value in . + public static sbyte Max(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref sbyte current = ref MemoryMarshal.GetReference(source); + ref sbyte secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 maxVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref secondToLast)); + + sbyte result = maxVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + sbyte currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref sbyte current = ref MemoryMarshal.GetReference(source); + ref sbyte secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 maxVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref secondToLast)); + + sbyte result = maxVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + sbyte currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref sbyte current = ref MemoryMarshal.GetReference(source); + ref sbyte secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 maxVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref secondToLast)); + + sbyte result = maxVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + sbyte currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + sbyte max = source[0]; + + for(int i = 1; i < source.Length; i++) + { + sbyte current = source[i]; + + if(current.CompareTo(max) > 0) + { + max = current; + } + } + + return max; + } + + /// + /// Returns the maximum value in . + /// + /// A to determine the maximum value of. + /// The maximum value in . + public static short Max(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref short current = ref MemoryMarshal.GetReference(source); + ref short secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 maxVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref secondToLast)); + + short result = maxVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + short currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref short current = ref MemoryMarshal.GetReference(source); + ref short secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 maxVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref secondToLast)); + + short result = maxVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + short currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref short current = ref MemoryMarshal.GetReference(source); + ref short secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 maxVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref secondToLast)); + + short result = maxVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + short currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + short max = source[0]; + + for(int i = 1; i < source.Length; i++) + { + short current = source[i]; + + if(current.CompareTo(max) > 0) + { + max = current; + } + } + + return max; + } + + /// + /// Returns the maximum value in . + /// + /// A to determine the maximum value of. + /// The maximum value in . + public static int Max(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref int current = ref MemoryMarshal.GetReference(source); + ref int secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 maxVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref secondToLast)); + + int result = maxVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + int currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref int current = ref MemoryMarshal.GetReference(source); + ref int secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 maxVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref secondToLast)); + + int result = maxVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + int currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref int current = ref MemoryMarshal.GetReference(source); + ref int secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 maxVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref secondToLast)); + + int result = maxVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + int currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + int max = source[0]; + + for(int i = 1; i < source.Length; i++) + { + int current = source[i]; + + if(current.CompareTo(max) > 0) + { + max = current; + } + } + + return max; + } + + /// + /// Returns the maximum value in . + /// + /// A to determine the maximum value of. + /// The maximum value in . + public static long Max(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref long current = ref MemoryMarshal.GetReference(source); + ref long secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 maxVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref secondToLast)); + + long result = maxVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + long currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref long current = ref MemoryMarshal.GetReference(source); + ref long secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 maxVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref secondToLast)); + + long result = maxVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + long currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + long max = source[0]; + + for(int i = 1; i < source.Length; i++) + { + long current = source[i]; + + if(current.CompareTo(max) > 0) + { + max = current; + } + } + + return max; + } + /// /// Returns the maximum value in . /// - /// The type of elements in . - /// A to determine the maximum value of. + /// A to determine the maximum value of. /// The maximum value in . - public static T Max(this ReadOnlySpan source) where T : IComparable + public static float Max(this ReadOnlySpan source) { - T max = source[0]; + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref float current = ref MemoryMarshal.GetReference(source); + ref float secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 maxVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref secondToLast)); + + float result = maxVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + float currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref float current = ref MemoryMarshal.GetReference(source); + ref float secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 maxVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref secondToLast)); + + float result = maxVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + float currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref float current = ref MemoryMarshal.GetReference(source); + ref float secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 maxVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + maxVector = Vector64.Max(maxVector, Vector64.LoadUnsafe(ref secondToLast)); + + float result = maxVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + float currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + float max = source[0]; + for(int i = 1; i < source.Length; i++) { - T current = source[i]; + float current = source[i]; + + if(current.CompareTo(max) > 0) + { + max = current; + } + } + + return max; + } + + /// + /// Returns the maximum value in . + /// + /// A to determine the maximum value of. + /// The maximum value in . + public static double Max(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref double current = ref MemoryMarshal.GetReference(source); + ref double secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 maxVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + maxVector = Vector256.Max(maxVector, Vector256.LoadUnsafe(ref secondToLast)); + + double result = maxVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + double currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref double current = ref MemoryMarshal.GetReference(source); + ref double secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 maxVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + maxVector = Vector128.Max(maxVector, Vector128.LoadUnsafe(ref secondToLast)); + + double result = maxVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + double currentResult = maxVector[i]; + + if(currentResult.CompareTo(result) > 0) + { + result = currentResult; + } + } + + return result; + } + + double max = source[0]; + + for(int i = 1; i < source.Length; i++) + { + double current = source[i]; + if(current.CompareTo(max) > 0) { max = current; } } + return max; } +#endif /// /// Invokes a transform function on each element in and returns the maximum resulting value. @@ -42,15 +1259,18 @@ public static TResult Max(this ReadOnlySpan source, F TSource first = source[0]; TResult max = selector(first); + for(int i = 1; i < source.Length; i++) { TSource value = source[i]; TResult current = selector(value); + if(current.CompareTo(max) > 0) { max = current; } } + return max; } #else @@ -114,14 +1334,17 @@ public static Half Max(this ReadOnlySpan source, Func selector) public static byte Max(this ReadOnlySpan source) { byte max = source[0]; + for(int i = 1; i < source.Length; i++) { byte current = source[i]; + if(current > max) { max = current; } } + return max; } @@ -133,14 +1356,17 @@ public static byte Max(this ReadOnlySpan source) public static ushort Max(this ReadOnlySpan source) { ushort max = source[0]; + for(int i = 1; i < source.Length; i++) { ushort current = source[i]; + if(current > max) { max = current; } } + return max; } @@ -152,14 +1378,17 @@ public static ushort Max(this ReadOnlySpan source) public static uint Max(this ReadOnlySpan source) { uint max = source[0]; + for(int i = 1; i < source.Length; i++) { uint current = source[i]; + if(current > max) { max = current; } } + return max; } @@ -171,14 +1400,17 @@ public static uint Max(this ReadOnlySpan source) public static ulong Max(this ReadOnlySpan source) { ulong max = source[0]; + for(int i = 1; i < source.Length; i++) { ulong current = source[i]; + if(current > max) { max = current; } } + return max; } @@ -190,14 +1422,17 @@ public static ulong Max(this ReadOnlySpan source) public static sbyte Max(this ReadOnlySpan source) { sbyte max = source[0]; + for(int i = 1; i < source.Length; i++) { sbyte current = source[i]; + if(current > max) { max = current; } } + return max; } @@ -209,14 +1444,17 @@ public static sbyte Max(this ReadOnlySpan source) public static short Max(this ReadOnlySpan source) { short max = source[0]; + for(int i = 1; i < source.Length; i++) { short current = source[i]; + if(current > max) { max = current; } } + return max; } @@ -228,14 +1466,17 @@ public static short Max(this ReadOnlySpan source) public static int Max(this ReadOnlySpan source) { int max = source[0]; + for(int i = 1; i < source.Length; i++) { int current = source[i]; + if(current > max) { max = current; } } + return max; } @@ -247,14 +1488,17 @@ public static int Max(this ReadOnlySpan source) public static long Max(this ReadOnlySpan source) { long max = source[0]; + for(int i = 1; i < source.Length; i++) { long current = source[i]; + if(current > max) { max = current; } } + return max; } @@ -266,14 +1510,17 @@ public static long Max(this ReadOnlySpan source) public static float Max(this ReadOnlySpan source) { float max = source[0]; + for(int i = 1; i < source.Length; i++) { float current = source[i]; + if(current > max) { max = current; } } + return max; } @@ -285,14 +1532,17 @@ public static float Max(this ReadOnlySpan source) public static double Max(this ReadOnlySpan source) { double max = source[0]; + for(int i = 1; i < source.Length; i++) { double current = source[i]; + if(current > max) { max = current; } } + return max; } @@ -304,14 +1554,17 @@ public static double Max(this ReadOnlySpan source) public static decimal Max(this ReadOnlySpan source) { decimal max = source[0]; + for(int i = 1; i < source.Length; i++) { decimal current = source[i]; + if(current > max) { max = current; } } + return max; } @@ -323,14 +1576,17 @@ public static decimal Max(this ReadOnlySpan source) public static BigInteger Max(this ReadOnlySpan source) { BigInteger max = source[0]; + for(int i = 1; i < source.Length; i++) { BigInteger current = source[i]; + if(current > max) { max = current; } } + return max; } @@ -351,15 +1607,18 @@ public static byte Max(this ReadOnlySpan source, Func selector) T first = source[0]; byte max = selector(first); + for(int i = 1; i < source.Length; i++) { T value = source[i]; byte current = selector(value); + if(current > max) { max = current; } } + return max; } @@ -380,15 +1639,18 @@ public static ushort Max(this ReadOnlySpan source, Func selecto T first = source[0]; ushort max = selector(first); + for(int i = 1; i < source.Length; i++) { T value = source[i]; ushort current = selector(value); + if(current > max) { max = current; } } + return max; } @@ -409,15 +1671,18 @@ public static uint Max(this ReadOnlySpan source, Func selector) T first = source[0]; uint max = selector(first); + for(int i = 1; i < source.Length; i++) { T value = source[i]; uint current = selector(value); + if(current > max) { max = current; } } + return max; } @@ -438,15 +1703,18 @@ public static ulong Max(this ReadOnlySpan source, Func selector) T first = source[0]; ulong max = selector(first); + for(int i = 1; i < source.Length; i++) { T value = source[i]; ulong current = selector(value); + if(current > max) { max = current; } } + return max; } @@ -460,22 +1728,22 @@ public static ulong Max(this ReadOnlySpan source, Func selector) /// is null. public static sbyte Max(this ReadOnlySpan source, Func selector) { - if(selector is null) - { - throw new ArgumentNullException(nameof(selector)); - } + ExceptionHelpers.ThrowIfNull(selector, nameof(selector)); T first = source[0]; sbyte max = selector(first); + for(int i = 1; i < source.Length; i++) { T value = source[i]; sbyte current = selector(value); + if(current > max) { max = current; } } + return max; } @@ -489,22 +1757,22 @@ public static sbyte Max(this ReadOnlySpan source, Func selector) /// is null. public static short Max(this ReadOnlySpan source, Func selector) { - if(selector is null) - { - throw new ArgumentNullException(nameof(selector)); - } + ExceptionHelpers.ThrowIfNull(selector, nameof(selector)); T first = source[0]; short max = selector(first); + for(int i = 1; i < source.Length; i++) { T value = source[i]; short current = selector(value); + if(current > max) { max = current; } } + return max; } @@ -518,22 +1786,22 @@ public static short Max(this ReadOnlySpan source, Func selector) /// is null. public static int Max(this ReadOnlySpan source, Func selector) { - if(selector is null) - { - throw new ArgumentNullException(nameof(selector)); - } + ExceptionHelpers.ThrowIfNull(selector, nameof(selector)); T first = source[0]; int max = selector(first); + for(int i = 1; i < source.Length; i++) { T value = source[i]; int current = selector(value); + if(current > max) { max = current; } } + return max; } @@ -547,22 +1815,22 @@ public static int Max(this ReadOnlySpan source, Func selector) /// is null. public static long Max(this ReadOnlySpan source, Func selector) { - if(selector is null) - { - throw new ArgumentNullException(nameof(selector)); - } + ExceptionHelpers.ThrowIfNull(selector, nameof(selector)); T first = source[0]; long max = selector(first); + for(int i = 1; i < source.Length; i++) { T value = source[i]; long current = selector(value); + if(current > max) { max = current; } } + return max; } @@ -576,22 +1844,22 @@ public static long Max(this ReadOnlySpan source, Func selector) /// is null. public static float Max(this ReadOnlySpan source, Func selector) { - if(selector is null) - { - throw new ArgumentNullException(nameof(selector)); - } + ExceptionHelpers.ThrowIfNull(selector, nameof(selector)); T first = source[0]; float max = selector(first); + for(int i = 1; i < source.Length; i++) { T value = source[i]; float current = selector(value); + if(current > max) { max = current; } } + return max; } @@ -605,22 +1873,22 @@ public static float Max(this ReadOnlySpan source, Func selector) /// is null. public static double Max(this ReadOnlySpan source, Func selector) { - if(selector is null) - { - throw new ArgumentNullException(nameof(selector)); - } + ExceptionHelpers.ThrowIfNull(selector, nameof(selector)); T first = source[0]; double max = selector(first); + for(int i = 1; i < source.Length; i++) { T value = source[i]; double current = selector(value); + if(current > max) { max = current; } } + return max; } @@ -634,22 +1902,22 @@ public static double Max(this ReadOnlySpan source, Func selecto /// is null. public static decimal Max(this ReadOnlySpan source, Func selector) { - if(selector is null) - { - throw new ArgumentNullException(nameof(selector)); - } + ExceptionHelpers.ThrowIfNull(selector, nameof(selector)); T first = source[0]; decimal max = selector(first); + for(int i = 1; i < source.Length; i++) { T value = source[i]; decimal current = selector(value); + if(current > max) { max = current; } } + return max; } @@ -663,22 +1931,22 @@ public static decimal Max(this ReadOnlySpan source, Func selec /// is null. public static BigInteger Max(this ReadOnlySpan source, Func selector) { - if(selector is null) - { - throw new ArgumentNullException(nameof(selector)); - } + ExceptionHelpers.ThrowIfNull(selector, nameof(selector)); T first = source[0]; BigInteger max = selector(first); + for(int i = 1; i < source.Length; i++) { T value = source[i]; BigInteger current = selector(value); + if(current > max) { max = current; } } + return max; } #endif diff --git a/src/Extensions/ReadOnlySpan/Linq/Min.cs b/src/Extensions/ReadOnlySpan/Linq/Min.cs index cffb0b5..9067e6b 100644 --- a/src/Extensions/ReadOnlySpan/Linq/Min.cs +++ b/src/Extensions/ReadOnlySpan/Linq/Min.cs @@ -1,5 +1,11 @@ using System; using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#if NETCOREAPP3_0_OR_GREATER +using System.Runtime.Intrinsics; +#endif namespace SpanExtensions { @@ -7,6 +13,8 @@ public static partial class ReadOnlySpanExtensions { #if NET7_0_OR_GREATER +#if NET8_0_OR_GREATER + /// /// Returns the minimum value in . /// @@ -15,18 +23,1227 @@ public static partial class ReadOnlySpanExtensions /// The minimum value in . public static T Min(this ReadOnlySpan source) where T : IComparable { + if(Vector512.IsHardwareAccelerated && Vector512.IsSupported && source.Length > Vector512.Count) + { + ref T current = ref MemoryMarshal.GetReference(source); + ref T secondToLast = ref Unsafe.Add(ref current, source.Length - Vector512.Count); + + Vector512 minVector = Vector512.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector512.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector512.Min(minVector, Vector512.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector512.Count); + } + + minVector = Vector512.Min(minVector, Vector512.LoadUnsafe(ref secondToLast)); + + T result = minVector[0]; + + for(int i = 1; i < Vector512.Count; i++) + { + T currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref T current = ref MemoryMarshal.GetReference(source); + ref T secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 minVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref secondToLast)); + + T result = minVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + T currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count * 2) + { + ref T current = ref MemoryMarshal.GetReference(source); + ref T secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 minVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref secondToLast)); + + T result = minVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + T currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count * 4) + { + ref T current = ref MemoryMarshal.GetReference(source); + ref T secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 minVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref secondToLast)); + + T result = minVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + T currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + T min = source[0]; + for(int i = 1; i < source.Length; i++) { T current = source[i]; + if(current.CompareTo(min) < 0) { min = current; } } + + return min; + } +#else + + /// + /// Returns the minimum value in . + /// + /// The type of elements in . + /// A to determine the minimum value of. + /// The minimum value in . + public static T Min(this ReadOnlySpan source) where T : IComparable + { + T min = source[0]; + + for(int i = 1; i < source.Length; i++) + { + T current = source[i]; + + if(current.CompareTo(min) > 0) + { + min = current; + } + } + + return min; + } + + /// + /// Returns the minimum value in . + /// + /// A to determine the minimum value of. + /// The minimum value in . + public static byte Min(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref byte current = ref MemoryMarshal.GetReference(source); + ref byte secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 minVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref secondToLast)); + + byte result = minVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + byte currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref byte current = ref MemoryMarshal.GetReference(source); + ref byte secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 minVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref secondToLast)); + + byte result = minVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + byte currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref byte current = ref MemoryMarshal.GetReference(source); + ref byte secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 minVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref secondToLast)); + + byte result = minVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + byte currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + byte min = source[0]; + + for(int i = 1; i < source.Length; i++) + { + byte current = source[i]; + + if(current.CompareTo(min) > 0) + { + min = current; + } + } + + return min; + } + + /// + /// Returns the minimum value in . + /// + /// A to determine the minimum value of. + /// The minimum value in . + public static ushort Min(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref ushort current = ref MemoryMarshal.GetReference(source); + ref ushort secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 minVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref secondToLast)); + + ushort result = minVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + ushort currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref ushort current = ref MemoryMarshal.GetReference(source); + ref ushort secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 minVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref secondToLast)); + + ushort result = minVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + ushort currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref ushort current = ref MemoryMarshal.GetReference(source); + ref ushort secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 minVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref secondToLast)); + + ushort result = minVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + ushort currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + ushort min = source[0]; + + for(int i = 1; i < source.Length; i++) + { + ushort current = source[i]; + + if(current.CompareTo(min) > 0) + { + min = current; + } + } + return min; } + /// + /// Returns the minimum value in . + /// + /// A to determine the minimum value of. + /// The minimum value in . + public static uint Min(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref uint current = ref MemoryMarshal.GetReference(source); + ref uint secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 minVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref secondToLast)); + + uint result = minVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + uint currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref uint current = ref MemoryMarshal.GetReference(source); + ref uint secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 minVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref secondToLast)); + + uint result = minVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + uint currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref uint current = ref MemoryMarshal.GetReference(source); + ref uint secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 minVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref secondToLast)); + + uint result = minVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + uint currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + uint min = source[0]; + + for(int i = 1; i < source.Length; i++) + { + uint current = source[i]; + + if(current.CompareTo(min) > 0) + { + min = current; + } + } + + return min; + } + + /// + /// Returns the minimum value in . + /// + /// A to determine the minimum value of. + /// The minimum value in . + public static ulong Min(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref ulong current = ref MemoryMarshal.GetReference(source); + ref ulong secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 minVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref secondToLast)); + + ulong result = minVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + ulong currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref ulong current = ref MemoryMarshal.GetReference(source); + ref ulong secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 minVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref secondToLast)); + + ulong result = minVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + ulong currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + ulong min = source[0]; + + for(int i = 1; i < source.Length; i++) + { + ulong current = source[i]; + + if(current.CompareTo(min) > 0) + { + min = current; + } + } + + return min; + } + + /// + /// Returns the minimum value in . + /// + /// A to determine the minimum value of. + /// The minimum value in . + public static sbyte Min(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref sbyte current = ref MemoryMarshal.GetReference(source); + ref sbyte secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 minVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref secondToLast)); + + sbyte result = minVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + sbyte currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref sbyte current = ref MemoryMarshal.GetReference(source); + ref sbyte secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 minVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref secondToLast)); + + sbyte result = minVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + sbyte currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref sbyte current = ref MemoryMarshal.GetReference(source); + ref sbyte secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 minVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref secondToLast)); + + sbyte result = minVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + sbyte currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + sbyte min = source[0]; + + for(int i = 1; i < source.Length; i++) + { + sbyte current = source[i]; + + if(current.CompareTo(min) > 0) + { + min = current; + } + } + + return min; + } + + /// + /// Returns the minimum value in . + /// + /// A to determine the minimum value of. + /// The minimum value in . + public static short Min(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref short current = ref MemoryMarshal.GetReference(source); + ref short secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 minVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref secondToLast)); + + short result = minVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + short currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref short current = ref MemoryMarshal.GetReference(source); + ref short secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 minVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref secondToLast)); + + short result = minVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + short currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref short current = ref MemoryMarshal.GetReference(source); + ref short secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 minVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref secondToLast)); + + short result = minVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + short currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + short min = source[0]; + + for(int i = 1; i < source.Length; i++) + { + short current = source[i]; + + if(current.CompareTo(min) > 0) + { + min = current; + } + } + + return min; + } + + /// + /// Returns the minimum value in . + /// + /// A to determine the minimum value of. + /// The minimum value in . + public static int Min(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref int current = ref MemoryMarshal.GetReference(source); + ref int secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 minVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref secondToLast)); + + int result = minVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + int currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref int current = ref MemoryMarshal.GetReference(source); + ref int secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 minVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref secondToLast)); + + int result = minVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + int currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref int current = ref MemoryMarshal.GetReference(source); + ref int secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 minVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref secondToLast)); + + int result = minVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + int currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + int min = source[0]; + + for(int i = 1; i < source.Length; i++) + { + int current = source[i]; + + if(current.CompareTo(min) > 0) + { + min = current; + } + } + + return min; + } + + /// + /// Returns the minimum value in . + /// + /// A to determine the minimum value of. + /// The minimum value in . + public static long Min(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref long current = ref MemoryMarshal.GetReference(source); + ref long secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 minVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref secondToLast)); + + long result = minVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + long currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref long current = ref MemoryMarshal.GetReference(source); + ref long secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 minVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref secondToLast)); + + long result = minVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + long currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + long min = source[0]; + + for(int i = 1; i < source.Length; i++) + { + long current = source[i]; + + if(current.CompareTo(min) > 0) + { + min = current; + } + } + + return min; + } + + /// + /// Returns the minimum value in . + /// + /// A to determine the minimum value of. + /// The minimum value in . + public static float Min(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref float current = ref MemoryMarshal.GetReference(source); + ref float secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 minVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref secondToLast)); + + float result = minVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + float currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref float current = ref MemoryMarshal.GetReference(source); + ref float secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 minVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref secondToLast)); + + float result = minVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + float currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector64.IsHardwareAccelerated && Vector64.IsSupported && source.Length > Vector64.Count) + { + ref float current = ref MemoryMarshal.GetReference(source); + ref float secondToLast = ref Unsafe.Add(ref current, source.Length - Vector64.Count); + + Vector64 minVector = Vector64.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector64.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector64.Count); + } + + minVector = Vector64.Min(minVector, Vector64.LoadUnsafe(ref secondToLast)); + + float result = minVector[0]; + + for(int i = 1; i < Vector64.Count; i++) + { + float currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + float min = source[0]; + + for(int i = 1; i < source.Length; i++) + { + float current = source[i]; + + if(current.CompareTo(min) > 0) + { + min = current; + } + } + + return min; + } + + /// + /// Returns the minimum value in . + /// + /// A to determine the minimum value of. + /// The minimum value in . + public static double Min(this ReadOnlySpan source) + { + if(Vector256.IsHardwareAccelerated && Vector256.IsSupported && source.Length > Vector256.Count) + { + ref double current = ref MemoryMarshal.GetReference(source); + ref double secondToLast = ref Unsafe.Add(ref current, source.Length - Vector256.Count); + + Vector256 minVector = Vector256.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector256.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector256.Count); + } + + minVector = Vector256.Min(minVector, Vector256.LoadUnsafe(ref secondToLast)); + + double result = minVector[0]; + + for(int i = 1; i < Vector256.Count; i++) + { + double currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + if(Vector128.IsHardwareAccelerated && Vector128.IsSupported && source.Length > Vector128.Count) + { + ref double current = ref MemoryMarshal.GetReference(source); + ref double secondToLast = ref Unsafe.Add(ref current, source.Length - Vector128.Count); + + Vector128 minVector = Vector128.LoadUnsafe(ref current); + current = ref Unsafe.Add(ref current, Vector128.Count); + + while(Unsafe.IsAddressLessThan(ref current, ref secondToLast)) + { + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref current)); + current = ref Unsafe.Add(ref current, Vector128.Count); + } + + minVector = Vector128.Min(minVector, Vector128.LoadUnsafe(ref secondToLast)); + + double result = minVector[0]; + + for(int i = 1; i < Vector128.Count; i++) + { + double currentResult = minVector[i]; + + if(currentResult.CompareTo(result) < 0) + { + result = currentResult; + } + } + + return result; + } + + double min = source[0]; + + for(int i = 1; i < source.Length; i++) + { + double current = source[i]; + + if(current.CompareTo(min) > 0) + { + min = current; + } + } + + return min; + } +#endif + /// /// Invokes a transform function on each element in and returns the minimum resulting value. /// diff --git a/src/Extensions/ReadOnlySpan/Span/Split.cs b/src/Extensions/ReadOnlySpan/Span/Split.cs index 550804f..5e0d835 100644 --- a/src/Extensions/ReadOnlySpan/Span/Split.cs +++ b/src/Extensions/ReadOnlySpan/Span/Split.cs @@ -1,16 +1,24 @@ using System; using System.Buffers; -using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; #if !NET9_0_OR_GREATER namespace SpanExtensions { + #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member public static partial class MemoryExtensions #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member { + static readonly char[] WhiteSpaceDelimiters = new char[] { ' ', '\t', '\n', '\v', '\f', '\r', '\u0085', '\u00A0', '\u1680', + '\u2000', '\u2001', '\u2002', '\u2003', '\u2004', '\u2005', '\u2006', '\u2007', '\u2008', '\u2009', + '\u200A', '\u2028', '\u2029', '\u202F', '\u205F', '\u3000' }; +#if NET8_0 + static readonly SearchValues WhiteSpaceSearchValues = SearchValues.Create(WhiteSpaceDelimiters); +#endif + /// /// Returns a type that allows for enumeration of each element within a split span /// using the provided separator character. @@ -47,6 +55,25 @@ public static SpanSplitEnumerator Split(this ReadOnlySpan source, ReadO /// Returns a . public static SpanSplitEnumerator SplitAny(this ReadOnlySpan source, ReadOnlySpan separators) where T : IEquatable { + if(separators.Length == 0 && typeof(T) == typeof(char)) + { +#if NET8_0 + return new SpanSplitEnumerator(source, Unsafe.As>(WhiteSpaceSearchValues)); +#elif NET5_0_OR_GREATER + ref char data = ref MemoryMarshal.GetArrayDataReference(WhiteSpaceDelimiters); + ref T convertedData = ref Unsafe.As(ref data); + separators = MemoryMarshal.CreateReadOnlySpan(ref convertedData, WhiteSpaceDelimiters.Length); +#else + unsafe + { + fixed(char* ptr = &WhiteSpaceDelimiters[0]) + { + separators = new ReadOnlySpan(ptr, WhiteSpaceDelimiters.Length); + } + } +#endif + } + return new SpanSplitEnumerator(source, separators, SpanSplitEnumeratorMode.Any); } diff --git a/src/Extensions/Span/Linq/First.cs b/src/Extensions/Span/Linq/First.cs index f9caac5..2bdb1bf 100644 --- a/src/Extensions/Span/Linq/First.cs +++ b/src/Extensions/Span/Linq/First.cs @@ -1,5 +1,4 @@ using System; -using System.Reflection; namespace SpanExtensions { diff --git a/src/Extensions/Span/Span/Split.cs b/src/Extensions/Span/Span/Split.cs index f218aca..e2ced30 100644 --- a/src/Extensions/Span/Span/Split.cs +++ b/src/Extensions/Span/Span/Split.cs @@ -1,7 +1,5 @@ using System; using System.Buffers; -using System.Diagnostics; -using System.Runtime.CompilerServices; #if !NET9_0_OR_GREATER @@ -21,7 +19,7 @@ public static partial class MemoryExtensions /// Returns a . public static SpanSplitEnumerator Split(this Span source, T separator) where T : IEquatable { - return new SpanSplitEnumerator(source, separator); + return Split((ReadOnlySpan)source, separator); } /// @@ -34,7 +32,7 @@ public static SpanSplitEnumerator Split(this Span source, T separator) /// Returns a . public static SpanSplitEnumerator Split(this Span source, ReadOnlySpan separator) where T : IEquatable { - return new SpanSplitEnumerator(source, separator, SpanSplitEnumeratorMode.Sequence); + return Split((ReadOnlySpan)source, separator); } /// @@ -47,7 +45,7 @@ public static SpanSplitEnumerator Split(this Span source, ReadOnlySpan< /// Returns a . public static SpanSplitEnumerator SplitAny(this Span source, ReadOnlySpan separators) where T : IEquatable { - return new SpanSplitEnumerator(source, separators, SpanSplitEnumeratorMode.Any); + return SplitAny((ReadOnlySpan)source, separators); } #if NET8_0 @@ -65,7 +63,7 @@ public static SpanSplitEnumerator SplitAny(this Span source, ReadOnlySp /// public static SpanSplitEnumerator SplitAny(this Span source, SearchValues separators) where T : IEquatable { - return new SpanSplitEnumerator(source, separators); + return SplitAny((ReadOnlySpan)source, separators); } #endif diff --git a/src/README.md b/src/README.md index 09c45ba..6797ee8 100644 --- a/src/README.md +++ b/src/README.md @@ -1,86 +1,17 @@ # SpanExtensions ## About -**`ReadonlySpan`** and **`Span`** are great Types in _C#_, but unfortunately working with them can sometimes be sort of a hassle and some use cases seem straight up impossible, even though they are not. - -**SpanExtensions.Net** aims to help developers use `ReadonlySpan` and `Span` more **productively**, **efficiently** and **safely** and write overall more **performant** Programs. - -Never again switch back to using `string` instead of `ReadonlySpan`, just because the method you seek is not supported. - -**SpanExtensions.Net** provides alternatives for many missing Extension Methods for **`ReadonlySpan`** and **`Span`**, ranging from `string.Split()` over `Enumerable.Skip()` and `Enumerable.Take()` to an improved `ReadOnlySpan.IndexOf()`. - -## Methods -The following **Extension Methods** are contained: - -#### String Methods made available for **`ReadonlySpan`** and **`Span`**: - -- `(ReadOnly-)Span.Split(T delimiter)` -- `(ReadOnly-)Span.Split(T delimiter, int count)` -- `(ReadOnly-)Span.Split(T delimiter, StringSplitOptions options)` -- `(ReadOnly-)Span.Split(T delimiter, StringSplitOptions options, int count)` -- `(ReadOnly-)Span.Split(ReadOnlySpan delimiters)` -- `(ReadOnly-)Span.Split(ReadOnlySpan delimiters, int count)` -- `(ReadOnly-)Span.Split(ReadOnlySpan delimiters, StringSplitOptions options)` -- `(ReadOnly-)Span.Split(ReadOnlySpan delimiters, StringSplitOptions options, int count)` -- `(ReadOnly-)Span.SplitAny(ReadOnlySpan delimiters)` -- `(ReadOnly-)Span.SplitAny(ReadOnlySpan delimiters, int count)` -- `(ReadOnly-)Span.SplitAny(ReadOnlySpan delimiters, StringSplitOptions options)` -- `(ReadOnly-)Span.SplitAny(ReadOnlySpan delimiters, StringSplitOptions options, int count)` -- `(ReadOnly-)Span.Remove(int startIndex)` -- `Span.Replace(T oldT, T newT)` -#### Linq Methods made available for **`ReadonlySpan`** and **`Span`**: +Never again switch back to using `string` instead of `ReadonlySpan`, just because the method you seek is not supported. -- `(ReadOnly-)Span.All(Predicate predicate)` -- `(ReadOnly-)Span.Any(Predicate predicate)` -- `(ReadOnly-)Span.Average()` -- `(ReadOnly-)Span.Sum()` -- `(ReadOnly-)Span.Min()` -- `(ReadOnly-)Span.Max()` -- `(ReadOnly-)Span.Skip(int count)` -- `(ReadOnly-)Span.Take(int count)` -- `(ReadOnly-)Span.SkipLast(int count)` -- `(ReadOnly-)Span.Takelast(int count)` -- `(ReadOnly-)Span.SkipWhile(Predicate condition)` -- `(ReadOnly-)Span.TakeWhile(Predicate condition)` -- `(Readonly-)Span.First()` -- `(Readonly-)Span.First(Predicate predicate)` -- `(Readonly-)Span.FirstOrDefault()` -- `(Readonly-)Span.FirstOrDefault(Predicate predicate)` -- `(Readonly-)Span.FirstOrDefault(T defaultValue)` -- `(Readonly-)Span.FirstOrDefault(Predicate predicate, T defaultValue)` -- `(Readonly-)Span.Last()` -- `(Readonly-)Span.Last(Predicate predicate)` -- `(Readonly-)Span.LastOrDefault()` -- `(Readonly-)Span.LastOrDefault(Predicate predicate)` -- `(Readonly-)Span.LastOrDefault(T defaultValue)` -- `(Readonly-)Span.LastOrDefault(Predicate predicate, T defaultValue)` -- `(Readonly-)Span.Single()` -- `(Readonly-)Span.Single(Predicate predicate)` -- `(Readonly-)Span.SingleOrDefault()` -- `(Readonly-)Span.SingleOrDefault(Predicate predicate)` -- `(Readonly-)Span.SingleOrDefault(T defaultValue)` -- `(Readonly-)Span.SingleOrDefault(Predicate predicate, T defaultValue)` -- `(Readonly-)Span.ElementAt(int index)` -- `(Readonly-)Span.ElementAt(Index index)` -- `(Readonly-)Span.ElementAtOrDefault(int index)` -- `(Readonly-)Span.ElementAtOrDefault(Index index)` -- `(Readonly-)Span.ElementAtOrDefault(int index, T defaultValue)` -- `(Readonly-)Span.ElementAtOrDefault(Index index, T defaultValue)` -- `(Readonly-)Span.Min()` -- `(Readonly-)Span.Min(Func selector)` -- `(Readonly-)Span.MinBy(Func keySelector)` -- `(Readonly-)Span.MinBy(Func keySelector, IComparer comparer)` -- `(Readonly-)Span.Max()` -- `(Readonly-)Span.Max(Func selector)` -- `(Readonly-)Span.MaxBy(Func keySelector)` -- `(Readonly-)Span.MaxBy(Func keySelector, IComparer comparer)` +**SpanExtensions.Net** aims to help developers use `ReadonlySpan` and `Span` more **productively**, **efficiently** and **safely** and write overall more **performant** Programs. ## Contributing -Thank you for your interest in contributing to this project! - You may contribute on [Github](https://github.com/draconware-dev/SpanExtensions.Net). +Thank you for your interest in contributing to this project! - You may contribute on [Github](https://github.com/draconware-dev/SpanExtensions.Net). + ## License -Copyright (c) draconware-dev. All rights reserved. +Copyright (c) draconware-dev. All rights reserved. Licensed under the [MIT](https://github.com/draconware-dev/SpanExtensions.Net/blob/main/LICENSE) license. diff --git a/src/SpanExtensions.csproj b/src/SpanExtensions.csproj index ba1cf8e..c710590 100644 --- a/src/SpanExtensions.csproj +++ b/src/SpanExtensions.csproj @@ -10,22 +10,21 @@ dragon-cs draconware - ReadonlySpan<T> and Span<T> are great Types in C#, but unfortunately working with them can sometimes be sort of a hassle and some use cases seem straight up impossible, even though they are not. - - SpanExtensions.Net aims to help developers use ReadonlySpan<T> and Span<T> more productively, efficiently and safely and write overall more performant Programs. - Never again switch back to using string instead of ReadonlySpan<T>, just because the method you seek is not supported. - + + SpanExtensions.Net aims to help developers use ReadonlySpan<T> and Span<T> more productively, efficiently and safely and write overall more performant Programs. + https://github.com/draconware-dev/SpanExtensions.Net True - Copyright (c) 2024 draconware-dev + Copyright (c) 2025 draconware-dev Span;Performance;Extension;String https://github.com/draconware-dev/SpanExtensions.Net/blob/main/Changelog.md LICENSE - 1.5.1 + 2.0.0 SpanExtensions.Net README.md icon.png + true diff --git a/tests/Performance/PerformanceTests.csproj b/tests/Performance/PerformanceTests.csproj index 9350706..c8f944b 100644 --- a/tests/Performance/PerformanceTests.csproj +++ b/tests/Performance/PerformanceTests.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 enable enable false diff --git a/tests/Performance/Tests/ReadOnlySpan/ReadOnlySpan_Max_Benchmark.cs b/tests/Performance/Tests/ReadOnlySpan/ReadOnlySpan_Max_Benchmark.cs new file mode 100644 index 0000000..996d26b --- /dev/null +++ b/tests/Performance/Tests/ReadOnlySpan/ReadOnlySpan_Max_Benchmark.cs @@ -0,0 +1,58 @@ +using BenchmarkDotNet.Attributes; + +namespace SpanExtensions.Tests.Performance +{ + [MemoryDiagnoser(false)] + public class ReadOnlySpan_Max_Benchmark + { + [Benchmark] + [ArgumentsSource(nameof(GetArgs))] + public int Max(int[] value) + { + return value.AsSpan().Max(); + } + + [Benchmark] + [ArgumentsSource(nameof(GetArgs))] + public int Max_Array(int[] value) + { + return value.Max(); + } + + [Benchmark] + [ArgumentsSource(nameof(GetArgs))] + public int Max_StraightForward(int[] value) + { + ReadOnlySpan source = value; + int max = source[0]; + for(int i = 1; i < source.Length; i++) + { + int current = source[i]; + if(current > max) + { + max = current; + } + } + return max; + } + + public IEnumerable GetArgs() + { + Random random = new Random(3); + + int[] choices = Enumerable.Range(0, 10000).ToArray(); + + int[] data = random.GetItems(choices, 10); + + yield return data; + + data = random.GetItems(choices, 100); + + yield return data; + + data = random.GetItems(choices, 1000); + + yield return data; + } + } +} \ No newline at end of file diff --git a/tests/Performance/Tests/ReadOnlySpan/ReadOnlySpan_Min_Benchmark.cs b/tests/Performance/Tests/ReadOnlySpan/ReadOnlySpan_Min_Benchmark.cs new file mode 100644 index 0000000..41b4be9 --- /dev/null +++ b/tests/Performance/Tests/ReadOnlySpan/ReadOnlySpan_Min_Benchmark.cs @@ -0,0 +1,58 @@ +using BenchmarkDotNet.Attributes; + +namespace SpanExtensions.Tests.Performance +{ + [MemoryDiagnoser(false)] + public class ReadOnlySpan_Min_Benchmark + { + [Benchmark] + [ArgumentsSource(nameof(GetArgs))] + public int Min(int[] value) + { + return value.AsSpan().Min(); + } + + [Benchmark] + [ArgumentsSource(nameof(GetArgs))] + public int Min_Array(int[] value) + { + return value.Min(); + } + + [Benchmark] + [ArgumentsSource(nameof(GetArgs))] + public int Min_StraightForward(int[] value) + { + ReadOnlySpan source = value; + int min = source[0]; + for(int i = 1; i < source.Length; i++) + { + int current = source[i]; + if(current < min) + { + min = current; + } + } + return min; + } + + public IEnumerable GetArgs() + { + Random random = new Random(3); + + int[] choices = Enumerable.Range(0, 10000).ToArray(); + + int[] data = random.GetItems(choices, 10); + + yield return data; + + data = random.GetItems(choices, 100); + + yield return data; + + data = random.GetItems(choices, 1000); + + yield return data; + } + } +} \ No newline at end of file diff --git a/tests/unit-tests/Tests/ReadOnlySpan/Linq/LinqMinMaxTests.cs b/tests/unit-tests/Tests/ReadOnlySpan/Linq/LinqMinMaxTests.cs new file mode 100644 index 0000000..61b7104 --- /dev/null +++ b/tests/unit-tests/Tests/ReadOnlySpan/Linq/LinqMinMaxTests.cs @@ -0,0 +1,73 @@ +namespace SpanExtensions.Tests.UnitTests +{ + public class LinqMinMaxTests + { + [Theory] + [MemberData(nameof(GetMaxData))] + public void Max(int[] source, int max) + { + ReadOnlySpan span = source.AsSpan(); + Assert.Equal(max, span.Max()); + } + + [Theory] + [MemberData(nameof(GetMinData))] + public void Min(int[] source, int min) + { + ReadOnlySpan span = source.AsSpan(); + Assert.Equal(min, span.Min()); + } + + public static TheoryData GetMaxData() + { + var data = new TheoryData(); + + var Samples10 = GetSampleSetInts(10); + var Samples100 = GetSampleSetInts(100); + var Samples1000 = GetSampleSetInts(1000); + + int max10 = Samples10.Max(); + int max100 = Samples100.Max(); + int max1000 = Samples1000.Max(); + + data.Add(Samples10, max10); + data.Add(Samples100, max100); + data.Add(Samples1000, max1000); + + return data; + } + + public static TheoryData GetMinData() + { + var data = new TheoryData(); + + var Samples10 = GetSampleSetInts(10); + var Samples100 = GetSampleSetInts(100); + var Samples1000 = GetSampleSetInts(1000); + + int min10 = Samples10.Min(); + int min100 = Samples100.Min(); + int min1000 = Samples1000.Min(); + + data.Add(Samples10, min10); + data.Add(Samples100, min100); + data.Add(Samples1000, min1000); + + return data; + } + + static int[] GetSampleSetInts(int count) + { + Random random = new Random(count); + + int[] sample = new int[count]; + + for(int i = 0; i < count; i++) + { + sample[i] = random.Next(); + } + + return sample; + } + } +} diff --git a/tests/unit-tests/Tests/ReadOnlySpan/Linq/Sum.cs b/tests/unit-tests/Tests/ReadOnlySpan/Linq/Sum.cs deleted file mode 100644 index 590bb64..0000000 --- a/tests/unit-tests/Tests/ReadOnlySpan/Linq/Sum.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SpanExtensions.Tests.UnitTests -{ - public static partial class ReadOnlySpanLinqTests - { - public sealed class Sum - { - // TODO - } - } -} diff --git a/tests/unit-tests/Tests/ReadOnlySpan/Split/SplitAny/Tests.cs b/tests/unit-tests/Tests/ReadOnlySpan/Split/SplitAny/Tests.cs index 4a14766..6eb2ea1 100644 --- a/tests/unit-tests/Tests/ReadOnlySpan/Split/SplitAny/Tests.cs +++ b/tests/unit-tests/Tests/ReadOnlySpan/Split/SplitAny/Tests.cs @@ -148,6 +148,31 @@ public void UndefinedCountExceedingBehaviourOptionThrowsArgumentException() { Assert.Throws(() => ReadOnlySpanExtensions.SplitAny("aabb".AsSpan(), ['b', 'c'], 1, InvalidCountExceedingBehaviour)); } + + [Fact] + public void EmptyDelimitersUsesWhiteSpaceCharacters() + { + ReadOnlySpan source = "Hello World!\nThis is a test.\rLet's see how it works."; + ReadOnlySpan delimiters = ""; + var expected = new[] + { + "Hello".ToCharArray(), + "World!".ToCharArray(), + "This".ToCharArray(), + "is".ToCharArray(), + "a".ToCharArray(), + "test.".ToCharArray(), + "Let's".ToCharArray(), + "see".ToCharArray(), + "how".ToCharArray(), + "it".ToCharArray(), + "works.".ToCharArray() + }; + + var actual = source.SplitAny(delimiters).ToSystemEnumerable(source); + + Assert.Equal(expected, actual); + } } } } diff --git a/tests/unit-tests/Tests/ReadOnlySpan/Split/SplitAny_StringSplitOptions/Data.cs b/tests/unit-tests/Tests/ReadOnlySpan/Split/SplitAny_StringSplitOptions/Data.cs index 850a38b..56c6533 100644 --- a/tests/unit-tests/Tests/ReadOnlySpan/Split/SplitAny_StringSplitOptions/Data.cs +++ b/tests/unit-tests/Tests/ReadOnlySpan/Split/SplitAny_StringSplitOptions/Data.cs @@ -1,6 +1,4 @@ -using static SpanExtensions.Tests.UnitTests.TestHelper; - -namespace SpanExtensions.Tests.UnitTests +namespace SpanExtensions.Tests.UnitTests { public static partial class ReadOnlySpanSplitTests { diff --git a/tests/unit-tests/ToSystemEnumerableExtensions.cs b/tests/unit-tests/ToSystemEnumerableExtensions.cs index 0297064..296389f 100644 --- a/tests/unit-tests/ToSystemEnumerableExtensions.cs +++ b/tests/unit-tests/ToSystemEnumerableExtensions.cs @@ -211,5 +211,22 @@ public static IEnumerable> ToSystemEnumerable(this SpanSplitSe return list; } + + public static IEnumerable> ToSystemEnumerable(this MemoryExtensions.SpanSplitEnumerator spanEnumerator, ReadOnlySpan span, int maxCount = 100) where T : IEquatable + { + List list = []; + + foreach(Range range in spanEnumerator) + { + list.Add(span[range].ToArray()); + + if(list.Count >= maxCount) + { + throw new IndexOutOfRangeException($"Enumeration exceeded {maxCount}."); + } + } + + return list; + } } }