From a6c7c7f2e60ab7ec392cdb664c2510a4f149c2d4 Mon Sep 17 00:00:00 2001 From: Fantom TM Date: Tue, 2 Jun 2026 01:13:08 +0700 Subject: [PATCH] ++ --- QWERTYkez.Mensura/Extensions.cs | 1066 ++++++++++++++++------- QWERTYkez.Mensura/Units/XXXXXXXX.Gen.cs | 48 +- 2 files changed, 765 insertions(+), 349 deletions(-) diff --git a/QWERTYkez.Mensura/Extensions.cs b/QWERTYkez.Mensura/Extensions.cs index 7ab69cf..9d0dd8e 100644 --- a/QWERTYkez.Mensura/Extensions.cs +++ b/QWERTYkez.Mensura/Extensions.cs @@ -1055,86 +1055,16 @@ public static partial class Extensions // ========================================== // CORE // ========================================== - internal static void MultiplyCore(ReadOnlySpan source, double multiplicator, Span destination) + + internal static void Divide(double dividend, ReadOnlySpan units, Span destination) where T : struct, IMensuraUnit, IEquatable { - if (source.IsEmpty) return; + if (units.IsEmpty) return; + int len = units.Length; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); - int len = source.Length; - ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); - Span dstDouble = MemoryMarshal.Cast(destination); - - var vectorizedMultiplicator = new Vector(multiplicator); - int vectorSize = Vector.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>(ref currentSrc); - var multiplied = vector * vectorizedMultiplicator; - - Unsafe.As>(ref currentDst) = multiplied; - } - - for (; i < len; i++) - { - Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) * multiplicator; - } - } - internal static void DivideCore(ReadOnlySpan dividends, double divisor, Span destination) - where T : struct, IMensuraUnit, IEquatable - { - if (dividends.IsEmpty) return; - - int len = dividends.Length; - ReadOnlySpan srcDouble = MemoryMarshal.Cast(dividends); - Span dstDouble = MemoryMarshal.Cast(destination); - - // Вместо деления в цикле, умножаем на обратное число (invDivisor) - double invDivisor = 1.0 / divisor; - var vectorizedInvDivisor = new Vector(invDivisor); - - int vectorSize = Vector.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>(ref currentSrc); - var multiplied = vector * vectorizedInvDivisor; - - Unsafe.As>(ref currentDst) = multiplied; - } - - // Хвост - for (; i < len; i++) - { - Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) * invDivisor; - } - } - internal static void DivideCore(double dividend, ReadOnlySpan divisors, Span destination) - where T : struct, IMensuraUnit, IEquatable - { - if (divisors.IsEmpty) return; - - int len = divisors.Length; - ReadOnlySpan srcDouble = MemoryMarshal.Cast(divisors); + ReadOnlySpan srcDouble = MemoryMarshal.Cast(units); Span dstDouble = MemoryMarshal.Cast(destination); var vectorizedDividend = new Vector(dividend); @@ -1165,13 +1095,15 @@ public static partial class Extensions Unsafe.Add(ref dstRef, i) = dividend / Unsafe.Add(ref srcRef, i); } } - internal static void PlusCore(ReadOnlySpan summands, double summand, Span destination) + internal static void Plus(ReadOnlySpan units, double summand, Span destination) where T : struct, IMensuraUnit, IEquatable { - if (summands.IsEmpty) return; + if (units.IsEmpty) return; + int len = units.Length; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); - int len = summands.Length; - ReadOnlySpan srcDouble = MemoryMarshal.Cast(summands); + ReadOnlySpan srcDouble = MemoryMarshal.Cast(units); Span dstDouble = MemoryMarshal.Cast(destination); var vectorizedSummand = new Vector(summand); @@ -1200,13 +1132,15 @@ public static partial class Extensions Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) + summand; } } - internal static void MinusCore(ReadOnlySpan minuends, double subtrahend, Span destination) + internal static void Minus(ReadOnlySpan units, double subtrahend, Span destination) where T : struct, IMensuraUnit, IEquatable { - if (minuends.IsEmpty) return; + if (units.IsEmpty) return; + int len = units.Length; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); - int len = minuends.Length; - ReadOnlySpan srcDouble = MemoryMarshal.Cast(minuends); + ReadOnlySpan srcDouble = MemoryMarshal.Cast(units); Span dstDouble = MemoryMarshal.Cast(destination); var vectorizedSubtrahend = new Vector(subtrahend); @@ -1235,13 +1169,15 @@ public static partial class Extensions Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) - subtrahend; } } - internal static void MinusCore(double minuend, ReadOnlySpan subtrahends, Span destination) + internal static void Minus(double minuend, ReadOnlySpan units, Span destination) where T : struct, IMensuraUnit, IEquatable { - if (subtrahends.IsEmpty) return; + if (units.IsEmpty) return; + int len = units.Length; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); - int len = subtrahends.Length; - ReadOnlySpan srcDouble = MemoryMarshal.Cast(subtrahends); + ReadOnlySpan srcDouble = MemoryMarshal.Cast(units); Span dstDouble = MemoryMarshal.Cast(destination); var vectorizedMinuend = new Vector(minuend); @@ -1273,14 +1209,17 @@ public static partial class Extensions } } - internal static void PowCore(ReadOnlySpan source, int power, Span destination) + internal static void Pow(ReadOnlySpan units, int power, Span destination) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { - if (source.IsEmpty) return; + if (units.IsEmpty) return; + int len = units.Length; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); - int len = source.Length; - ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); - Span dstDouble = MemoryMarshal.Cast(destination); + ReadOnlySpan srcDouble = MemoryMarshal.Cast(units); + Span dstDouble = MemoryMarshal.Cast(destination); int vectorSize = Vector.Count; int i = 0; @@ -1354,95 +1293,17 @@ public static partial class Extensions Unsafe.Add(ref dstRef, i) = power < 0 ? 1.0 / result : result; } } - internal static void PowCore(ReadOnlySpan source, double power, Span destination) + internal static void Sqrt(ReadOnlySpan units, Span destination) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { - if (source.IsEmpty) return; + if (units.IsEmpty) return; + int len = units.Length; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); - int len = source.Length; - ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); - Span dstDouble = MemoryMarshal.Cast(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>(ref currentSrc); - - // Аппаратный корень через инструкцию vsqrtpd - var sqrtV = Avx.Sqrt(v); - - // Совместимый с .NET 6 способ записи вектора в ref double - ref double currentDst = ref Unsafe.Add(ref dstRef, i); - Unsafe.As>(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(ReadOnlySpan source, Span destination) - where T : struct, IMensuraUnit, IEquatable - { - if (source.IsEmpty) return; - - int len = source.Length; - ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); - Span dstDouble = MemoryMarshal.Cast(destination); + ReadOnlySpan srcDouble = MemoryMarshal.Cast(units); + Span dstDouble = MemoryMarshal.Cast(destination); int i = 0; @@ -1510,7 +1371,7 @@ public static partial class Extensions if (len == 0) return []; var result = new T[len]; - MultiplyCore(units, multiplicator, result); + Multiply(units, multiplicator, result); return result; } public static T?[] Multiply(this T?[] units, double multiplicator) @@ -1549,16 +1410,46 @@ public static partial class Extensions public static void Multiply(this ReadOnlySpan units, double multiplicator, Span destination) where T : struct, IMensuraUnit, IEquatable { - if (units.Length > destination.Length) + if (units.IsEmpty) return; + int len = units.Length; + if (len > destination.Length) throw new ArgumentException("Целевой буфер destination меньше исходного source."); - Extensions.MultiplyCore(units, multiplicator, destination); + + ReadOnlySpan srcDouble = MemoryMarshal.Cast(units); + Span dstDouble = MemoryMarshal.Cast(destination); + + var vectorizedMultiplicator = new Vector(multiplicator); + int vectorSize = Vector.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>(ref currentSrc); + var multiplied = vector * vectorizedMultiplicator; + + Unsafe.As>(ref currentDst) = multiplied; + } + + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) * multiplicator; + } } public static void Multiply(this ReadOnlySpan units, double multiplicator, Span destination) where T : struct, IMensuraUnit, IEquatable { if (units.IsEmpty) return; - int len = units.Length; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); // Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов ref var srcRef = ref MemoryMarshal.GetReference(units); @@ -1616,7 +1507,7 @@ public static partial class Extensions var result = new List(len); result.SetCountUnsafe(len); - Extensions.MultiplyCore(CollectionsMarshal.AsSpan(units), multiplicator, CollectionsMarshal.AsSpan(result)); + Extensions.Multiply(CollectionsMarshal.AsSpan(units), multiplicator, CollectionsMarshal.AsSpan(result)); return result; } public static List Multiply(this List units, double multiplicator) @@ -1663,7 +1554,7 @@ public static partial class Extensions try { units.CopyTo(sharedArray, 0); - MultiplyCore(new ReadOnlySpan(sharedArray, 0, count), multiplicator, finalResult.AsSpan()); + Multiply(new ReadOnlySpan(sharedArray, 0, count), multiplicator, finalResult.AsSpan()); return finalResult; } finally @@ -1717,8 +1608,9 @@ public static partial class Extensions var finalResult = new T[count]; try { - units.CopyTo(sharedArray, 0); - MultiplyCore(new ReadOnlySpan(sharedArray, 0, count), multiplicator, finalResult.AsSpan()); + int index = 0; + foreach (var item in units) sharedArray[index++] = item; + Multiply(sharedArray, multiplicator, finalResult.AsSpan()); return finalResult; } finally @@ -1735,13 +1627,21 @@ public static partial class Extensions if (units is T?[] array) return array.Multiply(multiplicator); if (units is List list) return list.Multiply(multiplicator); - var resultArray = new T?[count]; T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + var resultArray = new T?[count]; try { - units.CopyTo(sharedNullableArray, 0); - var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); - srcSpan.Multiply(multiplicator, resultArray.AsSpan()); + int index = 0; + foreach (var item in units) sharedNullableArray[index++] = item; + for (int i = 0; i < count; i++) + { + T? item = sharedNullableArray[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = (item.Value.ToDouble() * multiplicator).ToUnit(); + } + } return resultArray; } finally @@ -1818,7 +1718,7 @@ public static partial class Extensions if (len == 0) return []; var result = new T[len]; - DivideCore(units, divisor, result); + Divide(units, divisor, result); return result; } public static T?[] Divide(this T?[] units, double divisor) @@ -1853,7 +1753,7 @@ public static partial class Extensions if (len == 0) return []; var result = new T[len]; - DivideCore(dividend, units, result); + Divide(dividend, units, result); return result; } public static T?[] Divide(this double dividend, T?[] units) @@ -1881,16 +1781,52 @@ public static partial class Extensions public static void Divide(this ReadOnlySpan units, double divisor, Span destination) where T : struct, IMensuraUnit, IEquatable { - if (units.Length > destination.Length) + if (units.IsEmpty) return; + int len = units.Length; + if (len > destination.Length) throw new ArgumentException("Целевой буфер destination меньше исходного source."); - Extensions.DivideCore(units, divisor, destination); + + ReadOnlySpan srcDouble = MemoryMarshal.Cast(units); + Span dstDouble = MemoryMarshal.Cast(destination); + + // Вместо деления в цикле, умножаем на обратное число (invDivisor) + double invDivisor = 1.0 / divisor; + var vectorizedInvDivisor = new Vector(invDivisor); + + int vectorSize = Vector.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>(ref currentSrc); + var multiplied = vector * vectorizedInvDivisor; + + Unsafe.As>(ref currentDst) = multiplied; + } + + // Хвост + for (; i < len; i++) + { + Unsafe.Add(ref dstRef, i) = Unsafe.Add(ref srcRef, i) * invDivisor; + } } public static void Divide(this ReadOnlySpan units, double divisor, Span destination) where T : struct, IMensuraUnit, IEquatable { if (units.IsEmpty) return; - int len = units.Length; + if (len > destination.Length) + throw new ArgumentException("Целевой буфер destination меньше исходного source."); + // Получаем прямые неуправляемые ref-ссылки на начало буферов за 0 тактов ref var srcRef = ref MemoryMarshal.GetReference(units); @@ -1935,7 +1871,7 @@ public static partial class Extensions { if (units.Length > destination.Length) throw new ArgumentException("Целевой буфер destination меньше исходного source."); - Extensions.DivideCore(dividend, units, destination); + Extensions.Divide(dividend, units, destination); } public static void Divide(this double dividend, ReadOnlySpan units, Span destination) where T : struct, IMensuraUnit, IEquatable @@ -1992,7 +1928,7 @@ public static partial class Extensions var result = new List(len); result.SetCountUnsafe(len); - Extensions.DivideCore(CollectionsMarshal.AsSpan(units), divisor, CollectionsMarshal.AsSpan(result)); + Extensions.Divide(CollectionsMarshal.AsSpan(units), divisor, CollectionsMarshal.AsSpan(result)); return result; } public static List Divide(this List units, double divisor) @@ -2025,7 +1961,7 @@ public static partial class Extensions var result = new List(len); result.SetCountUnsafe(len); - Extensions.DivideCore(dividend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); + Extensions.Divide(dividend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); return result; } public static List Divide(this double dividend, List units) @@ -2064,7 +2000,7 @@ public static partial class Extensions try { units.CopyTo(sharedArray, 0); - DivideCore(new ReadOnlySpan(sharedArray, 0, count), divisor, finalResult.AsSpan()); + Divide(new ReadOnlySpan(sharedArray, 0, count), divisor, finalResult.AsSpan()); return finalResult; } finally @@ -2109,7 +2045,7 @@ public static partial class Extensions try { units.CopyTo(sharedArray, 0); - DivideCore(dividend, new ReadOnlySpan(sharedArray, 0, count), finalResult.AsSpan()); + Divide(dividend, new ReadOnlySpan(sharedArray, 0, count), finalResult.AsSpan()); return finalResult; } finally @@ -2148,15 +2084,16 @@ public static partial class Extensions 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 list) return list.Divide(divisor); + if (units is T[] array) return array.Multiply(divisor); + if (units is List list) return list.Multiply(divisor); T[] sharedArray = ArrayPool.Shared.Rent(count); var finalResult = new T[count]; try { - units.CopyTo(sharedArray, 0); - DivideCore(new ReadOnlySpan(sharedArray, 0, count), divisor, finalResult.AsSpan()); + int index = 0; + foreach (var item in units) sharedArray[index++] = item; + Divide(sharedArray, divisor, finalResult.AsSpan()); return finalResult; } finally @@ -2170,16 +2107,25 @@ public static partial class Extensions 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 list) return list.Divide(divisor); + if (units is T?[] array) return array.Multiply(divisor); + if (units is List list) return list.Multiply(divisor); - var resultArray = new T?[count]; T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + var resultArray = new T?[count]; + double invDivisor = 1.0 / divisor; try { - units.CopyTo(sharedNullableArray, 0); - var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); - srcSpan.Divide(divisor, resultArray.AsSpan()); + int index = 0; + foreach (var item in units) sharedNullableArray[index++] = item; + for (int i = 0; i < count; i++) + { + T? item = sharedNullableArray[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = (item.Value.ToDouble() / invDivisor).ToUnit(); + } + } return resultArray; } finally @@ -2193,15 +2139,16 @@ public static partial class Extensions 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 list) return dividend.Divide(list); + if (units is T[] array) return array.Multiply(dividend); + if (units is List list) return list.Multiply(dividend); T[] sharedArray = ArrayPool.Shared.Rent(count); var finalResult = new T[count]; try { - units.CopyTo(sharedArray, 0); - DivideCore(dividend, new ReadOnlySpan(sharedArray, 0, count), finalResult.AsSpan()); + int index = 0; + foreach (var item in units) sharedArray[index++] = item; + Divide(dividend, sharedArray, finalResult.AsSpan()); return finalResult; } finally @@ -2215,16 +2162,24 @@ public static partial class Extensions 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 list) return dividend.Divide(list); + if (units is T?[] array) return array.Multiply(dividend); + if (units is List list) return list.Multiply(dividend); - var resultArray = new T?[count]; T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + var resultArray = new T?[count]; try { - units.CopyTo(sharedNullableArray, 0); - var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); - dividend.Divide(srcSpan, resultArray.AsSpan()); + int index = 0; + foreach (var item in units) sharedNullableArray[index++] = item; + for (int i = 0; i < count; i++) + { + T? item = sharedNullableArray[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = (dividend / item.Value.ToDouble()).ToUnit(); + } + } return resultArray; } finally @@ -2320,7 +2275,7 @@ public static partial class Extensions if (len == 0) return []; var result = new T[len]; - PlusCore(units, summand, result); + Plus(units, summand, result); return result; } public static T?[] Plus(this T?[] units, double summand) @@ -2361,7 +2316,7 @@ public static partial class Extensions { if (units.Length > destination.Length) throw new ArgumentException("Целевой буфер destination меньше исходного source."); - Extensions.PlusCore(units, summand, destination); + Extensions.Plus(units, summand, destination); } public static void Plus(this ReadOnlySpan units, double summand, Span destination) where T : struct, IMensuraUnit, IEquatable @@ -2426,7 +2381,7 @@ public static partial class Extensions var result = new List(len); result.SetCountUnsafe(len); - Extensions.PlusCore(CollectionsMarshal.AsSpan(units), summand, CollectionsMarshal.AsSpan(result)); + Extensions.Plus(CollectionsMarshal.AsSpan(units), summand, CollectionsMarshal.AsSpan(result)); return result; } public static List Plus(this List units, double summand) @@ -2473,7 +2428,7 @@ public static partial class Extensions try { units.CopyTo(sharedArray, 0); - PlusCore(new ReadOnlySpan(sharedArray, 0, count), summand, finalResult.AsSpan()); + Plus(new ReadOnlySpan(sharedArray, 0, count), summand, finalResult.AsSpan()); return finalResult; } finally @@ -2527,8 +2482,9 @@ public static partial class Extensions var finalResult = new T[count]; try { - units.CopyTo(sharedArray, 0); - PlusCore(new ReadOnlySpan(sharedArray, 0, count), summand, finalResult.AsSpan()); + int index = 0; + foreach (var item in units) sharedArray[index++] = item; + Plus(sharedArray, summand, finalResult.AsSpan()); return finalResult; } finally @@ -2545,13 +2501,21 @@ public static partial class Extensions if (units is T?[] array) return array.Plus(summand); if (units is List list) return list.Plus(summand); - var resultArray = new T?[count]; T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + var resultArray = new T?[count]; try { - units.CopyTo(sharedNullableArray, 0); - var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); - srcSpan.Plus(summand, resultArray.AsSpan()); + int index = 0; + foreach (var item in units) sharedNullableArray[index++] = item; + for (int i = 0; i < count; i++) + { + T? item = sharedNullableArray[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = (item.Value.ToDouble() + summand).ToUnit(); + } + } return resultArray; } finally @@ -2628,7 +2592,7 @@ public static partial class Extensions if (len == 0) return []; var result = new T[len]; - MinusCore(units, subtrahend, result); + Minus(units, subtrahend, result); return result; } public static T?[] Minus(this T?[] units, double subtrahend) @@ -2661,7 +2625,7 @@ public static partial class Extensions if (len == 0) return []; var result = new T[len]; - MinusCore(minuend, units, result); + Minus(minuend, units, result); return result; } public static T?[] Minus(this double minuend, T?[] units) @@ -2691,7 +2655,7 @@ public static partial class Extensions { if (units.Length > destination.Length) throw new ArgumentException("Целевой буфер destination меньше исходного source."); - Extensions.MinusCore(units, subtrahend, destination); + Extensions.Minus(units, subtrahend, destination); } public static void Minus(this ReadOnlySpan units, double subtrahend, Span destination) where T : struct, IMensuraUnit, IEquatable @@ -2742,7 +2706,7 @@ public static partial class Extensions { if (units.Length > destination.Length) throw new ArgumentException("Целевой буфер destination меньше исходного source."); - Extensions.MinusCore(minuend, units, destination); + Extensions.Minus(minuend, units, destination); } public static void Minus(this double minuend, ReadOnlySpan units, Span destination) where T : struct, IMensuraUnit, IEquatable @@ -2799,7 +2763,7 @@ public static partial class Extensions var result = new List(len); result.SetCountUnsafe(len); - Extensions.MinusCore(CollectionsMarshal.AsSpan(units), subtrahend, CollectionsMarshal.AsSpan(result)); + Extensions.Minus(CollectionsMarshal.AsSpan(units), subtrahend, CollectionsMarshal.AsSpan(result)); return result; } public static List Minus(this List units, double subtrahend) @@ -2831,7 +2795,7 @@ public static partial class Extensions var result = new List(len); result.SetCountUnsafe(len); - Extensions.MinusCore(minuend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); + Extensions.Minus(minuend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); return result; } public static List Minus(this double minuend, List units) @@ -2870,7 +2834,7 @@ public static partial class Extensions try { units.CopyTo(sharedArray, 0); - MinusCore(new ReadOnlySpan(sharedArray, 0, count), subtrahend, finalResult.AsSpan()); + Minus(new ReadOnlySpan(sharedArray, 0, count), subtrahend, finalResult.AsSpan()); return finalResult; } finally @@ -2915,7 +2879,7 @@ public static partial class Extensions try { units.CopyTo(sharedArray, 0); - MinusCore(minuend, new ReadOnlySpan(sharedArray, 0, count), finalResult.AsSpan()); + Minus(minuend, new ReadOnlySpan(sharedArray, 0, count), finalResult.AsSpan()); return finalResult; } finally @@ -2954,15 +2918,16 @@ public static partial class Extensions 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 list) return list.Minus(subtrahend); + if (units is T[] array) return array.Multiply(subtrahend); + if (units is List list) return list.Multiply(subtrahend); T[] sharedArray = ArrayPool.Shared.Rent(count); var finalResult = new T[count]; try { - units.CopyTo(sharedArray, 0); - MinusCore(new ReadOnlySpan(sharedArray, 0, count), subtrahend, finalResult.AsSpan()); + int index = 0; + foreach (var item in units) sharedArray[index++] = item; + Minus(sharedArray, subtrahend, finalResult.AsSpan()); return finalResult; } finally @@ -2976,16 +2941,24 @@ public static partial class Extensions 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 list) return list.Minus(subtrahend); + if (units is T?[] array) return array.Multiply(subtrahend); + if (units is List list) return list.Multiply(subtrahend); - var resultArray = new T?[count]; T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + var resultArray = new T?[count]; try { - units.CopyTo(sharedNullableArray, 0); - var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); - srcSpan.Minus(subtrahend, resultArray.AsSpan()); + int index = 0; + foreach (var item in units) sharedNullableArray[index++] = item; + for (int i = 0; i < count; i++) + { + T? item = sharedNullableArray[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = (item.Value.ToDouble() - subtrahend).ToUnit(); + } + } return resultArray; } finally @@ -2999,15 +2972,16 @@ public static partial class Extensions 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 list) return minuend.Minus(list); + if (units is T[] array) return array.Multiply(minuend); + if (units is List list) return list.Multiply(minuend); T[] sharedArray = ArrayPool.Shared.Rent(count); var finalResult = new T[count]; try { - units.CopyTo(sharedArray, 0); - MinusCore(minuend, new ReadOnlySpan(sharedArray, 0, count), finalResult.AsSpan()); + int index = 0; + foreach (var item in units) sharedArray[index++] = item; + Minus(minuend, sharedArray, finalResult.AsSpan()); return finalResult; } finally @@ -3021,16 +2995,24 @@ public static partial class Extensions 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 list) return minuend.Minus(list); + if (units is T?[] array) return array.Multiply(minuend); + if (units is List list) return list.Multiply(minuend); - var resultArray = new T?[count]; T?[] sharedNullableArray = ArrayPool.Shared.Rent(count); + var resultArray = new T?[count]; try { - units.CopyTo(sharedNullableArray, 0); - var srcSpan = new ReadOnlySpan(sharedNullableArray, 0, count); - minuend.Minus(srcSpan, resultArray.AsSpan()); + int index = 0; + foreach (var item in units) sharedNullableArray[index++] = item; + for (int i = 0; i < count; i++) + { + T? item = sharedNullableArray[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = (minuend - item.Value.ToDouble()).ToUnit(); + } + } return resultArray; } finally @@ -3116,175 +3098,609 @@ public static partial class Extensions // === ЦЕЛАЯ СТЕПЕНЬ === // ========================================== - public static T[] Pow(this T[] units, int power) + internal static R[] Pow(this T[] units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { if (units is null) return null!; int len = units.Length; if (len == 0) return []; - var result = new T[len]; - Extensions.PowCore(units, power, result); + var result = new R[len]; + Extensions.Pow(units, power, result); return result; } - public static T?[] Pow(this T?[] units, int power) + internal static R?[] Pow(this T?[] units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { if (units is null) return null!; int len = units.Length; if (len == 0) return []; - var result = new T?[len]; + var result = new R?[len]; for (int i = 0; i < len; i++) { ref var item = ref result[i]; if (item.HasValue) - item = item.Value.ToDouble().QuickPow(power).ToUnit(); + item = item.Value.ToDouble().QuickPow(power).ToUnit(); } return result; } // === ReadOnlySpan === - public static Span Pow(this ReadOnlySpan units, int power) + internal static Span Pow(this ReadOnlySpan units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { if (units.Length == 0) return []; - var result = new T[units.Length]; - Extensions.PowCore(units, power, result); + var result = new R[units.Length]; + Extensions.Pow(units, power, result); return result; } - public static Span Pow(this ReadOnlySpan units, int power) + internal static Span Pow(this ReadOnlySpan units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { int len = units.Length; if (len == 0) return []; - var result = new T?[len]; - units.CopyTo(result); + var res = new T?[len]; + units.CopyTo(res); + var result = Unsafe.As(ref res); for (int i = 0; i < len; i++) { ref var item = ref result[i]; - if (item.HasValue) item = item.Value.ToDouble().QuickPow(power).ToUnit(); + if (item.HasValue) item = item.Value.ToDouble().QuickPow(power).ToUnit(); } return result; } // === List === - public static List Pow(this List units, int power) + internal static List Pow(this List units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { if (units is null) return null!; - int len = units.Count; - if (len == 0) return []; + int count = units.Count; + if (count == 0) return []; - var result = new List(len); - result.SetCountUnsafe(len); - Extensions.PowCore(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result)); + var result = new List(count); + result.SetCountUnsafe(count); + Extensions.Pow(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result)); return result; } - public static List Pow(this List units, int power) + internal static List Pow(this List units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { if (units is null) return null!; int len = units.Count; if (len == 0) return []; - var result = new List(units); - var dstSpan = CollectionsMarshal.AsSpan(result); + var resultArray = new R?[len]; + var srcSpan = CollectionsMarshal.AsSpan(units); for (int i = 0; i < len; i++) { - ref var item = ref dstSpan[i]; - if (item.HasValue) item = item.Value.ToDouble().QuickPow(power).ToUnit(); + T? item = srcSpan[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = item.Value.ToDouble().QuickPow(power).ToUnit(); + } } - return result; + return [.. resultArray]; } // === ICollection === - public static ICollection Pow(this ICollection units, int power) + internal static ICollection Pow(this ICollection units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { if (units is null) return null!; - if (units is T[] array) return array.Pow(power); - if (units is List list) return list.Pow(power); + if (units is T[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); - ICollection result = []; + ICollection result = []; foreach (var item in units) - result.Add(item.ToDouble().QuickPow(power).ToUnit()); + result.Add(item.ToDouble().QuickPow(power).ToUnit()); return result; } - public static ICollection Pow(this ICollection units, int power) + internal static ICollection Pow(this ICollection units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { if (units is null) return null!; - if (units is T?[] array) return array.Pow(power); - if (units is List list) return list.Pow(power); + if (units is T?[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); - ICollection result = []; + ICollection result = []; foreach (var item in units) - result.Add((item.HasValue ? item.Value.ToDouble().QuickPow(power) : 0d).ToUnit()); + result.Add((item.HasValue ? item.Value.ToDouble().QuickPow(power) : 0d).ToUnit()); return result; } // === IReadOnlyCollection === - public static IReadOnlyCollection Pow(this IReadOnlyCollection units, int power) + internal static IReadOnlyCollection Pow(this IReadOnlyCollection units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { if (units is null) return null!; - if (units is T[] array) return array.Pow(power); - if (units is List list) return list.Pow(power); + int count = units.Count; + if (count == 0) return []; + if (units is T[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); - IReadOnlyCollection result = []; - foreach (var item in units) - result.Add(item.ToDouble().QuickPow(power).ToUnit()); - return result; + T[] sharedArray = ArrayPool.Shared.Rent(count); + var finalResult = new R[count]; + + try + { + int index = 0; + foreach (var item in units) sharedArray[index++] = item; + Pow(sharedArray, power, finalResult); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } } - public static IReadOnlyCollection Pow(this IReadOnlyCollection units, int power) + internal static IReadOnlyCollection Pow(this IReadOnlyCollection units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { if (units is null) return null!; - if (units is T?[] array) return array.Pow(power); - if (units is List list) return list.Pow(power); + int count = units.Count; + if (count == 0) return []; + if (units is T?[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + + // Сразу выделяем итоговый массив. Никаких ArrayPool и try-finally! + var result = new R?[units.Count]; + int i = 0; + int absPower = Math.Abs(power); + + foreach (T? item in units) + { + if (item.HasValue) + { + double @base = item.Value.ToDouble(); + double res = 1.0; + int currentPower = absPower; + + // Быстрое бинарное возведение в степень на лету + while (currentPower > 0) + { + if ((currentPower & 1) == 1) res *= @base; + @base *= @base; + currentPower >>= 1; + } + + // Прямая ref-запись в итоговый массив за 1 проход + ref var dst = ref result[i]; + dst = (power < 0 ? 1.0 / res : res).ToUnit(); + } + i++; + } - IReadOnlyCollection result = []; - foreach (var item in units) - result.Add((item.HasValue ? item.Value.ToDouble().QuickPow(power) : 0d).ToUnit()); return result; } - // === IEnumerable + yeild === - static IEnumerable PowIterator(IEnumerable units, int power) + // === IEnumerable + yeild === + static IEnumerable PowIterator(IEnumerable units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { foreach (var item in units) - yield return (item.ToDouble().QuickPow(power)).ToUnit(); + yield return (item.ToDouble().QuickPow(power)).ToUnit(); } - static IEnumerable PowNullableIterator(IEnumerable units, int power) + static IEnumerable PowNullableIterator(IEnumerable units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { foreach (var item in units) yield return item.HasValue - ? item.Value.ToDouble().QuickPow(power).ToUnit() : null; + ? item.Value.ToDouble().QuickPow(power).ToUnit() : null; } // === IEnumerable === - public static IEnumerable Pow(this IEnumerable units, int power) + internal static IEnumerable Pow(this IEnumerable units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { if (units is null) return null!; - if (units is T[] array) return array.Pow(power); - if (units is List list) return list.Pow(power); - if (units is ICollection col) return col.Pow(power); - if (units is IReadOnlyCollection roc) return roc.Pow(power); - else return PowIterator(units, power); + if (units is T[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + if (units is ICollection col) return col.Pow(power); + if (units is IReadOnlyCollection roc) return roc.Pow(power); + else return PowIterator(units, power); } - public static IEnumerable Pow(this IEnumerable units, int power) + internal static IEnumerable Pow(this IEnumerable units, int power) where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable { if (units is null) return null!; - if (units is T?[] array) return array.Pow(power); - if (units is List list) return list.Pow(power); - if (units is ICollection col) return col.Pow(power); - if (units is IReadOnlyCollection roc) return roc.Pow(power); - else return PowNullableIterator(units, power); + if (units is T?[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + if (units is ICollection col) return col.Pow(power); + if (units is IReadOnlyCollection roc) return roc.Pow(power); + else return PowNullableIterator(units, power); + } + + + + + // ========================================== + // === ДРОБНАЯ СТЕПЕНЬ === + // ========================================== + + internal static R[] Pow(this T[] units, double power) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Length; + if (len == 0) return []; + + var finalResult = new R[len]; + if (power != 0.5) + { + for (int i = 0; i < len; i++) + { + ref var dst = ref finalResult[i]; + dst = Math.Pow(units[i].ToDouble(), power).ToUnit(); + } + } + else Sqrt(MemoryMarshal.Cast(units), finalResult); + return finalResult; + } + internal static R?[] Pow(this T?[] units, double power) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units.Length == 0) return []; + + var result = (R?[])units.Clone(); + int len = result.Length; + for (int i = 0; i < len; i++) + { + ref var item = ref result[i]; + if (item.HasValue) + { + item = Math.Pow(item.Value.ToDouble(), power).ToUnit(); + } + } + return result; + } + + // === ReadOnlySpan === + internal static void Pow(this ReadOnlySpan source, double power, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (source.IsEmpty) return; + + int len = source.Length; + ReadOnlySpan srcDouble = MemoryMarshal.Cast(source); + Span dstDouble = MemoryMarshal.Cast(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>(ref currentSrc); + + // Аппаратный корень через инструкцию vsqrtpd + var sqrtV = Avx.Sqrt(v); + + // Совместимый с .NET 6 способ записи вектора в ref double + ref double currentDst = ref Unsafe.Add(ref dstRef, i); + Unsafe.As>(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 Pow(this ReadOnlySpan units, double power, Span destination) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + int len = units.Length; + if (len == 0) return; + + for (int i = 0; i < len; i++) + { + T? item = units[i]; + if (item.HasValue) + { + // Прямая ref-запись в переданный снаружи destination + ref var dst = ref destination[i]; + dst = Math.Pow(item.Value.ToDouble(), power).ToUnit(); + } + } + } + + // === List === + internal static List Pow(this List units, double power) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var resultArray = new R[len]; + var srcSpan = CollectionsMarshal.AsSpan(units); + if (power == 0.5) + { + ReadOnlySpan srcAsR = MemoryMarshal.Cast(srcSpan); + Sqrt(srcAsR, resultArray.AsSpan()); + } + else + { + Span dstSpan = resultArray.AsSpan(); + for (int i = 0; i < len; i++) + { + ref var dst = ref dstSpan[i]; + dst = Math.Pow(srcSpan[i].ToDouble(), power).ToUnit(); + } + } + return [.. resultArray]; + } + internal static List Pow(this List units, double power) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + + var resultArray = new R?[len]; + var srcSpan = CollectionsMarshal.AsSpan(units); + for (int i = 0; i < len; i++) + { + T? item = srcSpan[i]; + if (item.HasValue) + { + ref var dst = ref resultArray[i]; + dst = Math.Pow(item.Value.ToDouble(), power).ToUnit(); + } + } + return [.. resultArray]; + } + + // === ICollection === + internal static ICollection Pow(this ICollection units, double power) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + if (units is T[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + + T[] sharedArray = ArrayPool.Shared.Rent(len); + var finalResult = new R[len]; + try + { + units.CopyTo(sharedArray, 0); + if (power == 0.5) + { + Sqrt(MemoryMarshal.Cast(sharedArray), finalResult); + } + else + { + for (int i = 0; i < len; i++) + finalResult[i] = Math.Pow(sharedArray[i].ToDouble(), power).ToUnit(); + } + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + internal static ICollection Pow(this ICollection units, double power) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + if (units is T?[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + + T?[] sharedNullableArray = ArrayPool.Shared.Rent(len); + var finalResult = new R?[len]; + try + { + units.CopyTo(sharedNullableArray, 0); + for (int i = 0; i < len; i++) + { + T? item = sharedNullableArray[i]; + finalResult[i] = item.HasValue ? Math.Pow(item.Value.ToDouble(), power).ToUnit() : default; + } + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + + // === IReadOnlyCollection === + internal static IReadOnlyCollection Pow(this IReadOnlyCollection units, double power) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + if (units is T[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + + var finalResult = new R[len]; + var sharedArray = ArrayPool.Shared.Rent(len); + try + { + if (units is ICollection collection) + { + collection.CopyTo(sharedArray, 0); + } + else + { + int idx = 0; + foreach (var item in units) sharedArray[idx++] = item; + } + + if (power != 0.5) + { + for (int i = 0; i < len; i++) + finalResult[i] = Math.Pow(sharedArray[i].ToDouble(), power).ToUnit(); + } + else Sqrt(MemoryMarshal.Cast(sharedArray), finalResult); + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedArray); + } + } + internal static IReadOnlyCollection Pow(this IReadOnlyCollection units, double power) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + int len = units.Count; + if (len == 0) return []; + if (units is T?[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + + var finalResult = new R?[len]; + T?[] sharedNullableArray = ArrayPool.Shared.Rent(len); + + try + { + if (units is ICollection collection) + { + collection.CopyTo(sharedNullableArray, 0); + } + else + { + int idx = 0; + foreach (T? item in units) sharedNullableArray[idx++] = item; + } + + for (int i = 0; i < len; i++) + { + T? item = sharedNullableArray[i]; + finalResult[i] = item.HasValue ? Math.Pow(item.Value.ToDouble(), power).ToUnit() : default; + } + + return finalResult; + } + finally + { + ArrayPool.Shared.Return(sharedNullableArray); + } + } + + // === IEnumerable + yield === + internal static IEnumerable PowIterator(this IEnumerable units, double power) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return Math.Pow(item.ToDouble(), power).ToUnit(); + } + internal static IEnumerable PowNullableIterator(this IEnumerable units, double power) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + foreach (var item in units) + yield return item.HasValue + ? Math.Pow(item.Value.ToDouble(), power).ToUnit() : null; + } + + // === IEnumerable === + internal static IEnumerable Pow(this IEnumerable units, double power) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + if (units is ICollection col) return col.Pow(power); + if (units is IReadOnlyCollection roc) return roc.Pow(power); + else return PowIterator(units, power); + } + internal static IEnumerable Pow(this IEnumerable units, double power) + where T : struct, IMensuraUnit, IEquatable + where R : struct, IMensuraUnit, IEquatable + { + if (units is null) return null!; + if (units is T?[] array) return array.Pow(power); + if (units is List list) return list.Pow(power); + if (units is ICollection col) return col.Pow(power); + if (units is IReadOnlyCollection roc) return roc.Pow(power); + else return PowNullableIterator(units, power); } diff --git a/QWERTYkez.Mensura/Units/XXXXXXXX.Gen.cs b/QWERTYkez.Mensura/Units/XXXXXXXX.Gen.cs index 485de93..52077f0 100644 --- a/QWERTYkez.Mensura/Units/XXXXXXXX.Gen.cs +++ b/QWERTYkez.Mensura/Units/XXXXXXXX.Gen.cs @@ -229,7 +229,7 @@ public static class XXXXXXXXXXXXXXExtension if (units.Length == 0) return []; var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.MultiplyCore(units, multiplicator, result); + Extensions.Multiply(units, multiplicator, result); return result; } public static XXXXXXXXXXXXXX[] Multiply(this double multiplicator, XXXXXXXXXXXXXX[] units) @@ -256,7 +256,7 @@ public static class XXXXXXXXXXXXXXExtension { if (units.Length == 0) return []; Span result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.MultiplyCore(units, multiplicator, result); + Extensions.Multiply(units, multiplicator, result); return result; } public static Span Multiply(this double multiplicator, ReadOnlySpan units) @@ -287,7 +287,7 @@ public static class XXXXXXXXXXXXXXExtension var result = new List(len); result.SetCountUnsafe(len); - Extensions.MultiplyCore(CollectionsMarshal.AsSpan(units), multiplicator, CollectionsMarshal.AsSpan(result)); + Extensions.Multiply(CollectionsMarshal.AsSpan(units), multiplicator, CollectionsMarshal.AsSpan(result)); return result; } public static List Multiply(this double multiplicator, List units) @@ -369,7 +369,7 @@ public static class XXXXXXXXXXXXXXExtension throw new ArgumentException("Делитель не может быть 0 или NaN.", nameof(divisor)); var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.DivideCore(units, divisor, result); + Extensions.Divide(units, divisor, result); return result; } public static XXXXXXXXXXXXXX[] Divide(this double dividend, XXXXXXXXXXXXXX[] units) @@ -378,7 +378,7 @@ public static class XXXXXXXXXXXXXXExtension if (units.Length == 0) return []; var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.DivideCore(dividend, units, result); // Вызов зеркального ядра (число / массив) + Extensions.Divide(dividend, units, result); // Вызов зеркального ядра (число / массив) return result; } public static XXXXXXXXXXXXXX?[] Divide(this XXXXXXXXXXXXXX?[] units, double divisor) @@ -417,14 +417,14 @@ public static class XXXXXXXXXXXXXXExtension if (units.Length == 0) return []; if (divisor == 0 || double.IsNaN(divisor)) throw new ArgumentException("Делитель не может быть 0 или NaN.", nameof(divisor)); var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.DivideCore(units, divisor, result); + Extensions.Divide(units, divisor, result); return result; } public static Span Divide(this double dividend, ReadOnlySpan units) { if (units.Length == 0) return []; var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.DivideCore(dividend, units, result); + Extensions.Divide(dividend, units, result); return result; } public static Span Divide(this ReadOnlySpan units, double divisor) @@ -469,7 +469,7 @@ public static class XXXXXXXXXXXXXXExtension var result = new List(len); result.SetCountUnsafe(len); - Extensions.DivideCore(CollectionsMarshal.AsSpan(units), divisor, CollectionsMarshal.AsSpan(result)); + Extensions.Divide(CollectionsMarshal.AsSpan(units), divisor, CollectionsMarshal.AsSpan(result)); return result; } public static List Divide(this double dividend, List units) @@ -480,7 +480,7 @@ public static class XXXXXXXXXXXXXXExtension var result = new List(len); result.SetCountUnsafe(len); - Extensions.DivideCore(dividend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); + Extensions.Divide(dividend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); return result; } public static List Divide(this List units, double divisor) @@ -599,7 +599,7 @@ public static class XXXXXXXXXXXXXXExtension if (units.Length == 0) return []; var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.PlusCore(units, summand, result); + Extensions.Plus(units, summand, result); return result; } public static XXXXXXXXXXXXXX[] Plus(this double summand, XXXXXXXXXXXXXX[] units) @@ -626,7 +626,7 @@ public static class XXXXXXXXXXXXXXExtension { if (units.Length == 0) return []; var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.PlusCore(units, summand, result); + Extensions.Plus(units, summand, result); return result; } public static Span Plus(this double summand, ReadOnlySpan units) @@ -657,7 +657,7 @@ public static class XXXXXXXXXXXXXXExtension var result = new List(len); result.SetCountUnsafe(len); - Extensions.PlusCore(CollectionsMarshal.AsSpan(units), summand, CollectionsMarshal.AsSpan(result)); + Extensions.Plus(CollectionsMarshal.AsSpan(units), summand, CollectionsMarshal.AsSpan(result)); return result; } public static List Plus(this double summand, List units) @@ -737,7 +737,7 @@ public static class XXXXXXXXXXXXXXExtension if (units.Length == 0) return []; var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.MinusCore(units, subtrahend, result); + Extensions.Minus(units, subtrahend, result); return result; } public static XXXXXXXXXXXXXX[] Minus(this double minuend, XXXXXXXXXXXXXX[] units) @@ -746,7 +746,7 @@ public static class XXXXXXXXXXXXXXExtension if (units.Length == 0) return []; var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.MinusCore(minuend, units, result); // Вызов зеркального ядра (число - массив) + Extensions.Minus(minuend, units, result); // Вызов зеркального ядра (число - массив) return result; } public static XXXXXXXXXXXXXX?[] Minus(this XXXXXXXXXXXXXX?[] units, double subtrahend) @@ -783,14 +783,14 @@ public static class XXXXXXXXXXXXXXExtension { if (units.Length == 0) return []; var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.MinusCore(units, subtrahend, result); + Extensions.Minus(units, subtrahend, result); return result; } public static Span Minus(this double minuend, ReadOnlySpan units) { if (units.Length == 0) return []; var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.MinusCore(minuend, units, result); + Extensions.Minus(minuend, units, result); return result; } public static Span Minus(this ReadOnlySpan units, double subtrahend) @@ -831,7 +831,7 @@ public static class XXXXXXXXXXXXXXExtension var result = new List(len); result.SetCountUnsafe(len); - Extensions.MinusCore(CollectionsMarshal.AsSpan(units), subtrahend, CollectionsMarshal.AsSpan(result)); + Extensions.Minus(CollectionsMarshal.AsSpan(units), subtrahend, CollectionsMarshal.AsSpan(result)); return result; } public static List Minus(this double minuend, List units) @@ -842,7 +842,7 @@ public static class XXXXXXXXXXXXXXExtension var result = new List(len); result.SetCountUnsafe(len); - Extensions.MinusCore(minuend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); + Extensions.Minus(minuend, CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); return result; } public static List Minus(this List units, double subtrahend) @@ -960,7 +960,7 @@ public static class XXXXXXXXXXXXXXExtension if (units.Length == 0) return []; var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.PowCore(units, power, result); + Extensions.Pow(units, power, result); return result; } public static XXXXXXXXXXXXXX?[] Pow(this XXXXXXXXXXXXXX?[] units, int power) @@ -987,7 +987,7 @@ public static class XXXXXXXXXXXXXXExtension { if (units.Length == 0) return []; var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.PowCore(units, power, result); + Extensions.Pow(units, power, result); return result; } public static Span Pow(this ReadOnlySpan units, int power) @@ -1013,7 +1013,7 @@ public static class XXXXXXXXXXXXXXExtension var result = new List(len); result.SetCountUnsafe(len); - Extensions.PowCore(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result)); + Extensions.Pow(CollectionsMarshal.AsSpan(units), power, CollectionsMarshal.AsSpan(result)); return result; } public static List Pow(this List units, int power) @@ -1202,7 +1202,7 @@ public static class XXXXXXXXXXXXXXExtension if (units.Length == 0) return []; var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.SqrtCore(units, result); + Extensions.Sqrt(units, result); return result; } public static XXXXXXXXXXXXXX?[] Sqrt(this XXXXXXXXXXXXXX?[] units) @@ -1228,7 +1228,7 @@ public static class XXXXXXXXXXXXXXExtension { if (units.Length == 0) return []; var result = new XXXXXXXXXXXXXX[units.Length]; - Extensions.SqrtCore(units, result); + Extensions.Sqrt(units, result); return result; } public static Span Sqrt(this ReadOnlySpan units) @@ -1254,7 +1254,7 @@ public static class XXXXXXXXXXXXXXExtension var result = new List(len); result.SetCountUnsafe(len); - Extensions.SqrtCore(CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); + Extensions.Sqrt(CollectionsMarshal.AsSpan(units), CollectionsMarshal.AsSpan(result)); return result; } public static List Sqrt(this List units)