258 lines
10 KiB
C#
258 lines
10 KiB
C#
namespace QWERTYkez.Mensura.Extensions;
|
|
|
|
internal static partial class CollectionsPow2Extensions
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] internal static double QuickPow2(this double base_Value) => base_Value * base_Value;
|
|
|
|
|
|
// === ВТОРАЯ СТЕПЕНЬ ==========================================
|
|
|
|
// === Pow2Core === SIMD
|
|
internal static void Pow2Core<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;
|
|
}
|
|
}
|
|
|
|
// Добираем остаток элементов, если длина не кратна размеру вектора
|
|
for (; i < len; i++)
|
|
{
|
|
double val = srcDouble[i];
|
|
dstDouble[i] = val * val;
|
|
}
|
|
}
|
|
internal static void Pow2Core<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;
|
|
d0.HasValue = s0.HasValue;
|
|
|
|
d1.Value = s1.Value * s1.Value;
|
|
d1.HasValue = s1.HasValue;
|
|
|
|
d2.Value = s2.Value * s2.Value;
|
|
d2.HasValue = s2.HasValue;
|
|
|
|
d3.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;
|
|
d.HasValue = s.HasValue;
|
|
}
|
|
}
|
|
|
|
|
|
// === ReadOnlySpan
|
|
internal static void Pow2<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.Pow2Core(len, destination);
|
|
}
|
|
internal static void Pow2<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.Pow2Core(len, destination);
|
|
}
|
|
|
|
// === Array ===
|
|
internal static R[] Pow2<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.Pow2Core(len, result);
|
|
return result;
|
|
}
|
|
internal static R?[] Pow2<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.Pow2Core(len, result);
|
|
return result;
|
|
}
|
|
|
|
// === List<Length> ===
|
|
internal static List<R> Pow2<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).Pow2Core(count, result);
|
|
return result.WrapAsList<R, R>();
|
|
}
|
|
internal static List<R?> Pow2<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).Pow2Core(count, result);
|
|
return result.WrapAsList<R, R>();
|
|
}
|
|
|
|
// === IReadOnlyCollection<Length> ===
|
|
internal static void Pow2<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.Pow2Core(count, destination); return; }
|
|
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Pow2Core(count, destination); return; }
|
|
|
|
int i = 0;
|
|
foreach (var item in units)
|
|
destination[i++] = item.ToDouble().QuickPow2().ToUnit<R>();
|
|
}
|
|
internal static void Pow2<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.Pow2Core(count, destination); return; }
|
|
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Pow2Core(count, destination); return; }
|
|
|
|
int i = 0;
|
|
foreach (var item in units)
|
|
destination[i++] = item.Protected().QuickPow2().ToUnit<R>();
|
|
}
|
|
|
|
// === IEnumerable<T, R> + yeild ===
|
|
static IEnumerable<R> Pow2Iterator<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().QuickPow2()).ToUnit<R>();
|
|
}
|
|
static IEnumerable<R?> Pow2NullableIterator<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().QuickPow2().ToUnit<R>();
|
|
}
|
|
|
|
// === IEnumerable<Length> ===
|
|
internal static IEnumerable<R> Pow2<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.Pow2<T, R>();
|
|
if (units is List<T> list) return list.Pow2<T, R>();
|
|
if (units is IReadOnlyCollection<T> roc)
|
|
{
|
|
var arr = roc.ToArray();
|
|
arr.Pow2Core(arr.Length, arr);
|
|
return arr.ReCast<T, R>();
|
|
}
|
|
else return Pow2Iterator<T, R>(units);
|
|
}
|
|
internal static IEnumerable<R?> Pow2<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.Pow2<T, R>();
|
|
if (units is List<T?> list) return list.Pow2<T, R>();
|
|
if (units is IReadOnlyCollection<T?> roc)
|
|
{
|
|
var arr = roc.ToArray();
|
|
arr.Pow2Core(arr.Length, arr);
|
|
return arr.ReCast<T, R>();
|
|
}
|
|
else return Pow2NullableIterator<T, R>(units);
|
|
}
|
|
} |