namespace QWERTYkez.Mensura.Extensions; public static partial class CollectionsMultiplyExtensions { // === MultiplyCore === SIMD internal static void MultiplyCore(this ReadOnlySpan units, double multiplicator, int len, Span destination) where T : struct, IMensuraUnit, IEquatable where R : struct, IMensuraUnit, IEquatable { 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; } } internal static void MultiplyCore(this ReadOnlySpan units, double multiplicator, int len, Span destination) where T : struct, IMensuraUnit, IEquatable where R : struct, IMensuraUnit, IEquatable { // Получаем прямые неуправляемые 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() : null; d1 = u1.HasValue ? (u1.Value.ToDouble() * multiplicator).ToUnit() : null; d2 = u2.HasValue ? (u2.Value.ToDouble() * multiplicator).ToUnit() : null; d3 = u3.HasValue ? (u3.Value.ToDouble() * multiplicator).ToUnit() : 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() : null; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void MultiplyCore(this double multiplicator, ReadOnlySpan units, int len, Span destination) where T : struct, IMensuraUnit, IEquatable where R : struct, IMensuraUnit, IEquatable => units.MultiplyCore(multiplicator, len, destination); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void MultiplyCore(this double multiplicator, ReadOnlySpan units, int len, Span destination) where T : struct, IMensuraUnit, IEquatable where R : struct, IMensuraUnit, IEquatable => units.MultiplyCore(multiplicator, len, destination); // === ReadOnlySpan 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."); units.MultiplyCore(multiplicator, len, destination); } 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."); units.MultiplyCore(multiplicator, len, destination); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Multiply(this double multiplicator, ReadOnlySpan units, Span destination) where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator, destination); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Multiply(this double multiplicator, ReadOnlySpan units, Span destination) where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator, destination); // === Array === public static T[] Multiply(this T[] units, double multiplicator) where T : struct, IMensuraUnit, IEquatable { if (units is null) return null!; int len = units.Length; if (len == 0) return []; var result = new T[len]; Multiply(units, multiplicator, result); return result; } public static T?[] Multiply(this T?[] units, double multiplicator) where T : struct, IMensuraUnit, IEquatable { if (units is null) return null!; int len = units.Length; if (len == 0) return []; var result = new T?[len]; Multiply(units, multiplicator, result); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T[] Multiply(this double multiplicator, T[] units) where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T?[] Multiply(this double multiplicator, T?[] units) where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator); // === List === public static List Multiply(this List units, double multiplicator) where T : struct, IMensuraUnit, IEquatable { if (units is null) return null!; int len = units.Count; if (len == 0) return []; var resultArray = new T[len]; Multiply(CollectionsMarshal.AsSpan(units), multiplicator, resultArray); return resultArray.WrapAsList(); } public static List Multiply(this List units, double multiplicator) where T : struct, IMensuraUnit, IEquatable { if (units is null) return null!; int count = units.Count; if (count == 0) return []; var resultArray = new T?[count]; Multiply(CollectionsMarshal.AsSpan(units), multiplicator, resultArray); return resultArray.WrapAsList(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Multiply(this double multiplicator, List units) where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List Multiply(this double multiplicator, List units) where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator); // === ICollection === public static void Multiply(this ICollection units, double multiplicator, Span destination) where T : struct, IMensuraUnit, IEquatable { if (units is null) return; int count = units.Count; if (count == 0) return; if (destination.Length < count) throw new ArgumentException("Destination too short"); if (units is T[] array) { array.Multiply(multiplicator, destination); return; } if (units is List list) { CollectionsMarshal.AsSpan(list).Multiply(multiplicator, destination); return; } int i = 0; foreach (var item in units) destination[i++] = (item.ToDouble() * multiplicator).ToUnit(); } public static void Multiply(this ICollection units, double multiplicator, Span destination) where T : struct, IMensuraUnit, IEquatable { if (units is null) return; int count = units.Count; if (count == 0) return; if (destination.Length < count) throw new ArgumentException("Destination too short"); if (units is T?[] array) { array.Multiply(multiplicator, destination); return; } if (units is List list) { CollectionsMarshal.AsSpan(list).Multiply(multiplicator, destination); return; } int i = 0; foreach (var item in units) destination[i++] = item.HasValue ? (item.Value.ToDouble() * multiplicator).ToUnit() : null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Multiply(this double multiplicator, ICollection units, Span destination) where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator, destination); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Multiply(this double multiplicator, ICollection units, Span destination) where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator, destination); // === IReadOnlyCollection === public static void Multiply(this IReadOnlyCollection units, double multiplicator, Span destination) where T : struct, IMensuraUnit, IEquatable { if (units is null) return; int count = units.Count; if (count == 0) return; if (destination.Length < count) throw new ArgumentException("Destination too short"); if (units is T[] array) { array.Multiply(multiplicator, destination); return; } if (units is List list) { CollectionsMarshal.AsSpan(list).Multiply(multiplicator, destination); return; } int i = 0; foreach (var item in units) destination[i++] = (item.ToDouble() * multiplicator).ToUnit(); } public static void Multiply(this IReadOnlyCollection units, double multiplicator, Span destination) where T : struct, IMensuraUnit, IEquatable { if (units is null) return; int count = units.Count; if (count == 0) return; if (destination.Length < count) throw new ArgumentException("Destination too short"); if (units is T?[] array) { array.Multiply(multiplicator, destination); return; } if (units is List list) { CollectionsMarshal.AsSpan(list).Multiply(multiplicator, destination); return; } int i = 0; foreach (var item in units) destination[i++] = item.HasValue ? (item.Value.ToDouble() * multiplicator).ToUnit() : null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Multiply(this double multiplicator, IReadOnlyCollection units, Span destination) where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator, destination); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Multiply(this double multiplicator, IReadOnlyCollection units, Span destination) where T : struct, IMensuraUnit, IEquatable => units.Multiply(multiplicator, destination); // === IEnumerable + yeild === static IEnumerable MultiplyIterator(IEnumerable units, double multiplicator) where T : struct, IMensuraUnit, IEquatable { foreach (var item in units) yield return (item.ToDouble() * multiplicator).ToUnit(); } static IEnumerable MultiplyNullableIterator(IEnumerable units, double multiplicator) where T : struct, IMensuraUnit, IEquatable { foreach (T? item in units) yield return item.HasValue ? (item.Value.ToDouble() * multiplicator).ToUnit() : null; } // === IEnumerable === public static IEnumerable Multiply(this IEnumerable units, double multiplicator) where T : struct, IMensuraUnit, IEquatable { if (units is null) return null!; if (units is T[] array) return array.Multiply(multiplicator); if (units is List list) return list.Multiply(multiplicator); if (units is ICollection col) { var arr = new T[col.Count]; col.Multiply(multiplicator, arr); return arr; } if (units is IReadOnlyCollection roc) { var arr = new T[roc.Count]; roc.Multiply(multiplicator, arr); return arr; } return MultiplyIterator(units, multiplicator); } public static IEnumerable Multiply(this IEnumerable units, double multiplicator) where T : struct, IMensuraUnit, IEquatable { if (units is null) return null!; if (units is T?[] array) return array.Multiply(multiplicator); if (units is List list) return list.Multiply(multiplicator); if (units is ICollection col) { var arr = new T?[col.Count]; col.Multiply(multiplicator, arr); return arr; } if (units is IReadOnlyCollection roc) { var arr = new T?[roc.Count]; roc.Multiply(multiplicator, arr); return arr; } return MultiplyNullableIterator(units, multiplicator); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Multiply(this double multiplicator, IEnumerable units) where T : struct, IMensuraUnit, IEquatable => Multiply(units, multiplicator); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IEnumerable Multiply(this double multiplicator, IEnumerable units) where T : struct, IMensuraUnit, IEquatable => Multiply(units, multiplicator); }