588 lines
23 KiB
C#
588 lines
23 KiB
C#
|
|
using System.Buffers;
|
|||
|
|
|
|||
|
|
namespace QWERTYkez.Mensura.Extensions;
|
|||
|
|
|
|||
|
|
public static partial class CollectionsDivideExtensions
|
|||
|
|
{
|
|||
|
|
// === ReadOnlySpan === SIMD
|
|||
|
|
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;
|
|||
|
|
if (len > destination.Length)
|
|||
|
|
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
|
|||
|
|
|
|||
|
|
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(units);
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
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;
|
|||
|
|
if (len > destination.Length)
|
|||
|
|
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
|
|||
|
|
|
|||
|
|
|
|||
|
|
// Получаем прямые неуправляемые 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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//SIMD
|
|||
|
|
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;
|
|||
|
|
if (len > destination.Length)
|
|||
|
|
throw new ArgumentException("Целевой буфер destination меньше исходного source.");
|
|||
|
|
|
|||
|
|
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(units);
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// === Array ===
|
|||
|
|
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];
|
|||
|
|
Divide(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];
|
|||
|
|
Divide(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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// === 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 resultArray = new T[len];
|
|||
|
|
Divide(CollectionsMarshal.AsSpan(units), divisor, resultArray);
|
|||
|
|
return resultArray.WrapAsList();
|
|||
|
|
}
|
|||
|
|
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.WrapAsList();
|
|||
|
|
}
|
|||
|
|
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 resultArray = new T[len];
|
|||
|
|
Divide(dividend, CollectionsMarshal.AsSpan(units), resultArray);
|
|||
|
|
return resultArray.WrapAsList();
|
|||
|
|
}
|
|||
|
|
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.WrapAsList();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// === 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);
|
|||
|
|
Divide(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);
|
|||
|
|
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);
|
|||
|
|
Divide(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);
|
|||
|
|
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
|
|||
|
|
{
|
|||
|
|
int index = 0;
|
|||
|
|
foreach (var item in units) sharedArray[index++] = item;
|
|||
|
|
Divide(sharedArray, 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);
|
|||
|
|
|
|||
|
|
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
|
|||
|
|
var resultArray = new T?[count];
|
|||
|
|
double invDivisor = 1.0 / divisor;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
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<T>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
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 array.Divide(dividend);
|
|||
|
|
if (units is List<T> list) return list.Divide(dividend);
|
|||
|
|
|
|||
|
|
T[] sharedArray = ArrayPool<T>.Shared.Rent(count);
|
|||
|
|
var finalResult = new T[count];
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
int index = 0;
|
|||
|
|
foreach (var item in units) sharedArray[index++] = item;
|
|||
|
|
Divide(dividend, sharedArray, 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 array.Divide(dividend);
|
|||
|
|
if (units is List<T?> list) return list.Divide(dividend);
|
|||
|
|
|
|||
|
|
T?[] sharedNullableArray = ArrayPool<T?>.Shared.Rent(count);
|
|||
|
|
var resultArray = new T?[count];
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
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<T>();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
}
|