Files
QWERTYkez.Mensura/QWERTYkez.Mensura/Extensions/CollectionsDivideExtensions.cs

924 lines
37 KiB
C#
Raw Normal View History

2026-06-03 12:07:27 +07:00
namespace QWERTYkez.Mensura.Extensions;
2026-06-02 12:28:46 +07:00
public static partial class CollectionsDivideExtensions
{
2026-06-03 12:07:27 +07:00
// === DivideCore === SIMD
internal static void DivideCore<T, R>(this ReadOnlySpan<T> units, double divisor, int len, Span<R> destination)
2026-06-02 12:28:46 +07:00
where T : struct, IMensuraUnit, IEquatable<T>
2026-06-03 12:07:27 +07:00
where R : struct, IMensuraUnit, IEquatable<R>
2026-06-02 12:28:46 +07:00
{
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(units);
2026-06-03 12:07:27 +07:00
Span<double> dstDouble = MemoryMarshal.Cast<R, double>(destination);
2026-06-02 12:28:46 +07:00
// Вместо деления в цикле, умножаем на обратное число (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;
}
}
2026-06-03 12:07:27 +07:00
internal static void DivideCore<T, R>(this ReadOnlySpan<T?> units, double divisor, int len, Span<R?> destination)
2026-06-02 12:28:46 +07:00
where T : struct, IMensuraUnit, IEquatable<T>
2026-06-03 12:07:27 +07:00
where R : struct, IMensuraUnit, IEquatable<R>
2026-06-02 12:28:46 +07:00
{
// Получаем прямые неуправляемые 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 (сбросятся байты флага)
2026-06-03 12:07:27 +07:00
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;
2026-06-02 12:28:46 +07:00
}
// 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук)
for (; i < len; i++)
{
T? unit = Unsafe.Add(ref srcRef, i);
ref var dst = ref Unsafe.Add(ref dstRef, i);
2026-06-03 12:07:27 +07:00
dst = unit.HasValue ? (unit.Value.ToDouble() * invDivisor).ToUnit<R>() : null;
2026-06-02 12:28:46 +07:00
}
}
//SIMD
2026-06-03 12:07:27 +07:00
internal static void DivideCore<T, R>(this double dividend, ReadOnlySpan<T> units, int len, Span<R> destination)
2026-06-02 12:28:46 +07:00
where T : struct, IMensuraUnit, IEquatable<T>
2026-06-03 12:07:27 +07:00
where R : struct, IMensuraUnit, IEquatable<R>
2026-06-02 12:28:46 +07:00
{
ReadOnlySpan<double> srcDouble = MemoryMarshal.Cast<T, double>(units);
2026-06-03 12:07:27 +07:00
Span<double> dstDouble = MemoryMarshal.Cast<R, double>(destination);
2026-06-02 12:28:46 +07:00
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);
}
}
2026-06-03 12:07:27 +07:00
internal static void DivideCore<T, R>(this double dividend, ReadOnlySpan<T?> units, int len, Span<R?> destination)
2026-06-02 12:28:46 +07:00
where T : struct, IMensuraUnit, IEquatable<T>
2026-06-03 12:07:27 +07:00
where R : struct, IMensuraUnit, IEquatable<R>
2026-06-02 12:28:46 +07:00
{
// Получаем прямые неуправляемые 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 (сбросятся байты флага)
2026-06-03 12:07:27 +07:00
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;
2026-06-02 12:28:46 +07:00
}
// 2. ХВОСТ ЦИКЛА: Довычисляем остаток элементов (от 1 до 3 штук)
for (; i < len; i++)
{
T? unit = Unsafe.Add(ref srcRef, i);
ref var dst = ref Unsafe.Add(ref dstRef, i);
2026-06-03 12:07:27 +07:00
dst = unit.HasValue ? (dividend / unit.Value.ToDouble()).ToUnit<R>() : null;
2026-06-02 12:28:46 +07:00
}
}
2026-06-03 12:07:27 +07:00
2026-06-04 12:03:39 +07:00
// === 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);
}
2026-06-03 12:07:27 +07:00
// === 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);
}
2026-06-02 12:28:46 +07:00
// === 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];
2026-06-03 12:07:27 +07:00
Divide(units, divisor, result);
2026-06-02 12:28:46 +07:00
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];
2026-06-03 12:07:27 +07:00
Divide(dividend, units, result);
2026-06-02 12:28:46 +07:00
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];
2026-06-03 12:07:27 +07:00
Divide(CollectionsMarshal.AsSpan(units), divisor, resultArray);
2026-06-02 12:28:46 +07:00
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!;
2026-06-03 12:07:27 +07:00
int count = units.Count;
if (count == 0) return [];
2026-06-02 12:28:46 +07:00
2026-06-03 12:07:27 +07:00
var resultArray = new T[count];
2026-06-02 12:28:46 +07:00
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];
2026-06-03 12:07:27 +07:00
Divide(dividend, CollectionsMarshal.AsSpan(units), resultArray);
2026-06-02 12:28:46 +07:00
return resultArray.WrapAsList();
}
// === ICollection<T> ===
2026-06-03 12:07:27 +07:00
public static void Divide<T>(this ICollection<T> units, double divisor, Span<T> destination)
2026-06-02 12:28:46 +07:00
where T : struct, IMensuraUnit, IEquatable<T>
{
2026-06-03 12:07:27 +07:00
if (units is null) return;
2026-06-02 12:28:46 +07:00
int count = units.Count;
2026-06-03 12:07:27 +07:00
if (count == 0) return;
if (destination.Length < count)
throw new ArgumentException("Destination too short");
2026-06-02 12:28:46 +07:00
2026-06-03 12:07:27 +07:00
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>();
2026-06-02 12:28:46 +07:00
}
2026-06-03 12:07:27 +07:00
public static void Divide<T>(this ICollection<T?> units, double divisor, Span<T?> destination)
2026-06-02 12:28:46 +07:00
where T : struct, IMensuraUnit, IEquatable<T>
{
2026-06-03 12:07:27 +07:00
if (units is null) return;
2026-06-02 12:28:46 +07:00
int count = units.Count;
2026-06-03 12:07:27 +07:00
if (count == 0) return;
if (destination.Length < count)
throw new ArgumentException("Destination too short");
2026-06-02 12:28:46 +07:00
2026-06-03 12:07:27 +07:00
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;
2026-06-02 12:28:46 +07:00
}
2026-06-03 12:07:27 +07:00
public static void Divide<T>(this double dividend, ICollection<T> units, Span<T> destination)
2026-06-02 12:28:46 +07:00
where T : struct, IMensuraUnit, IEquatable<T>
{
2026-06-03 12:07:27 +07:00
if (units is null) return;
2026-06-02 12:28:46 +07:00
int count = units.Count;
2026-06-03 12:07:27 +07:00
if (count == 0) return;
if (destination.Length < count)
throw new ArgumentException("Destination too short");
2026-06-02 12:28:46 +07:00
2026-06-03 12:07:27 +07:00
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>();
2026-06-02 12:28:46 +07:00
}
2026-06-03 12:07:27 +07:00
public static void Divide<T>(this double dividend, ICollection<T?> units, Span<T?> destination)
2026-06-02 12:28:46 +07:00
where T : struct, IMensuraUnit, IEquatable<T>
{
2026-06-03 12:07:27 +07:00
if (units is null) return;
2026-06-02 12:28:46 +07:00
int count = units.Count;
2026-06-03 12:07:27 +07:00
if (count == 0) return;
if (destination.Length < count)
throw new ArgumentException("Destination too short");
2026-06-02 12:28:46 +07:00
2026-06-03 12:07:27 +07:00
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;
2026-06-02 12:28:46 +07:00
}
// === IReadOnlyCollection<T> ===
2026-06-03 12:07:27 +07:00
public static void Divide<T>(this IReadOnlyCollection<T> units, double divisor, Span<T> destination)
2026-06-02 12:28:46 +07:00
where T : struct, IMensuraUnit, IEquatable<T>
{
2026-06-03 12:07:27 +07:00
if (units is null) return;
2026-06-02 12:28:46 +07:00
int count = units.Count;
2026-06-03 12:07:27 +07:00
if (count == 0) return;
if (destination.Length < count)
throw new ArgumentException("Destination too short");
2026-06-02 12:28:46 +07:00
2026-06-03 12:07:27 +07:00
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>();
2026-06-02 12:28:46 +07:00
}
2026-06-03 12:07:27 +07:00
public static void Divide<T>(this IReadOnlyCollection<T?> units, double divisor, Span<T?> destination)
2026-06-02 12:28:46 +07:00
where T : struct, IMensuraUnit, IEquatable<T>
{
2026-06-03 12:07:27 +07:00
if (units is null) return;
2026-06-02 12:28:46 +07:00
int count = units.Count;
2026-06-03 12:07:27 +07:00
if (count == 0) return;
if (destination.Length < count)
throw new ArgumentException("Destination too short");
2026-06-02 12:28:46 +07:00
2026-06-03 12:07:27 +07:00
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;
2026-06-02 12:28:46 +07:00
double invDivisor = 1.0 / divisor;
2026-06-03 12:07:27 +07:00
foreach (var item in units)
destination[i++] = item.HasValue
? (item.Value.ToDouble() * invDivisor).ToUnit<T>() : null;
2026-06-02 12:28:46 +07:00
}
2026-06-03 12:07:27 +07:00
public static void Divide<T>(this double dividend, IReadOnlyCollection<T> units, Span<T> destination)
2026-06-02 12:28:46 +07:00
where T : struct, IMensuraUnit, IEquatable<T>
{
2026-06-03 12:07:27 +07:00
if (units is null) return;
2026-06-02 12:28:46 +07:00
int count = units.Count;
2026-06-03 12:07:27 +07:00
if (count == 0) return;
if (destination.Length < count)
throw new ArgumentException("Destination too short");
2026-06-02 12:28:46 +07:00
2026-06-03 12:07:27 +07:00
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>();
2026-06-02 12:28:46 +07:00
}
2026-06-03 12:07:27 +07:00
public static void Divide<T>(this double dividend, IReadOnlyCollection<T?> units, Span<T?> destination)
2026-06-02 12:28:46 +07:00
where T : struct, IMensuraUnit, IEquatable<T>
{
2026-06-03 12:07:27 +07:00
if (units is null) return;
2026-06-02 12:28:46 +07:00
int count = units.Count;
2026-06-03 12:07:27 +07:00
if (count == 0) return;
if (destination.Length < count)
throw new ArgumentException("Destination too short");
2026-06-02 12:28:46 +07:00
2026-06-03 12:07:27 +07:00
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;
2026-06-02 12:28:46 +07:00
}
// === 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);
2026-06-03 12:07:27 +07:00
if (units is ICollection<T> col)
{
2026-06-04 12:03:39 +07:00
var arr = col.ToArray();
arr.Divide(divisor, arr);
2026-06-03 12:07:27 +07:00
return arr;
}
if (units is IReadOnlyCollection<T> roc)
{
2026-06-04 12:03:39 +07:00
var arr = roc.ToArray();
arr.Divide(divisor, arr);
2026-06-03 12:07:27 +07:00
return arr;
}
2026-06-02 12:28:46 +07:00
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);
2026-06-03 12:07:27 +07:00
if (units is ICollection<T?> col)
{
2026-06-04 12:03:39 +07:00
var arr = col.ToArray();
arr.Divide(divisor, arr);
2026-06-03 12:07:27 +07:00
return arr;
}
if (units is IReadOnlyCollection<T?> roc)
{
2026-06-04 12:03:39 +07:00
var arr = roc.ToArray();
arr.Divide(divisor, arr);
2026-06-03 12:07:27 +07:00
return arr;
}
2026-06-02 12:28:46 +07:00
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);
2026-06-03 12:07:27 +07:00
if (units is ICollection<T> col)
{
2026-06-04 12:03:39 +07:00
var arr = col.ToArray();
dividend.Divide(arr, arr);
2026-06-03 12:07:27 +07:00
return arr;
}
if (units is IReadOnlyCollection<T> roc)
{
2026-06-04 12:03:39 +07:00
var arr = roc.ToArray();
dividend.Divide(arr, arr);
2026-06-03 12:07:27 +07:00
return arr;
}
2026-06-02 12:28:46 +07:00
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);
2026-06-03 12:07:27 +07:00
if (units is ICollection<T?> col)
{
2026-06-04 12:03:39 +07:00
var arr = col.ToArray();
dividend.Divide(arr, arr);
2026-06-03 12:07:27 +07:00
return arr;
}
if (units is IReadOnlyCollection<T?> roc)
{
2026-06-04 12:03:39 +07:00
var arr = roc.ToArray();
dividend.Divide(arr, arr);
2026-06-03 12:07:27 +07:00
return arr;
}
2026-06-02 12:28:46 +07:00
return DivideNullableIterator(dividend, units);
}
}