Files
QWERTYkez.Mensura/QWERTYkez.Mensura/Extensions.cs
2026-06-01 01:31:31 +07:00

3560 lines
140 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Buffers;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace QWERTYkez.Mensura;
public static partial class Extensions
{
internal static double ToDouble(this sbyte number) => Convert.ToDouble(number);
internal static double ToDouble(this short number) => Convert.ToDouble(number);
internal static double ToDouble(this int number) => Convert.ToDouble(number);
internal static double ToDouble(this long number) => Convert.ToDouble(number);
internal static double ToDouble(this byte number) => Convert.ToDouble(number);
internal static double ToDouble(this ushort number) => Convert.ToDouble(number);
internal static double ToDouble(this uint number) => Convert.ToDouble(number);
internal static double ToDouble(this ulong number) => Convert.ToDouble(number);
internal static double ToDouble(this nint number) => Convert.ToDouble(number);
internal static double ToDouble(this nuint number) => Convert.ToDouble(number);
internal static double ToDouble(this float number) => Convert.ToDouble(number);
internal static double ToDouble(this decimal number) => Convert.ToDouble(number);
internal static double ToDouble(this sbyte? number) => number is null ? 0d : Convert.ToDouble(number);
internal static double ToDouble(this short? number) => number is null ? 0d : Convert.ToDouble(number);
internal static double ToDouble(this int? number) => number is null ? 0d : Convert.ToDouble(number);
internal static double ToDouble(this long? number) => number is null ? 0d : Convert.ToDouble(number);
internal static double ToDouble(this byte? number) => number is null ? 0d : Convert.ToDouble(number);
internal static double ToDouble(this ushort? number) => number is null ? 0d : Convert.ToDouble(number);
internal static double ToDouble(this uint? number) => number is null ? 0d : Convert.ToDouble(number);
internal static double ToDouble(this ulong? number) => number is null ? 0d : Convert.ToDouble(number);
internal static double ToDouble(this nint? number) => number is null ? 0d : Convert.ToDouble(number);
internal static double ToDouble(this nuint? number) => number is null ? 0d : Convert.ToDouble(number);
internal static double ToDouble(this float? number) => number is null ? 0d : Convert.ToDouble(number);
internal static double ToDouble(this decimal? number) => number is null ? 0d : Convert.ToDouble(number);
#if NET7_0_OR_GREATER
internal static double ToDouble(this Int128 number) => Convert.ToDouble(number);
internal static double ToDouble(this UInt128 number) => Convert.ToDouble(number);
internal static double ToDouble(this Int128? number) => number is null ? 0d : Convert.ToDouble(number);
internal static double ToDouble(this UInt128? number) => number is null ? 0d : Convert.ToDouble(number);
#endif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe void SetCountUnsafe<T>(this List<T> list, int count)
{
// Берем адрес управляемого объекта List в памяти
// Объект передается по ref-ссылке, преобразуется в указатель
ref var mimic = ref Unsafe.As<List<T>, ListLayoutMimic<T>>(ref list);
// Меняем приватный размер напрямую в памяти объекта!
mimic.Size = count;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static double QuickPow(this double base_Value, int exp)
{
switch (exp)
{
case 0: return 1.0;
case 1: return base_Value;
case 2: return base_Value * base_Value;
case 3: return base_Value * base_Value * base_Value;
case 4: { double x2 = base_Value * base_Value; return x2 * x2; }
case -1: return 1.0 / base_Value;
case -2: return 1.0 / (base_Value * base_Value);
default: return Math.Pow(base_Value, exp);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double ToDouble<T>(this T unit)
where T : struct, IMensuraUnit, IEquatable<T>
=> Unsafe.As<T, double>(ref unit);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double ToDouble<T>(this T? unit)
where T : struct, IMensuraUnit, IEquatable<T>
{
T actual = unit.GetValueOrDefault();
return Unsafe.As<T, double>(ref actual);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T ToUnit<T>(this double val)
where T : struct, IMensuraUnit, IEquatable<T>
=> Unsafe.As<double, T>(ref val);
// ==========================================
// Sum Average Max Min (не nullable)
// ==========================================
public static T Sum<T>(this ReadOnlySpan<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (Unsafe.SizeOf<T>() != sizeof(double))
{
throw new InvalidOperationException($"Тип {typeof(T).Name} имеет неверный размер для SIMD.");
}
if (units.IsEmpty) return default;
ReadOnlySpan<double> values = MemoryMarshal.Cast<T, double>(units);
double sum = 0;
int i = 0;
// 1. ПУТЬ AVX2 (Intel/AMD) — Самый быстрый, обрабатывает по 4 элемента через Unsafe
if (Avx2.IsSupported && values.Length >= 4)
{
Vector256<double> vSum = Vector256<double>.Zero;
ref double start = ref MemoryMarshal.GetReference(values);
for (; i <= values.Length - 4; i += 4)
{
ref double currentRef = ref Unsafe.Add(ref start, i);
var v = Unsafe.As<double, Vector256<double>>(ref currentRef);
vSum = Avx2.Add(vSum, v);
}
sum = vSum.GetElement(0) + vSum.GetElement(1) + vSum.GetElement(2) + vSum.GetElement(3);
}
// 2. ПУТЬ AVX (Старые x64 CPU) — Чуть медленнее AVX2, но тоже по 4 элемента
else if (Avx.IsSupported && values.Length >= 4)
{
Vector256<double> vSum = Vector256<double>.Zero;
ref double start = ref MemoryMarshal.GetReference(values);
for (; i <= values.Length - 4; i += 4)
{
ref double currentRef = ref Unsafe.Add(ref start, i);
var v = Unsafe.As<double, Vector256<double>>(ref currentRef);
vSum = Avx.Add(vSum, v);
}
sum = vSum.GetElement(0) + vSum.GetElement(1) + vSum.GetElement(2) + vSum.GetElement(3);
}
// 3. ПУТЬ VECTOR (ARM64 / Apple Silicon / SSE) — Кроссплатформенный SIMD fallback
else if (Vector.IsHardwareAccelerated && values.Length >= Vector<double>.Count)
{
int vCount = Vector<double>.Count;
Vector<double> vSum = Vector<double>.Zero;
for (; i <= values.Length - vCount; i += vCount)
{
var v = new Vector<double>(values.Slice(i, vCount));
vSum += v;
}
for (int j = 0; j < vCount; j++)
{
sum += vSum[j];
}
}
// Хвост массива (или обычный расчет, если SIMD вообще нет)
for (; i < values.Length; i++)
{
sum += values[i];
}
return Unsafe.As<double, T>(ref sum);
}
public static T Average<T>(this ReadOnlySpan<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
// 0. Защитная проверка для JIT (0 рантайм оверхеда)
if (Unsafe.SizeOf<T>() != sizeof(double))
{
throw new InvalidOperationException($"Тип {typeof(T).Name} имеет неверный размер для SIMD.");
}
// Если коллекция пустая, возвращаем структуру, содержащую double.NaN
if (units.IsEmpty)
{
double nan = double.NaN;
return Unsafe.As<double, T>(ref nan);
}
ReadOnlySpan<double> values = MemoryMarshal.Cast<T, double>(units);
double sum = 0;
int i = 0;
// 1. ПУТЬ AVX2 (Intel/AMD)
if (Avx2.IsSupported && values.Length >= 4)
{
Vector256<double> vSum = Vector256<double>.Zero;
ref double start = ref MemoryMarshal.GetReference(values);
for (; i <= values.Length - 4; i += 4)
{
ref double currentRef = ref Unsafe.Add(ref start, i);
var v = Unsafe.As<double, Vector256<double>>(ref currentRef);
vSum = Avx2.Add(vSum, v);
}
sum = vSum.GetElement(0) + vSum.GetElement(1) + vSum.GetElement(2) + vSum.GetElement(3);
}
// 2. ПУТЬ AVX (Старые x64 CPU)
else if (Avx.IsSupported && values.Length >= 4)
{
Vector256<double> vSum = Vector256<double>.Zero;
ref double start = ref MemoryMarshal.GetReference(values);
for (; i <= values.Length - 4; i += 4)
{
ref double currentRef = ref Unsafe.Add(ref start, i);
var v = Unsafe.As<double, Vector256<double>>(ref currentRef);
vSum = Avx.Add(vSum, v);
}
sum = vSum.GetElement(0) + vSum.GetElement(1) + vSum.GetElement(2) + vSum.GetElement(3);
}
// 3. ПУТЬ VECTOR (ARM64 / SSE)
else if (Vector.IsHardwareAccelerated && values.Length >= Vector<double>.Count)
{
int vCount = Vector<double>.Count;
Vector<double> vSum = Vector<double>.Zero;
for (; i <= values.Length - vCount; i += vCount)
{
var v = new Vector<double>(values.Slice(i, vCount));
vSum += v;
}
for (int j = 0; j < vCount; j++)
{
sum += vSum[j];
}
}
// 4. Довычисление хвоста
for (; i < values.Length; i++)
{
sum += values[i];
}
// Находим среднее значение
double average = sum / values.Length;
// Упаковываем double обратно в структуру T (zero-cost)
return Unsafe.As<double, T>(ref average);
}
public static T Max<T>(this ReadOnlySpan<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (Unsafe.SizeOf<T>() != sizeof(double))
{
throw new InvalidOperationException($"Тип {typeof(T).Name} имеет неверный размер для SIMD.");
}
if (units.IsEmpty)
{
double minVal = double.MinValue;
return Unsafe.As<double, T>(ref minVal);
}
ReadOnlySpan<double> values = MemoryMarshal.Cast<T, double>(units);
double max = double.MinValue;
int i = 0;
if (Avx2.IsSupported && values.Length >= 4)
{
Vector256<double> vMax = Vector256.Create(double.MinValue);
ref double start = ref MemoryMarshal.GetReference(values);
for (; i <= values.Length - 4; i += 4)
{
ref double currentRef = ref Unsafe.Add(ref start, i);
var v = Unsafe.As<double, Vector256<double>>(ref currentRef);
vMax = Avx2.Max(vMax, v);
}
max = Math.Max(Math.Max(vMax.GetElement(0), vMax.GetElement(1)), Math.Max(vMax.GetElement(2), vMax.GetElement(3)));
}
else if (Avx.IsSupported && values.Length >= 4)
{
Vector256<double> vMax = Vector256.Create(double.MinValue);
ref double start = ref MemoryMarshal.GetReference(values);
for (; i <= values.Length - 4; i += 4)
{
ref double currentRef = ref Unsafe.Add(ref start, i);
var v = Unsafe.As<double, Vector256<double>>(ref currentRef);
vMax = Avx.Max(vMax, v);
}
max = Math.Max(Math.Max(vMax.GetElement(0), vMax.GetElement(1)), Math.Max(vMax.GetElement(2), vMax.GetElement(3)));
}
else if (Vector.IsHardwareAccelerated && values.Length >= Vector<double>.Count)
{
int vCount = Vector<double>.Count;
var vMax = new Vector<double>(double.MinValue);
for (; i <= values.Length - vCount; i += vCount)
{
var v = new Vector<double>(values.Slice(i, vCount));
vMax = Vector.Max(vMax, v);
}
for (int j = 0; j < vCount; j++)
{
if (vMax[j] > max) max = vMax[j];
}
}
// Довычисление хвоста
for (; i < values.Length; i++)
{
if (values[i] > max) max = values[i];
}
return Unsafe.As<double, T>(ref max);
}
public static T Min<T>(this ReadOnlySpan<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (Unsafe.SizeOf<T>() != sizeof(double))
{
throw new InvalidOperationException($"Тип {typeof(T).Name} имеет неверный размер для SIMD.");
}
if (units.IsEmpty)
{
double maxVal = double.MaxValue;
return Unsafe.As<double, T>(ref maxVal);
}
ReadOnlySpan<double> values = MemoryMarshal.Cast<T, double>(units);
double min = double.MaxValue;
int i = 0;
if (Avx2.IsSupported && values.Length >= 4)
{
Vector256<double> vMin = Vector256.Create(double.MaxValue);
ref double start = ref MemoryMarshal.GetReference(values);
for (; i <= values.Length - 4; i += 4)
{
ref double currentRef = ref Unsafe.Add(ref start, i);
var v = Unsafe.As<double, Vector256<double>>(ref currentRef);
vMin = Avx2.Min(vMin, v);
}
min = Math.Min(Math.Min(vMin.GetElement(0), vMin.GetElement(1)), Math.Min(vMin.GetElement(2), vMin.GetElement(3)));
}
else if (Avx.IsSupported && values.Length >= 4)
{
Vector256<double> vMin = Vector256.Create(double.MaxValue);
ref double start = ref MemoryMarshal.GetReference(values);
for (; i <= values.Length - 4; i += 4)
{
ref double currentRef = ref Unsafe.Add(ref start, i);
var v = Unsafe.As<double, Vector256<double>>(ref currentRef);
vMin = Avx.Min(vMin, v);
}
min = Math.Min(Math.Min(vMin.GetElement(0), vMin.GetElement(1)), Math.Min(vMin.GetElement(2), vMin.GetElement(3)));
}
else if (Vector.IsHardwareAccelerated && values.Length >= Vector<double>.Count)
{
int vCount = Vector<double>.Count;
var vMin = new Vector<double>(double.MaxValue);
for (; i <= values.Length - vCount; i += vCount)
{
var v = new Vector<double>(values.Slice(i, vCount));
vMin = Vector.Min(vMin, v);
}
for (int j = 0; j < vCount; j++)
{
if (vMin[j] < min) min = vMin[j];
}
}
// Довычисление хвоста
for (; i < values.Length; i++)
{
if (values[i] < min) min = values[i];
}
return Unsafe.As<double, T>(ref min);
}
public static T Sum<T>(this List<T> list)
where T : struct, IMensuraUnit, IEquatable<T>
{
return CollectionsMarshal.AsSpan(list).Sum();
}
public static T Average<T>(this List<T> list)
where T : struct, IMensuraUnit, IEquatable<T>
{
return CollectionsMarshal.AsSpan(list).Average();
}
public static T Max<T>(this List<T> list)
where T : struct, IMensuraUnit, IEquatable<T>
{
return CollectionsMarshal.AsSpan(list).Max();
}
public static T Min<T>(this List<T> list)
where T : struct, IMensuraUnit, IEquatable<T>
{
return CollectionsMarshal.AsSpan(list).Min();
}
public static T Sum<T>(this ICollection<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return default;
if (collection is T[] array) return array.Sum();
if (collection is List<T> list) return list.Sum();
T[] sharedArray = ArrayPool<T>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0); // Встроенный CopyTo работает быстрее, чем ручной foreach
return new ReadOnlySpan<T>(sharedArray, 0, collection.Count).Sum();
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static T Average<T>(this ICollection<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.NaN.ToUnit<T>();
if (collection is T[] array) return array.Average();
if (collection is List<T> list) return list.Average();
T[] sharedArray = ArrayPool<T>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T>(sharedArray, 0, collection.Count).Average();
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static T Max<T>(this ICollection<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.MinValue.ToUnit<T>();
if (collection is T[] array) return array.Max();
if (collection is List<T> list) return list.Max();
T[] sharedArray = ArrayPool<T>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T>(sharedArray, 0, collection.Count).Max();
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static T Min<T>(this ICollection<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.MaxValue.ToUnit<T>();
if (collection is T[] array) return array.Min();
if (collection is List<T> list) return list.Min();
T[] sharedArray = ArrayPool<T>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T>(sharedArray, 0, collection.Count).Min();
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static T Sum<T>(this IReadOnlyCollection<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return default;
if (collection is T[] array) return array.Sum();
if (collection is List<T> list) return list.Sum();
T[] sharedArray = ArrayPool<T>.Shared.Rent(collection.Count);
int index = 0;
try
{
foreach (var item in collection) sharedArray[index++] = item;
return new ReadOnlySpan<T>(sharedArray, 0, collection.Count).Sum();
}
finally { ArrayPool<T>.Shared.Return(sharedArray); }
}
public static T Average<T>(this IReadOnlyCollection<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.NaN.ToUnit<T>();
if (collection is T[] array) return array.Average();
if (collection is List<T> list) return list.Average();
T[] sharedArray = ArrayPool<T>.Shared.Rent(collection.Count);
int index = 0;
try
{
foreach (var item in collection) sharedArray[index++] = item;
return new ReadOnlySpan<T>(sharedArray, 0, collection.Count).Average();
}
finally { ArrayPool<T>.Shared.Return(sharedArray); }
}
public static T Max<T>(this IReadOnlyCollection<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.MinValue.ToUnit<T>();
if (collection is T[] array) return array.Max();
if (collection is List<T> list) return list.Max();
T[] sharedArray = ArrayPool<T>.Shared.Rent(collection.Count);
int index = 0;
try
{
foreach (var item in collection) sharedArray[index++] = item;
return new ReadOnlySpan<T>(sharedArray, 0, collection.Count).Max();
}
finally { ArrayPool<T>.Shared.Return(sharedArray); }
}
public static T Min<T>(this IReadOnlyCollection<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.MaxValue.ToUnit<T>();
if (collection is T[] array) return array.Min();
if (collection is List<T> list) return list.Min();
T[] sharedArray = ArrayPool<T>.Shared.Rent(collection.Count);
int index = 0;
try
{
foreach (var item in collection) sharedArray[index++] = item;
return new ReadOnlySpan<T>(sharedArray, 0, collection.Count).Min();
}
finally { ArrayPool<T>.Shared.Return(sharedArray); }
}
public static T Sum<T>(this IEnumerable<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null) return default;
// Быстрый SIMD-путь для готовых коллекций (0 аллокаций)
if (collection is T[] array) return array.Sum();
if (collection is List<T> list) return list.Sum();
if (collection is ICollection<T> col) return col.Sum();
if (collection is IReadOnlyCollection<T> roc) return roc.Sum();
// Медленный путь для yield return: считаем на лету без буферов
double sum = 0;
bool hasElements = false;
foreach (var item in collection)
{
sum += item.ToDouble();
hasElements = true;
}
return hasElements ? sum.ToUnit<T>() : default;
}
public static T Average<T>(this IEnumerable<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null) return double.NaN.ToUnit<T>();
if (collection is T[] array) return array.Average();
if (collection is List<T> list) return list.Average();
if (collection is ICollection<T> col) return col.Average();
if (collection is IReadOnlyCollection<T> roc) return roc.Average();
double sum = 0;
long count = 0;
foreach (var item in collection)
{
sum += item.ToDouble();
count++;
}
if (count == 0) return double.NaN.ToUnit<T>();
double avg = sum / count;
return avg.ToUnit<T>();
}
public static T Max<T>(this IEnumerable<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null) return double.MinValue.ToUnit<T>();
if (collection is T[] array) return array.Max();
if (collection is List<T> list) return list.Max();
if (collection is ICollection<T> col) return col.Max();
if (collection is IReadOnlyCollection<T> roc) return roc.Max();
double max = double.MinValue;
bool hasElements = false;
foreach (var item in collection)
{
double val = item.ToDouble();
if (val > max) max = val;
hasElements = true;
}
return hasElements ? max.ToUnit<T>() : double.MinValue.ToUnit<T>();
}
public static T Min<T>(this IEnumerable<T> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null) return double.MaxValue.ToUnit<T>();
if (collection is T[] array) return array.Min();
if (collection is List<T> list) return list.Min();
if (collection is ICollection<T> col) return col.Min();
if (collection is IReadOnlyCollection<T> roc) return roc.Min();
double min = double.MaxValue;
bool hasElements = false;
foreach (var item in collection)
{
double val = item.ToDouble();
if (val < min) min = val;
hasElements = true;
}
return hasElements ? min.ToUnit<T>() : double.MaxValue.ToUnit<T>();
}
// ==========================================
// Sum Average Max Min (nullable)
// ==========================================
public static T Sum<T>(this ReadOnlySpan<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.IsEmpty) return default;
// Берем массив из пула строго под размер исходного Span
double[] sharedArray = ArrayPool<double>.Shared.Rent(units.Length);
int validCount = 0;
try
{
for (int i = 0; i < units.Length; i++)
{
T? unit = units[i];
if (unit.HasValue)
{
sharedArray[validCount++] = unit.Value.ToDouble();
}
}
if (validCount == 0) return default;
var validValues = new ReadOnlySpan<double>(sharedArray, 0, validCount);
ReadOnlySpan<T> validStructs = MemoryMarshal.Cast<double, T>(validValues);
return validStructs.Sum(); // Наш SIMD метод
}
finally
{
ArrayPool<double>.Shared.Return(sharedArray);
}
}
public static T Average<T>(this ReadOnlySpan<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.IsEmpty)
{
double nan = double.NaN;
return Unsafe.As<double, T>(ref nan);
}
double[] sharedArray = ArrayPool<double>.Shared.Rent(units.Length);
int validCount = 0;
try
{
for (int i = 0; i < units.Length; i++)
{
T? unit = units[i];
if (unit.HasValue)
{
sharedArray[validCount++] = unit.Value.ToDouble();
}
}
if (validCount == 0)
{
double nan = double.NaN;
return Unsafe.As<double, T>(ref nan);
}
var validValues = new ReadOnlySpan<double>(sharedArray, 0, validCount);
ReadOnlySpan<T> validStructs = MemoryMarshal.Cast<double, T>(validValues);
return validStructs.Average(); // Наш SIMD метод
}
finally
{
ArrayPool<double>.Shared.Return(sharedArray);
}
}
public static T Max<T>(this ReadOnlySpan<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.IsEmpty)
{
double minVal = double.MinValue;
return Unsafe.As<double, T>(ref minVal);
}
double[] sharedArray = ArrayPool<double>.Shared.Rent(units.Length);
int validCount = 0;
try
{
for (int i = 0; i < units.Length; i++)
{
T? unit = units[i];
if (unit.HasValue)
{
sharedArray[validCount++] = unit.Value.ToDouble();
}
}
if (validCount == 0)
{
double minVal = double.MinValue;
return Unsafe.As<double, T>(ref minVal);
}
var validValues = new ReadOnlySpan<double>(sharedArray, 0, validCount);
ReadOnlySpan<T> validStructs = MemoryMarshal.Cast<double, T>(validValues);
return validStructs.Max(); // Наш SIMD метод
}
finally
{
ArrayPool<double>.Shared.Return(sharedArray);
}
}
public static T Min<T>(this ReadOnlySpan<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.IsEmpty)
{
double maxVal = double.MaxValue;
return Unsafe.As<double, T>(ref maxVal);
}
double[] sharedArray = ArrayPool<double>.Shared.Rent(units.Length);
int validCount = 0;
try
{
for (int i = 0; i < units.Length; i++)
{
T? unit = units[i];
if (unit.HasValue)
{
sharedArray[validCount++] = unit.Value.ToDouble();
}
}
if (validCount == 0)
{
double maxVal = double.MaxValue;
return Unsafe.As<double, T>(ref maxVal);
}
var validValues = new ReadOnlySpan<double>(sharedArray, 0, validCount);
ReadOnlySpan<T> validStructs = MemoryMarshal.Cast<double, T>(validValues);
return validStructs.Min(); // Наш SIMD метод
}
finally
{
ArrayPool<double>.Shared.Return(sharedArray);
}
}
public static T Sum<T>(this List<T?> list)
where T : struct, IMensuraUnit, IEquatable<T>
{
return CollectionsMarshal.AsSpan(list).Sum();
}
public static T Average<T>(this List<T?> list)
where T : struct, IMensuraUnit, IEquatable<T>
{
return CollectionsMarshal.AsSpan(list).Average();
}
public static T Max<T>(this List<T?> list)
where T : struct, IMensuraUnit, IEquatable<T>
{
return CollectionsMarshal.AsSpan(list).Max();
}
public static T Min<T>(this List<T?> list)
where T : struct, IMensuraUnit, IEquatable<T>
{
return CollectionsMarshal.AsSpan(list).Min();
}
public static T Sum<T>(this ICollection<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return default;
if (collection is T?[] array) return array.Sum();
if (collection is List<T?> list) return list.Sum();
T?[] sharedArray = ArrayPool<T?>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T?>(sharedArray, 0, collection.Count).Sum();
}
finally
{
ArrayPool<T?>.Shared.Return(sharedArray);
}
}
public static T Average<T>(this ICollection<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.NaN.ToUnit<T>();
if (collection is T?[] array) return array.Average();
if (collection is List<T?> list) return list.Average();
T?[] sharedArray = ArrayPool<T?>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T?>(sharedArray, 0, collection.Count).Average();
}
finally
{
ArrayPool<T?>.Shared.Return(sharedArray);
}
}
public static T Max<T>(this ICollection<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.MinValue.ToUnit<T>();
if (collection is T?[] array) return array.Max();
if (collection is List<T?> list) return list.Max();
T?[] sharedArray = ArrayPool<T?>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T?>(sharedArray, 0, collection.Count).Max();
}
finally
{
ArrayPool<T?>.Shared.Return(sharedArray);
}
}
public static T Min<T>(this ICollection<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.MaxValue.ToUnit<T>();
if (collection is T?[] array) return array.Min();
if (collection is List<T?> list) return list.Min();
T?[] sharedArray = ArrayPool<T?>.Shared.Rent(collection.Count);
try
{
collection.CopyTo(sharedArray, 0);
return new ReadOnlySpan<T?>(sharedArray, 0, collection.Count).Min();
}
finally
{
ArrayPool<T?>.Shared.Return(sharedArray);
}
}
public static T Sum<T>(this IReadOnlyCollection<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return default;
if (collection is T?[] array) return array.Sum();
if (collection is List<T?> list) return list.Sum();
T?[] sharedArray = ArrayPool<T?>.Shared.Rent(collection.Count);
int index = 0;
try
{
foreach (var item in collection) sharedArray[index++] = item;
return new ReadOnlySpan<T?>(sharedArray, 0, collection.Count).Sum();
}
finally { ArrayPool<T?>.Shared.Return(sharedArray); }
}
public static T Average<T>(this IReadOnlyCollection<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.NaN.ToUnit<T>();
if (collection is T?[] array) return array.Average();
if (collection is List<T?> list) return list.Sum(); // Подхватит Average() из-за правильной перегрузки
T?[] sharedArray = ArrayPool<T?>.Shared.Rent(collection.Count);
int index = 0;
try
{
foreach (var item in collection) sharedArray[index++] = item;
return new ReadOnlySpan<T?>(sharedArray, 0, collection.Count).Average();
}
finally { ArrayPool<T?>.Shared.Return(sharedArray); }
}
public static T Max<T>(this IReadOnlyCollection<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.MinValue.ToUnit<T>();
if (collection is T?[] array) return array.Max();
if (collection is List<T?> list) return list.Max();
T?[] sharedArray = ArrayPool<T?>.Shared.Rent(collection.Count);
int index = 0;
try
{
foreach (var item in collection) sharedArray[index++] = item;
return new ReadOnlySpan<T?>(sharedArray, 0, collection.Count).Max();
}
finally { ArrayPool<T?>.Shared.Return(sharedArray); }
}
public static T Min<T>(this IReadOnlyCollection<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null || collection.Count == 0) return double.MaxValue.ToUnit<T>();
if (collection is T?[] array) return array.Min();
if (collection is List<T?> list) return list.Min();
T?[] sharedArray = ArrayPool<T?>.Shared.Rent(collection.Count);
int index = 0;
try
{
foreach (var item in collection) sharedArray[index++] = item;
return new ReadOnlySpan<T?>(sharedArray, 0, collection.Count).Min();
}
finally { ArrayPool<T?>.Shared.Return(sharedArray); }
}
public static T Sum<T>(this IEnumerable<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null) return default;
// Быстрый SIMD-путь для готовых коллекций за 0 аллокаций
if (collection is T?[] array) return array.Sum();
if (collection is List<T?> list) return list.Sum();
if (collection is ICollection<T?> col) return col.Sum();
if (collection is IReadOnlyCollection<T?> roc) return roc.Sum();
// Медленный путь для ленивого yield return: вычисляем на лету
double sum = 0;
bool hasElements = false;
foreach (T? item in collection)
{
if (item.HasValue)
{
sum += item.Value.ToDouble();
hasElements = true;
}
}
return hasElements ? sum.ToUnit<T>() : default;
}
public static T Average<T>(this IEnumerable<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null) return double.NaN.ToUnit<T>();
if (collection is T?[] array) return array.Average();
if (collection is List<T?> list) return list.Average();
if (collection is ICollection<T?> col) return col.Average();
if (collection is IReadOnlyCollection<T?> roc) return roc.Average();
double sum = 0;
long count = 0;
foreach (T? item in collection)
{
if (item.HasValue)
{
sum += item.Value.ToDouble();
count++;
}
}
if (count == 0) return double.NaN.ToUnit<T>();
double avg = sum / count;
return avg.ToUnit<T>();
}
public static T Max<T>(this IEnumerable<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null) return double.MinValue.ToUnit<T>();
if (collection is T?[] array) return array.Max();
if (collection is List<T?> list) return list.Max();
if (collection is ICollection<T?> col) return col.Max();
if (collection is IReadOnlyCollection<T?> roc) return roc.Max();
double max = double.MinValue;
bool hasElements = false;
foreach (T? item in collection)
{
if (item.HasValue)
{
double val = item.Value.ToDouble();
if (val > max) max = val;
hasElements = true;
}
}
return hasElements ? max.ToUnit<T>() : double.MinValue.ToUnit<T>();
}
public static T Min<T>(this IEnumerable<T?> collection)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (collection == null) return double.MaxValue.ToUnit<T>();
if (collection is T?[] array) return array.Min();
if (collection is List<T?> list) return list.Min();
if (collection is ICollection<T?> col) return col.Min();
if (collection is IReadOnlyCollection<T?> roc) return roc.Min();
double min = double.MaxValue;
bool hasElements = false;
foreach (T? item in collection)
{
if (item.HasValue)
{
double val = item.Value.ToDouble();
if (val < min) min = val;
hasElements = true;
}
}
return hasElements ? min.ToUnit<T>() : double.MaxValue.ToUnit<T>();
}
// ==========================================
// ==========================================
// CORE
// ==========================================
internal static void MultiplyCore<T>(ReadOnlySpan<T> source, double multiplicator, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (source.IsEmpty) return;
int len = source.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(source);
Span<double> dstDouble = MemoryMarshal.Cast<T, double>(destination);
var vectorizedMultiplicator = new Vector<double>(multiplicator);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int simdEnd = len & ~(vectorSize - 1);
for (; i < simdEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
var vector = Unsafe.As<double, Vector<double>>(ref currentSrc);
var multiplied = vector * vectorizedMultiplicator;
Unsafe.As<double, Vector<double>>(ref currentDst) = multiplied;
}
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) * multiplicator;
}
}
internal static void DivideCore<T>(ReadOnlySpan<T> dividends, double divisor, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (dividends.IsEmpty) return;
int len = dividends.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(dividends);
Span<double> dstDouble = MemoryMarshal.Cast<T, double>(destination);
// Вместо деления в цикле, умножаем на обратное число (invDivisor)
double invDivisor = 1.0 / divisor;
var vectorizedInvDivisor = new Vector<double>(invDivisor);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int simdEnd = len & ~(vectorSize - 1);
for (; i < simdEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
// Загрузка, быстрое умножение вместо деления, и сохранение
var vector = Unsafe.As<double, Vector<double>>(ref currentSrc);
var multiplied = vector * vectorizedInvDivisor;
Unsafe.As<double, Vector<double>>(ref currentDst) = multiplied;
}
// Хвост
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) * invDivisor;
}
}
internal static void DivideCore<T>(double dividend, ReadOnlySpan<T> divisors, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (divisors.IsEmpty) return;
int len = divisors.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(divisors);
Span<double> dstDouble = MemoryMarshal.Cast<T, double>(destination);
var vectorizedDividend = new Vector<double>(dividend);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int simdEnd = len & ~(vectorSize - 1);
for (; i < simdEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
var vector = Unsafe.As<double, Vector<double>>(ref currentSrc);
// Векторное деление: константа делится на покомпонентные элементы массива
var divided = Vector.Divide(vectorizedDividend, vector);
Unsafe.As<double, Vector<double>>(ref currentDst) = divided;
}
// Хвост
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = dividend / Unsafe.Add(ref srcRef, i);
}
}
internal static void PlusCore<T>(ReadOnlySpan<T> summands, double summand, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (summands.IsEmpty) return;
int len = summands.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(summands);
Span<double> dstDouble = MemoryMarshal.Cast<T, double>(destination);
var vectorizedSummand = new Vector<double>(summand);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int simdEnd = len & ~(vectorSize - 1);
for (; i < simdEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
var vector = Unsafe.As<double, Vector<double>>(ref currentSrc);
var added = vector + vectorizedSummand;
Unsafe.As<double, Vector<double>>(ref currentDst) = added;
}
// Хвост
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) + summand;
}
}
internal static void MinusCore<T>(ReadOnlySpan<T> minuends, double subtrahend, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (minuends.IsEmpty) return;
int len = minuends.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(minuends);
Span<double> dstDouble = MemoryMarshal.Cast<T, double>(destination);
var vectorizedSubtrahend = new Vector<double>(subtrahend);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int simdEnd = len & ~(vectorSize - 1);
for (; i < simdEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
var vector = Unsafe.As<double, Vector<double>>(ref currentSrc);
var subtracted = vector - vectorizedSubtrahend;
Unsafe.As<double, Vector<double>>(ref currentDst) = subtracted;
}
// Хвост
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) - subtrahend;
}
}
internal static void MinusCore<T>(double minuend, ReadOnlySpan<T> subtrahends, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (subtrahends.IsEmpty) return;
int len = subtrahends.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(subtrahends);
Span<double> dstDouble = MemoryMarshal.Cast<T, double>(destination);
var vectorizedMinuend = new Vector<double>(minuend);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
int simdEnd = len & ~(vectorSize - 1);
for (; i < simdEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
var vector = Unsafe.As<double, Vector<double>>(ref currentSrc);
// Векторное вычитание: константа уменьшается на покомпонентные элементы массива
var subtracted = vectorizedMinuend - vector;
Unsafe.As<double, Vector<double>>(ref currentDst) = subtracted;
}
// Хвост
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = minuend - Unsafe.Add(ref srcRef, i);
}
}
internal static void PowCore<T>(ReadOnlySpan<T> source, int power, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (source.IsEmpty) return;
int len = source.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(source);
Span<double> dstDouble = MemoryMarshal.Cast<T, double>(destination);
int vectorSize = Vector<double>.Count;
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
// Быстрая обработка граничных случаев для исключения лишних циклов
if (power == 0)
{
// Любое число в степени 0 равно 1
dstDouble.Fill(1.0);
return;
}
if (power == 1)
{
// В степени 1 — это просто копирование памяти
srcDouble.CopyTo(dstDouble);
return;
}
int simdEnd = len & ~(vectorSize - 1);
for (; i < simdEnd; i += vectorSize)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
// Загружаем вектор за 1 такт процессора
var @base = Unsafe.As<double, Vector<double>>(ref currentSrc);
// Векторный аналог быстрого (бинарного) возведения в степень
var result = Vector<double>.One;
var currentPower = Math.Abs(power);
while (currentPower > 0)
{
if ((currentPower & 1) == 1)
{
result *= @base;
}
@base *= @base;
currentPower >>= 1;
}
// Если степень отрицательная, делим единицу на полученный результат
if (power < 0)
{
result = Vector.Divide(Vector<double>.One, result);
}
// Сохраняем итоговый вектор
Unsafe.As<double, Vector<double>>(ref currentDst) = result;
}
// Хвост массива (или обычный расчет, если SIMD не поддерживается)
var absPower = Math.Abs(power);
for (; i < len; i++)
{
double @base = Unsafe.Add(ref srcRef, i);
double result = 1.0;
int currentPower = absPower;
while (currentPower > 0)
{
if ((currentPower & 1) == 1) result *= @base;
@base *= @base;
currentPower >>= 1;
}
Unsafe.Add(ref dstRef, i) = power < 0 ? 1.0 / result : result;
}
}
internal static void PowCore<T>(ReadOnlySpan<T> source, double power, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (source.IsEmpty) return;
int len = source.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(source);
Span<double> dstDouble = MemoryMarshal.Cast<T, double>(destination);
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
// 1. Оптимизация тривиальных случаев
if (power == 1.0)
{
srcDouble.CopyTo(dstDouble);
return;
}
if (power == 0.0)
{
dstDouble.Fill(1.0);
return;
}
// 2. Оптимизация для квадратного корня (степень 0.5) через аппаратный AVX
if (power == 0.5)
{
if (Avx.IsSupported && len >= 4)
{
int simdEnd = len & ~3;
for (; i < simdEnd; i += 4)
{
// Совместимый с .NET 6 способ чтения вектора из ref double
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
var v = Unsafe.As<double, Vector256<double>>(ref currentSrc);
// Аппаратный корень через инструкцию vsqrtpd
var sqrtV = Avx.Sqrt(v);
// Совместимый с .NET 6 способ записи вектора в ref double
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
Unsafe.As<double, Vector256<double>>(ref currentDst) = sqrtV;
}
}
// Хвост для корня
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Math.Sqrt(Unsafe.Add(ref srcRef, i));
}
return;
}
// 3. ОБЩИЙ СЛУЧАЙ ДЛЯ СЛОЖНЫХ СТЕПЕНЕЙ
// Из-за ограничений .NET 6 здесь мы разворачиваем цикл вручную на 4 элемента,
// но делаем это через ref-ссылки без использования fixed-указателей
if (len >= 4)
{
int simdEnd = len & ~3;
for (; i < simdEnd; i += 4)
{
ref double sRef = ref Unsafe.Add(ref srcRef, i);
ref double dRef = ref Unsafe.Add(ref dstRef, i);
// JIT попытается это автовекторизовать, но даже без нее
// этот код работает быстрее fixed за счет отсутствия пиннинга памяти GC
Unsafe.Add(ref dRef, 0) = Math.Pow(Unsafe.Add(ref sRef, 0), power);
Unsafe.Add(ref dRef, 1) = Math.Pow(Unsafe.Add(ref sRef, 1), power);
Unsafe.Add(ref dRef, 2) = Math.Pow(Unsafe.Add(ref sRef, 2), power);
Unsafe.Add(ref dRef, 3) = Math.Pow(Unsafe.Add(ref sRef, 3), power);
}
}
// Хвост массива
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Math.Pow(Unsafe.Add(ref srcRef, i), power);
}
}
internal static void SqrtCore<T>(ReadOnlySpan<T> source, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (source.IsEmpty) return;
int len = source.Length;
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(source);
Span<double> dstDouble = MemoryMarshal.Cast<T, double>(destination);
int i = 0;
ref double srcRef = ref MemoryMarshal.GetReference(srcDouble);
ref double dstRef = ref MemoryMarshal.GetReference(dstDouble);
// 1. ПУТЬ AVX (x64 Процессоры Intel/AMD) — обрабатываем по 4 элемента double
if (Avx.IsSupported && len >= 4)
{
int simdEnd = len & ~3; // Вычисляем границу по маске (быстрее, чем len - len % 4)
for (; i < simdEnd; i += 4)
{
ref double currentSrc = ref Unsafe.Add(ref srcRef, i);
ref double currentDst = ref Unsafe.Add(ref dstRef, i);
// Загружаем 4 элемента double в AVX регистр за 1 такт
var v = Unsafe.As<double, Vector256<double>>(ref currentSrc);
// Аппаратный корень на уровне CPU
var sqrtV = Avx.Sqrt(v);
// Выгружаем результат обратно в память назначения за 1 такт
Unsafe.As<double, Vector256<double>>(ref currentDst) = sqrtV;
}
}
// 2. ПУТЬ VECTOR (ARM64 / Apple Silicon / Старые CPU без AVX)
else if (Vector.IsHardwareAccelerated && len >= Vector<double>.Count)
{
int vCount = Vector<double>.Count;
int simdEnd = len & ~(vCount - 1);
for (; i < simdEnd; i += vCount)
{
// Используем Span для кроссплатформенного создания вектора
var v = new Vector<double>(srcDouble.Slice(i, vCount));
// Кроссплатформенный аппаратный корень (на ARM превратится в NEON инструкцию)
var sqrtV = Vector.SquareRoot(v);
// Копируем напрямую в целевой Span
sqrtV.CopyTo(dstDouble.Slice(i, vCount));
}
}
// 3. Хвост массива (или обычный расчет, если SIMD на процессоре недоступен)
for (; i < len; i++)
{
Unsafe.Add(ref dstRef, i) = Math.Sqrt(Unsafe.Add(ref srcRef, i));
}
}
// ==========================================
// === MULTIPLY ===
// ==========================================
public static T[] Multiply<T>(this T[] units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new T[len];
MultiplyCore(units, multiplicator, result);
return result;
}
public static T?[] Multiply<T>(this T?[] units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
// Выделяем чистую память (0 аллокаций логики, только сам массив)
var result = new T?[len];
for (int i = 0; i < len; i++)
{
// Читаем из исходного по значению (бесплатно для 8 байт)
T? item = units[i];
if (item.HasValue)
{
// Пишем напрямую в результат по ref-ссылке (всего 1 запись в память!)
ref var dst = ref result[i];
dst = (item.Value.ToDouble() * multiplicator).ToUnit<T>();
}
}
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T[] Multiply<T>(this double multiplicator, T[] units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T?[] Multiply<T>(this double multiplicator, T?[] units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator);
// === ReadOnlySpan ===
public static void Multiply<T>(this ReadOnlySpan<T> units, double multiplicator, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.Length > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
Extensions.MultiplyCore(units, multiplicator, destination);
}
public static void Multiply<T>(this ReadOnlySpan<T?> units, double multiplicator, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.IsEmpty) return;
int len = units.Length;
// Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов
ref var srcRef = ref MemoryMarshal.GetReference(units);
ref var dstRef = ref MemoryMarshal.GetReference(destination);
int i = 0;
int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4)
// 1. ОСНОВНОЙ ЦИКЛ: Обрабатываем конвейером по 4 элемента за итерацию
for (; i < unrollEnd; i += 4)
{
T? u0 = Unsafe.Add(ref srcRef, i);
T? u1 = Unsafe.Add(ref srcRef, i + 1);
T? u2 = Unsafe.Add(ref srcRef, i + 2);
T? u3 = Unsafe.Add(ref srcRef, i + 3);
// Получаем ref-ссылки на ячейки назначения (zero-cost адресация)
ref var d0 = ref Unsafe.Add(ref dstRef, i);
ref var d1 = ref Unsafe.Add(ref dstRef, i + 1);
ref var d2 = ref Unsafe.Add(ref dstRef, i + 2);
ref var d3 = ref Unsafe.Add(ref dstRef, i + 3);
// Пишем строго по месту. Если HasValue == false, в ячейку dX запишется null (сбросятся байты флага)
d0 = u0.HasValue ? (u0.Value.ToDouble() * multiplicator).ToUnit<T>() : null;
d1 = u1.HasValue ? (u1.Value.ToDouble() * multiplicator).ToUnit<T>() : null;
d2 = u2.HasValue ? (u2.Value.ToDouble() * multiplicator).ToUnit<T>() : null;
d3 = u3.HasValue ? (u3.Value.ToDouble() * multiplicator).ToUnit<T>() : null;
}
// 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук)
for (; i < len; i++)
{
T? unit = Unsafe.Add(ref srcRef, i);
ref var dst = ref Unsafe.Add(ref dstRef, i);
dst = unit.HasValue ? (unit.Value.ToDouble() * multiplicator).ToUnit<T>() : null;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Multiply<T>(this double multiplicator, ReadOnlySpan<T> units, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Multiply<T>(this double multiplicator, ReadOnlySpan<T?> units, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator, destination);
// === List<T> ===
public static List<T> Multiply<T>(this List<T> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<T>(len);
result.SetCountUnsafe(len);
Extensions.MultiplyCore(CollectionsMarshal.AsSpan(units), multiplicator, CollectionsMarshal.AsSpan(result));
return result;
}
public static List<T?> Multiply<T>(this List<T?> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
var resultArray = new T?[count];
ReadOnlySpan<T?> srcSpan = CollectionsMarshal.AsSpan(units);
for (int i = 0; i < count; i++)
{
T? item = srcSpan[i];
if (item.HasValue)
{
ref var dst = ref resultArray[i];
dst = (item.Value.ToDouble() * multiplicator).ToUnit<T>();
}
}
return [.. resultArray];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static List<T> Multiply<T>(this double multiplicator, List<T> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static List<T?> Multiply<T>(this double multiplicator, List<T?> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator);
// === ICollection<T> ===
public static ICollection<T> Multiply<T>(this ICollection<T> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T[] array) return array.Multiply(multiplicator);
if (units is List<T> list) return list.Multiply(multiplicator);
T[] sharedArray = ArrayPool<T>.Shared.Rent(count);
var finalResult = new T[count];
try
{
units.CopyTo(sharedArray, 0);
MultiplyCore(new ReadOnlySpan<T>(sharedArray, 0, count), multiplicator, finalResult.AsSpan());
return finalResult;
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static ICollection<T?> Multiply<T>(this ICollection<T?> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T?[] array) return array.Multiply(multiplicator);
if (units is List<T?> list) return list.Multiply(multiplicator);
var resultArray = new T?[count];
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
try
{
units.CopyTo(sharedNullableArray, 0);
var srcSpan = new ReadOnlySpan<T?>(sharedNullableArray, 0, count);
srcSpan.Multiply(multiplicator, resultArray.AsSpan());
return resultArray;
}
finally
{
ArrayPool<T?>.Shared.Return(sharedNullableArray);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ICollection<T> Multiply<T>(this double multiplicator, ICollection<T> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ICollection<T?> Multiply<T>(this double multiplicator, ICollection<T?> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator);
// === IReadOnlyCollection<T> ===
public static IReadOnlyCollection<T> Multiply<T>(this IReadOnlyCollection<T> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T[] array) return array.Multiply(multiplicator);
if (units is List<T> list) return list.Multiply(multiplicator);
T[] sharedArray = ArrayPool<T>.Shared.Rent(count);
var finalResult = new T[count];
try
{
units.CopyTo(sharedArray, 0);
MultiplyCore(new ReadOnlySpan<T>(sharedArray, 0, count), multiplicator, finalResult.AsSpan());
return finalResult;
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static IReadOnlyCollection<T?> Multiply<T>(this IReadOnlyCollection<T?> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T?[] array) return array.Multiply(multiplicator);
if (units is List<T?> list) return list.Multiply(multiplicator);
var resultArray = new T?[count];
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
try
{
units.CopyTo(sharedNullableArray, 0);
var srcSpan = new ReadOnlySpan<T?>(sharedNullableArray, 0, count);
srcSpan.Multiply(multiplicator, resultArray.AsSpan());
return resultArray;
}
finally
{
ArrayPool<T?>.Shared.Return(sharedNullableArray);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IReadOnlyCollection<T> Multiply<T>(this double multiplicator, IReadOnlyCollection<T> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IReadOnlyCollection<T?> Multiply<T>(this double multiplicator, IReadOnlyCollection<T?> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Multiply(multiplicator);
// === IEnumerable<T> + yeild ===
static IEnumerable<T> MultiplyIterator<T>(IEnumerable<T> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (var item in units)
yield return (item.ToDouble() * multiplicator).ToUnit<T>();
}
static IEnumerable<T?> MultiplyNullableIterator<T>(IEnumerable<T?> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (T? item in units)
yield return item.HasValue
? (item.Value.ToDouble() * multiplicator).ToUnit<T>() : null;
}
// === IEnumerable<T> ===
public static IEnumerable<T> Multiply<T>(this IEnumerable<T> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T[] array) return array.Multiply(multiplicator);
if (units is List<T> list) return list.Multiply(multiplicator);
if (units is ICollection<T> col) return col.Multiply(multiplicator);
if (units is IReadOnlyCollection<T> roc) return roc.Multiply(multiplicator);
return MultiplyIterator(units, multiplicator);
}
public static IEnumerable<T?> Multiply<T>(this IEnumerable<T?> units, double multiplicator)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T?[] array) return array.Multiply(multiplicator);
if (units is List<T?> list) return list.Multiply(multiplicator);
if (units is ICollection<T?> col) return col.Multiply(multiplicator);
if (units is IReadOnlyCollection<T?> roc) return roc.Multiply(multiplicator);
return MultiplyNullableIterator(units, multiplicator);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IEnumerable<T> Multiply<T>(this double multiplicator, IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T> => Multiply(units, multiplicator);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IEnumerable<T?> Multiply<T>(this double multiplicator, IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T> => Multiply(units, multiplicator);
// ==========================================
// === DIVIDE ===
// ==========================================
public static T[] Divide<T>(this T[] units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new T[len];
DivideCore(units, divisor, result);
return result;
}
public static T?[] Divide<T>(this T?[] units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
// Выделяем чистую память (0 аллокаций логики, только сам массив)
var result = new T?[len];
double invDivisor = 1.0 / divisor;
for (int i = 0; i < len; i++)
{
// Читаем из исходного по значению (бесплатно для 8 байт)
T? item = units[i];
if (item.HasValue)
{
// Пишем напрямую в результат по ref-ссылке (всего 1 запись в память!)
ref var dst = ref result[i];
dst = (item.Value.ToDouble() / invDivisor).ToUnit<T>();
}
}
return result;
}
public static T[] Divide<T>(this double dividend, T[] units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new T[len];
DivideCore(dividend, units, result);
return result;
}
public static T?[] Divide<T>(this double dividend, T?[] units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new T?[len];
for (int i = 0; i < len; i++)
{
T? item = units[i];
if (item.HasValue)
{
ref var dst = ref result[i];
dst = (dividend / item.Value.ToDouble()).ToUnit<T>();
}
}
return result;
}
// === ReadOnlySpan ===
public static void Divide<T>(this ReadOnlySpan<T> units, double divisor, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.Length > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
Extensions.DivideCore(units, divisor, destination);
}
public static void Divide<T>(this ReadOnlySpan<T?> units, double divisor, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.IsEmpty) return;
int len = units.Length;
// Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов
ref var srcRef = ref MemoryMarshal.GetReference(units);
ref var dstRef = ref MemoryMarshal.GetReference(destination);
int i = 0;
int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4)
double invDivisor = 1.0 / divisor;
// 1. ОСНОВНОЙ ЦИКЛ: Обрабатываем конвейером по 4 элемента за итерацию
for (; i < unrollEnd; i += 4)
{
T? u0 = Unsafe.Add(ref srcRef, i);
T? u1 = Unsafe.Add(ref srcRef, i + 1);
T? u2 = Unsafe.Add(ref srcRef, i + 2);
T? u3 = Unsafe.Add(ref srcRef, i + 3);
// Получаем ref-ссылки на ячейки назначения (zero-cost адресация)
ref var d0 = ref Unsafe.Add(ref dstRef, i);
ref var d1 = ref Unsafe.Add(ref dstRef, i + 1);
ref var d2 = ref Unsafe.Add(ref dstRef, i + 2);
ref var d3 = ref Unsafe.Add(ref dstRef, i + 3);
// Пишем строго по месту. Если HasValue == false, в ячейку dX запишется null (сбросятся байты флага)
d0 = u0.HasValue ? (u0.Value.ToDouble() / invDivisor).ToUnit<T>() : null;
d1 = u1.HasValue ? (u1.Value.ToDouble() / invDivisor).ToUnit<T>() : null;
d2 = u2.HasValue ? (u2.Value.ToDouble() / invDivisor).ToUnit<T>() : null;
d3 = u3.HasValue ? (u3.Value.ToDouble() / invDivisor).ToUnit<T>() : null;
}
// 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук)
for (; i < len; i++)
{
T? unit = Unsafe.Add(ref srcRef, i);
ref var dst = ref Unsafe.Add(ref dstRef, i);
dst = unit.HasValue ? (unit.Value.ToDouble() / invDivisor).ToUnit<T>() : null;
}
}
public static void Divide<T>(this double dividend, ReadOnlySpan<T> units, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.Length > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
Extensions.DivideCore(dividend, units, destination);
}
public static void Divide<T>(this double dividend, ReadOnlySpan<T?> units, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.IsEmpty) return;
int len = units.Length;
// Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов
ref var srcRef = ref MemoryMarshal.GetReference(units);
ref var dstRef = ref MemoryMarshal.GetReference(destination);
int i = 0;
int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4)
// 1. ОСНОВНОЙ ЦИКЛ: Обрабатываем конвейером по 4 элемента за итерацию
for (; i < unrollEnd; i += 4)
{
T? u0 = Unsafe.Add(ref srcRef, i);
T? u1 = Unsafe.Add(ref srcRef, i + 1);
T? u2 = Unsafe.Add(ref srcRef, i + 2);
T? u3 = Unsafe.Add(ref srcRef, i + 3);
// Получаем ref-ссылки на ячейки назначения (zero-cost адресация)
ref var d0 = ref Unsafe.Add(ref dstRef, i);
ref var d1 = ref Unsafe.Add(ref dstRef, i + 1);
ref var d2 = ref Unsafe.Add(ref dstRef, i + 2);
ref var d3 = ref Unsafe.Add(ref dstRef, i + 3);
// Пишем строго по месту. Если HasValue == false, в ячейку dX запишется null (сбросятся байты флага)
d0 = u0.HasValue ? (dividend / u0.Value.ToDouble()).ToUnit<T>() : null;
d1 = u1.HasValue ? (dividend / u1.Value.ToDouble()).ToUnit<T>() : null;
d2 = u2.HasValue ? (dividend / u2.Value.ToDouble()).ToUnit<T>() : null;
d3 = u3.HasValue ? (dividend / u3.Value.ToDouble()).ToUnit<T>() : null;
}
// 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук)
for (; i < len; i++)
{
T? unit = Unsafe.Add(ref srcRef, i);
ref var dst = ref Unsafe.Add(ref dstRef, i);
dst = unit.HasValue ? (dividend / unit.Value.ToDouble()).ToUnit<T>() : null;
}
}
// === List<T> ===
public static List<T> Divide<T>(this List<T> units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<T>(len);
result.SetCountUnsafe(len);
Extensions.DivideCore(CollectionsMarshal.AsSpan(units), divisor, CollectionsMarshal.AsSpan(result));
return result;
}
public static List<T?> Divide<T>(this List<T?> units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
var resultArray = new T?[count];
double invDivisor = 1.0 / divisor;
ReadOnlySpan<T?> srcSpan = CollectionsMarshal.AsSpan(units);
for (int i = 0; i < count; i++)
{
T? item = srcSpan[i];
if (item.HasValue)
{
ref var dst = ref resultArray[i];
dst = (item.Value.ToDouble() / invDivisor).ToUnit<T>();
}
}
return [.. resultArray];
}
public static List<T> Divide<T>(this double dividend, List<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<T>(len);
result.SetCountUnsafe(len);
Extensions.DivideCore(dividend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result));
return result;
}
public static List<T?> Divide<T>(this double dividend, List<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
var resultArray = new T?[count];
ReadOnlySpan<T?> srcSpan = CollectionsMarshal.AsSpan(units);
for (int i = 0; i < count; i++)
{
T? item = srcSpan[i];
if (item.HasValue)
{
ref var dst = ref resultArray[i];
dst = (dividend / item.Value.ToDouble()).ToUnit<T>();
}
}
return [.. resultArray];
}
// === ICollection<T> ===
public static ICollection<T> Divide<T>(this ICollection<T> units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T[] array) return array.Divide(divisor);
if (units is List<T> list) return list.Divide(divisor);
T[] sharedArray = ArrayPool<T>.Shared.Rent(count);
var finalResult = new T[count];
try
{
units.CopyTo(sharedArray, 0);
DivideCore(new ReadOnlySpan<T>(sharedArray, 0, count), divisor, finalResult.AsSpan());
return finalResult;
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static ICollection<T?> Divide<T>(this ICollection<T?> units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T?[] array) return array.Divide(divisor);
if (units is List<T?> list) return list.Divide(divisor);
var resultArray = new T?[count];
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
try
{
units.CopyTo(sharedNullableArray, 0);
var srcSpan = new ReadOnlySpan<T?>(sharedNullableArray, 0, count);
srcSpan.Divide(divisor, resultArray.AsSpan());
return resultArray;
}
finally
{
ArrayPool<T?>.Shared.Return(sharedNullableArray);
}
}
public static ICollection<T> Divide<T>(this double dividend, ICollection<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T[] array) return dividend.Divide(array);
if (units is List<T> list) return dividend.Divide(list);
T[] sharedArray = ArrayPool<T>.Shared.Rent(count);
var finalResult = new T[count];
try
{
units.CopyTo(sharedArray, 0);
DivideCore(dividend, new ReadOnlySpan<T>(sharedArray, 0, count), finalResult.AsSpan());
return finalResult;
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static ICollection<T?> Divide<T>(this double dividend, ICollection<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T?[] array) return dividend.Divide(array);
if (units is List<T?> list) return dividend.Divide(list);
var resultArray = new T?[count];
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
try
{
units.CopyTo(sharedNullableArray, 0);
var srcSpan = new ReadOnlySpan<T?>(sharedNullableArray, 0, count);
dividend.Divide(srcSpan, resultArray.AsSpan());
return resultArray;
}
finally
{
ArrayPool<T?>.Shared.Return(sharedNullableArray);
}
}
// === IReadOnlyCollection<T> ===
public static IReadOnlyCollection<T> Divide<T>(this IReadOnlyCollection<T> units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T[] array) return array.Divide(divisor);
if (units is List<T> list) return list.Divide(divisor);
T[] sharedArray = ArrayPool<T>.Shared.Rent(count);
var finalResult = new T[count];
try
{
units.CopyTo(sharedArray, 0);
DivideCore(new ReadOnlySpan<T>(sharedArray, 0, count), divisor, finalResult.AsSpan());
return finalResult;
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static IReadOnlyCollection<T?> Divide<T>(this IReadOnlyCollection<T?> units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T?[] array) return array.Divide(divisor);
if (units is List<T?> list) return list.Divide(divisor);
var resultArray = new T?[count];
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
try
{
units.CopyTo(sharedNullableArray, 0);
var srcSpan = new ReadOnlySpan<T?>(sharedNullableArray, 0, count);
srcSpan.Divide(divisor, resultArray.AsSpan());
return resultArray;
}
finally
{
ArrayPool<T?>.Shared.Return(sharedNullableArray);
}
}
public static IReadOnlyCollection<T> Divide<T>(this double dividend, IReadOnlyCollection<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T[] array) return dividend.Divide(array);
if (units is List<T> list) return dividend.Divide(list);
T[] sharedArray = ArrayPool<T>.Shared.Rent(count);
var finalResult = new T[count];
try
{
units.CopyTo(sharedArray, 0);
DivideCore(dividend, new ReadOnlySpan<T>(sharedArray, 0, count), finalResult.AsSpan());
return finalResult;
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static IReadOnlyCollection<T?> Divide<T>(this double dividend, IReadOnlyCollection<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T?[] array) return dividend.Divide(array);
if (units is List<T?> list) return dividend.Divide(list);
var resultArray = new T?[count];
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
try
{
units.CopyTo(sharedNullableArray, 0);
var srcSpan = new ReadOnlySpan<T?>(sharedNullableArray, 0, count);
dividend.Divide(srcSpan, resultArray.AsSpan());
return resultArray;
}
finally
{
ArrayPool<T?>.Shared.Return(sharedNullableArray);
}
}
// === IEnumerable<T> + yeild ===
static IEnumerable<T> DivideIterator<T>(IEnumerable<T> units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
{
double invDivisor = 1.0 / divisor;
foreach (var item in units)
yield return (item.ToDouble() / invDivisor).ToUnit<T>();
}
static IEnumerable<T?> DivideNullableIterator<T>(IEnumerable<T?> units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
{
double invDivisor = 1.0 / divisor;
foreach (T? item in units)
yield return item.HasValue
? (item.Value.ToDouble() / invDivisor).ToUnit<T>() : null;
}
static IEnumerable<T> DivideIterator<T>(double dividend, IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (var item in units)
yield return (dividend / item.ToDouble()).ToUnit<T>();
}
static IEnumerable<T?> DivideNullableIterator<T>(double dividend, IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (T? item in units)
yield return item.HasValue
? (dividend / item.Value.ToDouble()).ToUnit<T>() : null;
}
// === IEnumerable<T> ===
public static IEnumerable<T> Divide<T>(this IEnumerable<T> units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T[] array) return array.Divide(divisor);
if (units is List<T> list) return list.Divide(divisor);
if (units is ICollection<T> col) return col.Divide(divisor);
if (units is IReadOnlyCollection<T> roc) return roc.Divide(divisor);
return DivideIterator(units, divisor);
}
public static IEnumerable<T?> Divide<T>(this IEnumerable<T?> units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T?[] array) return array.Divide(divisor);
if (units is List<T?> list) return list.Divide(divisor);
if (units is ICollection<T?> col) return col.Divide(divisor);
if (units is IReadOnlyCollection<T?> roc) return roc.Divide(divisor);
return DivideNullableIterator(units, divisor);
}
public static IEnumerable<T> Divide<T>(this double dividend, IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T[] array) return dividend.Divide(array);
if (units is List<T> list) return dividend.Divide(list);
if (units is ICollection<T> col) return dividend.Divide(col);
if (units is IReadOnlyCollection<T> roc) return dividend.Divide(roc);
return DivideIterator(dividend, units);
}
public static IEnumerable<T?> Divide<T>(this double dividend, IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T?[] array) return dividend.Divide(array);
if (units is List<T?> list) return dividend.Divide(list);
if (units is ICollection<T?> col) return dividend.Divide(col);
if (units is IReadOnlyCollection<T?> roc) return dividend.Divide(roc);
return DivideNullableIterator(dividend, units);
}
// ==========================================
// === PLUS ===
// ==========================================
public static T[] Plus<T>(this T[] units, double summand)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new T[len];
PlusCore(units, summand, result);
return result;
}
public static T?[] Plus<T>(this T?[] units, double summand)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
// Выделяем чистую память (0 аллокаций логики, только сам массив)
var result = new T?[len];
for (int i = 0; i < len; i++)
{
// Читаем из исходного по значению (бесплатно для 8 байт)
T? item = units[i];
if (item.HasValue)
{
// Пишем напрямую в результат по ref-ссылке (всего 1 запись в память!)
ref var dst = ref result[i];
dst = (item.Value.ToDouble() + summand).ToUnit<T>();
}
}
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T[] Plus<T>(this double summand, T[] units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T?[] Plus<T>(this double summand, T?[] units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand);
// === ReadOnlySpan ===
public static void Plus<T>(this ReadOnlySpan<T> units, double summand, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.Length > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
Extensions.PlusCore(units, summand, destination);
}
public static void Plus<T>(this ReadOnlySpan<T?> units, double summand, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.IsEmpty) return;
int len = units.Length;
// Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов
ref var srcRef = ref MemoryMarshal.GetReference(units);
ref var dstRef = ref MemoryMarshal.GetReference(destination);
int i = 0;
int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4)
// 1. ОСНОВНОЙ ЦИКЛ: Обрабатываем конвейером по 4 элемента за итерацию
for (; i < unrollEnd; i += 4)
{
T? u0 = Unsafe.Add(ref srcRef, i);
T? u1 = Unsafe.Add(ref srcRef, i + 1);
T? u2 = Unsafe.Add(ref srcRef, i + 2);
T? u3 = Unsafe.Add(ref srcRef, i + 3);
// Получаем ref-ссылки на ячейки назначения (zero-cost адресация)
ref var d0 = ref Unsafe.Add(ref dstRef, i);
ref var d1 = ref Unsafe.Add(ref dstRef, i + 1);
ref var d2 = ref Unsafe.Add(ref dstRef, i + 2);
ref var d3 = ref Unsafe.Add(ref dstRef, i + 3);
// Пишем строго по месту. Если HasValue == false, в ячейку dX запишется null (сбросятся байты флага)
d0 = u0.HasValue ? (u0.Value.ToDouble() + summand).ToUnit<T>() : null;
d1 = u1.HasValue ? (u1.Value.ToDouble() + summand).ToUnit<T>() : null;
d2 = u2.HasValue ? (u2.Value.ToDouble() + summand).ToUnit<T>() : null;
d3 = u3.HasValue ? (u3.Value.ToDouble() + summand).ToUnit<T>() : null;
}
// 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук)
for (; i < len; i++)
{
T? unit = Unsafe.Add(ref srcRef, i);
ref var dst = ref Unsafe.Add(ref dstRef, i);
dst = unit.HasValue ? (unit.Value.ToDouble() + summand).ToUnit<T>() : null;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Plus<T>(this double summand, ReadOnlySpan<T> units, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand, destination);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Plus<T>(this double summand, ReadOnlySpan<T?> units, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand, destination);
// === List<T> ===
public static List<T> Plus<T>(this List<T> units, double summand)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<T>(len);
result.SetCountUnsafe(len);
Extensions.PlusCore(CollectionsMarshal.AsSpan(units), summand, CollectionsMarshal.AsSpan(result));
return result;
}
public static List<T?> Plus<T>(this List<T?> units, double summand)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
var resultArray = new T?[count];
ReadOnlySpan<T?> srcSpan = CollectionsMarshal.AsSpan(units);
for (int i = 0; i < count; i++)
{
T? item = srcSpan[i];
if (item.HasValue)
{
ref var dst = ref resultArray[i];
dst = (item.Value.ToDouble() + summand).ToUnit<T>();
}
}
return [.. resultArray];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static List<T> Plus<T>(this double summand, List<T> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static List<T?> Plus<T>(this double summand, List<T?> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand);
// === ICollection<T> ===
public static ICollection<T> Plus<T>(this ICollection<T> units, double summand)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T[] array) return array.Plus(summand);
if (units is List<T> list) return list.Plus(summand);
T[] sharedArray = ArrayPool<T>.Shared.Rent(count);
var finalResult = new T[count];
try
{
units.CopyTo(sharedArray, 0);
PlusCore(new ReadOnlySpan<T>(sharedArray, 0, count), summand, finalResult.AsSpan());
return finalResult;
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static ICollection<T?> Plus<T>(this ICollection<T?> units, double summand)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T?[] array) return array.Plus(summand);
if (units is List<T?> list) return list.Plus(summand);
var resultArray = new T?[count];
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
try
{
units.CopyTo(sharedNullableArray, 0);
var srcSpan = new ReadOnlySpan<T?>(sharedNullableArray, 0, count);
srcSpan.Plus(summand, resultArray.AsSpan());
return resultArray;
}
finally
{
ArrayPool<T?>.Shared.Return(sharedNullableArray);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ICollection<T> Plus<T>(this double summand, ICollection<T> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ICollection<T?> Plus<T>(this double summand, ICollection<T?> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand);
// === IReadOnlyCollection<T> ===
public static IReadOnlyCollection<T> Plus<T>(this IReadOnlyCollection<T> units, double summand)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T[] array) return array.Plus(summand);
if (units is List<T> list) return list.Plus(summand);
T[] sharedArray = ArrayPool<T>.Shared.Rent(count);
var finalResult = new T[count];
try
{
units.CopyTo(sharedArray, 0);
PlusCore(new ReadOnlySpan<T>(sharedArray, 0, count), summand, finalResult.AsSpan());
return finalResult;
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static IReadOnlyCollection<T?> Plus<T>(this IReadOnlyCollection<T?> units, double summand)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T?[] array) return array.Plus(summand);
if (units is List<T?> list) return list.Plus(summand);
var resultArray = new T?[count];
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
try
{
units.CopyTo(sharedNullableArray, 0);
var srcSpan = new ReadOnlySpan<T?>(sharedNullableArray, 0, count);
srcSpan.Plus(summand, resultArray.AsSpan());
return resultArray;
}
finally
{
ArrayPool<T?>.Shared.Return(sharedNullableArray);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IReadOnlyCollection<T> Plus<T>(this double summand, IReadOnlyCollection<T> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IReadOnlyCollection<T?> Plus<T>(this double summand, IReadOnlyCollection<T?> units)
where T : struct, IMensuraUnit, IEquatable<T> => units.Plus(summand);
// === IEnumerable<T> + yeild ===
static IEnumerable<T> PlusIterator<T>(IEnumerable<T> units, double summand)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (var item in units)
yield return (item.ToDouble() + summand).ToUnit<T>();
}
static IEnumerable<T?> PlusNullableIterator<T>(IEnumerable<T?> units, double summand)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (T? item in units)
yield return item.HasValue
? (item.Value.ToDouble() + summand).ToUnit<T>() : null;
}
// === IEnumerable<T> ===
public static IEnumerable<T> Plus<T>(this IEnumerable<T> units, double summand)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T[] array) return array.Plus(summand);
if (units is List<T> list) return list.Plus(summand);
if (units is ICollection<T> col) return col.Plus(summand);
if (units is IReadOnlyCollection<T> roc) return roc.Plus(summand);
return PlusIterator(units, summand);
}
public static IEnumerable<T?> Plus<T>(this IEnumerable<T?> units, double summand)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T?[] array) return array.Plus(summand);
if (units is List<T?> list) return list.Plus(summand);
if (units is ICollection<T?> col) return col.Plus(summand);
if (units is IReadOnlyCollection<T?> roc) return roc.Plus(summand);
return PlusNullableIterator(units, summand);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IEnumerable<T> Plus<T>(this double summand, IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T> => Plus(units, summand);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IEnumerable<T?> Plus<T>(this double summand, IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T> => Plus(units, summand);
// ==========================================
// === MINUS ===
// ==========================================
public static T[] Minus<T>(this T[] units, double subtrahend)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new T[len];
MinusCore(units, subtrahend, result);
return result;
}
public static T?[] Minus<T>(this T?[] units, double subtrahend)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
// Выделяем чистую память (0 аллокаций логики, только сам массив)
var result = new T?[len];
for (int i = 0; i < len; i++)
{
// Читаем из исходного по значению (бесплатно для 8 байт)
T? item = units[i];
if (item.HasValue)
{
// Пишем напрямую в результат по ref-ссылке (всего 1 запись в память!)
ref var dst = ref result[i];
dst = (item.Value.ToDouble() - subtrahend).ToUnit<T>();
}
}
return result;
}
public static T[] Minus<T>(this double minuend, T[] units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new T[len];
MinusCore(minuend, units, result);
return result;
}
public static T?[] Minus<T>(this double minuend, T?[] units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new T?[len];
for (int i = 0; i < len; i++)
{
T? item = units[i];
if (item.HasValue)
{
ref var dst = ref result[i];
dst = (minuend - item.Value.ToDouble()).ToUnit<T>();
}
}
return result;
}
// === ReadOnlySpan ===
public static void Minus<T>(this ReadOnlySpan<T> units, double subtrahend, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.Length > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
Extensions.MinusCore(units, subtrahend, destination);
}
public static void Minus<T>(this ReadOnlySpan<T?> units, double subtrahend, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.IsEmpty) return;
int len = units.Length;
// Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов
ref var srcRef = ref MemoryMarshal.GetReference(units);
ref var dstRef = ref MemoryMarshal.GetReference(destination);
int i = 0;
int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4)
// 1. ОСНОВНОЙ ЦИКЛ: Обрабатываем конвейером по 4 элемента за итерацию
for (; i < unrollEnd; i += 4)
{
T? u0 = Unsafe.Add(ref srcRef, i);
T? u1 = Unsafe.Add(ref srcRef, i + 1);
T? u2 = Unsafe.Add(ref srcRef, i + 2);
T? u3 = Unsafe.Add(ref srcRef, i + 3);
// Получаем ref-ссылки на ячейки назначения (zero-cost адресация)
ref var d0 = ref Unsafe.Add(ref dstRef, i);
ref var d1 = ref Unsafe.Add(ref dstRef, i + 1);
ref var d2 = ref Unsafe.Add(ref dstRef, i + 2);
ref var d3 = ref Unsafe.Add(ref dstRef, i + 3);
// Пишем строго по месту. Если HasValue == false, в ячейку dX запишется null (сбросятся байты флага)
d0 = u0.HasValue ? (u0.Value.ToDouble() - subtrahend).ToUnit<T>() : null;
d1 = u1.HasValue ? (u1.Value.ToDouble() - subtrahend).ToUnit<T>() : null;
d2 = u2.HasValue ? (u2.Value.ToDouble() - subtrahend).ToUnit<T>() : null;
d3 = u3.HasValue ? (u3.Value.ToDouble() - subtrahend).ToUnit<T>() : null;
}
// 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук)
for (; i < len; i++)
{
T? unit = Unsafe.Add(ref srcRef, i);
ref var dst = ref Unsafe.Add(ref dstRef, i);
dst = unit.HasValue ? (unit.Value.ToDouble() - subtrahend).ToUnit<T>() : null;
}
}
public static void Minus<T>(this double minuend, ReadOnlySpan<T> units, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.Length > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
Extensions.MinusCore(minuend, units, destination);
}
public static void Minus<T>(this double minuend, ReadOnlySpan<T?> units, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.IsEmpty) return;
int len = units.Length;
// Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов
ref var srcRef = ref MemoryMarshal.GetReference(units);
ref var dstRef = ref MemoryMarshal.GetReference(destination);
int i = 0;
int unrollEnd = len & ~3; // Граница развернутого цикла (кратная 4)
// 1. ОСНОВНОЙ ЦИКЛ: Обрабатываем конвейером по 4 элемента за итерацию
for (; i < unrollEnd; i += 4)
{
T? u0 = Unsafe.Add(ref srcRef, i);
T? u1 = Unsafe.Add(ref srcRef, i + 1);
T? u2 = Unsafe.Add(ref srcRef, i + 2);
T? u3 = Unsafe.Add(ref srcRef, i + 3);
// Получаем ref-ссылки на ячейки назначения (zero-cost адресация)
ref var d0 = ref Unsafe.Add(ref dstRef, i);
ref var d1 = ref Unsafe.Add(ref dstRef, i + 1);
ref var d2 = ref Unsafe.Add(ref dstRef, i + 2);
ref var d3 = ref Unsafe.Add(ref dstRef, i + 3);
// Пишем строго по месту. Если HasValue == false, в ячейку dX запишется null (сбросятся байты флага)
d0 = u0.HasValue ? (minuend - u0.Value.ToDouble()).ToUnit<T>() : null;
d1 = u1.HasValue ? (minuend - u1.Value.ToDouble()).ToUnit<T>() : null;
d2 = u2.HasValue ? (minuend - u2.Value.ToDouble()).ToUnit<T>() : null;
d3 = u3.HasValue ? (minuend - u3.Value.ToDouble()).ToUnit<T>() : null;
}
// 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук)
for (; i < len; i++)
{
T? unit = Unsafe.Add(ref srcRef, i);
ref var dst = ref Unsafe.Add(ref dstRef, i);
dst = unit.HasValue ? (minuend - unit.Value.ToDouble()).ToUnit<T>() : null;
}
}
// === List<T> ===
public static List<T> Minus<T>(this List<T> units, double subtrahend)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<T>(len);
result.SetCountUnsafe(len);
Extensions.MinusCore(CollectionsMarshal.AsSpan(units), subtrahend, CollectionsMarshal.AsSpan(result));
return result;
}
public static List<T?> Minus<T>(this List<T?> units, double subtrahend)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
var resultArray = new T?[count];
ReadOnlySpan<T?> srcSpan = CollectionsMarshal.AsSpan(units);
for (int i = 0; i < count; i++)
{
T? item = srcSpan[i];
if (item.HasValue)
{
ref var dst = ref resultArray[i];
dst = (item.Value.ToDouble() - subtrahend).ToUnit<T>();
}
}
return [.. resultArray];
}
public static List<T> Minus<T>(this double minuend, List<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<T>(len);
result.SetCountUnsafe(len);
Extensions.MinusCore(minuend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result));
return result;
}
public static List<T?> Minus<T>(this double minuend, List<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
var resultArray = new T?[count];
ReadOnlySpan<T?> srcSpan = CollectionsMarshal.AsSpan(units);
for (int i = 0; i < count; i++)
{
T? item = srcSpan[i];
if (item.HasValue)
{
ref var dst = ref resultArray[i];
dst = (minuend - item.Value.ToDouble()).ToUnit<T>();
}
}
return [.. resultArray];
}
// === ICollection<T> ===
public static ICollection<T> Minus<T>(this ICollection<T> units, double subtrahend)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T[] array) return array.Minus(subtrahend);
if (units is List<T> list) return list.Minus(subtrahend);
T[] sharedArray = ArrayPool<T>.Shared.Rent(count);
var finalResult = new T[count];
try
{
units.CopyTo(sharedArray, 0);
MinusCore(new ReadOnlySpan<T>(sharedArray, 0, count), subtrahend, finalResult.AsSpan());
return finalResult;
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static ICollection<T?> Minus<T>(this ICollection<T?> units, double subtrahend)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T?[] array) return array.Minus(subtrahend);
if (units is List<T?> list) return list.Minus(subtrahend);
var resultArray = new T?[count];
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
try
{
units.CopyTo(sharedNullableArray, 0);
var srcSpan = new ReadOnlySpan<T?>(sharedNullableArray, 0, count);
srcSpan.Minus(subtrahend, resultArray.AsSpan());
return resultArray;
}
finally
{
ArrayPool<T?>.Shared.Return(sharedNullableArray);
}
}
public static ICollection<T> Minus<T>(this double minuend, ICollection<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T[] array) return minuend.Minus(array);
if (units is List<T> list) return minuend.Minus(list);
T[] sharedArray = ArrayPool<T>.Shared.Rent(count);
var finalResult = new T[count];
try
{
units.CopyTo(sharedArray, 0);
MinusCore(minuend, new ReadOnlySpan<T>(sharedArray, 0, count), finalResult.AsSpan());
return finalResult;
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static ICollection<T?> Minus<T>(this double minuend, ICollection<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T?[] array) return minuend.Minus(array);
if (units is List<T?> list) return minuend.Minus(list);
var resultArray = new T?[count];
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
try
{
units.CopyTo(sharedNullableArray, 0);
var srcSpan = new ReadOnlySpan<T?>(sharedNullableArray, 0, count);
minuend.Minus(srcSpan, resultArray.AsSpan());
return resultArray;
}
finally
{
ArrayPool<T?>.Shared.Return(sharedNullableArray);
}
}
// === IReadOnlyCollection<T> ===
public static IReadOnlyCollection<T> Minus<T>(this IReadOnlyCollection<T> units, double subtrahend)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T[] array) return array.Minus(subtrahend);
if (units is List<T> list) return list.Minus(subtrahend);
T[] sharedArray = ArrayPool<T>.Shared.Rent(count);
var finalResult = new T[count];
try
{
units.CopyTo(sharedArray, 0);
MinusCore(new ReadOnlySpan<T>(sharedArray, 0, count), subtrahend, finalResult.AsSpan());
return finalResult;
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static IReadOnlyCollection<T?> Minus<T>(this IReadOnlyCollection<T?> units, double subtrahend)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T?[] array) return array.Minus(subtrahend);
if (units is List<T?> list) return list.Minus(subtrahend);
var resultArray = new T?[count];
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
try
{
units.CopyTo(sharedNullableArray, 0);
var srcSpan = new ReadOnlySpan<T?>(sharedNullableArray, 0, count);
srcSpan.Minus(subtrahend, resultArray.AsSpan());
return resultArray;
}
finally
{
ArrayPool<T?>.Shared.Return(sharedNullableArray);
}
}
public static IReadOnlyCollection<T> Minus<T>(this double minuend, IReadOnlyCollection<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T[] array) return minuend.Minus(array);
if (units is List<T> list) return minuend.Minus(list);
T[] sharedArray = ArrayPool<T>.Shared.Rent(count);
var finalResult = new T[count];
try
{
units.CopyTo(sharedArray, 0);
MinusCore(minuend, new ReadOnlySpan<T>(sharedArray, 0, count), finalResult.AsSpan());
return finalResult;
}
finally
{
ArrayPool<T>.Shared.Return(sharedArray);
}
}
public static IReadOnlyCollection<T?> Minus<T>(this double minuend, IReadOnlyCollection<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
if (units is T?[] array) return minuend.Minus(array);
if (units is List<T?> list) return minuend.Minus(list);
var resultArray = new T?[count];
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
try
{
units.CopyTo(sharedNullableArray, 0);
var srcSpan = new ReadOnlySpan<T?>(sharedNullableArray, 0, count);
minuend.Minus(srcSpan, resultArray.AsSpan());
return resultArray;
}
finally
{
ArrayPool<T?>.Shared.Return(sharedNullableArray);
}
}
// === IEnumerable<T> + yeild ===
static IEnumerable<T> MinusIterator<T>(IEnumerable<T> units, double subtrahend)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (var item in units)
yield return (item.ToDouble() - subtrahend).ToUnit<T>();
}
static IEnumerable<T?> MinusNullableIterator<T>(IEnumerable<T?> units, double subtrahend)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (T? item in units)
yield return item.HasValue
? (item.Value.ToDouble() - subtrahend).ToUnit<T>() : null;
}
static IEnumerable<T> MinusIterator<T>(double minuend, IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (var item in units)
yield return (minuend - item.ToDouble()).ToUnit<T>();
}
static IEnumerable<T?> MinusNullableIterator<T>(double minuend, IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (T? item in units)
yield return item.HasValue
? (minuend - item.Value.ToDouble()).ToUnit<T>() : null;
}
// === IEnumerable<T> ===
public static IEnumerable<T> Minus<T>(this IEnumerable<T> units, double subtrahend)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T[] array) return array.Minus(subtrahend);
if (units is List<T> list) return list.Minus(subtrahend);
if (units is ICollection<T> col) return col.Minus(subtrahend);
if (units is IReadOnlyCollection<T> roc) return roc.Minus(subtrahend);
return MinusIterator(units, subtrahend);
}
public static IEnumerable<T?> Minus<T>(this IEnumerable<T?> units, double subtrahend)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T?[] array) return array.Minus(subtrahend);
if (units is List<T?> list) return list.Minus(subtrahend);
if (units is ICollection<T?> col) return col.Minus(subtrahend);
if (units is IReadOnlyCollection<T?> roc) return roc.Minus(subtrahend);
return MinusNullableIterator(units, subtrahend);
}
public static IEnumerable<T> Minus<T>(this double minuend, IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T[] array) return minuend.Minus(array);
if (units is List<T> list) return minuend.Minus(list);
if (units is ICollection<T> col) return minuend.Minus(col);
if (units is IReadOnlyCollection<T> roc) return minuend.Minus(roc);
return MinusIterator(minuend, units);
}
public static IEnumerable<T?> Minus<T>(this double minuend, IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T?[] array) return minuend.Minus(array);
if (units is List<T?> list) return minuend.Minus(list);
if (units is ICollection<T?> col) return minuend.Minus(col);
if (units is IReadOnlyCollection<T?> roc) return minuend.Minus(roc);
return MinusNullableIterator(minuend, units);
}
// ==========================================
// === ЦЕЛАЯ СТЕПЕНЬ ===
// ==========================================
public static T[] Pow<T>(this T[] units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new T[len];
Extensions.PowCore(units, power, result);
return result;
}
public static T?[] Pow<T>(this T?[] units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new T?[len];
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue)
item = item.Value.ToDouble().QuickPow(power).ToUnit<T>();
}
return result;
}
// === ReadOnlySpan ===
public static Span<T> Pow<T>(this ReadOnlySpan<T> units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units.Length == 0) return [];
var result = new T[units.Length];
Extensions.PowCore(units, power, result);
return result;
}
public static Span<T?> Pow<T>(this ReadOnlySpan<T?> units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
int len = units.Length;
if (len == 0) return [];
var result = new T?[len];
units.CopyTo(result);
for (int i = 0; i < len; i++)
{
ref var item = ref result[i];
if (item.HasValue) item = item.Value.ToDouble().QuickPow(power).ToUnit<T>();
}
return result;
}
// === List<Length> ===
public static List<T> Pow<T>(this List<T> units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<T>(len);
result.SetCountUnsafe(len);
Extensions.PowCore(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result));
return result;
}
public static List<T?> Pow<T>(this List<T?> units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
int len = units.Count;
if (len == 0) return [];
var result = new List<T?>(units);
var dstSpan = CollectionsMarshal.AsSpan(result);
for (int i = 0; i < len; i++)
{
ref var item = ref dstSpan[i];
if (item.HasValue) item = item.Value.ToDouble().QuickPow(power).ToUnit<T>();
}
return result;
}
// === ICollection<Length> ===
public static ICollection<T> Pow<T>(this ICollection<T> units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T[] array) return array.Pow(power);
if (units is List<T> list) return list.Pow(power);
ICollection<T> result = [];
foreach (var item in units)
result.Add(item.ToDouble().QuickPow(power).ToUnit<T>());
return result;
}
public static ICollection<T?> Pow<T>(this ICollection<T?> units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T?[] array) return array.Pow(power);
if (units is List<T?> list) return list.Pow(power);
ICollection<T?> result = [];
foreach (var item in units)
result.Add((item.HasValue ? item.Value.ToDouble().QuickPow(power) : 0d).ToUnit<T>());
return result;
}
// === IReadOnlyCollection<Length> ===
public static IReadOnlyCollection<T> Pow<T>(this IReadOnlyCollection<T> units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T[] array) return array.Pow(power);
if (units is List<T> list) return list.Pow(power);
IReadOnlyCollection<T> result = [];
foreach (var item in units)
result.Add(item.ToDouble().QuickPow(power).ToUnit<T>());
return result;
}
public static IReadOnlyCollection<T?> Pow<T>(this IReadOnlyCollection<T?> units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T?[] array) return array.Pow(power);
if (units is List<T?> list) return list.Pow(power);
IReadOnlyCollection<T?> result = [];
foreach (var item in units)
result.Add((item.HasValue ? item.Value.ToDouble().QuickPow(power) : 0d).ToUnit<T>());
return result;
}
// === IEnumerable<T> + yeild ===
static IEnumerable<T> PowIterator<T>(IEnumerable<T> units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (var item in units)
yield return (item.ToDouble().QuickPow(power)).ToUnit<T>();
}
static IEnumerable<T?> PowNullableIterator<T>(IEnumerable<T?> units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
foreach (var item in units)
yield return item.HasValue
? item.Value.ToDouble().QuickPow(power).ToUnit<T>() : null;
}
// === IEnumerable<Length> ===
public static IEnumerable<T> Pow<T>(this IEnumerable<T> units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T[] array) return array.Pow(power);
if (units is List<T> list) return list.Pow(power);
if (units is ICollection<T> col) return col.Pow(power);
if (units is IReadOnlyCollection<T> roc) return roc.Pow(power);
else return PowIterator(units, power);
}
public static IEnumerable<T?> Pow<T>(this IEnumerable<T?> units, int power)
where T : struct, IMensuraUnit, IEquatable<T>
{
if (units is null) return null!;
if (units is T?[] array) return array.Pow(power);
if (units is List<T?> list) return list.Pow(power);
if (units is ICollection<T> col) return col.Pow(power);
if (units is IReadOnlyCollection<T> roc) return roc.Pow(power);
else return PowNullableIterator(units, power);
}
//internal static U Protect<U>(this U? metric) where U : class, IMetric<U>, new() => metric ?? new();
//internal static C Protect<C, U>(this C? collection)
// where C : IMetricCollection<U>, new() where U : class, IMetric<U>, new() => collection ?? new();
//public static C MetricSelect<C, U>(this C? collection, Func<U, U>? selector)
// where C : IMetricCollection<U>, new() where U : class, IMetric<U>, new()
//{
// var source = (collection ??= new());
// var nColl = (C)source.CreateByInstanceU(source.Count);
// if (selector is not null)
// {
// for (int i = 0; i < nColl.Count; i++)
// nColl[i] = selector(source[i]);
// return nColl;
// }
// return new();
//}
//public static IEnumerable<double> MetricSelect<U>(this IMetricCollection<U> collection, Func<U, double> selector)
// where U : class, IMetric<U>, new()
//{
// if (collection is not null)
// {
// if (selector is not null)
// return collection.Select(selector);
// return collection.Select(u => double.NaN);
// }
// else return [];
//}
//public static IMetricCollection<Uz> MetricSelect<Ux, Uz>(this IMetricCollection<Ux>? collection, Func<Ux, Uz>? selector)
// where Ux : class, IMetric<Ux>, new() where Uz : class, IMetric<Uz>, new()
//{
// if (collection is not null && selector is not null)
// {
// var destCollection = collection.CreateByInstance<Uz>(collection.Count);
// for (int i = 0; i < collection.Count; i++)
// destCollection[i] = selector(collection[i]);
// return destCollection;
// }
// return null!;
//}
//public static MetricCollection<Uz> MetricSelect<Ux, Uz>(this MetricCollection<Ux>? collection, Func<Ux, Uz>? selector)
// where Ux : class, IMetric<Ux>, new() where Uz : class, IMetric<Uz>, new()
//{
// if (collection is not null && selector is not null)
// {
// var destCollection = collection.CreateByInstance<Uz>(collection.Count());
// for (int i = 0; i < collection.Count(); i++)
// destCollection[i] = selector(collection[i]);
// return destCollection;
// }
// return null!;
//}
//public static MetricArray<Uz> MetricSelect<Ux, Uz>(this MetricArray<Ux>? collection, Func<Ux, Uz>? selector)
// where Ux : class, IMetric<Ux>, new() where Uz : class, IMetric<Uz>, new()
//{
// var coll = collection?.ToArray();
// if (coll is not null && selector is not null)
// {
// var destCollection = new MetricArray<Uz>(coll.Length);
// for (int i = 0; i < coll.Length; i++)
// destCollection[i] = selector(coll[i]);
// return destCollection;
// }
// return null!;
//}
//public static MetricList<Uz> MetricSelect<Ux, Uz>(this MetricList<Ux>? collection, Func<Ux, Uz>? selector)
// where Ux : class, IMetric<Ux>, new() where Uz : class, IMetric<Uz>, new()
//{
// if (collection is not null && selector is not null)
// {
// var destCollection = new MetricList<Uz>(collection.Count);
// for (int i = 0; i < collection.Count; i++)
// destCollection[i] = selector(collection[i]);
// return destCollection;
// }
// return null!;
//}
//public static MetricObservableCollection<Uz> MetricSelect<Ux, Uz>(this MetricObservableCollection<Ux>? collection, Func<Ux, Uz>? selector)
// where Ux : class, IMetric<Ux>, new() where Uz : class, IMetric<Uz>, new()
//{
// if (collection is not null && selector is not null)
// {
// var destCollection = new MetricObservableCollection<Uz>(collection.Count);
// for (int i = 0; i < collection.Count; i++)
// destCollection[i] = selector(collection[i]);
// return destCollection;
// }
// return null!;
//}
//public static double[] MetricSelect<U>(this MetricArray<U> collection, Func<U, double> selector)
// where U : class, IMetric<U>, new()
//{
// var coll = collection ?? [];
// var arr = new double[coll.Length];
// if (selector is not null)
// for (int i = 0; i < arr.Length; i++)
// arr[i] = selector(coll[i]);
// return arr;
//}
//public static List<double> MetricSelect<U>(this MetricList<U> collection, Func<U, double> selector)
// where U : class, IMetric<U>, new()
//{
// var coll = collection ?? [];
// var list = new List<double>(coll.Count);
// if (selector is not null)
// for (int i = 0; i < list.Count; i++)
// list[i] = selector(coll[i]);
// return list;
//}
//public static ObservableCollection<double> MetricSelect<U>(this MetricObservableCollection<U> collection, Func<U, double> selector)
// where U : class, IMetric<U>, new()
//{
// var coll = collection ?? [];
// var list = new List<double>(coll.Count);
// if (selector is not null)
// for (int i = 0; i < list.Count; i++)
// list[i] = selector(coll[i]);
// return new(list);
//}
//internal static C ForEachC<C, U>(this C? collection, Func<U, U>? Set)
// where C : IMetricCollection<U>, new() where U : class, IMetric<U>, new()
//{
// var nColl = (C)(collection ??= new()).CreateByInstanceU(collection.Count);
// if (Set is not null)
// for (int i = 0; i < nColl.Count; i++)
// nColl[i] = Set(nColl[i]);
// return nColl;
//}
//internal static double Protect_Value(this IMetric? metric) => metric is null ? 0d : metric._Value;
//public static U Min<U>(this U? T1, U? T2) where U : class, IMetric<U>, new() => (T1.Protect_Value() < T2.Protect_Value() ? T1 : T2).Protect();
//public static U Min<U>(this U T1, IEnumerable<U> units) where U : class, IMetric<U>, new() => (T1 ?? new()).Min((units ?? []).MaxBy(u => u.Protect_Value()));
//public static U Max<U>(this U? T1, U? T2) where U : class, IMetric<U>, new() => (T1.Protect_Value() > T2.Protect_Value() ? T1 : T2).Protect();
//public static U Max<U>(this U T1, IEnumerable<U> units) where U : class, IMetric<U>, new() => (T1 ?? new()).Max((units ?? []).MaxBy(u => u.Protect_Value()));
////internal static double ToDouble(this double number) => number;
////internal static double ToDouble(this double? number) => number ?? 0d;
////internal static double ToDouble<N>(this N number) where N : INumber<N> => Convert.ToDouble(number);
////internal static double ToDouble<N>(this N? number) where N : struct, INumber<N> => number is not null ? Convert.ToDouble(number) : 0d;
//internal static IEnumerable<U> MetricSelect<U>(this double[] nums, Func<double, U> selector) where U : class, IMetric<U>, new() => nums.Select(selector);
//internal static IEnumerable<U> MetricSelect<U>(this double?[] nums, Func<double, U> selector) where U : class, IMetric<U>, new() => nums.Select(num => selector(num.ToDouble()));
//internal static IEnumerable<U> MetricSelect<U, N>(this N[] nums, Func<double, U> selector) where N : INumber<N> where U : class, IMetric<U>, new() => nums.Select(num => selector(num.ToDouble()));
//internal static IEnumerable<U> MetricSelect<U, N>(this N?[] nums, Func<double, U> selector) where N : struct, INumber<N> where U : class, IMetric<U>, new() => nums.Select(num => selector(num.ToDouble()));
//internal static IEnumerable<U> MetricSelect<U>(this IEnumerable<double> nums, Func<double, U> selector) where U : class, IMetric<U>, new() => nums.Select(selector);
//internal static IEnumerable<U> MetricSelect<U>(this IEnumerable<double?> nums, Func<double, U> selector) where U : class, IMetric<U>, new() => nums.Select(num => selector(num.ToDouble()));
//internal static IEnumerable<U> MetricSelect<U, N>(this IEnumerable<N> nums, Func<double, U> selector) where N : INumber<N> where U : class, IMetric<U>, new() => nums.Select(num => selector(num.ToDouble()));
//internal static IEnumerable<U> MetricSelect<U, N>(this IEnumerable<N?> nums, Func<double, U> selector) where N : struct, INumber<N> where U : class, IMetric<U>, new() => nums.Select(num => selector(num.ToDouble()));
//public static U Clone<U>(this U? metric) where U : class, IMetric<U>, new() => new() { _Value = metric.Protect_Value() };
//public static U Abs<U>(this U? metric) where U : class, IMetric<U>, new() => new() { _Value = Math.Abs(metric.Protect_Value()) };
///// <summary>C^2 = A^2 + B^2</summary>
///// <returns>C = (A^2 + B^2).Sqrt(2)</returns>
//public static U Hypotenuse<U>(this U? A, U? B) where U : class, IMetric<U>, new()
//{
// var a = A.Protect_Value();
// var b = B.Protect_Value();
// return new U() { _Value = Math.Sqrt(a * a + b * b) };
//}
///// <summary>C^2 = A^2 + B^2</summary>
///// <returns>B = (C^2 - A^2).Sqrt(2)</returns>
//public static U KatetFromHyp<U>(this U? A, U? C) where U : class, IMetric<U>, new()
//{
// var a = A.Protect_Value();
// var c = C.Protect_Value();
// return new U() { _Value = Math.Sqrt(c * c - a * a) };
//}
///// <summary>C^2 = A^2 + B^2</summary>
///// <returns>B = (C^2 - A^2).Sqrt(2)</returns>
//public static U KatetFromKatet<U>(this U? C, U? A) where U : class, IMetric<U>, new()
//{
// var a = A.Protect_Value();
// var c = C.Protect_Value();
// return new U() { _Value = Math.Sqrt(c * c - a * a) };
//}
//public static Area Pow(this Length? metric, double? val = 2) => new() { _Value = Math.Pow(metric.Protect_Value(), val ?? 2) };
//public static Length Sqrt(this Area? metric) => new() { _Value = Math.Sqrt(metric.Protect_Value()) };
//public static U MetricSum<U>(this IEnumerable<U> args) where U : IMetric, new() => new() { _Value = args?.Where(t => t is not null).Sum(m => m.Protect_Value()) ?? 0d };
//public static U MetricAverage<U>(this IEnumerable<U> args) where U : IMetric, new() => new() { _Value = args?.Average(m => m.Protect_Value()) ?? double.NaN };
//public static U MetricMax<U>(this IEnumerable<U> args) where U : IMetric, new() => new() { _Value = args.Max(m => m.Protect_Value()) };
//public static U MetricMin<U>(this IEnumerable<U> args) where U : IMetric, new() => new() { _Value = args.Min(m => m.Protect_Value()) };
//public static C MetricSum<C, U>(this IEnumerable<MetricCollection<C, U>> collections)
// where C : MetricCollection<C, U>, ICreateByCapacity, new() where U : class, IMetric<U>, new()
//{
// var cArr = collections.ToArray();
// C accumulator = (C)cArr.FirstOrDefault(new C());
// for (int i = 1; i < cArr.Length; i++)
// accumulator = accumulator.FuncByPairOrOneToMany(cArr[i], (a, b) => a + b, out C _);
// return accumulator;
//}
//public static C MetricAverage<C, U>(this IEnumerable<MetricCollection<C, U>> collections)
// where C : MetricCollection<C, U>, ICreateByCapacity, new() where U : class, IMetric<U>, new()
// => collections.Sum() / collections.Count();
//public static U MetricSumBy<TSource, U>(this IEnumerable<TSource> source, Func<TSource, U> selector)
// where U : class, IMetric<U>, new()
//{
// if (source is null) return new();
// if (selector is null) throw new ArgumentNullException("selector is null");
// return new() { _Value = source.Select(selector).Where(t => t is not null).Sum(t => t.Protect_Value()) };
//}
//public static U MetricAverageBy<TSource, U>(this IEnumerable<TSource> source, Func<TSource, U> selector)
// where U : class, IMetric<U>, new()
//{
// if (source is null) return new();
// if (selector is null) throw new ArgumentNullException("selector is null");
// return new() { _Value = source.Select(selector).Average(t => t.Protect_Value()) };
//}
//public static C MetricSumBy<TSource, C, U>(this IEnumerable<TSource> source, Func<TSource, MetricCollection<C, U>> selector)
// where C : MetricCollection<C, U>, ICreateByCapacity, new() where U : class, IMetric<U>, new()
//{
// if (source is null) return new();
// if (selector is null) throw new ArgumentNullException("selector is null");
// return source.Select(selector).Sum();
//}
//public static C MetricAverageBy<TSource, C, U>(this IEnumerable<TSource> source, Func<TSource, MetricCollection<C, U>> selector)
// where C : MetricCollection<C, U>, ICreateByCapacity, new() where U : class, IMetric<U>, new()
//{
// if (source is null) return new();
// if (selector is null) throw new ArgumentNullException("selector is null");
// return source.Select(selector).Average();
//}
//public static MetricArray<U> ToMetricArray<U>(this IEnumerable<U> source) where U : class, IMetric<U>, new() => new(source);
//public static MetricList<U> ToMetricList<U>(this IEnumerable<U> source) where U : class, IMetric<U>, new() => new(source);
}