Files
QWERTYkez.Mensura/QWERTYkez.Mensura/Extensions/CollectionsPow3Extensions.cs
2026-06-12 23:34:00 +07:00

258 lines
10 KiB
C#

namespace QWERTYkez.Mensura.Extensions;
internal static partial class CollectionsPow3Extensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)] internal static double QuickPow3(this double base_Value) => base_Value * base_Value * base_Value;
// === ТРЕТЬЯ СТЕПЕНЬ ==========================================
// === Pow3Core === SIMD
internal static void Pow3Core<T, R>(this ReadOnlySpan<T> units, int len, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
// Делаем реинтерпретацию памяти без выделений и копирований
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(units[..len]);
Span<double> dstDouble = MemoryMarshal.Cast<R, double>(destination[..len]);
int i = 0;
// Используем SIMD. Vector<double> автоматически выберет AVX2 (4 элемента) или AVX-512 (8 элементов)
if (Vector.IsHardwareAccelerated && len >= Vector<double>.Count)
{
ref double srcStart = ref MemoryMarshal.GetReference(srcDouble);
ref double dstStart = ref MemoryMarshal.GetReference(dstDouble);
int limit = len - Vector<double>.Count;
for (; i <= limit; i += Vector<double>.Count)
{
ref double srcCurrent = ref Unsafe.Add(ref srcStart, i);
ref double dstCurrent = ref Unsafe.Add(ref dstStart, i);
// Загружаем вектор, умножаем сам на себя (квадрат) и сохраняем
Vector<double> v = Unsafe.As<double, Vector<double>>(ref srcCurrent);
Unsafe.As<double, Vector<double>>(ref dstCurrent) = v * v * v;
}
}
// Добираем остаток элементов, если длина не кратна размеру вектора
for (; i < len; i++)
{
double val = srcDouble[i];
dstDouble[i] = val * val * val;
}
}
internal static void Pow3Core<T, R>(this ReadOnlySpan<T?> units, int len, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
// 1. Получаем низкоуровневые ссылки на первый элемент каждого Span
ref T? srcRef = ref MemoryMarshal.GetReference(units);
ref R? dstRef = ref MemoryMarshal.GetReference(destination);
// 2. Делаем каст самих ref-ссылок в наш мимик. Компилятор это пропускает.
ref NullableDoubleMimic srcStart = ref Unsafe.As<T?, NullableDoubleMimic>(ref srcRef);
ref NullableDoubleMimic dstStart = ref Unsafe.As<R?, NullableDoubleMimic>(ref dstRef);
int i = 0;
int limit = len - 4;
// Loop Unrolling (Развёртка)
for (; i <= limit; i += 4)
{
ref var s0 = ref Unsafe.Add(ref srcStart, i);
ref var s1 = ref Unsafe.Add(ref srcStart, i + 1);
ref var s2 = ref Unsafe.Add(ref srcStart, i + 2);
ref var s3 = ref Unsafe.Add(ref srcStart, i + 3);
ref var d0 = ref Unsafe.Add(ref dstStart, i);
ref var d1 = ref Unsafe.Add(ref dstStart, i + 1);
ref var d2 = ref Unsafe.Add(ref dstStart, i + 2);
ref var d3 = ref Unsafe.Add(ref dstStart, i + 3);
d0.Value = s0.Value * s0.Value * s0.Value;
d0.HasValue = s0.HasValue;
d1.Value = s1.Value * s1.Value * s1.Value;
d1.HasValue = s1.HasValue;
d2.Value = s2.Value * s2.Value * s2.Value;
d2.HasValue = s2.HasValue;
d3.Value = s3.Value * s3.Value * s3.Value;
d3.HasValue = s3.HasValue;
}
// Добираем остатки
for (; i < len; i++)
{
ref var s = ref Unsafe.Add(ref srcStart, i);
ref var d = ref Unsafe.Add(ref dstStart, i);
d.Value = s.Value * s.Value * s.Value;
d.HasValue = s.HasValue;
}
}
// === ReadOnlySpan
internal static void Pow3<T, R>(this ReadOnlySpan<T> units, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
int len = units.Length;
if (len == 0) return;
if (len > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
units.Pow3Core(len, destination);
}
internal static void Pow3<T, R>(this ReadOnlySpan<T?> units, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
int len = units.Length;
if (len == 0) return;
if (len > destination.Length)
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
units.Pow3Core(len, destination);
}
// === Array ===
internal static R[] Pow3<T, R>(this T[] units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new R[len];
units.Pow3Core(len, result);
return result;
}
internal static R?[] Pow3<T, R>(this T?[] units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
int len = units.Length;
if (len == 0) return [];
var result = new R?[len];
units.Pow3Core(len, result);
return result;
}
// === List<Length> ===
internal static List<R> Pow3<T, R>(this List<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
var result = new R[count];
CollectionsMarshal.AsSpan(units).Pow3Core(count, result);
return result.WrapAsList<R, R>();
}
internal static List<R?> Pow3<T, R>(this List<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
int count = units.Count;
if (count == 0) return [];
var result = new R?[count];
CollectionsMarshal.AsSpan(units).Pow3Core(count, result);
return result.WrapAsList<R, R>();
}
// === IReadOnlyCollection<Length> ===
internal static void Pow3<T, R>(this IReadOnlyCollection<T> units, Span<R> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
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.Pow3Core(count, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Pow3Core(count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.ToDouble().QuickPow3().ToUnit<R>();
}
internal static void Pow3<T, R>(this IReadOnlyCollection<T?> units, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
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.Pow3Core(count, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Pow3Core(count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.Protected().QuickPow3().ToUnit<R>();
}
// === IEnumerable<T, R> + yeild ===
static IEnumerable<R> PowIterator3<T, R>(IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
foreach (var item in units)
yield return (item.ToDouble().QuickPow3()).ToUnit<R>();
}
static IEnumerable<R?> PowNullableIterator3<T, R>(IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
foreach (var item in units)
yield return item.Protected().QuickPow3().ToUnit<R>();
}
// === IEnumerable<Length> ===
internal static IEnumerable<R> Pow3<T, R>(this IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
if (units is T[] array) return array.Pow3<T, R>();
if (units is List<T> list) return list.Pow3<T, R>();
if (units is IReadOnlyCollection<T> roc)
{
var arr = roc.ToArray();
arr.Pow3Core(arr.Length, arr);
return arr.ReCast<T, R>();
}
else return PowIterator3<T, R>(units);
}
internal static IEnumerable<R?> Pow3<T, R>(this IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
if (units is null) return null!;
if (units is T?[] array) return array.Pow3<T, R>();
if (units is List<T?> list) return list.Pow3<T, R>();
if (units is IReadOnlyCollection<T?> roc)
{
var arr = roc.ToArray();
arr.Pow3Core(arr.Length, arr);
return arr.ReCast<T, R>();
}
else return PowNullableIterator3<T, R>(units);
}
}