Files
QWERTYkez.Mensura/QWERTYkez.Mensura/Extensions/CollectionsDivideExtensions.cs
melekhin 96bbbbd292 26.06.04
2026-06-04 12:03:39 +07:00

924 lines
37 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace QWERTYkez.Mensura.Extensions;
public static partial class CollectionsDivideExtensions
{
// === DivideCore === SIMD
internal static void DivideCore<T, R>(this ReadOnlySpan<T> units, double divisor, 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);
Span<double> dstDouble = MemoryMarshal.Cast<R, 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;
}
}
internal static void DivideCore<T, R>(this ReadOnlySpan<T?> units, double divisor, int len, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
// Получаем прямые неуправляемые 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<R>() : null;
d1 = u1.HasValue ? (u1.Value.ToDouble() * invDivisor).ToUnit<R>() : null;
d2 = u2.HasValue ? (u2.Value.ToDouble() * invDivisor).ToUnit<R>() : null;
d3 = u3.HasValue ? (u3.Value.ToDouble() * invDivisor).ToUnit<R>() : 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<R>() : null;
}
}
//SIMD
internal static void DivideCore<T, R>(this double dividend, 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);
Span<double> dstDouble = MemoryMarshal.Cast<R, 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);
}
}
internal static void DivideCore<T, R>(this double dividend, ReadOnlySpan<T?> units, int len, Span<R?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
// Получаем прямые неуправляемые 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<R>() : null;
d1 = u1.HasValue ? (dividend / u1.Value.ToDouble()).ToUnit<R>() : null;
d2 = u2.HasValue ? (dividend / u2.Value.ToDouble()).ToUnit<R>() : null;
d3 = u3.HasValue ? (dividend / u3.Value.ToDouble()).ToUnit<R>() : 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<R>() : null;
}
}
// === Array ===
internal static R[] Divide<T, R>(this T[] units, double divisor)
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.DivideCore(divisor, len, result);
return result;
}
internal static R?[] Divide<T, R>(this T?[] units, double divisor)
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.DivideCore(divisor, len, result);
return result;
}
internal static R[] Divide<T, R>(this double dividend, 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];
dividend.DivideCore(units, len, result);
return result;
}
internal static R?[] Divide<T, R>(this double dividend, 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];
dividend.DivideCore(units, len, result);
return result;
}
// === List<T> ===
internal static List<R> Divide<T, R>(this List<T> units, double divisor)
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 resultArray = new R[count];
DivideCore(CollectionsMarshal.AsSpan(units), divisor, count, resultArray);
return resultArray.WrapAsList();
}
internal static List<R?> Divide<T, R>(this List<T?> units, double divisor)
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 resultArray = new R?[count];
DivideCore(CollectionsMarshal.AsSpan(units), divisor, count, resultArray);
return resultArray.WrapAsList();
}
internal static List<R> Divide<T, R>(this double dividend, 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 resultArray = new R[count];
DivideCore(dividend, CollectionsMarshal.AsSpan(units), count, resultArray);
return resultArray.WrapAsList();
}
internal static List<R?> Divide<T, R>(this double dividend, 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 resultArray = new R?[count];
DivideCore(dividend, CollectionsMarshal.AsSpan(units), count, resultArray);
return resultArray.WrapAsList();
}
// === ICollection<T> ===
internal static void Divide<T, R>(this ICollection<T> units, double divisor, 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) { DivideCore(array, divisor, count, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).DivideCore(divisor, count, destination); return; }
int i = 0;
double invDivisor = 1.0 / divisor;
foreach (var item in units)
destination[i++] = (item.ToDouble() * invDivisor).ToUnit<R>();
}
internal static void Divide<T, R>(this ICollection<T?> units, double divisor, 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.DivideCore(divisor, count, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).DivideCore(divisor, count, destination); return; }
int i = 0;
double invDivisor = 1.0 / divisor;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value.ToDouble() * invDivisor).ToUnit<R>() : null;
}
internal static void Divide<T, R>(this double dividend, ICollection<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) { dividend.DivideCore(array, count, destination); return; }
if (units is List<T> list) { dividend.DivideCore(CollectionsMarshal.AsSpan(list), count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (dividend / item.ToDouble()).ToUnit<R>();
}
internal static void Divide<T, R>(this double dividend, ICollection<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) { dividend.DivideCore(array, count, destination); return; }
if (units is List<T?> list) { dividend.DivideCore(CollectionsMarshal.AsSpan(list), count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (dividend / item.Value.ToDouble()).ToUnit<R>() : null;
}
// === IReadOnlyCollection<T> ===
internal static void Divide<T, R>(this IReadOnlyCollection<T> units, double divisor, 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.DivideCore(divisor, count, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).DivideCore(divisor, count, destination); return; }
int i = 0;
double invDivisor = 1.0 / divisor;
foreach (var item in units)
destination[i++] = (item.ToDouble() * invDivisor).ToUnit<R>();
}
internal static void Divide<T, R>(this IReadOnlyCollection<T?> units, double divisor, 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.DivideCore(divisor, count, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).DivideCore(divisor, count, destination); return; }
int i = 0;
double invDivisor = 1.0 / divisor;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value.ToDouble() * invDivisor).ToUnit<R>() : null;
}
internal static void Divide<T, R>(this double dividend, 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) { dividend.DivideCore(array, count, destination); return; }
if (units is List<T> list) { dividend.DivideCore(CollectionsMarshal.AsSpan(list), count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (dividend / item.ToDouble()).ToUnit<R>();
}
internal static void Divide<T, R>(this double dividend, 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) { dividend.DivideCore(array, count, destination); return; }
if (units is List<T?> list) { dividend.DivideCore(CollectionsMarshal.AsSpan(list), count, destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (dividend / item.Value.ToDouble()).ToUnit<R>() : null;
}
// === IEnumerable<T> + yeild ===
static IEnumerable<R> DivideIterator<T, R>(IEnumerable<T> units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
double invDivisor = 1.0 / divisor;
foreach (var item in units)
yield return (item.ToDouble() * invDivisor).ToUnit<R>();
}
static IEnumerable<R?> DivideNullableIterator<T, R>(IEnumerable<T?> units, double divisor)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
double invDivisor = 1.0 / divisor;
foreach (T? item in units)
yield return item.HasValue
? (item.Value.ToDouble() * invDivisor).ToUnit<R>() : null;
}
static IEnumerable<R> DivideIterator<T, R>(double dividend, IEnumerable<T> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
foreach (var item in units)
yield return (dividend / item.ToDouble()).ToUnit<R>();
}
static IEnumerable<R?> DivideNullableIterator<T, R>(double dividend, IEnumerable<T?> units)
where T : struct, IMensuraUnit, IEquatable<T>
where R : struct, IMensuraUnit, IEquatable<R>
{
foreach (T? item in units)
yield return item.HasValue
? (dividend / item.Value.ToDouble()).ToUnit<R>() : null;
}
// === IEnumerable<T> ===
internal static IEnumerable<R> Divide<T, R>(this IEnumerable<T> units, double divisor)
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.Divide<T, R>(divisor);
if (units is List<T> list) return list.Divide<T, R>(divisor);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
arr.Divide(divisor, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc)
{
var arr = roc.ToArray();
arr.Divide(divisor, arr);
return arr.ReCast<T, R>();
}
return DivideIterator<T, R>(units, divisor);
}
internal static IEnumerable<R?> Divide<T, R>(this IEnumerable<T?> units, double divisor)
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.Divide<T, R>(divisor);
if (units is List<T?> list) return list.Divide<T, R>(divisor);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
arr.Divide(divisor, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc)
{
var arr = roc.ToArray();
arr.Divide(divisor, arr);
return arr.ReCast<T, R>();
}
return DivideNullableIterator<T, R>(units, divisor);
}
internal static IEnumerable<R> Divide<T, R>(this double dividend, 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 dividend.Divide<T, R>(array);
if (units is List<T> list) return dividend.Divide<T, R>(list);
if (units is ICollection<T> col)
{
var arr = col.ToArray();
dividend.Divide(arr, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T> roc)
{
var arr = roc.ToArray();
dividend.Divide(arr, arr);
return arr.ReCast<T, R>();
}
return DivideIterator<T, R>(dividend, units);
}
internal static IEnumerable<R?> Divide<T, R>(this double dividend, 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 dividend.Divide<T, R>(array);
if (units is List<T?> list) return dividend.Divide<T, R>(list);
if (units is ICollection<T?> col)
{
var arr = col.ToArray();
dividend.Divide(arr, arr);
return arr.ReCast<T, R>();
}
if (units is IReadOnlyCollection<T?> roc)
{
var arr = roc.ToArray();
dividend.Divide(arr, arr);
return arr.ReCast<T, R>();
}
return DivideNullableIterator<T, R>(dividend, units);
}
// === ReadOnlySpan
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.");
units.DivideCore(divisor, len, destination);
}
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.");
units.DivideCore(divisor, len, destination);
}
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.");
dividend.DivideCore(units, len, destination);
}
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.");
dividend.DivideCore(units, len, destination);
}
// === 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 [];
var result = new T?[len];
Divide(units, divisor, 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];
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];
Divide(dividend, units, result);
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];
Divide(CollectionsMarshal.AsSpan(units), divisor, 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];
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];
Divide(dividend, CollectionsMarshal.AsSpan(units), resultArray);
return resultArray.WrapAsList();
}
// === ICollection<T> ===
public static void Divide<T>(this ICollection<T> units, double divisor, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
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.Divide(divisor, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Divide(divisor, destination); return; }
int i = 0;
double invDivisor = 1.0 / divisor;
foreach (var item in units)
destination[i++] = (item.ToDouble() * invDivisor).ToUnit<T>();
}
public static void Divide<T>(this ICollection<T?> units, double divisor, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
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.Divide(divisor, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Divide(divisor, destination); return; }
int i = 0;
double invDivisor = 1.0 / divisor;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value.ToDouble() * invDivisor).ToUnit<T>() : null;
}
public static void Divide<T>(this double dividend, ICollection<T> units, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
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) { dividend.Divide(array, destination); return; }
if (units is List<T> list) { dividend.Divide(CollectionsMarshal.AsSpan(list), destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (dividend / item.ToDouble()).ToUnit<T>();
}
public static void Divide<T>(this double dividend, ICollection<T?> units, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
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) { dividend.Divide(array, destination); return; }
if (units is List<T?> list) { dividend.Divide(CollectionsMarshal.AsSpan(list), destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (dividend / item.Value.ToDouble()).ToUnit<T>() : null;
}
// === IReadOnlyCollection<T> ===
public static void Divide<T>(this IReadOnlyCollection<T> units, double divisor, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
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.Divide(divisor, destination); return; }
if (units is List<T> list) { CollectionsMarshal.AsSpan(list).Divide(divisor, destination); return; }
int i = 0;
double invDivisor = 1.0 / divisor;
foreach (var item in units)
destination[i++] = (item.ToDouble() * invDivisor).ToUnit<T>();
}
public static void Divide<T>(this IReadOnlyCollection<T?> units, double divisor, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
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.Divide(divisor, destination); return; }
if (units is List<T?> list) { CollectionsMarshal.AsSpan(list).Divide(divisor, destination); return; }
int i = 0;
double invDivisor = 1.0 / divisor;
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value.ToDouble() * invDivisor).ToUnit<T>() : null;
}
public static void Divide<T>(this double dividend, IReadOnlyCollection<T> units, Span<T> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
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) { dividend.Divide(array, destination); return; }
if (units is List<T> list) { dividend.Divide(CollectionsMarshal.AsSpan(list), destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = (dividend / item.ToDouble()).ToUnit<T>();
}
public static void Divide<T>(this double dividend, IReadOnlyCollection<T?> units, Span<T?> destination)
where T : struct, IMensuraUnit, IEquatable<T>
{
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) { dividend.Divide(array, destination); return; }
if (units is List<T?> list) { dividend.Divide(CollectionsMarshal.AsSpan(list), destination); return; }
int i = 0;
foreach (var item in units)
destination[i++] = item.HasValue
? (dividend / item.Value.ToDouble()).ToUnit<T>() : null;
}
// === 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)
{
var arr = col.ToArray();
arr.Divide(divisor, arr);
return arr;
}
if (units is IReadOnlyCollection<T> roc)
{
var arr = roc.ToArray();
arr.Divide(divisor, arr);
return arr;
}
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)
{
var arr = col.ToArray();
arr.Divide(divisor, arr);
return arr;
}
if (units is IReadOnlyCollection<T?> roc)
{
var arr = roc.ToArray();
arr.Divide(divisor, arr);
return arr;
}
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)
{
var arr = col.ToArray();
dividend.Divide(arr, arr);
return arr;
}
if (units is IReadOnlyCollection<T> roc)
{
var arr = roc.ToArray();
dividend.Divide(arr, arr);
return arr;
}
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)
{
var arr = col.ToArray();
dividend.Divide(arr, arr);
return arr;
}
if (units is IReadOnlyCollection<T?> roc)
{
var arr = roc.ToArray();
dividend.Divide(arr, arr);
return arr;
}
return DivideNullableIterator(dividend, units);
}
}